Using Soundscapy for Binaural Recording Analysis¶
Soundscapy has evolved to provide a comprehensive suite of acoustic and psychoacoustic analyses. This tutorial will guide you through using the new AcousticAnalysis
class, which serves as the primary interface for performing these analyses. The system is optimized for batch processing, ease of use, and reproducibility.
Background¶
Soundscapy relies on three main packages to provide its analysis functions:
- Acoustic Toolbox (
acoustic_toolbox
): Provides standard acoustic metrics with direct references to relevant standards. - scikit-maad (
maad
): Offers a suite of ecological soundscape and bioacoustic indices. - MoSQITo (
mosqito
): Provides key psychoacoustic metrics.
The metrics available include:
- From Acoustic Toolbox: $L_{Zeq}$, $L_{Aeq}$, $L_{Ceq}$, SEL, and associated statistics.
- From scikit-maad: Temporal and spectral alpha indices.
- From MoSQITo: Loudness, Sharpness, and Roughness.
Soundscapy combines all of these metrics and makes it easy and (relatively) fast to compute any or all of them for a binaural audio recording. These results have been preliminarily confirmed through comparison of results obtained from Head Acoustics ArtemiS suite on a set of real-world recordings.
Getting Started¶
Let's begin by importing the necessary modules and setting up our environment:
import json
from pathlib import Path
# imports
from soundscapy import AnalysisSettings, AudioAnalysis
Set up where the data is located. In this case, we'll use the sample recordings located under the test
folder.
# May need to adjust for your system
wav_folder = Path().cwd().parent.parent.joinpath("test", "data")
Calibration Levels¶
Ensuring correct calibration is crucial for accurate analysis. If you used equipment such as the Head Acoustics SqoBold, and were careful about how the recordings are exported to .wav, then they may already be correctly adjusted (as ours are here). However its best to be safe and calibrate each signal to their real-world dB level. To do this, we load in a .json that contains the per-channel correct dB $L_{eq}$ level.
levels_file = wav_folder.joinpath("Levels.json")
with open(levels_file) as f:
levels = json.load(f)
# Look at the first five sets of levels
list(levels.items())[:5]
[('CT101', {'Left': 79.0, 'Right': 79.72}), ('CT102', {'Left': 79.35, 'Right': 79.88}), ('CT103', {'Left': 76.25, 'Right': 76.41}), ('CT104', {'Left': 79.9, 'Right': 79.93}), ('CT107', {'Left': 78.21, 'Right': 78.47})]
Initializing AudioAnalysis¶
The AudioAnalysis
class is our main interface for performing acoustic analyses. Let's initialize it with default settings:
analysis = AudioAnalysis()
By default, this loads the standard configuration. If you have a custom configuration file, you can specify it:
# analysis = AudioAnalysis("path/to/custom_config.yaml")
Analyzing a Single File¶
Let's analyze a single audio file:
%%time
binaural_wav = wav_folder.joinpath("CT101.wav")
decibel = (levels["CT101"]["Left"], levels["CT101"]["Right"])
single_file_result = analysis.analyze_file(
binaural_wav, calibration_levels=decibel, resample=48000
)
single_file_result
CPU times: user 2min 58s, sys: 36 s, total: 3min 34s Wall time: 3min
LAeq | LAeq_5 | LAeq_10 | LAeq_50 | LAeq_90 | LAeq_95 | LAeq_min | LAeq_max | LAeq_kurt | LAeq_skew | ... | TFSD | H_Havrda | H_Renyi | H_pairedShannon | H_gamma | H_GiniSimpson | RAOQ | AGI | ROItotal | ROIcover | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Recording | Channel | |||||||||||||||||||||
CT101 | Left | 68.875703 | 72.257301 | 71.154342 | 68.113339 | 63.375091 | 62.366533 | 60.560166 | 77.382651 | 0.272011 | -0.013877 | ... | 0.596465 | 0.306220 | 1.254552 | 2.981413 | 1004.342366 | 0.767638 | 0.012133 | 1.502836 | 38 | 1.794776 |
Right | 69.953333 | 73.623236 | 72.578152 | 68.495399 | 64.533057 | 63.097659 | 60.520566 | 78.708783 | 0.473515 | 0.140450 | ... | 0.596309 | 0.306565 | 1.260965 | 3.032816 | 1054.904824 | 0.771196 | 0.014212 | 1.505843 | 23 | 0.940919 |
2 rows × 131 columns
This performs all the analyses specified in our configuration on the single file. The calibration_levels
parameter ensures that the analysis is calibrated correctly.
Batch Processing¶
Now, let's analyze all the WAV files in our folder:
import time
start = time.perf_counter()
folder_results = analysis.analyze_folder(
wav_folder, calibration_file=levels_file, resample=48000
)
end = time.perf_counter()
print(f"Time taken: {end - start:.2f} seconds")
folder_results
Analyzing files: 0%| | 0/8 [00:00<?, ?it/s]
Time taken: 234.62 seconds
LAeq | LAeq_5 | LAeq_10 | LAeq_50 | LAeq_90 | LAeq_95 | LAeq_min | LAeq_max | LAeq_kurt | LAeq_skew | ... | TFSD | H_Havrda | H_Renyi | H_pairedShannon | H_gamma | H_GiniSimpson | RAOQ | AGI | ROItotal | ROIcover | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Recording | Channel | |||||||||||||||||||||
CT108 | Left | 70.589507 | 74.558756 | 73.843937 | 69.525786 | 64.824693 | 64.187174 | 62.768378 | 75.729902 | -0.672714 | -0.097663 | ... | 0.601720 | 0.315433 | 1.462151 | 3.302710 | 1379.622896 | 0.816123 | 0.017529 | 1.460654 | 22 | 0.849617 |
Right | 70.112106 | 73.867805 | 73.206158 | 69.500252 | 63.767889 | 63.381380 | 62.296449 | 76.077951 | -1.033197 | -0.220893 | ... | 0.597461 | 0.312580 | 1.388225 | 3.167239 | 1171.735670 | 0.799022 | 0.013242 | 1.623013 | 5 | 1.254925 | |
CT107 | Left | 68.044340 | 72.248420 | 71.395037 | 66.199040 | 62.448782 | 61.533067 | 60.039913 | 76.177939 | -0.535311 | 0.386803 | ... | 0.596434 | 0.306238 | 1.254894 | 3.044223 | 1338.067296 | 0.770212 | 0.014416 | 1.821029 | 31 | 3.092858 |
Right | 66.957640 | 71.154329 | 69.520778 | 65.567530 | 62.737097 | 62.105325 | 59.427035 | 73.699931 | -0.229152 | 0.490734 | ... | 0.600826 | 0.301908 | 1.180762 | 2.863631 | 875.440353 | 0.747898 | 0.008257 | 1.578739 | 45 | 2.396588 | |
CT101 | Left | 68.875703 | 72.257301 | 71.154342 | 68.113339 | 63.375091 | 62.366533 | 60.560166 | 77.382651 | 0.272011 | -0.013877 | ... | 0.596465 | 0.306220 | 1.254552 | 2.981413 | 1004.342366 | 0.767638 | 0.012133 | 1.502836 | 38 | 1.794776 |
Right | 69.953333 | 73.623236 | 72.578152 | 68.495399 | 64.533057 | 63.097659 | 60.520566 | 78.708783 | 0.473515 | 0.140450 | ... | 0.596309 | 0.306565 | 1.260965 | 3.032816 | 1054.904824 | 0.771196 | 0.014212 | 1.505843 | 23 | 0.940919 | |
CT109 | Left | 69.629802 | 73.858804 | 72.535949 | 68.088413 | 65.395855 | 64.841201 | 63.608754 | 77.369593 | 0.101203 | 0.709280 | ... | 0.594079 | 0.316275 | 1.486264 | 3.292027 | 1341.924154 | 0.820587 | 0.013846 | 1.545552 | 21 | 1.545712 |
Right | 68.140737 | 71.302562 | 70.788590 | 67.176560 | 64.900719 | 64.394600 | 62.678234 | 74.251971 | -0.501266 | 0.377754 | ... | 0.593684 | 0.310974 | 1.350959 | 3.076539 | 1161.508584 | 0.788654 | 0.014172 | 1.580760 | 43 | 2.021005 | |
CT110 | Left | 68.696864 | 73.688152 | 73.278617 | 66.395310 | 61.541000 | 61.077132 | 59.521031 | 75.734980 | -0.902389 | 0.321096 | ... | 0.596778 | 0.315119 | 1.453479 | 3.330735 | 1742.442706 | 0.817141 | 0.019447 | 1.635892 | 4 | 0.903032 |
Right | 68.086205 | 72.842216 | 72.233530 | 65.950830 | 60.897040 | 60.072192 | 58.683020 | 74.284699 | -1.039466 | 0.153424 | ... | 0.598589 | 0.306559 | 1.260850 | 2.989754 | 1138.129400 | 0.768657 | 0.011267 | 1.667605 | 3 | 2.584879 | |
CT102 | Left | 70.613447 | 74.541317 | 73.322597 | 69.297264 | 65.074481 | 64.561974 | 63.337182 | 78.922344 | 0.270694 | 0.530142 | ... | 0.601338 | 0.315584 | 1.466408 | 3.338957 | 1570.553756 | 0.817882 | 0.018555 | 1.649929 | 2 | 3.491107 |
Right | 70.491840 | 75.681356 | 73.055797 | 69.131814 | 64.975452 | 64.209771 | 63.114758 | 81.634943 | 0.648596 | 0.601576 | ... | 0.594765 | 0.314666 | 1.441181 | 3.266859 | 1171.085446 | 0.813338 | 0.012127 | 1.497619 | 50 | 2.569614 | |
CT104 | Left | 72.385734 | 76.823628 | 75.846019 | 70.883017 | 68.178606 | 67.906102 | 67.060970 | 78.252226 | -0.551273 | 0.581452 | ... | 0.601207 | 0.319373 | 1.586474 | 3.467531 | 1784.788259 | 0.840308 | 0.022362 | 1.607451 | 21 | 1.532046 |
Right | 70.661228 | 75.007596 | 72.920992 | 69.693243 | 67.615660 | 67.257368 | 65.945547 | 76.179642 | 0.158796 | 0.744622 | ... | 0.592096 | 0.313899 | 1.421059 | 3.162598 | 1054.720367 | 0.803737 | 0.013435 | 1.517441 | 98 | 4.713390 | |
CT103 | Left | 66.330006 | 69.010487 | 68.241612 | 65.579475 | 63.680847 | 63.194765 | 62.103353 | 74.234566 | 2.236365 | 1.127518 | ... | 0.591106 | 0.309768 | 1.324691 | 3.109320 | 1062.342802 | 0.785795 | 0.013687 | 1.484990 | 117 | 3.370993 |
Right | 66.320960 | 69.247212 | 68.344756 | 65.717681 | 64.145147 | 63.852638 | 62.685126 | 72.061583 | 1.008471 | 0.981831 | ... | 0.583920 | 0.307667 | 1.281979 | 3.053171 | 1152.246760 | 0.775524 | 0.013850 | 1.521505 | 136 | 6.565286 |
16 rows × 131 columns
Saving Results¶
We can easily save our results to a file:
analysis.save_results(folder_results, "acoustic_analysis_results.xlsx")
new_config = {"AcousticToolbox": {"LAeq": {"run": False}}}
analysis.update_config(new_config)
print("Configuration updated")
Configuration updated
This would disable the LAeq analysis in subsequent runs.
Saving the Updated Configuration¶
We can save the updated configuration to a file:
analysis.save_config("updated_config.yaml")
print("Updated configuration saved to 'updated_config.yaml'")
custom_settings = AnalysisSettings.from_yaml("example_settings.yaml")
custom_settings.update_setting("scikit_maad", "all_temporal_alpha_indices", run=True)
custom_settings.update_setting("scikit_maad", "all_spectral_alpha_indices", run=True)
# Create a new AudioAnalysis instance with the custom settings
custom_analysis = AudioAnalysis(config_path="example_settings.yaml")
# Or update an existing instance
analysis.update_config(custom_settings.model_dump())
Parallel Processing Control¶
The analyze_folder
method uses parallel processing by default. You can control the number of worker processes using the max_workers
argument. Setting max_workers = None
(the default) will use all available CPU cores. Setting max_workers = 1
will disable parallel processing, and will take significantly longer to process:
start = time.perf_counter()
serial_analysis = AudioAnalysis()
folder_results = serial_analysis.analyze_folder(
wav_folder, calibration_file=levels_file, max_workers=1, resample=48000
)
end = time.perf_counter()
print(f"Time taken: {end - start:.2f} seconds")
folder_results
Analyzing files: 0%| | 0/8 [00:00<?, ?it/s]
Time taken: 1468.20 seconds
LAeq | LAeq_5 | LAeq_10 | LAeq_50 | LAeq_90 | LAeq_95 | LAeq_min | LAeq_max | LAeq_kurt | LAeq_skew | ... | TFSD | H_Havrda | H_Renyi | H_pairedShannon | H_gamma | H_GiniSimpson | RAOQ | AGI | ROItotal | ROIcover | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Recording | Channel | |||||||||||||||||||||
CT108 | Left | 70.589507 | 74.558756 | 73.843937 | 69.525786 | 64.824693 | 64.187174 | 62.768378 | 75.729902 | -0.672714 | -0.097663 | ... | 0.601720 | 0.315433 | 1.462151 | 3.302710 | 1379.622896 | 0.816123 | 0.017529 | 1.460654 | 22 | 0.849617 |
Right | 70.112106 | 73.867805 | 73.206158 | 69.500252 | 63.767889 | 63.381380 | 62.296449 | 76.077951 | -1.033197 | -0.220893 | ... | 0.597461 | 0.312580 | 1.388225 | 3.167239 | 1171.735670 | 0.799022 | 0.013242 | 1.623013 | 5 | 1.254925 | |
CT109 | Left | 69.629802 | 73.858804 | 72.535949 | 68.088413 | 65.395855 | 64.841201 | 63.608754 | 77.369593 | 0.101203 | 0.709280 | ... | 0.594079 | 0.316275 | 1.486264 | 3.292027 | 1341.924154 | 0.820587 | 0.013846 | 1.545552 | 21 | 1.545712 |
Right | 68.140737 | 71.302562 | 70.788590 | 67.176560 | 64.900719 | 64.394600 | 62.678234 | 74.251971 | -0.501266 | 0.377754 | ... | 0.593684 | 0.310974 | 1.350959 | 3.076539 | 1161.508584 | 0.788654 | 0.014172 | 1.580760 | 43 | 2.021005 | |
CT101 | Left | 68.875703 | 72.257301 | 71.154342 | 68.113339 | 63.375091 | 62.366533 | 60.560166 | 77.382651 | 0.272011 | -0.013877 | ... | 0.596465 | 0.306220 | 1.254552 | 2.981413 | 1004.342366 | 0.767638 | 0.012133 | 1.502836 | 38 | 1.794776 |
Right | 69.953333 | 73.623236 | 72.578152 | 68.495399 | 64.533057 | 63.097659 | 60.520566 | 78.708783 | 0.473515 | 0.140450 | ... | 0.596309 | 0.306565 | 1.260965 | 3.032816 | 1054.904824 | 0.771196 | 0.014212 | 1.505843 | 23 | 0.940919 | |
CT102 | Left | 70.613447 | 74.541317 | 73.322597 | 69.297264 | 65.074481 | 64.561974 | 63.337182 | 78.922344 | 0.270694 | 0.530142 | ... | 0.601338 | 0.315584 | 1.466408 | 3.338957 | 1570.553756 | 0.817882 | 0.018555 | 1.649929 | 2 | 3.491107 |
Right | 70.491840 | 75.681356 | 73.055797 | 69.131814 | 64.975452 | 64.209771 | 63.114758 | 81.634943 | 0.648596 | 0.601576 | ... | 0.594765 | 0.314666 | 1.441181 | 3.266859 | 1171.085446 | 0.813338 | 0.012127 | 1.497619 | 50 | 2.569614 | |
CT103 | Left | 66.330006 | 69.010487 | 68.241612 | 65.579475 | 63.680847 | 63.194765 | 62.103353 | 74.234566 | 2.236365 | 1.127518 | ... | 0.591106 | 0.309768 | 1.324691 | 3.109320 | 1062.342802 | 0.785795 | 0.013687 | 1.484990 | 117 | 3.370993 |
Right | 66.320960 | 69.247212 | 68.344756 | 65.717681 | 64.145147 | 63.852638 | 62.685126 | 72.061583 | 1.008471 | 0.981831 | ... | 0.583920 | 0.307667 | 1.281979 | 3.053171 | 1152.246760 | 0.775524 | 0.013850 | 1.521505 | 136 | 6.565286 | |
CT107 | Left | 68.044340 | 72.248420 | 71.395037 | 66.199040 | 62.448782 | 61.533067 | 60.039913 | 76.177939 | -0.535311 | 0.386803 | ... | 0.596434 | 0.306238 | 1.254894 | 3.044223 | 1338.067296 | 0.770212 | 0.014416 | 1.821029 | 31 | 3.092858 |
Right | 66.957640 | 71.154329 | 69.520778 | 65.567530 | 62.737097 | 62.105325 | 59.427035 | 73.699931 | -0.229152 | 0.490734 | ... | 0.600826 | 0.301908 | 1.180762 | 2.863631 | 875.440353 | 0.747898 | 0.008257 | 1.578739 | 45 | 2.396588 | |
CT104 | Left | 72.385734 | 76.823628 | 75.846019 | 70.883017 | 68.178606 | 67.906102 | 67.060970 | 78.252226 | -0.551273 | 0.581452 | ... | 0.601207 | 0.319373 | 1.586474 | 3.467531 | 1784.788259 | 0.840308 | 0.022362 | 1.607451 | 21 | 1.532046 |
Right | 70.661228 | 75.007596 | 72.920992 | 69.693243 | 67.615660 | 67.257368 | 65.945547 | 76.179642 | 0.158796 | 0.744622 | ... | 0.592096 | 0.313899 | 1.421059 | 3.162598 | 1054.720367 | 0.803737 | 0.013435 | 1.517441 | 98 | 4.713390 | |
CT110 | Left | 68.696864 | 73.688152 | 73.278617 | 66.395310 | 61.541000 | 61.077132 | 59.521031 | 75.734980 | -0.902389 | 0.321096 | ... | 0.596778 | 0.315119 | 1.453479 | 3.330735 | 1742.442706 | 0.817141 | 0.019447 | 1.635892 | 4 | 0.903032 |
Right | 68.086205 | 72.842216 | 72.233530 | 65.950830 | 60.897040 | 60.072192 | 58.683020 | 74.284699 | -1.039466 | 0.153424 | ... | 0.598589 | 0.306559 | 1.260850 | 2.989754 | 1138.129400 | 0.768657 | 0.011267 | 1.667605 | 3 | 2.584879 |
16 rows × 131 columns
As we can see, on my system, enabling parallel processing reduces the processing time for these 8 files from almost 25 minutes to less than 4 minutes. This will vary depending on your system and the number of files you are processing. The more CPU cores and the more files, the more beneficial parallel processing will be.
Conclusion¶
The AudioAnalysis
class provides a powerful and flexible interface for performing acoustic and psychoacoustic analyses on binaural recordings. It simplifies the process of batch analysis, configuration management, and result handling, making it easier to process large datasets consistently and efficiently.
Remember that the specific metrics calculated and their settings are determined by the configuration. Always ensure your configuration accurately reflects your analysis needs, keep in mind that the psychoacoustic analyses in MoSQITo can be computationally intensive, and don't hesitate to customize it for your specific research or application requirements.