├── .gitignore ├── Doxyfile ├── GUI-Release ├── makefile ├── objects.mk ├── sources.mk └── src │ └── subdir.mk ├── LICENSE ├── Ligo.md ├── Makefile ├── README.md ├── Release-ffmpeg ├── makefile ├── objects.mk ├── sources.mk └── src │ └── subdir.mk ├── Release ├── makefile ├── objects.mk ├── sources.mk └── src │ └── subdir.mk ├── Stereo.md ├── bash_completion.sh ├── create_tab_completion.py ├── examples ├── ligo-event.mp3 ├── ligo-filter.mp3 ├── ligo-raw.mp3 ├── mono-test.mp3 ├── spatial-strong-mono.mp3 ├── spatial-strong.mp3 ├── spatial-test-mono.mp3 ├── spatial-test.mp3 ├── stereo-mono-test.mp3 ├── stereo-strong-mono.mp3 ├── stereo-strong.mp3 ├── stereo-test-mono.mp3 └── stereo-test.mp3 ├── footer.html ├── images ├── crosstalk-example.png ├── factor-result.png ├── leveler-result.png ├── ligo-analysis.png ├── ligo-event.png ├── ligo-filter.png ├── ligo-raw.png ├── mono-test.png ├── multi-test.png ├── noise-result.png ├── normalize-result.png ├── normalize-space-result.png ├── skip-result-05.png ├── skip-result-06.png ├── skip-result-07.png ├── skip-result-08.png ├── skip-result-09.png ├── skip-result.png ├── skip-target.png ├── spatial-strong.png ├── spatial-test.png ├── stereo-mono-test.png ├── stereo-strong.png ├── stereo-test.png ├── xfilter-result.png └── xgate-result.png ├── logo ├── Makefile ├── icon.icns ├── icon.iconset │ ├── icon_128x128.png │ ├── icon_128x128@2x.png │ ├── icon_16x16.png │ ├── icon_16x16@2x.png │ ├── icon_256x256.png │ ├── icon_256x256@2x.png │ ├── icon_32x32.png │ ├── icon_32x32@2x.png │ ├── icon_512x512.png │ └── icon_512x512@2x.png └── ospac-logo.xcf ├── makefile.targets ├── ospac-internal.pdf ├── ospac.1 ├── src ├── Analyzer.cpp ├── Analyzer.h ├── Channel.cpp ├── Channel.h ├── CrosstalkFilter.cpp ├── CrosstalkFilter.h ├── CrosstalkGate.cpp ├── CrosstalkGate.h ├── Encode.cpp ├── Encode.h ├── Equalizer.cpp ├── Equalizer.h ├── Frequency.cpp ├── Frequency.h ├── GuiMain.cpp ├── Log.cpp ├── Log.h ├── Maximizer.cpp ├── Maximizer.h ├── Merge.cpp ├── Merge.h ├── MonoMix.cpp ├── MonoMix.h ├── OspacMain.cpp ├── OspacMain.h ├── Physics.cpp ├── Physics.h ├── Plot.cpp ├── Plot.h ├── SelectiveLeveler.cpp ├── SelectiveLeveler.h ├── Skip.cpp ├── Skip.h ├── StereoMix.cpp ├── StereoMix.h ├── Wave.cpp └── Wave.h ├── test ├── 1.wav ├── 2.wav ├── 3.wav ├── 4.wav ├── 5.wav ├── Makefile ├── dynamic-test.wav ├── intro-cooking-math-4.wav ├── intro-reverb-only.wav └── volume-changes.wav ├── version └── zsh_completion.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Release 2 | GUI-Release 3 | .DS_Store 4 | .cproject 5 | .project 6 | .settings 7 | *~ 8 | documentation 9 | -------------------------------------------------------------------------------- /GUI-Release/makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | -include ../makefile.init 6 | 7 | RM := rm -rf 8 | 9 | # All of the sources participating in the build are defined here 10 | -include sources.mk 11 | -include src/subdir.mk 12 | -include subdir.mk 13 | -include objects.mk 14 | 15 | ifneq ($(MAKECMDGOALS),clean) 16 | ifneq ($(strip $(CC_DEPS)),) 17 | -include $(CC_DEPS) 18 | endif 19 | ifneq ($(strip $(C++_DEPS)),) 20 | -include $(C++_DEPS) 21 | endif 22 | ifneq ($(strip $(C_UPPER_DEPS)),) 23 | -include $(C_UPPER_DEPS) 24 | endif 25 | ifneq ($(strip $(CXX_DEPS)),) 26 | -include $(CXX_DEPS) 27 | endif 28 | ifneq ($(strip $(C_DEPS)),) 29 | -include $(C_DEPS) 30 | endif 31 | ifneq ($(strip $(CPP_DEPS)),) 32 | -include $(CPP_DEPS) 33 | endif 34 | endif 35 | 36 | -include ../makefile.defs 37 | 38 | # Add inputs and outputs from these tool invocations to the build variables 39 | 40 | # All Target 41 | all: ospac-gui 42 | 43 | # Tool invocations 44 | ospac-gui: $(OBJS) $(USER_OBJS) 45 | @echo 'Building target: $@' 46 | @echo 'Invoking: Cross G++ Linker' 47 | g++ `fltk-config --ldstaticflags` -L/usr/local/lib -o "ospac-gui" $(OBJS) $(USER_OBJS) $(LIBS) 48 | @echo 'Finished building target: $@' 49 | @echo ' ' 50 | 51 | # Other Targets 52 | clean: 53 | -$(RM) $(CC_DEPS)$(C++_DEPS)$(EXECUTABLES)$(OBJS)$(C_UPPER_DEPS)$(CXX_DEPS)$(C_DEPS)$(CPP_DEPS) ospac-gui 54 | -@echo ' ' 55 | 56 | .PHONY: all clean dependents 57 | .SECONDARY: 58 | 59 | -include ../makefile.targets 60 | -------------------------------------------------------------------------------- /GUI-Release/objects.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | USER_OBJS := 6 | 7 | LIBS := -lsndfile 8 | 9 | -------------------------------------------------------------------------------- /GUI-Release/sources.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | C_UPPER_SRCS := 6 | CXX_SRCS := 7 | C++_SRCS := 8 | OBJ_SRCS := 9 | CC_SRCS := 10 | ASM_SRCS := 11 | C_SRCS := 12 | CPP_SRCS := 13 | O_SRCS := 14 | S_UPPER_SRCS := 15 | CC_DEPS := 16 | C++_DEPS := 17 | EXECUTABLES := 18 | OBJS := 19 | C_UPPER_DEPS := 20 | CXX_DEPS := 21 | C_DEPS := 22 | CPP_DEPS := 23 | 24 | # Every subdirectory with source files must be described here 25 | SUBDIRS := \ 26 | src \ 27 | 28 | -------------------------------------------------------------------------------- /GUI-Release/src/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | CPP_SRCS += \ 7 | ../src/Analyzer.cpp \ 8 | ../src/Channel.cpp \ 9 | ../src/CrosstalkFilter.cpp \ 10 | ../src/CrosstalkGate.cpp \ 11 | ../src/Encode.cpp \ 12 | ../src/Equalizer.cpp \ 13 | ../src/Frequency.cpp \ 14 | ../src/GuiMain.cpp \ 15 | ../src/Log.cpp \ 16 | ../src/Maximizer.cpp \ 17 | ../src/Merge.cpp \ 18 | ../src/MonoMix.cpp \ 19 | ../src/OspacMain.cpp \ 20 | ../src/Physics.cpp \ 21 | ../src/Plot.cpp \ 22 | ../src/SelectiveLeveler.cpp \ 23 | ../src/Skip.cpp \ 24 | ../src/StereoMix.cpp \ 25 | ../src/Wave.cpp 26 | 27 | OBJS += \ 28 | ./src/Analyzer.o \ 29 | ./src/Channel.o \ 30 | ./src/CrosstalkFilter.o \ 31 | ./src/CrosstalkGate.o \ 32 | ./src/Encode.o \ 33 | ./src/Equalizer.o \ 34 | ./src/Frequency.o \ 35 | ./src/GuiMain.o \ 36 | ./src/Log.o \ 37 | ./src/Maximizer.o \ 38 | ./src/Merge.o \ 39 | ./src/MonoMix.o \ 40 | ./src/OspacMain.o \ 41 | ./src/Physics.o \ 42 | ./src/Plot.o \ 43 | ./src/SelectiveLeveler.o \ 44 | ./src/Skip.o \ 45 | ./src/StereoMix.o \ 46 | ./src/Wave.o 47 | 48 | CPP_DEPS += \ 49 | ./src/Analyzer.d \ 50 | ./src/Channel.d \ 51 | ./src/CrosstalkFilter.d \ 52 | ./src/CrosstalkGate.d \ 53 | ./src/Encode.d \ 54 | ./src/Equalizer.d \ 55 | ./src/Frequency.d \ 56 | ./src/GuiMain.d \ 57 | ./src/Log.d \ 58 | ./src/Maximizer.d \ 59 | ./src/Merge.d \ 60 | ./src/MonoMix.d \ 61 | ./src/OspacMain.d \ 62 | ./src/Physics.d \ 63 | ./src/Plot.d \ 64 | ./src/SelectiveLeveler.d \ 65 | ./src/Skip.d \ 66 | ./src/StereoMix.d \ 67 | ./src/Wave.d 68 | 69 | 70 | # Each subdirectory must supply rules for building sources it contributes 71 | src/%.o: ../src/%.cpp 72 | @echo 'Building file: $<' 73 | @echo 'Invoking: Cross G++ Compiler' 74 | g++ -DVERSION=\"`cat ../version`\" -DGUI `fltk-config --cxxflags` -I/usr/local/include -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" 75 | @echo 'Finished building: $<' 76 | @echo ' ' 77 | 78 | 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sebastian Ritterbusch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Ligo.md: -------------------------------------------------------------------------------- 1 | ## Bandpass and LIGO 2 | 3 | Ospac has a very accurate band pass filter that can be used to reduce rumble 4 | or high frequency hissing. It is so accurate that it can be used to remove 5 | noise from the gravitational wave detection experiment 6 | [LIGO](https://en.wikipedia.org/wiki/LIGO). 7 | 8 | ## Fetch data and generate raw sound 9 | 10 | The [LIGO experiment detected a special event](https://losc.ligo.org/events/GW150914/) 11 | of which the raw data can be downloaded: 12 | 13 | wget https://losc.ligo.org/s/events/GW150914/H-H1_LOSC_4_V1-1126259446-32.txt.gz 14 | wget https://losc.ligo.org/s/events/GW150914/L-L1_LOSC_4_V1-1126259446-32.txt.gz 15 | gzip -d H-H1_LOSC_4_V1-1126259446-32.txt.gz 16 | gzip -d L-L1_LOSC_4_V1-1126259446-32.txt.gz 17 | 18 | This data represents the signals from two "laser microphones" located in 19 | Hanford and Livingston, USA, which are separated from each other by about 20 | 3000km. The microphone have shown to be able to detect planes flying over 21 | them as well as seismic waves. But apart from that, they should also be able 22 | to detect gravitational waves as they were predicted by Einstein. Using 23 | ospac you can listen to the recording with 4096Hz sample frequence (they 24 | also provide files with 16384Hz sample frequency): 25 | 26 | ospac --raw --ascii 4096 H-H1_LOSC_4_V1-1126259446-32.txt \ 27 | --ascii 4096 L-L1_LOSC_4_V1-1126259446-32.txt \ 28 | --output ligo-raw.wav 29 | ospac --raw ligo-raw.wav --plot ligo-raw.ppm 30 | 31 | ![Raw ligo data](https://github.com/sritterbusch/ospac/raw/master/images/ligo-raw.png) 32 | 33 | [Listen to the raw ligo data](https://github.com/sritterbusch/ospac/raw/master/examples/ligo-raw.mp3) 34 | 35 | ## Band pass filter 36 | 37 | This already sounds fascinating, but the actual signal is covered by noise. 38 | Using Audacity we ca have a look at the spectrum: 39 | 40 | ![Raw ligo data spectrum](https://github.com/sritterbusch/ospac/raw/master/images/ligo-analysis.png) 41 | 42 | Within the interval from 80Hz to 300Hz there seems to be a valley of less 43 | noise, as [this was hinted by Ligo](https://losc.ligo.org/s/events/GW150914/GW150914_tutorial.html)). 44 | Using the --bandfilter of ospac with a very thin transition interval of just 45 | 10Hz, we can have a closer look at this less noisy segment: 46 | 47 | ospac --raw --ascii 4096 H-H1_LOSC_4_V1-1126259446-32.txt \ 48 | --ascii 4096 L-L1_LOSC_4_V1-1126259446-32.txt \ 49 | --bandpass 80 300 10 --normalize \ 50 | --output ligo-filter.wav 51 | ospac --raw ligo-filter.wav --plot ligo-filter.ppm 52 | 53 | ![Raw data band filtered 80-300Hz](https://github.com/sritterbusch/ospac/raw/master/images/ligo-filter.png) 54 | 55 | [Listen to the filtered ligo data](https://github.com/sritterbusch/ospac/raw/master/examples/ligo-filter.mp3) 56 | 57 | ## Isolate the final result 58 | 59 | What's was at 16s? We can isolate this event using the skip filter: 60 | 61 | ospac --raw --ascii 4096 H-H1_LOSC_4_V1-1126259446-32.txt \ 62 | --ascii 4096 L-L1_LOSC_4_V1-1126259446-32.txt \ 63 | --bandpass 80 300 10 \ 64 | --skip --skip-level 0.5 --skip-order 0.95 \ 65 | --normalize \ 66 | --output ligo-event.wav 67 | ospac --raw ligo-event.wav --plot ligo-event.ppm 68 | 69 | ![Isolated event in the raw data band filtered 80-300Hz](https://github.com/sritterbusch/ospac/raw/master/images/ligo-event.png) 70 | 71 | [Listen to the isolated event in the filtered ligo data](https://github.com/sritterbusch/ospac/raw/master/examples/ligo-event.mp3) 72 | 73 | These are two massive black holes well beyond our sight that fell into each 74 | other generating gravitational waves that reached Earth. 75 | 76 | Apart from this, you can very well use the --bandpass filter to reduce low 77 | frequency rumbling or high frequency hissing. For this, you should most 78 | probably use wider transition intervals for faster processing. 79 | 80 | 81 | This text originally appeared on 82 | https://sendegate.de/t/ospac-neuer-bandpass-oder-wir-lauschen-den-gravitationswellen/2890 83 | in German. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: ospac 2 | 3 | ospac: src/*cpp src/*h 4 | cd Release;make all;cd .. 5 | 6 | ospac-ffmpeg: src/*cpp src/*h 7 | cd Release-ffmpeg;make all;cd .. 8 | 9 | gui: ospac-gui 10 | 11 | ospac-gui: src/*cpp src/*h 12 | cd GUI-Release;make all;cd .. 13 | 14 | ospac-gui-linux: src/*cpp src/*h 15 | cd GUI-Release;make ospac-gui-linux;cd .. 16 | 17 | GUI-Release/ospac-gui: src/*cpp src/*h 18 | cd GUI-Release;make all;cd .. 19 | 20 | GUI-Release/ospac-gui-linux: src/*cpp src/*h 21 | cd GUI-Release;make ospac-gui-linux;cd .. 22 | 23 | Release/ospac: src/*cpp src/*h 24 | cd Release;make all;cd .. 25 | 26 | tab-completion: create_tab_completion.py 27 | python3 create_tab_completion.py zsh zsh_completion.sh 28 | python3 create_tab_completion.py bash bash_completion.sh 29 | 30 | test: Release/ospac 31 | cd test;make all;cd .. 32 | 33 | install-ospac: Release/ospac 34 | cd Release;make install;cd .. 35 | 36 | install-ospac-ffmpeg: Release-ffmpeg/ospac 37 | cd Release-ffmpeg;make install;cd .. 38 | 39 | install-gui: GUI-Release/ospac-gui 40 | cd GUI-Release;make install-gui;cd .. 41 | 42 | install: install-ospac 43 | 44 | uninstall: 45 | cd Release;make uninstall; cd .. 46 | cd Release-ffmpeg;make uninstall; cd .. 47 | cd GUI-Release;make uninstall; cd .. 48 | 49 | uninstall-gui: 50 | cd Release;make uninstall; cd .. 51 | cd Release-ffmpeg;make uninstall; cd .. 52 | cd GUI-Release;make uninstall; cd .. 53 | 54 | clean: 55 | cd Release;make clean; cd .. 56 | cd Release-ffmpeg;make clean; cd .. 57 | cd GUI-Release;make clean; cd .. 58 | cd test;make clean;cd .. 59 | rm -f *~ 60 | rm -f src/*~ 61 | rm zsh_completion.sh bash_completion.sh 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | Ospac will take a multi-channel recording of an audio podcast conversation 4 | and master this to a high-quality mix-down with support for intro and outro. 5 | 6 | ## Features 7 | 8 | Beside other features, ospac includes a robust leveler, two solutions to 9 | avoid crosstalk and soft silence skipping. 10 | 11 | ### Leveler 12 | 13 | Ospac provides an energy based leveler. The following plot shows the 14 | original on top and the levelled outcome on the bottom: 15 | 16 | ![Example for the leveling algorithm: Original top, leveling bottom](https://github.com/sritterbusch/ospac/raw/master/images/leveler-result.png) 17 | 18 | ### Crosstalk Filter 19 | 20 | This waveform shows a crosstalk from the first channel in to the second 21 | channel, and some original content in the second half of the second channel: 22 | 23 | ![Example for crosstalk](https://github.com/sritterbusch/ospac/raw/master/images/crosstalk-example.png) 24 | 25 | The included crosstalk gate is a very robust algorithm that results in the 26 | following: 27 | 28 | ![Result of crosstalk gate](https://github.com/sritterbusch/ospac/raw/master/images/xgate-result.png) 29 | 30 | A new experimental crosstalk filter tries to improve the result: 31 | 32 | ![Result of crosstalk filter](https://github.com/sritterbusch/ospac/raw/master/images/xfilter-result.png) 33 | 34 | ### Soft silence skipping 35 | 36 | When enabled, ospac may reduce silence above 0.5s to shorter pauses: 37 | 38 | ![Result of soft silence skipping](https://github.com/sritterbusch/ospac/raw/master/images/skip-result.png) 39 | 40 | Alternatively, ospac can try to reach target lengths (90%, 80%, 70%, 60%, 41 | and 50%) compared to the original: 42 | 43 | ![Result of target silence skipping](https://github.com/sritterbusch/ospac/raw/master/images/skip-target.png) 44 | 45 | Of course, the result will only be really acceptible until all silence space is 46 | used up. 47 | 48 | ## Examples 49 | 50 | Mix 2 mono voice recordings with crosstalk filter, leveling and normalization: 51 | 52 | ospac person1.wav person2.wav --output target.wav 53 | 54 | Mix a podcast with stereo intro and outro: 55 | 56 | ospac --mix in.wav --overlap 4 \ 57 | --voice person1.wav person2.wav --overlap 4 \ 58 | --mix out.wav --output target.wav 59 | 60 | ## Motivation 61 | 62 | Ospac was developed due to the need of a batch solution for audio podcast 63 | creation. It is a rewrite and compilation of the scripts and methods used 64 | for the Modellansatz podcast, http://modellansatz.de/ . 65 | 66 | ## Installation 67 | 68 | Check that you have libsndfile and a c++-compiler on your system. 69 | Then, you may build and install ospac in this way: 70 | 71 | $ make ospac && sudo make install 72 | 73 | If you have ffmpeg and libav on your system, you may also build ospac with 74 | support for loading of various audio formats and writing aac files: 75 | 76 | $ make ospac-ffmpeg && sudo make install-ospac-ffmpeg 77 | 78 | On MacOS you can install libsndfile using homebrew simply by: 79 | 80 | $ brew install libsndfile 81 | 82 | If you want to build the GUI, install the development packages for 83 | fltk-1.3 and then build ospac-gui: 84 | 85 | $ make ospac-gui && sudo make install-gui 86 | 87 | Since there seem to be some broken fltk-1.3 source packages on linux, you 88 | can alternatively try the following to build a non-statically linked 89 | version: 90 | 91 | $ make ospac-gui-linux && sudo make install-gui 92 | 93 | To simplify usage tab-completion files for bash and zsh can be generated: 94 | 95 | $ make tab-completion 96 | 97 | The tab-completion is then usable by appending the following to your 98 | `.[bash|zsh]rc` file: 99 | 100 | source PATH_TO_OSPAC/[zsh|bash]_completion.sh 101 | 102 | 103 | ## API Reference 104 | 105 | See the doxygen docs 106 | https://github.com/sritterbusch/ospac/raw/master/ospac-internal.pdf 107 | for a detailed API reference. 108 | 109 | ## Tests 110 | 111 | You may run tests that create exemplary mixdowns and waveform images 112 | that are used in the API documentation. 113 | 114 | $ make test 115 | 116 | ## License 117 | 118 | The MIT License (MIT) 119 | 120 | Copyright (c) 2016-2017 Sebastian Ritterbusch 121 | 122 | Permission is hereby granted, free of charge, to any person obtaining a copy 123 | of this software and associated documentation files (the "Software"), to deal 124 | in the Software without restriction, including without limitation the rights 125 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 126 | copies of the Software, and to permit persons to whom the Software is 127 | furnished to do so, subject to the following conditions: 128 | 129 | The above copyright notice and this permission notice shall be included in all 130 | copies or substantial portions of the Software. 131 | 132 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 133 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 134 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 135 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 136 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 137 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 138 | SOFTWARE. 139 | -------------------------------------------------------------------------------- /Release-ffmpeg/makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | -include ../makefile.init 6 | 7 | RM := rm -rf 8 | 9 | # All of the sources participating in the build are defined here 10 | -include sources.mk 11 | -include src/subdir.mk 12 | -include subdir.mk 13 | -include objects.mk 14 | 15 | ifneq ($(MAKECMDGOALS),clean) 16 | ifneq ($(strip $(CC_DEPS)),) 17 | -include $(CC_DEPS) 18 | endif 19 | ifneq ($(strip $(C++_DEPS)),) 20 | -include $(C++_DEPS) 21 | endif 22 | ifneq ($(strip $(C_UPPER_DEPS)),) 23 | -include $(C_UPPER_DEPS) 24 | endif 25 | ifneq ($(strip $(CXX_DEPS)),) 26 | -include $(CXX_DEPS) 27 | endif 28 | ifneq ($(strip $(CPP_DEPS)),) 29 | -include $(CPP_DEPS) 30 | endif 31 | ifneq ($(strip $(C_DEPS)),) 32 | -include $(C_DEPS) 33 | endif 34 | endif 35 | 36 | -include ../makefile.defs 37 | 38 | # Add inputs and outputs from these tool invocations to the build variables 39 | 40 | # All Target 41 | all: ospac 42 | 43 | # Tool invocations 44 | ospac: $(OBJS) $(USER_OBJS) 45 | @echo 'Building target: $@' 46 | @echo 'Invoking: C++ Linker' 47 | g++ -L/usr/local/lib -o "ospac" $(OBJS) $(USER_OBJS) $(LIBS) 48 | @echo 'Finished building target: $@' 49 | @echo ' ' 50 | 51 | # Other Targets 52 | clean: 53 | -$(RM) $(CC_DEPS)$(C++_DEPS)$(EXECUTABLES)$(OBJS)$(C_UPPER_DEPS)$(CXX_DEPS)$(CPP_DEPS)$(C_DEPS) ospac 54 | -@echo ' ' 55 | 56 | .PHONY: all clean dependents 57 | .SECONDARY: 58 | 59 | -include ../makefile.targets 60 | -------------------------------------------------------------------------------- /Release-ffmpeg/objects.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | USER_OBJS := 6 | 7 | LIBS := -lsndfile -lavformat -lavcodec -lavutil 8 | 9 | -------------------------------------------------------------------------------- /Release-ffmpeg/sources.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | C_UPPER_SRCS := 6 | CXX_SRCS := 7 | OBJ_SRCS := 8 | C++_SRCS := 9 | CC_SRCS := 10 | ASM_SRCS := 11 | CPP_SRCS := 12 | C_SRCS := 13 | O_SRCS := 14 | S_UPPER_SRCS := 15 | CC_DEPS := 16 | C++_DEPS := 17 | EXECUTABLES := 18 | OBJS := 19 | C_UPPER_DEPS := 20 | CXX_DEPS := 21 | CPP_DEPS := 22 | C_DEPS := 23 | 24 | # Every subdirectory with source files must be described here 25 | SUBDIRS := \ 26 | src 27 | 28 | -------------------------------------------------------------------------------- /Release-ffmpeg/src/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | CPP_SRCS += \ 7 | ../src/Analyzer.cpp \ 8 | ../src/Channel.cpp \ 9 | ../src/CrosstalkFilter.cpp \ 10 | ../src/CrosstalkGate.cpp \ 11 | ../src/Encode.cpp \ 12 | ../src/Equalizer.cpp \ 13 | ../src/Frequency.cpp \ 14 | ../src/GuiMain.cpp \ 15 | ../src/Log.cpp \ 16 | ../src/Maximizer.cpp \ 17 | ../src/Merge.cpp \ 18 | ../src/MonoMix.cpp \ 19 | ../src/OspacMain.cpp \ 20 | ../src/Physics.cpp \ 21 | ../src/Plot.cpp \ 22 | ../src/SelectiveLeveler.cpp \ 23 | ../src/Skip.cpp \ 24 | ../src/StereoMix.cpp \ 25 | ../src/Wave.cpp 26 | 27 | OBJS += \ 28 | ./src/Analyzer.o \ 29 | ./src/Channel.o \ 30 | ./src/CrosstalkFilter.o \ 31 | ./src/CrosstalkGate.o \ 32 | ./src/Encode.o \ 33 | ./src/Equalizer.o \ 34 | ./src/Frequency.o \ 35 | ./src/GuiMain.o \ 36 | ./src/Log.o \ 37 | ./src/Maximizer.o \ 38 | ./src/Merge.o \ 39 | ./src/MonoMix.o \ 40 | ./src/OspacMain.o \ 41 | ./src/Physics.o \ 42 | ./src/Plot.o \ 43 | ./src/SelectiveLeveler.o \ 44 | ./src/Skip.o \ 45 | ./src/StereoMix.o \ 46 | ./src/Wave.o 47 | 48 | CPP_DEPS += \ 49 | ./src/Analyzer.d \ 50 | ./src/Channel.d \ 51 | ./src/CrosstalkFilter.d \ 52 | ./src/CrosstalkGate.d \ 53 | ./src/Encode.d \ 54 | ./src/Equalizer.d \ 55 | ./src/Frequency.d \ 56 | ./src/GuiMain.d \ 57 | ./src/Log.d \ 58 | ./src/Maximizer.d \ 59 | ./src/Merge.d \ 60 | ./src/MonoMix.d \ 61 | ./src/OspacMain.d \ 62 | ./src/Physics.d \ 63 | ./src/Plot.d \ 64 | ./src/SelectiveLeveler.d \ 65 | ./src/Skip.d \ 66 | ./src/StereoMix.d \ 67 | ./src/Wave.d 68 | 69 | 70 | # Each subdirectory must supply rules for building sources it contributes 71 | src/%.o: ../src/%.cpp 72 | @echo 'Building file: $<' 73 | @echo 'Invoking: GCC C++ Compiler' 74 | g++ -DHAS_FFMPEG -DCLI -DVERSION=\"`cat ../version`\" -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" 75 | @echo 'Finished building: $<' 76 | @echo ' ' 77 | 78 | 79 | -------------------------------------------------------------------------------- /Release/makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | -include ../makefile.init 6 | 7 | RM := rm -rf 8 | 9 | # All of the sources participating in the build are defined here 10 | -include sources.mk 11 | -include src/subdir.mk 12 | -include subdir.mk 13 | -include objects.mk 14 | 15 | ifneq ($(MAKECMDGOALS),clean) 16 | ifneq ($(strip $(CC_DEPS)),) 17 | -include $(CC_DEPS) 18 | endif 19 | ifneq ($(strip $(C++_DEPS)),) 20 | -include $(C++_DEPS) 21 | endif 22 | ifneq ($(strip $(C_UPPER_DEPS)),) 23 | -include $(C_UPPER_DEPS) 24 | endif 25 | ifneq ($(strip $(CXX_DEPS)),) 26 | -include $(CXX_DEPS) 27 | endif 28 | ifneq ($(strip $(C_DEPS)),) 29 | -include $(C_DEPS) 30 | endif 31 | ifneq ($(strip $(CPP_DEPS)),) 32 | -include $(CPP_DEPS) 33 | endif 34 | endif 35 | 36 | -include ../makefile.defs 37 | 38 | # Add inputs and outputs from these tool invocations to the build variables 39 | 40 | # All Target 41 | all: ospac 42 | 43 | # Tool invocations 44 | ospac: $(OBJS) $(USER_OBJS) 45 | @echo 'Building target: $@' 46 | @echo 'Invoking: Cross G++ Linker' 47 | g++ -L/usr/local/lib -o "ospac" $(OBJS) $(USER_OBJS) $(LIBS) 48 | @echo 'Finished building target: $@' 49 | @echo ' ' 50 | 51 | # Other Targets 52 | clean: 53 | -$(RM) $(CC_DEPS)$(C++_DEPS)$(EXECUTABLES)$(OBJS)$(C_UPPER_DEPS)$(CXX_DEPS)$(C_DEPS)$(CPP_DEPS) ospac 54 | -@echo ' ' 55 | 56 | .PHONY: all clean dependents 57 | .SECONDARY: 58 | 59 | -include ../makefile.targets 60 | -------------------------------------------------------------------------------- /Release/objects.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | USER_OBJS := 6 | 7 | LIBS := -lsndfile 8 | 9 | -------------------------------------------------------------------------------- /Release/sources.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | C_UPPER_SRCS := 6 | CXX_SRCS := 7 | C++_SRCS := 8 | OBJ_SRCS := 9 | CC_SRCS := 10 | ASM_SRCS := 11 | C_SRCS := 12 | CPP_SRCS := 13 | O_SRCS := 14 | S_UPPER_SRCS := 15 | CC_DEPS := 16 | C++_DEPS := 17 | EXECUTABLES := 18 | OBJS := 19 | C_UPPER_DEPS := 20 | CXX_DEPS := 21 | C_DEPS := 22 | CPP_DEPS := 23 | 24 | # Every subdirectory with source files must be described here 25 | SUBDIRS := \ 26 | src \ 27 | 28 | -------------------------------------------------------------------------------- /Release/src/subdir.mk: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Automatically-generated file. Do not edit! 3 | ################################################################################ 4 | 5 | # Add inputs and outputs from these tool invocations to the build variables 6 | CPP_SRCS += \ 7 | ../src/Analyzer.cpp \ 8 | ../src/Channel.cpp \ 9 | ../src/CrosstalkFilter.cpp \ 10 | ../src/CrosstalkGate.cpp \ 11 | ../src/Encode.cpp \ 12 | ../src/Equalizer.cpp \ 13 | ../src/Frequency.cpp \ 14 | ../src/GuiMain.cpp \ 15 | ../src/Log.cpp \ 16 | ../src/Maximizer.cpp \ 17 | ../src/Merge.cpp \ 18 | ../src/MonoMix.cpp \ 19 | ../src/OspacMain.cpp \ 20 | ../src/Physics.cpp \ 21 | ../src/Plot.cpp \ 22 | ../src/SelectiveLeveler.cpp \ 23 | ../src/Skip.cpp \ 24 | ../src/StereoMix.cpp \ 25 | ../src/Wave.cpp 26 | 27 | OBJS += \ 28 | ./src/Analyzer.o \ 29 | ./src/Channel.o \ 30 | ./src/CrosstalkFilter.o \ 31 | ./src/CrosstalkGate.o \ 32 | ./src/Encode.o \ 33 | ./src/Equalizer.o \ 34 | ./src/Frequency.o \ 35 | ./src/GuiMain.o \ 36 | ./src/Log.o \ 37 | ./src/Maximizer.o \ 38 | ./src/Merge.o \ 39 | ./src/MonoMix.o \ 40 | ./src/OspacMain.o \ 41 | ./src/Physics.o \ 42 | ./src/Plot.o \ 43 | ./src/SelectiveLeveler.o \ 44 | ./src/Skip.o \ 45 | ./src/StereoMix.o \ 46 | ./src/Wave.o 47 | 48 | CPP_DEPS += \ 49 | ./src/Analyzer.d \ 50 | ./src/Channel.d \ 51 | ./src/CrosstalkFilter.d \ 52 | ./src/CrosstalkGate.d \ 53 | ./src/Encode.d \ 54 | ./src/Equalizer.d \ 55 | ./src/Frequency.d \ 56 | ./src/GuiMain.d \ 57 | ./src/Log.d \ 58 | ./src/Maximizer.d \ 59 | ./src/Merge.d \ 60 | ./src/MonoMix.d \ 61 | ./src/OspacMain.d \ 62 | ./src/Physics.d \ 63 | ./src/Plot.d \ 64 | ./src/SelectiveLeveler.d \ 65 | ./src/Skip.d \ 66 | ./src/StereoMix.d \ 67 | ./src/Wave.d 68 | 69 | 70 | # Each subdirectory must supply rules for building sources it contributes 71 | src/%.o: ../src/%.cpp 72 | @echo 'Building file: $<' 73 | @echo 'Invoking: Cross G++ Compiler' 74 | g++ -DVERSION=\"`cat ../version`\" -DCLI -I/usr/local/include -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" 75 | @echo 'Finished building: $<' 76 | @echo ' ' 77 | 78 | 79 | -------------------------------------------------------------------------------- /Stereo.md: -------------------------------------------------------------------------------- 1 | ## Ospac Rendering Modes: Mono, Stereo, Spatial, Multi 2 | 3 | It can be highly beneficial to a podcast to render conversation in stereo, 4 | to help the listener to differentiate speakers even with similar voices. 5 | But there are quite some issues that need to be taken into consideration: 6 | For example, listening experience, one-channel and mono reproduction, 7 | compression and audio quality. There is a German version of this text on 8 | https://sendegate.de/t/ospac-mono-stereo-raeumlich-multichannel/2933 . 9 | 10 | ## Mono mode 11 | 12 | In mono mode, ospac renders all signals into a one channel signal. 13 | 14 | ospac --mono {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 15 | --output mono-test.wav 16 | ospac --raw mono-test.wav --plot mono-test.ppm 17 | 18 | ![Mono rendering](https://github.com/sritterbusch/ospac/raw/master/images/mono-test.png) 19 | 20 | [Listen to the mono rendering](https://github.com/sritterbusch/ospac/raw/master/examples/mono-test.mp3) 21 | 22 | Please be aware, that ospac does the mono mix-down without scaling, thus you 23 | must use --factor or --normalize when including stereo signals using --mix 24 | or --raw. 25 | 26 | ## Stereo mode 27 | 28 | The standard stereo mode renders the audio signals with different intensity 29 | settings on the two stereo channels. In the default setting (0.9 or 1.1), the 30 | left-most channel has a intensity boost of 10% on the left and 10% damping 31 | on the right channel. The right-most channel is treated accordingly and all 32 | other channels are evenly distributed in between. 33 | 34 | ospac {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 35 | --output stereo-test.wav 36 | ospac --raw stereo-test.wav --plot stereo-test.ppm 37 | 38 | ![Standard stereo rendering](https://github.com/sritterbusch/ospac/raw/master/images/stereo-test.png) 39 | 40 | [Listen to the standard stereo rendering](https://github.com/sritterbusch/ospac/raw/master/examples/stereo-test.mp3) 41 | 42 | You can increase the stereo effect by changing the --set-stereo-level setting: 43 | 44 | ospac --set-stereo-level 0.25 \ 45 | {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 46 | --output stereo-strong.wav 47 | ospac --raw stereo-strong.wav --plot stereo-strong.ppm 48 | 49 | ![Strong stereo rendering](https://github.com/sritterbusch/ospac/raw/master/images/stereo-strong.png) 50 | 51 | [Listen to the strong stereo rendering](https://github.com/sritterbusch/ospac/raw/master/examples/stereo-strong.mp3) 52 | 53 | You may also disable the stereo effect by setting the stereo level to 1 and 54 | generate a two-channel mono signal: 55 | 56 | ospac --set-stereo-level 1 \ 57 | {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 58 | --output stereo-mono-test.wav 59 | ospac --raw stereo-mono-test.wav --plot stereo-mono-test.ppm 60 | 61 | ![Mono stereo rendering](https://github.com/sritterbusch/ospac/raw/master/images/stereo-mono-test.png) 62 | 63 | [Listen to the mono stereo rendering](https://github.com/sritterbusch/ospac/raw/master/examples/stereo-mono-test.mp3) 64 | 65 | This mode can be used to have a full stereo intro while the actual 66 | conversation should be in mono. 67 | 68 | ## Spatial mode 69 | 70 | Inter-aural differences in intensity are only one hint for spatial 71 | localization of speakers. Another hint are inter-aural time delays that 72 | occur since sound travels with about 330m/s in air and the two ears are 73 | separated by about 0.22m. The standard setting for the spatial mode are 74 | 0.09m sound distance in addition the the standard stereo intensity setting 75 | of 0.9: 76 | 77 | ospac --spatial {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 78 | --output spatial-test.wav 79 | ospac --raw spatial-test.wav --plot spatial-test.ppm 80 | 81 | ![Standard spatial rendering](https://github.com/sritterbusch/ospac/raw/master/images/spatial-test.png) 82 | 83 | [Listen to the standard spatial rendering](https://github.com/sritterbusch/ospac/raw/master/examples/spatial-test.mp3) 84 | 85 | You can also increase the interaural time delay to the maximum delay 86 | representing 0.2m for sounds coming directly from the sides: 87 | 88 | ospac --spatial --set-stereo-spatial 0.2 \ 89 | {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 90 | --output spatial-strong.wav 91 | ospac --raw spatial-strong.wav --plot spatial-strong.ppm 92 | 93 | ![Standard spatial rendering](https://github.com/sritterbusch/ospac/raw/master/images/spatial-strong.png) 94 | 95 | [Listen to the standard spatial rendering](https://github.com/sritterbusch/ospac/raw/master/examples/spatial-strong.mp3) 96 | 97 | ## Multi mode 98 | 99 | Ospac also supports a multi-channel mode where no rendering takes place and 100 | results in as many channels as they were provided. This mode is useful for 101 | just using the crosstalk gate or filter and then rely on a another audio 102 | tool for the actual stereo or mono rendering. 103 | 104 | ospac --multi \ 105 | {1,2,3,4,5}.wav --voice {5,4,3,2,1}.wav \ 106 | --output multi-test.wav 107 | ospac --raw multi-test.wav --plot multi-test.ppm 108 | 109 | ![Standard spatial rendering](https://github.com/sritterbusch/ospac/raw/master/images/multi-test.png) 110 | 111 | ## Discussion 112 | 113 | While the stereo rendering can be very beneficial for the listener, there 114 | are also some down sides that need to be taken into consideration: First of 115 | all, strong stereo settings can be very annoying. If someone talks to you 116 | from the right, you would simply turn towards this person- the listener 117 | cannot do this, and is right to be annoyed. Secondly, you cannot be sure 118 | that there are always two speakers available. Many handhelds only have one 119 | speaker and either play a mono mono mixdown or just one channel. Also, there 120 | might be listeners that can only listen with one ear. Thirdly, the use of 121 | stereo rendering decreases the compressibility of the signal, which will 122 | lead to a decrease of quality with constant compression rate. This gets even 123 | worse if a mono-mixdown occurs of a compressed audio file. 124 | 125 | Especially the --spatial mode has problems with mono-mixdowns as 126 | you can experience in this 127 | [mono mixdown of the strong spatial rendering](https://github.com/sritterbusch/ospac/raw/master/examples/spatial-strong-mono.mp3). 128 | This is the same effect you experience when listening to the radio which 129 | switches from mono reception to stereo reception. Suddenly you hear more 130 | high frequencies. They were there before, but were canceled in the mono 131 | mixdown. 132 | 133 | The following table shows the discussion of the modes: 134 | 135 | Rendering | Mono | Stereo mono | Stereo | Strong stereo | Spatial | Strong spatial 136 | -------------- | ---- | ----------- | ------ | ------------- | ------- | -------------- 137 | Stereo effect | - | - | + | ++ | ++ | +++ 138 | Not annoying | + | + | + | -- | + | -- 139 | One channel | ++ | ++ | + | -- | + | + 140 | Mono rendering | ++ | ++ | ++ | ++ | - | -- 141 | Compression | 43 | 47 | 54 | 60 | 59 | 63 142 | 143 | The compression rates are in kBit/s with fixed quality setting in lame for above 144 | examples. 145 | -------------------------------------------------------------------------------- /bash_completion.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Completion script for OSPAC (https://github.com/sritterbusch/ospac) 4 | 5 | _ospac(){ 6 | local opts 7 | opts=( 8 | --mix 9 | --raw 10 | --voice 11 | --overlap 12 | --fade 13 | --parallel 14 | --year 15 | --comment 16 | --image 17 | --album 18 | --title 19 | --episode 20 | --category 21 | --artist 22 | --skip-order 23 | --skip-target 24 | --skip-level 25 | --no-skip 26 | --soft 27 | --noise 28 | --trim 29 | --help 30 | --verbosity 31 | --normalize 32 | --no-eqvoice 33 | --eqvoice 34 | --level-mode 35 | --leveler 36 | --no-factor 37 | --analyze 38 | --low-pass 39 | --band-pass 40 | --no-leveler 41 | --highpass 42 | --no-normalize 43 | --factor 44 | --target 45 | --set-stereo-level 46 | --spatial 47 | --stereo 48 | --multi 49 | --mono 50 | --set-stereo-spatial 51 | --xfilter 52 | --no-xfilter 53 | --xgate 54 | --no-xgate 55 | --ascii 56 | --left 57 | --to-mono 58 | --right 59 | --output 60 | --mp3 61 | --quality 62 | --plot 63 | --ogg 64 | ) 65 | local cur=${COMP_WORDS[COMP_CWORD]} 66 | COMPREPLY=( $(compgen -W "${opts[*]}" -- $cur) ) 67 | } 68 | 69 | complete -F _ospac ospac 70 | 71 | -------------------------------------------------------------------------------- /create_tab_completion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # By Johannes Bechberger, under MIT License 4 | 5 | import sys 6 | import os 7 | 8 | description = "description" # hacks that allow better editing in IDEs 9 | zsh = "zsh" 10 | flag = "flag" 11 | multiple = "multiple" 12 | section = "section" 13 | 14 | default_completion_item = { 15 | multiple: True, # allow multiple applications 16 | description: "", # description of this parameter 17 | flag: True, # is this argument a flag (without any arguments)? 18 | zsh: "" # completion string for zsh or list of possible arguments 19 | } 20 | 21 | completion = { 22 | "Information": { 23 | "--help": { 24 | description: "Display the help text", 25 | }, 26 | "--verbosity": { 27 | description: "Set the verbosity level", 28 | flag: False, 29 | zsh: list(range(7)), 30 | } 31 | }, 32 | "Output modes": { 33 | "--spatial": { 34 | description: "Create 3d stereo with interaural delays" 35 | }, 36 | "--stereo": { 37 | description: "Create intensity stereo (default)" 38 | }, 39 | "--multi": { 40 | description: "Create multi channel output" 41 | }, 42 | "--mono": { 43 | description: "Create mono output" 44 | }, 45 | "--set-stereo-level": { 46 | description: "Set maximum channel volume factor (0.9)", 47 | flag: False 48 | }, 49 | "--set-stereo-spatial": { 50 | description: "Set maximum interaural delay distance (0.03)", 51 | flag: False 52 | } 53 | }, 54 | "Output targets": { 55 | "--output": { 56 | description: "Write final output to the file in netbpm format", 57 | zsh: "_files" 58 | }, 59 | "--plot": { 60 | description: "Write final output to the file in wave format", 61 | zsh: "_files" 62 | }, 63 | "--mp3": { 64 | description: "Write final output to the file using external lame", 65 | zsh: "_files" 66 | }, 67 | "--ogg": { 68 | description: "Write final output to the file using external oggenc", 69 | zsh: "_files" 70 | }, 71 | "--quality": { 72 | description: "Quality from 0-low, 1-standard, 2-high, 3-insane", 73 | zsh: [0, 1, 2, 3] 74 | } 75 | }, 76 | "Output meta data": { 77 | "--title": { 78 | description: "Set the title tag if this exists in the output", 79 | flag: False 80 | }, 81 | "--artist": { 82 | description: "Set the artist tag if this exists in the output", 83 | flag: False 84 | }, 85 | "--album": { 86 | description: "Set the album tag if this exists in the output", 87 | flag: False 88 | }, 89 | "--comment": { 90 | description: "Set the comment tag if this exists in the output", 91 | flag: False 92 | }, 93 | "--category": { 94 | description: "Set the category tag if this exists in the output", 95 | flag: False 96 | }, 97 | "--episode": { 98 | description: "Set the episode tag if this exists in the output", 99 | flag: False 100 | }, 101 | "--year": { 102 | description: "Set the year tag if this exists in the output", 103 | flag: False 104 | }, 105 | "--image": { 106 | description: "Set the image tag if this exists in the output", 107 | flag: False, 108 | zsh: "_files" 109 | } 110 | }, 111 | "New segment selection": { 112 | "--voice": { 113 | description: "Start of voice channel segment (default)" 114 | }, 115 | "--mix": { 116 | description: "Start pre-mixed channel segment" 117 | }, 118 | "--raw": { 119 | description: "Start of raw channel segment" 120 | } 121 | }, 122 | "Segment transition": { 123 | "--fade": { 124 | description: "Fading transition over the given number of seconds", 125 | flag: False 126 | }, 127 | "--overlap": { 128 | description: "Overlapping transition over the given number of seconds", 129 | flag: False 130 | }, 131 | "--parallel": { 132 | description: "Render previous and next segment in parallel" 133 | } 134 | }, 135 | "Crosstalk filter": { 136 | "--xgate": { 137 | description: "Enable robust crosstalk gate" 138 | }, 139 | "--no-xgate": { 140 | description: "Disable crosstalk gate" 141 | }, 142 | "--xfilter": { 143 | description: "Enable experimental crosstalk filter" 144 | }, 145 | "--no-xfilter": { 146 | description: "Disable crosstalk filter" 147 | } 148 | }, 149 | "Adaptive silence skip": { 150 | "--soft": { 151 | description: "Soft skip silent passages over 0.5s length" 152 | }, 153 | "--skip-target": { 154 | description: "Target length fraction for iteration", 155 | flag: False 156 | }, 157 | "--skip-level": { 158 | description: "Fraction of maximum level considered silence (0.01)", 159 | flag: False 160 | }, 161 | "--skip-order": { 162 | description: "Order of reduction (0-1, default: 0.75)", 163 | flag: False 164 | }, 165 | "--no-skip": { 166 | description: "Do not skip any content" 167 | }, 168 | "--trim": { 169 | description: "Trim audio from start and end" 170 | }, 171 | "--noise": { 172 | description: "Skip all but silence" 173 | } 174 | }, 175 | "Leveling, equalizer and normalization": { 176 | "--leveler": { 177 | description: "Enable selective leveler" 178 | }, 179 | "--target": { 180 | description: "Set average target L2 energy for leveler (3000)", 181 | flag: False 182 | }, 183 | "--level-mode": { 184 | description: "Shall channels be joined for leveling", 185 | zsh: ["single", "stereo", "multi"] 186 | }, 187 | "--no-leveler": { 188 | description: "Disable selective leveler" 189 | }, 190 | "--factor": { 191 | description: "Multiply channels by the given factor with sigmoid limiter (1.25)", 192 | flag: False 193 | }, 194 | "--no-factor": { 195 | description: "Disable channel multiplier" 196 | }, 197 | "--eqvoice": { 198 | description: "Attenuate voice frequency bands" 199 | }, 200 | "--no-eqvoice": { 201 | description: "Do not attenuate frequency bands" 202 | }, 203 | "--analyze": { 204 | description: "Analyze frequency band components" 205 | }, 206 | "--normalize": { 207 | description: "Normalize final mix" 208 | }, 209 | "--no-normalize": { 210 | description: "Disable final normalization" 211 | }, 212 | "--band-pass": { 213 | description: "[l] [h] [t] Bandpass from l to h Hertz, sharpness t Hertz", 214 | flag: False 215 | }, 216 | "--low-pass": { 217 | description: "[f] [t] Lowpass below f Hertz, sharpness t Hertz", 218 | flag: False 219 | }, 220 | "--highpass": { 221 | description: "[f] [t] Highpass above f Hertz, sharpness t Hertz", 222 | flag: False 223 | } 224 | }, 225 | "Import audio": { 226 | "--left": { 227 | description: "Load left channel of wave file (if stereo)", 228 | zsh: "_files" 229 | }, 230 | "--right": { 231 | description: "Load right channel of wave file (if stereo)", 232 | zsh: "_files" 233 | }, 234 | "--to-mono": { 235 | description: "Load mono-mixdown of wave file (if stereo)", 236 | zsh: "_files" 237 | }, 238 | "--ascii": { 239 | description: "[s] [file] Load ascii wave file with sample rate s", 240 | flag: False 241 | } 242 | } 243 | } 244 | 245 | def zsh_completion() -> str: 246 | """ 247 | Generate the tab completion for zsh 248 | """ 249 | def process_item(section: str, name: str, item: dict) -> str: 250 | tmp = default_completion_item.copy() 251 | tmp.update(item) 252 | item = tmp 253 | if item[multiple]: 254 | name = "*" + name 255 | item[description] = item[description].replace("[", "<").replace("]", ">") 256 | ret = name + "[" + item[description] + "]" 257 | if not item[flag] or item[zsh]: 258 | ret += ": :" 259 | if item[zsh]: 260 | if isinstance(item[zsh], list): 261 | ret += "(" + " ".join(repr(x) for x in item[zsh]) + ")" 262 | elif isinstance(item[zsh], str): 263 | ret += item[zsh] 264 | else: 265 | print("Error on zsh completion in " + repr(item), file=sys.stderr) 266 | os.error(1) 267 | return repr(ret) 268 | return """ 269 | 270 | # Completion script for OSPAC (https://github.com/sritterbusch/ospac) 271 | 272 | _ospac(){{ 273 | local -a opts 274 | opts=( 275 | "*: :_files" 276 | {} 277 | ) 278 | _arguments $opts 279 | }} 280 | 281 | compdef _ospac ospac=ospac 282 | 283 | """.format("\n ".join(process_item(s, n, completion[s][n]) for s in completion for n in completion[s])) 284 | 285 | def bash_completion() -> str: 286 | """ 287 | Generate the tab completion for bash 288 | """ 289 | def process_item(section: str, name: str, item: dict) -> str: 290 | return name 291 | return """ 292 | 293 | # Completion script for OSPAC (https://github.com/sritterbusch/ospac) 294 | 295 | _ospac(){{ 296 | local opts 297 | opts=( 298 | {} 299 | ) 300 | local cur=${{COMP_WORDS[COMP_CWORD]}} 301 | COMPREPLY=( $(compgen -W "${{opts[*]}}" -- $cur) ) 302 | }} 303 | 304 | complete -F _ospac ospac 305 | 306 | """.format("\n ".join(process_item(s, n, completion[s][n]) for s in completion for n in completion[s])) 307 | 308 | 309 | if __name__ == "__main__": 310 | USAGE = """ 311 | Shell completion generator for ospac. 312 | It generates a tab completion file for bash or zsh. 313 | 314 | Usage: 315 | 316 | {} [zsh|bash] OUTPUT_FILE 317 | """.format(sys.argv[0]) 318 | 319 | if len(sys.argv) != 3 or sys.argv[1] not in ["zsh", "bash"]: 320 | print(USAGE) 321 | exit() 322 | compl = "" 323 | if sys.argv[1] == "zsh": 324 | compl = zsh_completion() 325 | elif sys.argv[1] == "bash": 326 | compl = bash_completion() 327 | with open(sys.argv[2], "w") as f: 328 | f.write(compl) 329 | -------------------------------------------------------------------------------- /examples/ligo-event.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/ligo-event.mp3 -------------------------------------------------------------------------------- /examples/ligo-filter.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/ligo-filter.mp3 -------------------------------------------------------------------------------- /examples/ligo-raw.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/ligo-raw.mp3 -------------------------------------------------------------------------------- /examples/mono-test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/mono-test.mp3 -------------------------------------------------------------------------------- /examples/spatial-strong-mono.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/spatial-strong-mono.mp3 -------------------------------------------------------------------------------- /examples/spatial-strong.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/spatial-strong.mp3 -------------------------------------------------------------------------------- /examples/spatial-test-mono.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/spatial-test-mono.mp3 -------------------------------------------------------------------------------- /examples/spatial-test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/spatial-test.mp3 -------------------------------------------------------------------------------- /examples/stereo-mono-test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/stereo-mono-test.mp3 -------------------------------------------------------------------------------- /examples/stereo-strong-mono.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/stereo-strong-mono.mp3 -------------------------------------------------------------------------------- /examples/stereo-strong.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/stereo-strong.mp3 -------------------------------------------------------------------------------- /examples/stereo-test-mono.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/stereo-test-mono.mp3 -------------------------------------------------------------------------------- /examples/stereo-test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/examples/stereo-test.mp3 -------------------------------------------------------------------------------- /footer.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/footer.html -------------------------------------------------------------------------------- /images/crosstalk-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/crosstalk-example.png -------------------------------------------------------------------------------- /images/factor-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/factor-result.png -------------------------------------------------------------------------------- /images/leveler-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/leveler-result.png -------------------------------------------------------------------------------- /images/ligo-analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/ligo-analysis.png -------------------------------------------------------------------------------- /images/ligo-event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/ligo-event.png -------------------------------------------------------------------------------- /images/ligo-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/ligo-filter.png -------------------------------------------------------------------------------- /images/ligo-raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/ligo-raw.png -------------------------------------------------------------------------------- /images/mono-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/mono-test.png -------------------------------------------------------------------------------- /images/multi-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/multi-test.png -------------------------------------------------------------------------------- /images/noise-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/noise-result.png -------------------------------------------------------------------------------- /images/normalize-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/normalize-result.png -------------------------------------------------------------------------------- /images/normalize-space-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/normalize-space-result.png -------------------------------------------------------------------------------- /images/skip-result-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-result-05.png -------------------------------------------------------------------------------- /images/skip-result-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-result-06.png -------------------------------------------------------------------------------- /images/skip-result-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-result-07.png -------------------------------------------------------------------------------- /images/skip-result-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-result-08.png -------------------------------------------------------------------------------- /images/skip-result-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-result-09.png -------------------------------------------------------------------------------- /images/skip-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-result.png -------------------------------------------------------------------------------- /images/skip-target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/skip-target.png -------------------------------------------------------------------------------- /images/spatial-strong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/spatial-strong.png -------------------------------------------------------------------------------- /images/spatial-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/spatial-test.png -------------------------------------------------------------------------------- /images/stereo-mono-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/stereo-mono-test.png -------------------------------------------------------------------------------- /images/stereo-strong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/stereo-strong.png -------------------------------------------------------------------------------- /images/stereo-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/stereo-test.png -------------------------------------------------------------------------------- /images/xfilter-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/xfilter-result.png -------------------------------------------------------------------------------- /images/xgate-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/images/xgate-result.png -------------------------------------------------------------------------------- /logo/Makefile: -------------------------------------------------------------------------------- 1 | icon.icns: icon.iconset/*png 2 | iconutil -c icns icon.iconset 3 | -------------------------------------------------------------------------------- /logo/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.icns -------------------------------------------------------------------------------- /logo/icon.iconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_128x128.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_16x16.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_256x256.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_32x32.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_512x512.png -------------------------------------------------------------------------------- /logo/icon.iconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/icon.iconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /logo/ospac-logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/logo/ospac-logo.xcf -------------------------------------------------------------------------------- /makefile.targets: -------------------------------------------------------------------------------- 1 | install: ospac 2 | cp ospac /usr/local/bin 3 | cp ../ospac.1 /usr/local/share/man/man1/ 4 | 5 | install-gui: ospac-gui 6 | cp ospac-gui /usr/local/bin 7 | cp ../ospac.1 /usr/local/share/man/man1/ 8 | 9 | uninstall: 10 | rm -f /usr/local/bin/ospac 11 | rm -f /usr/local/bin/ospac-gui 12 | rm -f /usr/local/share/man/man1/ospac.1 13 | 14 | ospac-gui-linux: $(OBJS) $(USER_OBJS) 15 | @echo 'Building target: $@' 16 | @echo 'Invoking: Cross G++ Linker' 17 | g++ `fltk-config --ldflags` -L/usr/local/lib -o "ospac-gui" $(OBJS) $(USER_OBJS) $(LIBS) 18 | @echo 'Finished building target: $@' 19 | @echo ' ' 20 | -------------------------------------------------------------------------------- /ospac-internal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/ospac-internal.pdf -------------------------------------------------------------------------------- /ospac.1: -------------------------------------------------------------------------------- 1 | .\" Process this file with 2 | .\" groff -man -Tascii ospac.1 3 | .\" 4 | .TH OSPAC 1 "May 2 2016" 5 | .SH NAME 6 | ospac \- open source podcast audio chain 7 | .SH SYNOPSIS 8 | .B ospac [-voice] [-mix] 9 | .I input1.wav input2.wav 10 | .B ... 11 | .B --output 12 | .I output.wav 13 | 14 | .SH DESCRIPTION 15 | .B ospac 16 | will take a multi-channel recording of a conversation and master this to 17 | a high-quality mix-down with support for intro and outro. It was developed 18 | due to the need of a batch solution for audio podcast creation. 19 | 20 | .SH "INFORMATION OPTIONS" 21 | .IP --help 22 | Display help 23 | .IP "--verbosity [n]" 24 | Set verbosity to level 25 | .I [n] 26 | 27 | .SH "OUTPUT OPTIONS" 28 | .IP --spatial 29 | Produce a stereo output with inter aural delay. 30 | .IP --stereo 31 | Standard mode to produce a stereo output. 32 | .IP --mono 33 | Create a mono output. 34 | .IP --multi 35 | Create a multichannel file where each first and further channels in 36 | each input phases are mixed to each other 37 | .IP "--set-stereo-level [n]" 38 | Set maximum volume factor for stereo and spatial mode (0.9) - 39 | the lower the stronger the effect is 40 | .IP "--set-stereo-spatial [n]" 41 | Set maximum inter-aural delay distance in meter in spatial mode (0.03) - 42 | the higher the stronger - the average distance between human ears are 43 | 0.22 (meter) so you probably should not go above that limit 44 | 45 | .SH "OUTPUT TARGETS" 46 | .IP "--output [file]" 47 | Write final output to 48 | .I [file] 49 | in wave format 50 | .IP "--mp3 [file]" 51 | Write final output to 52 | .I [file] 53 | in mp3 format using the external 54 | .B lame 55 | command which has to be in the path 56 | .IP "--ogg [file]" 57 | Write final output to 58 | .I [file] 59 | in ogg format using the external 60 | .B oggenc 61 | command which has to be in the path 62 | .IP "--plot [file]" 63 | Write a wave form image to 64 | .I [file] 65 | in netpbm PPM format 66 | 67 | .SH "OUTPUT META DATA" 68 | .IP "--title [text]" 69 | Set title tag to 70 | .I [text] 71 | if this exists in output 72 | .IP "--artist [text]" 73 | Set artist tag to 74 | .I [text] 75 | if this exists in output 76 | .IP "--album [text]" 77 | Set album tag to 78 | .I [text] 79 | if this exists in output 80 | .IP "--comment [text]" 81 | Set comment tag to 82 | .I [text] 83 | if this exists in output 84 | .IP "--category [text]" 85 | Set category tag to 86 | .I [text] 87 | if this exists in output 88 | .IP "--episode [text]" 89 | Set episode tag to 90 | .I [text] 91 | if this exists in output 92 | .IP "--year [text]" 93 | Set year tag to 94 | .I [text] 95 | if this exists in output 96 | .IP "--image [file]" 97 | Set image tag to 98 | .I [file] 99 | if this exists in output 100 | 101 | .SH "NEW SEGMENT OPTIONS" 102 | .IP --voice 103 | Start of voice channel segment (default) 104 | .IP --mix 105 | Start pre-mixed channel segment 106 | .IP --raw 107 | Start of raw channel segment 108 | 109 | .SH "SEGMENT TRANSITION OPTIONS" 110 | .IP "--fade [s]" 111 | Fading transition over 112 | .I [s] 113 | seconds 114 | .IP "--overlap [s]" 115 | Overlapping transition over 116 | .I [s] 117 | seconds 118 | .IP "--parallel" 119 | Render the previous and next segment in parallel. By this, you can have 120 | independent processing for parallel audio channels. For example, for music 121 | or noise channels. Be aware, that filters such as 122 | .I --skip 123 | or 124 | .I --noise 125 | or 126 | .I --trim 127 | will most likely generate out-of-sync audio as the cuts are not synchronized 128 | between parallel segments. 129 | 130 | .SH "CROSSTALK FILTER OPTIONS" 131 | .IP --xgate 132 | Enable robust crosstalk gate: This tries to mute channels that do not 133 | contribute significant input to cancel crosstalk. 134 | Each channel is given an activity score computed by its windowed 135 | energy multiple compared to its assumed silence level. The channel 136 | with highest score has 100%, the rest are scaled down linearily 137 | with their score compared to the highest. 138 | .IP --no-xgate 139 | Disable crosstalk gate 140 | .IP --xfilter 141 | Enable experimental crosstalk filter: This tries to mute channels that 142 | contain mainly input that was recorded by other channels before, i.e. 143 | crosstalk. 144 | .IP --no-xfilter 145 | Disable crosstalk filter for current input section 146 | 147 | .SH "ADAPTIVE SILENCE SKIP OPTIONS" 148 | .IP --skip 149 | Soft skip silent passages over 0.5s length 150 | .IP "--skip-level [n]" 151 | Fraction of maximum level considered silence (0.01) 152 | .IP "--skip-order [n]" 153 | Order of time of detected silence that is to be skipped. 154 | A level of 0 only skips the 0.5s, 155 | a level 0.5 skips about the square root of the silence, 156 | a level of 1 skips all of the silence (default: 0.75). 157 | .IP "--skip-target [n]" 158 | Try to adapt given parameters of level and order to achieve the given 159 | fraction of length. A parameter of 0.9 will try to reduce the file 160 | to 90% of its size. Note, that the result will sound less acceptible 161 | as soon as all silence space was used up. The algorithm iterates to 162 | the target size, and is therefore only suitable for small files. 163 | .IP --no-skip 164 | Do not skip any content 165 | .IP --noise 166 | Skip all actual signal to get an impression of the noise on the channel. 167 | .IP --trim 168 | Skip silence from the beginning or the end of channels. 169 | 170 | .SH "LEVELING, EQUALIZER AND NORMALIZATION OPTIONS" 171 | .IP --leveler 172 | Enable selective leveler: Enable windowed energy based leveler for current input section. 173 | Depending on the energy level in a window around the current position 174 | the filter decideds whether this is an active channel or not. In 175 | case of an active channel it tries to amplify the current position 176 | such the a target energy for the window is achieved. If the channel 177 | is deemed inactive, the channel is de-amplified. To avoid to fast 178 | changes in amplification levels, the levels are averaged over time. 179 | .IP "--level-mode {single|stereo|multi}" 180 | The leveling can work on single channels, on each two consecutive stereo 181 | channels, or on all channels at once. When using the single mode on a stereo 182 | signal, it might destroy wanted differences in loudness between the two channels- 183 | therefore they should be joined in the leveling. The multi mode joins all channels 184 | for the analysis, and levels all channels by the same amount. The single mode 185 | is default for voice segments, the stereo mode is default for mix segments. 186 | .IP "--target [n]" 187 | Set average target L2 energy 188 | .I [n] 189 | for leveler. The L2 energy is normed to one entry, so a level of 190 | 2000-6000 seems suitable. Default: 3000. 191 | .IP --no-leveler 192 | Disable selective leveler 193 | .IP "--factor [n]" 194 | Multiply channels by factor 195 | .I [n] 196 | with sigmoid limiter (default: 1.25): 197 | Amplify the signal by given factor and pass the resulting signal 198 | through a sigmoid function to prevent overdrive, but eventual 199 | distortion cannot be avoided. 200 | .IP --no-factor 201 | Disable channel multiplier 202 | .IP --eqvoice 203 | Attenuate frequency bands for improved diction 204 | .IP --no-eqvoice 205 | Do not attenuate frequency bands 206 | .IP --analyze 207 | Analyze frequency band components of active segments. 208 | .IP --normalize 209 | Normalize final mix 210 | .IP --no-normalize 211 | Disable final normalization 212 | .IP "--lowpass [frequency] [transition]" 213 | Apply a lowpass filter to the current audio segment up to 214 | .I [frequency] 215 | Hertz. The 216 | .I [transition] 217 | specifies the quality of the filter in Hertz of transition. 218 | .IP "--highpass [frequency] [transition]" 219 | Apply a highpass filter to the current audio segment from 220 | .I [frequency] 221 | Hertz. The 222 | .I [transition] 223 | specifies the quality of the filter in Hertz of transition. 224 | .IP "--bandpass [low] [high] [transition]" 225 | Apply a bandpass filter to the current audio segment, starting from 226 | .I [low] 227 | Hertz up to 228 | .I [high] 229 | Hertz. The 230 | .I [transition] 231 | specifies the quality of the filter in Hertz of transition. 232 | 233 | .SH "INPUT AUDIO FILES" 234 | .IP "[wave file]" 235 | Load the file 236 | .I [wave file] 237 | and regard each channel as an individual input. 238 | .IP "--left [wave file]" 239 | Load the file 240 | .I [wave file] 241 | and only use the left channel if it was in stereo. 242 | .IP "--right [wave file]" 243 | Load the file 244 | .I [wave file] 245 | and only use the right channel if it was in stereo. 246 | .IP "--to-mono [wave file]" 247 | Load the file 248 | .I [wave file] 249 | and use a mono-mixdown of all channels in that file. 250 | .IP "--ascii [sample rate] [text file]" 251 | Load the ascii file 252 | .I [text file] 253 | which is assumed to have a sample rate of 254 | .I [sample rate] 255 | Hertz. The values can be integer or float values separated by 256 | white space. The input is rescaled to [-32000,32000] and comments 257 | starting with '#' are discarded until the next end of line. 258 | 259 | .SH EXAMPLES 260 | Mix 2 mono voice recordings with crosstalk filter, leveling and normalization: 261 | .PP 262 | .nf 263 | .RS 264 | ospac person1.wav person2.wav --output target.wav 265 | .RE 266 | .fi 267 | .PP 268 | 269 | Mix podcast with stereo intro and outro: 270 | .PP 271 | .nf 272 | .RS 273 | ospac --mix in.wav --overlap 4 \\ 274 | --voice person1.wav person2.wav --overlap 4 \\ 275 | --mix out.wav --output target.wav 276 | .RE 277 | .fi 278 | .PP 279 | 280 | Again with shortened options: 281 | .PP 282 | .nf 283 | .RS 284 | ospac -mi in.wav -ov 4 -vo person1.wav person2.wav -ov 4 -mi out.wav -out target.wav 285 | .RE 286 | .fi 287 | .PP 288 | 289 | Just run the crosstalk filter and create an un-mixed multi-channel output: 290 | .PP 291 | .nf 292 | .RS 293 | ospac --multi --raw t1.wav t2.wav t3.wav t4.wav --xfilter --output multi.wav 294 | .RE 295 | .fi 296 | .PP 297 | 298 | .SH AUTHOR 299 | Sebastian Ritterbusch 300 | .SH "SEE ALSO" 301 | .BR sox (1), lame (1), oggenc (1), pnmtopng (1), ppm (5) 302 | -------------------------------------------------------------------------------- /src/Analyzer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Analyzer.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 15.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Frequency band activity analysis 8 | */ 9 | 10 | #include 11 | #include 12 | #include "Analyzer.h" 13 | #include "Frequency.h" 14 | #include "Log.h" 15 | 16 | 17 | std::vector Analyzer::bandedAnalysis(const Channel & c, 18 | std::vector f) 19 | { 20 | float l2=c.l2norm(); 21 | Channels bands=Frequency::split(c,f); 22 | unsigned n=bands.size(); 23 | 24 | std::vector target(n); 25 | for(unsigned i=0;il2) 34 | { 35 | for(unsigned j=0;j Analyzer::bandedAnalysis(const Channel & c) 75 | { 76 | std::vector f(4); 77 | f[0]=100; 78 | f[1]=500; 79 | f[2]=2500; 80 | f[3]=4000; 81 | return bandedAnalysis(c,f); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/Analyzer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Analyzer.h 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 15.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Frequency band activity analysis 8 | */ 9 | 10 | #ifndef ANALYZER_H_ 11 | #define ANALYZER_H_ 12 | 13 | #include "Channel.h" 14 | 15 | /** 16 | * @brief Frequency band activity analysis 17 | */ 18 | class Analyzer 19 | { 20 | private: 21 | static float sqr(const float & a) { return a*a; } 22 | public: 23 | /** 24 | * Analysis of frequency band distribution if activity is detected 25 | * @param c audio channel to work on 26 | * @param frequencies n cut-off frequencies 27 | * @return resulting l2 normalized n+1 l2 energy levels 28 | */ 29 | static std::vector bandedAnalysis(const Channel & c, 30 | std::vector frequencies); 31 | 32 | /** 33 | * Analysis of frequency band distribution if activity is detected 34 | * for fixed cut-off frequencies 100Hz, 500Hz, 2.5k, 4k 35 | * @param c audio channel to work on 36 | * @return resulting l2 normalized n+1 l2 energy levels 37 | */ 38 | static std::vector bandedAnalysis(const Channel & c); 39 | 40 | }; 41 | 42 | #endif /* ANALYZER_H_ */ 43 | -------------------------------------------------------------------------------- /src/Channel.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Channel.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 5.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Audio channel abstraction 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | 14 | #include "Channel.h" 15 | #include "Log.h" 16 | 17 | float Channel::zero=0; 18 | 19 | Channel::Channel() 20 | { 21 | rate=44100; 22 | } 23 | 24 | Channel::Channel(unsigned aRate) : rate(aRate) 25 | { 26 | } 27 | 28 | Channel::Channel(unsigned aRate,const std::vector & aData) : rate(aRate), data(aData) 29 | { 30 | } 31 | 32 | Channel::Channel(unsigned aRate,unsigned size) : rate(aRate), data(std::vector(size)) 33 | { 34 | } 35 | 36 | Channel::~Channel() 37 | { 38 | } 39 | 40 | float & Channel::operator [](int index) 41 | { 42 | if(index<0 || (unsigned)index>=data.size()) 43 | { 44 | zero=0; 45 | return zero; 46 | } 47 | return data[index]; 48 | } 49 | float Channel::operator [](int index) const 50 | { 51 | if(index<0 || (unsigned)index>=data.size()) 52 | { 53 | return 0; 54 | } 55 | return data[index]; 56 | } 57 | 58 | unsigned Channel::size() const 59 | { 60 | return data.size(); 61 | } 62 | 63 | unsigned Channel::samplerate() const 64 | { 65 | return rate; 66 | } 67 | 68 | double Channel::l2norm(void) const 69 | { 70 | double sum=0.0; 71 | for(unsigned i=0;i(data[i]); 73 | if(sum!=0.0) 74 | sum/=data.size(); 75 | return sqrt(sum); 76 | } 77 | 78 | double Channel::l2upnorm(float limit) const 79 | { 80 | double sum=0.0; 81 | int count=0; 82 | float v; 83 | limit*=limit; 84 | for(unsigned i=0;i(data[i]); 87 | if(v>limit) 88 | { 89 | sum+=v; 90 | count++; 91 | } 92 | } 93 | if(sum!=0.0) 94 | sum/=count; 95 | return sqrt(sum); 96 | } 97 | double Channel::l2downnorm(float limit) const 98 | { 99 | double sum=0.0; 100 | int count=0; 101 | float v; 102 | limit*=limit; 103 | for(unsigned i=0;i(data[i]); 106 | if(vmax) 124 | max=data[i]; 125 | if(-data[i]>max) 126 | max=-data[i]; 127 | } 128 | return max; 129 | } 130 | 131 | Channel Channel::downsample(unsigned factor) const 132 | { 133 | if(factor>0) 134 | { 135 | unsigned newSize=data.size()/factor; 136 | std::vector target=std::vector(newSize); 137 | float acc; 138 | for(unsigned i=0,j=0;j0) 153 | { 154 | unsigned newSize=data.size()/factor; 155 | std::vector target=std::vector(newSize); 156 | float acc; 157 | for(unsigned i=0,j=0;j target=std::vector(size); 172 | unsigned i; 173 | for(i=0;i target=std::vector(newSize); 184 | LOG(logDEBUG) << "Old rate "<< rate << " New Rate: " << newRate << std::endl; 185 | LOG(logDEBUG) << "Old size " << data.size() << " New Size: " << newSize << std::endl; 186 | 187 | unsigned oldSize=data.size(); 188 | 189 | // TODO: Only nearest "interpolation"... 190 | for(unsigned i=0;isamplerate) 206 | samplerate=a[c].samplerate(); 207 | 208 | for(unsigned c=0;clen) 221 | len=a[c].size(); 222 | 223 | for(unsigned c=0;c 4 | * @version 1.0 5 | * @date 5.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Audio channel abstraction 8 | */ 9 | 10 | #ifndef CHANNEL_H_ 11 | #define CHANNEL_H_ 12 | 13 | #include 14 | 15 | /** 16 | * @brief Audio channel abstraction class 17 | * 18 | * TODO: Switch to auto_ptr to avoid copy operations at assignments. 19 | * TODO: Virtualization of memory segments to avoid full memory operations. 20 | */ 21 | class Channel 22 | { 23 | unsigned rate; 24 | std::vector data; 25 | static float zero; 26 | public: 27 | /** 28 | * Create a new audio channel. 29 | */ 30 | Channel(); 31 | 32 | /** 33 | * Create an audio channel with given sample rate 34 | * @param rate sample rate in Hertz (1/s) 35 | */ 36 | Channel(unsigned rate); 37 | 38 | /** 39 | * Create an audio channel with given rate and sample data 40 | * @param rate sample rate in Hertz (1/s) 41 | * @param data audio data as float vector 42 | */ 43 | Channel(unsigned rate, const std::vector & data); 44 | 45 | /** 46 | * Create an audio channel with given rate and number of samples 47 | * @param rate sample rate in Hetz (1/s) 48 | * @param size number of samples 49 | */ 50 | Channel(unsigned rate, unsigned size); 51 | 52 | virtual ~Channel(); 53 | 54 | /** 55 | * Access a sample for read/write access 56 | * The bounds are checked on the index and an impostor is returned 57 | * in case of out-of-bounds requests. 58 | * @param index of sample 59 | * @return float reference on sample 60 | */ 61 | float & operator [](int); 62 | 63 | /** 64 | * Access to a sample with read only access 65 | * The bounds are checked on the index and zero is returned in case of 66 | * out-of-bounds requests. 67 | * @param index of sample 68 | * @return float value of sample 69 | */ 70 | float operator [](int) const; 71 | 72 | /** 73 | * Number of samples in this channel 74 | * @return number of samples 75 | */ 76 | unsigned size() const; 77 | 78 | /** 79 | * Sample rate of this channel 80 | * @return sample rate in Hertz (1/s) 81 | */ 82 | unsigned samplerate() const; 83 | 84 | /** 85 | * Returns the l2-norm of the channel normalized to one sample. 86 | * @f[ ||u||_2:=\sqrt{ \frac1n \sum_{i=0}^{n-1} u_i^2 } @f] 87 | * @return normalized l2-norm of the channel 88 | */ 89 | double l2norm() const; 90 | 91 | 92 | /** 93 | * Returns the "up" l2-norm of the channel normalized to one sample, 94 | * taking only the values into account that are higher than the given limit L. 95 | * This hints to the energy of the channels when it is active, when 96 | * invoked with the l2norm of the channel as limit. 97 | * @f[ ||u||^{>L}_2:=\sqrt{ \frac1m \sum_{j=0}^{m-1} v_j^2 }, \textrm{ where } \forall i: u_i> L \exists_1 j(i): v_{j(i)}=u_i @f] 98 | * @param limit value 99 | * @return normalized "up" l2-norm of the channel 100 | */ 101 | double l2upnorm(float) const; 102 | 103 | /** 104 | * Returns the "down" l2-norm of the channel normalized to one sample, 105 | * taking only the values into account that are higher than the given limit L. 106 | * This hints to the energy of the channels when it is not active, when 107 | * invoked with the l2norm of the channel as limit. 108 | * @f[ ||u||^{ 151 | static T sqr(const T&a){return a*a;} 152 | 153 | }; 154 | /** 155 | * Vector of channels as type for multiple channels. 156 | */ 157 | typedef std::vector Channels; 158 | 159 | /** 160 | * Unify samplerate and length of channels 161 | * @param channels to be unified 162 | */ 163 | void unify(Channels &channels); 164 | 165 | /** 166 | * Unify samplerate of channels 167 | * @param channels to be unified 168 | * @return samplerate in Hertz 169 | */ 170 | unsigned unifiedSamplerate(Channels &channels); 171 | 172 | /** 173 | * Unify length of channels 174 | * @param channels to be unified 175 | * @return length in number of channels 176 | */ 177 | unsigned unifiedLength(Channels &channels); 178 | 179 | #endif /* CHANNEL_H_ */ 180 | -------------------------------------------------------------------------------- /src/CrosstalkFilter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CrosstalkFilter.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 6.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Filter to actively identify crosstalk in other channels 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "CrosstalkFilter.h" 14 | #include "Wave.h" 15 | #include "Physics.h" 16 | #include "Log.h" 17 | 18 | 19 | CrosstalkFilter::CrosstalkFilter(Channels &aChannels, 20 | unsigned aDownsampleLevel, 21 | double windowsec, 22 | double mindistance, 23 | double maxdistance, 24 | float aMuteStartRatio, 25 | float aMuteFullRatio) 26 | : downsampleLevel(aDownsampleLevel), 27 | channels(aChannels), 28 | muteStartRatio(aMuteStartRatio), 29 | muteFullRatio(aMuteFullRatio) 30 | { 31 | int f=44100; 32 | if(channels.size()>0) 33 | f=channels[0].samplerate(); 34 | 35 | f/=aDownsampleLevel; 36 | 37 | workWindow=int(windowsec*f+0.5); 38 | if(workWindow<2) 39 | workWindow=2; 40 | 41 | minShift=unsigned(Physics::meterToSec(mindistance)*f+0.5); 42 | maxShift=unsigned(Physics::meterToSec(maxdistance)*f+0.5); 43 | 44 | if(minShift<1) 45 | minShift=1; 46 | if(maxShift<=minShift) 47 | maxShift=minShift+1; 48 | 49 | LOG(logINFO) << "setting up " << workWindow << " samples workWindow, " 50 | << minShift << " samples minShift and " 51 | << maxShift << " samples maxShift"<< std::endl; 52 | 53 | prepareVectors(); 54 | } 55 | 56 | 57 | CrosstalkFilter::CrosstalkFilter(Channels &aChannels, 58 | unsigned aDownsampleLevel, 59 | unsigned aWorkWindow, 60 | unsigned aMinShift, 61 | unsigned aMaxShift, 62 | float aMuteStartRatio, 63 | float aMuteFullRatio) 64 | : downsampleLevel(aDownsampleLevel), 65 | channels(aChannels), 66 | workWindow(aWorkWindow), 67 | minShift(aMinShift), 68 | maxShift(aMaxShift), 69 | muteStartRatio(aMuteStartRatio), 70 | muteFullRatio(aMuteFullRatio) 71 | { 72 | prepareVectors(); 73 | } 74 | 75 | void CrosstalkFilter::prepareVectors() 76 | { 77 | downsample=Channels(channels.size()); 78 | muteFactor=Channels(channels.size()); 79 | activity=Channels(channels.size()); 80 | l2norm=std::vector(channels.size()); 81 | l2upnorm=std::vector(channels.size()); 82 | l2downnorm=std::vector(channels.size()); 83 | 84 | for(unsigned i=0;imaxsum) 133 | { 134 | maxsum=sum; 135 | max=k; 136 | } 137 | LOG(logDEBUG) << i << " vs " << j << " in " << k << ": " << sum << " - " << n1 << " - " << n2 << std::endl; 138 | } 139 | LOG(logDEBUG) << "Maximum bei " << max << " also " << Physics::secToMeter(double(max)/downsample[i].samplerate()) << "m mit " << maxsum << std::endl; 140 | } 141 | 142 | 143 | for(unsigned i=0;i0;k++) 153 | { 154 | for(unsigned c=0;c0;c++) 155 | { 156 | if(c!=i) 157 | { 158 | double n=0,n1=0; 159 | 160 | for(int l=-workWindow;l<(int)workWindow;l++) 161 | { 162 | n+=fabs(downsample[c][j+l-k]); 163 | n1+=fabs(downsample[i][j+l]); 164 | } 165 | if(n1>0) 166 | { 167 | double factor=n1/n; 168 | 169 | n1*=factor; 170 | n=0; 171 | 172 | for(int l=-workWindow;l<(int)workWindow;l++) 173 | { 174 | n+=fabs(downsample[i][j+l]-factor*downsample[c][j+l-k]); 175 | } 176 | 177 | 178 | double newmute=1; 179 | double r=n/n1; 180 | 181 | if(activity[i][j]>activity[c][j-k]) 182 | r-=0.25; 183 | else 184 | r+=0.25; 185 | 186 | if(r>muteStartRatio && rmuteFullRatio) 191 | { 192 | newmute=0; 193 | count++; 194 | shiftsum+=k; 195 | } 196 | 197 | if(newmute0) 207 | { 208 | LOG(logDEBUG) << "Found: " << count << " with avg-shift " << double(shiftsum)/count << std::endl; 209 | } 210 | } 211 | } 212 | 213 | /** CrosstalkFilter analysis 214 | * Looks through all channels if sound bits of other channels are contained and sets 215 | * mute vector correspondingly. Does not alter any channels. 216 | */ 217 | void CrosstalkFilter::analyze() 218 | { 219 | LOG(logDEBUG) << "We have " << channels.size() << " channels." << std::endl; 220 | std::vector > shift(channels.size()); 221 | for(unsigned i=0;i(channels.size()); 224 | for(unsigned j=0;jmaxsum) 240 | { 241 | maxsum=sum; 242 | max=k; 243 | } 244 | 245 | } 246 | LOG(logDEBUG) << "Maximum bei " << max << " also " << Physics::secToMeter(double(max)/downsample[i].samplerate()) << "m mit " << maxsum << std::endl; 247 | shift[i][j]=max; 248 | } 249 | } 250 | 251 | for(unsigned i=0;i skpsIn(deltaIn*2+1); 275 | std::vector njsIn2(deltaIn*2+1); 276 | std::vector skpsOut(deltaOut*2+1); 277 | std::vector njsOut2(deltaOut*2+1); 278 | for(int v=-deltaIn;v<=deltaIn;v++) 279 | { 280 | skpsIn[v+deltaIn]=0; 281 | njsIn2[v+deltaIn]=0; 282 | } 283 | for(int v=-deltaOut;v<=deltaOut;v++) 284 | { 285 | skpsOut[v+deltaOut]=0; 286 | njsOut2[v+deltaOut]=0; 287 | } 288 | 289 | for(int l=-workWindow;l<0;l++) 290 | { 291 | for(int v=-deltaIn;v<=deltaIn;v++) 292 | { 293 | skpsIn[v+deltaIn]+=downsample[i][l+workWindow]*downsample[j][l+workWindow-sIn+v]; 294 | njsIn2[v+deltaIn]+=sqr(downsample[j][l+workWindow-sIn+v]); 295 | } 296 | for(int v=-deltaOut;v<=deltaOut;v++) 297 | { 298 | skpsOut[v+deltaOut]+=downsample[i][l+workWindow]*downsample[j][l+workWindow+sOut+v]; 299 | njsOut2[v+deltaOut]+=sqr(downsample[j][l+workWindow+sOut+v]); 300 | } 301 | ni2+=sqr(downsample[i][l+workWindow]); 302 | } 303 | 304 | 305 | for(unsigned l=0;lskpIn) 317 | { 318 | skpIn=fabs(skpsIn[v+deltaIn]); 319 | vIn=v+deltaIn; 320 | } 321 | njsIn2[v+deltaIn]+=sqr(downsample[j][l+workWindow-sIn+v]); 322 | njsIn2[v+deltaIn]-=sqr(downsample[j][l-workWindow-sIn+v]); 323 | if(njsIn2[v+deltaIn]<0) 324 | njsIn2[v+deltaIn]=0; 325 | } 326 | for(int v=-deltaOut;v<=deltaOut;v++) 327 | { 328 | skpsOut[v+deltaOut]+=downsample[i][l+workWindow]*downsample[j][l+workWindow+sOut+v]; 329 | skpsOut[v+deltaOut]-=downsample[i][l-workWindow]*downsample[j][l-workWindow+sOut+v]; 330 | if(fabs(skpsOut[v+deltaOut])>skpOut) 331 | { 332 | skpOut=fabs(skpsOut[v+deltaOut]); 333 | vOut=v+deltaOut; 334 | } 335 | njsOut2[v+deltaOut]+=sqr(downsample[j][l+workWindow+sOut+v]); 336 | njsOut2[v+deltaOut]-=sqr(downsample[j][l-workWindow+sOut+v]); 337 | if(njsOut2[v+deltaOut]<0) 338 | njsOut2[v+deltaOut]=0; 339 | } 340 | 341 | 342 | ni2+=sqr(downsample[i][l+workWindow]); 343 | ni2-=sqr(downsample[i][l-workWindow]); 344 | 345 | if(ni2<0) 346 | ni2=0; 347 | 348 | nr=ni2; 349 | if(njsIn2[vIn]>0) 350 | { 351 | 352 | nr=(nr-sqr(skpIn)/njsIn2[vIn]); 353 | 354 | } 355 | if(njsOut2[vOut]>0) 356 | { 357 | 358 | nr=(nr+sqr(skpOut)/njsOut2[vOut]); 359 | } 360 | 361 | if(!(muteFactor[i][l]==muteFactor[i][l])) 362 | { 363 | LOG(logERROR) << "nan in factor! " << i << " " << l << " " << nj2 << " " << ni2 << " " << nr << " " <0 && nr>0) 368 | muteFactor[i][l]+=(sqrt(ni2)-sqrt(nr))/sqrt(ni2); 369 | 370 | if(!(muteFactor[i][l]==muteFactor[i][l])) 371 | { 372 | LOG(logERROR) << "nan in factor! " << i << " " << l << " " << nj2 << " " << ni2 << " " << nr << " " <(channels.size()); 387 | for(unsigned c=0;cmax) 408 | { 409 | max=muteFactor[c][j]; 410 | } 411 | if(muteFactor[c][j] 4 | * @version 1.0 5 | * @date 6.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Filter to actively identify crosstalk in other channels 8 | */ 9 | 10 | #ifndef CROSSTALKFILTER_H_ 11 | #define CROSSTALKFILTER_H_ 12 | 13 | #include 14 | #include 15 | #include "Channel.h" 16 | 17 | /** 18 | * @brief The CrosstalkFilter tries to identify time-delayed crosstalk of each channel in other channels 19 | * by comparing integrals of l2power and mutes identified sections. 20 | * 21 | * Main issue is that it does not recognize the channel with more quality. It tends to mute channels 22 | * less with more open mics than channels with directed mics. This is unwanted behaviour. 23 | * 24 | * Starting from this two channel example, where the second channel consists 25 | * of crosstalk from the first channel and a short original input. 26 | * @image html crosstalk-example.png 27 | * @image latex crosstalk-example.png "Exemplary two channel input" width=10cm 28 | * 29 | * The crosstalk filter decreases the volume of the passages where mainly 30 | * previous signals from other channels are detected. 31 | * @image html xfilter-result.png 32 | * @image latex xfilter-result.png "Result of crosstalk filter" width=10cm 33 | * 34 | * 35 | */ 36 | class CrosstalkFilter 37 | { 38 | private: 39 | unsigned downsampleLevel; 40 | Channels & channels; 41 | Channels downsample; 42 | Channels muteFactor; 43 | Channels activity; 44 | unsigned workWindow; 45 | unsigned minShift; 46 | unsigned maxShift; 47 | float muteStartRatio; 48 | float muteFullRatio; 49 | 50 | std::vector l2norm; 51 | std::vector l2upnorm; 52 | std::vector l2downnorm; 53 | 54 | public: 55 | /** CrosstalkFilter Constructor with sample settings (please consider using the variant with physical settings!) 56 | * \param aChannels std::vector of channels the filter will operate on 57 | * \param aDownsampleLevel factor of downsampling for the analysis 58 | * \param aWorkwindow window size in samples the comparism is made on 59 | * \param aMinShift number of minimum time-delay in samples (dependent on aDownsampleLevel) 60 | * \param aMaxShift number of maximum time-delay in samples (dependent on aDownsampleLevel) 61 | * \param aMuteStartRatio ratio of integrals of original to rest from where muting will linearly start 62 | * \param aMuteFullRatio ratio of integrals of original to rest where linearity ends and full mute will occur 63 | * 64 | * \return CrosstalkFilter object 65 | * \sa analyze() and mute() 66 | */ 67 | CrosstalkFilter(Channels &aChannels,unsigned aDownsampleLevel,unsigned aWorkwindow,unsigned aMinShift,unsigned aMaxShift,float aMuteStartRatio=1.2,float aMuteFullRatio=1.5); 68 | 69 | /** CrosstalkFilter Constructor with sample settings (please consider using the variant with physical settings!) 70 | * \param aChannels std::vector of channels the filter will operate on 71 | * \param aDownsampleLevel factor of downsampling for the analysis 72 | * \param windowsec window size in seconds the comparism is made on 73 | * \param mindistance minimum assumed spatial distance between channels (in meters) 74 | * \param maxdistance maximum assumed spatial distance between channels (in meters) 75 | * \param aMuteStartRatio ratio of integrals of original to rest from where muting will linearly start 76 | * \param aMuteFullRatio ratio of integrals of original to rest where linearity ends and full mute will occur 77 | * 78 | * \return CrosstalkFilter object 79 | * \sa analyze() and mute() 80 | */ 81 | CrosstalkFilter(Channels &aChannels,unsigned aDownsampleLevel,double windowsec=0.1,double mindistance=1.5,double maxdistance=5.0,float aMuteStartRatio=1.2,float aMuteFullRatio=1.5); 82 | 83 | void analyze2(); 84 | void analyze(); 85 | void save(std::string); 86 | void mute(); 87 | 88 | private: 89 | template 90 | static T sqr(const T&a){return a*a;} 91 | 92 | void prepareVectors(); 93 | 94 | 95 | }; 96 | 97 | #endif /* CROSSTALKFILTER_H_ */ 98 | -------------------------------------------------------------------------------- /src/CrosstalkGate.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CrosstalkGate.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 9.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Crosstalk gate mutes less active channels 8 | */ 9 | 10 | 11 | #include "CrosstalkGate.h" 12 | #include "Log.h" 13 | #include "Wave.h" 14 | 15 | void CrosstalkGate::gate(Channels &channels,unsigned downsampleLevel,double windowsec,double mixsec) 16 | { 17 | if(channels.size()==0) 18 | return; 19 | 20 | unsigned workWindow=windowsec*channels[0].samplerate()/downsampleLevel; 21 | unsigned mixWindow=mixsec*channels[0].samplerate(); 22 | 23 | LOG(logINFO) << "Crossgate with workWindow " << workWindow << std::endl; 24 | 25 | Channels activity=Channels(channels.size()); 26 | Channels downsample=Channels(channels.size()); 27 | //Channels muteFactor=Channels(channels.size()); 28 | 29 | std::vector l2norm=std::vector(channels.size()); 30 | std::vector l2upnorm=std::vector(channels.size()); 31 | std::vector l2downnorm=std::vector(channels.size()); 32 | 33 | for(unsigned i=0;i factor(channels.size()); 67 | std::vector > memory(channels.size()); 68 | unsigned n=0; 69 | unsigned p=0; 70 | 71 | for(unsigned i=0;i(mixWindow); 74 | for(unsigned j=0;jmaxActivity) 88 | maxActivity=my_act; 89 | } 90 | n++; 91 | if(n>mixWindow) 92 | n=mixWindow; 93 | for(unsigned c=0;c 4 | * @version 1.0 5 | * @date 9.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Crosstalk gate mutes less active channels 8 | */ 9 | 10 | #ifndef CROSSTALKGATE_H_ 11 | #define CROSSTALKGATE_H_ 12 | 13 | #include "Channel.h" 14 | 15 | /** 16 | * @brief Simple and robust crosstalk gate 17 | * 18 | * Starting from this two channel example, where the second channel consists 19 | * of crosstalk from the first channel and a short original input. 20 | * @image html crosstalk-example.png 21 | * @image latex crosstalk-example.png "Exemplary two channel input" width=10cm 22 | * 23 | * The crosstalk gate decreases the volume of the passages in the second channel 24 | * where its channel activity is below its maximum. 25 | * @image html xgate-result.png 26 | * @image latex xgate-result.png "Result of crosstalk gate filter" width=10cm 27 | * 28 | */ 29 | class CrosstalkGate 30 | { 31 | public: 32 | /** 33 | * Crosstalk gate based on downsampled energy level of channels: 34 | * For each channel the l2 energy of all sample below the 35 | * averaged l2 level of the signal is taken as silence level. 36 | * Then the activity in a window is assumed as factor above this silence 37 | * level. (This should be limited in future.) 38 | * The maximum activity will gain 100%, all other channels will be 39 | * muted linearily due to their activity. 40 | * The actual muting is averaged on a mixsec window to soften changes. 41 | * @param aChannels audio channels to work on 42 | * @param aDownsampleLevel downsample factor 43 | * @param windowsec activity window (in seconds) 44 | * @param mixsec mixing average window (in seconds) 45 | */ 46 | static void gate(Channels &aChannels,unsigned aDownsampleLevel,double windowsec=0.1,double mixsec=0.1); 47 | 48 | }; 49 | 50 | #endif /* CROSSTALKGATE_H_ */ 51 | -------------------------------------------------------------------------------- /src/Encode.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Encode.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 29.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Encoding to various formats using external tools 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include "Encode.h" 16 | #include "Wave.h" 17 | #include "Log.h" 18 | 19 | int Encode::lame(Channels &c, 20 | std::string filename, 21 | QualitySetting quality, 22 | std::string title, 23 | std::string artist, 24 | std::string album, 25 | std::string comment, 26 | std::string image, 27 | std::string category, 28 | std::string episode, 29 | std::string year) 30 | { 31 | 32 | if(c.size()<1) 33 | return 0; 34 | 35 | std::string tempfile=filename+".wav"; 36 | 37 | Wave::save(tempfile,c); 38 | 39 | unsigned rate=c[0].samplerate(); 40 | if(rate>44100) 41 | rate=44100; 42 | 43 | unsigned halfrate=rate/2; 44 | 45 | std::string Vrate="9"; 46 | switch(quality) 47 | { 48 | case LOW: 49 | Vrate="9"; 50 | break; 51 | case STANDARD: 52 | Vrate="8.4"; 53 | break; 54 | case HIGH: 55 | Vrate="5"; 56 | break; 57 | case INSANE: 58 | Vrate="1"; 59 | break; 60 | } 61 | 62 | std::stringstream stream; 63 | 64 | stream << "lame -V" << Vrate 65 | << " -q0" 66 | << " --lowpass "<< halfrate 67 | << " --resample "<< rate; 68 | 69 | 70 | if(title!="") 71 | stream << " --tt \""<44100) 120 | rate=44100; 121 | 122 | unsigned halfrate=rate/2; 123 | 124 | std::string Vrate="2"; 125 | switch(quality) 126 | { 127 | case LOW: 128 | Vrate="1"; 129 | break; 130 | case STANDARD: 131 | Vrate="2"; 132 | break; 133 | case HIGH: 134 | Vrate="4"; 135 | break; 136 | case INSANE: 137 | Vrate="6"; 138 | break; 139 | } 140 | 141 | std::stringstream stream; 142 | 143 | stream << "oggenc -q" << Vrate 144 | << " --advanced-encode-option lowpass_frequency="<< halfrate; 145 | 146 | if(title!="") 147 | stream << " -t \""< 4 | * @version 1.0 5 | * @date 29.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Encoding to various formats using external tools 8 | */ 9 | 10 | #ifndef ENCODE_H_ 11 | #define ENCODE_H_ 12 | 13 | #include 14 | 15 | #include "Channel.h" 16 | #include "Wave.h" // Nur fuer FFMPEG-Define und saveAac(...) 17 | 18 | /** 19 | * @brief Encoding to various formats using external tools 20 | */ 21 | 22 | class Encode 23 | { 24 | public: 25 | /** 26 | * General quality setting for all encoding formats 27 | */ 28 | enum QualitySetting { LOW, STANDARD, HIGH, INSANE}; 29 | private: 30 | Channels &channels; 31 | std::string title; 32 | std::string artist; 33 | std::string album; 34 | std::string comment; 35 | std::string category; 36 | std::string episode; 37 | std::string year; 38 | std::string image; 39 | QualitySetting quality; 40 | #ifdef HAS_FFMPEG 41 | int aacBitrate; 42 | #endif 43 | 44 | public: 45 | /** 46 | * Builder pattern constructor from channels to encode. 47 | * @param aChannels 48 | */ 49 | Encode(Channels &aChannels) 50 | : channels(aChannels), 51 | title(""),artist(""),album(""), 52 | comment("Encoded by ospac.net"), 53 | category("Speech"),episode(""), 54 | year(""),image(""), 55 | quality(STANDARD) 56 | #ifdef HAS_FFMPEG 57 | ,aacBitrate(64000) 58 | #endif 59 | { 60 | 61 | } 62 | 63 | /** 64 | * Set title meta tag of encoded file to given title 65 | * @param aTitle title to be set 66 | * @return modified builder object 67 | */ 68 | Encode & Title(std::string aTitle) { title=aTitle; return *this; } 69 | /** 70 | * set artist meta tag of encoded file to given artist 71 | * @param aArtist artist to be set 72 | * @return modified builder object 73 | */ 74 | Encode & Artist(std::string aArtist) { artist=aArtist; return *this; } 75 | /** 76 | * set album meta tag of encoded file to given album 77 | * @param aAlbum album to be set 78 | * @return modified builder object 79 | */ 80 | Encode & Album(std::string aAlbum) { album=aAlbum; return *this; } 81 | /** 82 | * set comment meta tag of encoded file to given comment 83 | * @param aComment comment to be set 84 | * @return modified builder object 85 | */ 86 | Encode & Comment(std::string aComment) { comment=aComment; return *this; } 87 | /** 88 | * set category meta tag of encoded file to given category 89 | * @param aCategory category to be set 90 | * @return modified builder object 91 | */ 92 | Encode & Category(std::string aCategory) { category=aCategory; return *this; } 93 | /** 94 | * set episode meta tag of encoded file to given episode 95 | * @param aEpisode episode to be set 96 | * @return modified builder object 97 | */ 98 | Encode & Episode(std::string aEpisode) { episode=aEpisode; return *this; } 99 | /** 100 | * set year meta tag of encoded file to given year 101 | * @param aYear year to be set 102 | * @return modified builder object 103 | */ 104 | Encode & Year(std::string aYear) { year=aYear; return *this; } 105 | /** 106 | * set image meta tag of encoded file to given image if possible 107 | * @param aImage image to be set 108 | * @return modified builder object 109 | */ 110 | Encode & Image(std::string aImage) { image=aImage; return *this; } 111 | /** 112 | * set quality of encoding to given meta value 113 | * @param aQuality quality meta value to be used 114 | * @return modified builder object 115 | */ 116 | Encode & Quality(QualitySetting aQuality) { quality=aQuality; return *this; } 117 | #ifdef HAS_FFMPEG 118 | 119 | /** 120 | * set aac bitrate to given value 121 | * @param aBitrate bitrate value to be used 122 | * @return modified builder object 123 | */ 124 | Encode & Bitrate(int aBitrate) { aacBitrate=aBitrate; return *this; } 125 | #endif 126 | 127 | /** 128 | * Create mp3 file from builder 129 | * @param filename under which the encoded file shall be saved 130 | * @return return value of external command 131 | */ 132 | int mp3(std::string filename) 133 | { 134 | return lame(channels,filename,quality,title,artist,album,comment, 135 | image,category,episode,year); 136 | 137 | } 138 | 139 | /** 140 | * Create ogg file from builder 141 | * @param filename under which the encoded file shall be saved 142 | * @return return value of external command 143 | */ 144 | int ogg(std::string filename) 145 | { 146 | return oggenc(channels,filename,quality,title,artist,album,comment, 147 | category,episode); 148 | 149 | } 150 | 151 | #ifdef HAS_FFMPEG 152 | /** 153 | * Create aac file from builder 154 | * @param filename under which the encoded file shall be saved 155 | * @return return value of command 156 | */ 157 | int aac(std::string filename) 158 | { 159 | return Wave::saveAac(filename,channels,aacBitrate); 160 | } 161 | #endif 162 | 163 | protected: 164 | 165 | /** 166 | * Encode given audio segment to mp3 using an external lame encoder 167 | * @param c channels to encode 168 | * @param filename destination filename 169 | * @param quality quality preset (LOW, STANDARD, HIGH, INSANE) 170 | * @param title title of the track 171 | * @param artist artist of the track 172 | * @param album album/podcast of the track 173 | * @param comment comment/license of the track 174 | * @param image optional image file 175 | * @param category category (such as Speech) 176 | * @param episode track/episode number 177 | * @param year year of the recording/publication 178 | * @return program return code 179 | */ 180 | static int lame(Channels &c, 181 | std::string filename, 182 | QualitySetting quality, 183 | std::string title, 184 | std::string artist, 185 | std::string album, 186 | std::string comment, 187 | std::string image, 188 | std::string category, 189 | std::string episode, 190 | std::string year); 191 | 192 | /** 193 | * Encode given audio segment to ogg using an external oggenc encoder 194 | * @param c channels to encode 195 | * @param filename destination filename 196 | * @param quality quality preset (LOW, STANDARD, HIGH, INSANE) 197 | * @param title title of the track 198 | * @param artist artist of the track 199 | * @param album album/podcast of the track 200 | * @param comment comment/license of the track 201 | * @param category category (such as Speech) 202 | * @param episode track/episode number 203 | * @return program return code 204 | */ 205 | static int oggenc(Channels &c, 206 | std::string filename, 207 | QualitySetting quality, 208 | std::string title, 209 | std::string artist, 210 | std::string album, 211 | std::string comment, 212 | std::string category, 213 | std::string episode); 214 | }; 215 | 216 | #endif /* ENCODE_H_ */ 217 | -------------------------------------------------------------------------------- /src/Equalizer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Equalizer.h 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 3.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Equalizer for sound enhancement 8 | */ 9 | 10 | 11 | #include "Equalizer.h" 12 | #include "Frequency.h" 13 | #include "MonoMix.h" 14 | 15 | Channel Equalizer::bandedEqualizer(const Channel & c, 16 | std::vector frequencies, 17 | std::vector factors) 18 | { 19 | Channels bands=Frequency::split(c,frequencies); 20 | 21 | MonoMix target; 22 | for(unsigned i=0;i freqs(4); 33 | freqs[0]=100; 34 | freqs[1]=400; 35 | freqs[2]=3000; 36 | freqs[3]=4000; 37 | std::vector factors(5); 38 | factors[0]=0.75; 39 | factors[1]=1.5; 40 | factors[2]=1; 41 | factors[3]=1.75; 42 | factors[4]=0.75; 43 | return bandedEqualizer(c,freqs,factors); 44 | } 45 | 46 | Channels Equalizer::voiceEnhance(const Channels & c) 47 | { 48 | Channels target(c.size()); 49 | for(unsigned i=0;i 4 | * @version 1.0 5 | * @date 3.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Equalizer for sound enhancement 8 | */ 9 | 10 | #ifndef EQUALIZER_H_ 11 | #define EQUALIZER_H_ 12 | 13 | #include 14 | 15 | #include "Channel.h" 16 | 17 | /** 18 | * @brief Preset equalizer using frequency banding 19 | */ 20 | class Equalizer 21 | { 22 | public: 23 | /** 24 | * Amplification for seperate frequency bands 25 | * @param c audio channel to work on 26 | * @param frequencies n cut-off frequencies 27 | * @param factors n+1 amplication factors 28 | * @return resulting audio channel 29 | */ 30 | static Channel bandedEqualizer(const Channel & c, 31 | std::vector frequencies, 32 | std::vector factors); 33 | /** 34 | * Preset equalizer for voice channel 35 | * @param c audio channel to work on 36 | * @return resulting audio channel 37 | */ 38 | static Channel voiceEnhance(const Channel & c); 39 | 40 | /** 41 | * Preset equalizer for voice channels 42 | * @param c audio channels to work on 43 | * @return resulting audio channel 44 | */ 45 | static Channels voiceEnhance(const Channels & c); 46 | }; 47 | 48 | #endif /* EQUALIZER_H_ */ 49 | -------------------------------------------------------------------------------- /src/Frequency.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Frequency.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 14.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Frequency filters 8 | */ 9 | 10 | #include 11 | 12 | 13 | #include "Frequency.h" 14 | #include "Log.h" 15 | #include "Wave.h" 16 | 17 | double Frequency::kernelF(double i,double f,double M) 18 | { 19 | if(i==M/2) 20 | return kernel0(f); 21 | 22 | 23 | return sin(2*M_PI*f*(i-M/2))/(i-M/2) 24 | *(0.42-0.5*cos(2*M_PI*i/M)+0.08*cos(4*M_PI*i/M)); 25 | } 26 | 27 | double Frequency::kernel0(double f) 28 | { 29 | return 2*M_PI*f; 30 | } 31 | 32 | 33 | Channel Frequency::convolution(const Channel &a,const Channel &kernel) 34 | { 35 | Channel target(a); 36 | int m2=kernel.size()/2; 37 | for(unsigned x=0;xf) 50 | width=f; 51 | 52 | f=f/a.samplerate(); 53 | 54 | float BW=width/a.samplerate(); 55 | int N=4/BW; 56 | if(N==0) 57 | N=1; 58 | return N; 59 | } 60 | 61 | Channels Frequency::split(const Channel & a,float f,float width,bool fade) 62 | { 63 | if(width>f) 64 | width=f; 65 | LOG(logDEBUG) << "Frequency split at " << f << "Hz " 66 | << " width "<< width << "Hz" << std::endl; 67 | // http://www.dspguide.com/ch16/2.htm 68 | 69 | unsigned N=windowSize(a,f,width); 70 | 71 | f=f/a.samplerate(); 72 | 73 | LOG(logDEBUG) << "Convolution window size " << N << std::endl; 74 | 75 | Channel kernel(a.samplerate(),N); 76 | double sum=0; 77 | for(unsigned i=0;i cutoff,float width,bool fade) 134 | { 135 | Channels target(cutoff.size()+1); 136 | unsigned i; 137 | for(i=0;i 4 | * @version 1.0 5 | * @date 14.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Frequency filters 8 | */ 9 | 10 | 11 | #ifndef FREQUENCY_H_ 12 | #define FREQUENCY_H_ 13 | 14 | #include "Channel.h" 15 | 16 | /** 17 | * @brief Frequency filter class 18 | */ 19 | class Frequency 20 | { 21 | private: 22 | static Channel convolution(const Channel &a,const Channel &kernel); 23 | static double kernelF(double i,double f,double M); 24 | static double kernel0(double f); 25 | static unsigned windowSize(const Channel &a,float cutoff,float width=1000); 26 | public: 27 | /** 28 | * Split the given channel in a high-frequency and low-frequency part 29 | * @param a given channel 30 | * @param cutoff frequency 31 | * @param width transition bandwidth 32 | * @param fade mute unfiltered start and end and fade in and out 33 | * @return two channels with lower and higher frequency part 34 | */ 35 | static Channels split(const Channel &a,float cutoff,float width=1000,bool fade=false); 36 | 37 | /** 38 | * Band filter a given channel with respect to cutoff frequencies 39 | * @param a given channel 40 | * @param cutoff frequency vector (strictly ascending frequencies!) 41 | * @param width transition bandwidth 42 | * @param fade mute unfiltered start and end and fade in and out 43 | * @return band filtered channels 44 | */ 45 | static Channels split(Channel a,std::vector cutoff,float width=1000,bool fade=false); 46 | }; 47 | 48 | #endif /* FREQUENCY_H_ */ 49 | -------------------------------------------------------------------------------- /src/Log.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Log.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Efficient logging stream 8 | * 9 | * Inspired by http://stackoverflow.com/questions/524524/creating-an-ostream 10 | * and http://www.drdobbs.com/article/print?articleId=201804215&siteSectionName=cpp 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "Log.h" 19 | 20 | std::ostream *Log::stdOutput=&std::cout; 21 | std::ostream *Log::errOutput=&std::cerr; 22 | TLogLevel Log::logLevel=logINFO; 23 | std::clock_t Log::clockStart=clock(); 24 | bool Log::showFunction=false; 25 | bool Log::showRuntime=false; 26 | 27 | std::ostream & Log::Get(std::string file,int line,TLogLevel level) 28 | { 29 | std::ostream *o; 30 | 31 | if(level<=logERROR) 32 | o=errOutput; 33 | else 34 | o=stdOutput; 35 | 36 | if(showRuntime) 37 | (*o) << std::setw(7) << std::setprecision(2) << std::fixed 38 | << int(clock()/double(CLOCKS_PER_SEC)*100)/100. << "\t"; 39 | if(showFunction) 40 | { 41 | char number[20]; 42 | sprintf(number,"%d",line); 43 | std::string target=file.substr(7)+":"+number; 44 | if(target.size()<30) 45 | target.insert(target.size(), 30-target.size(),' '); 46 | 47 | 48 | (*o) << std::setw(25) << target << "\t"; 49 | } 50 | return *o; 51 | } 52 | -------------------------------------------------------------------------------- /src/Log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Log.h 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Efficient logging stream 8 | * 9 | * Inspired by http://stackoverflow.com/questions/524524/creating-an-ostream 10 | * and http://www.drdobbs.com/article/print?articleId=201804215&siteSectionName=cpp 11 | * 12 | */ 13 | 14 | #ifndef LOG_H_ 15 | #define LOG_H_ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | /** 22 | * @def LOG(level) 23 | * @brief A macro for efficient creation of logging stream 24 | * 25 | * Use this macro for logging information that is only computed when 26 | * logging is activiated at given level: It expands to an if-statement 27 | * that computes the right hand side only if the logging level is reached. 28 | * 29 | * Example: 30 | * LOG(logDEBUG) << "test information " << expensiveFunction << std::endl; 31 | * expands to 32 | * if(logDEBUG > LOG::getLoglevel()) 33 | * ; // do nothing 34 | * else 35 | * LOG::GET(__FILE__,__LINE__,logDEBUG) << "test information " ... 36 | */ 37 | 38 | #define LOG(level) \ 39 | if (level > Log::getLoglevel()) ; \ 40 | else Log::Get(__FILE__,__LINE__,level) 41 | 42 | /** 43 | * Logging Levels 44 | */ 45 | enum TLogLevel {logFATAL, logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4}; 46 | 47 | /** 48 | * @brief Logging class to specify output format and level. Use LOG(level) for logging 49 | */ 50 | class Log 51 | { 52 | private: 53 | static std::ostream *stdOutput; 54 | static std::ostream *errOutput; 55 | static TLogLevel logLevel; 56 | static std::clock_t clockStart; 57 | static bool showFunction; 58 | static bool showRuntime; 59 | 60 | public: 61 | /** 62 | * Return suitable stream for logging level and write configured prefix 63 | * @param file Source file 64 | * @param line Source line 65 | * @param level Logging Level 66 | * @return output stream for logging 67 | */ 68 | static std::ostream & Get(std::string file, int line,TLogLevel level = logINFO); 69 | 70 | /** 71 | * Set both error and standard logging output stream 72 | * @param o output stream to use 73 | */ 74 | static void setOutput(std::ostream & o) { stdOutput=&o; errOutput=&o; } 75 | 76 | /** 77 | * Set standard output stream for logging 78 | * @param o output stream to use 79 | */ 80 | static void setStandardOutput(std::ostream & o) { stdOutput=&o; } 81 | 82 | /** 83 | * Set error output stream for logging 84 | * @param o output stream to use 85 | */ 86 | static void setErrorOutput(std::ostream & o) { errOutput=&o; } 87 | 88 | /** 89 | * Set logging level to use 90 | * @param level logging level 91 | */ 92 | static void setLoglevel(TLogLevel level) { logLevel=level; } 93 | 94 | /** 95 | * Should source file be displayed while logging 96 | * @param show source file 97 | */ 98 | static void setShowFunction(bool show) { showFunction=show; } 99 | 100 | /** 101 | * Should the running time be displayed while logging 102 | * @param show logging time 103 | */ 104 | static void setShowRuntime(bool show) { showRuntime=show; } 105 | 106 | /** 107 | * request current logging level 108 | * @return current logging level 109 | */ 110 | static TLogLevel getLoglevel() { return logLevel; } 111 | }; 112 | 113 | #endif /* LOG_H_ */ 114 | -------------------------------------------------------------------------------- /src/Maximizer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Maximizer.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Amplification and normalization 8 | */ 9 | 10 | #include 11 | 12 | #include "Maximizer.h" 13 | #include "Log.h" 14 | 15 | double Maximizer::expander(float c,float factor,int order) // Sigmoid-Funktion 16 | { 17 | switch(order) 18 | { 19 | case 1: 20 | return factor*c*32000/(32000.+factor*fabs(c)); 21 | case 2: 22 | return factor*c*32000/sqrt(sqr(32000.)+sqr(factor)*sqr(c)); 23 | case 4: 24 | return factor*c*32000/sqrt(sqrt(sqr(sqr(32000.))+sqr(sqr(factor*c)) )); 25 | case 8: 26 | return factor*c*32000/sqrt(sqrt(sqrt(sqr(sqr(sqr(32000.)))+sqr(sqr(sqr(factor*c))) ))); 27 | default: 28 | return factor*c*32000/pow(pow(32000.,order)+pow(factor*c,order),1./order); 29 | } 30 | } 31 | 32 | double Maximizer::expanderDenoiser(float c,float factor,float minlevel,int order) // Sigmoid-Funktion mit mehrfacher Nullstelle an 0 33 | { 34 | float cs=sqr(c); 35 | 36 | switch(order) 37 | { 38 | case 1: 39 | return factor*c*32000/(32000.+factor*fabs(c))*cs/(sqr(minlevel)+cs); 40 | case 2: 41 | return factor*c*32000/sqrt(sqr(32000.)+sqr(factor)*cs)*cs/(sqr(minlevel)+cs); 42 | case 4: 43 | return factor*c*32000/sqrt(sqrt(sqr(sqr(32000.))+sqr(sqr(factor)*cs) ))*cs/(sqr(minlevel)+cs); 44 | case 8: 45 | return factor*c*32000/sqrt(sqrt(sqrt(sqr(sqr(sqr(32000.)))+sqr(sqr(sqr(factor)*cs)) )))*cs/(sqr(minlevel)+cs); 46 | default: 47 | return factor*c*32000/pow(pow(32000.,order)+pow(factor*c,order),1./order)*cs/(sqr(minlevel)+cs); 48 | } 49 | } 50 | 51 | 52 | void Maximizer::amplify(Channel &c,float factor,int order) 53 | { 54 | for(unsigned i=0;isamplerate) 64 | samplerate=c[i].samplerate(); 65 | 66 | for(unsigned i=0;ilength) 72 | length=c[i].size(); 73 | 74 | for(unsigned j=0;jmax) 110 | max=e; 111 | } 112 | LOG(logINFO) << "Normalization factor: " << level/max << std::endl; 113 | if(max!=level) 114 | { 115 | float factor=level/max; 116 | 117 | for(unsigned i=0;imax) 128 | max=fabs(c[i]); 129 | 130 | if(max!=level) 131 | { 132 | float factor=level/max; 133 | for(unsigned i=0;i 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Amplification and normalization 8 | */ 9 | 10 | #ifndef MAXIMIZER_H_ 11 | #define MAXIMIZER_H_ 12 | 13 | #include "Channel.h" 14 | 15 | /** 16 | * @brief Amplification with constant factor and soft clipping by sigmoid function 17 | * 18 | * This is the result of the factor filter: 19 | * @image html factor-result.png 20 | * @image latex factor-result.png "Result of factors 2 and 4" width=10cm 21 | * 22 | * This is the result of the normalize filter: 23 | * @image html normalize-result.png 24 | * @image latex normalize-result.png "Result of normalization" width=10cm 25 | */ 26 | class Maximizer 27 | { 28 | public: 29 | /** 30 | * Multiplication of signal by constant factor and soft limiting by 31 | * sigmoid function: 32 | * @f[ v(x):=\frac{c\cdot u(x)}{\sqrt[n]{1+\left|\frac{c\cdot u(x)}{32000}\right|^n}} @f] 33 | * @param channel audio segment to be multiplied 34 | * @param factor factor 35 | * @param order of sigmoid function 36 | */ 37 | static void amplify(Channel &channel,float factor,int order=4); 38 | 39 | /** 40 | * Multiplication of signal by constant factor and soft limiting by 41 | * sigmoid function: 42 | * @f[ v(x):=\frac{c\cdot u(x)}{\sqrt[n]{1+\left|\frac{c\cdot u(x)}{32000}\right|^n}} @f] 43 | * @param channels audio segments to be multiplied 44 | * @param factor factor 45 | * @param order of sigmoid function 46 | */ 47 | static void amplify(Channels &channels,float factor,int order=4); 48 | 49 | 50 | /** 51 | * Multiplication of signal by constant factor and soft limiting by 52 | * sigmoid multiplied with quadratic root function: 53 | * @f[ v(x):=\frac{c\cdot u(x)}{\sqrt[n]{1+\left|\frac{c\cdot u(x)}{32000}\right|^n}} \cdot \frac{\left(u(x)\right)^2}{\left(u(x)\right)^2+\varrho^2} @f] 54 | * @param channel audio segment to be multiplied 55 | * @param factor factor 56 | * @param minlevel noise voltage level 57 | * @param order of sigmoid function 58 | */ 59 | static void amplifyDenoise(Channel &channel,float factor,float minlevel,int order=4); 60 | 61 | /** 62 | * Multiplication of signal by constant factor and soft limiting by 63 | * sigmoid multiplied with quadratic root function: 64 | * @f[ v(x):=\frac{c\cdot u(x)}{\sqrt[n]{1+\left|\frac{c\cdot u(x)}{32000}\right|^n}} \cdot \frac{\left(u(x)\right)^2}{\left(u(x)\right)^2+\varrho^2} @f] 65 | * @param channels audio segments to be multiplied 66 | * @param factor factor 67 | * @param minlevel noise voltage level 68 | * @param order of sigmoid function 69 | */ 70 | static void amplifyDenoise(Channels &channels,float factor,float minlevel,int order=4); 71 | 72 | /** 73 | * Normalize the maximum absolute value to given level. 74 | * @param channel audio segment to be normalized 75 | * @param level target voltage level 76 | */ 77 | static void normalize(Channel &channel,float level=32767.); 78 | 79 | /** 80 | * Normalize the maximum absolute value to given level. 81 | * @param channels audio segments to be normalized 82 | * @param level target voltage level 83 | */ 84 | static void normalize(Channels &channels,float level=32767.); 85 | 86 | private: 87 | static double expander(float c,float factor,int order); 88 | static double expanderDenoiser(float c,float factor,float minlevel,int order); 89 | 90 | template 91 | static T sqr(const T&a){return a*a;} 92 | }; 93 | 94 | #endif /* MAXIMIZER_H_ */ 95 | -------------------------------------------------------------------------------- /src/Merge.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Merge.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 11.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Merging of audio segments either with overlap or fading 8 | */ 9 | 10 | #include "Merge.h" 11 | #include "Log.h" 12 | 13 | Channels Merge::overlap(Channels &a,Channels &b,float sec) 14 | { 15 | unsigned samplerate=unifySamplerate(a,b); 16 | unsigned overlap=sec*samplerate; 17 | //Channels target(a); 18 | if(a.size()==0) 19 | return b; 20 | if(b.size()==0) 21 | return a; 22 | 23 | LOG(logDEBUG) << "Size a: " << a[0].size() << " Size b: " << b[0].size() << " Overlap: " << overlap << std::endl; 24 | 25 | if(a[0].size()+b[0].size()b[i].size()) 89 | offset=a[i].size()-b[i].size(); 90 | for(unsigned j=0;jfreq) 110 | freq=a[i].samplerate(); 111 | 112 | for(unsigned i=0;ifreq) 114 | freq=b[i].samplerate(); 115 | 116 | for(unsigned i=0;i 4 | * @version 1.0 5 | * @date 11.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Merging of audio segments either with overlap or fading 8 | */ 9 | 10 | 11 | #ifndef MERGE_H_ 12 | #define MERGE_H_ 13 | 14 | #include "Channel.h" 15 | 16 | /** 17 | * @brief Merging of sound data segments (overlapping or fading) 18 | */ 19 | class Merge 20 | { 21 | public: 22 | /** 23 | * Render the overlap (i.e. both on full volume) of two sound data segments 24 | * @param a prior sound data segment 25 | * @param b later sound data segment 26 | * @param sec seconds of overlap 27 | * @return resulting overlapped sound data segment 28 | */ 29 | static Channels overlap(Channels &a,Channels &b,float sec); 30 | 31 | /** 32 | * Render the fading (i.e. with de- and increasing volume) of two sound 33 | * data segments 34 | * @param a prior sound data segment 35 | * @param b later sound data segment 36 | * @param sec seconds of fading 37 | * @return resulting faded sound data segment 38 | */ 39 | static Channels fade(Channels &a,Channels &b,float sec); 40 | 41 | /** 42 | * Render two channel segments in parallel 43 | * @param a first sound data segment 44 | * @param b second sound data segment 45 | * @return resulting faded sound data segment 46 | */ 47 | static Channels parallel(Channels &a,Channels &b); 48 | 49 | private: 50 | static int unifySamplerate(Channels &a,Channels &b); 51 | 52 | }; 53 | 54 | #endif /* MERGE_H_ */ 55 | -------------------------------------------------------------------------------- /src/MonoMix.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MonoMix.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 12.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Mono mix-down 8 | */ 9 | 10 | #include "Log.h" 11 | #include "MonoMix.h" 12 | 13 | MonoMix::MonoMix() : target(1) 14 | { 15 | } 16 | 17 | void MonoMix::mix(Channel &c,float factor) 18 | { 19 | if(target[0].size()==0 || c.samplerate()>target[0].samplerate()) 20 | { 21 | target[0]=target[0].resampleTo(c.samplerate()); 22 | LOG(logDEBUG) << "Target samplerate set to " << c.samplerate() << std::endl; 23 | } 24 | if(c.samplerate() 4 | * @version 1.0 5 | * @date 12.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Mono mix-down 8 | */ 9 | 10 | #ifndef MONOMIX_H_ 11 | #define MONOMIX_H_ 12 | 13 | #include "Channel.h" 14 | 15 | /** 16 | * @brief Create mono mix-down 17 | */ 18 | class MonoMix 19 | { 20 | private: 21 | Channels target; 22 | public: 23 | MonoMix(); 24 | 25 | /** 26 | * Mix one channel into the mix-down 27 | * @param c Channel 28 | * @param factor intensity of rendering 29 | */ 30 | void mix(Channel &c,float factor=1.0); 31 | 32 | /** 33 | * Mix several channels into the mix-down 34 | * @param c Channels 35 | */ 36 | void mix(Channels &c); 37 | 38 | /** 39 | * Return current mono mix-down 40 | * @return mix-down Channel 41 | */ 42 | Channels & getTarget() { return target; } 43 | }; 44 | 45 | #endif /* MONOMIX_H_ */ 46 | -------------------------------------------------------------------------------- /src/OspacMain.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file OspacMain.h 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 5.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Command line interface 8 | */ 9 | 10 | #ifndef OSPACMAIN_H_ 11 | #define OSPACMAIN_H_ 12 | 13 | #include 14 | #include 15 | 16 | #include "Channel.h" 17 | #include "Encode.h" 18 | #include "SelectiveLeveler.h" 19 | 20 | /** 21 | * @brief Main program class for dealing with command line options 22 | * 23 | * This class represents the command line interface of ospac, 24 | * and defines standard values for its settings. 25 | */ 26 | class OspacMain 27 | { 28 | protected: 29 | /** 30 | * Vector of all command line arguments 31 | */ 32 | std::vector arg; 33 | 34 | /** 35 | * List of all valid command line tokens 36 | */ 37 | static std::string options[]; 38 | 39 | /** 40 | * Tests the given parameter if it is an options by checking with options 41 | * list 42 | * @param s parameter string to check 43 | * @return true if an option was detected 44 | */ 45 | bool isOption(std::string &s); 46 | 47 | /** 48 | * Downmix mode for voice channels. 49 | */ 50 | enum MixMode { SPATIAL, STEREO, MONO, MULTI , MaxMixMode}; 51 | 52 | /** 53 | * Argument mode what kind of acoustic data is to be processed. 54 | */ 55 | enum ArgMode { VOICE, MIX, RAW, MaxArgMode}; 56 | 57 | /** 58 | * Transition mode between acoustic data segments 59 | */ 60 | enum TransitionMode { NONE, OVERLAP, FADE, PARALLEL, MaxTransitionMode}; 61 | 62 | /** 63 | * Downmix mode for current acoustic data segment 64 | */ 65 | MixMode mixMode; 66 | 67 | /** 68 | * Kind of acoustic data in this acoustic data segment 69 | */ 70 | ArgMode argMode; 71 | 72 | /** 73 | * Transition mode from last acoustic data segment 74 | */ 75 | TransitionMode transitionMode; 76 | 77 | /** 78 | * Transition time from last acoustic data segment 79 | */ 80 | float transitionSeconds; 81 | 82 | /** 83 | * Transition mode to next acoustic data segment 84 | */ 85 | TransitionMode nextTransitionMode; 86 | 87 | /** 88 | * Transition time to next acoustic data segment 89 | */ 90 | float nextTransitionSeconds; 91 | 92 | /** 93 | * Current factor for maximizer 94 | */ 95 | float maximizer; 96 | 97 | /** 98 | * Should current segment be normalized 99 | */ 100 | bool normalizer; 101 | 102 | /** 103 | * Should the current segment be levelled 104 | */ 105 | bool leveler; 106 | 107 | /** 108 | * Leveling target energy 109 | */ 110 | float levelTarget; 111 | 112 | /** 113 | * Shall channels be joined for leveling analysis? 114 | */ 115 | SelectiveLeveler::ChannelMode levelChannelMode; 116 | 117 | /** 118 | * Should the current segment be cross-gated 119 | */ 120 | bool xGate; 121 | 122 | /** 123 | * Should the current segment be filtered by the experimental cross-filter 124 | */ 125 | bool xFilter; 126 | 127 | /** 128 | * Should the current segment apply the all but silence-filter 129 | */ 130 | bool noise; 131 | 132 | /** 133 | * Should the current segment apply the skip-filter 134 | */ 135 | bool skip; 136 | 137 | /** 138 | * Should the current segment trim silence from start and end 139 | */ 140 | bool trim; 141 | 142 | /** 143 | * Silence detection for skipping filter 144 | */ 145 | float skipSilence; 146 | 147 | /** 148 | * Skip order (1 for all, 0.5 for sqrt(time) skip) 149 | */ 150 | float skipOrder; 151 | 152 | /** 153 | * Skip target length (0 for disabled, 0); 339 | 340 | virtual ~OspacMain(); 341 | 342 | /** 343 | * Run the application and do all actions that were requested 344 | * my the option given. 345 | * @return 0 in case of success, 1 in case of error. 346 | */ 347 | int run(void); 348 | 349 | private: 350 | /** 351 | * target cache 352 | */ 353 | Channels target; 354 | }; 355 | 356 | #endif /* OSPACMAIN_H_ */ 357 | -------------------------------------------------------------------------------- /src/Physics.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Physics.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Conversion of physical quantities 8 | */ 9 | 10 | #include "Physics.h" 11 | -------------------------------------------------------------------------------- /src/Physics.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Physics.h 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Conversion of physical quantities 8 | */ 9 | 10 | #ifndef PHYSICS_H_ 11 | #define PHYSICS_H_ 12 | 13 | /** 14 | * Speed of sound in air (at room temperature) 15 | */ 16 | const float v_Schall=343.2; // m/s 17 | 18 | /** 19 | * @brief Conversion of physical quantities 20 | */ 21 | class Physics 22 | { 23 | public: 24 | /** 25 | * Convert sound seconds to meter distance 26 | * @param s seconds 27 | * @return meter distance 28 | */ 29 | static double secToMeter(double s) { return s*v_Schall; } 30 | 31 | /** 32 | * Convert meter distance to sound seconds 33 | * @param m meter distance 34 | * @return sound seconds 35 | */ 36 | static double meterToSec(double m) { return m/v_Schall; } 37 | }; 38 | 39 | #endif /* PHYSICS_H_ */ 40 | -------------------------------------------------------------------------------- /src/Plot.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Plot.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 6.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Simple plots of audio channels 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | 14 | #include "Plot.h" 15 | 16 | std::vector > Plot::histogramm(const Channel &channel,unsigned sizeX,unsigned sizeY,unsigned size) 17 | { 18 | std::vector > map=std::vector >(sizeX); 19 | for(unsigned x=0;x(sizeY); 22 | for(unsigned y=0;y=sizeY) 41 | value=sizeY-1; 42 | int newLast=value; 43 | if(value>=last) 44 | { 45 | for(;value>=last;value--) 46 | { 47 | unsigned v=(map[x][value]++); 48 | if(v>maxv) 49 | maxv=v; 50 | } 51 | } else 52 | { 53 | for(;value<=last;value++) 54 | { 55 | unsigned v=(map[x][value]++); 56 | if(v>maxv) 57 | maxv=v; 58 | } 59 | } 60 | last=newLast; 61 | 62 | } 63 | for(unsigned x=0;x > image=std::vector >(sizeX); 85 | for(unsigned x=0;x(totalY); 87 | 88 | unsigned size=0; 89 | for(unsigned c=0;csize) 91 | size=channels[c].size(); 92 | 93 | for(unsigned c=0;c > hist=histogramm(channels[c],sizeX,sizeY,size); 96 | for(unsigned x=0;x0) 101 | v=v/2+128; 102 | image[x][(c+1)*sizeY-y]=255-v; 103 | } 104 | } 105 | std::ofstream out(name.c_str()); 106 | out << "P2" << std::endl; 107 | out << sizeX << " " << totalY << std::endl; 108 | out << 255 << std::endl; 109 | for(unsigned y=0;y > imageR=std::vector >(sizeX); 131 | std::vector > imageG=std::vector >(sizeX); 132 | std::vector > imageB=std::vector >(sizeX); 133 | for(unsigned x=0;x(totalY); 136 | imageG[x]=std::vector(totalY); 137 | imageB[x]=std::vector(totalY); 138 | } 139 | 140 | unsigned size=0; 141 | unsigned samplerate=channels[0].samplerate(); 142 | for(unsigned c=0;csize) 144 | size=channels[c].size(); 145 | float timelength=size/(samplerate+1e-10); 146 | 147 | unsigned back=255-7; 148 | for(unsigned c=0;c > hist=histogramm(channels[c],sizeX,sizeY,size); 151 | for(unsigned x=0;x0) 157 | { 158 | imageR[x][y+c*sizeY]=64-v/4; 159 | imageG[x][y+c*sizeY]=65-(v+1)/4; 160 | imageB[x][y+c*sizeY]=64+((v*3)/4);//127+v/4; 161 | } else 162 | { 163 | if(x!=0 && x!=sizeX-1 && y!=0 && y!=sizeY-1) 164 | { 165 | imageR[x][y+c*sizeY]=back; 166 | imageG[x][y+c*sizeY]=back; 167 | imageB[x][y+c*sizeY]=back; 168 | } else 169 | { 170 | imageR[x][y+c*sizeY]=128+64; 171 | imageG[x][y+c*sizeY]=128+64; 172 | imageB[x][y+c*sizeY]=128+64; 173 | } 174 | 175 | } 176 | } 177 | } 178 | unsigned secondsG=back-16; 179 | unsigned secondsG10=secondsG-16; 180 | unsigned minutesG=secondsG10-16; 181 | unsigned minutesG10=minutesG-16; 182 | unsigned hoursG=minutesG10-16; 183 | if(timelength/3600 4 | * @version 1.0 5 | * @date 6.3.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Simple plots of audio channels 8 | */ 9 | 10 | #ifndef PLOT_H_ 11 | #define PLOT_H_ 12 | 13 | #include 14 | 15 | #include "Channel.h" 16 | 17 | /** 18 | * @brief Simple plots of audio channels 19 | */ 20 | class Plot 21 | { 22 | private: 23 | static std::vector > histogramm(const Channel &channel,unsigned sizeX,unsigned sizeY,unsigned size=0); 24 | public: 25 | /** 26 | * Create PGM plot of audio channels 27 | * @param channels the channels to plot 28 | * @param name target file name 29 | * @param sizeX width 30 | * @param sizeY height per channel 31 | */ 32 | static void createPGMPlot(const Channels &channels, std::string name,unsigned sizeX=1280, unsigned sizeY=251); 33 | 34 | /** 35 | * Create PGM plot of an audio channel 36 | * @param channel the channel to plot 37 | * @param name target file name 38 | * @param sizeX width 39 | * @param sizeY height 40 | */ 41 | static void createPGMPlot(const Channel &channel, std::string name,unsigned sizeX=1280, unsigned sizeY=251); 42 | 43 | /** 44 | * Create PPM plot of audio channels 45 | * @param channels the channels to plot 46 | * @param name target file name 47 | * @param sizeX width 48 | * @param sizeY height per channel 49 | */ 50 | static void createPPMPlot(const Channels &channels, std::string name,unsigned sizeX=1280, unsigned sizeY=251); 51 | 52 | /** 53 | * Create PPM plot of an audio channel 54 | * @param channel the channel to plot 55 | * @param name target file name 56 | * @param sizeX width 57 | * @param sizeY height 58 | */ 59 | static void createPPMPlot(const Channel &channel, std::string name,unsigned sizeX=1280, unsigned sizeY=251); 60 | 61 | }; 62 | 63 | #endif /* PLOT_H_ */ 64 | -------------------------------------------------------------------------------- /src/SelectiveLeveler.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SelectiveLeveler.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Selective Leveler working on windowed l2 energy of signal 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "SelectiveLeveler.h" 14 | #include "Log.h" 15 | #include "Wave.h" 16 | 17 | 18 | void SelectiveLeveler::level(Channels &aChannels,float targetL2,double windowSec,float minFraction,float silentFraction,float forwardWindowSec,float backWindowSec) 19 | { 20 | for(unsigned i=0;ifloat(c.size())/c.samplerate()/4) 48 | windowSec=float(c.size())/c.samplerate()/4; 49 | const unsigned window=windowSec*c.samplerate(); 50 | const unsigned forwardWindow=forwardWindowSec*c.samplerate(); 51 | const unsigned backWindow=backWindowSec*c.samplerate(); 52 | 53 | Channel factors(c); 54 | Channel factors2(c); 55 | double l2=0; 56 | 57 | for(unsigned j=0;jmaxL2) 64 | maxL2=factors[i]; 65 | l2+=sqr(c[i+window/2])-sqr(c[i-window/2]); 66 | if(l2<0) 67 | l2=0; 68 | } 69 | 70 | // float maxL2save=maxL2; 71 | 72 | for(unsigned i=0;i=factors[i]) 85 | { 86 | sum+=c[i]*c[i]; 87 | count++; 88 | } 89 | } 90 | for(unsigned i=window/2;i=factors[i-window/2]) 93 | { 94 | sum-=c[i-window/2]*c[i-window/2]; 95 | count--; 96 | } 97 | if(c[i+window/2]>=factors[i+window/2]) 98 | { 99 | sum+=c[i+window/2]*c[i+window/2]; 100 | count++; 101 | } 102 | if(count<1) 103 | count=1; 104 | if(sum<0) 105 | sum=0; 106 | 107 | factors2[i]=sqrt(sum/count); 108 | if(factors2[i]>maxL2) 109 | maxL2=factors2[i]; 110 | 111 | } 112 | 113 | /*factors2=factors; 114 | maxL2=maxL2save;*/ 115 | 116 | float minLevel=maxL2*minFraction; 117 | float silentLevel=maxL2*silentFraction; 118 | 119 | 120 | 121 | // Wave::save("levels.wav",factors); 122 | 123 | LOG(logINFO) << "Maximum windowed L2 energy: " << maxL2 << std::endl; 124 | LOG(logINFO) << "Level minimum : " << minLevel << std::endl; 125 | LOG(logINFO) << "Level silence : " << silentLevel << std::endl; 126 | 127 | int c0=0,c1=0,c2=0,o=0; 128 | 129 | for(unsigned i=window/2;i32000) 146 | { 147 | factors2[i]=32000/fabs(c[i]); 148 | o++; 149 | } 150 | } 151 | for(unsigned i=0;ifactors2[i]) 178 | f=factors2[i]; 179 | 180 | movingF=(65535*movingF+f)/65536; 181 | 182 | if(movingFf*tolerance) 185 | movingF*=0.999;//movingF=f*tolerance; 186 | if(fabs(movingF*c[i])>32000) 187 | movingF=fabs(32000./c[i]); 188 | c[i]*=movingF;//f; 189 | //if((i%500)==0) 190 | // out << double(i)/c.samplerate() << "\t" << factors[i]<< "\t" << f << "\t" << movingF<< std::endl; 191 | /*if((i%c.samplerate())==0) 192 | { 193 | LOG(logDEBUG) << i/c.samplerate() << " " << factors[i] << " " << (factorSum/windowcount) << " " << factorSum << " " << windowcount << std::endl; 194 | }*/ 195 | if((int)i-(int)backWindow>=0) 196 | { 197 | factorSum-=factors2[i-backWindow]; 198 | windowcount--; 199 | if(factorSum<0) 200 | factorSum=0; 201 | if(windowcount<1) 202 | windowcount=1; 203 | 204 | } 205 | if(i+forwardWindowb.samplerate()) 216 | b=b.resampleTo(a.samplerate()); 217 | if(a.samplerate()a.size()) 222 | size=b.size(); 223 | 224 | if(size==0) 225 | return; 226 | 227 | if(windowSec>float(size)/a.samplerate()/4) 228 | windowSec=float(size)/a.samplerate()/4; 229 | const unsigned window=windowSec*a.samplerate(); 230 | const unsigned forwardWindow=forwardWindowSec*a.samplerate(); 231 | const unsigned backWindow=backWindowSec*a.samplerate(); 232 | 233 | Channel factors(a.samplerate(),size); 234 | double l2=0; 235 | 236 | for(unsigned j=0;jmaxL2) 243 | maxL2=factors[i]; 244 | l2+=sqr(a[i+window/2])+sqr(b[i+window/2])-sqr(a[i-window/2])-sqr(b[i-window/2]); 245 | if(l2<0) 246 | l2=0; 247 | } 248 | float minLevel=maxL2*minFraction; 249 | float silentLevel=maxL2*silentFraction; 250 | 251 | // Wave::save("levels.wav",factors); 252 | 253 | LOG(logINFO) << "Maximum windowed L2 energy: " << maxL2 << std::endl; 254 | LOG(logINFO) << "Level minimum : " << minLevel << std::endl; 255 | LOG(logINFO) << "Level silence : " << silentLevel << std::endl; 256 | 257 | int c0=0,c1=0,c2=0,o=0; 258 | 259 | for(unsigned i=window/2;i32000 || fabs(factors[i]*b[i])>32000) 276 | { 277 | factors[i]=min(32000/fabs(a[i]),32000/fabs(b[i])); 278 | o++; 279 | } 280 | 281 | } 282 | for(unsigned i=0;ifactors[i]) 304 | f=factors[i]; 305 | a[i]*=f; 306 | b[i]*=f; 307 | /*if((i%c.samplerate())==0) 308 | { 309 | LOG(logDEBUG) << i/c.samplerate() << " " << factors[i] << " " << (factorSum/windowcount) << " " << factorSum << " " << windowcount << std::endl; 310 | }*/ 311 | if((int)i-(int)backWindow>=0) 312 | { 313 | factorSum-=factors[i-backWindow]; 314 | windowcount--; 315 | if(factorSum<0) 316 | factorSum=0; 317 | } 318 | if(i+forwardWindowsamplerate) 350 | samplerate=c[i].samplerate(); 351 | if(c[i].size()>size) 352 | size=c[i].size(); 353 | } 354 | if(size==0) 355 | return; 356 | 357 | for(unsigned i=0;ifloat(size)/samplerate/4) 364 | windowSec=float(size)/samplerate/4; 365 | const unsigned window=windowSec*samplerate; 366 | const unsigned forwardWindow=forwardWindowSec*samplerate; 367 | const unsigned backWindow=backWindowSec*samplerate; 368 | 369 | Channel factors(samplerate,size); 370 | double l2=0; 371 | 372 | for(unsigned j=0;jmaxL2) 380 | maxL2=factors[i]; 381 | for(unsigned k=0;k32000) 415 | { 416 | factors[i]=32000/fabs(c[k][i]); 417 | o++; 418 | } 419 | 420 | } 421 | for(unsigned i=0;ifactors[i]) 443 | f=factors[i]; 444 | for(unsigned k=0;k=0) 451 | { 452 | factorSum-=factors[i-backWindow]; 453 | windowcount--; 454 | if(factorSum<0) 455 | factorSum=0; 456 | } 457 | if(i+forwardWindow 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Selective Leveler working on windowed l2 energy of signal 8 | */ 9 | 10 | #ifndef SELECTIVELEVELER_H_ 11 | #define SELECTIVELEVELER_H_ 12 | 13 | #include "Channel.h" 14 | 15 | /** 16 | * @brief Selective Leveling by windowed average l2 energy 17 | * Contains experimental code for constant leveling in tolerance area 18 | * @image html leveler-result.png 19 | * @image latex leveler-result.png "Result of selective leveler" width=10cm 20 | */ 21 | class SelectiveLeveler 22 | { 23 | public: 24 | /** 25 | * Mode selection which channels are joint for leveling, with 26 | * SINGLE all channels are treated seperately, with STEREO every two 27 | * channels are treated together, and with MULTI all channels are joined. 28 | */ 29 | enum ChannelMode { SINGLE, STEREO, MULTI }; 30 | 31 | /** 32 | * Compute windowed average l2 energy. If the energy is below silent 33 | * fraction, the signal is muted. If the energy is between silent fraction 34 | * to minFraction compared to the maximal windows l2 energy it is linearily 35 | * damped. The actual damping factor is windowed by forward and backwards 36 | * window interval. 37 | * @param aChannels channels to do the individual leveling on 38 | * @param targetL2 target average l2 energy 39 | * @param windowSec window size in seconds for l2 average energy 40 | * @param minFraction fraction compared to l2 maximal value assumed signal 41 | * @param silentFraction fraction compared to l2 maximal value assumed silence 42 | * @param forwardWindowSec average forward part of window for factor application 43 | * @param backWindowSec average backward part of window for factor application 44 | */ 45 | static void level(Channels &aChannels,float targetL2,double windowSec,float minFraction,float silentFraction,float forwardWindowSec,float backWindowSec); 46 | 47 | /** 48 | * Compute windowed average l2 energy. If the energy is below silent 49 | * fraction, the signal is muted. If the energy is between silent fraction 50 | * to minFraction compared to the maximal windows l2 energy it is linearily 51 | * damped. The actual damping factor is windowed by forward and backwards 52 | * window interval. 53 | * @param aChannel channel to do the individual leveling on 54 | * @param targetL2 target average l2 energy 55 | * @param windowSec window size in seconds for l2 average energy 56 | * @param minFraction fraction compared to l2 maximal value assumed signal 57 | * @param silentFraction fraction compared to l2 maximal value assumed silence 58 | * @param forwardWindowSec average forward part of window for factor application 59 | * @param backWindowSec average backward part of window for factor application 60 | */ 61 | static void level(Channel &aChannel,float targetL2,double windowSec,float minFraction,float silentFraction,float forwardWindowSec,float backWindowSec); 62 | 63 | /** 64 | * Compute windowed average l2 energy. If the energy is below silent 65 | * fraction, the signal is muted. If the energy is between silent fraction 66 | * to minFraction compared to the maximal windows l2 energy it is linearily 67 | * damped. The actual damping factor is windowed by forward and backwards 68 | * window interval. This function each considers two channels for analysis. 69 | * @param aChannels channels to do the individual leveling on 70 | * @param targetL2 target average l2 energy 71 | * @param windowSec window size in seconds for l2 average energy 72 | * @param minFraction fraction compared to l2 maximal value assumed signal 73 | * @param silentFraction fraction compared to l2 maximal value assumed silence 74 | * @param forwardWindowSec average forward part of window for factor application 75 | * @param backWindowSec average backward part of window for factor application 76 | */ 77 | static void levelStereo(Channels &aChannels,float targetL2,double windowSec,float minFraction,float silentFraction,float forwardWindowSec,float backWindowSec); 78 | 79 | /** 80 | * Compute windowed average l2 energy. If the energy is below silent 81 | * fraction, the signal is muted. If the energy is between silent fraction 82 | * to minFraction compared to the maximal windows l2 energy it is linearily 83 | * damped. The actual damping factor is windowed by forward and backwards 84 | * window interval. This function each considers two channels for analysis. 85 | * @param aChannel left channel to do the joint leveling on 86 | * @param bChannel right channel to do the joint leveling on 87 | * @param targetL2 target average l2 energy 88 | * @param windowSec window size in seconds for l2 average energy 89 | * @param minFraction fraction compared to l2 maximal value assumed signal 90 | * @param silentFraction fraction compared to l2 maximal value assumed silence 91 | * @param forwardWindowSec average forward part of window for factor application 92 | * @param backWindowSec average backward part of window for factor application 93 | */ 94 | static void levelStereo(Channel &aChannel,Channel &bChannel,float targetL2,double windowSec,float minFraction,float silentFraction,float forwardWindowSec,float backWindowSec); 95 | 96 | /** 97 | * Compute windowed average l2 energy. If the energy is below silent 98 | * fraction, the signal is muted. If the energy is between silent fraction 99 | * to minFraction compared to the maximal windows l2 energy it is linearily 100 | * damped. The actual damping factor is windowed by forward and backwards 101 | * window interval. This function each considers two channels for analysis. 102 | * @param aChannels channels to do the individual leveling on 103 | * @param mode if and how channels are joined (SINGLE, STEREO, MULTI) 104 | * @param targetL2 target average l2 energy 105 | * @param windowSec window size in seconds for l2 average energy 106 | * @param minFraction fraction compared to l2 maximal value assumed signal 107 | * @param silentFraction fraction compared to l2 maximal value assumed silence 108 | * @param forwardWindowSec average forward part of window for factor application 109 | * @param backWindowSec average backward part of window for factor application 110 | */ 111 | static void level(Channels &aChannels,ChannelMode mode,float targetL2,double windowSec,float minFraction,float silentFraction,float forwardWindowSec,float backWindowSec); 112 | 113 | 114 | private: 115 | template 116 | static T sqr(const T&a){return a*a;} 117 | template 118 | static T min(const T&a,const T&b){return a 4 | * @version 1.0 5 | * @date 18.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Skip silence 8 | */ 9 | 10 | #include 11 | 12 | #include "Skip.h" 13 | #include "Log.h" 14 | 15 | float Skip::silence(Channels & a,float level,float minsec,float mintransition,float reductionOrder) 16 | { 17 | if(a.size()==0) 18 | return 0; 19 | if(reductionOrder>1) 20 | { 21 | LOG(logWARNING) << "Reduction reduction order to maximum 1" << std::endl; 22 | reductionOrder=1; 23 | } 24 | 25 | 26 | unsigned skip=0; 27 | unsigned len=unifiedLength(a); 28 | unsigned samplerate=unifiedSamplerate(a); 29 | 30 | mintransition*=samplerate; 31 | unsigned mintransition_u=(int)mintransition; 32 | 33 | float max=0; 34 | 35 | for(unsigned i=0;imax) 41 | max=sum; 42 | } 43 | max/=a.size(); 44 | 45 | level*=max; 46 | 47 | unsigned mincount=minsec*samplerate; 48 | 49 | for(unsigned i=0;i+skipmincount+mintransition_u) 62 | { 63 | float delta; 64 | 65 | if(i*1./samplerate>0.1) 66 | delta=float(d-mincount)/samplerate; 67 | else 68 | delta=float(d)/samplerate; 69 | float ndelta=pow(delta+1,reductionOrder)-1; 70 | if(ndelta>delta) 71 | ndelta=delta; 72 | LOG(logDEBUG) << "Found silence at " << double(i)/samplerate 73 | << " (" << double(i+skip)/samplerate << ")" 74 | << " for " << double(d)/samplerate << "s " 75 | << " cutting " << double(ndelta) << "s" 76 | << std::endl; 77 | 78 | ndelta*=samplerate; 79 | ndelta=(int)ndelta; 80 | int nskip=skip+ndelta; 81 | LOG(logDEBUG) << "From " << skip << " to " << nskip << std::endl; 82 | 83 | int transition=d-ndelta; 84 | //if(transition=0;d--,i++) 111 | for(unsigned c=0;c=1) 130 | return 0; 131 | 132 | unsigned samplerate=unifiedSamplerate(a),len=unifiedLength(a); 133 | 134 | float size=float(len)/samplerate; 135 | float targetCut=size*(1-targetFraction); 136 | 137 | float step=0.5; 138 | float state=1; 139 | Channels work; 140 | float cut; 141 | int iteration=0; 142 | float cSilenceLevel,cMinsec,cMintransition,cReductionOrder; 143 | 144 | do { 145 | work=a; 146 | cSilenceLevel=pow(state,0.6)*silenceLevel; 147 | cMinsec=minsec/state; 148 | cMintransition=mintransition/state; 149 | cReductionOrder=(reductionOrder*3+1*state)/(state+3); 150 | iteration++; 151 | LOG(logINFO) << "Target skip search "<0.01 && iteration<100); 165 | a=work; 166 | return cut; 167 | } 168 | 169 | 170 | float Skip::trim(Channels &a,float level) 171 | { 172 | if(a.size()==0) 173 | return 0; 174 | 175 | unsigned len=unifiedLength(a); 176 | unsigned samplerate=unifiedSamplerate(a); 177 | 178 | float max=0; 179 | 180 | for(unsigned i=0;imax) 186 | max=sum; 187 | } 188 | 189 | level*=max; 190 | 191 | unsigned start; 192 | 193 | for(start=0;startlevel) 199 | break; 200 | } 201 | 202 | unsigned end; 203 | 204 | for(end=len-1;end>=start;end--) 205 | { 206 | float sum=0; 207 | for(unsigned c=0;clevel) 210 | break; 211 | } 212 | LOG(logDEBUG) << "Start: " << start << " End: "<minsec/2) 241 | transition=minsec/2; 242 | 243 | minsec*=samplerate; 244 | unsigned minsec_u=(unsigned)minsec; 245 | 246 | transition*=samplerate; 247 | unsigned transition_u=(unsigned)transition; 248 | 249 | float max=0; 250 | 251 | for(unsigned i=0;imax) 257 | max=sum; 258 | } 259 | max/=a.size(); 260 | 261 | level*=max; 262 | 263 | unsigned lastend=0; 264 | 265 | for(unsigned i=0;ilevel && (d+int(i+skip))=minsec) 292 | { 293 | skip+=d; 294 | 295 | lastend=i+s; 296 | 297 | if(i>transition_u) 298 | for(unsigned j=0;j 4 | * @version 1.0 5 | * @date 18.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Skip silence 8 | * 9 | */ 10 | 11 | #ifndef SKIP_H_ 12 | #define SKIP_H_ 13 | 14 | #include "Channel.h" 15 | 16 | /** 17 | * @brief Skip silence 18 | * @image html skip-result.png 19 | * @image latex skip-result.png "Result of standard skip filter" width=10cm 20 | */ 21 | class Skip 22 | { 23 | public: 24 | /** 25 | * Skip silence in channels if absolute sum of voltages are below 26 | * silence level fraction compared to maximum level for longer than 27 | * minsec seconds. Shorten the period by the time to the reduction order. 28 | * The transition period is in the middle of silence for a maximum time 29 | * of maxtransition seconds. 30 | * @param channels Channels where silence is to be skipped 31 | * @param silenceLevel fraction compared to maximum what is considered silence 32 | * @param minsec minimum time of silence before skipping is considered 33 | * @param mintransition minimum time of transition 34 | * @param reductionOrder reduction by time to the reduction order 35 | * @return total seconds that were skipped 36 | */ 37 | static float silence(Channels &channels,float silenceLevel=0.01,float minsec=0.5,float mintransition=0.05,float reductionOrder=0.75); 38 | 39 | /** 40 | * Skip silence in channels to decrease length to the fraction given, 41 | * if absolute sum of voltages are below 42 | * silence level fraction compared to maximum level for longer than 43 | * minsec seconds. Shorten the period by the time to the reduction order. 44 | * The transition period is in the middle of silence for a maximum time 45 | * of maxtransition seconds. 46 | * @param channels Channels where silence is to be skipped 47 | * @param targetFraction target length fraction 48 | * @param silenceLevel start fraction compared to maximum what is considered silence 49 | * @param minsec start minimum time of silence before skipping is considered 50 | * @param mintransition start minimum time of transition 51 | * @param reductionOrder reduction by time to the reduction order 52 | * @return total seconds that were skipped 53 | */ 54 | static float silenceTarget(Channels &channels,float targetFraction,float silenceLevel=0.01,float minsec=0.5,float mintransition=0.05,float reductionOrder=0.75); 55 | 56 | /** 57 | * Trim silence in channels if absolute sum of voltages are below 58 | * silence level fraction compared to maximum level only at the beginning 59 | * or the end. 60 | * @param channels Channels where silence is to be skipped 61 | * @param silenceLevel fraction compared to maximum what is considered silence 62 | * @return total seconds that were skipped 63 | */ 64 | static float trim(Channels &channels,float silenceLevel=0.01); 65 | 66 | /** 67 | * Skip noise in channels if absolute sum of voltages are higher than 68 | * silence level fraction compared to maximum level. 69 | * @param channels Channels where silence is to be skipped 70 | * @param silenceLevel fraction compared to maximum what is considered silence 71 | * @param minsec minimum time of silence before skipping is considered 72 | * @param transition time in seconds 73 | * @return total seconds signal that was skipped 74 | */ 75 | static float noise(Channels &channels,float silenceLevel=0.01,float minsec=0.1,float transition=0.05); 76 | }; 77 | 78 | #endif /* SKIP_H_ */ 79 | -------------------------------------------------------------------------------- /src/StereoMix.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file StereoMix.cpp 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Stereo mix-down 8 | */ 9 | 10 | #include 11 | 12 | #include "StereoMix.h" 13 | #include "Physics.h" 14 | #include "Log.h" 15 | #include "Frequency.h" 16 | 17 | StereoMix::StereoMix() : target(2) 18 | { 19 | } 20 | 21 | // http://www.feilding.net/sfuad/musi3012-01/html/lectures/008_hearing_III.htm 22 | 23 | void StereoMix::mix(Channel &c,float leftFactor,float rightFactor,float leftDistance,float rightDistance) 24 | { 25 | if(target[0].size()==0 || c.samplerate()>target[0].samplerate()) 26 | { 27 | target[0]=target[0].resampleTo(c.samplerate()); 28 | target[1]=target[1].resampleTo(c.samplerate()); 29 | LOG(logDEBUG) << "Target samplerate set to " << c.samplerate() << std::endl; 30 | } 31 | if(c.samplerate()=leftShift) 46 | target[0][i]+=leftFactor*c[i-leftShift]; 47 | if(i>=rightShift) 48 | target[1][i]+=rightFactor*c[i-rightShift]; 49 | } 50 | } 51 | 52 | void StereoMix::mixBanded(Channel &c,float leftFactor,float rightFactor,float leftDistance,float rightDistance) 53 | { 54 | std::vector freqs(3); 55 | freqs[0]=500; 56 | freqs[1]=2000; 57 | freqs[2]=7000; 58 | 59 | Channels b=Frequency::split(c,freqs); 60 | 61 | if(target[0].size()==0 || c.samplerate()>target[0].samplerate()) 62 | { 63 | target[0]=target[0].resampleTo(c.samplerate()); 64 | target[1]=target[1].resampleTo(c.samplerate()); 65 | LOG(logDEBUG) << "Target samplerate set to " << c.samplerate() << std::endl; 66 | } 67 | if(c.samplerate()=leftShift) 92 | { 93 | if(leftShift==0) 94 | { 95 | target[0][i]+=leftFactor*c[i-leftShift]; 96 | } else 97 | { 98 | target[0][i]+=l0*b[0][i-leftShift] 99 | +l1*b[1][i-leftShift] 100 | +l2*b[2][i-leftShift] 101 | +l3*b[3][i-leftShift]; 102 | } 103 | } 104 | if(i>=rightShift) 105 | { 106 | if(rightShift==0) 107 | { 108 | target[1][i]+=rightFactor*c[i-rightShift]; 109 | } else 110 | { 111 | target[1][i]+=r0*b[0][i-rightShift] 112 | +r1*b[1][i-rightShift] 113 | +r2*b[2][i-rightShift] 114 | +r3*b[3][i-rightShift]; 115 | 116 | } 117 | } 118 | } 119 | } 120 | 121 | 122 | void StereoMix::mix(Channels &c,float maxfactor,bool spatial,float maxdelay, bool banded) 123 | { 124 | if(c.size()==0) 125 | return; 126 | 127 | const int sets=3; 128 | const int vars=4; 129 | 130 | static const double settingsSpatial[sets][vars]={ 131 | {maxfactor,1.0/maxfactor,maxdelay,0}, 132 | {1.0,1.0,maxdelay/2,maxdelay/2}, 133 | {1.0/maxfactor,maxfactor,0,maxdelay}}; 134 | 135 | static const double settingsStereo[sets][vars]={ 136 | {maxfactor,1.0/maxfactor,0.00,0}, 137 | {1.0,1.0,0.0000,0.0000}, 138 | {1.0/maxfactor,maxfactor,0,0.00}}; 139 | 140 | double settings[sets][vars]; 141 | 142 | if(spatial) 143 | for(int i=0;i 4 | * @version 1.0 5 | * @date 7.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Stereo mix-down 8 | */ 9 | 10 | #ifndef STEREOMIX_H_ 11 | #define STEREOMIX_H_ 12 | 13 | #include "Channel.h" 14 | 15 | /** 16 | * @brief Create stereo mixdown of channels 17 | */ 18 | class StereoMix 19 | { 20 | private: 21 | Channels target; 22 | public: 23 | /** 24 | * Create initial target 25 | */ 26 | StereoMix(); 27 | 28 | /** 29 | * Mix single Channel into the target 30 | * @param c Channel 31 | * @param leftFactor Rendering intensity left target channel 32 | * @param rightFactor Rendering intensity right target channel 33 | * @param leftDistance Distance in meter from left channel 34 | * @param rightDistance Distance in meter from right channel 35 | */ 36 | void mix(Channel &c,float leftFactor,float rightFactor,float leftDistance,float rightDistance); 37 | 38 | /** 39 | * Mix single Channel into target with frequency dependence 40 | * @param c Channel 41 | * @param leftFactor Rendering intensity left target channel 42 | * @param rightFactor Rendering intensity right target channel 43 | * @param leftDistance Distance in meter from left channel 44 | * @param rightDistance Distance in meter from right channel 45 | */ 46 | void mixBanded(Channel &c,float leftFactor,float rightFactor,float leftDistance,float rightDistance); 47 | 48 | /** 49 | * Mix channels into target using equidistant positions 50 | * @param c Channels 51 | * @param maxfactor Maximum factor for spatial volume change 52 | * @param spatial Use spatial delay? 53 | * @param maxdelay Maximum interaural delay 54 | * @param banded Use frequency dependence? 55 | */ 56 | void mix(Channels &c,float maxfactor=0.9,bool spatial=false,float maxdelay=0.03, bool banded=false); 57 | 58 | /** 59 | * Request current stereo mixdown 60 | * @return stereo mixdown 61 | */ 62 | Channels & getTarget() { return target; } 63 | }; 64 | 65 | #endif /* STEREOMIX_H_ */ 66 | -------------------------------------------------------------------------------- /src/Wave.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Wave.h 3 | * @author Sebastian Ritterbusch 4 | * @version 1.0 5 | * @date 5.2.2016 6 | * @copyright MIT License (see LICENSE file) 7 | * @brief Wave file management via libsndfile 8 | */ 9 | 10 | 11 | #ifndef WAVE_H_ 12 | #define WAVE_H_ 13 | 14 | // #define HAS_FFMPEG 15 | 16 | #include 17 | #include 18 | 19 | #include "Channel.h" 20 | 21 | /** 22 | * @brief Wave-file loading and saving via libsndfile 23 | */ 24 | class Wave 25 | { 26 | public: 27 | /** 28 | * Load a wave file from the file system using libsndfile. 29 | * @param name file system name of file 30 | * @param skip skip seconds 31 | * @param length maximum length to load (after skip) 32 | * @return Channels containing the wave channels 33 | */ 34 | static Channels load(const std::string &,float skip=0,float length=1e+99); 35 | 36 | /** 37 | * Load a wave file from the file system using libsndfile, avoiding 38 | * copy operations. 39 | * @param name file system name of file 40 | * @param target Channel object to save the data in 41 | * @param skip skip seconds 42 | * @param length maximum length to load (after skip) 43 | * @return Channels references containing the wave channels 44 | */ 45 | static Channels & load(const std::string &,Channels & target,float skip=0,float length=1e+99); 46 | 47 | #ifdef HAS_FFMPEG 48 | /** 49 | * Load a wave file from the file system using libavcodec. 50 | * @param name file system name of file 51 | * @param skip skip seconds 52 | * @param length maximum length to load (after skip) 53 | * @return Channels containing the wave channels 54 | */ 55 | static Channels loadFfmpeg(const std::string &,float skip=0,float length=1e+99); 56 | 57 | /** 58 | * Load a wave file from the file system using libavcodec, avoiding 59 | * copy operations. 60 | * @param name file system name of file 61 | * @param target Channel object to save the data in 62 | * @param skip skip seconds 63 | * @param length maximum length to load (after skip) 64 | * @return Channels references containing the wave channels 65 | */ 66 | static Channels & loadFfmpeg(const std::string &,Channels & target,float skip=0,float length=1e+99); 67 | 68 | /** 69 | * Save a multi-channel aac file to the file system using libffmpeg. 70 | * The sample data is assumed to be in the range of [-32767,32767] and 71 | * entries beyond are limited to the range. 72 | * @param name file system name of file 73 | * @param channels channels to be saved. 74 | * @param bitrate bitrate in bits per second 75 | * @return 0 in case of success, 1 in case of error. 76 | */ 77 | static int saveAac(const std::string &,Channels &,int); 78 | 79 | /** 80 | * Save a single-channel aac file to the file system using libffmpeg. 81 | * The sample data is assumed to be in the range of [-32767,32767] and 82 | * entries beyond are limited to the range. 83 | * @param name file system name of file 84 | * @param channel channels to be saved. 85 | * @param bitrate bitrate in bits per second 86 | * @return 0 in case of success, 1 in case of error. 87 | */ 88 | static int saveAac(const std::string &,Channel &,int); 89 | 90 | #endif // HAS_FFMPEG 91 | 92 | /** 93 | * Load a ascii wave file from the file system using libsndfile, avoiding 94 | * copy operations. This routine rescales the input to [-32000,32000]. 95 | * @param name file system name of file 96 | * @param samplerate sample rate of file 97 | * @param target Channel object to save the data in 98 | * @param skip skip seconds 99 | * @param length maximum length to load (after skip) 100 | * @return Channels references containing the wave channels 101 | */ 102 | static Channels & loadAscii(const std::string &name,int samplerate,Channels & target,float skip=0,float length=1e+99); 103 | 104 | /** 105 | * Save a multi-channel wave file to the file system using libsndfile. 106 | * The sample data is assumed to be in the range of [-32767,32767] and 107 | * entries beyond are limited to the range. 108 | * @param name file system name of file 109 | * @param channels channels to be saved. 110 | * @return 0 in case of success, 1 in case of error. 111 | */ 112 | static int save(const std::string &,Channels &); 113 | 114 | /** 115 | * Save a single-channel wave file to the file system using libsndfile. 116 | * The sample data is assumed to be in the range of [-32767,32767] and 117 | * entries beyond are limited to the range. 118 | * @param name file system name of file 119 | * @param channel channels to be saved. 120 | * @return 0 in case of success, 1 in case of error. 121 | */ 122 | static int save(const std::string &,Channel &); 123 | }; 124 | 125 | #endif /* WAVE_H_ */ 126 | -------------------------------------------------------------------------------- /test/1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/1.wav -------------------------------------------------------------------------------- /test/2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/2.wav -------------------------------------------------------------------------------- /test/3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/3.wav -------------------------------------------------------------------------------- /test/4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/4.wav -------------------------------------------------------------------------------- /test/5.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/5.wav -------------------------------------------------------------------------------- /test/dynamic-test.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/dynamic-test.wav -------------------------------------------------------------------------------- /test/intro-cooking-math-4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/intro-cooking-math-4.wav -------------------------------------------------------------------------------- /test/intro-reverb-only.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/intro-reverb-only.wav -------------------------------------------------------------------------------- /test/volume-changes.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sritterbusch/ospac/76e0035b85ac1958ada6045c37b65030b087c543/test/volume-changes.wav -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | 1.0.1 -------------------------------------------------------------------------------- /zsh_completion.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Completion script for OSPAC (https://github.com/sritterbusch/ospac) 4 | 5 | _ospac(){ 6 | local -a opts 7 | opts=( 8 | "*: :_files" 9 | '*--quality[Quality from 0-low, 1-standard, 2-high, 3-insane]: :(0 1 2 3)' 10 | '*--mp3[Write final output to the file using external lame]: :_files' 11 | '*--output[Write final output to the file in netbpm format]: :_files' 12 | '*--plot[Write final output to the file in wave format]: :_files' 13 | '*--ogg[Write final output to the file using external oggenc]: :_files' 14 | '*--highpass[ Highpass above f Hertz, sharpness t Hertz]: :' 15 | '*--leveler[Enable selective leveler]' 16 | '*--no-factor[Disable channel multiplier]' 17 | '*--no-normalize[Disable final normalization]' 18 | '*--eqvoice[Attenuate voice frequency bands]' 19 | '*--band-pass[ Bandpass from l to h Hertz, sharpness t Hertz]: :' 20 | '*--target[Set average target L2 energy for leveler (3000)]: :' 21 | "*--level-mode[Shall channels be joined for leveling]: :('single' 'stereo' 'multi')" 22 | '*--normalize[Normalize final mix]' 23 | '*--no-leveler[Disable selective leveler]' 24 | '*--low-pass[ Lowpass below f Hertz, sharpness t Hertz]: :' 25 | '*--analyze[Analyze frequency band components]' 26 | '*--factor[Multiply channels by the given factor with sigmoid limiter (1.25)]: :' 27 | '*--no-eqvoice[Do not attenuate frequency bands]' 28 | '*--verbosity[Set the verbosity level]: :(0 1 2 3 4 5 6)' 29 | '*--help[Display the help text]' 30 | '*--no-xfilter[Disable crosstalk filter]' 31 | '*--no-xgate[Disable crosstalk gate]' 32 | '*--xfilter[Enable experimental crosstalk filter]' 33 | '*--xgate[Enable robust crosstalk gate]' 34 | '*--parallel[Render previous and next segment in parallel]' 35 | '*--fade[Fading transition over the given number of seconds]: :' 36 | '*--overlap[Overlapping transition over the given number of seconds]: :' 37 | '*--right[Load right channel of wave file (if stereo)]: :_files' 38 | '*--ascii[ Load ascii wave file with sample rate s]: :' 39 | '*--left[Load left channel of wave file (if stereo)]: :_files' 40 | '*--to-mono[Load mono-mixdown of wave file (if stereo)]: :_files' 41 | '*--album[Set the album tag if this exists in the output]: :' 42 | '*--category[Set the category tag if this exists in the output]: :' 43 | '*--title[Set the title tag if this exists in the output]: :' 44 | '*--comment[Set the comment tag if this exists in the output]: :' 45 | '*--artist[Set the artist tag if this exists in the output]: :' 46 | '*--year[Set the year tag if this exists in the output]: :' 47 | '*--episode[Set the episode tag if this exists in the output]: :' 48 | '*--image[Set the image tag if this exists in the output]: :_files' 49 | '*--trim[Trim audio from start and end]' 50 | '*--skip-level[Fraction of maximum level considered silence (0.01)]: :' 51 | '*--no-skip[Do not skip any content]' 52 | '*--soft[Soft skip silent passages over 0.5s length]' 53 | '*--noise[Skip all but silence]' 54 | '*--skip-target[Target length fraction for iteration]: :' 55 | '*--skip-order[Order of reduction (0-1, default: 0.75)]: :' 56 | '*--mix[Start pre-mixed channel segment]' 57 | '*--voice[Start of voice channel segment (default)]' 58 | '*--raw[Start of raw channel segment]' 59 | '*--set-stereo-level[Set maximum channel volume factor (0.9)]: :' 60 | '*--spatial[Create 3d stereo with interaural delays]' 61 | '*--mono[Create mono output]' 62 | '*--multi[Create multi channel output]' 63 | '*--set-stereo-spatial[Set maximum interaural delay distance (0.03)]: :' 64 | '*--stereo[Create intensity stereo (default)]' 65 | ) 66 | _arguments $opts 67 | } 68 | 69 | compdef _ospac ospac=ospac 70 | 71 | --------------------------------------------------------------------------------