From bd63fafb75857670d25312510db65ca830746ead Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Mon, 14 Jun 2021 20:46:04 +0100 Subject: [PATCH] Version 1.1.1 --- .gitignore | 5 +- 3dti_Anechoic/3dti_Anechoic.jucer | 498 +++++ 3dti_AudioToolkit_Plugin.jucer | 44 +- 3dti_Hearing_Aid_Simulator.jucer | 263 --- .../3dti_Hearing_Aid_Simulator.jucer | 263 +++ 3dti_Hearing_Loss_Simulator.jucer | 280 --- .../3dti_Hearing_Loss_Simulator.jucer | 280 +++ 3dti_Reverb/3dti_Reverb.jucer | 484 +++++ Resources/About.txt | 24 +- Resources/About_Anechoic.txt | 38 + Resources/About_HAS.txt | 33 + Resources/About_HLS.txt | 37 + Resources/About_Reverb.txt | 38 + Source/Binaural/AmbisonicEncoder.cpp | 114 + Source/Binaural/AmbisonicEncoder.h | 41 + Source/Binaural/AnechoicControls.cpp | 14 +- Source/Binaural/AnechoicControls.h | 9 +- Source/Binaural/AnechoicPluginEditor.cpp | 96 + Source/Binaural/AnechoicPluginEditor.h | 77 + Source/Binaural/AnechoicPluginProcessor.cpp | 432 ++++ Source/Binaural/AnechoicPluginProcessor.h | 111 + Source/Binaural/AnechoicProcessor.cpp | 553 ++--- Source/Binaural/AnechoicProcessor.h | 212 +- Source/Binaural/ElevationDial.h | 4 +- Source/Binaural/ReverbControls.cpp | 48 +- Source/Binaural/ReverbControls.h | 10 +- Source/Binaural/ReverbPluginEditor.cpp | 75 + Source/Binaural/ReverbPluginEditor.h | 57 + Source/Binaural/ReverbPluginProcessor.cpp | 297 +++ Source/Binaural/ReverbPluginProcessor.h | 99 + Source/Binaural/ReverbProcessor.cpp | 159 +- Source/Binaural/ReverbProcessor.h | 26 +- Source/Binaural/SourceControls.h | 21 +- Source/Binaural/SourceReverbControls.cpp | 92 + Source/Binaural/SourceReverbControls.h | 87 + ...nEditor.cpp => SpatialisePluginEditor.cpp} | 14 +- ...luginEditor.h => SpatialisePluginEditor.h} | 9 +- ...ssor.cpp => SpatialisePluginProcessor.cpp} | 102 +- ...rocessor.h => SpatialisePluginProcessor.h} | 4 +- Source/Binaural/SpatializerWidget.h | 32 +- Source/Binaural/v1/ReverbControls.cpp | 134 ++ Source/Binaural/v1/ReverbControls.h | 104 + Source/Common/AboutBanner.h | 60 + Source/Common/StackedSliderComponent.h | 1 + .../ChannelSettingsComponent.cpp | 2 +- .../DynamicEQComponent.cpp | 26 +- Source/HearingAidSimulator/PluginEditor.cpp | 44 +- Source/HearingAidSimulator/PluginEditor.h | 15 +- .../HearingAidSimulator/PluginProcessor.cpp | 4 +- .../FrequencySmearingProcessor.cpp | 52 +- .../FrequencySmearingProcessor.h | 22 +- Source/HearingLossSimulator/PluginEditor.cpp | 53 +- Source/HearingLossSimulator/PluginEditor.h | 15 +- .../HearingLossSimulator/PluginProcessor.cpp | 82 +- Source/HearingLossSimulator/PluginProcessor.h | 8 + Source/HearingLossSimulator/Presets.h | 2 +- .../TemporalDistortionComponent.h | 15 +- Source/Utils.h | 23 +- extras/InstallerSetup.iss | 15 +- extras/InstallerSetup.pkgproj | 1850 +++++++++++++++++ libs/3dti_AudioToolkit | 2 +- libs/JUCE | 2 +- 62 files changed, 6305 insertions(+), 1308 deletions(-) create mode 100644 3dti_Anechoic/3dti_Anechoic.jucer delete mode 100644 3dti_Hearing_Aid_Simulator.jucer create mode 100644 3dti_Hearing_Aid_Simulator/3dti_Hearing_Aid_Simulator.jucer delete mode 100644 3dti_Hearing_Loss_Simulator.jucer create mode 100644 3dti_Hearing_Loss_Simulator/3dti_Hearing_Loss_Simulator.jucer create mode 100644 3dti_Reverb/3dti_Reverb.jucer create mode 100644 Resources/About_Anechoic.txt create mode 100644 Resources/About_HAS.txt create mode 100644 Resources/About_HLS.txt create mode 100644 Resources/About_Reverb.txt create mode 100644 Source/Binaural/AmbisonicEncoder.cpp create mode 100644 Source/Binaural/AmbisonicEncoder.h create mode 100644 Source/Binaural/AnechoicPluginEditor.cpp create mode 100644 Source/Binaural/AnechoicPluginEditor.h create mode 100644 Source/Binaural/AnechoicPluginProcessor.cpp create mode 100644 Source/Binaural/AnechoicPluginProcessor.h create mode 100644 Source/Binaural/ReverbPluginEditor.cpp create mode 100644 Source/Binaural/ReverbPluginEditor.h create mode 100644 Source/Binaural/ReverbPluginProcessor.cpp create mode 100644 Source/Binaural/ReverbPluginProcessor.h create mode 100644 Source/Binaural/SourceReverbControls.cpp create mode 100644 Source/Binaural/SourceReverbControls.h rename Source/Binaural/{PluginEditor.cpp => SpatialisePluginEditor.cpp} (91%) rename Source/Binaural/{PluginEditor.h => SpatialisePluginEditor.h} (93%) rename Source/Binaural/{PluginProcessor.cpp => SpatialisePluginProcessor.cpp} (79%) rename Source/Binaural/{PluginProcessor.h => SpatialisePluginProcessor.h} (95%) create mode 100644 Source/Binaural/v1/ReverbControls.cpp create mode 100644 Source/Binaural/v1/ReverbControls.h create mode 100644 Source/Common/AboutBanner.h create mode 100644 extras/InstallerSetup.pkgproj diff --git a/.gitignore b/.gitignore index 5233d58..3d13966 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ builds/ -/JuceLibraryCode +JuceLibraryCode/ +Output/ +Scripts/ .DS_Store +**/.DS_Store diff --git a/3dti_Anechoic/3dti_Anechoic.jucer b/3dti_Anechoic/3dti_Anechoic.jucer new file mode 100644 index 0000000..42445a7 --- /dev/null +++ b/3dti_Anechoic/3dti_Anechoic.jucer @@ -0,0 +1,498 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3dti_AudioToolkit_Plugin.jucer b/3dti_AudioToolkit_Plugin.jucer index 84e7056..59d451e 100644 --- a/3dti_AudioToolkit_Plugin.jucer +++ b/3dti_AudioToolkit_Plugin.jucer @@ -1,15 +1,16 @@ - - + version="1.1.1" companyWebsite="http://3d-tune-in.eu" jucerFormatVersion="1" + pluginManufacturerCode="Dtun" pluginCode="Plug" pluginAUMainType="'aufx'" + displaySplashScreen="1"> + @@ -395,34 +396,37 @@ - - - - - - + + + + + + + vstLegacyFolder="~/SDKs/VST_SDK/VST2_SDK" microphonePermissionNeeded="0" + xcodeValidArchs="i386,x86_64"> - - + + diff --git a/3dti_Hearing_Aid_Simulator.jucer b/3dti_Hearing_Aid_Simulator.jucer deleted file mode 100644 index 70b7191..0000000 --- a/3dti_Hearing_Aid_Simulator.jucer +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/3dti_Hearing_Aid_Simulator/3dti_Hearing_Aid_Simulator.jucer b/3dti_Hearing_Aid_Simulator/3dti_Hearing_Aid_Simulator.jucer new file mode 100644 index 0000000..5578381 --- /dev/null +++ b/3dti_Hearing_Aid_Simulator/3dti_Hearing_Aid_Simulator.jucer @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3dti_Hearing_Loss_Simulator.jucer b/3dti_Hearing_Loss_Simulator.jucer deleted file mode 100644 index 61f3f37..0000000 --- a/3dti_Hearing_Loss_Simulator.jucer +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/3dti_Hearing_Loss_Simulator/3dti_Hearing_Loss_Simulator.jucer b/3dti_Hearing_Loss_Simulator/3dti_Hearing_Loss_Simulator.jucer new file mode 100644 index 0000000..801a339 --- /dev/null +++ b/3dti_Hearing_Loss_Simulator/3dti_Hearing_Loss_Simulator.jucer @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3dti_Reverb/3dti_Reverb.jucer b/3dti_Reverb/3dti_Reverb.jucer new file mode 100644 index 0000000..44b4ec8 --- /dev/null +++ b/3dti_Reverb/3dti_Reverb.jucer @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/About.txt b/Resources/About.txt index 5aae841..0647a3b 100644 --- a/Resources/About.txt +++ b/Resources/About.txt @@ -1,32 +1,38 @@ -3D Tune-In Toolkit VST plugin V1.0.2 +3D TUNE-IN TOOLKIT VST PLUGIN v1.1.0 The 3D Tune-In Toolkit VST plugin was developed by Ragnar Hrafnkelsson, under the supervision of Lorenzo Picinali and Arcadio Reyes-Lecuona, as a VST interface for the 3D Tune-In Toolkit. For technical details, please refer to: -Picinali, L., Hrafnkelsson, R., & Reyes-Lecuona, A. (2019, March). The 3D Tune-In Toolkit VST Binaural Audio Plugin. In Audio Engineering Society Conference: 2019 AES International Conference on Immersive and Interactive Audio. Audio Engineering Society. + Picinali, L., Hrafnkelsson, R., & Reyes-Lecuona, A. (2019, March). The 3D Tune-In + Toolkit VST Binaural Audio Plugin. In Audio Engineering Society + Conference: 2019 AES International Conference on Immersive and Interactive + Audio. Audio Engineering Society. More information about the 3D Tune-In Toolkit can be found in the open-source GitHub repository at https://github.com/3DTune-In/3dti_AudioToolkit/. Technical details about the 3D Tune-In Toolkit spatialiser are described in: -Cuevas-Rodríguez M, Picinali L, González-Toledo D, Garre C, de la Rubia-Cuestas E, Molina-Tanco L and Reyes-Lecuona A. (2019) 3D Tune-In Toolkit: An open-source library for real-time binaural spatialisation. PLOS ONE 14(3): e0211899. https://doi.org/10.1371/journal.pone.0211899 + Cuevas-Rodríguez M, Picinali L, González-Toledo D, Garre C, + de la Rubia-Cuestas E, Molina-Tanco L and Reyes-Lecuona A. (2019) + 3D Tune-In Toolkit: An open-source library for real-time binaural + spatialisation. PLOS ONE 14(3): e0211899. + https://doi.org/10.1371/journal.pone.0211899 More information about the 3D Tune-In project can be found at http://3d-tune-in.eu/. -HRTF files are extracted from the IRCAM LISTEN databased and processed to remove ITDs. -BRIR files were created within the 3D Tune-In project. +HRTF files are extracted from the IRCAM LISTEN databased and processed to remove ITDs. BRIR files were created within the 3D Tune-In project. -Copyright and License +COPYRIGHT AND LICENSE -The 3D Tune-In Toolkit VST plugin, Copyright (c) 2019 Imperial College London and University of Malaga, is distributed as open source under GPLv3. +The 3D Tune-In Toolkit VST plugin, Copyright (c) 2021 Imperial College London and University of Malaga, is distributed as open source under GPLv3. You may use this program to generate 3D sounds without additional restrictions to those imposed by the license of the original audio. You are not compelled to make any mention to this software or the 3D Tune-In project when using or distributing these audio files, but we would highly appreciate if you could kindly acknowledge the use of the 3D Tune-In Toolkit. -Acknowledgements +ACKNOWLEDGEMENTS This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. We would also like to acknowledge Angel Rodríguez-Rivero for his help in testing the first release of this software. -Contact +CONTACT Lorenzo Picinali (l.picinali@imperial.ac.uk) and Arcadio Reyes-Lecuona (areyes@uma.es). diff --git a/Resources/About_Anechoic.txt b/Resources/About_Anechoic.txt new file mode 100644 index 0000000..98edcfc --- /dev/null +++ b/Resources/About_Anechoic.txt @@ -0,0 +1,38 @@ +3DTI ANECHOIC SPATIALISATION v1.1.0 + +The 3DTI Anechoic Spatialisation plugin was developed by Ragnar Hrafnkelsson, under the supervision of Lorenzo Picinali and Arcadio Reyes-Lecuona, as a VST interface for the 3D Tune-In Toolkit. For technical details, please refer to: + + Picinali, L., Hrafnkelsson, R., & Reyes-Lecuona, A. (2019, March). The + 3D Tune-In Toolkit VST Binaural Audio Plugin. In Audio Engineering Society + Conference: 2019 AES International Conference on Immersive and Interactive + Audio. Audio Engineering Society. + +More information about the 3D Tune-In Toolkit can be found in the open-source GitHub repository at https://github.com/3DTune-In/3dti_AudioToolkit/. Technical details about the 3D Tune-In Toolkit spatialiser are described in: + + Cuevas-Rodríguez M, Picinali L, González-Toledo D, Garre C, + de la Rubia-Cuestas E, Molina-Tanco L and Reyes-Lecuona A. (2019) + 3D Tune-In Toolkit: An open-source library for real-time binaural + spatialisation. PLOS ONE 14(3): e0211899. + https://doi.org/10.1371/journal.pone.0211899 + +More information about the 3D Tune-In project can be found at http://3d-tune-in.eu/. + +HRTF files are extracted from the IRCAM LISTEN database and processed to remove ITDs. BRIR files were created within the 3D Tune-In project. + + +COPYRIGHT AND LICENSE + +The 3D Tune-In Toolkit VST plugin, Copyright (c) 2019 Imperial College London and University of Malaga, is distributed as open source under GPLv3. + +You may use this program to generate 3D sounds without additional restrictions to those imposed by the license of the original audio. You are not compelled to make any mention to this software or the 3D Tune-In project when using or distributing these audio files, but we would highly appreciate if you could kindly acknowledge the use of the 3D Tune-In Toolkit. + + +ACKNOWLEDGEMENTS + +This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +We would also like to acknowledge Angel Rodríguez-Rivero for his help in testing the first release of this software. + + +CONTACT + +Lorenzo Picinali (l.picinali@imperial.ac.uk) and Arcadio Reyes-Lecuona (areyes@uma.es). diff --git a/Resources/About_HAS.txt b/Resources/About_HAS.txt new file mode 100644 index 0000000..d90dae2 --- /dev/null +++ b/Resources/About_HAS.txt @@ -0,0 +1,33 @@ +3DTI HEARING AID SIMULATOR v1.1.0 + +The 3DTI Hearing Aid Simulator plugin was developed by Ragnar Hrafnkelsson, under the supervision of Lorenzo Picinali and Arcadio Reyes-Lecuona, as a VST interface for the hearing aid simulation within the 3D Tune-In Toolkit. + +More information about the 3D Tune-In Toolkit can be found in the open-source GitHub repository at https://github.com/3DTune-In/3dti_AudioToolkit/. Technical details about the 3D Tune-In Toolkit spatialiser are described in: + + Cuevas-Rodríguez M, Picinali L, González-Toledo D, Garre C, de la Rubia-Cuestas E, Molina-Tanco L and + Reyes-Lecuona A. (2019) 3D Tune-In Toolkit: An open-source library for real-time binaural spatialisation. + PLOS ONE 14(3): e0211899. https://doi.org/10.1371/journal.pone.0211899 + +More information about the 3D Tune-In project can be found at http://3d-tune-in.eu/. + +Details about the Fig6 hearing aid fitting algorithm can be found here: + + Killion, M. (1994). Fig6.exe software: Hearing aid fitting targets for 40, 65 & 95 dB SPL inputs (Version 1.01D). + Copyright, Mead Killion, Etymotic Research. + + +COPYRIGHT AND LICENSE + +The 3D Tune-In Toolkit VST plugins, Copyright (c) 2020 Imperial College London and University of Malaga, are distributed as open source under GPLv3. + +You may use this program to generate 3D sounds without additional restrictions to those imposed by the license of the original audio. You are not compelled to make any mention to this software or the 3D Tune-In project when using or distributing these audio files, but we would highly appreciate if you could kindly acknowledge the use of the 3D Tune-In Toolkit. + + +ACKNOWLEDGEMENTS + +This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + + +CONTACT + +Lorenzo Picinali (l.picinali@imperial.ac.uk) and Arcadio Reyes-Lecuona (areyes@uma.es). \ No newline at end of file diff --git a/Resources/About_HLS.txt b/Resources/About_HLS.txt new file mode 100644 index 0000000..bfc0a20 --- /dev/null +++ b/Resources/About_HLS.txt @@ -0,0 +1,37 @@ +3DTI HEARING LOSS SIMULATOR v1.1.0 + +The 3DTI Hearing Loss Simulator plugin was developed by Ragnar Hrafnkelsson, under the supervision of Lorenzo Picinali and Arcadio Reyes-Lecuona, as a VST interface for the hearing loss simulation within the 3D Tune-In Toolkit. + +More information about the 3D Tune-In Toolkit can be found in the open-source GitHub repository at https://github.com/3DTune-In/3dti_AudioToolkit/. Technical details about the 3D Tune-In Toolkit spatialiser are described in: + + Cuevas-Rodríguez M, Picinali L, González-Toledo D, Garre C, de la Rubia-Cuestas E, Molina-Tanco L and + Reyes-Lecuona A. (2019) 3D Tune-In Toolkit: An open-source library for real-time binaural spatialisation. + PLOS ONE 14(3): e0211899. https://doi.org/10.1371/journal.pone.0211899 + +More information about the 3D Tune-In project can be found at http://3d-tune-in.eu/. + +More information about the two options implemented for the frequency smearing can be found here: + + (Baer&Moore). Baer, T., & Moore, B. C. (1993). Effects of spectral smearing on the intelligibility of sentences in + noise. The Journal of the Acoustical Society of America, 94(3), 1229-1241. + + (Graf+3DTI) is a re-elaboration of the implementation presented in Badri, R., Siegel, J. H., & Wright, B. A. (2011). + Auditory filter shapes and high-frequency hearing in adults who have impaired speech in noise performance + despite clinically normal audiograms. The Journal of the Acoustical Society of America, 129(2), 852-863. + + +COPYRIGHT AND LICENSE + +The 3D Tune-In Toolkit VST plugins, Copyright (c) 2020 Imperial College London and University of Malaga, are distributed as open source under GPLv3. + +You may use this program to generate 3D sounds without additional restrictions to those imposed by the license of the original audio. You are not compelled to make any mention to this software or the 3D Tune-In project when using or distributing these audio files, but we would highly appreciate if you could kindly acknowledge the use of the 3D Tune-In Toolkit. + + +ACKNOWLEDGEMENTS + +This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + + +CONTACT + +Lorenzo Picinali (l.picinali@imperial.ac.uk) and Arcadio Reyes-Lecuona (areyes@uma.es). \ No newline at end of file diff --git a/Resources/About_Reverb.txt b/Resources/About_Reverb.txt new file mode 100644 index 0000000..d8383e4 --- /dev/null +++ b/Resources/About_Reverb.txt @@ -0,0 +1,38 @@ +3DTI 3D REVERB v1.1.0 + +The 3DTI 3D Reverb plugin was developed by Ragnar Hrafnkelsson, under the supervision of Lorenzo Picinali and Arcadio Reyes-Lecuona, as a VST interface for the 3D Tune-In Toolkit. For technical details, please refer to: + + Picinali, L., Hrafnkelsson, R., & Reyes-Lecuona, A. (2019, March). The + 3D Tune-In Toolkit VST Binaural Audio Plugin. In Audio Engineering Society + Conference: 2019 AES International Conference on Immersive and Interactive + Audio. Audio Engineering Society. + +More information about the 3D Tune-In Toolkit can be found in the open-source GitHub repository at https://github.com/3DTune-In/3dti_AudioToolkit/. Technical details about the 3D Tune-In Toolkit spatialiser are described in: + + Cuevas-Rodríguez M, Picinali L, González-Toledo D, Garre C, + de la Rubia-Cuestas E, Molina-Tanco L and Reyes-Lecuona A. (2019) + 3D Tune-In Toolkit: An open-source library for real-time binaural + spatialisation. PLOS ONE 14(3): e0211899. + https://doi.org/10.1371/journal.pone.0211899 + +More information about the 3D Tune-In project can be found at http://3d-tune-in.eu/. + +HRTF files are extracted from the IRCAM LISTEN database and processed to remove ITDs. BRIR files were created within the 3D Tune-In project. + + +COPYRIGHT AND LICENSE + +The 3D Tune-In Toolkit VST plugin, Copyright (c) 2019 Imperial College London and University of Malaga, is distributed as open source under GPLv3. + +You may use this program to generate 3D sounds without additional restrictions to those imposed by the license of the original audio. You are not compelled to make any mention to this software or the 3D Tune-In project when using or distributing these audio files, but we would highly appreciate if you could kindly acknowledge the use of the 3D Tune-In Toolkit. + + +ACKNOWLEDGEMENTS + +This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +We would also like to acknowledge Angel Rodríguez-Rivero for his help in testing the first release of this software. + + +CONTACT + +Lorenzo Picinali (l.picinali@imperial.ac.uk) and Arcadio Reyes-Lecuona (areyes@uma.es). diff --git a/Source/Binaural/AmbisonicEncoder.cpp b/Source/Binaural/AmbisonicEncoder.cpp new file mode 100644 index 0000000..264af46 --- /dev/null +++ b/Source/Binaural/AmbisonicEncoder.cpp @@ -0,0 +1,114 @@ +/** + * \class AmbisonicEncoder + * + * \brief Declaration of AmbisonicEncoder interface. + * \date November 2020 + * + * \authors Reactify Music LLP: R. Hrafnkelsson || + * Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || + * \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk + * + * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || + * \b Website: http://3d-tune-in.eu/ + * + * \b Copyright: University of Malaga and Imperial College London - 2020 + * + * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. + * + * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + */ + +#include "AmbisonicEncoder.h" + +AmbisonicEncoder::AmbisonicEncoder (Binaural::CCore& core) : mCore (core) +{ +} + +void AmbisonicEncoder::processBlock (const std::vector& sources, AudioBuffer& quadOut) +{ + static constexpr float WScale = 0.707107f; + + quadOut.clear(); + + ///////////////////////////////////////// + // 1-st Order Virtual Ambisonics Encoder + ///////////////////////////////////////// + jassert (quadOut.getNumChannels() == 4); + + // Go through each source + for (auto source : sources) + { + // Check source flags for reverb process + if (! source->IsReverbProcessEnabled()) + continue; + + // Get azimuth, elevation and distance from listener to each source + // We precompute everything, to minimize per-sample computations. + auto sourceTransform = source->GetSourceTransform(); + auto vectorToSource = mCore.GetListener()->GetListenerTransform().GetVectorTo (sourceTransform); + + float sourceElevation = vectorToSource.GetElevationRadians(); + float sinElevationAbs = std::fabs (std::sin (sourceElevation)); // TEST: adding power to W channel to compensate for the lack of Z channel + float sinElevation = std::sin (sourceElevation); + float cosElevation = std::cos (sourceElevation); + + float cosAcosE = 0.0f; + float sinAcosE = 0.0f; + + if (! Common::CMagnitudes::AreSame (0.0f, cosElevation, EPSILON)) + { + float sourceAzimuth = vectorToSource.GetAzimuthRadians(); + float cosAzimuth = std::cos (sourceAzimuth); + float sinAzimuth = std::sin (sourceAzimuth); + cosAcosE = cosAzimuth * cosElevation; + sinAcosE = sinAzimuth * cosElevation; + } + + float sourceDistance = vectorToSource.GetDistance(); + + // Check if the source is in the same position as the listener head. If yes, do not apply spatialization to this source + if (sourceDistance < mCore.GetListener()->GetHeadRadius()) + continue; + + CMonoBuffer sourceBuffer = source->GetBuffer(); + jassert (sourceBuffer.size() > 0); + + if (sourceBuffer.empty()) + continue; + + // Apply Distance Attenuation + float distanceAttenuation_ReverbConstant = mCore.GetMagnitudes().GetReverbDistanceAttenuation(); + + if (source->IsDistanceAttenuationEnabledReverb()) + { + distanceAttenuatorReverb.Process (sourceBuffer, sourceDistance, distanceAttenuation_ReverbConstant, mCore.GetAudioState().bufferSize, mCore.GetAudioState().sampleRate); + } + + auto numSamples = mCore.GetAudioState().bufferSize; + jassert (sourceBuffer.size() >= numSamples + && quadOut.getNumSamples() >= numSamples); + + for (int nSample = 0; nSample < numSamples; nSample++) + { + // Value from the input buffer + float newSample = sourceBuffer[nSample]; + + if (true /* Bi-dimensional. TODO: Support other order types */) + { + // Add partial contribution of this source to each B-format channel + quadOut.getWritePointer (0)[nSample] += newSample * WScale; + quadOut.getWritePointer (1)[nSample] += newSample * cosAcosE; + quadOut.getWritePointer (1)[nSample] += newSample * sinElevationAbs; // Adding power to X channel to compensate for the lack of Z channel + quadOut.getWritePointer (2)[nSample] += newSample * sinAcosE; + } + else // Three-dimiensional + { + // Add partial contribution of this source to each B-format channel + quadOut.getWritePointer (0)[nSample] += newSample * WScale; + quadOut.getWritePointer (1)[nSample] += newSample * cosAcosE; + quadOut.getWritePointer (2)[nSample] += newSample * sinElevation; + quadOut.getWritePointer (3)[nSample] += newSample * sinAcosE; + } + } + } +} diff --git a/Source/Binaural/AmbisonicEncoder.h b/Source/Binaural/AmbisonicEncoder.h new file mode 100644 index 0000000..b7021fc --- /dev/null +++ b/Source/Binaural/AmbisonicEncoder.h @@ -0,0 +1,41 @@ +/** + * \class AmbisonicEncoder + * + * \brief Declaration of AmbisonicEncoder interface. + * \date November 2020 + * + * \authors Reactify Music LLP: R. Hrafnkelsson || + * Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || + * \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk + * + * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || + * \b Website: http://3d-tune-in.eu/ + * + * \b Copyright: University of Malaga and Imperial College London - 2020 + * + * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. + * + * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + */ + +#pragma once + +#include +#include + +using CSingleSourceRef = std::shared_ptr; + +//============================================================================== +class AmbisonicEncoder +{ +public: + //========================================================================== + AmbisonicEncoder (Binaural::CCore& core); + + void processBlock (const std::vector& sources, AudioBuffer& quadOut); + +private: + //========================================================================== + Binaural::CCore& mCore; + Common::CDistanceAttenuator distanceAttenuatorReverb; +}; diff --git a/Source/Binaural/AnechoicControls.cpp b/Source/Binaural/AnechoicControls.cpp index b8d9ddc..49ee77c 100644 --- a/Source/Binaural/AnechoicControls.cpp +++ b/Source/Binaural/AnechoicControls.cpp @@ -11,21 +11,22 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. */ -#include "../Utils.h" +#include "Utils.h" #include "AnechoicControls.h" -AnechoicControls::AnechoicControls(Toolkit3dtiPluginAudioProcessor& processor) - : mProcessor(processor), - mCore(processor.getCore()), +AnechoicControls::AnechoicControls (AnechoicProcessor& processor) + : mCore (processor), distanceAttenuationLabel("Distance Label", "dB attenuation per double distance") { + setOpaque (true); + for ( int i = 0; i < BundledHRTFs.size(); i++ ) { hrtfMenu.addItem( BundledHRTFs[i], i+1 ); // IDs must be non-zero } @@ -69,7 +70,8 @@ AnechoicControls::AnechoicControls(Toolkit3dtiPluginAudioProcessor& processor) // addAndMakeVisible( qualityToggle ); setLabelStyle( distanceAttenuationLabel ); - distanceAttenuationLabel.setJustificationType( Justification::left ); + distanceAttenuationLabel.setFont (Font (13.0f, Font::plain)); + distanceAttenuationLabel.setJustificationType (Justification::right); addAndMakeVisible( distanceAttenuationLabel ); mapParameterToSlider( distanceAttenuationSlider, mCore.sourceDistanceAttenuation ); diff --git a/Source/Binaural/AnechoicControls.h b/Source/Binaural/AnechoicControls.h index dd05f51..0eb0a52 100644 --- a/Source/Binaural/AnechoicControls.h +++ b/Source/Binaural/AnechoicControls.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -21,7 +21,7 @@ #pragma once #include -#include "PluginProcessor.h" +#include "SpatialisePluginProcessor.h" //============================================================================== /* @@ -29,7 +29,7 @@ class AnechoicControls : public Component, public Slider::Listener { public: - AnechoicControls(Toolkit3dtiPluginAudioProcessor& processor); + AnechoicControls (AnechoicProcessor& processor); ~AnechoicControls() {} @@ -37,8 +37,6 @@ class AnechoicControls : public Component, public Slider::Listener void paint (Graphics& g) override { g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); // clear the background - g.setColour (Colours::grey); - g.drawRect (getLocalBounds(), 1); g.setColour(Colours::white); g.setFont(18.0f); g.drawText("Anechoic Path", getLocalBounds().withTrimmedBottom( getLocalBounds().getHeight() - 32 ), @@ -69,7 +67,6 @@ class AnechoicControls : public Component, public Slider::Listener void updateQualitySetting(); void updateDistanceAttenuation(); - Toolkit3dtiPluginAudioProcessor& mProcessor; AnechoicProcessor& mCore; ComboBox hrtfMenu; diff --git a/Source/Binaural/AnechoicPluginEditor.cpp b/Source/Binaural/AnechoicPluginEditor.cpp new file mode 100644 index 0000000..147cf5e --- /dev/null +++ b/Source/Binaural/AnechoicPluginEditor.cpp @@ -0,0 +1,96 @@ +/** +* \class AnechoicPluginProcessorEditor +* +* \brief Declaration of AnechoicPluginProcessorEditor interface. +* \date November 2020 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#include "Utils.h" +#include "AnechoicPluginProcessor.h" +#include "AnechoicPluginEditor.h" + +//============================================================================== +AnechoicPluginProcessorEditor::AnechoicPluginProcessorEditor (AnechoicPluginProcessor& p) + : AudioProcessorEditor (&p) + , processor (p) + , sourceControls (p.getCore()) + , anechoicControls (p.getCore()) + , reverbControls (p) + , spatializerWidget (p.getCore()) +{ + setOpaque (true); + + aboutText.setMultiLine (true); + aboutText.setFont (Font(16.0f, Font::plain)); + aboutText.setText (String::fromUTF8(BinaryData::About_Anechoic_txt, BinaryData::About_Anechoic_txtSize)); + aboutText.setReadOnly (true); + aboutText.setAlpha (0.9f); + aboutText.setVisible (false); + aboutText.setEnabled (false); + aboutText.addMouseListener (this, true); + + toolkitVersionLabel.setFont (Font (15.f, Font::plain)); + toolkitVersionLabel.setText ("Version " + String(JucePlugin_VersionString) + " (3DTI Toolkit v1.4)", dontSendNotification); + + aboutBanner.button.onClick = [this] { aboutText.setVisible(!aboutText.isVisible()); }; + + addAndMakeVisible (aboutBanner); + addAndMakeVisible (sourceControls); + addAndMakeVisible (anechoicControls); + addAndMakeVisible (reverbControls); + addAndMakeVisible (spatializerWidget); + addChildComponent (aboutText); + + setSize (900, 726); + + startTimerHz (30); +} + +AnechoicPluginProcessorEditor::~AnechoicPluginProcessorEditor() +{ + stopTimer(); +} + +//============================================================================== +void AnechoicPluginProcessorEditor::paint (Graphics& g) +{ + g.fillAll (Colour (24,31,34)); +} + +void AnechoicPluginProcessorEditor::paintOverChildren (Graphics& g) +{ + g.setColour (Colours::grey); + auto sourceBounds = anechoicControls.getBoundsInParent(); + auto reverbBounds = reverbControls.getBoundsInParent(); + sourceBounds.setBottom (reverbBounds.getY()); + g.drawRect (sourceBounds); + g.drawRect (reverbBounds); +} + +void AnechoicPluginProcessorEditor::resized() +{ + auto r = getLocalBounds(); + + aboutBanner.setBounds (r.removeFromTop (50)); + + auto controlsBounds = r.removeFromLeft (r.proportionOfWidth (0.33)); + anechoicControls.setBounds (controlsBounds.removeFromTop (252)); + sourceControls.setBounds (controlsBounds.removeFromTop (324)); + reverbControls.setBounds (controlsBounds); + spatializerWidget.setBounds (r); + + aboutText.setBounds(r); +} diff --git a/Source/Binaural/AnechoicPluginEditor.h b/Source/Binaural/AnechoicPluginEditor.h new file mode 100644 index 0000000..75f69d7 --- /dev/null +++ b/Source/Binaural/AnechoicPluginEditor.h @@ -0,0 +1,77 @@ +/** +* \class AnechoicPluginProcessorEditor +* +* \brief Declaration of AnechoicPluginProcessorEditor interface. +* \date June 2019 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#pragma once + +#include +#include "Common/AboutBanner.h" +#include "AnechoicControls.h" +#include "SourceControls.h" +#include "SourceReverbControls.h" +#include "SpatializerWidget.h" +#include "ElevationDial.h" +#include "AnechoicPluginProcessor.h" + +//============================================================================== +/** +*/ +class AnechoicPluginProcessorEditor : public AudioProcessorEditor, public Timer +{ +public: + AnechoicPluginProcessorEditor (AnechoicPluginProcessor&); + ~AnechoicPluginProcessorEditor(); + + //============================================================================ + void paint (Graphics&) override; + void paintOverChildren (Graphics& g) override; + void resized() override; + + void timerCallback() override + { + anechoicControls.updateGui(); + sourceControls.updateGui(); + spatializerWidget.updateGui(); + + bool anechoicEnabled = anechoicControls.bypassToggle.getToggleState(); + anechoicControls.setAlpha(anechoicEnabled + 0.4f); + sourceControls.setAlpha(anechoicEnabled + 0.4f); + } + + void mouseDown (const MouseEvent &e) override { + aboutText.setVisible (false); + }; + +private: + //============================================================================ + AnechoicPluginProcessor& processor; + + AboutBanner aboutBanner; + + SourceControls sourceControls; + AnechoicControls anechoicControls; + ReverbControls reverbControls; + SpatializerWidget spatializerWidget; + + TextEditor aboutText; + Label pluginVersionLabel; + Label toolkitVersionLabel; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnechoicPluginProcessorEditor) +}; diff --git a/Source/Binaural/AnechoicPluginProcessor.cpp b/Source/Binaural/AnechoicPluginProcessor.cpp new file mode 100644 index 0000000..cb939b9 --- /dev/null +++ b/Source/Binaural/AnechoicPluginProcessor.cpp @@ -0,0 +1,432 @@ +/** +* \class AnechoicPluginProcessor +* +* \brief Declaration of AnechoicPluginProcessor interface. +* \date June 2019 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#include "AnechoicPluginProcessor.h" +#include "AnechoicPluginEditor.h" + +static constexpr int kTOOLKIT_BUFFER_SIZE = 512; // TODO(Ragnar): Make variable + +void addBooleanHostParameter(AudioProcessorValueTreeState& treeState, String name, int value) { + const auto bypassValueToText = [](float value) { + return value < 0.5f ? "Off" : "On"; + }; + + const auto bypassTextToValue = [](const String& text) { + if (text == "On") { return 1.0f; } + return 0.0f; + }; + + treeState.createAndAddParameter(name, name, "", NormalisableRange(0.f, 1.f, 1.f), + value, bypassValueToText, bypassTextToValue, false, true, + true, AudioProcessorParameter::genericParameter, true); +} + +//============================================================================== +AnechoicPluginProcessor::AnechoicPluginProcessor() +#ifndef JucePlugin_PreferredChannelConfigurations + : AudioProcessor (BusesProperties() + .withInput ("Input", JUCEApplication::isStandaloneApp() ? AudioChannelSet::stereo() : AudioChannelSet::mono(), true) + .withOutput ("Output", AudioChannelSet::stereo(), true) + .withOutput ("Sidechain", AudioChannelSet::quadraphonic(), true) + ) +#endif + , treeState (*this, nullptr) +{ + mSpatializer.addSoundSource (Common::CVector3(0,1,0)); + + auto position = getCore().getSourcePosition(0); + + treeState.createAndAddParameter("Azimuth", "Azimuth", "", NormalisableRange(0.0f, 359.99f), position.GetAzimuthDegrees(), [](float value) { return String (value, 1); }, nullptr); + treeState.addParameterListener("Azimuth", this); + + treeState.createAndAddParameter("Elevation", "Elevation", "", NormalisableRange(-89.f, 89.f), position.GetElevationDegrees(), [](float value) { return String (value, 0); }, nullptr); + treeState.addParameterListener("Elevation", this); + + treeState.createAndAddParameter("Distance", "Distance", "", NormalisableRange(0.f, 40.f), position.GetDistance(), [](float value) { return String (value, 2); }, nullptr); + treeState.addParameterListener("Distance", this); + + treeState.createAndAddParameter("X", "X", "", NormalisableRange(-40.f, 40.f), position.x, [](float value) { return String (value, 2); }, nullptr); + treeState.addParameterListener("X", this); + + treeState.createAndAddParameter("Y", "Y", "", NormalisableRange(-40.f, 40.f), position.y, [](float value) { return String (value, 2); }, nullptr); + treeState.addParameterListener("Y", this); + + treeState.createAndAddParameter("Z", "Z", "", NormalisableRange(-40.f, 40.f), position.z, [](float value) { return String (value, 2); }, nullptr); + treeState.addParameterListener("Z", this); + + treeState.createAndAddParameter("Source Attenuation", "Src Attenuation", "", getCore().sourceDistanceAttenuation.range, getCore().sourceDistanceAttenuation.get(), nullptr, nullptr); + treeState.addParameterListener("Source Attenuation", this); + + treeState.createAndAddParameter("Reverb Attenuation", "Rev Attenuation", "", getCore().reverbDistanceAttenuation.range, getCore().reverbDistanceAttenuation.get(), nullptr, nullptr); + treeState.addParameterListener("Reverb Attenuation", this); + + addBooleanHostParameter(treeState, "Near Field", getCore().enableNearDistanceEffect); + treeState.addParameterListener("Near Field", this); + + addBooleanHostParameter(treeState, "Far Field", getCore().enableFarDistanceEffect); + treeState.addParameterListener("Far Field", this); + + addBooleanHostParameter(treeState, "Custom Head", getCore().enableCustomizedITD); + treeState.addParameterListener("Custom Head", this); + + treeState.createAndAddParameter("Head Circumference", "Head Circumference", "", NormalisableRange(getCore().headCircumference.getRange().getStart(), getCore().headCircumference.getRange().getEnd()), getCore().headCircumference.get(), [](float value) { return String (value, 0); }, nullptr); + treeState.addParameterListener("Head Circumference", this); + + addBooleanHostParameter(treeState, "Enable Anechoic", true); + treeState.addParameterListener("Enable Anechoic", this); + + addBooleanHostParameter(treeState, "Enable Reverb", true); + treeState.addParameterListener("Enable Reverb", this); + + treeState.createAndAddParameter("HRTF", "HRTF", "", NormalisableRange(0, BundledHRTFs.size()-1), 0, [](float value) { return String (value, 0); }, nullptr); + treeState.addParameterListener("HRTF", this); + + treeState.state = ValueTree("3DTI Anechoic Parameters"); + +#if DEBUG + ERRORHANDLER3DTI.SetVerbosityMode(VERBOSITYMODE_ERRORSANDWARNINGS); + ERRORHANDLER3DTI.SetErrorLogStream(&std::cout, true); +#endif +} + +AnechoicPluginProcessor::~AnechoicPluginProcessor() +{ + stopTimer(); +} + +//============================================================================== +const String AnechoicPluginProcessor::getName() const +{ + return JucePlugin_Name; +} + +bool AnechoicPluginProcessor::acceptsMidi() const +{ + #if JucePlugin_WantsMidiInput + return true; + #else + return false; + #endif +} + +bool AnechoicPluginProcessor::producesMidi() const +{ + #if JucePlugin_ProducesMidiOutput + return true; + #else + return false; + #endif +} + +bool AnechoicPluginProcessor::isMidiEffect() const +{ + #if JucePlugin_IsMidiEffect + return true; + #else + return false; + #endif +} + +double AnechoicPluginProcessor::getTailLengthSeconds() const +{ + return 0.0; +} + +int AnechoicPluginProcessor::getNumPrograms() +{ + return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, + // so this should be at least 1, even if you're not really implementing programs. +} + +int AnechoicPluginProcessor::getCurrentProgram() +{ + return 0; +} + +void AnechoicPluginProcessor::setCurrentProgram (int index) +{ +} + +const String AnechoicPluginProcessor::getProgramName (int index) +{ + return {}; +} + +void AnechoicPluginProcessor::changeProgramName (int index, const String& newName) +{ +} + +//============================================================================== +void AnechoicPluginProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) +{ + const int blockSizeInternal = kTOOLKIT_BUFFER_SIZE; + + // Set up anechoic buffers + inFifoMain.clear(); + inFifoMain.setSize (1, blockSizeInternal + 1); + + outFifoMain.clear(); + outFifoMain.setSize (2, std::max(samplesPerBlock, blockSizeInternal) * 2); + + scratchBufferMain.setSize (2, blockSizeInternal); + + // Set up ambisonic buffers + inFifoBuss.clear(); + inFifoBuss.setSize (4, blockSizeInternal + 1); + + outFifoBuss.clear(); + outFifoBuss.setSize (4, std::max (samplesPerBlock, blockSizeInternal) * 2); + + scratchBufferBuss.setSize (4, blockSizeInternal); + + // Initalise 3dti toolkit + mCore.SetAudioState ({(int)sampleRate, blockSizeInternal}); + + mSpatializer.setup (sampleRate/*,blockSizeInternal*/); + + startTimer (30); +} + +void AnechoicPluginProcessor::releaseResources() { + // When playback stops, you can use this as an opportunity to free up any + // spare memory, etc. +} + +#ifndef JucePlugin_PreferredChannelConfigurations +bool AnechoicPluginProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const +{ + if (layouts.getMainInputChannelSet() != AudioChannelSet::mono() + && layouts.getMainInputChannelSet() != AudioChannelSet::stereo()) + return false; + + if (layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) + return false; + + return true; +} +#endif + +void AnechoicPluginProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) +{ + ScopedNoDenormals noDenormals; + + auto inChannels = getTotalNumInputChannels(); + auto outChannels = getTotalNumOutputChannels(); + + for (auto i = inChannels; i < outChannels; ++i) + buffer.clear(i, 0, buffer.getNumSamples()); + + AudioSampleBuffer mainInput = getBusBuffer (buffer, true, 0); + AudioSampleBuffer sideChain = getBusBuffer (buffer, false, 1); + + AudioBuffer tempMain (1, 1); + + int numSamples = mainInput.getNumSamples(); + int numChannels = mainInput.getNumChannels(); + + const int blockSizeInternal = kTOOLKIT_BUFFER_SIZE; + // Some hosts send buffers of varying sizes so we maintain + // an internal buffer to pass the correct size to the 3dti core + for (int sample = 0; sample < numSamples; ++sample) + { + tempMain.clear(); + + // Sum to mono in 'temp' + for (int channel = 0; channel < numChannels; ++channel) + tempMain.addSample (0, 0, mainInput.getSample (channel, sample)); + + tempMain.applyGain (1.0f / (float)numChannels); + + inFifoMain.addToFifo (tempMain); + + if (inFifoMain.getFreeSpace() == 0) + { + AudioBuffer monoIn (1, blockSizeInternal); + + inFifoMain.readFromFifo (monoIn, blockSizeInternal); + + bool isReady = ! mSpatializer.isLoading.load() + && mCore.GetListener()->GetHRTF()->IsHRTFLoaded(); + + mSpatializer.processBlock (monoIn, scratchBufferMain); + outFifoMain.addToFifo (scratchBufferMain); + + if (isReady) + { + mEncoder.processBlock (mSpatializer.getSources(), scratchBufferBuss); + outFifoBuss.addToFifo (scratchBufferBuss); + } + else + { + outFifoBuss.addSilenceToFifo (blockSizeInternal); + } + } + } + + int numReady = outFifoMain.getNumReady(); + if (numReady < numSamples) + { + int diff = numSamples - numReady; + outFifoMain.addSilenceToFifo (diff); + outFifoBuss.addSilenceToFifo (diff); + + // Update the host latency + int latency = getLatencySamples() + diff; + setLatencySamples (latency); + } + + AudioSampleBuffer mainOutput = getBusBuffer (buffer, false, 0); + + outFifoMain.readFromFifo (mainOutput); + + if (sideChain.getNumChannels() >= outFifoBuss.getNumChannels()) + outFifoBuss.readFromFifo (sideChain); + else + outFifoBuss.removeSamplesFromFifo (numSamples); +} + +//============================================================================== +bool AnechoicPluginProcessor::hasEditor() const +{ + return true; // (change this to false if you choose to not supply an editor) +} + +AudioProcessorEditor* AnechoicPluginProcessor::createEditor() +{ + return new AnechoicPluginProcessorEditor (*this); +} + +//============================================================================== +void AnechoicPluginProcessor::getStateInformation (MemoryBlock& destData) +{ + // You should use this method to store your parameters in the memory block. + // You could do that either as raw data, or use the XML or ValueTree classes + // as intermediaries to make it easy to save and load complex data. +} + +void AnechoicPluginProcessor::setStateInformation (const void* data, int sizeInBytes) +{ + // You should use this method to restore your parameters from this memory block, + // whose contents will have been created by the getStateInformation() call. +} + +void AnechoicPluginProcessor::updateHostParameters() +{ + auto position = getCore().getSourcePosition (0); + + auto sources = getCore().getSources(); + + std::unordered_map parameters = { + {"Azimuth", position.GetAzimuthDegrees()}, + {"Distance", position.GetDistance()}, + {"Elevation", mapElevationToSliderValue(position.GetElevationDegrees())}, + {"X", position.x}, + {"Y", position.y}, + {"Z", position.z}, + {"Source Attenuation", getCore().sourceDistanceAttenuation}, + {"Reverb Attenuation", getCore().reverbDistanceAttenuation}, + {"Near Field", getCore().enableNearDistanceEffect}, + {"Far Field", getCore().enableFarDistanceEffect}, + {"Custom Head", getCore().enableCustomizedITD}, + {"Head Circumference", getCore().headCircumference}, + {"HRFT", getCore().getHrtfIndex() }, + }; + + if (sources.size() > 0) + { + parameters["Enable Anechoic"] = sources.front()->IsAnechoicProcessEnabled(); + parameters["Enable Reverb"] = sources.front()->IsReverbProcessEnabled(); + } + + for ( auto const & parameter : parameters ) { + if ( AudioProcessorParameter* p = treeState.getParameter(parameter.first) ) { + const float newValue = treeState.getParameterRange(parameter.first).convertTo0to1(parameter.second); + + if ( fabs(p->getValue() - newValue) > std::numeric_limits::epsilon() ) + p->setValueNotifyingHost(newValue); + } + } +} + +void AnechoicPluginProcessor::parameterChanged (const String& parameterID, float newValue) +{ + auto sources = getCore().getSources(); + + auto position = getCore().getSourcePosition(0); + + if ( parameterID == "Azimuth" ) { + DBG("Azimuth: " + String(newValue)); + position.SetFromAED( newValue, position.GetElevationDegrees(), position.GetDistance() ); + } else if ( parameterID == "Distance" ) { + position.SetFromAED( position.GetAzimuthDegrees(), position.GetElevationDegrees(), newValue ); + } else if ( parameterID == "Elevation" ) { + position.SetFromAED( position.GetAzimuthDegrees(), mapSliderValueToElevation(newValue), position.GetDistance() ); + } else if ( parameterID == "X" ) { + position.x = newValue; + } else if ( parameterID == "Y" ) { + position.y = newValue; + } else if ( parameterID == "Z" ) { + position.z = newValue; + } else if ( parameterID == "Source Attenuation" ) { + getCore().sourceDistanceAttenuation = newValue; + } else if ( parameterID == "Reverb Attenuation" ) { + getCore().reverbDistanceAttenuation = newValue; + } else if ( parameterID == "Near Field" ) { + getCore().enableNearDistanceEffect = (int)(newValue + 0.49f); + } else if ( parameterID == "Far Field" ) { + getCore().enableFarDistanceEffect = (int)(newValue + 0.49f); + } else if ( parameterID == "Custom Head" ) { + getCore().enableCustomizedITD = (int)(newValue + 0.49f); + } else if ( parameterID == "Head Circumference" ) { + if ( getCore().enableCustomizedITD ){ + getCore().headCircumference = newValue; + } + } else if ( parameterID == "Enable Anechoic" ) { + if ( auto source = getCore().getSources().front() ) { + bool enabled = (bool)newValue; + if ( enabled ) { + source->EnableAnechoicProcess(); + } else { + source->DisableAnechoicProcess(); + } + } + } else if ( parameterID == "Enable Reverb" ) { + if ( auto source = getCore().getSources().front() ) { + bool enabled = (bool)newValue; + if ( enabled ) { + sources.front()->EnableReverbProcess(); + } else { + sources.front()->DisableReverbProcess(); + } + } + } else if ( parameterID == "HRTF" ) { + getCore().loadHRTF((int)newValue); + } + + if (sources.size() > 0) + getCore().setSourcePosition (sources.front(), position); +} + +//============================================================================== +// This creates new instances of the plugin.. +AudioProcessor* JUCE_CALLTYPE createPluginFilter() +{ + return new AnechoicPluginProcessor(); +} diff --git a/Source/Binaural/AnechoicPluginProcessor.h b/Source/Binaural/AnechoicPluginProcessor.h new file mode 100644 index 0000000..38242cd --- /dev/null +++ b/Source/Binaural/AnechoicPluginProcessor.h @@ -0,0 +1,111 @@ +/** + * \class AnechoicPluginProcessor + * + * \brief Declaration of AnechoicPluginProcessor interface. + * \date November 2020 + * + * \authors Reactify Music LLP: R. Hrafnkelsson || + * Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || + * \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk + * + * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || + * \b Website: http://3d-tune-in.eu/ + * + * \b Copyright: University of Malaga and Imperial College London - 2019 + * + * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. + * + * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + */ + +#pragma once + +#include +#include +#include "AnechoicProcessor.h" +#include "AmbisonicEncoder.h" +#include "ReverbProcessor.h" + +//============================================================================== +/** +*/ + +using CSingleSourceRef = std::shared_ptr; + +class AnechoicPluginProcessor : public AudioProcessor + , private AudioProcessorValueTreeState::Listener + , private Timer +{ +public: + //============================================================================ + AnechoicPluginProcessor(); + ~AnechoicPluginProcessor(); + + //============================================================================ + void prepareToPlay (double sampleRate, int samplesPerBlock) override; + void releaseResources() override; + + #ifndef JucePlugin_PreferredChannelConfigurations + bool isBusesLayoutSupported (const BusesLayout& layouts) const override; + #endif + + void processBlock (AudioBuffer&, MidiBuffer&) override; + + //============================================================================ + AudioProcessorEditor* createEditor() override; + bool hasEditor() const override; + + //============================================================================ + const String getName() const override; + + bool acceptsMidi() const override; + bool producesMidi() const override; + bool isMidiEffect() const override; + double getTailLengthSeconds() const override; + + //============================================================================ + int getNumPrograms() override; + int getCurrentProgram() override; + void setCurrentProgram (int index) override; + const String getProgramName (int index) override; + void changeProgramName (int index, const String& newName) override; + + //============================================================================ + void getStateInformation (MemoryBlock& destData) override; + void setStateInformation (const void* data, int sizeInBytes) override; + + //============================================================================ + const std::vector& getSources() { + return getCore().getSources(); + } + + AnechoicProcessor& getCore() { return mSpatializer; } + + AudioProcessorValueTreeState treeState; + + int pluginInstance{-1}; + +private: + //========================================================================== + void timerCallback() override + { + } + + void updateHostParameters(); + + void parameterChanged(const String& parameterID, float newValue) override; + + AudioBuffer scratchBufferMain, scratchBufferBuss; + AudioBufferFIFO inFifoMain {2, 512}, + outFifoMain {2, 512}, + inFifoBuss {2, 512}, + outFifoBuss {2, 512}; + + //============================================================================ + Binaural::CCore mCore; + AnechoicProcessor mSpatializer {mCore}; + AmbisonicEncoder mEncoder {mCore}; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnechoicPluginProcessor) +}; diff --git a/Source/Binaural/AnechoicProcessor.cpp b/Source/Binaural/AnechoicProcessor.cpp index 888f2ca..fc3e6e8 100644 --- a/Source/Binaural/AnechoicProcessor.cpp +++ b/Source/Binaural/AnechoicProcessor.cpp @@ -1,316 +1,333 @@ /** -* \class AnechoicProcessor -* -* \brief Declaration of Toolkit3dtiProcessor interface. -* \date June 2019 -* -* \authors Reactify Music LLP: R. Hrafnkelsson || -* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || -* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk -* -* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || -* \b Website: http://3d-tune-in.eu/ -* -* \b Copyright: University of Malaga and Imperial College London - 2019 -* -* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. -* -* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. -*/ + * \class AnechoicProcessor + * + * \brief Declaration of AnechoicProcessor interface. + * \date November 2020 + * + * \authors Reactify Music LLP: R. Hrafnkelsson || + * Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || + * \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk + * + * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || + * \b Website: http://3d-tune-in.eu/ + * + * \b Copyright: University of Malaga and Imperial College London - 2020 + * + * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. + * + * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + */ +#include +#include +#include #include "../Utils.h" #include "AnechoicProcessor.h" -void copySourceSettings(CSingleSourceRef oldSource, CSingleSourceRef newSource); - -AnechoicProcessor::Impl::Ptr CreateImpl(Binaural::CCore& core) { - auto impl = std::make_unique (core); - - auto blockSize = core.GetAudioState().bufferSize; - - // Declaration and initialization of stereo buffer - impl->mOutputBuffer.left .resize(blockSize); - impl->mOutputBuffer.right.resize(blockSize); - - // Create listener if necessary - // NOTE(Ragnar): There can only be one listener and - // CreateListnener() returns nullptr if one exists - if (auto listener = core.GetListener()) - impl->mListener = listener; - else - impl->mListener = core.CreateListener(); - - return impl; +void initSource (CSingleSourceRef source, const Common::CVector3& position) +{ + auto sourcePosition = Common::CTransform(); + sourcePosition.SetPosition(position); + source->SetSourceTransform(sourcePosition); + source->SetSpatializationMode(Binaural::TSpatializationMode::HighQuality); + source->DisableNearFieldEffect(); + source->EnableAnechoicProcess(); + source->EnableReverbProcess(); + source->EnableDistanceAttenuationReverb(); + source->EnableDistanceAttenuationAnechoic(); } AnechoicProcessor::AnechoicProcessor(Binaural::CCore& core) - : enableCustomizedITD("0", "Custom Head Circumference", false), - headCircumference("1", "Head Circumference", 450, 620, 550), - enableNearDistanceEffect("2", "Near Distance Effect", true), - enableFarDistanceEffect("3", "Far Distance Effect", false), - spatializationMode("4", "SpatializationMode", 0, 2, 2), - sourceDistanceAttenuation("5", "Source Distance Attenuation", NormalisableRange(-6.f, 0.f, 0.1f), -6.f) - , mCore (core) - , pimpl (CreateImpl (core)) + : enableCustomizedITD("0", "Custom Head Circumference", false) + , headCircumference("1", "Head Circumference", 450, 620, 550) + , enableNearDistanceEffect("2", "Near Distance Effect", true) + , enableFarDistanceEffect("3", "Far Distance Effect", false) + , spatializationMode("4", "SpatializationMode", 0, 2, 2) + , sourceDistanceAttenuation("5", "Source Distance Attenuation", NormalisableRange(-6.f, 0.f, 0.1f), -6.f) + , reverbDistanceAttenuation("6", "Reverb Distance Attenuation", NormalisableRange(-6.f, 0.f, 0.1f), -3.f) + , mCore (core) + , mListener (core.CreateListener()) { -#if DEBUG - ERRORHANDLER3DTI.SetVerbosityMode(VERBOSITYMODE_ERRORSANDWARNINGS); - ERRORHANDLER3DTI.SetErrorLogStream(&std::cout, true); -#endif - - mTransform.SetPosition( Common::CVector3(1,0,0) ); } -void AnechoicProcessor::setup(double sampleRate, int samplesPerBlock) { - // Create new internal implementation - auto impl = CreateImpl (mCore); - - // Load HRTF - // If we have an existing section we reload - // the same HRTF but of new sample rate - int hrtfIndex = pimpl ? pimpl->hrtfIndex : 0; - File hrtf = getBundledHRTF(hrtfIndex, sampleRate); - - reset(std::move(impl), hrtf); +AnechoicProcessor::~AnechoicProcessor() +{ + stopTimer(); +} + +void AnechoicProcessor::setup (double sampleRate) +{ + auto blockSize = mCore.GetAudioState().bufferSize; + + // Declaration and initialization of stereo buffer + mOutputBuffer.left .resize (blockSize); + mOutputBuffer.right.resize (blockSize); + // Load HRTF + // If we have an existing section we reload + // the same HRTF but of new sample rate + loadHRTF (getBundledHRTF (hrtfIndex, sampleRate)); + + JUCE_ASSERT_MESSAGE_MANAGER_EXISTS; + startTimerHz (2); + timerCallback(); } -void AnechoicProcessor::reset(Impl::Ptr impl, const File& hrtf) { - if ( !hrtf.existsAsFile() ) { - DBG("HRTF file doesn't exist"); - } - loadHRTF( *impl, hrtf ); - - auto sampleRate = impl->mCore.GetAudioState().sampleRate; - - // Load HRTF ILD - File hrtfILD = ILDDirectory().getChildFile(SampleRateToDefaultHRTF_ILD.at((int)sampleRate)); - if ( !hrtfILD.existsAsFile() ) { - DBG("HRTF ILD file doesn't exist"); - } - loadHRTF_ILD( *impl, hrtfILD ); - - // Load near field ILD - File nearFieldConf = ILDDirectory().getChildFile(SampleRateToNearFieldILD.at((int)sampleRate)); - if ( !nearFieldConf.existsAsFile() ) { - DBG("Near field ILD file doesn't exist"); - } - - DBG("Loading ILD (near field): " + nearFieldConf.getFullPathName()); - if ( !ILD::CreateFrom3dti_ILDNearFieldEffectTable( nearFieldConf.getFullPathName().toStdString(), impl->mListener )) { - DBG("Unable to load ILD Near Field Effect simulation file. Near (ILD) will not work"); - } - - // Add a sound source - auto position = getSourcePosition(); - addSoundSource(*impl.get(), position); - - if ( pimpl != nullptr ) { - for ( int i = 0; i < pimpl->sources.size(); i++ ) { - auto newSource = impl->sources[i]; - auto oldSource = pimpl->sources[i]; - copySourceSettings(oldSource, newSource); +void AnechoicProcessor::timerCallback() +{ + if (mHRTFsToLoad.size() > 0) + { + if (isLoading.load()) + return; + + reset (mHRTFsToLoad[0]); + mHRTFsToLoad.remove (0); } - } - - const ScopedLock sl (loadLock); - // Assign the new private implementation - pimpl = std::move(impl); } -void AnechoicProcessor::processAnechoic (AudioBuffer& buffer, MidiBuffer& midiMessages) { - if ( pimpl == nullptr) { - buffer.clear(); - return; - } - - const ScopedTryLock sl (loadLock); - if (! sl.isLocked()) - return; +void AnechoicProcessor::reset (const File& hrtf) +{ + isLoading.store (true); - updateParameters(*pimpl); - - // Process audio - auto bufferSize = buffer.getNumSamples(); - - // Initializes buffer with zeros - _3dti_clear(pimpl->mOutputBuffer); + if (! hrtf.existsAsFile()) + { + DBG ("HRTF file doesn't exist"); + isLoading.store (false); + return; + } + + __loadHRTF(hrtf); - // Getting the processed audio - // Declaration, initialization and filling mono buffers - CMonoBuffer input(bufferSize); + auto sampleRate = mCore.GetAudioState().sampleRate; - // Fill input buffer with incoming audio - std::memcpy(input.data(), buffer.getReadPointer(0), bufferSize*sizeof(float)); + // Load HRTF ILD + File hrtfILD = ILDDirectory().getChildFile(SampleRateToDefaultHRTF_ILD.at((int)sampleRate)); + if (! hrtfILD.existsAsFile()) + { + DBG ("HRTF ILD file doesn't exist"); + isLoading.store (false); + return; + } + + __loadHRTF_ILD (hrtfILD); - // Spatialize - if ( pimpl->mListener->GetHRTF()->IsHRTFLoaded() ) { - for ( auto const& source : getSources() ) { - CMonoBufferPairf anechoicBuffer; + // Load near field ILD + File nearFieldConf = ILDDirectory().getChildFile (SampleRateToNearFieldILD.at((int)sampleRate)); + if (! nearFieldConf.existsAsFile() ) { + DBG ("Near field ILD file doesn't exist"); + isLoading.store (false); + return; + } - source->SetBuffer(input); - source->ProcessAnechoic(anechoicBuffer.left, anechoicBuffer.right); - pimpl->mOutputBuffer.left += anechoicBuffer.left; - pimpl->mOutputBuffer.right += anechoicBuffer.right; + DBG("Loading ILD (near field): " + nearFieldConf.getFullPathName()); + if ( !ILD::CreateFrom3dti_ILDNearFieldEffectTable( nearFieldConf.getFullPathName().toStdString(), mListener )) { + DBG ("Unable to load ILD Near Field Effect simulation file. Near (ILD) will not work"); } - } + + // Re-enable processing + isLoading.store (false); +} - // Fill the output with processed audio - // Incoming buffer should have two channels - // for spatialised audio but we check just in case - int numChannels = std::max(buffer.getNumChannels(), 2); - - for ( int i = 0; i < bufferSize; i++ ) { - switch (numChannels) { - case 2: - buffer.getWritePointer(1)[i] = pimpl->mOutputBuffer.right[i]; - default: - buffer.getWritePointer(0)[i] = pimpl->mOutputBuffer.left[i]; +void AnechoicProcessor::processBlock (AudioBuffer& monoIn, AudioBuffer& stereoOut) +{ + if (isLoading.load()) + { + stereoOut.clear(); + return; + } + + updateParameters(); + + // Initializes buffer with zeros + _3dti_clear (mOutputBuffer); + + // Getting the processed audio + // Declaration, initialization and filling mono buffers + CMonoBuffer input = juceTo3dti (monoIn); + + // Spatialize + if (mListener->GetHRTF()->IsHRTFLoaded()) + { + for ( auto const& source : getSources() ) + { + CMonoBufferPair anechoicBuffer; + + source->SetBuffer(input); + source->ProcessAnechoic(anechoicBuffer.left, anechoicBuffer.right); + + mOutputBuffer.left += anechoicBuffer.left; + mOutputBuffer.right += anechoicBuffer.right; + } + } + + // Fill the output with processed audio + // Incoming buffer should have two channels + // for spatialised audio but we check just in case + int numChannels = std::max (stereoOut.getNumChannels(), 2); + + for (int i = 0; i < stereoOut.getNumSamples(); i++) + { + switch (numChannels) { + case 2: + stereoOut.getWritePointer(1)[i] = mOutputBuffer.right[i]; + default: + stereoOut.getWritePointer(0)[i] = mOutputBuffer.left[i]; + } } - } } -void AnechoicProcessor::updateParameters(Impl& impl) { - if ( enableCustomizedITD ) { - impl.mListener->EnableCustomizedITD(); - } else { - impl.mListener->DisableCustomizedITD(); - } - - auto headradius_cm = headCircumference / (2.0 * M_PI * 10.0); - impl.mListener->SetHeadRadius(headradius_cm / 100.0); - - for ( auto const& source : impl.sources ) { - source->SetSpatializationMode((Binaural::TSpatializationMode)spatializationMode.get()); - - if ( enableNearDistanceEffect ) { - source->EnableNearFieldEffect(); +void AnechoicProcessor::updateParameters() +{ + if ( enableCustomizedITD ) { + mListener->EnableCustomizedITD(); } else { - source->DisableNearFieldEffect(); + mListener->DisableCustomizedITD(); } - if ( enableFarDistanceEffect ) { - source->EnableFarDistanceEffect(); - } else { - source->DisableFarDistanceEffect(); + auto headradius_cm = headCircumference / (2.0 * M_PI * 10.0); + mListener->SetHeadRadius(headradius_cm / 100.0); + + const auto numSources = mSources.size(); + for ( auto i = 0; i < numSources; i++ ) + { + auto const& source = mSources[i]; + + source->SetSpatializationMode ((Binaural::TSpatializationMode)spatializationMode.get()); + + if ( enableNearDistanceEffect ) { + source->EnableNearFieldEffect(); + } else { + source->DisableNearFieldEffect(); + } + + if ( enableFarDistanceEffect ) { + source->EnableFarDistanceEffect(); + } else { + source->DisableFarDistanceEffect(); + } + + source->SetSourceTransform( mTransforms[i] ); } - source->SetSourceTransform( mTransform ); - } - - auto magnitudes = impl.mCore.GetMagnitudes(); - magnitudes.SetAnechoicDistanceAttenuation(sourceDistanceAttenuation); - impl.mCore.SetMagnitudes(magnitudes); -} - -bool AnechoicProcessor::loadHRTF(int bundledIndex) { - auto sampleRate = pimpl->mCore.GetAudioState().sampleRate; - return loadHRTF (getBundledHRTF(bundledIndex, sampleRate)); + auto magnitudes = mCore.GetMagnitudes(); + magnitudes.SetAnechoicDistanceAttenuation (sourceDistanceAttenuation); + magnitudes.SetReverbDistanceAttenuation (reverbDistanceAttenuation); + mCore.SetMagnitudes(magnitudes); } -bool AnechoicProcessor::loadHRTF(const File& file) { - reset (CreateImpl (mCore), file); - return true; +bool AnechoicProcessor::loadHRTF (int bundledIndex) +{ + if (bundledIndex == hrtfIndex) + return false; + + auto sampleRate = mCore.GetAudioState().sampleRate; + loadHRTF (getBundledHRTF (bundledIndex, sampleRate)); + + return true; } -// TODO: Move to private implmentation -bool AnechoicProcessor::loadHRTF(Impl& impl, const File& file) { - DBG("Loading HRTF: " << file.getFullPathName()); - bool success = false; - success = loadResourceFile(impl, file, true); - impl.hrtfIndex = hrtfPathToBundledIndex(file); - impl.hrtfPath = file; - return success; +bool AnechoicProcessor::loadHRTF (const File& file) +{ + if (file == hrtfPath) + return false; + + return mHRTFsToLoad.addIfNotAlreadyThere (file); } -bool AnechoicProcessor::loadHRTF_ILD(const File& file) { - return loadHRTF_ILD(*pimpl, file); +bool AnechoicProcessor::__loadHRTF (const File& file) +{ + DBG("Loading HRTF: " << file.getFullPathName()); + bool success = loadResourceFile(file, true); + if (success) + { + hrtfIndex = hrtfPathToBundledIndex(file); + hrtfPath = file; + } + return success; } -bool AnechoicProcessor::loadHRTF_ILD(Impl& impl, const File& file) { - auto path = file.getFullPathName().toStdString(); - DBG("Loading HRTF ILD: " << path); - auto fileSampleRate = ILD::GetSampleRateFrom3dti(path); - if ( impl.mCore.GetAudioState().sampleRate != fileSampleRate ) { - // TODO(Ragnar): Report error - DBG("Error: HRTF ILD file sample rate doesn't match current session"); - return false; - } - - bool success = ILD::CreateFrom3dti_ILDSpatializationTable(path, impl.mListener); - if ( !success ) { - // TODO(Ragnar): Report error - DBG("Error: Unable to load HRTF ILD file"); - } - return success; +bool AnechoicProcessor::__loadHRTF_ILD (const File& file) +{ + auto path = file.getFullPathName().toStdString(); + DBG("Loading HRTF ILD: " << path); + auto fileSampleRate = ILD::GetSampleRateFrom3dti(path); + if ( mCore.GetAudioState().sampleRate != fileSampleRate ) { + // TODO(Ragnar): Report error + DBG("Error: HRTF ILD file sample rate doesn't match current session"); + return false; + } + + bool success = ILD::CreateFrom3dti_ILDSpatializationTable (path, mListener); + if ( !success ) { + // TODO(Ragnar): Report error + DBG("Error: Unable to load HRTF ILD file"); + } + return success; } -bool AnechoicProcessor::loadResourceFile(Impl &impl, const File& file, bool isHRTF) { - int sampleRate = impl.mCore.GetAudioState().sampleRate; - int fileSampleRate = checkResourceSampleRate(file, isHRTF); - // TODO: Throw exception / return error and trigger warning from editor - if ( fileSampleRate != sampleRate ) { - AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Wrong sample rate", - "Please select a file that matches the project sample rate", - "OK"); - return false; - } - bool isSofa = isSofaFile(file); - auto path = file.getFullPathName().toStdString(); - if ( isHRTF ) { - bool specificDelays; - return isSofa ? HRTF::CreateFromSofa(path, impl.mListener, specificDelays) : HRTF::CreateFrom3dti(path, impl.mListener); - } - return false; +bool AnechoicProcessor::loadResourceFile(const File& file, bool isHRTF) +{ + int sampleRate = mCore.GetAudioState().sampleRate; + int fileSampleRate = checkResourceSampleRate (file, isHRTF); + // TODO: Throw exception / return error and trigger warning from editor + if (fileSampleRate != sampleRate) + { + AlertWindow::showMessageBoxAsync(AlertWindow::WarningIcon, "Wrong sample rate", + "Please select a file that matches the project sample rate", + "OK"); + return false; + } + + bool success = false; + + if (isHRTF) + { + bool specificDelays; + auto path = file.getFullPathName().toStdString(); + + success = isSofaFile(file) ? HRTF::CreateFromSofa (path, mListener, specificDelays) + : HRTF::CreateFrom3dti (path, mListener); + } + + return success; } -void AnechoicProcessor::addSoundSource(Impl &impl, Common::CVector3& position) { - if ( impl.sources.size() == 1 ) { - DBG("Only one source allowed at this time."); - return; - } - - auto source = impl.mCore.CreateSingleSourceDSP(); - auto sourcePosition = Common::CTransform(); - sourcePosition.SetPosition(position); - source->SetSourceTransform(sourcePosition); - source->SetSpatializationMode(Binaural::TSpatializationMode::HighQuality); - source->DisableNearFieldEffect(); - source->EnableAnechoicProcess(); - source->EnableReverbProcess(); - source->EnableDistanceAttenuationReverb(); - source->EnableDistanceAttenuationAnechoic(); - - impl.sources.push_back(source); +void AnechoicProcessor::addSoundSource (const Common::CVector3& position) { + auto source = mCore.CreateSingleSourceDSP(); + initSource (source, position); + + mSources.push_back (source); + mTransforms.push_back (Common::CTransform()); + mTransforms.back().SetPosition (Common::CVector3 (1,0,0)); } void copySourceSettings(CSingleSourceRef oldSource, CSingleSourceRef newSource) { - newSource->SetSourceTransform(oldSource->GetSourceTransform()); - newSource->SetSpatializationMode(oldSource->GetSpatializationMode()); - - if ( oldSource->IsNearFieldEffectEnabled() ) { - newSource->EnableNearFieldEffect(); - } else { - newSource->DisableNearFieldEffect(); - } - if ( oldSource->IsAnechoicProcessEnabled() ) { - newSource->EnableAnechoicProcess(); - } else { - newSource->DisableAnechoicProcess(); - } - if ( oldSource->IsReverbProcessEnabled() ) { - newSource->EnableReverbProcess(); - } else { - newSource->DisableReverbProcess(); - } - if ( oldSource->IsDistanceAttenuationEnabledAnechoic() ) { - newSource->EnableDistanceAttenuationAnechoic(); - } else { - newSource->DisableDistanceAttenuationAnechoic(); - } - if ( oldSource->IsDistanceAttenuationEnabledReverb() ) { - newSource->EnableDistanceAttenuationReverb(); - } else { - newSource->DisableDistanceAttenuationReverb(); - } + newSource->SetSourceTransform(oldSource->GetSourceTransform()); + newSource->SetSpatializationMode(oldSource->GetSpatializationMode()); + + if ( oldSource->IsNearFieldEffectEnabled() ) { + newSource->EnableNearFieldEffect(); + } else { + newSource->DisableNearFieldEffect(); + } + if ( oldSource->IsAnechoicProcessEnabled() ) { + newSource->EnableAnechoicProcess(); + } else { + newSource->DisableAnechoicProcess(); + } + if ( oldSource->IsReverbProcessEnabled() ) { + newSource->EnableReverbProcess(); + } else { + newSource->DisableReverbProcess(); + } + if ( oldSource->IsDistanceAttenuationEnabledAnechoic() ) { + newSource->EnableDistanceAttenuationAnechoic(); + } else { + newSource->DisableDistanceAttenuationAnechoic(); + } + if ( oldSource->IsDistanceAttenuationEnabledReverb() ) { + newSource->EnableDistanceAttenuationReverb(); + } else { + newSource->DisableDistanceAttenuationReverb(); + } } diff --git a/Source/Binaural/AnechoicProcessor.h b/Source/Binaural/AnechoicProcessor.h index 42cb013..68c39c3 100644 --- a/Source/Binaural/AnechoicProcessor.h +++ b/Source/Binaural/AnechoicProcessor.h @@ -1,112 +1,132 @@ /** -* \class AnechoicProcessor -* -* \brief Declaration of Toolkit3dtiProcessor interface. -* \date June 2019 -* -* \authors Reactify Music LLP: R. Hrafnkelsson || -* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || -* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk -* -* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || -* \b Website: http://3d-tune-in.eu/ -* -* \b Copyright: University of Malaga and Imperial College London - 2019 -* -* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. -* -* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. -*/ + * \class AnechoicProcessor + * + * \brief Declaration of AnechoicProcessor interface. + * \date November 2020 + * + * \authors Reactify Music LLP: R. Hrafnkelsson || + * Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || + * \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk + * + * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || + * \b Website: http://3d-tune-in.eu/ + * + * \b Copyright: University of Malaga and Imperial College London - 2020 + * + * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. + * + * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + */ #pragma once #include -#include -#include -#include #include -using CListenerRef = shared_ptr; +using CListenerRef = shared_ptr; using CSingleSourceRef = shared_ptr; -using CMonoBufferPairf = Common::CEarPair>; +using CMonoBufferPair = Common::CEarPair>; -class AnechoicProcessor { +// Utility function to copy all settings to a new source. Does NOT update position +void copySourceSettings(CSingleSourceRef oldSource, CSingleSourceRef newSource); + +class AnechoicProcessor : private Timer +{ public: + //============================================================================ + AnechoicProcessor (Binaural::CCore& core); + ~AnechoicProcessor(); + + //============================================================================ + void setup (double sampleRate/*, int frameSize TODO: Currently fixed*/); + + void processBlock (AudioBuffer& monoIn, AudioBuffer& stereoOut); + + //============================================================================ + void addSoundSource(const Common::CVector3& position); + + const std::vector& getSources() { return mSources; } + + //============================================================================ + bool loadHRTF (int bundledIndex); // A number between 0-6 for bundled HRTFs + bool loadHRTF (const File& file); + + int getHrtfIndex() const { return hrtfIndex; }; - struct Impl { - using Ptr = std::unique_ptr; + const File& getHrtfPath() const { return hrtfPath; } + + //========================================================================== + inline float getHeadRadius() const { + return (headCircumference / (2.f * M_PI)) * 0.001f; + } + + // TODO: Source Position as AudioParameterValueTree? + inline void setSourcePosition(const CSingleSourceRef source, Common::CVector3 pos) + { + auto headRadius = getHeadRadius(); + auto distance = pos.GetDistance(); + if ( distance <= headRadius ) { + auto newDistance = distance + (headRadius-distance) + 0.01f; + pos.SetFromAED(pos.GetAzimuthDegrees(), pos.GetElevationDegrees(), newDistance); + } + + auto it = std::find (mSources.begin(), mSources.end(), source); + if ( it != mSources.end() ) + { + auto idx = std::distance (mSources.begin(), it); + mTransforms[idx].SetPosition (pos); + } + else DBG ("Source not found"); + } + + inline Common::CVector3 getSourcePosition (int index = 0) + { + if (index > mSources.size() - 1) + return Common::CVector3(); + + return mTransforms[index].GetPosition(); + } - Impl (Binaural::CCore& core) : mCore (core) {}; - - Binaural::CCore& mCore; + inline Common::CVector3 getSourcePosition (CSingleSourceRef source) + { + if (mSources.empty()) + return Common::CVector3(); + + return getSourcePosition (0); + } + + void timerCallback() override; + + //========================================================================== + AudioParameterBool enableCustomizedITD; + AudioParameterInt headCircumference; + AudioParameterBool enableNearDistanceEffect; + AudioParameterBool enableFarDistanceEffect; + AudioParameterInt spatializationMode; + // AudioParameterFloat sourceGain; // ranges from -12 to + 12 dB + AudioParameterFloat sourceDistanceAttenuation; // ranges from -6 to 0 dB + AudioParameterFloat reverbDistanceAttenuation; + + std::atomic isLoading {false}; + +private: + //============================================================================ + void updateParameters(); + + void reset(const File& hrtf); + bool __loadHRTF (const File& file); + bool __loadHRTF_ILD (const File& file); + bool loadResourceFile(const File& file, bool isHRTF); + + //============================================================================ + Binaural::CCore& mCore; CListenerRef mListener; - CMonoBufferPairf mOutputBuffer; - std::vector sources; + CMonoBufferPair mOutputBuffer; + std::vector mSources; + std::vector mTransforms; + + Array mHRTFsToLoad; + int hrtfIndex; File hrtfPath; - }; - - //============================================================================ - AnechoicProcessor(Binaural::CCore& core); - - //============================================================================ - void setup(double sampleRate, int frameSize); - - void processAnechoic (AudioBuffer& buffer, MidiBuffer& midiMessages); - - //============================================================================ - // Note(Ragnar): Currently never larger than 1 - const std::vector& getSources() { - return pimpl->sources; - } - - //============================================================================ - bool loadHRTF(int bundledIndex); // A number between 0-6 for bundled HRTFs - bool loadHRTF(const File& file); - bool loadHRTF_ILD(const File& file); - - int getHrtfIndex() const { return pimpl->hrtfIndex; }; - const File& getHrtfPath() const { return pimpl->hrtfPath; } - - //============================================================================ - float getHeadRadius() const { - return (headCircumference / (2.f * M_PI)) * 0.001f; - } - - // TODO: Source Position as AudioParameterValueTree? - void setSourcePosition( Common::CVector3 pos ) { - auto headRadius = getHeadRadius(); - auto distance = pos.GetDistance(); - if ( distance <= headRadius ) { - auto newDistance = distance + (headRadius-distance) + 0.01f; - pos.SetFromAED(pos.GetAzimuthDegrees(), pos.GetElevationDegrees(), newDistance); - } - mTransform.SetPosition( pos ); - } - - Common::CVector3 getSourcePosition() { return mTransform.GetPosition(); } - - AudioParameterBool enableCustomizedITD; - AudioParameterInt headCircumference; - AudioParameterBool enableNearDistanceEffect; - AudioParameterBool enableFarDistanceEffect; - AudioParameterInt spatializationMode; - // AudioParameterFloat sourceGain; // ranges from -12 to + 12 dB - AudioParameterFloat sourceDistanceAttenuation; // ranges from -6 to 0 dB - -private: - CriticalSection loadLock; - - void reset(Impl::Ptr p, const File& hrtf); - void updateParameters(Impl& impl); - void addSoundSource(Impl& impl, Common::CVector3& position); - bool loadResourceFile(Impl& impl, const File& file, bool isHRTF); - bool loadHRTF(Impl& impl, const File& file); - bool loadHRTF_ILD(Impl& impl, const File& file); - - //============================================================================ - Binaural::CCore& mCore; - Impl::Ptr pimpl; - - Common::CTransform mTransform; // Source transform }; diff --git a/Source/Binaural/ElevationDial.h b/Source/Binaural/ElevationDial.h index 84bbcab..8fa6468 100644 --- a/Source/Binaural/ElevationDial.h +++ b/Source/Binaural/ElevationDial.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -20,7 +20,7 @@ #pragma once -#include "../JuceLibraryCode/JuceHeader.h" +#include //============================================================================== /* diff --git a/Source/Binaural/ReverbControls.cpp b/Source/Binaural/ReverbControls.cpp index abcb130..4f0ee86 100644 --- a/Source/Binaural/ReverbControls.cpp +++ b/Source/Binaural/ReverbControls.cpp @@ -20,13 +20,12 @@ #include "ReverbControls.h" -ReverbControls::ReverbControls(Toolkit3dtiPluginAudioProcessor& p) - : mProcessor(p), - mReverb (p.getReverbProcessor()), - gainLabel("Gain Label", "Gain (dB)"), +ReverbControls::ReverbControls(ReverbProcessor& p) + : mReverb (p), + gainLabel("Level Label", "Level [dB]"), distanceAttenuationLabel("Distance Label", "dB attenuation per double distance") { - std::vector options = {"Small", "Medium", "Large", "Load 3DTI", "Load SOFA"}; + std::vector options = {"Small", "Medium", "Large", "Library", "Trapezoid", "Load 3DTI", "Load SOFA"}; for ( int i = 0; i < options.size(); i++ ) { brirMenu.addItem( options[i], i+1 ); // IDs must be non-zero } @@ -38,7 +37,7 @@ ReverbControls::ReverbControls(Toolkit3dtiPluginAudioProcessor& p) gainLabel.setJustificationType( Justification::left ); addAndMakeVisible( gainLabel ); - mapParameterToSlider( gainSlider, mReverb.reverbGain ); + mapParameterToSlider( gainSlider, mReverb.reverbLevel ); gainSlider.setTextValueSuffix(" dB"); gainSlider.setTextBoxStyle( Slider::TextBoxRight, false, 65, 24 ); gainSlider.addListener( this ); @@ -47,11 +46,11 @@ ReverbControls::ReverbControls(Toolkit3dtiPluginAudioProcessor& p) distanceAttenuationToggle.setButtonText("On/Off"); distanceAttenuationToggle.setToggleState(true, dontSendNotification); distanceAttenuationToggle.onClick = [this] { updateDistanceAttenuation(); }; - addAndMakeVisible( distanceAttenuationToggle ); + // addAndMakeVisible( distanceAttenuationToggle ); setLabelStyle( distanceAttenuationLabel ); distanceAttenuationLabel.setJustificationType( Justification::left ); - addAndMakeVisible( distanceAttenuationLabel ); + // addAndMakeVisible( distanceAttenuationLabel ); mapParameterToSlider( distanceAttenuationSlider, mReverb.reverbDistanceAttenuation ); distanceAttenuationSlider.setTextValueSuffix(" dB"); @@ -68,18 +67,9 @@ ReverbControls::ReverbControls(Toolkit3dtiPluginAudioProcessor& p) } void ReverbControls::updateGui() { - gainSlider.setValue(mReverb.reverbGain.get(), dontSendNotification); + gainSlider.setValue(mReverb.reverbLevel.get(), dontSendNotification); distanceAttenuationSlider.setValue(mReverb.reverbDistanceAttenuation, dontSendNotification ); - if ( !mProcessor.getSources().empty() ) { - auto source = mProcessor.getSources().front(); - bypassToggle.setToggleState(source->IsReverbProcessEnabled(), dontSendNotification); - bool distanceAttenuationEnabled = source->IsDistanceAttenuationEnabledReverb(); - distanceAttenuationToggle.setToggleState(distanceAttenuationEnabled, dontSendNotification); - distanceAttenuationLabel.setEnabled(distanceAttenuationEnabled); - distanceAttenuationSlider.setEnabled(distanceAttenuationEnabled); - } - auto brirIndex = mReverb.getBrirIndex(); if ( brirIndex != brirMenu.getSelectedItemIndex() && brirIndex < BundledBRIRs.size()-2 ) { // Show filename if custom file is selected brirMenu.setSelectedItemIndex(brirIndex, dontSendNotification); @@ -111,11 +101,11 @@ void ReverbControls::loadCustomBRIR(String fileTypes) { void ReverbControls::updateBypass() { bool enabled = bypassToggle.getToggleState(); - if ( enabled ) { - mProcessor.getSources().front()->EnableReverbProcess(); - } else { - mProcessor.getSources().front()->DisableReverbProcess(); - } +// if ( enabled ) { +// mProcessor.getSources().front()->EnableReverbProcess(); +// } else { +// mProcessor.getSources().front()->DisableReverbProcess(); +// } setAlpha( enabled + 0.4f ); } @@ -125,10 +115,10 @@ void ReverbControls::updateBrirLabel() { } void ReverbControls::updateDistanceAttenuation() { - auto source = mProcessor.getSources().front(); - if ( distanceAttenuationToggle.getToggleState() ) { - source->EnableDistanceAttenuationReverb(); - } else { - source->DisableDistanceAttenuationReverb(); - } +// auto source = mProcessor.getSources().front(); +// if ( distanceAttenuationToggle.getToggleState() ) { +// source->EnableDistanceAttenuationReverb(); +// } else { +// source->DisableDistanceAttenuationReverb(); +// } } diff --git a/Source/Binaural/ReverbControls.h b/Source/Binaural/ReverbControls.h index 3c2b2f7..d924ace 100644 --- a/Source/Binaural/ReverbControls.h +++ b/Source/Binaural/ReverbControls.h @@ -22,8 +22,7 @@ #include #include "ReverbProcessor.h" -#include "PluginProcessor.h" -#include "../Utils.h" +#include "Utils.h" //============================================================================== /* @@ -31,7 +30,7 @@ class ReverbControls : public Component, public Slider::Listener { public: - ReverbControls(Toolkit3dtiPluginAudioProcessor& processor); + ReverbControls (ReverbProcessor& processor); ~ReverbControls() {} @@ -51,7 +50,7 @@ class ReverbControls : public Component, public Slider::Listener { auto area = getLocalBounds(); bypassToggle.setBounds( 10, 4, 80, 24 ); brirMenu.setBounds( 12, 40, area.getWidth()-24, 22); - gainLabel.setBounds( 10, brirMenu.getBottom() + 8, area.getWidth()-20, 24); + gainLabel.setBounds( 10, brirMenu.getBottom() + 16, area.getWidth()-20, 24); gainSlider.setBounds( 6, gainLabel.getBottom(), area.getWidth()-18, 24); distanceAttenuationToggle.setBounds( 10, gainSlider.getBottom() +2, 80, 24); distanceAttenuationLabel.setBounds( 93, distanceAttenuationToggle.getY(), area.getWidth()-100, 24); @@ -62,7 +61,7 @@ class ReverbControls : public Component, public Slider::Listener { void sliderValueChanged (Slider* slider) override { if ( slider == &gainSlider ) { - mReverb.reverbGain = (float)slider->getValue(); + mReverb.reverbLevel = (float)slider->getValue(); } else { mReverb.reverbDistanceAttenuation = (float)slider->getValue(); } @@ -89,7 +88,6 @@ class ReverbControls : public Component, public Slider::Listener { void updateBrirLabel(); void updateDistanceAttenuation(); - Toolkit3dtiPluginAudioProcessor& mProcessor; ReverbProcessor& mReverb; ComboBox brirMenu; diff --git a/Source/Binaural/ReverbPluginEditor.cpp b/Source/Binaural/ReverbPluginEditor.cpp new file mode 100644 index 0000000..c52677e --- /dev/null +++ b/Source/Binaural/ReverbPluginEditor.cpp @@ -0,0 +1,75 @@ +/** +* \class ReverbPluginProcessorEditor +* +* \brief Declaration of ReverbPluginProcessorEditor interface. +* \date November 2020 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#include "Utils.h" +#include "ReverbPluginProcessor.h" +#include "ReverbPluginEditor.h" + +//============================================================================== +ReverbPluginProcessorEditor::ReverbPluginProcessorEditor (ReverbPluginProcessor& p) + : AudioProcessorEditor(&p) + , processor(p) + , reverbControls (p.getReverbProcessor()) +{ + setOpaque (true); + + aboutText.setMultiLine (true); + aboutText.setFont (Font (16.0f, Font::plain)); + aboutText.setText (String::fromUTF8 (BinaryData::About_Reverb_txt, BinaryData::About_Reverb_txtSize)); + aboutText.setReadOnly (true); + aboutText.setAlpha (0.9f); + aboutText.setVisible (false); + aboutText.addMouseListener (this, true); + + aboutBanner.button.onClick = [this] { aboutText.setVisible(!aboutText.isVisible()); }; + + addAndMakeVisible (aboutBanner); + addAndMakeVisible (reverbControls); + addChildComponent (aboutText); + + setSize (600, 200); + + startTimer (30); +} + +ReverbPluginProcessorEditor::~ReverbPluginProcessorEditor() { + stopTimer(); +} + +//============================================================================== +void ReverbPluginProcessorEditor::paint (Graphics& g) +{ + g.fillAll (Colour (24,31,34)); +} + +void ReverbPluginProcessorEditor::resized() +{ + auto r = getLocalBounds(); + + aboutBanner.setBounds (r.removeFromTop (50)); + reverbControls.setBounds (r); + aboutText.setBounds(r); +} + +//============================================================================== +void ReverbPluginProcessorEditor::timerCallback() +{ + reverbControls.updateGui(); +} diff --git a/Source/Binaural/ReverbPluginEditor.h b/Source/Binaural/ReverbPluginEditor.h new file mode 100644 index 0000000..1acb67c --- /dev/null +++ b/Source/Binaural/ReverbPluginEditor.h @@ -0,0 +1,57 @@ +/** +* \class ReverbPluginProcessorEditor +* +* \brief Declaration of ReverbPluginProcessorEditor interface. +* \date December 2020 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#pragma once + +#include +#include "Common/AboutBanner.h" +#include "ReverbControls.h" +#include "ReverbPluginProcessor.h" + +//============================================================================== +/** +*/ +class ReverbPluginProcessorEditor : public AudioProcessorEditor, public Timer +{ +public: + ReverbPluginProcessorEditor (ReverbPluginProcessor&); + ~ReverbPluginProcessorEditor(); + + //============================================================================ + void paint (Graphics&) override; + void resized() override; + + void timerCallback() override; + + void mouseDown(const MouseEvent &e) override { + aboutText.setVisible(false); + }; + +private: + //============================================================================ + ReverbPluginProcessor& processor; + + AboutBanner aboutBanner; + ReverbControls reverbControls; + + TextEditor aboutText; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbPluginProcessorEditor) +}; diff --git a/Source/Binaural/ReverbPluginProcessor.cpp b/Source/Binaural/ReverbPluginProcessor.cpp new file mode 100644 index 0000000..b5932f7 --- /dev/null +++ b/Source/Binaural/ReverbPluginProcessor.cpp @@ -0,0 +1,297 @@ +/** +* \class ReverbPluginProcessor +* +* \brief Declaration of ReverbPluginProcessor interface. +* \date December 2020 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2020 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#include "ReverbPluginProcessor.h" +#include "ReverbPluginEditor.h" + +static constexpr int kTOOLKIT_BUFFER_SIZE = 512; // TODO(Ragnar): Make variable + +void addBooleanHostParameter(AudioProcessorValueTreeState& treeState, String name, int value) { + const auto bypassValueToText = [](float value) { + return value < 0.5f ? "Off" : "On"; + }; + + const auto bypassTextToValue = [](const String& text) { + if (text == "On") { return 1.0f; } + return 0.0f; + }; + + treeState.createAndAddParameter(name, name, "", NormalisableRange(0.f, 1.f, 1.f), + value, bypassValueToText, bypassTextToValue, false, true, + true, AudioProcessorParameter::genericParameter, true); +} + +//============================================================================== +ReverbPluginProcessor::ReverbPluginProcessor() +#ifndef JucePlugin_PreferredChannelConfigurations + : AudioProcessor (BusesProperties() + .withInput ("Input", AudioChannelSet::stereo(), true) + .withInput ("Reverb Inputs", AudioChannelSet::quadraphonic(), true) + .withOutput ("Output", AudioChannelSet::stereo(), true) + ) +#endif + , treeState (*this, nullptr) + , inFifo (2, 512) + , outFifo (2, 512) +{ + using Parameter = AudioProcessorValueTreeState::Parameter; + + addBooleanHostParameter (treeState, "Reverb Enabled", getReverbProcessor().reverbEnabled); + treeState.addParameterListener ("Reverb Enabled", this); + + treeState.createAndAddParameter (std::make_unique ("Reverb Level", "Reverb Level", "", getReverbProcessor().reverbLevel.range, getReverbProcessor().reverbLevel.get(), nullptr, nullptr)); + treeState.addParameterListener ("Reverb Level", this); + + treeState.createAndAddParameter (std::make_unique ("Reverb Attenuation", "Rev Attenuation", "", getReverbProcessor().reverbDistanceAttenuation.range, getReverbProcessor().reverbDistanceAttenuation.get(), nullptr, nullptr)); + treeState.addParameterListener ("Reverb Attenuation", this); + + treeState.createAndAddParameter (std::make_unique ("BRIR", "BRIR", "", NormalisableRange(0, BundledBRIRs.size()-1), 0, [](float value) { return String (value, 0); }, nullptr)); + treeState.addParameterListener ("BRIR", this); + + treeState.state = ValueTree("3DTI Reverb Parameters"); + +#if DEBUG + ERRORHANDLER3DTI.SetVerbosityMode(VERBOSITYMODE_ERRORSANDWARNINGS); + ERRORHANDLER3DTI.SetErrorLogStream(&std::cout, true); +#endif +} + +ReverbPluginProcessor::~ReverbPluginProcessor() +{ +} + +//============================================================================== +const String ReverbPluginProcessor::getName() const +{ + return JucePlugin_Name; +} + +bool ReverbPluginProcessor::acceptsMidi() const +{ + #if JucePlugin_WantsMidiInput + return true; + #else + return false; + #endif +} + +bool ReverbPluginProcessor::producesMidi() const +{ + #if JucePlugin_ProducesMidiOutput + return true; + #else + return false; + #endif +} + +bool ReverbPluginProcessor::isMidiEffect() const +{ + #if JucePlugin_IsMidiEffect + return true; + #else + return false; + #endif +} + +double ReverbPluginProcessor::getTailLengthSeconds() const +{ + return 0.0; +} + +int ReverbPluginProcessor::getNumPrograms() +{ + return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs, + // so this should be at least 1, even if you're not really implementing programs. +} + +int ReverbPluginProcessor::getCurrentProgram() +{ + return 0; +} + +void ReverbPluginProcessor::setCurrentProgram (int index) +{ +} + +const String ReverbPluginProcessor::getProgramName (int index) +{ + return {}; +} + +void ReverbPluginProcessor::changeProgramName (int index, const String& newName) +{ +} + +//============================================================================== +void ReverbPluginProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { + const int blockSizeInternal = kTOOLKIT_BUFFER_SIZE; + + auto numInputs = getBus (true, 1)->getNumberOfChannels(); + + inFifo.clear(); + inFifo.setSize (numInputs, std::max (blockSizeInternal, samplesPerBlock) + 1); + + outFifo.clear(); + outFifo.setSize (2, std::max (samplesPerBlock, blockSizeInternal) * 2); + + scratchBufferStereo.setSize (2, blockSizeInternal); + scratchBufferQuad.setSize (4, blockSizeInternal); + + mCore.SetAudioState ({(int)sampleRate, blockSizeInternal}); + + mReverb.setup (sampleRate, blockSizeInternal); + + // startTimer(30); +} + +void ReverbPluginProcessor::releaseResources() { + // When playback stops, you can use this as an opportunity to free up any + // spare memory, etc. +} + +#ifndef JucePlugin_PreferredChannelConfigurations +bool ReverbPluginProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const +{ + if (layouts.getMainInputChannelSet() != AudioChannelSet::mono() + && layouts.getMainInputChannelSet() != AudioChannelSet::stereo()) + return false; + + if (layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) + return false; + + return true; +} +#endif + +void ReverbPluginProcessor::processBlock (AudioBuffer& buffer, MidiBuffer& midiMessages) +{ + ScopedNoDenormals noDenormals; + + auto inChannels = getTotalNumInputChannels(); + auto outChannels = getTotalNumOutputChannels(); + + for (auto i = inChannels; i < outChannels; ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + + auto mainInput = getBusBuffer (buffer, true, 0); // Stereo I/O + auto reverbInputs = getBusBuffer (buffer, true, 1); // Quad side chain input + + auto numSamples = mainInput.getNumSamples(); + // auto numReverbChannels = reverbInputs.getNumChannels(); + + const int blockSizeInternal = kTOOLKIT_BUFFER_SIZE; + // Some hosts send buffers of varying sizes so we maintain + // an internal buffer to pass the correct size to the 3dti core + + inFifo.addToFifo (reverbInputs); + + while (inFifo.getNumReady() >= blockSizeInternal) + { + scratchBufferStereo.clear(); + + inFifo.readFromFifo (scratchBufferQuad, blockSizeInternal); + mReverb.process (scratchBufferQuad, scratchBufferStereo); + outFifo.addToFifo (scratchBufferStereo); + } + + int numReady = outFifo.getNumReady(); + if (numReady < numSamples) + { + int diff = numSamples - numReady; + outFifo.addSilenceToFifo (diff); + + // Update the host latency + int latency = getLatencySamples() + diff; + setLatencySamples (latency); + } + + AudioSampleBuffer reverb (mainInput.getNumChannels(), numSamples); + outFifo.readFromFifo (reverb); + + for (int ch = 0; ch < mainInput.getNumChannels(); ch++) + mainInput.addFrom (ch, 0, reverb, ch, 0, numSamples); +} + +//============================================================================== +bool ReverbPluginProcessor::hasEditor() const +{ + return true; // (change this to false if you choose to not supply an editor) +} + +AudioProcessorEditor* ReverbPluginProcessor::createEditor() +{ + return new ReverbPluginProcessorEditor (*this); +} + +//============================================================================== +void ReverbPluginProcessor::getStateInformation (MemoryBlock& destData) +{ + // You should use this method to store your parameters in the memory block. + // You could do that either as raw data, or use the XML or ValueTree classes + // as intermediaries to make it easy to save and load complex data. +} + +void ReverbPluginProcessor::setStateInformation (const void* data, int sizeInBytes) +{ + // You should use this method to restore your parameters from this memory block, + // whose contents will have been created by the getStateInformation() call. +} + +void ReverbPluginProcessor::updateHostParameters() +{ + std::unordered_map parameters = { + {"Reverb Enabled", getReverbProcessor().reverbEnabled}, + {"Reverb Level", getReverbProcessor().reverbLevel}, + {"Reverb Attenuation", getReverbProcessor().reverbDistanceAttenuation}, + {"BRIR", getReverbProcessor().getBrirIndex() }, + }; + + for ( auto const & parameter : parameters ) + { + if ( AudioProcessorParameter* p = treeState.getParameter(parameter.first) ) + { + const float newValue = treeState.getParameterRange(parameter.first).convertTo0to1(parameter.second); + + if ( fabs(p->getValue() - newValue) > std::numeric_limits::epsilon() ) + p->setValueNotifyingHost(newValue); + } + } +} + +void ReverbPluginProcessor::parameterChanged(const String& parameterID, float newValue) +{ + DBG ("Changing Parameter: " + parameterID); + if ( parameterID == "Reverb Enabled" ) { + mReverb.reverbEnabled = (bool)newValue; + } else if ( parameterID == "Reverb Level" ) { + mReverb.reverbLevel = newValue; + } else if ( parameterID == "Reverb Attenuation" ) { + mReverb.reverbDistanceAttenuation = newValue; + } else if ( parameterID == "BRIR" ) { + mReverb.loadBRIR((int)newValue); + } +} + +//============================================================================== +// This creates new instances of the plugin.. +AudioProcessor* JUCE_CALLTYPE createPluginFilter() +{ + return new ReverbPluginProcessor(); +} diff --git a/Source/Binaural/ReverbPluginProcessor.h b/Source/Binaural/ReverbPluginProcessor.h new file mode 100644 index 0000000..76d7083 --- /dev/null +++ b/Source/Binaural/ReverbPluginProcessor.h @@ -0,0 +1,99 @@ +/** + * \class ReverbPluginProcessor + * + * \brief Declaration of ReverbPluginProcessor interface. + * \date December 2020 + * + * \authors Reactify Music LLP: R. Hrafnkelsson || + * Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || + * \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk + * + * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || + * \b Website: http://3d-tune-in.eu/ + * + * \b Copyright: University of Malaga and Imperial College London - 2019 + * + * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. + * + * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. + */ + +#pragma once + +#include +#include +#include "ReverbProcessor.h" + +//============================================================================== +/** +*/ + +using CSingleSourceRef = std::shared_ptr; + +class ReverbPluginProcessor : public AudioProcessor + , private AudioProcessorValueTreeState::Listener + , private Timer +{ +public: + //============================================================================ + ReverbPluginProcessor(); + ~ReverbPluginProcessor(); + + //============================================================================ + void prepareToPlay (double sampleRate, int samplesPerBlock) override; + void releaseResources() override; + + #ifndef JucePlugin_PreferredChannelConfigurations + bool isBusesLayoutSupported (const BusesLayout& layouts) const override; + #endif + + void processBlock (AudioBuffer&, MidiBuffer&) override; + + //============================================================================ + AudioProcessorEditor* createEditor() override; + bool hasEditor() const override; + + //============================================================================ + const String getName() const override; + + bool acceptsMidi() const override; + bool producesMidi() const override; + bool isMidiEffect() const override; + double getTailLengthSeconds() const override; + + //============================================================================ + int getNumPrograms() override; + int getCurrentProgram() override; + void setCurrentProgram (int index) override; + const String getProgramName (int index) override; + void changeProgramName (int index, const String& newName) override; + + //============================================================================ + void getStateInformation (MemoryBlock& destData) override; + void setStateInformation (const void* data, int sizeInBytes) override; + + //============================================================================ + ReverbProcessor& getReverbProcessor() { return mReverb; } + + AudioProcessorValueTreeState treeState; + +private: + //========================================================================== + void timerCallback() override + { + } + + void updateHostParameters(); + + void parameterChanged(const String& parameterID, float newValue) override; + + AudioBuffer scratchBufferStereo, scratchBufferQuad; + AudioBufferFIFO inFifo, outFifo; + + //============================================================================ + Binaural::CCore mCore; + ReverbProcessor mReverb {mCore}; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbPluginProcessor) +}; diff --git a/Source/Binaural/ReverbProcessor.cpp b/Source/Binaural/ReverbProcessor.cpp index 9446883..f0b44de 100644 --- a/Source/Binaural/ReverbProcessor.cpp +++ b/Source/Binaural/ReverbProcessor.cpp @@ -11,88 +11,170 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. */ -#include "../Utils.h" +#include +#include +#include "Utils.h" #include "ReverbProcessor.h" ReverbProcessor::ReverbProcessor (Binaural::CCore& core) - : reverbGain ("6", "Reverb Gain", NormalisableRange (-30.f, 6.f, 0.1f), -3.f) - , reverbDistanceAttenuation ("7", "Reverb Distance Attenuation", NormalisableRange (-6.f, 0.f, 0.1f), -3.f) + : reverbEnabled ("Reverb Enabled", "Reverb Enabled", true) + , reverbLevel ("Reverb Level", "Reverb Level", NormalisableRange (-30.f, 6.f, 0.1f), -3.f) + , reverbDistanceAttenuation ("Reverb Distance Attenuation", "Reverb Distance Attenuation", NormalisableRange (-6.f, 0.f, 0.1f), -3.f) , mCore (core) { // Environment setup mEnvironment = core.CreateEnvironment(); - mEnvironment->SetReverberationOrder(TReverberationOrder::BIDIMENSIONAL); + mEnvironment->SetReverberationOrder (TReverberationOrder::BIDIMENSIONAL); +} + +ReverbProcessor::~ReverbProcessor() +{ + stopTimer(); } void ReverbProcessor::setup (double sampleRate, int samplesPerBlock) { - // Load BRIR - // If we have an existing section we reload - // the same BRIR but of new sample rate - File brir = getBundledBRIR (mBRIRIndex, sampleRate); + loadBRIR (0); - if (! brir.existsAsFile()) + JUCE_ASSERT_MESSAGE_MANAGER_EXISTS; + startTimerHz (2); + timerCallback(); +} + +void ReverbProcessor::process (AudioBuffer& buffer) +{ + if (isLoading.load() || ! reverbEnabled.get()) { - DBG ("BRIR file doesn't exist"); - jassertfalse; + buffer.clear(); + return; + } + + Common::CEarPair> outputBuffer; + mEnvironment->ProcessVirtualAmbisonicReverb (outputBuffer.left, + outputBuffer.right); + + // If BRIR is not loaded, buffer will be set to zero + if (outputBuffer.left.size() == 0) + { + buffer.clear(); return; } - loadBRIR (brir); + // Fill the output with processed audio + // Incoming buffer should have two channels + // for spatialised audio + jassert (buffer.getNumChannels() >= 2); + + int numSamples = buffer.getNumSamples(); + + for (int i = 0; i < numSamples; i++) + { + buffer.getWritePointer(0)[i] = outputBuffer.left[i]; + buffer.getWritePointer(1)[i] = outputBuffer.right[i]; + } + + auto reverbGain = Decibels::decibelsToGain (reverbLevel.get()); + buffer.applyGain (reverbGain); + + mPower = outputBuffer.left.GetPower(); } -void ReverbProcessor::process (AudioBuffer& buffer) +void ReverbProcessor::process (AudioBuffer& quadIn, AudioBuffer& stereoOut) { - if (isLoading) + if (isLoading.load() || !reverbEnabled.get()) + { + stereoOut.clear(); return; + } - auto magnitudes = mCore.GetMagnitudes(); - magnitudes.SetReverbDistanceAttenuation (reverbDistanceAttenuation); - mCore.SetMagnitudes (magnitudes); + jassert (quadIn.getNumChannels() == 4); + jassert (stereoOut.getNumChannels() >= 2); - mEnvironment->ProcessVirtualAmbisonicReverb (mOutputBuffer.left, - mOutputBuffer.right); + int numSamples = stereoOut.getNumSamples(); - auto reverbGain = Decibels::decibelsToGain(this->reverbGain.get()); - mOutputBuffer.left .ApplyGain(reverbGain); - mOutputBuffer.right.ApplyGain(reverbGain); + CMonoBuffer input (numSamples); + CStereoBuffer outputBuffer; - // Fill the output with processed audio - // Incoming buffer should have two channels - // for spatialised audio but we check just in case - int numChannels = std::max (buffer.getNumChannels(), 2); - int numSamples = buffer.getNumSamples(); - - for ( int i = 0; i < numSamples; i++ ) { - switch (numChannels) { - case 2: - buffer.getWritePointer(1)[i] = mOutputBuffer.right[i]; - default: - buffer.getWritePointer(0)[i] = mOutputBuffer.left[i]; + for (int ch = 0; ch < quadIn.getNumChannels(); ch++) + { + // Fill input buffer with incoming audio + std::memcpy (input.data(), quadIn.getReadPointer (ch), numSamples*sizeof(float)); + + mEnvironment->ProcessEncodedChannelReverb (TBFormatChannel(ch), input, outputBuffer); + + // If BRIR is not loaded, buffer will be set to zero + if (outputBuffer.size() == 0) + { + stereoOut.clear(); + return; + } + + // Fill the output with processed audio + jassert (outputBuffer.GetNChannels() == 2); + + numSamples = (int)outputBuffer.GetNsamples(); + + for (int i = 0; i < numSamples; i++) + { + stereoOut.getWritePointer(0)[i] += outputBuffer.GetMonoChannel (0)[i]; + stereoOut.getWritePointer(1)[i] += outputBuffer.GetMonoChannel (1)[i]; } } - mPower = mOutputBuffer.left.GetPower(); + auto reverbGain = Decibels::decibelsToGain (reverbLevel.get()); + stereoOut.applyGain (reverbGain); + + mPower = stereoOut.getRMSLevel (0, 0, numSamples); } bool ReverbProcessor::loadBRIR (int bundledIndex) { + if (bundledIndex < 0 || bundledIndex > BundledBRIRs.size()-1) + return false; + return loadBRIR (getBundledBRIR (bundledIndex, getSampleRate())); } bool ReverbProcessor::loadBRIR (const File& file) { - isLoading = true; + if (file == mBRIRPath) + return false; + + return mBRIRsToLoad.addIfNotAlreadyThere (file); +} + +void ReverbProcessor::timerCallback() +{ + if (mBRIRsToLoad.size() > 0) + { + if (isLoading.load()) + return; + + __loadBRIR (mBRIRsToLoad[0]); + mBRIRsToLoad.remove (0); + } +} + +bool ReverbProcessor::__loadBRIR (const File& file) +{ + isLoading.store (true); DBG ("Loading BRIR: " << file.getFullPathName()); + if (! file.existsAsFile()) + { + DBG ("BRIR file doesn't exist"); + isLoading.store (false); + return false; + } + int fileSampleRate = checkResourceSampleRate (file, false); // TODO: Throw exception / return error and trigger warning from editor if (fileSampleRate != getSampleRate()) @@ -101,6 +183,7 @@ bool ReverbProcessor::loadBRIR (const File& file) "Wrong sample rate", "Please select a file that matches the project sample rate", "OK"); + isLoading.store (false); return false; } @@ -118,7 +201,7 @@ bool ReverbProcessor::loadBRIR (const File& file) success = true; } - isLoading = false; + isLoading.store (false); return success; } diff --git a/Source/Binaural/ReverbProcessor.h b/Source/Binaural/ReverbProcessor.h index ce3f264..aeea63a 100644 --- a/Source/Binaural/ReverbProcessor.h +++ b/Source/Binaural/ReverbProcessor.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -21,24 +21,25 @@ #pragma once #include -#include -#include #include //============================================================================== -class ReverbProcessor +class ReverbProcessor : private Timer { public: //========================================================================== ReverbProcessor (Binaural::CCore& core); + ~ReverbProcessor(); void setup (double sampleRate, int samplesPerBlock); //========================================================================== void process (AudioBuffer& buffer); + void process (AudioBuffer& quadIn, AudioBuffer& stereoOut); + //========================================================================== - bool loadBRIR (int bundledIndex); // A number between 0-2 for bundled HRTFs + bool loadBRIR (int bundledIndex); // A number between 0-6 for bundled HRTFs bool loadBRIR (const File& file); int getBrirIndex() const { return mBRIRIndex; } @@ -47,18 +48,25 @@ class ReverbProcessor float getPower() const { return mPower; }; //========================================================================== - AudioParameterFloat reverbGain; // ranges from -12 to +12 dB + AudioParameterBool reverbEnabled; + AudioParameterFloat reverbLevel; // ranges from -30 to +6 dB AudioParameterFloat reverbDistanceAttenuation; // ranges from -6 to 0 dB + std::atomic isLoading {false}; + private: + void timerCallback() override; + + bool __loadBRIR (const File& file); + double getSampleRate(); //========================================================================== Binaural::CCore& mCore; - Common::CEarPair> mOutputBuffer; std::shared_ptr mEnvironment; - bool isLoading; - int mBRIRIndex; + Array mBRIRsToLoad; + + int mBRIRIndex = 0; File mBRIRPath; float mPower; diff --git a/Source/Binaural/SourceControls.h b/Source/Binaural/SourceControls.h index 9319e70..54afe7f 100644 --- a/Source/Binaural/SourceControls.h +++ b/Source/Binaural/SourceControls.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -21,14 +21,15 @@ #pragma once #include +#include "Utils.h" //============================================================================== /* */ class SourceControls : public Component, public Slider::Listener { public: - SourceControls(Toolkit3dtiPluginAudioProcessor& processor) - : mCore(processor.getCore()), + SourceControls (AnechoicProcessor& processor) + : mCore (processor), azimuthLabel("Azimuth Label", "Azimuth"), distanceLabel("Distance Label", "Distance"), elevationLabel("Elevation Label", "Elevation"), @@ -36,6 +37,8 @@ class SourceControls : public Component, public Slider::Listener { yLabel("Y Label", "Y"), zLabel("Z Label", "Z") { + setOpaque (true); + setLabelStyle( azimuthLabel ); azimuthLabel.setJustificationType( Justification::left ); addAndMakeVisible( azimuthLabel ); @@ -102,7 +105,7 @@ class SourceControls : public Component, public Slider::Listener { } void updateGui() { - auto position = mCore.getSourcePosition(); + auto position = mCore.getSourcePosition(0); distanceSlider.setValue(position.GetDistance(), dontSendNotification); azimuthSlider.setValue(position.GetAzimuthDegrees(), dontSendNotification); elevationSlider.setValue(mapElevationToSliderValue(position.GetElevationDegrees()), dontSendNotification); @@ -112,9 +115,7 @@ class SourceControls : public Component, public Slider::Listener { } void paint (Graphics& g) override { - g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); - g.setColour (Colours::grey); - g.drawRect (getLocalBounds(), 1); + g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));; } void resized() override { @@ -134,7 +135,9 @@ class SourceControls : public Component, public Slider::Listener { } void sliderValueChanged( Slider* slider ) override { - auto position = mCore.getSourcePosition(); + auto source = mCore.getSources().front(); + + auto position = mCore.getSourcePosition(source); if ( slider == &azimuthSlider ) { position.SetFromAED(slider->getValue(), position.GetElevationDegrees(), position.GetDistance()); @@ -151,7 +154,7 @@ class SourceControls : public Component, public Slider::Listener { position.z = slider->getValue(); } - mCore.setSourcePosition(position); + mCore.setSourcePosition(source, position); repaint(); updateGui(); diff --git a/Source/Binaural/SourceReverbControls.cpp b/Source/Binaural/SourceReverbControls.cpp new file mode 100644 index 0000000..128fd8f --- /dev/null +++ b/Source/Binaural/SourceReverbControls.cpp @@ -0,0 +1,92 @@ +/** +* \class ReverbControls +* +* \brief Declaration of ReverbControls interface. +* \date June 2019 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#include "SourceReverbControls.h" + +ReverbControls::ReverbControls (AnechoicPluginProcessor& p) + : mProcessor(p) + , mCore (p.getCore()) + // , gainLabel("Gain Label", "Reverb Gain (dB)") + , distanceAttenuationLabel("Distance Label", "Reverb dB attenuation per double distance") +{ +// setLabelStyle( gainLabel ); +// gainLabel.setJustificationType( Justification::left ); +// addAndMakeVisible( gainLabel ); +// +// mapParameterToSlider( gainSlider, mReverb.reverbLevel ); +// gainSlider.setTextValueSuffix(" dB"); +// gainSlider.setTextBoxStyle( Slider::TextBoxRight, false, 65, 24 ); +// gainSlider.addListener( this ); +// addAndMakeVisible( gainSlider ); + + distanceAttenuationToggle.setButtonText ("On/Off"); + distanceAttenuationToggle.setToggleState (true, dontSendNotification); + distanceAttenuationToggle.onClick = [this] { updateDistanceAttenuation(); }; + addAndMakeVisible (distanceAttenuationToggle); + + setLabelStyle (distanceAttenuationLabel); + distanceAttenuationLabel.setFont (Font (13.0f, Font::plain)); + distanceAttenuationLabel.setJustificationType (Justification::right); + addAndMakeVisible (distanceAttenuationLabel); + + mapParameterToSlider (distanceAttenuationSlider, mCore.reverbDistanceAttenuation); + distanceAttenuationSlider.setTextValueSuffix (" dB"); + distanceAttenuationSlider.setTextBoxStyle (Slider::TextBoxRight, false, 65, 24); + addAndMakeVisible (distanceAttenuationSlider ); + distanceAttenuationSlider.onValueChange = [=] { + mCore.reverbDistanceAttenuation = distanceAttenuationSlider.getValue(); + }; + + bypassToggle.setButtonText("On/Off"); + bypassToggle.setToggleState(true, dontSendNotification); + bypassToggle.onClick = [this] { updateBypass(); }; + addAndMakeVisible( bypassToggle ); + + updateGui(); +} + +void ReverbControls::updateGui() { + // gainSlider.setValue(mReverb.reverbLevel.get(), dontSendNotification); + distanceAttenuationSlider.setValue (mCore.reverbDistanceAttenuation, dontSendNotification); +} + +void ReverbControls::updateBypass() { + bool enabled = bypassToggle.getToggleState(); + if ( enabled ) { + mProcessor.getSources().front()->EnableReverbProcess(); + } else { + mProcessor.getSources().front()->DisableReverbProcess(); + } + setAlpha( enabled + 0.4f ); +} + +//void ReverbControls::updateBrirLabel() { +// auto brir = mReverb.getBrirPath().getFileNameWithoutExtension().upToLastOccurrenceOf("_", false, false); +// brirMenu.setText(brir, dontSendNotification); +//} + +void ReverbControls::updateDistanceAttenuation() { + auto source = mProcessor.getSources().front(); + if (distanceAttenuationToggle.getToggleState()) { + source->EnableDistanceAttenuationReverb(); + } else { + source->DisableDistanceAttenuationReverb(); + } +} diff --git a/Source/Binaural/SourceReverbControls.h b/Source/Binaural/SourceReverbControls.h new file mode 100644 index 0000000..9c93bcd --- /dev/null +++ b/Source/Binaural/SourceReverbControls.h @@ -0,0 +1,87 @@ +/** +* \class ReverbControls +* +* \brief Declaration of ReverbControls interface. +* \date June 2019 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2019 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#pragma once + +#include +#include "AnechoicProcessor.h" +#include "AnechoicPluginProcessor.h" +#include "Utils.h" + +//============================================================================== +/* + */ + +class ReverbControls : public Component, public Slider::Listener { +public: + ReverbControls (AnechoicPluginProcessor& processor); + + ~ReverbControls() {} + + void paint (Graphics& g) override { + g.fillAll (getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); + g.setColour (Colours::white); + g.setFont (18.0f); + g.drawText ("3D Reverb send", + getLocalBounds().withTrimmedBottom( getLocalBounds().getHeight() - 32 ), + Justification::centred, + true); + } + + void resized() override + { + bypassToggle.setBounds( 10, 4, 80, 24 ); + + auto area = getLocalBounds(); + //gainLabel.setBounds( 10, brirMenu.getBottom() + 8, area.getWidth()-20, 24); + //gainSlider.setBounds( 6, gainLabel.getBottom(), area.getWidth()-18, 24); + distanceAttenuationToggle.setBounds( 10, 40, 80, 24); + distanceAttenuationLabel.setBounds( 93, distanceAttenuationToggle.getY(), area.getWidth()-100, 24); + distanceAttenuationSlider.setBounds( 6, distanceAttenuationToggle.getBottom() + 4, area.getWidth()-18, 24); + } + + void updateGui(); + + void sliderValueChanged (Slider* slider) override { + // if ( slider == &gainSlider ) { + // mReverb.reverbLevel = (float)slider->getValue(); + // } else { + mCore.reverbDistanceAttenuation = (float)slider->getValue(); + // } + } + + void loadCustomBRIR(String fileTypes); + + ToggleButton bypassToggle; + +private: + //========================================================================== + void updateBypass(); + void updateDistanceAttenuation(); + + AnechoicPluginProcessor& mProcessor; + AnechoicProcessor& mCore; + + ToggleButton distanceAttenuationToggle; + Label distanceAttenuationLabel; + Slider distanceAttenuationSlider; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbControls) +}; diff --git a/Source/Binaural/PluginEditor.cpp b/Source/Binaural/SpatialisePluginEditor.cpp similarity index 91% rename from Source/Binaural/PluginEditor.cpp rename to Source/Binaural/SpatialisePluginEditor.cpp index 699c444..c8781eb 100644 --- a/Source/Binaural/PluginEditor.cpp +++ b/Source/Binaural/SpatialisePluginEditor.cpp @@ -11,24 +11,24 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. */ -#include "../Utils.h" -#include "PluginProcessor.h" -#include "PluginEditor.h" +#include "Utils.h" +#include "SpatialisePluginProcessor.h" +#include "SpatialisePluginEditor.h" //============================================================================== Toolkit3dtiPluginAudioProcessorEditor::Toolkit3dtiPluginAudioProcessorEditor (Toolkit3dtiPluginAudioProcessor& p) : AudioProcessorEditor(&p), processor(p), - sourceControls(p), + sourceControls(p.getCore()), reverbControls(p), - anechoicControls(p), + anechoicControls(p.getCore()), spatializerWidget(p.getCore()) { // Make sure that before the constructor has finished, you've set the @@ -90,7 +90,7 @@ void Toolkit3dtiPluginAudioProcessorEditor::resized() { toolkitVersionLabel.setBounds(aboutButton.getRight()+6, aboutButton.getY(), area.getWidth()-aboutButton.getWidth()-6, 24); anechoicControls.setBounds(10, 54, (area.getWidth()/3)-20, 252); - reverbControls.setBounds(10, anechoicControls.getBottom()-1, anechoicControls.getWidth(), 184); + reverbControls.setBounds(10, anechoicControls.getBottom(), anechoicControls.getWidth(), 184); sourceControls.setBounds(10, reverbControls.getBottom()-1, anechoicControls.getWidth(), 310); spatializerWidget.setBounds(anechoicControls.getRight() + 10, 99, area.getWidth() - anechoicControls.getRight() - 20, area.getHeight() - 110); aboutText.setBounds(spatializerWidget.getBoundsInParent()); diff --git a/Source/Binaural/PluginEditor.h b/Source/Binaural/SpatialisePluginEditor.h similarity index 93% rename from Source/Binaural/PluginEditor.h rename to Source/Binaural/SpatialisePluginEditor.h index 38bccf1..7a7b3fb 100755 --- a/Source/Binaural/PluginEditor.h +++ b/Source/Binaural/SpatialisePluginEditor.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -20,13 +20,14 @@ #pragma once -#include "../JuceLibraryCode/JuceHeader.h" +#include #include "AnechoicControls.h" -#include "ReverbControls.h" +#include "v1/ReverbControls.h" #include "SourceControls.h" #include "SpatializerWidget.h" #include "ElevationDial.h" -#include "PluginProcessor.h" + +class Toolkit3dtiPluginAudioProcessor; //============================================================================== /** diff --git a/Source/Binaural/PluginProcessor.cpp b/Source/Binaural/SpatialisePluginProcessor.cpp similarity index 79% rename from Source/Binaural/PluginProcessor.cpp rename to Source/Binaural/SpatialisePluginProcessor.cpp index a643865..30a9bf3 100644 --- a/Source/Binaural/PluginProcessor.cpp +++ b/Source/Binaural/SpatialisePluginProcessor.cpp @@ -11,15 +11,17 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. */ -#include "PluginProcessor.h" -#include "PluginEditor.h" +#include "SpatialisePluginProcessor.h" +#include "SpatialisePluginEditor.h" + +static constexpr int kTOOLKIT_BUFFER_SIZE = 512; // TODO(Ragnar): Make variable void addBooleanHostParameter(AudioProcessorValueTreeState& treeState, String name, int value) { const auto bypassValueToText = [](float value) { @@ -42,7 +44,7 @@ Toolkit3dtiPluginAudioProcessor::Toolkit3dtiPluginAudioProcessor() : AudioProcessor (BusesProperties() #if ! JucePlugin_IsMidiEffect #if ! JucePlugin_IsSynth - .withInput ("Input", AudioChannelSet::mono(), true) + .withInput ("Input", JUCEApplication::isStandaloneApp() ? AudioChannelSet::stereo() : AudioChannelSet::mono(), true) #endif .withOutput ("Output", AudioChannelSet::stereo(), true) #endif @@ -52,6 +54,8 @@ Toolkit3dtiPluginAudioProcessor::Toolkit3dtiPluginAudioProcessor() inFifo (2, 512), outFifo(2, 512) { + mSpatializer.addSoundSource (Common::CVector3(0,1,0)); + auto position = getCore().getSourcePosition(); treeState.createAndAddParameter("Azimuth", "Azimuth", "", NormalisableRange(0.0f, 359.99f), position.GetAzimuthDegrees(), [](float value) { return String (value, 1); }, nullptr); @@ -75,8 +79,8 @@ Toolkit3dtiPluginAudioProcessor::Toolkit3dtiPluginAudioProcessor() treeState.createAndAddParameter("Source Attenuation", "Src Attenuation", "", getCore().sourceDistanceAttenuation.range, getCore().sourceDistanceAttenuation.get(), nullptr, nullptr); treeState.addParameterListener("Source Attenuation", this); - treeState.createAndAddParameter("Reverb Gain", "Reverb Gain", "", getReverbProcessor().reverbGain.range, getReverbProcessor().reverbGain.get(), nullptr, nullptr); - treeState.addParameterListener("Reverb Gain", this); + treeState.createAndAddParameter("Reverb Level", "Reverb Level", "", getReverbProcessor().reverbLevel.range, getReverbProcessor().reverbLevel.get(), nullptr, nullptr); + treeState.addParameterListener("Reverb Level", this); treeState.createAndAddParameter("Reverb Attenuation", "Rev Attenuation", "", getReverbProcessor().reverbDistanceAttenuation.range, getReverbProcessor().reverbDistanceAttenuation.get(), nullptr, nullptr); treeState.addParameterListener("Reverb Attenuation", this); @@ -99,13 +103,19 @@ Toolkit3dtiPluginAudioProcessor::Toolkit3dtiPluginAudioProcessor() addBooleanHostParameter(treeState, "Enable Reverb", true); treeState.addParameterListener("Enable Reverb", this); - treeState.createAndAddParameter("HRTF", "HRTF", "", NormalisableRange(0, BundledHRTFs.size()-1), 0, [](float value) { return String (value, 0); }, nullptr); + using Parameter = AudioProcessorValueTreeState::Parameter; + treeState.createAndAddParameter(std::make_unique ("HRTF", "HRTF", "", NormalisableRange(0, BundledHRTFs.size()-1), 0, [](float value) { return String (value, 0); }, nullptr)); treeState.addParameterListener("HRTF", this); - treeState.createAndAddParameter("BRIR", "BRIR", "", NormalisableRange(0, BundledBRIRs.size()-1), 0, [](float value) { return String (value, 0); }, nullptr); + treeState.createAndAddParameter(std::make_unique ("BRIR", "BRIR", "", NormalisableRange(0, BundledBRIRs.size()+1), 0, [](float value) { return String (value, 0); }, nullptr)); treeState.addParameterListener("BRIR", this); treeState.state = ValueTree("3D Tune-In Parameters"); + +#if DEBUG + ERRORHANDLER3DTI.SetVerbosityMode(VERBOSITYMODE_ERRORSANDWARNINGS); + ERRORHANDLER3DTI.SetErrorLogStream(&std::cout, true); +#endif } Toolkit3dtiPluginAudioProcessor::~Toolkit3dtiPluginAudioProcessor() @@ -179,19 +189,19 @@ void Toolkit3dtiPluginAudioProcessor::prepareToPlay (double sampleRate, int samp const int blockSizeInternal = kTOOLKIT_BUFFER_SIZE; inFifo.clear(); - inFifo.setSize (getTotalNumOutputChannels(), blockSizeInternal + 1); + inFifo.setSize (1, blockSizeInternal + 1); outFifo.clear(); - outFifo.setSize(getTotalNumOutputChannels(), std::max(samplesPerBlock, blockSizeInternal) * 2); + outFifo.setSize (2, std::max (samplesPerBlock, blockSizeInternal) * 2); - scratchBuffer.setSize(getTotalNumOutputChannels(), blockSizeInternal); + scratchBuffer.setSize (2, blockSizeInternal); Common::TAudioStateStruct audioState; audioState.bufferSize = blockSizeInternal; audioState.sampleRate = sampleRate; mCore.SetAudioState (audioState); - mSpatializer.setup (sampleRate, blockSizeInternal); + mSpatializer.setup (sampleRate); mReverb.setup (sampleRate, blockSizeInternal); startTimer(60); @@ -205,24 +215,14 @@ void Toolkit3dtiPluginAudioProcessor::releaseResources() { #ifndef JucePlugin_PreferredChannelConfigurations bool Toolkit3dtiPluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const { - #if JucePlugin_IsMidiEffect - ignoreUnused (layouts); - return true; - #else - // This is the place where you check if the layout is supported. - // In this template code we only support mono or stereo. - if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono() - && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) + if (layouts.getMainInputChannelSet() != AudioChannelSet::mono() + && layouts.getMainInputChannelSet() != AudioChannelSet::stereo()) return false; - - // This checks if the input layout matches the output layout - #if ! JucePlugin_IsSynth - if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) + + if (layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) return false; - #endif return true; - #endif } #endif @@ -239,24 +239,30 @@ void Toolkit3dtiPluginAudioProcessor::processBlock (AudioBuffer& buffer, int numSamples = buffer.getNumSamples(); int numChannels = buffer.getNumChannels(); - AudioBuffer temp (numChannels, 1); + AudioBuffer temp (1, 1); // Some hosts send buffers of varying sizes so we maintain // an internal buffer to pass the correct size to the 3dti core for (int sample = 0; sample < numSamples; ++sample) { - temp.setDataToReferTo (buffer.getArrayOfWritePointers(), - numChannels, sample, 1); + temp.clear(); + + // Sum to mono in 'temp' + for (int channel = 0; channel < numChannels; ++channel) + temp.addSample (0, 0, buffer.getSample (channel, sample)); + + temp.applyGain (1.0f / (float)numChannels); - inFifo.addToFifo(temp); + inFifo.addToFifo (temp); if (inFifo.getFreeSpace() == 0) { const int blockSizeInternal = kTOOLKIT_BUFFER_SIZE; - inFifo.readFromFifo(scratchBuffer, blockSizeInternal); + AudioBuffer monoIn (1, blockSizeInternal); + inFifo.readFromFifo (monoIn, blockSizeInternal); // Main process - mSpatializer.processAnechoic(scratchBuffer, midiMessages); + mSpatializer.processBlock (monoIn, scratchBuffer); #ifndef DEBUG // NOTE(Ragnar): Reverb processing is too heavy for debug mode bool reverbEnabled = getSources().front()->IsReverbProcessEnabled(); @@ -325,7 +331,7 @@ void Toolkit3dtiPluginAudioProcessor::updateHostParameters() { {"Y", position.y}, {"Z", position.z}, {"Source Attenuation", getCore().sourceDistanceAttenuation}, - {"Reverb Gain", getReverbProcessor().reverbGain}, + {"Reverb Gain", getReverbProcessor().reverbLevel}, {"Reverb Attenuation", getReverbProcessor().reverbDistanceAttenuation}, {"Near Field", getCore().enableNearDistanceEffect}, {"Far Field", getCore().enableFarDistanceEffect}, @@ -337,12 +343,21 @@ void Toolkit3dtiPluginAudioProcessor::updateHostParameters() { {"BRIR", getReverbProcessor().getBrirIndex() }, }; - for ( auto const & parameter : parameters ) { - if ( AudioProcessorParameter* p = treeState.getParameter(parameter.first) ) { - const float newValue = treeState.getParameterRange(parameter.first).convertTo0to1(parameter.second); + for (auto const & parameter : parameters) { + if (auto* p = treeState.getParameter(parameter.first) ) { + auto range = treeState.getParameterRange (parameter.first); + + if (parameter.second < range.start || parameter.second > range.end) + continue; + + auto newValue = range.convertTo0to1 (parameter.second); - if ( fabs(p->getValue() - newValue) > std::numeric_limits::epsilon() ) - p->setValueNotifyingHost(newValue); + if (fabsf (p->getValue() - newValue) > std::numeric_limits::epsilon()) + { + treeState.removeParameterListener (parameter.first, this); + p->setValueNotifyingHost(newValue); + treeState.addParameterListener (parameter.first, this); + } } } } @@ -351,7 +366,6 @@ void Toolkit3dtiPluginAudioProcessor::parameterChanged(const String& parameterID auto position = getCore().getSourcePosition(); if ( parameterID == "Azimuth" ) { - DBG("Azimuth: " + String(newValue)); position.SetFromAED( newValue, position.GetElevationDegrees(), position.GetDistance() ); } else if ( parameterID == "Distance" ) { position.SetFromAED( position.GetAzimuthDegrees(), position.GetElevationDegrees(), newValue ); @@ -365,8 +379,8 @@ void Toolkit3dtiPluginAudioProcessor::parameterChanged(const String& parameterID position.z = newValue; } else if ( parameterID == "Source Attenuation" ) { getCore().sourceDistanceAttenuation = newValue; - } else if ( parameterID == "Reverb Gain" ) { - getReverbProcessor().reverbGain = newValue; + } else if ( parameterID == "Reverb Level" ) { + getReverbProcessor().reverbLevel = newValue; } else if ( parameterID == "Reverb Attenuation" ) { getReverbProcessor().reverbDistanceAttenuation = newValue; } else if ( parameterID == "Near Field" ) { @@ -398,12 +412,12 @@ void Toolkit3dtiPluginAudioProcessor::parameterChanged(const String& parameterID } } } else if ( parameterID == "HRTF" ) { - getCore().loadHRTF((int)newValue); + getCore().loadHRTF((int)newValue); } else if ( parameterID == "BRIR" ) { - getReverbProcessor().loadBRIR((int)newValue); + getReverbProcessor().loadBRIR((int)newValue); } - getCore().setSourcePosition(position); + getCore().setSourcePosition(getCore().getSources().front(), position); } //============================================================================== diff --git a/Source/Binaural/PluginProcessor.h b/Source/Binaural/SpatialisePluginProcessor.h similarity index 95% rename from Source/Binaural/PluginProcessor.h rename to Source/Binaural/SpatialisePluginProcessor.h index 8ee83c0..a1e89a7 100644 --- a/Source/Binaural/PluginProcessor.h +++ b/Source/Binaural/SpatialisePluginProcessor.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * - * \b Copyright: University of Malaga and Imperial College London - 2019 + * \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -29,8 +29,6 @@ /** */ -static constexpr int kTOOLKIT_BUFFER_SIZE = 512; // TODO(Ragnar): Make variable - class Toolkit3dtiPluginAudioProcessor : public AudioProcessor, private AudioProcessorValueTreeState::Listener, private Timer diff --git a/Source/Binaural/SpatializerWidget.h b/Source/Binaural/SpatializerWidget.h index e0f4853..97e65c5 100644 --- a/Source/Binaural/SpatializerWidget.h +++ b/Source/Binaural/SpatializerWidget.h @@ -11,7 +11,7 @@ * \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || * \b Website: http://3d-tune-in.eu/ * -* \b Copyright: University of Malaga and Imperial College London - 2019 +* \b Copyright: University of Malaga and Imperial College London - 2021 * * \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. * @@ -20,7 +20,7 @@ #pragma once -#include "../JuceLibraryCode/JuceHeader.h" +#include #include "ElevationDial.h" //============================================================================== @@ -29,10 +29,12 @@ // The distance in meters at the edge of screen coordinates static const float RANGE_METERS = 40.f; +static const int kMargins = 20; class SpatializerWidget : public Component, public Slider::Listener { public: SpatializerWidget(AnechoicProcessor& core) : mCore(core) { + setOpaque (true); elevationDial.setSliderStyle( Slider::Rotary ); elevationDial.setRange( -89, 89, 1 ); elevationDial.setValue(0, dontSendNotification); @@ -50,9 +52,7 @@ class SpatializerWidget : public Component, public Slider::Listener { g.setColour(Colours::grey); - auto localBounds = getLocalBounds(); - g.drawRect(localBounds, 1); // draw an outline around the component - + auto localBounds = getLocalBounds().reduced (kMargins); auto centrePoint = localBounds.getCentre().toFloat(); // Draw listener head and distance radii @@ -100,9 +100,9 @@ class SpatializerWidget : public Component, public Slider::Listener { g.drawLine(rect.getX(), rect.getBottom(), rect.getRight(), rect.getY()); } - auto position = mCore.getSourcePosition(); + auto position = mCore.getSourcePosition(0); - auto drawPosition = mCore.getSourcePosition(); + auto drawPosition = position; drawPosition.SetFromAED(position.GetAzimuthDegrees(), 0, position.GetDistance()); auto distance = scaledRange.convertFrom0to1(position.GetDistance() / RANGE_METERS); auto angle = drawPosition.GetAzimuthDegrees(); @@ -136,7 +136,8 @@ class SpatializerWidget : public Component, public Slider::Listener { } void resized() override { - scaledRange = NormalisableRange(0, getHeight() * 0.5f, 1.f, 3.5f); + auto height = getLocalBounds().reduced (kMargins).getHeight(); + scaledRange = NormalisableRange(0, height * 0.5f, 1.f, 3.5f); } void mouseDown(const MouseEvent&) override { @@ -169,9 +170,10 @@ class SpatializerWidget : public Component, public Slider::Listener { auto value = scaledRange.convertTo0to1(distance) * maxValue; auto distanceScaled = jlimit(minValue, maxValue, value); - auto position = mCore.getSourcePosition(); + auto source = mCore.getSources().front(); + auto position = mCore.getSourcePosition(source); position.SetFromAED(azimuth, position.GetElevationDegrees(), distanceScaled); - mCore.setSourcePosition(position); + mCore.setSourcePosition(source, position); applyElevationRotation(); updateGui(); @@ -179,7 +181,7 @@ class SpatializerWidget : public Component, public Slider::Listener { void updateGui() { if ( !mouseIsDown ) { - auto elevation = mCore.getSourcePosition().GetElevationDegrees(); + auto elevation = mCore.getSourcePosition(0).GetElevationDegrees(); elevationDial.setValue(mapElevationToSliderValue(elevation) * -1.f, // Elevation slider range is swapped because dontSendNotification); // it plays better with the rotary style } @@ -195,11 +197,13 @@ class SpatializerWidget : public Component, public Slider::Listener { private: AnechoicProcessor& mCore; - void applyElevationRotation() { // Elevation slider range is swapped because + void applyElevationRotation() { // Elevation slider range is swapped becauses auto degrees = mapSliderValueToElevation(elevationDial.getValue()) * -1.f; // it plays better with the rotary style - auto position = mCore.getSourcePosition(); + auto position = mCore.getSourcePosition (mCore.getSources().front()); position.SetFromAED(position.GetAzimuthDegrees(), degrees, position.GetDistance()); - mCore.setSourcePosition( position ); + + auto source = mCore.getSources().front(); + mCore.setSourcePosition (source, position); } bool mouseIsDown; diff --git a/Source/Binaural/v1/ReverbControls.cpp b/Source/Binaural/v1/ReverbControls.cpp new file mode 100644 index 0000000..b10e08a --- /dev/null +++ b/Source/Binaural/v1/ReverbControls.cpp @@ -0,0 +1,134 @@ +/** +* \class ReverbControls +* +* \brief Declaration of ReverbControls interface. +* \date June 2019 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2021 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#include "ReverbControls.h" + +ReverbControls::ReverbControls(Toolkit3dtiPluginAudioProcessor& p) + : mProcessor(p), + mReverb (p.getReverbProcessor()), + gainLabel("Level Label", "Level [dB]"), + distanceAttenuationLabel("Distance Label", "dB attenuation per double distance") +{ + std::vector options = {"Small", "Medium", "Large", "Library", "Trapezoid", "Load 3DTI", "Load SOFA"}; + for ( int i = 0; i < options.size(); i++ ) { + brirMenu.addItem( options[i], i+1 ); // IDs must be non-zero + } + brirMenu.onChange = [this] { brirMenuChanged(); }; + brirMenu.setSelectedId(1, dontSendNotification); + addAndMakeVisible( brirMenu ); + + setLabelStyle( gainLabel ); + gainLabel.setJustificationType( Justification::left ); + addAndMakeVisible( gainLabel ); + + mapParameterToSlider( gainSlider, mReverb.reverbLevel ); + gainSlider.setTextValueSuffix(" dB"); + gainSlider.setTextBoxStyle( Slider::TextBoxRight, false, 65, 24 ); + gainSlider.addListener( this ); + addAndMakeVisible( gainSlider ); + + distanceAttenuationToggle.setButtonText("On/Off"); + distanceAttenuationToggle.setToggleState(true, dontSendNotification); + distanceAttenuationToggle.onClick = [this] { updateDistanceAttenuation(); }; + addAndMakeVisible( distanceAttenuationToggle ); + + setLabelStyle( distanceAttenuationLabel ); + distanceAttenuationLabel.setJustificationType( Justification::left ); + addAndMakeVisible( distanceAttenuationLabel ); + + mapParameterToSlider( distanceAttenuationSlider, mReverb.reverbDistanceAttenuation ); + distanceAttenuationSlider.setTextValueSuffix(" dB"); + distanceAttenuationSlider.setTextBoxStyle( Slider::TextBoxRight, false, 65, 24 ); + distanceAttenuationSlider.addListener( this ); + addAndMakeVisible( distanceAttenuationSlider ); + + bypassToggle.setButtonText("On/Off"); + bypassToggle.setToggleState(true, dontSendNotification); + bypassToggle.onClick = [this] { updateBypass(); }; + addAndMakeVisible( bypassToggle ); + + updateGui(); +} + +void ReverbControls::updateGui() { + gainSlider.setValue(mReverb.reverbLevel.get(), dontSendNotification); + distanceAttenuationSlider.setValue(mReverb.reverbDistanceAttenuation, dontSendNotification ); + + if ( !mProcessor.getSources().empty() ) { + auto source = mProcessor.getSources().front(); + bypassToggle.setToggleState(source->IsReverbProcessEnabled(), dontSendNotification); + bool distanceAttenuationEnabled = source->IsDistanceAttenuationEnabledReverb(); + distanceAttenuationToggle.setToggleState(distanceAttenuationEnabled, dontSendNotification); + distanceAttenuationLabel.setEnabled(distanceAttenuationEnabled); + distanceAttenuationSlider.setEnabled(distanceAttenuationEnabled); + } + + auto brirIndex = mReverb.getBrirIndex(); + if ( brirIndex != brirMenu.getSelectedItemIndex() && brirIndex >= 0 && brirIndex < BundledBRIRs.size()) { // Show filename if custom file is selected + brirMenu.setSelectedItemIndex(brirIndex, dontSendNotification); + } +} + +void ReverbControls::loadCustomBRIR(String fileTypes) { + fc.reset (new FileChooser ("Choose a file to open...", + BRIRDirectory(), + fileTypes, + true)); + + fc->launchAsync(FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles, + [this] (const FileChooser& chooser) + { + String chosen; + auto results = chooser.getURLResults(); + + auto result = results.getFirst(); + + chosen << (result.isLocalFile() ? result.getLocalFile().getFullPathName() + : result.toString (false)); + + mReverb.loadBRIR(File(chosen.removeCharacters("\n"))); + + updateBrirLabel(); + }); +} + +void ReverbControls::updateBypass() { + bool enabled = bypassToggle.getToggleState(); + if ( enabled ) { + mProcessor.getSources().front()->EnableReverbProcess(); + } else { + mProcessor.getSources().front()->DisableReverbProcess(); + } + setAlpha( enabled + 0.4f ); +} + +void ReverbControls::updateBrirLabel() { + auto brir = mReverb.getBrirPath().getFileNameWithoutExtension(); + brirMenu.setText(brir, dontSendNotification); +} + +void ReverbControls::updateDistanceAttenuation() { + auto source = mProcessor.getSources().front(); + if ( distanceAttenuationToggle.getToggleState() ) { + source->EnableDistanceAttenuationReverb(); + } else { + source->DisableDistanceAttenuationReverb(); + } +} diff --git a/Source/Binaural/v1/ReverbControls.h b/Source/Binaural/v1/ReverbControls.h new file mode 100644 index 0000000..cf2afa0 --- /dev/null +++ b/Source/Binaural/v1/ReverbControls.h @@ -0,0 +1,104 @@ +/** +* \class ReverbControls +* +* \brief Declaration of ReverbControls interface. +* \date June 2019 +* +* \authors Reactify Music LLP: R. Hrafnkelsson || +* Coordinated by , A. Reyes-Lecuona (University of Malaga) and L.Picinali (Imperial College London) || +* \b Contact: areyes@uma.es and l.picinali@imperial.ac.uk +* +* \b Project: 3DTI (3D-games for TUNing and lEarnINg about hearing aids) || +* \b Website: http://3d-tune-in.eu/ +* +* \b Copyright: University of Malaga and Imperial College London - 2021 +* +* \b Licence: This copy of the 3D Tune-In Toolkit Plugin is licensed to you under the terms described in the LICENSE.md file included in this distribution. +* +* \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. +*/ + +#pragma once + +#include +#include "ReverbProcessor.h" +#include "SpatialisePluginProcessor.h" +#include "Utils.h" + +//============================================================================== +/* + */ + +class ReverbControls : public Component, public Slider::Listener { +public: + ReverbControls(Toolkit3dtiPluginAudioProcessor& processor); + + ~ReverbControls() {} + + void paint (Graphics& g) override { + g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); + g.setColour(Colours::grey); + g.drawRect(getLocalBounds(), 1); + g.setColour(Colours::white); + g.setFont(18.0f); + g.drawText("Reverberation", + getLocalBounds().withTrimmedBottom( getLocalBounds().getHeight() - 32 ), + Justification::centred, + true); + } + + void resized() override { + auto area = getLocalBounds(); + bypassToggle.setBounds( 10, 4, 80, 24 ); + brirMenu.setBounds( 12, 40, area.getWidth()-24, 22); + gainLabel.setBounds( 10, brirMenu.getBottom() + 8, area.getWidth()-20, 24); + gainSlider.setBounds( 6, gainLabel.getBottom(), area.getWidth()-18, 24); + distanceAttenuationToggle.setBounds( 10, gainSlider.getBottom() +2, 80, 24); + distanceAttenuationLabel.setBounds( 93, distanceAttenuationToggle.getY(), area.getWidth()-100, 24); + distanceAttenuationSlider.setBounds( 6, distanceAttenuationToggle.getBottom() + 4, area.getWidth()-18, 24); + } + + void updateGui(); + + void sliderValueChanged (Slider* slider) override { + if ( slider == &gainSlider ) { + mReverb.reverbLevel = (float)slider->getValue(); + } else { + mReverb.reverbDistanceAttenuation = (float)slider->getValue(); + } + } + + void brirMenuChanged() { + auto text = brirMenu.getText(); + if ( text == "Load 3DTI" ) { + loadCustomBRIR("*.3dti-brir"); + } else if ( text == "Load SOFA" ) { + loadCustomBRIR("*.sofa"); + } else { + mReverb.loadBRIR(brirMenu.getSelectedItemIndex()); + } + } + + void loadCustomBRIR(String fileTypes); + + ToggleButton bypassToggle; + +private: + + void updateBypass(); + void updateBrirLabel(); + void updateDistanceAttenuation(); + + Toolkit3dtiPluginAudioProcessor& mProcessor; + ReverbProcessor& mReverb; + + ComboBox brirMenu; + Label gainLabel; + Slider gainSlider; + ToggleButton distanceAttenuationToggle; + Label distanceAttenuationLabel; + Slider distanceAttenuationSlider; + std::unique_ptr fc; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbControls) +}; diff --git a/Source/Common/AboutBanner.h b/Source/Common/AboutBanner.h new file mode 100644 index 0000000..5bfd81d --- /dev/null +++ b/Source/Common/AboutBanner.h @@ -0,0 +1,60 @@ + +#pragma once + +#include + +//============================================================================== +/* +*/ +class AboutBanner : public Component +{ +public: + AboutBanner() + { + setOpaque (true); + + _3dTuneInLogo = ImageCache::getFromMemory(BinaryData::_3DTI_Logo_png, BinaryData::_3DTI_Logo_pngSize); + imperialLogo = ImageCache::getFromMemory(BinaryData::Imperial_Logo_png, BinaryData::Imperial_Logo_pngSize); + umaLogo = ImageCache::getFromMemory(BinaryData::UMA_Logo_png, BinaryData::UMA_Logo_pngSize); + + button.setButtonText("About"); + addAndMakeVisible( button ); + + toolkitVersionLabel.setFont(Font(15.f, Font::plain)); + toolkitVersionLabel.setText("Version " + String(JucePlugin_VersionString) + " (3DTI Toolkit v1.4)", dontSendNotification); + addAndMakeVisible (toolkitVersionLabel); + } + + ~AboutBanner() + { + } + + void paint (Graphics& g) override + { + g.fillAll (Colour (24, 31, 34)); + + auto r = getLocalBounds().reduced (0, 4).toFloat(); + auto size = r.proportionOfWidth (0.17); + g.drawImage (umaLogo, r.removeFromRight (size).reduced (2), RectanglePlacement::centred); + g.drawImage (imperialLogo, r.removeFromRight (size).reduced (2), RectanglePlacement::centred); + g.drawImage (_3dTuneInLogo, r.removeFromRight (size).reduced (2), RectanglePlacement::centred); + } + + void resized() override + { + auto r = getLocalBounds(); + button.setBounds (r.removeFromLeft (80).reduced (12)); + toolkitVersionLabel.setBounds (r); + } + + TextButton button; + +private: + Label toolkitVersionLabel; + + Image _3dTuneInLogo; + Image imperialLogo; + Image umaLogo; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AboutBanner) +}; diff --git a/Source/Common/StackedSliderComponent.h b/Source/Common/StackedSliderComponent.h index 9475783..8685fa1 100644 --- a/Source/Common/StackedSliderComponent.h +++ b/Source/Common/StackedSliderComponent.h @@ -50,6 +50,7 @@ class StackedSliderComponentT : public Component, public Slider::Listener addAndMakeVisible (slider); auto* label = new Label ("100", "100"); + label->setFont (Font (14)); label->setJustificationType (Justification::centred); labels.add (label); addAndMakeVisible (label); diff --git a/Source/HearingAidSimulator/ChannelSettingsComponent.cpp b/Source/HearingAidSimulator/ChannelSettingsComponent.cpp index 076aa4a..fa6930d 100644 --- a/Source/HearingAidSimulator/ChannelSettingsComponent.cpp +++ b/Source/HearingAidSimulator/ChannelSettingsComponent.cpp @@ -23,7 +23,7 @@ ChannelSettingsComponent::SingleChannelComponent::SingleChannelComponent (String heading, bool mirrored) : headingLabel (heading, heading), - gainLabel ("Gain", "Gain (dB)"), + gainLabel ("Gain", "Gain [dB]"), mirrored (mirrored) { headingLabel.setFont (Font (20)); diff --git a/Source/HearingAidSimulator/DynamicEQComponent.cpp b/Source/HearingAidSimulator/DynamicEQComponent.cpp index 4b07599..503a49a 100644 --- a/Source/HearingAidSimulator/DynamicEQComponent.cpp +++ b/Source/HearingAidSimulator/DynamicEQComponent.cpp @@ -104,12 +104,12 @@ DynamicEQComponent::DynamicEQComponent (HASPluginAudioProcessor& p) DynamicEQComponent::Channel::Channel (bool mirrored) : mirrored (mirrored), eqComponent (BANDS_NUMBER, Range (0.0, 65.0), 0.0, Slider::SliderStyle::ThreeValueVertical, Slider::TextBoxBelow, 0), - thresholdLabel ("Threshold", "Threshold"), - attackLabel ("Attack", "Attack"), - releaseLabel ("Release", "Release"), - compressionPercentLabel ("Compression (%)", "Compression (%)") + thresholdLabel ("Threshold", "Threshold\n[dBfs]"), + attackLabel ("Attack", "Attack [ms]"), + releaseLabel ("Release", "Release [ms]"), + compressionPercentLabel ("Compression", "Compression [%]") { - fig6Button.setButtonText ("Fig6"); + fig6Button.setButtonText ("Fit HA\nusing\nFig6"); addAndMakeVisible (fig6Button); // juce::Array slidercolours = {Colours::green, Colours::red, getLookAndFeel().findColour (Slider::thumbColourId)}; @@ -117,6 +117,8 @@ DynamicEQComponent::Channel::Channel (bool mirrored) for (auto* s : eqComponent.getSliders()) s->setPopupDisplayEnabled (true, true, this, -1); + thresholdLabel.setJustificationType (Justification::centred); + thresholdLabel.setFont (Font (14)); addAndMakeVisible (thresholdLabel); thresholdSlider.setSliderStyle (Slider::SliderStyle::ThreeValueVertical); @@ -147,8 +149,8 @@ DynamicEQComponent::Channel::Channel (bool mirrored) addAndMakeVisible (releaseLabel); addAndMakeVisible (compressionPercentLabel); - attackSlider.setRange (0.0f, 300.0f); - attackSlider.setValue (0.0f); + attackSlider.setRange (1.0f, 300.0f); + attackSlider.setValue (1.0f); attackSlider.setNumDecimalPlacesToDisplay (0); attackSlider.setSliderStyle (Slider::SliderStyle::LinearHorizontal); attackSlider.setTextBoxStyle (mirrored ? Slider::TextBoxLeft : Slider::TextBoxRight, @@ -186,11 +188,11 @@ void DynamicEQComponent::Channel::resized() Rectangle thresholdBounds; if (mirrored) - thresholdBounds = eqBounds.removeFromLeft (eqBounds.proportionOfWidth (0.2f)); + thresholdBounds = eqBounds.removeFromLeft (eqBounds.proportionOfWidth (0.22f)); else - thresholdBounds = eqBounds.removeFromRight (eqBounds.proportionOfWidth (0.2f)); + thresholdBounds = eqBounds.removeFromRight (eqBounds.proportionOfWidth (0.22f)); - thresholdLabel.setBounds (thresholdBounds.removeFromTop (30)); + thresholdLabel.setBounds (thresholdBounds.removeFromTop (28)); thresholdSlider.setBounds (thresholdBounds); eqComponent.setBounds (eqBounds); @@ -200,7 +202,7 @@ void DynamicEQComponent::Channel::resized() auto sectionHeight = r.proportionOfHeight (0.33f); if (mirrored) { - fig6Button.setBounds (r.removeFromLeft (r.proportionOfWidth (0.2f)).reduced (8).withHeight (36)); + fig6Button.setBounds (r.removeFromLeft (r.proportionOfWidth (0.2f)).reduced (8, 24)); auto attackBounds = r.removeFromTop (sectionHeight); attackLabel.setBounds (attackBounds.removeFromLeft (attackBounds.proportionOfWidth (0.25f))); attackSlider.setBounds (attackBounds); @@ -212,7 +214,7 @@ void DynamicEQComponent::Channel::resized() } else { - fig6Button.setBounds (r.removeFromRight (r.proportionOfWidth (0.2f)).reduced (8).withHeight (36)); + fig6Button.setBounds (r.removeFromRight (r.proportionOfWidth (0.2f)).reduced (8, 24)); auto attackBounds = r.removeFromTop (sectionHeight); attackLabel.setBounds (attackBounds.removeFromRight (attackBounds.proportionOfWidth (0.25f))); attackSlider.setBounds (attackBounds); diff --git a/Source/HearingAidSimulator/PluginEditor.cpp b/Source/HearingAidSimulator/PluginEditor.cpp index aca3a30..61bc90e 100644 --- a/Source/HearingAidSimulator/PluginEditor.cpp +++ b/Source/HearingAidSimulator/PluginEditor.cpp @@ -18,7 +18,7 @@ * \b Acknowledgement: This project has received funding from the European Union's Horizon 2020 research and innovation programme under grant agreements No 644051 and 726765. */ -#include "../Common/Constants.h" +#include "Common/Constants.h" #include "Presets.h" #include "PluginProcessor.h" #include "PluginEditor.h" @@ -30,28 +30,16 @@ HASPluginAudioProcessorEditor::HASPluginAudioProcessorEditor (HASPluginAudioProc channelSettingsComponent (p), dynamicEQComponent (p) { - addAndMakeVisible (aboutButton); - addAndMakeVisible (toolkitVersionLabel); - - _3dTuneInLogo = ImageCache::getFromMemory(BinaryData::_3DTI_Logo_png, BinaryData::_3DTI_Logo_pngSize); - imperialLogo = ImageCache::getFromMemory(BinaryData::Imperial_Logo_png, BinaryData::Imperial_Logo_pngSize); - umaLogo = ImageCache::getFromMemory(BinaryData::UMA_Logo_png, BinaryData::UMA_Logo_pngSize); - aboutText.setMultiLine(true); aboutText.setFont(Font(16.0f, Font::plain)); - aboutText.setText(String::fromUTF8(BinaryData::About_txt, BinaryData::About_txtSize)); + aboutText.setText(String::fromUTF8(BinaryData::About_HAS_txt, BinaryData::About_HAS_txtSize)); aboutText.setReadOnly(true); aboutText.setAlpha(0.9f); + aboutText.setEnabled (false); + aboutText.addMouseListener (this, true); aboutText.setVisible (false); - aboutButton.setButtonText("About"); - aboutButton.onClick = [this] { - aboutText.setVisible(! aboutText.isVisible()); - }; - addAndMakeVisible (aboutButton); - - toolkitVersionLabel.setFont(Font(15.f, Font::plain)); - toolkitVersionLabel.setText("Version " + String(JucePlugin_VersionString) + " (3DTI Toolkit v1.4)", dontSendNotification); + aboutBanner.button.onClick = [this] { aboutText.setVisible(!aboutText.isVisible()); }; audiogramComponent.reset (new AudiogramComponent (p.getBandFrequenciesAudiometry(), 2)); audiogramComponent->addListener (this); @@ -62,9 +50,10 @@ HASPluginAudioProcessorEditor::HASPluginAudioProcessorEditor (HASPluginAudioProc addAndMakeVisible (channelSettingsComponent); addAndMakeVisible (audiogramComponent.get()); addAndMakeVisible (dynamicEQComponent); + addAndMakeVisible (aboutBanner); addChildComponent (aboutText); - setSize (800, 900); + setSize (800, 960); startTimer (30); } @@ -106,30 +95,13 @@ void HASPluginAudioProcessorEditor::timerCallback() void HASPluginAudioProcessorEditor::paint (Graphics& g) { g.fillAll (Colour (24, 31, 34)); - - auto bounds = getLocalBounds(); - - auto aboutRect = bounds.removeFromTop (proportionOfHeight (0.06)); - g.setColour(getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker()); - g.fillRect(aboutRect); - g.setColour (Colours::grey); - g.drawRect(aboutRect); - - auto aboutRectf = aboutRect.toFloat(); - int amountToRemove = aboutRectf.proportionOfWidth (0.17); - g.drawImage (umaLogo, aboutRectf.removeFromRight (amountToRemove).reduced (4), RectanglePlacement::centred); - g.drawImage (imperialLogo, aboutRectf.removeFromRight (amountToRemove).reduced (4), RectanglePlacement::centred); - g.drawImage (_3dTuneInLogo, aboutRectf.removeFromRight (amountToRemove).reduced (4), RectanglePlacement::centred); } void HASPluginAudioProcessorEditor::resized() { auto r = getLocalBounds(); - auto aboutBounds = r.removeFromTop (proportionOfHeight (0.06)); - aboutButton.setBounds (aboutBounds.removeFromLeft (74).reduced (8)); - toolkitVersionLabel.setBounds (aboutBounds); - + aboutBanner.setBounds (r.removeFromTop (50)); aboutText.setBounds (r); channelSettingsComponent.setBounds (r.removeFromTop (r.proportionOfHeight (0.14f))); diff --git a/Source/HearingAidSimulator/PluginEditor.h b/Source/HearingAidSimulator/PluginEditor.h index b776416..fc86147 100644 --- a/Source/HearingAidSimulator/PluginEditor.h +++ b/Source/HearingAidSimulator/PluginEditor.h @@ -20,11 +20,12 @@ #pragma once -#include "JuceHeader.h" +#include #include "PluginProcessor.h" #include "ChannelSettingsComponent.h" #include "DynamicEQComponent.h" -#include "../Common/AudiogramComponent.h" +#include "Common/AboutBanner.h" +#include "Common/AudiogramComponent.h" //============================================================================== /** @@ -43,7 +44,7 @@ class HASPluginAudioProcessorEditor : public AudioProcessorEditor, void timerCallback() override; - void mouseDown(const MouseEvent &e) override { + void mouseUp (const MouseEvent &e) override { aboutText.setVisible(false); }; @@ -55,13 +56,7 @@ class HASPluginAudioProcessorEditor : public AudioProcessorEditor, HASPluginAudioProcessor& processor; TextEditor aboutText; - TextButton aboutButton; - Label pluginVersionLabel; - Label toolkitVersionLabel; - - Image _3dTuneInLogo; - Image imperialLogo; - Image umaLogo; + AboutBanner aboutBanner; ChannelSettingsComponent channelSettingsComponent; std::unique_ptr audiogramComponent; diff --git a/Source/HearingAidSimulator/PluginProcessor.cpp b/Source/HearingAidSimulator/PluginProcessor.cpp index 657bf24..4368df9 100644 --- a/Source/HearingAidSimulator/PluginProcessor.cpp +++ b/Source/HearingAidSimulator/PluginProcessor.cpp @@ -155,8 +155,8 @@ HASPluginAudioProcessor::HASPluginAudioProcessor() } } - addParameter (dynamicEQAttack[0] = new AudioParameterInt ("dynamic_eq_attack_left", "Attack Left", 0, 300, 100)); - addParameter (dynamicEQAttack[1] = new AudioParameterInt ("dynamic_eq_attack_right", "Attack Right", 0, 300, 100)); + addParameter (dynamicEQAttack[0] = new AudioParameterInt ("dynamic_eq_attack_left", "Attack Left", 1, 300, 100)); + addParameter (dynamicEQAttack[1] = new AudioParameterInt ("dynamic_eq_attack_right", "Attack Right", 1, 300, 100)); addParameter (dynamicEQRelease[0] = new AudioParameterInt ("dynamic_eq_release_left", "Release Left", 0, 2000, 1000)); addParameter (dynamicEQRelease[1] = new AudioParameterInt ("dynamic_eq_release_right", "Release Right", 0, 2000, 1000)); addParameter (dynamicEQCompressionPct[0] = new AudioParameterInt ("dynamic_eq_compression_pct_left", "Comp Pct Left", 0, 120, 100)); diff --git a/Source/HearingLossSimulator/FrequencySmearingProcessor.cpp b/Source/HearingLossSimulator/FrequencySmearingProcessor.cpp index 3e5e668..7c7aae6 100644 --- a/Source/HearingLossSimulator/FrequencySmearingProcessor.cpp +++ b/Source/HearingLossSimulator/FrequencySmearingProcessor.cpp @@ -23,15 +23,15 @@ static Common::T_ear ears[2] = {Common::T_ear::LEFT, Common::T_ear::RIGHT}; //============================================================================== -static bool compareFloat (float one, float two) +template +static bool compare (Type one, Type two) { - return (std::abs (one - two) > std::numeric_limits::epsilon()); + return (std::abs (one - two) > std::numeric_limits::epsilon()); } //============================================================================== FrequencySmearingProcessor::FrequencySmearingProcessor (HAHLSimulation::CHearingLossSim& sim) - : Thread ("FrequencySmearThread"), - simulator (sim) + : simulator (sim) { StringArray longSuffix = {"left", "right"}; StringArray shortSuffix = {"L", "R"}; @@ -110,21 +110,23 @@ void FrequencySmearingProcessor::updateSettingsIfNeeded() if (freqSmearType[channel]->get() == FrequencySmearType::BaerMoore) { - auto frequencySmearing = dynamic_pointer_cast (channels[ch]->baerMooreSmearing); - simulator.SetFrequencySmearer (ears[ch], frequencySmearing); + auto baerMooreSmearing = dynamic_pointer_cast (simulator.GetFrequencySmearingSimulator (ears[ch])); - // Cast back to Baer & Moore - auto baerMooreSmearing = static_pointer_cast (simulator.GetFrequencySmearingSimulator (ears[ch])); + if (! baerMooreSmearing) + { + simulator.SetFrequencySmearer (ears[ch], channels[ch]->baerMooreSmearing); + baerMooreSmearing = dynamic_pointer_cast (simulator.GetFrequencySmearingSimulator (ears[ch])); + } float value = freqSmearSpectralBroadFactorUp[channel]->get(); - if (compareFloat (value, previousSpectralBroadFactorUp[ch])) + if (compare (value, previousSpectralBroadFactorUp[ch])) { baerMooreSmearing->SetUpwardBroadeningFactor (value); previousSpectralBroadFactorUp[ch] = value; } value = freqSmearSpectralBroadFactorDown[channel]->get(); - if (compareFloat (value, previousSpectralBroadFactorDown[ch])) + if (compare (value, previousSpectralBroadFactorDown[ch])) { baerMooreSmearing->SetDownwardBroadeningFactor (value); previousSpectralBroadFactorDown[ch] = freqSmearSpectralBroadFactorDown[channel]->get(); @@ -132,36 +134,42 @@ void FrequencySmearingProcessor::updateSettingsIfNeeded() } else { - simulator.SetFrequencySmearer (ears[ch], dynamic_pointer_cast (channels[ch]->graf3dtiSmearing)); - // Graf & 3dti - auto frequencySmearer = static_pointer_cast (simulator.GetFrequencySmearingSimulator (ears[ch])); + auto graf3dtiSmearing = dynamic_pointer_cast (simulator.GetFrequencySmearingSimulator (ears[ch])); + + if (! graf3dtiSmearing) + { + simulator.SetFrequencySmearer (ears[ch], channels[ch]->graf3dtiSmearing); + graf3dtiSmearing = dynamic_pointer_cast (simulator.GetFrequencySmearingSimulator (ears[ch])); + } + + simulator.SetFrequencySmearer (ears[ch], dynamic_pointer_cast (channels[ch]->graf3dtiSmearing)); float value = freqSmearSpectralFrequencyDown[channel]->get(); - if (compareFloat (previousSpectralFrequencyDown[ch], value)) + if (compare (previousSpectralFrequencyDown[ch], value)) { - frequencySmearer->SetDownwardSmearing_Hz (value); + graf3dtiSmearing->SetDownwardSmearing_Hz (value); previousSpectralFrequencyDown[ch] = value; } value = freqSmearSpectralFrequencyUp[channel]->get(); - if (compareFloat (previousSpectralFrequencyUp[ch], value)) + if (compare (previousSpectralFrequencyUp[ch], value)) { - frequencySmearer->SetUpwardSmearing_Hz (value); + graf3dtiSmearing->SetUpwardSmearing_Hz (value); previousSpectralFrequencyUp[ch] = value; } value = freqSmearSpectralBufferSizeDown[channel]->get(); - if (compareFloat (previousSpectralBufferSizeDown[ch], value)) + if (compare (previousSpectralBufferSizeDown[ch], value)) { - frequencySmearer->SetDownwardSmearingBufferSize ((int)value); + graf3dtiSmearing->SetDownwardSmearingBufferSize ((int)value); previousSpectralBufferSizeDown[ch] = value; } value = freqSmearSpectralBufferSizeUp[channel]->get(); - if (compareFloat (previousSpectralBufferSizeUp[ch], value)) + if (compare (previousSpectralBufferSizeUp[ch], value)) { - frequencySmearer->SetUpwardSmearingBufferSize ((int)value); + graf3dtiSmearing->SetUpwardSmearingBufferSize ((int)value); previousSpectralBufferSizeUp[ch] = value; } } @@ -177,7 +185,7 @@ void FrequencySmearingProcessor::updateSettingsIfNeeded() FrequencySmearingProcessor::Channel::Channel() { baerMooreSmearing = make_shared(); - graf3dtiSmearing = make_shared(); + graf3dtiSmearing = make_shared(); } void FrequencySmearingProcessor::Channel::prepareToPlay (double sampleRate, int samplesPerBlock) diff --git a/Source/HearingLossSimulator/FrequencySmearingProcessor.h b/Source/HearingLossSimulator/FrequencySmearingProcessor.h index 81633ac..3f8fe66 100644 --- a/Source/HearingLossSimulator/FrequencySmearingProcessor.h +++ b/Source/HearingLossSimulator/FrequencySmearingProcessor.h @@ -26,16 +26,12 @@ using namespace HAHLSimulation; //============================================================================== -class FrequencySmearingProcessor : public Thread +class FrequencySmearingProcessor { public: FrequencySmearingProcessor (CHearingLossSim& sim); - ~FrequencySmearingProcessor() override - { - // allow the thread 2 seconds to stop cleanly - should be plenty of time. - stopThread (2000); - } + ~FrequencySmearingProcessor() {} //============================================================================== @@ -43,17 +39,6 @@ class FrequencySmearingProcessor : public Thread { for (auto const& channel : channels) channel->prepareToPlay (sampleRate, samplesPerBlock); - - startThread (1); - } - - void run() override - { - while (! threadShouldExit()) - { - wait (30); - updateSettingsIfNeeded(); - } } //============================================================================== @@ -88,9 +73,10 @@ class FrequencySmearingProcessor : public Thread const OwnedArray& getChannels() { return channels; } //============================================================================== -private: void updateSettingsIfNeeded(); +private: + CHearingLossSim& simulator; OwnedArray channels; diff --git a/Source/HearingLossSimulator/PluginEditor.cpp b/Source/HearingLossSimulator/PluginEditor.cpp index e9c6836..78d3452 100644 --- a/Source/HearingLossSimulator/PluginEditor.cpp +++ b/Source/HearingLossSimulator/PluginEditor.cpp @@ -31,29 +31,15 @@ HLSPluginAudioProcessorEditor::HLSPluginAudioProcessorEditor (HLSPluginAudioProc temporalDistortionComponent (p), frequencySmearingComponent (p) { - //addAndMakeVisible (aboutText); - addAndMakeVisible (aboutButton); - addAndMakeVisible (toolkitVersionLabel); - - _3dTuneInLogo = ImageCache::getFromMemory(BinaryData::_3DTI_Logo_png, BinaryData::_3DTI_Logo_pngSize); - imperialLogo = ImageCache::getFromMemory(BinaryData::Imperial_Logo_png, BinaryData::Imperial_Logo_pngSize); - umaLogo = ImageCache::getFromMemory(BinaryData::UMA_Logo_png, BinaryData::UMA_Logo_pngSize); - aboutText.setMultiLine(true); aboutText.setFont(Font(16.0f, Font::plain)); - aboutText.setText(String::fromUTF8(BinaryData::About_txt, BinaryData::About_txtSize)); + aboutText.setText(String::fromUTF8(BinaryData::About_HLS_txt, BinaryData::About_HLS_txtSize)); aboutText.setReadOnly(true); aboutText.setAlpha(0.9f); + aboutText.addMouseListener (this, true); aboutText.setVisible (false); - aboutButton.setButtonText("About"); - aboutButton.onClick = [this] { - aboutText.setVisible(! aboutText.isVisible()); - }; - addAndMakeVisible (aboutButton); - - toolkitVersionLabel.setFont(Font(15.f, Font::plain)); - toolkitVersionLabel.setText("Version " + String(JucePlugin_VersionString) + " (3DTI Toolkit v1.4)", dontSendNotification); + aboutBanner.button.onClick = [this] { aboutText.setVisible(!aboutText.isVisible()); }; audiogramComponent.reset (new AudiogramComponent (p.getBandFrequencies(), 2)); audiogramComponent->addListener (this); @@ -66,6 +52,7 @@ HLSPluginAudioProcessorEditor::HLSPluginAudioProcessorEditor (HLSPluginAudioProc addAndMakeVisible (nonLinearAttenuatorComponent); addAndMakeVisible (temporalDistortionComponent); addAndMakeVisible (frequencySmearingComponent); + addAndMakeVisible (aboutBanner); addChildComponent (aboutText); setSize (800, 800); @@ -109,37 +96,21 @@ void HLSPluginAudioProcessorEditor::timerCallback() void HLSPluginAudioProcessorEditor::paint (Graphics& g) { g.fillAll (Colour (24, 31, 34)); - - auto bounds = getLocalBounds(); - - auto aboutRect = bounds.removeFromTop (proportionOfHeight (0.06)); - g.setColour(getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker()); - g.fillRect(aboutRect); - g.setColour (Colours::grey); - g.drawRect(aboutRect); - - auto aboutRectf = aboutRect.toFloat(); - int amountToRemove = aboutRectf.proportionOfWidth (0.17); - g.drawImage (umaLogo, aboutRectf.removeFromRight (amountToRemove).reduced (4), RectanglePlacement::centred); - g.drawImage (imperialLogo, aboutRectf.removeFromRight (amountToRemove).reduced (4), RectanglePlacement::centred); - g.drawImage (_3dTuneInLogo, aboutRectf.removeFromRight (amountToRemove).reduced (4), RectanglePlacement::centred); } void HLSPluginAudioProcessorEditor::resized() { - auto bounds = getLocalBounds(); + auto r = getLocalBounds(); - auto aboutBounds = bounds.removeFromTop (proportionOfHeight (0.06)); - aboutButton.setBounds (aboutBounds.removeFromLeft (74).reduced (8)); - toolkitVersionLabel.setBounds (aboutBounds); + aboutBanner.setBounds (r.removeFromTop (50)); - aboutText.setBounds (bounds); + aboutText.setBounds (r); - channelSwitchComponent.setBounds (bounds.removeFromTop (proportionOfHeight (0.05))); - audiogramComponent->setBounds (bounds.removeFromTop (proportionOfHeight (0.25f))); - nonLinearAttenuatorComponent.setBounds (bounds.removeFromTop (proportionOfHeight (0.1))); - temporalDistortionComponent.setBounds (bounds.removeFromTop (bounds.proportionOfHeight (0.5))); - frequencySmearingComponent.setBounds (bounds); + channelSwitchComponent.setBounds (r.removeFromTop (proportionOfHeight (0.05))); + audiogramComponent->setBounds (r.removeFromTop (proportionOfHeight (0.25f))); + nonLinearAttenuatorComponent.setBounds (r.removeFromTop (proportionOfHeight (0.1))); + temporalDistortionComponent.setBounds (r.removeFromTop (r.proportionOfHeight (0.5))); + frequencySmearingComponent.setBounds (r); } void HLSPluginAudioProcessorEditor::audiogramPresetSelected (int channel, int index) diff --git a/Source/HearingLossSimulator/PluginEditor.h b/Source/HearingLossSimulator/PluginEditor.h index 40f4c05..65e6628 100644 --- a/Source/HearingLossSimulator/PluginEditor.h +++ b/Source/HearingLossSimulator/PluginEditor.h @@ -20,13 +20,14 @@ #pragma once -#include "JuceHeader.h" +#include #include "PluginProcessor.h" #include "ChannelSwitchComponent.h" #include "NonLinearAttenuatorComponent.h" #include "TemporalDistortionComponent.h" #include "FrequencySmearingComponent.h" -#include "../Common/AudiogramComponent.h" +#include "Common/AboutBanner.h" +#include "Common/AudiogramComponent.h" //============================================================================== /** @@ -42,7 +43,7 @@ class HLSPluginAudioProcessorEditor : public AudioProcessorEditor, public Timer, void timerCallback() override; - void mouseDown(const MouseEvent &e) override { + void mouseUp (const MouseEvent &e) override { aboutText.setVisible(false); }; @@ -54,13 +55,7 @@ class HLSPluginAudioProcessorEditor : public AudioProcessorEditor, public Timer, HLSPluginAudioProcessor& processor; TextEditor aboutText; - TextButton aboutButton; - Label pluginVersionLabel; - Label toolkitVersionLabel; - - Image _3dTuneInLogo; - Image imperialLogo; - Image umaLogo; + AboutBanner aboutBanner; ChannelSwitchComponent channelSwitchComponent; std::unique_ptr audiogramComponent; diff --git a/Source/HearingLossSimulator/PluginProcessor.cpp b/Source/HearingLossSimulator/PluginProcessor.cpp index 6adfc1d..6665131 100644 --- a/Source/HearingLossSimulator/PluginProcessor.cpp +++ b/Source/HearingLossSimulator/PluginProcessor.cpp @@ -20,11 +20,12 @@ #include #include -#include "../Common/Constants.h" +#include "Common/Constants.h" #include "PluginProcessor.h" #include "PluginEditor.h" #define NUM_BANDS Constants::NUM_BANDS +static constexpr int kINTERNAL_BLOCK_SIZE = 512; static Common::T_ear ears[2] = {Common::T_ear::LEFT, Common::T_ear::RIGHT}; @@ -49,8 +50,8 @@ HLSPluginAudioProcessor::HLSPluginAudioProcessor() // addParameter (channelsLink = new AudioParameterBool ("channels_link", "Channel Link", false)); - addParameter (enableSimulation[0] = new AudioParameterBool ("enable_simulation_left", "Enable Left", false)); - addParameter (enableSimulation[1] = new AudioParameterBool ("enable_simulation_right", "Enable Right", false)); + addParameter (enableSimulation[0] = new AudioParameterBool ("enable_simulation_left", "Enable Left", true)); + addParameter (enableSimulation[1] = new AudioParameterBool ("enable_simulation_right", "Enable Right", true)); for (int i = 0; i < NUM_BANDS; i++) { @@ -96,10 +97,10 @@ HLSPluginAudioProcessor::HLSPluginAudioProcessor() addParameter (temporalDistortionLink = new AudioParameterBool ("temporal_distortion_link", "Temp Dist Link", false)); addParameter (jitterBandLimit[0] = new AudioParameterInt ("jitter_band_limit_left", "Jitter Band Limit Left", - 0, 6, 0)); + 0, 6, 3)); addParameter (jitterBandLimit[1] = new AudioParameterInt ("jitter_band_limit_right", "Jitter Band Limit Right", - 0, 6, 0)); + 0, 6, 3)); addParameter (jitterNoisePower[0] = new AudioParameterFloat ("jitter_noise_power_left", "Jitter Noise L", NormalisableRange (0.0f, 1.0f), @@ -118,10 +119,10 @@ HLSPluginAudioProcessor::HLSPluginAudioProcessor() [] (const String& text) { return text.getFloatValue(); })); addParameter (jitterNoiseAutocorrelationCutoff[0] = new AudioParameterInt ("jitter_autocorrelation_cutoff_left", "Jitter Autocorrelation Cutoff Left", - 0, 1000, 0)); + 0, 1000, 500)); addParameter (jitterNoiseAutocorrelationCutoff[1] = new AudioParameterInt ("jitter_autocorrelation_cutoff_right", "Jitter Autocorrelation Cutoff Right", - 0, 1000, 0)); + 0, 1000, 500)); addParameter (jitterLeftRightSynchronicity = new AudioParameterFloat ("jitter_left_right_synchronicity", "Jitter L/R Sync", NormalisableRange (0.0f, 1.0f), @@ -218,7 +219,18 @@ void HLSPluginAudioProcessor::changeProgramName (int index, const String& newNam //============================================================================== void HLSPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { - simulator.Setup ((int)sampleRate, 100, NUM_BANDS, samplesPerBlock); + auto const maxChannels = std::max (getTotalNumInputChannels(), getTotalNumOutputChannels()); + + auto const blockSizeInternal = kINTERNAL_BLOCK_SIZE; + inFifo.clear(); + inFifo.setSize (maxChannels, std::max (blockSizeInternal, samplesPerBlock) + 1); + + outFifo.clear(); + outFifo.setSize (maxChannels, std::max (samplesPerBlock, blockSizeInternal) * 2); + + scratchBuffer.setSize (maxChannels, blockSizeInternal); + + simulator.Setup ((int)sampleRate, 100, NUM_BANDS, blockSizeInternal); // Set up multiband expanders for (int i = 0; i < getTotalNumOutputChannels(); i++) @@ -242,14 +254,14 @@ void HLSPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBl gammatoneExpanders.add (gammatoneExpander); } - frequencySmearProcessor.prepareToPlay (sampleRate, samplesPerBlock); + frequencySmearProcessor.prepareToPlay (sampleRate, blockSizeInternal); // Set up buffers - bIn.left .assign (samplesPerBlock, 0.0f); - bIn.right.assign (samplesPerBlock, 0.0f); + bIn.left .assign (blockSizeInternal, 0.0f); + bIn.right.assign (blockSizeInternal, 0.0f); - bOut.left .assign (samplesPerBlock, 0.0f); - bOut.right.assign (samplesPerBlock, 0.0f); + bOut.left .assign (blockSizeInternal, 0.0f); + bOut.right.assign (blockSizeInternal, 0.0f); } void HLSPluginAudioProcessor::releaseResources() @@ -289,18 +301,54 @@ void HLSPluginAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuff auto inChannels = getTotalNumInputChannels(); auto outChannels = getTotalNumOutputChannels(); - for (auto i = inChannels; i < outChannels; ++i) { - buffer.clear(i, 0, buffer.getNumSamples()); + for (auto i = inChannels; i < outChannels; ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + + inFifo.addToFifo (buffer); + + const int numSamples = buffer.getNumSamples(); + const int blockSizeInternal = kINTERNAL_BLOCK_SIZE; + + while (inFifo.getNumReady() >= blockSizeInternal) + { + scratchBuffer.clear(); + + inFifo.readFromFifo (scratchBuffer); + + processBlockInternal (scratchBuffer); + + outFifo.addToFifo (scratchBuffer); + } + + int numReady = outFifo.getNumReady(); + if (numReady < numSamples) + { + int diff = numSamples - numReady; + outFifo.addSilenceToFifo (diff); + + // Update the host latency + int latency = getLatencySamples() + diff; + setLatencySamples (latency); } + outFifo.readFromFifo (buffer); +} + +void HLSPluginAudioProcessor::processBlockInternal (AudioBuffer& buffer) +{ // We currently assume working in stereo jassert (buffer.getNumChannels() == 2); + jassert (bIn.left.size() >= buffer.getNumSamples()); + jassert (bOut.left.size() >= buffer.getNumSamples()); int numSamples = buffer.getNumSamples(); /* /// Update parameters */ + + frequencySmearProcessor.updateSettingsIfNeeded(); + for (int ch = 0; ch < getTotalNumOutputChannels(); ch++) { if (enableSimulation[ch]->get()) @@ -343,13 +391,11 @@ void HLSPluginAudioProcessor::processBlock (AudioBuffer& buffer, MidiBuff temporalDistortionSimulator->SetNoiseAutocorrelationFilterCutoffFrequency (ears[i], jitterNoiseAutocorrelationCutoff[channel]->get()); } temporalDistortionSimulator->SetLeftRightNoiseSynchronicity (jitterLeftRightSynchronicity->get()); - - // frequencySmearProcessor.processBlock (buffer); // Fill input buffer and clear output for (int i = 0; i < numSamples; i++) { - bIn.left[i] = buffer.getReadPointer (0)[i]; + bIn.left [i] = buffer.getReadPointer (0)[i]; bIn.right[i] = buffer.getReadPointer (1)[i]; } diff --git a/Source/HearingLossSimulator/PluginProcessor.h b/Source/HearingLossSimulator/PluginProcessor.h index febca9f..bf18a0e 100644 --- a/Source/HearingLossSimulator/PluginProcessor.h +++ b/Source/HearingLossSimulator/PluginProcessor.h @@ -20,6 +20,8 @@ #pragma once +#include +#include #include #include "FrequencySmearingProcessor.h" @@ -122,10 +124,16 @@ class HLSPluginAudioProcessor : public AudioProcessor, private AudioProcessorVa // Frequency Smear settings FrequencySmearingProcessor frequencySmearProcessor; private: + void processBlockInternal (AudioBuffer&); + //============================================================================== Common::CEarPair> bIn; Common::CEarPair> bOut; + AudioBuffer scratchBuffer; + AudioBufferFIFO inFifo {2, 512}, + outFifo {2, 512}; + juce::Array> butterWorthExpanders; juce::Array> gammatoneExpanders; diff --git a/Source/HearingLossSimulator/Presets.h b/Source/HearingLossSimulator/Presets.h index 1db785f..f651f32 100644 --- a/Source/HearingLossSimulator/Presets.h +++ b/Source/HearingLossSimulator/Presets.h @@ -2,7 +2,7 @@ #pragma once #include -#include "../Common/Constants.h" +#include "Common/Constants.h" // ============================================================================= static std::vector> hearingLossPresetValues = { diff --git a/Source/HearingLossSimulator/TemporalDistortionComponent.h b/Source/HearingLossSimulator/TemporalDistortionComponent.h index bdaec94..cab7d67 100644 --- a/Source/HearingLossSimulator/TemporalDistortionComponent.h +++ b/Source/HearingLossSimulator/TemporalDistortionComponent.h @@ -241,7 +241,7 @@ class TemporalDistortionComponent : public Component, public Button::Listener, p bandLimitSlider->setSliderStyle (Slider::SliderStyle::RotaryVerticalDrag); bandLimitSlider->setTextBoxStyle (Slider::TextBoxBelow, false, - 40, + 60, 20); bandLimitSlider->textFromValueFunction = [] (double value) { static std::map mappedLabels = { @@ -253,7 +253,7 @@ class TemporalDistortionComponent : public Component, public Button::Listener, p {5.0, "6k4"}, {6.0, "12k8"}, }; - return mappedLabels[value]; + return mappedLabels[value] + " Hz"; }; sliders.add (bandLimitSlider); @@ -265,13 +265,16 @@ class TemporalDistortionComponent : public Component, public Button::Listener, p Slider* noisePowerSlider; addAndMakeVisible (noisePowerSlider = new Slider); + noisePowerSlider->textFromValueFunction = [](double value) { + return String (value, 1) + " ms^s"; + }; noisePowerSlider->setNormalisableRange (NormalisableRange (0.0, 1.0)); - noisePowerSlider->setNumDecimalPlacesToDisplay (1); noisePowerSlider->setSliderStyle (Slider::SliderStyle::RotaryVerticalDrag); noisePowerSlider->setTextBoxStyle (Slider::TextBoxBelow, false, - 40, + 60, 20); + noisePowerSlider->setValue (0.0, dontSendNotification); sliders.add (noisePowerSlider); noisePowerLabel.setFont (labelFont); @@ -285,10 +288,10 @@ class TemporalDistortionComponent : public Component, public Button::Listener, p noiseAutocorrelationCutoffSlider->setSliderStyle (Slider::SliderStyle::RotaryVerticalDrag); noiseAutocorrelationCutoffSlider->setTextBoxStyle (Slider::TextBoxBelow, false, - 40, + 60, 20); noiseAutocorrelationCutoffSlider->textFromValueFunction = [] (double value) { - return value == 1000.0 ? "1k" : String (value, 0); + return value == 1000.0 ? "1k" : String (value, 0) + " Hz"; }; sliders.add (noiseAutocorrelationCutoffSlider); diff --git a/Source/Utils.h b/Source/Utils.h index 3111707..083718e 100644 --- a/Source/Utils.h +++ b/Source/Utils.h @@ -28,7 +28,7 @@ #include #include #include -#include "../JuceLibraryCode/JuceHeader.h" +#include static StringArray BundledHRTFs = { "3DTI_HRTF_IRC1008_256s", @@ -46,8 +46,8 @@ static StringArray BundledBRIRs = { "3DTI_BRIR_small", "3DTI_BRIR_medium", "3DTI_BRIR_large", - "Load 3DTI", - "Load SOFA", + "BRIR_Library", + "BRIR_Trapezoid", }; static const std::unordered_map SampleRateToDefaultHRTF_ILD { @@ -76,10 +76,6 @@ static inline int hrtfPathToBundledIndex(const File& path) { static inline int brirPathToBundledIndex(const File& path) { String fileName = path.getFileNameWithoutExtension(); auto index = BundledBRIRs.indexOf(fileName.upToLastOccurrenceOf("_", false, false), false); - if ( index == -1 ) { - bool isSofa = path.getFileExtension() == "sofa"; - return isSofa ? BundledHRTFs.size()-1 : BundledHRTFs.size()-2; - } return index; } @@ -150,6 +146,14 @@ static inline Point getCentref(Component& component) { // Audio Utils // +inline CMonoBuffer juceTo3dti (AudioBuffer& buffer) +{ + int numSamples = buffer.getNumSamples(); + CMonoBuffer copy (numSamples); + std::memcpy (copy.data(), buffer.getReadPointer (0), numSamples*sizeof(float)); + return copy; +} + static inline void _3dti_clear(Common::CEarPair>& buffer) { CMonoBuffer& l = buffer.left; CMonoBuffer& r = buffer.right; @@ -174,7 +178,7 @@ static inline juce::File resourceDirectory() { if ( isWindows() ) { return File::getSpecialLocation(File::userApplicationDataDirectory).getChildFile("eu.3d-tune-in.toolkitplugin"); } - return File::getSpecialLocation(File::currentExecutableFile).getParentDirectory().getParentDirectory(); + return File::getSpecialLocation(File::commonApplicationDataDirectory).getChildFile("Application Support").getChildFile("eu.3d-tune-in.plugins"); } static inline juce::File HRTFDirectory() { @@ -196,7 +200,6 @@ static inline File getBundledHRTF(int index, double sampleRate) { } static inline File getBundledBRIR(int index, double sampleRate) { - index = jlimit(0, BundledBRIRs.size()-3, index); - auto brirName = "3DTI/" + BundledBRIRs[index] + "_" + String(sampleRate) + "Hz.3dti-brir"; + auto brirName = "SOFA/" + BundledBRIRs[index] + "_" + String(sampleRate) + "Hz.sofa"; return BRIRDirectory().getChildFile( brirName ); } diff --git a/extras/InstallerSetup.iss b/extras/InstallerSetup.iss index a22d2d5..9c8e91d 100644 --- a/extras/InstallerSetup.iss +++ b/extras/InstallerSetup.iss @@ -12,7 +12,7 @@ ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{30BAF99F-B01C-4A65-ACCC-C7F997F22D58} AppName={#MyAppName} -AppVersion=1.0.2 +AppVersion=1.1.1 ;AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} @@ -21,7 +21,7 @@ AppUpdatesURL={#MyAppURL} UsePreviousAppDir=no DisableDirPage=no CreateAppDir=no -OutputBaseFilename=3DTI_Toolkit_VST_plugin_WIN_Installer-v1.0.2 +OutputBaseFilename=3DTI_Toolkit_VST_plugins_WIN_Installer_x64-v1.1.1 Compression=lzma SolidCompression=yes ArchitecturesInstallIn64BitMode=x64 @@ -35,11 +35,16 @@ Name: "full"; Description: "Full installation" ;Name: "custom"; Description: "Custom installation"; Flags: iscustom [Components] -Name: "vst2_64"; Description: "64-bit VST2 Plugin (.dll)"; Types: full; Check: Is64BitInstallMode; +Name: "vst2_64"; Description: "64-bit VST2 Plugins (.dll)"; Types: full; Check: Is64BitInstallMode; [Files] -Source: "..\Builds\VisualStudio2017\x64\Release\VST\3D Tune-In Toolkit.dll"; DestDir: {code:GetVST2Dir_64}; Check: Is64BitInstallMode; Components:vst2_64; Flags: ignoreversion; -Source: "..\libs\3dti_AudioToolkit\resources\*"; DestDir: "{userappdata}\eu.3d-tune-in.toolkitplugin\Resources"; Flags:createallsubdirs recursesubdirs comparetimestamp +Source: "..\Builds\VisualStudio2017\x64\Release\VST\3DTI Spatialisation.dll"; DestDir: {code:GetVST2Dir_64}; Check: Is64BitInstallMode; Components:vst2_64; Flags: ignoreversion; +Source: "..\3dti_Anechoic\Builds\VisualStudio2017\x64\Release\VST\3dti_Anechoic.dll"; DestDir: {code:GetVST2Dir_64}; Check: Is64BitInstallMode; Components:vst2_64; Flags: ignoreversion; +Source: "..\3dti_Reverb\Builds\VisualStudio2017\x64\Release\VST\3dti_Reverb.dll"; DestDir: {code:GetVST2Dir_64}; Check: Is64BitInstallMode; Components:vst2_64; Flags: ignoreversion; +Source: "..\3dti_Hearing_Aid_Simulator\Builds\VisualStudio2017\x64\Release\VST\3dti_Hearing_Aid_Simulator.dll"; DestDir: {code:GetVST2Dir_64}; Check: Is64BitInstallMode; Components:vst2_64; Flags: ignoreversion; +Source: "..\3dti_Hearing_Loss_Simulator\Builds\VisualStudio2017\x64\Release\VST\3dti_Hearing_Loss_Simulator.dll"; DestDir: {code:GetVST2Dir_64}; Check: Is64BitInstallMode; Components:vst2_64; Flags: ignoreversion; + +Source: "..\libs\3dti_AudioToolkit\resources\*"; DestDir: "{userappdata}\eu.3d-tune-in.toolkitplugin\Resources"; Flags:createallsubdirs recursesubdirs comparetimestamp; Source: "..\libs\3dti_AudioToolkit\3dti_ResourceManager\third_party_libraries\sofacoustics\libsofa\dependencies\lib\win\x64\*"; DestDir: {sys}; Source: "..\libs\3dti_AudioToolkit\3dti_ResourceManager\third_party_libraries\sofacoustics\libsofa\lib\libsofa_x64.lib"; DestDir: {sys}; diff --git a/extras/InstallerSetup.pkgproj b/extras/InstallerSetup.pkgproj new file mode 100644 index 0000000..916f854 --- /dev/null +++ b/extras/InstallerSetup.pkgproj @@ -0,0 +1,1850 @@ + + + + + PACKAGES + + + MUST-CLOSE-APPLICATION-ITEMS + + MUST-CLOSE-APPLICATIONS + + PACKAGE_FILES + + DEFAULT_INSTALL_LOCATION + /Library/Application Support/eu.3d-tune-in.plugins/Resources + HIERARCHY + + CHILDREN + + + CHILDREN + + GID + 80 + PATH + Applications + PATH_TYPE + 0 + PERMISSIONS + 509 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + + CHILDREN + + + CHILDREN + + + CHILDREN + + GID + 80 + PATH + ../libs/3dti_AudioToolkit/resources/BRIR + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + ../libs/3dti_AudioToolkit/resources/HRTF + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + ../libs/3dti_AudioToolkit/resources/ILD + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + GID + 80 + PATH + Resources + PATH_TYPE + 2 + PERMISSIONS + 509 + TYPE + 2 + UID + 0 + + + GID + 80 + PATH + eu.3d-tune-in.plugins + PATH_TYPE + 2 + PERMISSIONS + 509 + TYPE + 2 + UID + 0 + + + GID + 80 + PATH + Application Support + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Automator + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Documentation + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Extensions + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Filesystems + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Frameworks + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Input Methods + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Internet Plug-Ins + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + LaunchAgents + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + LaunchDaemons + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + PreferencePanes + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Preferences + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + Printers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + PrivilegedHelperTools + PATH_TYPE + 0 + PERMISSIONS + 1005 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + QuickLook + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + QuickTime + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Screen Savers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Scripts + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Services + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Widgets + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + Library + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + Shared + PATH_TYPE + 0 + PERMISSIONS + 1023 + TYPE + 1 + UID + 0 + + + GID + 80 + PATH + Users + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + / + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + PAYLOAD_TYPE + 0 + PRESERVE_EXTENDED_ATTRIBUTES + + SHOW_INVISIBLE + + SPLIT_FORKS + + TREAT_MISSING_FILES_AS_WARNING + + VERSION + 5 + + PACKAGE_SCRIPTS + + POSTINSTALL_PATH + + PATH_TYPE + 0 + + PREINSTALL_PATH + + PATH_TYPE + 0 + + RESOURCES + + + PACKAGE_SETTINGS + + AUTHENTICATION + 1 + CONCLUSION_ACTION + 0 + FOLLOW_SYMBOLIC_LINKS + + IDENTIFIER + eu.3d-tune-in.resources + LOCATION + 0 + NAME + Resources + OVERWRITE_PERMISSIONS + + PAYLOAD_SIZE + -1 + REFERENCE_PATH + + RELOCATABLE + + USE_HFS+_COMPRESSION + + VERSION + 1.0 + + TYPE + 0 + UUID + B9E2D658-B850-4B8D-AB7F-EB691F0B1299 + + + MUST-CLOSE-APPLICATION-ITEMS + + MUST-CLOSE-APPLICATIONS + + PACKAGE_FILES + + DEFAULT_INSTALL_LOCATION + /Library/Audio/Plug-Ins/VST + HIERARCHY + + CHILDREN + + + CHILDREN + + + CHILDREN + + GID + 80 + PATH + Utilities + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + GID + 80 + PATH + Applications + PATH_TYPE + 0 + PERMISSIONS + 509 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + bin + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 80 + PATH + Application Support + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + + CHILDREN + + + BUNDLE_CAN_DOWNGRADE + + BUNDLE_POSTINSTALL_PATH + + PATH_TYPE + 0 + + BUNDLE_PREINSTALL_PATH + + PATH_TYPE + 0 + + CHILDREN + + GID + 0 + PATH + ../Builds/MacOS/3dti_Anechoic.vst + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + BUNDLE_CAN_DOWNGRADE + + BUNDLE_POSTINSTALL_PATH + + PATH_TYPE + 0 + + BUNDLE_PREINSTALL_PATH + + PATH_TYPE + 0 + + CHILDREN + + GID + 0 + PATH + ../Builds/MacOS/3dti_Hearing_Aid_Simulator.vst + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + BUNDLE_CAN_DOWNGRADE + + BUNDLE_POSTINSTALL_PATH + + PATH_TYPE + 0 + + BUNDLE_PREINSTALL_PATH + + PATH_TYPE + 0 + + CHILDREN + + GID + 0 + PATH + ../Builds/MacOS/3dti_Hearing_Loss_Simulator.vst + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + BUNDLE_CAN_DOWNGRADE + + BUNDLE_POSTINSTALL_PATH + + PATH_TYPE + 0 + + BUNDLE_PREINSTALL_PATH + + PATH_TYPE + 0 + + CHILDREN + + GID + 0 + PATH + ../Builds/MacOS/3dti_Reverb.vst + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + BUNDLE_CAN_DOWNGRADE + + BUNDLE_POSTINSTALL_PATH + + PATH_TYPE + 0 + + BUNDLE_PREINSTALL_PATH + + PATH_TYPE + 0 + + CHILDREN + + GID + 0 + PATH + ../Builds/MacOS/3dti_Spatialisation.vst + PATH_TYPE + 1 + PERMISSIONS + 493 + TYPE + 3 + UID + 0 + + + GID + 0 + PATH + VST + PATH_TYPE + 2 + PERMISSIONS + 509 + TYPE + 2 + UID + 0 + + + GID + 0 + PATH + Plug-Ins + PATH_TYPE + 2 + PERMISSIONS + 509 + TYPE + 2 + UID + 0 + + + GID + 0 + PATH + Audio + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Automator + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + ColorPickers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Documentation + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Extensions + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Filesystems + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + Fonts + PATH_TYPE + 0 + PERMISSIONS + 1021 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Frameworks + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Input Methods + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Internet Plug-Ins + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + LaunchAgents + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + LaunchDaemons + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + PreferencePanes + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Preferences + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 80 + PATH + Printers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + PrivilegedHelperTools + PATH_TYPE + 0 + PERMISSIONS + 1005 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + QuickLook + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + QuickTime + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Screen Savers + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Scripts + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Services + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + Widgets + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + Library + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + etc + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + var + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + GID + 0 + PATH + private + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + sbin + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + Extensions + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + Library + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + GID + 0 + PATH + System + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + Shared + PATH_TYPE + 0 + PERMISSIONS + 1023 + TYPE + 1 + UID + 0 + + + GID + 80 + PATH + Users + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + bin + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + include + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + lib + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + + CHILDREN + + GID + 0 + PATH + bin + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + GID + 0 + PATH + local + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + sbin + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + CHILDREN + + GID + 0 + PATH + share + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + GID + 0 + PATH + usr + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + -1 + UID + 0 + + + GID + 0 + PATH + / + PATH_TYPE + 0 + PERMISSIONS + 493 + TYPE + 1 + UID + 0 + + PAYLOAD_TYPE + 0 + PRESERVE_EXTENDED_ATTRIBUTES + + SHOW_INVISIBLE + + SPLIT_FORKS + + TREAT_MISSING_FILES_AS_WARNING + + VERSION + 5 + + PACKAGE_SCRIPTS + + POSTINSTALL_PATH + + PATH_TYPE + 0 + + PREINSTALL_PATH + + PATH_TYPE + 0 + + RESOURCES + + + PACKAGE_SETTINGS + + AUTHENTICATION + 1 + CONCLUSION_ACTION + 0 + FOLLOW_SYMBOLIC_LINKS + + IDENTIFIER + eu.3d-tune-in.anechoic + LOCATION + 0 + NAME + Plugins + OVERWRITE_PERMISSIONS + + PAYLOAD_SIZE + -1 + REFERENCE_PATH + + RELOCATABLE + + USE_HFS+_COMPRESSION + + VERSION + 1.1.1 + + TYPE + 0 + UUID + 32478317-6357-41AB-A85A-DB1DEA3A91FC + + + PROJECT + + PROJECT_COMMENTS + + NOTES + + + + PROJECT_PRESENTATION + + BACKGROUND + + APPAREANCES + + DARK_AQUA + + LIGHT_AQUA + + + SHARED_SETTINGS_FOR_ALL_APPAREANCES + + + INSTALLATION_STEPS + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewIntroductionController + INSTALLER_PLUGIN + Introduction + LIST_TITLE_KEY + InstallerSectionTitle + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewReadMeController + INSTALLER_PLUGIN + ReadMe + LIST_TITLE_KEY + InstallerSectionTitle + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewLicenseController + INSTALLER_PLUGIN + License + LIST_TITLE_KEY + InstallerSectionTitle + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewDestinationSelectController + INSTALLER_PLUGIN + TargetSelect + LIST_TITLE_KEY + InstallerSectionTitle + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewInstallationTypeController + INSTALLER_PLUGIN + PackageSelection + LIST_TITLE_KEY + InstallerSectionTitle + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewInstallationController + INSTALLER_PLUGIN + Install + LIST_TITLE_KEY + InstallerSectionTitle + + + ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS + ICPresentationViewSummaryController + INSTALLER_PLUGIN + Summary + LIST_TITLE_KEY + InstallerSectionTitle + + + INTRODUCTION + + LOCALIZATIONS + + + LICENSE + + LOCALIZATIONS + + MODE + 0 + + README + + LOCALIZATIONS + + + TITLE + + LOCALIZATIONS + + + LANGUAGE + English + VALUE + 3DTI Plugins + + + + + PROJECT_REQUIREMENTS + + LIST + + RESOURCES + + ROOT_VOLUME_ONLY + + + PROJECT_SETTINGS + + BUILD_FORMAT + 0 + BUILD_PATH + + PATH + Output + PATH_TYPE + 1 + + EXCLUDED_FILES + + + PATTERNS_ARRAY + + + REGULAR_EXPRESSION + + STRING + .DS_Store + TYPE + 0 + + + PROTECTED + + PROXY_NAME + Remove .DS_Store files + PROXY_TOOLTIP + Remove ".DS_Store" files created by the Finder. + STATE + + + + PATTERNS_ARRAY + + + REGULAR_EXPRESSION + + STRING + .pbdevelopment + TYPE + 0 + + + PROTECTED + + PROXY_NAME + Remove .pbdevelopment files + PROXY_TOOLTIP + Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. + STATE + + + + PATTERNS_ARRAY + + + REGULAR_EXPRESSION + + STRING + CVS + TYPE + 1 + + + REGULAR_EXPRESSION + + STRING + .cvsignore + TYPE + 0 + + + REGULAR_EXPRESSION + + STRING + .cvspass + TYPE + 0 + + + REGULAR_EXPRESSION + + STRING + .svn + TYPE + 1 + + + REGULAR_EXPRESSION + + STRING + .git + TYPE + 1 + + + REGULAR_EXPRESSION + + STRING + .gitignore + TYPE + 0 + + + PROTECTED + + PROXY_NAME + Remove SCM metadata + PROXY_TOOLTIP + Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. + STATE + + + + PATTERNS_ARRAY + + + REGULAR_EXPRESSION + + STRING + classes.nib + TYPE + 0 + + + REGULAR_EXPRESSION + + STRING + designable.db + TYPE + 0 + + + REGULAR_EXPRESSION + + STRING + info.nib + TYPE + 0 + + + PROTECTED + + PROXY_NAME + Optimize nib files + PROXY_TOOLTIP + Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. + STATE + + + + PATTERNS_ARRAY + + + REGULAR_EXPRESSION + + STRING + Resources Disabled + TYPE + 1 + + + PROTECTED + + PROXY_NAME + Remove Resources Disabled folders + PROXY_TOOLTIP + Remove "Resources Disabled" folders. + STATE + + + + SEPARATOR + + + + NAME + Install 3DTI Plugins + PAYLOAD_ONLY + + TREAT_MISSING_PRESENTATION_DOCUMENTS_AS_WARNING + + + + TYPE + 0 + VERSION + 2 + + diff --git a/libs/3dti_AudioToolkit b/libs/3dti_AudioToolkit index a3d5146..75a6cc2 160000 --- a/libs/3dti_AudioToolkit +++ b/libs/3dti_AudioToolkit @@ -1 +1 @@ -Subproject commit a3d5146e8b5a609f1aff340a31e2255507c1f41e +Subproject commit 75a6cc2fbdebb98d865172b13c7d79de01d26440 diff --git a/libs/JUCE b/libs/JUCE index b8206e3..90e8da0 160000 --- a/libs/JUCE +++ b/libs/JUCE @@ -1 +1 @@ -Subproject commit b8206e3604ebaca64779bf19f1613c373b9adf4f +Subproject commit 90e8da0cfb54ac593cdbed74c3d0c9b09bad3a9f