diff --git a/.gitignore b/.gitignore
index fa9a082..65d704f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,13 +7,13 @@ data/*.ipynb
__pycache__
*.egg-info
-dasp-pytorch/
+
mix_KE_adv/**
.vscode/
logs/**
checkpoints/
debug
-dasp-pytorch
*.wav
*.png
-data/FXencoder_ps.pt
\ No newline at end of file
+data/FXencoder_ps.pt
+outputs/**
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..66b2013
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "dasp-pytorch"]
+ path = dasp-pytorch
+ url = https://github.com/csteinmetz1/dasp-pytorch
diff --git a/Assets/diffmst-main_modified.jpg b/Assets/diffmst-main_modified.jpg
new file mode 100644
index 0000000..705280a
Binary files /dev/null and b/Assets/diffmst-main_modified.jpg differ
diff --git a/Assets/mst_final.png b/Assets/mst_final.png
deleted file mode 100644
index eb79192..0000000
Binary files a/Assets/mst_final.png and /dev/null differ
diff --git a/Assets/mst_wbg.png b/Assets/mst_wbg.png
deleted file mode 100644
index f3c2e1e..0000000
Binary files a/Assets/mst_wbg.png and /dev/null differ
diff --git a/README.md b/README.md
index 09c3cf2..eaa47d8 100644
--- a/README.md
+++ b/README.md
@@ -2,95 +2,76 @@
# Differentiable Mixing Style Transfer
-[Paper]() | [Website]()
+[Paper](https://sai-soum.github.io/assets/pdf/diffmst.pdf) | [Website](https://sai-soum.github.io/projects/diffmst/)
-
+
-Mixing style transfer using reference mix.
+
+# Repository Structure
+1. 'configs' - Contains configuration files for training and inference.
+2. 'mst' - Contains the main codebase for the project.
+ - 'dataloaders' - Contains dataloaders for the project.
+ - 'modules' - Contains the modules for different components of the system.
+ - 'mixing' - Contains the mixing modules for creating mixes.
+ - 'loss' - Contains the loss functions for the project.
+ - 'panns' - contains the most basic components like cnn14, resnet, etc.
+ - 'utils' - Contains utility functions for the project.
+3. 'scripts' - Contains scripts for running inference.
# Usage
-
Clone the repository and install the `mst` package.
```
-git clone https://github.com/sai-soum/mix_style_transfer.git
-cd mix_style_transfer
+git clone --recursive https://github.com/sai-soum/Diff-MST.git
+cd Diff-MST
python -m venv env
source env/bin/activate
pip install -e .
```
-[dasp-pytorch](https://github.com/csteinmetz1/dasp-pytorch) is required for differentiable audio effects.
-Clone the repo into the top-level of the project directory.
+[dasp-pytorch](https://github.com/csteinmetz1/dasp-pytorch) is required for differentiable audio effects.
+Install the dependencies for dasp-pytorch.
```
-git clone https://github.com/csteinmetz1/dasp-pytorch.git
cd dasp-pytorch
pip install -e .
```
-Since `dasp` is currently under development you need to pull changes periodically.
-To do so change to the directory and pull.
-```
-cd dasp-pytorch
-git pull
-```
-
-## Inference
-
-```
-CUDA_VISIBLE_DEVICES=5 python scripts/run.py \
-checkpoints/20230719/config.yaml \
-checkpoints/20230719/epoch=132-step=83125.ckpt \
-"/import/c4dm-02/acw639/DiffMST/song 2/Kat Wright_By My Side/" \
-output/ref_mix.wav \
-```
-
## Train
-
-First update the paths in the configuration file for both the logger and the dataset root directory.
+We use [LightningCLI](https://lightning.ai/docs/pytorch/stable/) for training and [Wandb](https://wandb.ai/site) for logging.
+First update the paths in the configuration file for both the logger, loss function, and the dataset root directory.
Then call the `main.py` script passing in the configuration file.
+
+### Method 1: Training with random mixes of the same song as reference using MRSTFT loss.
```
-# new model configuration with audio feature loss
CUDA_VISIBLE_DEVICES=0 python main.py fit \
--c configs/config_cjs.yaml \
+-c configs/config.yaml \
-c configs/optimizer.yaml \
--c configs/data/medley+cambridge+jamendo-8.yaml \
--c configs/models/gain+eq+comp-feat.yaml
+-c configs/data/medley+cambridge-8.yaml \
+-c configs/models/naive.yaml
+```
+You can change the number of tracks, the size of training data for an epoch, and the batch size in the data configuration file located at `configs/data/`
-# new model configuration with CLAP loss
+### Method 2: Training with real unpaired songs as reference using AFloss.
+```
CUDA_VISIBLE_DEVICES=0 python main.py fit \
--c configs/config_cjs.yaml \
+-c configs/config.yaml \
-c configs/optimizer.yaml \
-c configs/data/medley+cambridge+jamendo-8.yaml \
--c configs/models/gain+eq+comp-clap.yaml
+-c configs/models/naive+feat.yaml
```
+## Inference
+To evaluate the model on real world data, run the ` scripts/eval_all_combo.py` script.
-# Stability (ignore)
-```
-source env/bin/activate
-cd /scratch
-mkdir medleydb
-cd medleydb
-aws s3 sync s3://stability-aws/MedleyDB ./
-tar -xvf MedleyDB_v1.tar
-tar -xvf MedleyDB_v2.tar
-python main.py fit -c configs/config.yaml -c configs/optimizer.yaml -c configs/data/medleydb_cjs.yaml -c configs/models/naive_dmc_adv.yaml
-CUDA_VISIBLE_DEVICES=7 python main.py fit -c configs/config_cjs.yaml -c configs/optimizer.yaml -c configs/data/medleydb_c4dm.yaml -c configs/models/ke_dmc_adv.yaml
-
-CUDA_VISIBLE_DEVICES=7 python main.py fit -c configs/config.yaml -c configs/optimizer.yaml -c configs/data/medley+cambridge-4.yaml -c configs/models/naive+fx_encoder_loss.yaml
-
-To run the paramloss code
-
-CUDA_VISIBLE_DEVICES=2 python main.py fit -c configs/config.yaml -c configs/optimizer.yaml -c configs/data/medley+cambridge-4.yaml -c configs/models/naive+paramloss.yaml
+Update the model checkpoints and the inference examples directory in the script.
-```
+`Python 3.10` was used for training.
diff --git a/configs/config.yaml b/configs/config.yaml
index cedbb7f..3dd2465 100644
--- a/configs/config.yaml
+++ b/configs/config.yaml
@@ -6,34 +6,34 @@ trainer:
init_args:
project: DiffMST
save_dir: /import/c4dm-datasets-ext/diffmst_logs_soum
-
enable_checkpointing: true
-
-
callbacks:
- class_path: mst.callbacks.audio.LogAudioCallback
- class_path: pytorch_lightning.callbacks.ModelSummary
init_args:
max_depth: 2
-
- class_path: mst.callbacks.mix.LogReferenceMix
init_args:
- root_dirs:
- - /import/c4dm-datasets-ext/diffmst-examples/song1/BenFlowers_Ecstasy_Full/
- - /import/c4dm-datasets-ext/diffmst-examples/song2/Kat Wright_By My Side/
- - /import/c4dm-datasets-ext/diffmst-examples/song3/Titanium_HauntedAge_Full/
+ root_dirs:
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/Soren_ALittleLate_Full
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/Soren_ALittleLate_Full
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/MR0903_Moosmusic_Full
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/MR0903_Moosmusic_Full
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song3/SaturnSyndicate_CatchTheWave_Full
ref_mixes:
- - /import/c4dm-datasets-ext/diffmst-examples/song1/ref/_Feel it all Around_ by Washed Out (Portlandia Theme).mp3
- - /import/c4dm-datasets-ext/diffmst-examples/song2/ref/The Dip - Paddle To The Stars (Lyric Video).mp3
- - /import/c4dm-datasets-ext/diffmst-examples/song3/ref/Architects - _Doomsday_.mp3
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/ref/Harry Styles - Late Night Talking (Official Video).wav
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/ref/Poom - Les Voiles (Official Audio).wav
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/ref/Justin Timberlake - Can't Stop The Feeling! [Lyrics].wav
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/ref/Taylor Swift - Shake It Off.wav
+ - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song3/ref/Miley Cyrus - Wrecking Ball (Lyrics).wav
default_root_dir: null
gradient_clip_val: 10.0
- devices: 3
- detect_anomaly: True
-
+ devices: 1
check_val_every_n_epoch: 1
- max_epochs: 10000
- log_every_n_steps: 200
+
+ max_epochs: 800
+
+ log_every_n_steps: 50
accelerator: gpu
strategy: ddp_find_unused_parameters_true
sync_batchnorm: true
@@ -42,8 +42,5 @@ trainer:
num_sanity_val_steps: 2
benchmark: true
accumulate_grad_batches: 1
- reload_dataloaders_every_n_epochs: 1
-
+ #reload_dataloaders_every_n_epochs: 1
-# - /import/c4dm-datasets-ext/diffmst-examples/song1/BenFlowers_Ecstasy_Full/
-# - /import/c4dm-datasets-ext/diffmst_validation/listening/diffmst-examples_wavref/Feel it all Around by Washed Out (Portlandia Theme).wav
\ No newline at end of file
diff --git a/configs/config_cjs.yaml b/configs/config_cjs.yaml
deleted file mode 100644
index 3dd2465..0000000
--- a/configs/config_cjs.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-seed_everything: 42
-
-trainer:
- logger:
- class_path: pytorch_lightning.loggers.WandbLogger
- init_args:
- project: DiffMST
- save_dir: /import/c4dm-datasets-ext/diffmst_logs_soum
- enable_checkpointing: true
- callbacks:
- - class_path: mst.callbacks.audio.LogAudioCallback
- - class_path: pytorch_lightning.callbacks.ModelSummary
- init_args:
- max_depth: 2
- - class_path: mst.callbacks.mix.LogReferenceMix
- init_args:
- root_dirs:
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/Soren_ALittleLate_Full
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/Soren_ALittleLate_Full
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/MR0903_Moosmusic_Full
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/MR0903_Moosmusic_Full
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song3/SaturnSyndicate_CatchTheWave_Full
- ref_mixes:
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/ref/Harry Styles - Late Night Talking (Official Video).wav
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song1/ref/Poom - Les Voiles (Official Audio).wav
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/ref/Justin Timberlake - Can't Stop The Feeling! [Lyrics].wav
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song2/ref/Taylor Swift - Shake It Off.wav
- - /import/c4dm-datasets-ext/diffmst_validation/validation_set/song3/ref/Miley Cyrus - Wrecking Ball (Lyrics).wav
- default_root_dir: null
- gradient_clip_val: 10.0
- devices: 1
- check_val_every_n_epoch: 1
-
- max_epochs: 800
-
- log_every_n_steps: 50
- accelerator: gpu
- strategy: ddp_find_unused_parameters_true
- sync_batchnorm: true
- precision: 32
- enable_model_summary: true
- num_sanity_val_steps: 2
- benchmark: true
- accumulate_grad_batches: 1
- #reload_dataloaders_every_n_epochs: 1
-
diff --git a/configs/config_param.yaml b/configs/config_param.yaml
deleted file mode 100644
index 58f88b4..0000000
--- a/configs/config_param.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-seed_everything: 42
-#ckpt_path: /import/c4dm-datasets-ext/Diff-MST/DiffMST/4bjbp29c/checkpoints/epoch=118-step=148750.ckpt
-
-trainer:
- logger:
- class_path: pytorch_lightning.loggers.WandbLogger
- init_args:
- project: DiffMST-Param
- save_dir: /import/c4dm-datasets-ext/Diff-MST
- enable_checkpointing: true
- callbacks:
- - class_path: pytorch_lightning.callbacks.ModelSummary
- init_args:
- max_depth: 2
- default_root_dir: null
- gradient_clip_val: 10.0
- devices: 1
- check_val_every_n_epoch: 1
- max_epochs: 500
- log_every_n_steps: 50
- accelerator: gpu
- strategy: ddp_find_unused_parameters_true
- sync_batchnorm: true
- precision: 32
- enable_model_summary: true
- num_sanity_val_steps: 2
- benchmark: true
- accumulate_grad_batches: 1
-
diff --git a/configs/configs_hpc.yaml b/configs/configs_hpc.yaml
deleted file mode 100644
index 547c84a..0000000
--- a/configs/configs_hpc.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-seed_everything: 42
-
-trainer:
- logger:
- class_path: pytorch_lightning.loggers.WandbLogger
- init_args:
- name: DiffMST_naive_advanced_combined_data
- project: DiffMST
- save_dir: /data/scratch/acw639/DiffMST/logs
- enable_checkpointing: true
- callbacks:
- - class_path: mst.callbacks.audio.LogAudioCallback
- - class_path: pytorch_lightning.callbacks.ModelSummary
- init_args:
- max_depth: 2
- default_root_dir: null
- gradient_clip_val: 4.0
- devices: 1
- check_val_every_n_epoch: 1
- max_steps: 1000000
- log_every_n_steps: 50
- accelerator: gpu
- sync_batchnorm: true
- precision: 32
- enable_model_summary: true
- num_sanity_val_steps: 2
- benchmark: true
- accumulate_grad_batches: 4
- reload_dataloaders_every_n_epochs: 1
diff --git a/configs/models/naive+fx_encoder_loss.yaml b/configs/models/naive+fx_encoder_loss.yaml
deleted file mode 100644
index e75f8b6..0000000
--- a/configs/models/naive+fx_encoder_loss.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-model:
- class_path: mst.system.System
- init_args:
- generate_mix: True
- active_eq_epoch: 0
- active_compressor_epoch: 0
- active_fx_bus_epoch: 1000000
- active_master_bus_epoch: 0
- mix_fn: mst.mixing.naive_random_mix
- mix_console:
- class_path: mst.modules.AdvancedMixConsole
- init_args:
- sample_rate: 44100
- input_min_gain_db: -48.0
- input_max_gain_db: 48.0
- output_min_gain_db: -48.0
- output_max_gain_db: 48.0
- eq_min_gain_db: -12.0
- eq_max_gain_db: 12.0
- min_pan: 0.0
- max_pan: 1.0
- model:
- class_path: mst.modules.MixStyleTransferModel
- init_args:
- track_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- mix_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- controller:
- class_path: mst.modules.TransformerController
- init_args:
- embed_dim: 512
- num_track_control_params: 27
- num_fx_bus_control_params: 25
- num_master_bus_control_params: 26
- num_layers: 12
- nhead: 8
- loss:
- class_path: mst.loss.FX_encoder_loss
- init_args:
- audiofeatures: False
- # weights:
- # - 0.1 # rms
- # - 0.001 # crest factor
- # - 0.1 # stereo width
- # - 0.01 # stereo imbalance
- # - 0.01 # bark spectrum
- # - 0.01 # fx_encoder
-
-
-
-
-#weights = [1.0, 0.001, 1.0, 1.0, 0.01 , 0.01]
\ No newline at end of file
diff --git a/configs/models/naive+ours.yaml b/configs/models/naive+ours.yaml
deleted file mode 100644
index 3356299..0000000
--- a/configs/models/naive+ours.yaml
+++ /dev/null
@@ -1,55 +0,0 @@
-model:
- class_path: mst.system.System
- init_args:
- generate_mix: false
- active_eq_epoch: 0
- active_compressor_epoch: 0
- active_fx_bus_epoch: 1000
- active_master_bus_epoch: 0
- mix_fn: mst.mixing.naive_random_mix
- mix_console:
- class_path: mst.modules.AdvancedMixConsole
- init_args:
- sample_rate: 44100
- input_min_gain_db: -48.0
- input_max_gain_db: 48.0
- output_min_gain_db: -48.0
- output_max_gain_db: 48.0
- eq_min_gain_db: -12.0
- eq_max_gain_db: 12.0
- min_pan: 0.0
- max_pan: 1.0
- model:
- class_path: mst.modules.MixStyleTransferModel
- init_args:
- track_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- mix_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- controller:
- class_path: mst.modules.TransformerController
- init_args:
- embed_dim: 512
- num_track_control_params: 27
- num_fx_bus_control_params: 25
- num_master_bus_control_params: 26
- num_layers: 12
- nhead: 8
-
- loss:
- class_path: mst.loss.ParameterEstimatorLoss
- init_args:
- ckpt_path: /import/c4dm-datasets-ext/Diff-MST/DiffMST-Param/0ymfi1pp/checkpoints/epoch=20-step=37947.ckpt
-
-
-
diff --git a/configs/models/naive+paramloss.yaml b/configs/models/naive+paramloss.yaml
deleted file mode 100644
index a9215e2..0000000
--- a/configs/models/naive+paramloss.yaml
+++ /dev/null
@@ -1,58 +0,0 @@
-model:
- class_path: mst.system.System
- init_args:
- generate_mix: True
- active_eq_epoch: 0
- active_compressor_epoch: 0
- active_fx_bus_epoch: 1000
- active_master_bus_epoch: 0
- use_track_loss : false
- use_mix_loss : false
- use_param_loss : true
- mix_fn: mst.mixing.naive_random_mix
- mix_console:
- class_path: mst.modules.AdvancedMixConsole
- init_args:
- sample_rate: 44100
- input_min_gain_db: -48.0
- input_max_gain_db: 48.0
- output_min_gain_db: -48.0
- output_max_gain_db: 48.0
- eq_min_gain_db: -12.0
- eq_max_gain_db: 12.0
- min_pan: 0.0
- max_pan: 1.0
- model:
- class_path: mst.modules.MixStyleTransferModel
- init_args:
- track_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- mix_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- controller:
- class_path: mst.modules.TransformerController
- init_args:
- embed_dim: 512
- num_track_control_params: 27
- num_fx_bus_control_params: 25
- num_master_bus_control_params: 26
- num_layers: 12
- nhead: 8
-
- loss:
- class_path: torch.nn.MSELoss
-
-
-
-
-
diff --git a/configs/models/naive+verb.yaml b/configs/models/naive+verb.yaml
deleted file mode 100644
index eb70265..0000000
--- a/configs/models/naive+verb.yaml
+++ /dev/null
@@ -1,65 +0,0 @@
-model:
- class_path: mst.system.System
- init_args:
- active_eq_epoch: 0
- active_compressor_epoch: 0
- active_fx_bus_epoch: 0
- active_master_bus_epoch: 0
- mix_fn: mst.mixing.naive_random_mix
- mix_console:
- class_path: mst.modules.AdvancedMixConsole
- init_args:
- sample_rate: 44100
- input_min_gain_db: -48.0
- input_max_gain_db: 48.0
- output_min_gain_db: -48.0
- output_max_gain_db: 48.0
- eq_min_gain_db: -12.0
- eq_max_gain_db: 12.0
- min_pan: 0.0
- max_pan: 1.0
- model:
- class_path: mst.modules.MixStyleTransferModel
- init_args:
- track_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- mix_encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 512
- n_fft: 2048
- hop_length: 512
- input_batchnorm: false
- controller:
- class_path: mst.modules.TransformerController
- init_args:
- embed_dim: 512
- num_track_control_params: 27
- num_fx_bus_control_params: 25
- num_master_bus_control_params: 26
- num_layers: 12
- nhead: 8
-
- loss:
- class_path: auraloss.freq.MultiResolutionSTFTLoss
- init_args:
- fft_sizes:
- - 512
- - 2048
- - 8192
- hop_sizes:
- - 256
- - 1024
- - 4096
- win_lengths:
- - 512
- - 2048
- - 8192
-
-
-
diff --git a/configs/models/param-estim.yaml b/configs/models/param-estim.yaml
deleted file mode 100644
index bb33019..0000000
--- a/configs/models/param-estim.yaml
+++ /dev/null
@@ -1,36 +0,0 @@
-model:
- class_path: mst.param_system.ParameterEstimationSystem
- init_args:
- mix_console:
- class_path: mst.modules.AdvancedMixConsole
- init_args:
- sample_rate: 44100
- input_min_gain_db: -48.0
- input_max_gain_db: 48.0
- output_min_gain_db: -48.0
- output_max_gain_db: 48.0
- eq_min_gain_db: -12.0
- eq_max_gain_db: 12.0
- min_pan: 0.0
- max_pan: 1.0
- remixer:
- class_path: mst.modules.Remixer
- init_args:
- sample_rate: 44100
- encoder:
- class_path: mst.modules.SpectrogramEncoder
- init_args:
- embed_dim: 1024
- n_inputs: 1
- projector:
- class_path: mst.modules.ParameterProjector
- init_args:
- embed_dim: 2048
- num_tracks: 8
- num_track_control_params: 27
- num_fx_bus_control_params: 25
- num_master_bus_control_params: 26
-
-
-
-
diff --git a/hpc_run.sh b/hpc_run.sh
deleted file mode 100644
index b97dfaa..0000000
--- a/hpc_run.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-#$ -cwd # Set the working directory for the job to the current directory
-#$ -j y
-#$ -pe smp 8 # Request 8 core
-#$ -l h_rt=240:0:0 # Request 10 days runtime
-#$ -l h_vmem= 20 # Request 8 * 20 = 160G total RAM
-#$ -l gpu=1 # request 1 GPU
-#$ -m bea
-#$ -l cluster="andrena"
-#$ -t 1-10
-
-
-python main.py fit -c configs/configs_hpc.yaml -c configs/optimizer.yaml -c configs/data/combined_hpc.yaml -c configs/models/naive_dmc_adv.yaml
\ No newline at end of file
diff --git a/mst/utils.py b/mst/utils.py
index 117f82d..6c8b757 100644
--- a/mst/utils.py
+++ b/mst/utils.py
@@ -66,14 +66,14 @@ def run_diffmst(
meter = pyln.Meter(44100)
# crop the input tracks and reference mix to the analysis length
- if tracks.shape[-1] > analysis_len:
+ if tracks.shape[-1] >= analysis_len:
analysis_tracks = tracks[
..., track_start_idx : track_start_idx + analysis_len
].clone()
else:
analysis_tracks = tracks.clone()
- if ref.shape[-1] > analysis_len:
+ if ref.shape[-1] >= analysis_len:
analysis_ref = ref[..., ref_start_idx : ref_start_idx + analysis_len]
else:
analysis_ref = ref.clone()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..57d9f32
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,97 @@
+absl-py==2.1.0
+aiohttp==3.9.3
+aiosignal==1.3.1
+antlr4-python3-runtime==4.9.3
+appdirs==1.4.4
+async-timeout==4.0.3
+attrs==23.2.0
+audioread==3.0.1
+auraloss==0.4.0
+bitsandbytes==0.41.0
+certifi==2024.2.2
+cffi==1.16.0
+charset-normalizer==3.3.2
+click==8.1.7
+contourpy==1.2.0
+cycler==0.12.1
+# Editable install with no version control (dasp-pytorch==0.0.1)
+-e /Users/svanka/Codes/Diff-MST/dasp-pytorch
+decorator==5.1.1
+-e git+ssh://git@github.com/sai-soum/Diff-MST.git@249706b4aaa66c16adaf5a0d46413c0337749508#egg=DiffMST
+docker-pycreds==0.4.0
+docstring_parser==0.16
+filelock==3.13.3
+fonttools==4.50.0
+frozenlist==1.4.1
+fsspec==2024.3.1
+future==1.0.0
+gitdb==4.0.11
+GitPython==3.1.43
+grpcio==1.62.1
+hydra-core==1.3.2
+idna==3.6
+importlib_resources==6.4.0
+Jinja2==3.1.3
+joblib==1.3.2
+jsonargparse==4.27.7
+kiwisolver==1.4.5
+lazy_loader==0.3
+librosa==0.10.1
+lightning-utilities==0.11.2
+llvmlite==0.42.0
+Markdown==3.6
+markdown-it-py==3.0.0
+MarkupSafe==2.1.5
+matplotlib==3.8.3
+mdurl==0.1.2
+mpmath==1.3.0
+msgpack==1.0.8
+multidict==6.0.5
+networkx==3.2.1
+numba==0.59.1
+numpy==1.26.4
+omegaconf==2.3.0
+packaging==24.0
+pandas==2.2.1
+pedalboard==0.9.3
+pillow==10.3.0
+platformdirs==4.2.0
+pooch==1.8.1
+protobuf==4.25.3
+psutil==5.9.8
+pycparser==2.22
+Pygments==2.17.2
+pyloudnorm==0.1.1
+pyparsing==3.1.2
+python-dateutil==2.9.0.post0
+pytorch-lightning==2.2.1
+pytz==2024.1
+PyYAML==6.0.1
+requests==2.31.0
+rich==13.7.1
+scikit-learn==1.4.1.post1
+scipy==1.12.0
+sentry-sdk==1.44.0
+setproctitle==1.3.3
+six==1.16.0
+smmap==5.0.1
+soundfile==0.12.1
+soxr==0.3.7
+sympy==1.12
+tabulate==0.9.0
+tensorboard==2.16.2
+tensorboard-data-server==0.7.2
+tensorboardX==2.6.2.2
+threadpoolctl==3.4.0
+torch==2.2.2
+torchaudio==2.2.2
+torchmetrics==1.3.2
+torchvision==0.17.2
+tqdm==4.66.2
+typeshed_client==2.5.1
+typing_extensions==4.10.0
+tzdata==2024.1
+urllib3==2.2.1
+wandb==0.16.5
+Werkzeug==3.0.2
+yarl==1.9.4
diff --git a/scripts/eval_ablation.py b/scripts/eval_ablation.py
new file mode 100644
index 0000000..6970cc2
--- /dev/null
+++ b/scripts/eval_ablation.py
@@ -0,0 +1,259 @@
+# run pretrained models over evaluation set to generate audio examples for the listening test
+import os
+import torch
+import torchaudio
+import pyloudnorm as pyln
+from mst.utils import load_diffmst, run_diffmst
+from mst.loss import compute_barkspectrum, compute_rms, compute_crest_factor, compute_stereo_width, compute_stereo_imbalance, AudioFeatureLoss
+import json
+import numpy as np
+import csv
+import glob
+
+
+def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
+
+ meter = pyln.Meter(44100)
+ target_lufs_db = -48.0
+
+ norm_tracks = []
+ for track_idx in range(tracks.shape[1]):
+ track = tracks[:, track_idx : track_idx + 1, :]
+ lufs_db = meter.integrated_loudness(track.squeeze(0).permute(1, 0).numpy())
+
+ if lufs_db < -80.0:
+ print(f"Skipping track {track_idx} with {lufs_db:.2f} LUFS.")
+ continue
+
+ lufs_delta_db = target_lufs_db - lufs_db
+ track *= 10 ** (lufs_delta_db / 20)
+ norm_tracks.append(track)
+
+ norm_tracks = torch.cat(norm_tracks, dim=1)
+ # create a sum mix with equal loudness
+ sum_mix = torch.sum(norm_tracks, dim=1, keepdim=True).repeat(1, 2, 1)
+ sum_mix /= sum_mix.abs().max()
+
+ return sum_mix, None, None, None
+
+class NumpyEncoder(json.JSONEncoder):
+ """ Special json encoder for numpy types """
+ def default(self, obj):
+ if isinstance(obj, np.integer):
+ return int(obj)
+ elif isinstance(obj, np.floating):
+ return float(obj)
+ elif isinstance(obj, np.ndarray):
+ return obj.tolist()
+ return json.JSONEncoder.default(self, obj)
+
+if __name__ == "__main__":
+ meter = pyln.Meter(44100)
+ target_lufs_db = -22.0
+ output_dir = "outputs/ablation"
+ os.makedirs(output_dir, exist_ok=True)
+
+ methods = {
+ "diffmst-16": {
+ "model": load_diffmst(
+ "/Users/svanka/Downloads/b4naquji/config.yaml",
+ "/Users/svanka/Downloads/b4naquji/checkpoints/epoch=191-step=626608.ckpt",
+ ),
+ "func": run_diffmst,
+ },
+ "sum": {
+ "model": (None, None),
+ "func": equal_loudness_mix,
+ },
+ }
+
+ # get the validation examples
+ examples = {
+ "ecstasy": {
+ "tracks": "/Users/svanka/Downloads//diffmst-examples/song1/BenFlowers_Ecstasy_Full/",
+ "ref": "/Users/svanka/Codes/Diff-MST/outputs/ablation_ref_examples/_Feel it all Around_ by Washed Out (Portlandia Theme)_01/",
+ },
+ "by-my-side": {
+ "tracks": "/Users/svanka/Downloads//diffmst-examples/song2/Kat Wright_By My Side/",
+ "ref": "/Users/svanka/Codes/Diff-MST/outputs/ablation_ref_examples/The Dip - Paddle To The Stars (Lyric Video)_01/",
+ },
+ "haunted-aged": {
+ "tracks": "/Users/svanka/Downloads//diffmst-examples/song3/Titanium_HauntedAge_Full/",
+ "ref": "/Users/svanka/Codes/Diff-MST/outputs/ablation_ref_examples/Architects - _Doomsday__01/",
+ },
+ }
+
+
+ loss = AudioFeatureLoss([0.1,0.001,1.0,1.0,0.1], 44100)
+ AF = {}
+ #initialise to negative infinity
+
+ for example_name, example in examples.items():
+
+
+ AF[example_name] = {}
+ print(example_name)
+ example_dir = os.path.join(output_dir, example_name)
+ os.makedirs(example_dir, exist_ok=True)
+ json_dir = os.path.join(output_dir, "AF")
+ if not os.path.exists(json_dir):
+ os.makedirs(json_dir, exist_ok=True)
+ csv_path = os.path.join(json_dir,f"{example_name}.csv")
+ # if not os.path.exists(csv_path):
+ # os.makedirs(csv_path)
+ with open(csv_path, 'w') as f:
+ writer = csv.writer(f)
+ writer.writerow(["method", "audio_type","ablation","start_idx", "stop_idx", "rms", "crest_factor", "stereo_width", "stereo_imbalance", "barkspectrum", "net_AF_loss"])
+ f.close()
+ ref_loudness_target = -16.0
+
+ # --------------first find all the tracks----------------
+ track_filepaths = []
+ for root, dirs, files in os.walk(example["tracks"]):
+ for filepath in files:
+ if filepath.endswith(".wav"):
+ track_filepaths.append(os.path.join(root, filepath))
+
+ print(f"Found {len(track_filepaths)} tracks.")
+
+ # ----------------load the tracks----------------------------
+ tracks = []
+ lengths = []
+ for track_idx, track_filepath in enumerate(track_filepaths):
+ audio, sr = torchaudio.load(track_filepath, backend="soundfile")
+
+ if sr != 44100:
+ audio = torchaudio.functional.resample(audio, sr, 44100)
+
+ # loudness normalize the tracks to -48 LUFS
+ lufs_db = meter.integrated_loudness(audio.permute(1, 0).numpy())
+ # lufs_delta_db = -48 - lufs_db
+ # audio = audio * 10 ** (lufs_delta_db / 20)
+
+ print(track_idx, os.path.basename(track_filepath), audio.shape, sr, lufs_db)
+
+ if audio.shape[0] == 2:
+ audio = audio.mean(dim=0, keepdim=True)
+
+ chs, seq_len = audio.shape
+
+ for ch_idx in range(chs):
+ tracks.append(audio[ch_idx : ch_idx + 1, :])
+ lengths.append(audio.shape[-1])
+
+ # find max length and pad if shorter
+ max_length = max(lengths)
+ min_length = min(lengths)
+ for track_idx in range(len(tracks)):
+ tracks[track_idx] = torch.nn.functional.pad(
+ tracks[track_idx], (0, max_length - lengths[track_idx])
+ )
+
+ # stack into a tensor
+ tracks = torch.cat(tracks, dim=0)
+ tracks = tracks.view(1, -1, max_length)
+ tracks_length = max_length
+ refs = glob.glob(os.path.join(example["ref"],"*.wav"))
+ print("found refs", len(refs))
+ for ref in refs:
+ ref_name = os.path.basename(ref).replace(".wav", "")
+ test_type = ref_name.split("_")[-2] + "_" + ref_name.split("_")[-1]
+ print(test_type)
+
+ print(ref_name)
+ AF[example_name]["ref"] = {}
+ AF[example_name]["pred_mix"] = {}
+ ref_audio, ref_sr = torchaudio.load(ref, backend="soundfile")
+ if ref_sr != 44100:
+ ref_audio = torchaudio.functional.resample(ref_audio, ref_sr, 44100)
+ print(ref_audio.shape, ref_sr)
+ ref_length = ref_audio.shape[-1]
+ ref_audio = ref_audio.view(1, 2, -1)
+
+ #loudness normalize the reference mix to -16 LUFS
+ ref_lufs_db = meter.integrated_loudness(ref_audio.squeeze().permute(1, 0).numpy())
+ lufs_delta_db = ref_loudness_target - ref_lufs_db
+ ref_audio = ref_audio * 10 ** (lufs_delta_db / 20)
+
+
+ # --------------run inference----------------
+ #print(tracks.shape)
+ track_idx = int(tracks_length / 2)
+ ref_idx = int(ref_length / 2)
+ mix_tracks = tracks[..., track_idx - 220500 : track_idx + 220500]
+ ref_analysis = ref_audio[..., ref_idx - 220500 : ref_idx + 220500]
+
+ ref_path = os.path.join(example_dir, os.path.basename(ref).replace(".wav", "-ref-16.wav"))
+ torchaudio.save(ref_path, ref_analysis.squeeze(), 44100)
+
+ for method_name, method in methods.items():
+ AF[example_name]["ref"] [method_name] = {}
+ AF[example_name]["pred_mix"] [method_name] = {}
+
+ print(method_name)
+ model, mix_console = method["model"]
+ func = method["func"]
+
+ with torch.no_grad():
+ result = func(
+ mix_tracks.clone(),
+ ref_analysis.clone(),
+ model,
+ mix_console,
+ track_start_idx=0,
+ ref_start_idx=0,
+ )
+
+ (
+ pred_mix,
+ pred_track_param_dict,
+ pred_fx_bus_param_dict,
+ pred_master_bus_param_dict,
+ ) = result
+
+ bs, chs, seq_len = pred_mix.shape
+ print("pred_mix shape", pred_mix.shape)
+ # loudness normalize the output mix
+ mix_lufs_db = meter.integrated_loudness(
+ pred_mix.squeeze(0).permute(1, 0).numpy()
+ )
+ print("pred_mix_lufs_db", mix_lufs_db)
+ #print(mix_lufs_db)
+ lufs_delta_db = target_lufs_db - mix_lufs_db
+ pred_mix = pred_mix * 10 ** (lufs_delta_db / 20)
+ name = os.path.basename(ref).replace(".wav", "-pred_mix-16.wav")
+ mix_filepath = os.path.join(example_dir, f"{method_name}_{name}")
+ torchaudio.save(mix_filepath, pred_mix.view(chs, -1), 44100)
+
+ # compute audio features
+
+ AF[example_name]["pred_mix"][method_name]["mix-rms"] = 0.1*compute_rms(pred_mix, sample_rate = sr).mean().detach().cpu().numpy()
+ AF[example_name]["pred_mix"][method_name]["mix-crest_factor"] = 0.001*compute_crest_factor(pred_mix, sample_rate = sr).mean().detach().cpu().numpy()
+ AF[example_name]["pred_mix"][method_name]["mix-stereo_width"] = 1.0*compute_stereo_width(pred_mix, sample_rate = sr).detach().cpu().numpy()
+ AF[example_name]["pred_mix"][method_name]["mix-stereo_imbalance"] = 1.0*compute_stereo_imbalance(pred_mix, sample_rate = sr).detach().cpu().numpy()
+ AF[example_name]["pred_mix"][method_name]["mix-barkspectrum"] = 0.1*compute_barkspectrum(pred_mix, sample_rate = sr).mean().detach().cpu().numpy()
+
+ AF[example_name]["ref"][method_name]["mix-rms"] = 0.1*compute_rms(ref_analysis, sample_rate = sr).mean().detach().cpu().numpy()
+ AF[example_name]["ref"][method_name]["mix-crest_factor"] = 0.001*compute_crest_factor(ref_analysis, sample_rate = sr).mean().detach().cpu().numpy()
+ AF[example_name]["ref"][method_name]["mix-stereo_width"] = 1.0*compute_stereo_width(ref_analysis, sample_rate = sr).detach().cpu().numpy()
+ AF[example_name]["ref"][method_name]["mix-stereo_imbalance"] = 1.0*compute_stereo_imbalance(ref_analysis, sample_rate = sr).detach().cpu().numpy()
+ AF[example_name]["ref"][method_name]["mix-barkspectrum"] = 0.1*compute_barkspectrum(ref_analysis, sample_rate = sr).mean().detach().cpu().numpy()
+
+ AF_loss = loss(pred_mix, ref_analysis)
+ AF[example_name]["pred_mix"][method_name]["net_AF_loss"] = sum(AF_loss.values()).detach().cpu().numpy()
+ AF[example_name]["ref"][method_name]["net_AF_loss"] = AF[example_name]["pred_mix"][method_name]["net_AF_loss"]
+
+
+ # save resulting audio and parameters
+ #append to csv the method name, audio section, audio features values and net loss on different columns
+ with open(csv_path, 'a') as f:
+ writer = csv.writer(f)
+ writer.writerow([method_name, "pred_mix", test_type, track_idx - 220500, track_idx + 220500,AF[example_name]["pred_mix"][method_name]["mix-rms"], AF[example_name]["pred_mix"][method_name]["mix-crest_factor"], AF[example_name]["pred_mix"][method_name]["mix-stereo_width"], AF[example_name]["pred_mix"][method_name]["mix-stereo_imbalance"], AF[example_name]["pred_mix"][method_name]["mix-barkspectrum"], AF[example_name]["pred_mix"][method_name]["net_AF_loss"]])
+ writer.writerow([method_name, "ref", test_type, ref_idx - 220500, ref_idx + 220500,AF[example_name]["ref"][method_name]["mix-rms"], AF[example_name]["ref"][method_name]["mix-crest_factor"], AF[example_name]["ref"][method_name]["mix-stereo_width"], AF[example_name]["ref"][method_name]["mix-stereo_imbalance"], AF[example_name]["ref"][method_name]["mix-barkspectrum"], AF[example_name]["ref"][method_name]["net_AF_loss"]])
+ f.close()
+
+
+
+ #write disctionary to json
+
+
diff --git a/scripts/eval_all_combo.py b/scripts/eval_all_combo.py
new file mode 100644
index 0000000..6fa141e
--- /dev/null
+++ b/scripts/eval_all_combo.py
@@ -0,0 +1,282 @@
+# run pretrained models over evaluation set to generate audio examples for the listening test
+import os
+import torch
+import torchaudio
+import pyloudnorm as pyln
+from mst.utils import load_diffmst, run_diffmst
+from mst.loss import compute_barkspectrum, compute_rms, compute_crest_factor, compute_stereo_width, compute_stereo_imbalance, AudioFeatureLoss
+import json
+import numpy as np
+import csv
+
+
+def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
+
+ meter = pyln.Meter(44100)
+ target_lufs_db = -48.0
+
+ norm_tracks = []
+ for track_idx in range(tracks.shape[1]):
+ track = tracks[:, track_idx : track_idx + 1, :]
+ lufs_db = meter.integrated_loudness(track.squeeze(0).permute(1, 0).numpy())
+
+ if lufs_db < -80.0:
+ print(f"Skipping track {track_idx} with {lufs_db:.2f} LUFS.")
+ continue
+
+ lufs_delta_db = target_lufs_db - lufs_db
+ track *= 10 ** (lufs_delta_db / 20)
+ norm_tracks.append(track)
+
+ norm_tracks = torch.cat(norm_tracks, dim=1)
+ # create a sum mix with equal loudness
+ sum_mix = torch.sum(norm_tracks, dim=1, keepdim=True).repeat(1, 2, 1)
+ sum_mix /= sum_mix.abs().max()
+
+ return sum_mix, None, None, None
+
+class NumpyEncoder(json.JSONEncoder):
+ """ Special json encoder for numpy types """
+ def default(self, obj):
+ if isinstance(obj, np.integer):
+ return int(obj)
+ elif isinstance(obj, np.floating):
+ return float(obj)
+ elif isinstance(obj, np.ndarray):
+ return obj.tolist()
+ return json.JSONEncoder.default(self, obj)
+
+if __name__ == "__main__":
+ meter = pyln.Meter(44100)
+ target_lufs_db = -22.0
+ output_dir = "outputs/listen"
+ os.makedirs(output_dir, exist_ok=True)
+
+ methods = {
+ "diffmst-16": {
+ "model": load_diffmst(
+ "/Users/svanka/Downloads/b4naquji/config.yaml",
+ "/Users/svanka/Downloads/b4naquji/checkpoints/epoch=191-step=626608.ckpt",
+ ),
+ "func": run_diffmst,
+ },
+ "sum": {
+ "model": (None, None),
+ "func": equal_loudness_mix,
+ },
+ }
+
+ # get the validation examples
+ examples = {
+ # "ecstasy": {
+ # "tracks": "/Users/svanka/Downloads//diffmst-examples/song1/BenFlowers_Ecstasy_Full/",
+ # "ref": "/Users/svanka/Downloads//diffmst-examples/song1/ref/_Feel it all Around_ by Washed Out (Portlandia Theme)_01.wav",
+ # },
+ # "by-my-side": {
+ # "tracks": "/Users/svanka/Downloads//diffmst-examples/song2/Kat Wright_By My Side/",
+ # "ref": "/Users/svanka/Downloads//diffmst-examples/song2/ref/The Dip - Paddle To The Stars (Lyric Video)_01.wav",
+ # },
+ "haunted-aged": {
+ "tracks": "/Users/svanka/Downloads//diffmst-examples/song3/Titanium_HauntedAge_Full/",
+ "ref": "/Users/svanka/Downloads//diffmst-examples/song3/ref/Architects - _Doomsday__01.wav",
+ },
+ }
+ loss = AudioFeatureLoss([0.1,0.001,1.0,1.0,0.1], 44100)
+ AF = {}
+ #initialise to negative infinity
+
+ for example_name, example in examples.items():
+
+ AF[example_name] = {}
+ print(example_name)
+ example_dir = os.path.join(output_dir, example_name)
+ os.makedirs(example_dir, exist_ok=True)
+ json_dir = os.path.join(output_dir, "AF")
+ if not os.path.exists(json_dir):
+ os.makedirs(json_dir, exist_ok=True)
+ csv_path = os.path.join(json_dir,f"{example_name}.csv")
+ # if not os.path.exists(csv_path):
+ # os.makedirs(csv_path)
+ with open(csv_path, 'w') as f:
+ writer = csv.writer(f)
+ writer.writerow(["method", "audio_section","track_start_idx", "track_stop_idx", "ref_start_idx", "ref_stop_idx", "rms", "crest_factor", "stereo_width", "stereo_imbalance", "barkspectrum", "net_AF_loss"])
+ f.close()
+
+ # ----------load reference mix---------------
+ ref_audio, ref_sr = torchaudio.load(example["ref"], backend="soundfile")
+ if ref_sr != 44100:
+ ref_audio = torchaudio.functional.resample(ref_audio, ref_sr, 44100)
+ print(ref_audio.shape, ref_sr)
+ ref_length = ref_audio.shape[-1]
+ # --------------first find all the tracks----------------
+ track_filepaths = []
+ for root, dirs, files in os.walk(example["tracks"]):
+ for filepath in files:
+ if filepath.endswith(".wav"):
+ track_filepaths.append(os.path.join(root, filepath))
+
+ print(f"Found {len(track_filepaths)} tracks.")
+
+ # ----------------load the tracks----------------------------
+ tracks = []
+ lengths = []
+ for track_idx, track_filepath in enumerate(track_filepaths):
+ audio, sr = torchaudio.load(track_filepath, backend="soundfile")
+
+ if sr != 44100:
+ audio = torchaudio.functional.resample(audio, sr, 44100)
+
+ # loudness normalize the tracks to -48 LUFS
+ lufs_db = meter.integrated_loudness(audio.permute(1, 0).numpy())
+ # lufs_delta_db = -48 - lufs_db
+ # audio = audio * 10 ** (lufs_delta_db / 20)
+
+ print(track_idx, os.path.basename(track_filepath), audio.shape, sr, lufs_db)
+
+ if audio.shape[0] == 2:
+ audio = audio.mean(dim=0, keepdim=True)
+
+ chs, seq_len = audio.shape
+
+ for ch_idx in range(chs):
+ tracks.append(audio[ch_idx : ch_idx + 1, :])
+ lengths.append(audio.shape[-1])
+
+ # find max length and pad if shorter
+ max_length = max(lengths)
+ min_length = min(lengths)
+ for track_idx in range(len(tracks)):
+ tracks[track_idx] = torch.nn.functional.pad(
+ tracks[track_idx], (0, max_length - lengths[track_idx])
+ )
+
+ # stack into a tensor
+ tracks = torch.cat(tracks, dim=0)
+ tracks = tracks.view(1, -1, max_length)
+ ref_audio = ref_audio.view(1, 2, -1)
+
+ # crop tracks to max of 60 seconds or so
+ # tracks = tracks[..., :4194304]
+ tracks_length = max_length
+
+ #print(tracks.shape)
+ track_start_idx = int(tracks_length / 4)
+ ref_start_idx = int(ref_length / 4)
+ track_stop_idx = int(3*tracks_length / 4)
+ ref_stop_idx = int(3*ref_length / 4)
+ #find the number of sets of track samples of 10 sec duration each
+ track_num_sets = int((track_stop_idx - track_start_idx) / 441000)
+ ref_num_sets = int((ref_stop_idx - ref_start_idx) / 441000)
+ print("track_num_sets", track_num_sets)
+ print("ref_num_sets", ref_num_sets)
+ min_AF_loss = float('inf')
+ min_AF_loss_example = None
+ for i in range(track_num_sets):
+ for j in range(ref_num_sets):
+ print(f"track-{i}-ref-{j}")
+ #run inference for every combination of track and ref samples and calculate audio features.
+ # We will save the audio features to a csv and audio files in the output directory
+ mix_tracks = tracks[..., track_start_idx + i*441000 : track_start_idx + (i+1)*441000]
+ ref_analysis = ref_audio[..., ref_start_idx + j*441000 : ref_start_idx + (j+1)*441000]
+
+ # create mixes varying the loudness of the reference
+ for ref_loudness_target in [-16.0]:
+ print("Ref loudness", ref_loudness_target)
+ ref_filepath = os.path.join(
+ example_dir,
+ f"ref-analysis-track-{i}-ref-{j}-lufs-{ref_loudness_target:0.0f}.wav",
+ )
+
+ # loudness normalize the reference mix section to -14 LUFS
+ ref_lufs_db = meter.integrated_loudness(
+ ref_analysis.squeeze().permute(1, 0).numpy()
+ )
+ print("ref_lufs_db", ref_lufs_db)
+ lufs_delta_db = ref_loudness_target - ref_lufs_db
+ ref_analysis = ref_analysis * 10 ** (lufs_delta_db / 20)
+
+ torchaudio.save(ref_filepath, ref_analysis.squeeze(), 44100)
+
+ AF_loss = 0
+ for method_name, method in methods.items():
+ AF[example_name][method_name] = {}
+ print(method_name)
+ # tracks (torch.Tensor): Set of input tracks with shape (bs, num_tracks, seq_len)
+ # ref_audio (torch.Tensor): Reference mix with shape (bs, 2, seq_len)
+
+ if method_name == "sum":
+ if ref_loudness_target != -16:
+ continue
+
+
+ model, mix_console = method["model"]
+ func = method["func"]
+
+ #print(tracks.shape, ref_audio.shape)
+ audio_section = f"track-{i}-ref-{j}-lufs-{ref_loudness_target:0.0f}"
+ AF[example_name][method_name][audio_section] = {}
+ AF[example_name][method_name][audio_section]["track_start_idx"] = track_start_idx + i*441000
+ AF[example_name][method_name][audio_section]["track_stop_idx"] = track_start_idx + (i+1)*441000
+ AF[example_name][method_name][audio_section]["ref_start_idx"] = ref_start_idx + j*441000
+ AF[example_name][method_name][audio_section]["ref_stop_idx"] = ref_start_idx + (j+1)*441000
+ with torch.no_grad():
+ result = func(
+ mix_tracks.clone(),
+ ref_analysis.clone(),
+ model,
+ mix_console,
+ track_start_idx=0,
+ ref_start_idx=0,
+ )
+
+ (
+ pred_mix,
+ pred_track_param_dict,
+ pred_fx_bus_param_dict,
+ pred_master_bus_param_dict,
+ ) = result
+
+ bs, chs, seq_len = pred_mix.shape
+ print("pred_mix shape", pred_mix.shape)
+ # loudness normalize the output mix
+ mix_lufs_db = meter.integrated_loudness(
+ pred_mix.squeeze(0).permute(1, 0).numpy()
+ )
+ print("pred_mix_lufs_db", mix_lufs_db)
+ #print(mix_lufs_db)
+ lufs_delta_db = target_lufs_db - mix_lufs_db
+ pred_mix = pred_mix * 10 ** (lufs_delta_db / 20)
+ mix_filepath = os.path.join(
+ example_dir,
+ f"{example_name}-{method_name}-tracks-{i}-ref={j}-lufs-{ref_loudness_target:0.0f}.wav",
+ )
+ torchaudio.save(mix_filepath, pred_mix.view(chs, -1), 44100)
+
+ # compute audio features
+ AF_loss = loss(pred_mix, ref_analysis)
+
+ for key, value in AF_loss.items():
+ AF[example_name][method_name][audio_section][key] = value.detach().cpu().numpy()
+ AF[example_name][method_name][audio_section]["net_AF_loss"] = sum(AF_loss.values()).detach().cpu().numpy()
+ print(AF[example_name][method_name][audio_section])
+
+ if AF[example_name][method_name][audio_section]["net_AF_loss"] < min_AF_loss:
+ min_AF_loss = AF[example_name][method_name][audio_section]["net_AF_loss"]
+ min_AF_loss_example = f"{example_name}-{method_name}-{audio_section}"
+ print("min_AF_loss", min_AF_loss)
+ print("min_AF_loss_example", min_AF_loss_example)
+ # save resulting audio and parameters
+ #append to csv the method name, audio section, audio features values and net loss on different columns
+
+ with open(csv_path, 'a') as f:
+ writer = csv.writer(f)
+ writer.writerow([method_name, audio_section, AF[example_name][method_name][audio_section]["track_start_idx"], AF[example_name][method_name][audio_section]["track_stop_idx"], AF[example_name][method_name][audio_section]["ref_start_idx"], AF[example_name][method_name][audio_section]["ref_stop_idx"], AF[example_name][method_name][audio_section]["mix-rms"], AF[example_name][method_name][audio_section]["mix-crest_factor"], AF[example_name][method_name][audio_section]["mix-stereo_width"], AF[example_name][method_name][audio_section]["mix-stereo_imbalance"], AF[example_name][method_name][audio_section]["mix-barkspectrum"], AF[example_name][method_name][audio_section]["net_AF_loss"]])
+ f.close()
+
+
+ print(f"for {example_name} min loss is {min_AF_loss} corresponding to {min_AF_loss_example}")
+ print()
+
+ #write disctionary to json
+
+
diff --git a/scripts/eval_listen.py b/scripts/eval_listen.py
index 6cd67b6..01f321f 100644
--- a/scripts/eval_listen.py
+++ b/scripts/eval_listen.py
@@ -35,7 +35,7 @@ def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
if __name__ == "__main__":
meter = pyln.Meter(44100)
target_lufs_db = -22.0
- output_dir = "outputs/listen"
+ output_dir = "outputs/listen_1"
os.makedirs(output_dir, exist_ok=True)
methods = {
@@ -113,7 +113,7 @@ def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
# lufs_delta_db = -48 - lufs_db
# audio = audio * 10 ** (lufs_delta_db / 20)
- print(track_idx, os.path.basename(track_filepath), audio.shape, sr, lufs_db)
+ #print(track_idx, os.path.basename(track_filepath), audio.shape, sr, lufs_db)
if audio.shape[0] == 2:
audio = audio.mean(dim=0, keepdim=True)
@@ -123,14 +123,14 @@ def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
for ch_idx in range(chs):
tracks.append(audio[ch_idx : ch_idx + 1, :])
lengths.append(audio.shape[-1])
-
+ print("Loaded tracks.")
# find max length and pad if shorter
max_length = max(lengths)
for track_idx in range(len(tracks)):
tracks[track_idx] = torch.nn.functional.pad(
tracks[track_idx], (0, max_length - lengths[track_idx])
)
-
+ print("Padded tracks.")
# stack into a tensor
tracks = torch.cat(tracks, dim=0)
tracks = tracks.view(1, -1, max_length)
@@ -144,6 +144,8 @@ def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
# create a sum mix with equal loudness
sum_mix = torch.sum(tracks, dim=1, keepdim=True).squeeze(0)
sum_filepath = os.path.join(example_dir, f"{example_name}-sum.wav")
+ os.makepath(sum_filepath)
+ print("sum_mix path created")
# loudness normalize the sum mix
sum_lufs_db = meter.integrated_loudness(sum_mix.permute(1, 0).numpy())
@@ -151,10 +153,12 @@ def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
sum_mix = sum_mix * 10 ** (lufs_delta_db / 20)
torchaudio.save(sum_filepath, sum_mix.view(1, -1), 44100)
+ print("Sum mix saved.")
# save the reference mix
ref_filepath = os.path.join(example_dir, "ref-full.wav")
torchaudio.save(ref_filepath, ref_audio.squeeze(), 44100)
+ print("Reference mix saved.")
for song_section in ["verse", "chorus"]:
print("Mixing", song_section)
diff --git a/scripts/find_lowest_loss.py b/scripts/find_lowest_loss.py
new file mode 100644
index 0000000..79bbf48
--- /dev/null
+++ b/scripts/find_lowest_loss.py
@@ -0,0 +1,19 @@
+import csv
+import sys
+import pandas as pd
+import os
+
+if __name__ == "__main__":
+ csv_paths = ["/Users/svanka/Codes/Diff-MST/outputs/listen/AF/by-my-side.csv",
+ "/Users/svanka/Codes/Diff-MST/outputs/listen/AF/ecstasy.csv"
+ ]
+
+ for csv_path in csv_paths:
+ print(os.path.basename(csv_path).replace('.csv', ''))
+ #read csv using pandas
+ df = pd.read_csv(csv_path)
+ #find the row with the lowest loss
+ lowest_loss = df['net_AF_loss'].min()
+ #find the method,audio_section with the lowest loss
+ lowest_loss_row = df.loc[df['net_AF_loss'] == lowest_loss]
+ print(lowest_loss_row)
diff --git a/scripts/gain_testing.py b/scripts/gain_testing.py
new file mode 100644
index 0000000..eddcc04
--- /dev/null
+++ b/scripts/gain_testing.py
@@ -0,0 +1,219 @@
+
+# run pretrained models over evaluation set to generate audio examples for the listening test
+import os
+import torch
+import torchaudio
+import pyloudnorm as pyln
+from mst.utils import load_diffmst, run_diffmst
+from mst.loss import compute_barkspectrum, compute_rms, compute_crest_factor, compute_stereo_width, compute_stereo_imbalance, AudioFeatureLoss
+import json
+import numpy as np
+import csv
+import glob
+import yaml
+
+
+def equal_loudness_mix(tracks: torch.Tensor, *args, **kwargs):
+
+ meter = pyln.Meter(44100)
+ target_lufs_db = -48.0
+
+ norm_tracks = []
+ for track_idx in range(tracks.shape[1]):
+ track = tracks[:, track_idx : track_idx + 1, :]
+ lufs_db = meter.integrated_loudness(track.squeeze(0).permute(1, 0).numpy())
+
+ if lufs_db < -80.0:
+ print(f"Skipping track {track_idx} with {lufs_db:.2f} LUFS.")
+ continue
+
+ lufs_delta_db = target_lufs_db - lufs_db
+ track *= 10 ** (lufs_delta_db / 20)
+ norm_tracks.append(track)
+
+ norm_tracks = torch.cat(norm_tracks, dim=1)
+ # create a sum mix with equal loudness
+ sum_mix = torch.sum(norm_tracks, dim=1, keepdim=True).repeat(1, 2, 1)
+ sum_mix /= sum_mix.abs().max()
+
+ return sum_mix, None, None, None
+
+
+
+if __name__ == "__main__":
+ meter = pyln.Meter(44100)
+ target_mix_lufs_db = -16.0
+ target_track_lufs_db = -48.0
+ output_dir = "outputs/gain_testing_diff_song_individual_tracks"
+ os.makedirs(output_dir, exist_ok=True)
+
+ methods = {
+ "diffmst-16": {
+ "model": load_diffmst(
+ "/Users/svanka/Downloads/b4naquji/config.yaml",
+ "/Users/svanka/Downloads/b4naquji/checkpoints/epoch=191-step=626608.ckpt",
+ ),
+ "func": run_diffmst,
+ },
+ # "sum": {
+ # "model": (None, None),
+ # "func": equal_loudness_mix,
+ # },
+ }
+
+ ref_dir = "/Users/svanka/Downloads/DSD100subset/sources/Dev/055 - Angels In Amplifiers - I'm Alright"
+ #mix_dir = "/Users/svanka/Downloads/DSD100subset/sources/Dev/055 - Angels In Amplifiers - I'm Alright"
+ mix_dir = "/Users/svanka/Downloads/DSD100subset/Sources/Test/049 - Young Griffo - Facade"
+
+ ref_tracks = glob.glob(os.path.join(ref_dir, "*.wav"))
+ mix_tracks = glob.glob(os.path.join(mix_dir, "*.wav"))
+
+ print(len(ref_tracks), len(mix_tracks))
+ #order the tracks in ref_tracks to vocals, bass, other, drums
+ ref_tracks_ordered = [""] * 4
+ for track in ref_tracks:
+ if "vocals" in track:
+ ref_tracks_ordered[0] = track
+ elif "bass" in track:
+ ref_tracks_ordered[1] = track
+ elif "other" in track:
+ ref_tracks_ordered[2] = track
+ elif "drums" in track:
+ ref_tracks_ordered[3] = track
+ ref_tracks = ref_tracks_ordered
+
+ print(ref_tracks)
+ # print(mix_tracks)
+
+
+ #we will predict a mix for one track from reference, sum of two, sum of three, sum of four tracks from reference as the reference for model
+ # and the mix as the input
+
+ tracks = []
+ #info = torchaudio.info(mix_tracks[0])
+
+
+ track_instrument = []
+ for track in mix_tracks:
+ #audio, sr = torchaudio.load(track, frame_offset = int((info.num_frames)/2 - 220500), num_frames = 441000, backend="soundfile")
+ audio, sr = torchaudio.load(track,num_frames = 441000, backend="soundfile")
+ if sr != 44100:
+ audio = torchaudio.functional.resample(audio, sr, 44100)
+
+ if audio.shape[0] == 2:
+ audio = audio.mean(dim=0, keepdim=True)
+
+ tracks.append(audio)
+ track_instrument.append(os.path.basename(track).replace(".wav", ""))
+
+
+ tracks = torch.cat(tracks, dim=0)
+ print("tracks shape", tracks.shape)
+ tracks = tracks.unsqueeze(0)
+ print("tracks shape", tracks.shape)
+
+ #create a sum mix
+ sum_mix, _, _, _ = equal_loudness_mix(tracks)
+ print("sum_mix shape", sum_mix.shape)
+ save_path = os.path.join(output_dir, f"{os.path.basename(mix_dir)}-sum_mix.wav")
+ torchaudio.save(save_path, sum_mix.view(2, -1), 44100)
+
+ ref_mix_tracks = []
+ info = torchaudio.info(ref_tracks[0])
+ name = "ref_mix-16="
+ data = {}
+ data["track_instrument"] = track_instrument
+ for i , ref_track in enumerate(ref_tracks):
+ instrument = name + "-" + os.path.basename(ref_track).replace(".wav", "")
+ print(instrument)
+ #name = instrument
+ ref_audio, sr = torchaudio.load(ref_track, frame_offset = int((info.num_frames)/2 - 220500), num_frames = 441000, backend="soundfile")
+ if sr != 44100:
+ ref_audio = torchaudio.functional.resample(ref_audio, sr, 44100)
+
+ #loudness normalize the reference mix to -48 LUFS
+ ref_lufs_db = meter.integrated_loudness(ref_audio.squeeze().permute(1, 0).numpy())
+ lufs_delta_db = target_track_lufs_db - ref_lufs_db
+ ref_audio = ref_audio * 10 ** (lufs_delta_db / 20)
+
+ #ref_mix_tracks.append(ref_audio)
+ ref_mix_tracks = [ref_audio]
+ ref_mix = torch.cat(ref_mix_tracks, dim=0)
+ #create a stereo sum mix
+ ref_mix = ref_mix.sum(dim=0, keepdim=True).repeat(1, 2, 1)
+ #normalise to -16 LUFS
+ ref_mix_lufs_db = meter.integrated_loudness(ref_mix.squeeze().permute(1, 0).numpy())
+ lufs_delta_db = target_mix_lufs_db - ref_mix_lufs_db
+ ref_mix = ref_mix * 10 ** (lufs_delta_db / 20)
+ ref_save_path = os.path.join(output_dir, f"{os.path.basename(ref_dir)}-{instrument}.wav")
+ torchaudio.save(ref_save_path, ref_mix.view(2, -1), 44100)
+
+ yaml_path = os.path.join(output_dir, f"{os.path.basename(ref_dir)}-{instrument}.yaml")
+ data["ref_mix"] = ref_save_path
+ data["ref_instruments"] = instrument
+ data["sum_mix"] = save_path
+ #check if the json file exists
+ print("tracks shape", tracks.shape)
+ print("ref_mix shape", ref_mix.shape)
+
+
+
+
+ for method_name, method in methods.items():
+ model, mix_console = method["model"]
+ func = method["func"]
+ with torch.no_grad():
+ result = func(
+ tracks.clone(),
+ ref_mix.clone(),
+ model,
+ mix_console,
+ track_start_idx=0,
+ ref_start_idx=0,
+ )
+
+ (
+ pred_mix,
+ pred_track_param_dict,
+ pred_fx_bus_param_dict,
+ pred_master_bus_param_dict,
+ ) = result
+
+
+ bs, chs, seq_len = pred_mix.shape
+ print("pred_mix shape", pred_mix.shape)
+ # loudness normalize the output mix
+ mix_lufs_db = meter.integrated_loudness(
+ pred_mix.squeeze(0).permute(1, 0).numpy()
+ )
+ print("pred_mix_lufs_db", mix_lufs_db)
+ #print(mix_lufs_db)
+ lufs_delta_db = target_mix_lufs_db - mix_lufs_db
+ pred_mix = pred_mix * 10 ** (lufs_delta_db / 20)
+ pred_mix_name = os.path.basename(mix_dir) + f"-pred_mix-ref_mix-16={instrument}.wav"
+ mix_filepath = os.path.join(output_dir, pred_mix_name)
+ torchaudio.save(mix_filepath, pred_mix.view(chs, -1), 44100)
+ # append to the json file param_dicts
+
+ #print(pred_track_param_dict["input_gain"])
+
+ data["pred_mix"] = pred_mix_name
+ data["gain_values"] = pred_track_param_dict['input_fader']['gain_db'].detach().cpu().numpy().tolist()[0]
+ #print(type(pred_track_param_dict['input_fader']['gain_db']))
+
+
+ with open(yaml_path, "w") as f:
+ yaml.dump(data, f)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/generate_ablation_examples.py b/scripts/generate_ablation_examples.py
new file mode 100644
index 0000000..942fbc7
--- /dev/null
+++ b/scripts/generate_ablation_examples.py
@@ -0,0 +1,99 @@
+import torch
+import torchaudio
+import os
+import glob
+
+def low_pass_audio(y, sr, cutoff_freq):
+ # cutoff_freq: in Hz
+ # y: waveform
+ # sr: sample rate
+ # cutoff_freq: cutoff frequency
+ # cutoff_freq = 4000
+ y = torchaudio.functional.lowpass_biquad(y, sr, cutoff_freq)
+ return y
+
+def high_pass_audio(y, sr, cutoff_freq):
+ # cutoff_freq: in Hz
+ # y: waveform
+ # sr: sample rate
+ # cutoff_freq: cutoff frequency
+ # cutoff_freq = 4000
+ y = torchaudio.functional.highpass_biquad(y, sr, cutoff_freq)
+ return y
+
+def band_pass_audio(y, sr, low_cutoff_freq, high_cutoff_freq):
+ # cutoff_freq: in Hz
+ # y: waveform
+ # sr: sample rate
+ # low_cutoff_freq: low cutoff frequency
+ # high_cutoff_freq: high cutoff frequency
+ # low_cutoff_freq = 4000
+ # high_cutoff_freq = 8000
+ y = torchaudio.functional.bandpass_biquad(y, sr, low_cutoff_freq, high_cutoff_freq)
+ return y
+
+def pan_left_audio(y, sr):
+ # y: waveform
+ # sr: sample rate
+ if y.shape[0] != 2:
+ raise ValueError("Audio must have 2 channels for panning.")
+
+ # Apply extreme panning to the left channel
+ panned_waveform = torch.zeros_like(y)
+ panned_waveform[0] = y[0] # Left channel remains unchanged
+ panned_waveform[1] = y[1] * 0.1 # Decrease amplitude of right channel (adjust value as needed)
+ return panned_waveform
+
+def pan_right_audio(y, sr):
+ # y: waveform
+ # sr: sample rate
+ if y.shape[0] != 2:
+ raise ValueError("Audio must have 2 channels for panning.")
+
+ # Apply extreme panning to the right channel
+ panned_waveform = torch.zeros_like(y)
+ panned_waveform[0] = y[0] * 0.1 # Decrease amplitude of left channel (adjust value as needed)
+ panned_waveform[1] = y[1] # Right channel remains unchanged
+ return panned_waveform
+
+
+
+
+if __name__ == "__main__":
+ ref_audio_paths = ["/Users/svanka/Downloads//diffmst-examples/song1/ref/_Feel it all Around_ by Washed Out (Portlandia Theme)_01.wav",
+ "/Users/svanka/Downloads//diffmst-examples/song2/ref/The Dip - Paddle To The Stars (Lyric Video)_01.wav",
+ "/Users/svanka/Downloads//diffmst-examples/song3/ref/Architects - _Doomsday__01.wav"]
+
+ ref_save_path = "outputs/ablation_ref_examples"
+ os.makedirs(ref_save_path, exist_ok=True)
+
+ for ref_audio_path in ref_audio_paths:
+ print(os.path.basename(ref_audio_path) + "...")
+
+ save_path = os.path.join(ref_save_path, os.path.basename(ref_audio_path).replace(".wav", ""))
+ os.makedirs(save_path, exist_ok=True)
+
+
+ y, sr = torchaudio.load(ref_audio_path, backend="soundfile")
+
+ # Apply low-pass filter
+ y_low_pass = low_pass_audio(y, sr, 5000)
+ torchaudio.save(os.path.join(save_path, os.path.basename(ref_audio_path).replace(".wav", "_low_pass.wav")), y_low_pass, sr)
+
+ # Apply high-pass filter
+ y_high_pass = high_pass_audio(y, sr, 4000)
+ torchaudio.save(os.path.join(save_path, os.path.basename(ref_audio_path).replace(".wav", "_high_pass.wav")), y_high_pass, sr)
+
+ # Apply band-pass filter
+ y_band_pass = band_pass_audio(y, sr, 500, 8000)
+ torchaudio.save(os.path.join(save_path, os.path.basename(ref_audio_path).replace(".wav", "_band_pass.wav")), y_band_pass, sr)
+
+ # Pan left
+ y_pan_left = pan_left_audio(y, sr)
+ torchaudio.save(os.path.join(save_path, os.path.basename(ref_audio_path).replace(".wav", "_pan_left.wav")), y_pan_left, sr)
+
+ # Pan right
+ y_pan_right = pan_right_audio(y, sr)
+ torchaudio.save(os.path.join(save_path, os.path.basename(ref_audio_path).replace(".wav", "_pan_right.wav")), y_pan_right, sr)
+
+
diff --git a/scripts/plots.ipynb b/scripts/plots.ipynb
new file mode 100644
index 0000000..d96bc41
--- /dev/null
+++ b/scripts/plots.ipynb
@@ -0,0 +1,2383 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: pandas in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (2.2.1)\n",
+ "Requirement already satisfied: numpy<2,>=1.26.0 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from pandas) (1.26.4)\n",
+ "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from pandas) (2.9.0)\n",
+ "Requirement already satisfied: pytz>=2020.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from pandas) (2024.1)\n",
+ "Requirement already satisfied: tzdata>=2022.7 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from pandas) (2024.1)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
+ "Requirement already satisfied: matplotlib in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (3.8.3)\n",
+ "Requirement already satisfied: contourpy>=1.0.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (1.2.1)\n",
+ "Requirement already satisfied: cycler>=0.10 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (0.12.1)\n",
+ "Requirement already satisfied: fonttools>=4.22.0 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (4.50.0)\n",
+ "Requirement already satisfied: kiwisolver>=1.3.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (1.4.5)\n",
+ "Requirement already satisfied: numpy<2,>=1.21 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (1.26.4)\n",
+ "Requirement already satisfied: packaging>=20.0 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (24.0)\n",
+ "Requirement already satisfied: pillow>=8 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (10.3.0)\n",
+ "Requirement already satisfied: pyparsing>=2.3.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (3.1.2)\n",
+ "Requirement already satisfied: python-dateutil>=2.7 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib) (2.9.0)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n",
+ "Requirement already satisfied: seaborn in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (0.13.2)\n",
+ "Requirement already satisfied: numpy!=1.24.0,>=1.20 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from seaborn) (1.26.4)\n",
+ "Requirement already satisfied: pandas>=1.2 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from seaborn) (2.2.1)\n",
+ "Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from seaborn) (3.8.3)\n",
+ "Requirement already satisfied: contourpy>=1.0.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.2.1)\n",
+ "Requirement already satisfied: cycler>=0.10 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.12.1)\n",
+ "Requirement already satisfied: fonttools>=4.22.0 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.50.0)\n",
+ "Requirement already satisfied: kiwisolver>=1.3.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.5)\n",
+ "Requirement already satisfied: packaging>=20.0 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (24.0)\n",
+ "Requirement already satisfied: pillow>=8 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (10.3.0)\n",
+ "Requirement already satisfied: pyparsing>=2.3.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.1.2)\n",
+ "Requirement already satisfied: python-dateutil>=2.7 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.9.0)\n",
+ "Requirement already satisfied: pytz>=2020.1 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from pandas>=1.2->seaborn) (2024.1)\n",
+ "Requirement already satisfied: tzdata>=2022.7 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from pandas>=1.2->seaborn) (2024.1)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.16.0)\n",
+ "Collecting scikit-learn\n",
+ " Using cached scikit_learn-1.4.1.post1-cp312-cp312-macosx_12_0_arm64.whl.metadata (11 kB)\n",
+ "Requirement already satisfied: numpy<2.0,>=1.19.5 in /Users/svanka/miniforge3/envs/dmst/lib/python3.12/site-packages (from scikit-learn) (1.26.4)\n",
+ "Collecting scipy>=1.6.0 (from scikit-learn)\n",
+ " Downloading scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl.metadata (60 kB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m60.6/60.6 kB\u001b[0m \u001b[31m3.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
+ "\u001b[?25hCollecting joblib>=1.2.0 (from scikit-learn)\n",
+ " Using cached joblib-1.3.2-py3-none-any.whl.metadata (5.4 kB)\n",
+ "Collecting threadpoolctl>=2.0.0 (from scikit-learn)\n",
+ " Using cached threadpoolctl-3.4.0-py3-none-any.whl.metadata (13 kB)\n",
+ "Using cached scikit_learn-1.4.1.post1-cp312-cp312-macosx_12_0_arm64.whl (10.5 MB)\n",
+ "Using cached joblib-1.3.2-py3-none-any.whl (302 kB)\n",
+ "Downloading scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl (30.4 MB)\n",
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m30.4/30.4 MB\u001b[0m \u001b[31m21.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n",
+ "\u001b[?25hUsing cached threadpoolctl-3.4.0-py3-none-any.whl (17 kB)\n",
+ "Installing collected packages: threadpoolctl, scipy, joblib, scikit-learn\n",
+ "Successfully installed joblib-1.3.2 scikit-learn-1.4.1.post1 scipy-1.13.0 threadpoolctl-3.4.0\n"
+ ]
+ }
+ ],
+ "source": [
+ "!pip install pandas\n",
+ "!pip install matplotlib\n",
+ "!pip install seaborn\n",
+ "!pip install scikit-learn"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import pandas as pd\n",
+ "import matplotlib\n",
+ "import csv\n",
+ "import glob\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "/Users/svanka/Codes/Diff-MST/outputs/ablation/AF/by-my-side.csv\n",
+ "20\n",
+ "/Users/svanka/Codes/Diff-MST/outputs/ablation/AF/ecstasy.csv\n",
+ "20\n",
+ "/Users/svanka/Codes/Diff-MST/outputs/ablation/AF/haunted-aged.csv\n",
+ "20\n",
+ "60\n"
+ ]
+ }
+ ],
+ "source": [
+ "csv_path = \"/Users/svanka/Codes/Diff-MST/outputs/ablation/AF\"\n",
+ "#append all the csv to a single dataframe and add a column with the name of the file\n",
+ "\n",
+ "df_list = []\n",
+ "\n",
+ "for files in glob.glob(csv_path + \"/*.csv\"):\n",
+ " print(files)\n",
+ " csv_df = pd.read_csv(files)\n",
+ " #add a new clumn with the name of the file and append it to the dataframe\n",
+ " csv_df['file_name'] = os.path.basename(files).replace('.csv','')\n",
+ " print(len(csv_df))\n",
+ " df_list.append(csv_df)\n",
+ "df = pd.concat(df_list, ignore_index=True)\n",
+ "print(len(df))\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " method | \n",
+ " audio_type | \n",
+ " ablation | \n",
+ " start_idx | \n",
+ " stop_idx | \n",
+ " rms | \n",
+ " crest_factor | \n",
+ " stereo_width | \n",
+ " stereo_imbalance | \n",
+ " barkspectrum | \n",
+ " net_AF_loss | \n",
+ " file_name | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " high_pass | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.004074 | \n",
+ " 0.021787 | \n",
+ " [0.3353039] | \n",
+ " [-0.17681053] | \n",
+ " 0.653527 | \n",
+ " 0.122524 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " high_pass | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.007692 | \n",
+ " 0.016933 | \n",
+ " [0.18699424] | \n",
+ " [-0.28859037] | \n",
+ " 0.698815 | \n",
+ " 0.122524 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 2 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " high_pass | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.006213 | \n",
+ " 0.020306 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.543323 | \n",
+ " 31.851294 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 3 | \n",
+ " sum | \n",
+ " ref | \n",
+ " high_pass | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.007692 | \n",
+ " 0.016933 | \n",
+ " [0.18699424] | \n",
+ " [-0.28859037] | \n",
+ " 0.698815 | \n",
+ " 31.851294 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 4 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " low_pass | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.006155 | \n",
+ " 0.015004 | \n",
+ " [0.22357161] | \n",
+ " [0.13522747] | \n",
+ " 0.654838 | \n",
+ " 0.117024 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 5 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " low_pass | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.011253 | \n",
+ " 0.013086 | \n",
+ " [0.2034282] | \n",
+ " [0.03135305] | \n",
+ " 0.751761 | \n",
+ " 0.117024 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 6 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " low_pass | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.006213 | \n",
+ " 0.020306 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.543323 | \n",
+ " 32.614502 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 7 | \n",
+ " sum | \n",
+ " ref | \n",
+ " low_pass | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.011253 | \n",
+ " 0.013086 | \n",
+ " [0.2034282] | \n",
+ " [0.03135305] | \n",
+ " 0.751761 | \n",
+ " 32.614502 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 8 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " band_pass | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.007041 | \n",
+ " 0.013663 | \n",
+ " [0.15272886] | \n",
+ " [-0.10040125] | \n",
+ " 0.531103 | \n",
+ " 0.946850 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 9 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " band_pass | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.016523 | \n",
+ " 0.006827 | \n",
+ " [0.02459108] | \n",
+ " [-0.16006266] | \n",
+ " 0.269596 | \n",
+ " 0.946850 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 10 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " band_pass | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.006213 | \n",
+ " 0.020306 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.543323 | \n",
+ " 22.868963 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 11 | \n",
+ " sum | \n",
+ " ref | \n",
+ " band_pass | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.016523 | \n",
+ " 0.006827 | \n",
+ " [0.02459108] | \n",
+ " [-0.16006266] | \n",
+ " 0.269596 | \n",
+ " 22.868963 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 12 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " pan_left | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.005618 | \n",
+ " 0.014758 | \n",
+ " [0.30145487] | \n",
+ " [-0.8022489] | \n",
+ " 0.679761 | \n",
+ " 0.446519 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 13 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " pan_left | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.008366 | \n",
+ " 0.013010 | \n",
+ " [0.7625234] | \n",
+ " [-0.97916067] | \n",
+ " 0.806224 | \n",
+ " 0.446519 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 14 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " pan_left | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.006213 | \n",
+ " 0.020306 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.543323 | \n",
+ " 36.472347 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 15 | \n",
+ " sum | \n",
+ " ref | \n",
+ " pan_left | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.008366 | \n",
+ " 0.013010 | \n",
+ " [0.7625234] | \n",
+ " [-0.97916067] | \n",
+ " 0.806224 | \n",
+ " 36.472347 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 16 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " pan_right | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.005376 | \n",
+ " 0.015142 | \n",
+ " [0.38029966] | \n",
+ " [0.79385364] | \n",
+ " 0.685591 | \n",
+ " 0.332059 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 17 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " pan_right | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.008894 | \n",
+ " 0.013010 | \n",
+ " [0.7729033] | \n",
+ " [0.9811843] | \n",
+ " 0.794389 | \n",
+ " 0.332059 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 18 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " pan_right | \n",
+ " 5022137 | \n",
+ " 5463137 | \n",
+ " 0.006213 | \n",
+ " 0.020306 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.543323 | \n",
+ " 36.097370 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 19 | \n",
+ " sum | \n",
+ " ref | \n",
+ " pan_right | \n",
+ " 3223724 | \n",
+ " 3664724 | \n",
+ " 0.008894 | \n",
+ " 0.013010 | \n",
+ " [0.7729033] | \n",
+ " [0.9811843] | \n",
+ " 0.794389 | \n",
+ " 36.097370 | \n",
+ " by-my-side | \n",
+ "
\n",
+ " \n",
+ " 20 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " low_pass | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.006615 | \n",
+ " 0.012723 | \n",
+ " [0.13142872] | \n",
+ " [0.01397015] | \n",
+ " 0.666661 | \n",
+ " 0.064587 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 21 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " low_pass | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.012675 | \n",
+ " 0.009881 | \n",
+ " [0.18987568] | \n",
+ " [0.08012921] | \n",
+ " 0.714403 | \n",
+ " 0.064587 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 22 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " low_pass | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005921 | \n",
+ " 0.016681 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.523093 | \n",
+ " 31.953974 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 23 | \n",
+ " sum | \n",
+ " ref | \n",
+ " low_pass | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.012675 | \n",
+ " 0.009881 | \n",
+ " [0.18987568] | \n",
+ " [0.08012921] | \n",
+ " 0.714403 | \n",
+ " 31.953974 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 24 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " pan_right | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005277 | \n",
+ " 0.012306 | \n",
+ " [1.0680251] | \n",
+ " [0.87477577] | \n",
+ " 0.709141 | \n",
+ " 0.147120 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 25 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " pan_right | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.009609 | \n",
+ " 0.010431 | \n",
+ " [0.77672297] | \n",
+ " [0.9830406] | \n",
+ " 0.761654 | \n",
+ " 0.147120 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 26 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " pan_right | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005921 | \n",
+ " 0.016681 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.523093 | \n",
+ " 35.442448 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 27 | \n",
+ " sum | \n",
+ " ref | \n",
+ " pan_right | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.009609 | \n",
+ " 0.010431 | \n",
+ " [0.77672297] | \n",
+ " [0.9830406] | \n",
+ " 0.761654 | \n",
+ " 35.442448 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 28 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " band_pass | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.007070 | \n",
+ " 0.009780 | \n",
+ " [0.09989402] | \n",
+ " [-0.23715419] | \n",
+ " 0.580188 | \n",
+ " 0.801008 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 29 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " band_pass | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.011060 | \n",
+ " 0.008109 | \n",
+ " [0.54091203] | \n",
+ " [0.0680251] | \n",
+ " 0.389929 | \n",
+ " 0.801008 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 30 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " band_pass | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005921 | \n",
+ " 0.016681 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.523093 | \n",
+ " 25.474741 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 31 | \n",
+ " sum | \n",
+ " ref | \n",
+ " band_pass | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.011060 | \n",
+ " 0.008109 | \n",
+ " [0.54091203] | \n",
+ " [0.0680251] | \n",
+ " 0.389929 | \n",
+ " 25.474741 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 32 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " pan_left | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005214 | \n",
+ " 0.014098 | \n",
+ " [0.7043585] | \n",
+ " [-0.97385424] | \n",
+ " 0.681030 | \n",
+ " 0.145156 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 33 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " pan_left | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.009433 | \n",
+ " 0.010431 | \n",
+ " [0.7444616] | \n",
+ " [-0.97688454] | \n",
+ " 0.773411 | \n",
+ " 0.145156 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 34 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " pan_left | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005921 | \n",
+ " 0.016681 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.523093 | \n",
+ " 35.710136 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 35 | \n",
+ " sum | \n",
+ " ref | \n",
+ " pan_left | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.009433 | \n",
+ " 0.010431 | \n",
+ " [0.7444616] | \n",
+ " [-0.97688454] | \n",
+ " 0.773411 | \n",
+ " 35.710136 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 36 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " high_pass | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.003912 | \n",
+ " 0.023446 | \n",
+ " [0.64094937] | \n",
+ " [0.39073116] | \n",
+ " 0.639729 | \n",
+ " 0.519041 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 37 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " high_pass | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.006252 | \n",
+ " 0.020270 | \n",
+ " [0.10621171] | \n",
+ " [-0.03977505] | \n",
+ " 0.641428 | \n",
+ " 0.519041 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 38 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " high_pass | \n",
+ " 5376628 | \n",
+ " 5817628 | \n",
+ " 0.005921 | \n",
+ " 0.016681 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.523093 | \n",
+ " 30.196657 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 39 | \n",
+ " sum | \n",
+ " ref | \n",
+ " high_pass | \n",
+ " 1340076 | \n",
+ " 1781076 | \n",
+ " 0.006252 | \n",
+ " 0.020270 | \n",
+ " [0.10621171] | \n",
+ " [-0.03977505] | \n",
+ " 0.641428 | \n",
+ " 30.196657 | \n",
+ " ecstasy | \n",
+ "
\n",
+ " \n",
+ " 40 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " high_pass | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.003969 | \n",
+ " 0.018614 | \n",
+ " [0.53428715] | \n",
+ " [-0.13642734] | \n",
+ " 0.665139 | \n",
+ " 0.095481 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 41 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " high_pass | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.008070 | \n",
+ " 0.020721 | \n",
+ " [0.3847169] | \n",
+ " [0.00603298] | \n",
+ " 0.708236 | \n",
+ " 0.095481 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 42 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " high_pass | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.005992 | \n",
+ " 0.016583 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.519098 | \n",
+ " 32.275440 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 43 | \n",
+ " sum | \n",
+ " ref | \n",
+ " high_pass | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.008070 | \n",
+ " 0.020721 | \n",
+ " [0.3847169] | \n",
+ " [0.00603298] | \n",
+ " 0.708236 | \n",
+ " 32.275440 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 44 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " low_pass | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.006412 | \n",
+ " 0.015502 | \n",
+ " [0.02812384] | \n",
+ " [-0.00424067] | \n",
+ " 0.675351 | \n",
+ " 0.180971 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 45 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " low_pass | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.012228 | \n",
+ " 0.010140 | \n",
+ " [0.16704528] | \n",
+ " [-0.00012205] | \n",
+ " 0.781249 | \n",
+ " 0.180971 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 46 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " low_pass | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.005992 | \n",
+ " 0.016583 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.519098 | \n",
+ " 33.682160 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 47 | \n",
+ " sum | \n",
+ " ref | \n",
+ " low_pass | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.012228 | \n",
+ " 0.010140 | \n",
+ " [0.16704528] | \n",
+ " [-0.00012205] | \n",
+ " 0.781249 | \n",
+ " 33.682160 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 48 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " band_pass | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.006382 | \n",
+ " 0.013647 | \n",
+ " [0.34050697] | \n",
+ " [-0.26903212] | \n",
+ " 0.628616 | \n",
+ " 1.383198 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 49 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " band_pass | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.009437 | \n",
+ " 0.008510 | \n",
+ " [0.02690221] | \n",
+ " [-0.0218279] | \n",
+ " 0.304304 | \n",
+ " 1.383198 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 50 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " band_pass | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.005992 | \n",
+ " 0.016583 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.519098 | \n",
+ " 23.336586 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 51 | \n",
+ " sum | \n",
+ " ref | \n",
+ " band_pass | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.009437 | \n",
+ " 0.008510 | \n",
+ " [0.02690221] | \n",
+ " [-0.0218279] | \n",
+ " 0.304304 | \n",
+ " 23.336586 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 52 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " pan_right | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.004372 | \n",
+ " 0.016404 | \n",
+ " [0.5658581] | \n",
+ " [0.931149] | \n",
+ " 0.756382 | \n",
+ " 0.147527 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 53 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " pan_right | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.009281 | \n",
+ " 0.009984 | \n",
+ " [0.7547042] | \n",
+ " [0.9802046] | \n",
+ " 0.831521 | \n",
+ " 0.147527 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 54 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " pan_right | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.005992 | \n",
+ " 0.016583 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.519098 | \n",
+ " 37.195953 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 55 | \n",
+ " sum | \n",
+ " ref | \n",
+ " pan_right | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.009281 | \n",
+ " 0.009984 | \n",
+ " [0.7547042] | \n",
+ " [0.9802046] | \n",
+ " 0.831521 | \n",
+ " 37.195953 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 56 | \n",
+ " diffmst-16 | \n",
+ " pred_mix | \n",
+ " pan_left | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.005333 | \n",
+ " 0.016097 | \n",
+ " [0.22387566] | \n",
+ " [-0.75253445] | \n",
+ " 0.722902 | \n",
+ " 0.521889 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 57 | \n",
+ " diffmst-16 | \n",
+ " ref | \n",
+ " pan_left | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.009256 | \n",
+ " 0.009984 | \n",
+ " [0.75463414] | \n",
+ " [-0.98019147] | \n",
+ " 0.830292 | \n",
+ " 0.521889 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 58 | \n",
+ " sum | \n",
+ " pred_mix | \n",
+ " pan_left | \n",
+ " 4989668 | \n",
+ " 5430668 | \n",
+ " 0.005992 | \n",
+ " 0.016583 | \n",
+ " [0.] | \n",
+ " [0.] | \n",
+ " -0.519098 | \n",
+ " 37.161100 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ " 59 | \n",
+ " sum | \n",
+ " ref | \n",
+ " pan_left | \n",
+ " 6420140 | \n",
+ " 6861140 | \n",
+ " 0.009256 | \n",
+ " 0.009984 | \n",
+ " [0.75463414] | \n",
+ " [-0.98019147] | \n",
+ " 0.830292 | \n",
+ " 37.161100 | \n",
+ " haunted-aged | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " method audio_type ablation start_idx stop_idx rms \\\n",
+ "0 diffmst-16 pred_mix high_pass 5022137 5463137 0.004074 \n",
+ "1 diffmst-16 ref high_pass 3223724 3664724 0.007692 \n",
+ "2 sum pred_mix high_pass 5022137 5463137 0.006213 \n",
+ "3 sum ref high_pass 3223724 3664724 0.007692 \n",
+ "4 diffmst-16 pred_mix low_pass 5022137 5463137 0.006155 \n",
+ "5 diffmst-16 ref low_pass 3223724 3664724 0.011253 \n",
+ "6 sum pred_mix low_pass 5022137 5463137 0.006213 \n",
+ "7 sum ref low_pass 3223724 3664724 0.011253 \n",
+ "8 diffmst-16 pred_mix band_pass 5022137 5463137 0.007041 \n",
+ "9 diffmst-16 ref band_pass 3223724 3664724 0.016523 \n",
+ "10 sum pred_mix band_pass 5022137 5463137 0.006213 \n",
+ "11 sum ref band_pass 3223724 3664724 0.016523 \n",
+ "12 diffmst-16 pred_mix pan_left 5022137 5463137 0.005618 \n",
+ "13 diffmst-16 ref pan_left 3223724 3664724 0.008366 \n",
+ "14 sum pred_mix pan_left 5022137 5463137 0.006213 \n",
+ "15 sum ref pan_left 3223724 3664724 0.008366 \n",
+ "16 diffmst-16 pred_mix pan_right 5022137 5463137 0.005376 \n",
+ "17 diffmst-16 ref pan_right 3223724 3664724 0.008894 \n",
+ "18 sum pred_mix pan_right 5022137 5463137 0.006213 \n",
+ "19 sum ref pan_right 3223724 3664724 0.008894 \n",
+ "20 diffmst-16 pred_mix low_pass 5376628 5817628 0.006615 \n",
+ "21 diffmst-16 ref low_pass 1340076 1781076 0.012675 \n",
+ "22 sum pred_mix low_pass 5376628 5817628 0.005921 \n",
+ "23 sum ref low_pass 1340076 1781076 0.012675 \n",
+ "24 diffmst-16 pred_mix pan_right 5376628 5817628 0.005277 \n",
+ "25 diffmst-16 ref pan_right 1340076 1781076 0.009609 \n",
+ "26 sum pred_mix pan_right 5376628 5817628 0.005921 \n",
+ "27 sum ref pan_right 1340076 1781076 0.009609 \n",
+ "28 diffmst-16 pred_mix band_pass 5376628 5817628 0.007070 \n",
+ "29 diffmst-16 ref band_pass 1340076 1781076 0.011060 \n",
+ "30 sum pred_mix band_pass 5376628 5817628 0.005921 \n",
+ "31 sum ref band_pass 1340076 1781076 0.011060 \n",
+ "32 diffmst-16 pred_mix pan_left 5376628 5817628 0.005214 \n",
+ "33 diffmst-16 ref pan_left 1340076 1781076 0.009433 \n",
+ "34 sum pred_mix pan_left 5376628 5817628 0.005921 \n",
+ "35 sum ref pan_left 1340076 1781076 0.009433 \n",
+ "36 diffmst-16 pred_mix high_pass 5376628 5817628 0.003912 \n",
+ "37 diffmst-16 ref high_pass 1340076 1781076 0.006252 \n",
+ "38 sum pred_mix high_pass 5376628 5817628 0.005921 \n",
+ "39 sum ref high_pass 1340076 1781076 0.006252 \n",
+ "40 diffmst-16 pred_mix high_pass 4989668 5430668 0.003969 \n",
+ "41 diffmst-16 ref high_pass 6420140 6861140 0.008070 \n",
+ "42 sum pred_mix high_pass 4989668 5430668 0.005992 \n",
+ "43 sum ref high_pass 6420140 6861140 0.008070 \n",
+ "44 diffmst-16 pred_mix low_pass 4989668 5430668 0.006412 \n",
+ "45 diffmst-16 ref low_pass 6420140 6861140 0.012228 \n",
+ "46 sum pred_mix low_pass 4989668 5430668 0.005992 \n",
+ "47 sum ref low_pass 6420140 6861140 0.012228 \n",
+ "48 diffmst-16 pred_mix band_pass 4989668 5430668 0.006382 \n",
+ "49 diffmst-16 ref band_pass 6420140 6861140 0.009437 \n",
+ "50 sum pred_mix band_pass 4989668 5430668 0.005992 \n",
+ "51 sum ref band_pass 6420140 6861140 0.009437 \n",
+ "52 diffmst-16 pred_mix pan_right 4989668 5430668 0.004372 \n",
+ "53 diffmst-16 ref pan_right 6420140 6861140 0.009281 \n",
+ "54 sum pred_mix pan_right 4989668 5430668 0.005992 \n",
+ "55 sum ref pan_right 6420140 6861140 0.009281 \n",
+ "56 diffmst-16 pred_mix pan_left 4989668 5430668 0.005333 \n",
+ "57 diffmst-16 ref pan_left 6420140 6861140 0.009256 \n",
+ "58 sum pred_mix pan_left 4989668 5430668 0.005992 \n",
+ "59 sum ref pan_left 6420140 6861140 0.009256 \n",
+ "\n",
+ " crest_factor stereo_width stereo_imbalance barkspectrum net_AF_loss \\\n",
+ "0 0.021787 [0.3353039] [-0.17681053] 0.653527 0.122524 \n",
+ "1 0.016933 [0.18699424] [-0.28859037] 0.698815 0.122524 \n",
+ "2 0.020306 [0.] [0.] -0.543323 31.851294 \n",
+ "3 0.016933 [0.18699424] [-0.28859037] 0.698815 31.851294 \n",
+ "4 0.015004 [0.22357161] [0.13522747] 0.654838 0.117024 \n",
+ "5 0.013086 [0.2034282] [0.03135305] 0.751761 0.117024 \n",
+ "6 0.020306 [0.] [0.] -0.543323 32.614502 \n",
+ "7 0.013086 [0.2034282] [0.03135305] 0.751761 32.614502 \n",
+ "8 0.013663 [0.15272886] [-0.10040125] 0.531103 0.946850 \n",
+ "9 0.006827 [0.02459108] [-0.16006266] 0.269596 0.946850 \n",
+ "10 0.020306 [0.] [0.] -0.543323 22.868963 \n",
+ "11 0.006827 [0.02459108] [-0.16006266] 0.269596 22.868963 \n",
+ "12 0.014758 [0.30145487] [-0.8022489] 0.679761 0.446519 \n",
+ "13 0.013010 [0.7625234] [-0.97916067] 0.806224 0.446519 \n",
+ "14 0.020306 [0.] [0.] -0.543323 36.472347 \n",
+ "15 0.013010 [0.7625234] [-0.97916067] 0.806224 36.472347 \n",
+ "16 0.015142 [0.38029966] [0.79385364] 0.685591 0.332059 \n",
+ "17 0.013010 [0.7729033] [0.9811843] 0.794389 0.332059 \n",
+ "18 0.020306 [0.] [0.] -0.543323 36.097370 \n",
+ "19 0.013010 [0.7729033] [0.9811843] 0.794389 36.097370 \n",
+ "20 0.012723 [0.13142872] [0.01397015] 0.666661 0.064587 \n",
+ "21 0.009881 [0.18987568] [0.08012921] 0.714403 0.064587 \n",
+ "22 0.016681 [0.] [0.] -0.523093 31.953974 \n",
+ "23 0.009881 [0.18987568] [0.08012921] 0.714403 31.953974 \n",
+ "24 0.012306 [1.0680251] [0.87477577] 0.709141 0.147120 \n",
+ "25 0.010431 [0.77672297] [0.9830406] 0.761654 0.147120 \n",
+ "26 0.016681 [0.] [0.] -0.523093 35.442448 \n",
+ "27 0.010431 [0.77672297] [0.9830406] 0.761654 35.442448 \n",
+ "28 0.009780 [0.09989402] [-0.23715419] 0.580188 0.801008 \n",
+ "29 0.008109 [0.54091203] [0.0680251] 0.389929 0.801008 \n",
+ "30 0.016681 [0.] [0.] -0.523093 25.474741 \n",
+ "31 0.008109 [0.54091203] [0.0680251] 0.389929 25.474741 \n",
+ "32 0.014098 [0.7043585] [-0.97385424] 0.681030 0.145156 \n",
+ "33 0.010431 [0.7444616] [-0.97688454] 0.773411 0.145156 \n",
+ "34 0.016681 [0.] [0.] -0.523093 35.710136 \n",
+ "35 0.010431 [0.7444616] [-0.97688454] 0.773411 35.710136 \n",
+ "36 0.023446 [0.64094937] [0.39073116] 0.639729 0.519041 \n",
+ "37 0.020270 [0.10621171] [-0.03977505] 0.641428 0.519041 \n",
+ "38 0.016681 [0.] [0.] -0.523093 30.196657 \n",
+ "39 0.020270 [0.10621171] [-0.03977505] 0.641428 30.196657 \n",
+ "40 0.018614 [0.53428715] [-0.13642734] 0.665139 0.095481 \n",
+ "41 0.020721 [0.3847169] [0.00603298] 0.708236 0.095481 \n",
+ "42 0.016583 [0.] [0.] -0.519098 32.275440 \n",
+ "43 0.020721 [0.3847169] [0.00603298] 0.708236 32.275440 \n",
+ "44 0.015502 [0.02812384] [-0.00424067] 0.675351 0.180971 \n",
+ "45 0.010140 [0.16704528] [-0.00012205] 0.781249 0.180971 \n",
+ "46 0.016583 [0.] [0.] -0.519098 33.682160 \n",
+ "47 0.010140 [0.16704528] [-0.00012205] 0.781249 33.682160 \n",
+ "48 0.013647 [0.34050697] [-0.26903212] 0.628616 1.383198 \n",
+ "49 0.008510 [0.02690221] [-0.0218279] 0.304304 1.383198 \n",
+ "50 0.016583 [0.] [0.] -0.519098 23.336586 \n",
+ "51 0.008510 [0.02690221] [-0.0218279] 0.304304 23.336586 \n",
+ "52 0.016404 [0.5658581] [0.931149] 0.756382 0.147527 \n",
+ "53 0.009984 [0.7547042] [0.9802046] 0.831521 0.147527 \n",
+ "54 0.016583 [0.] [0.] -0.519098 37.195953 \n",
+ "55 0.009984 [0.7547042] [0.9802046] 0.831521 37.195953 \n",
+ "56 0.016097 [0.22387566] [-0.75253445] 0.722902 0.521889 \n",
+ "57 0.009984 [0.75463414] [-0.98019147] 0.830292 0.521889 \n",
+ "58 0.016583 [0.] [0.] -0.519098 37.161100 \n",
+ "59 0.009984 [0.75463414] [-0.98019147] 0.830292 37.161100 \n",
+ "\n",
+ " file_name \n",
+ "0 by-my-side \n",
+ "1 by-my-side \n",
+ "2 by-my-side \n",
+ "3 by-my-side \n",
+ "4 by-my-side \n",
+ "5 by-my-side \n",
+ "6 by-my-side \n",
+ "7 by-my-side \n",
+ "8 by-my-side \n",
+ "9 by-my-side \n",
+ "10 by-my-side \n",
+ "11 by-my-side \n",
+ "12 by-my-side \n",
+ "13 by-my-side \n",
+ "14 by-my-side \n",
+ "15 by-my-side \n",
+ "16 by-my-side \n",
+ "17 by-my-side \n",
+ "18 by-my-side \n",
+ "19 by-my-side \n",
+ "20 ecstasy \n",
+ "21 ecstasy \n",
+ "22 ecstasy \n",
+ "23 ecstasy \n",
+ "24 ecstasy \n",
+ "25 ecstasy \n",
+ "26 ecstasy \n",
+ "27 ecstasy \n",
+ "28 ecstasy \n",
+ "29 ecstasy \n",
+ "30 ecstasy \n",
+ "31 ecstasy \n",
+ "32 ecstasy \n",
+ "33 ecstasy \n",
+ "34 ecstasy \n",
+ "35 ecstasy \n",
+ "36 ecstasy \n",
+ "37 ecstasy \n",
+ "38 ecstasy \n",
+ "39 ecstasy \n",
+ "40 haunted-aged \n",
+ "41 haunted-aged \n",
+ "42 haunted-aged \n",
+ "43 haunted-aged \n",
+ "44 haunted-aged \n",
+ "45 haunted-aged \n",
+ "46 haunted-aged \n",
+ "47 haunted-aged \n",
+ "48 haunted-aged \n",
+ "49 haunted-aged \n",
+ "50 haunted-aged \n",
+ "51 haunted-aged \n",
+ "52 haunted-aged \n",
+ "53 haunted-aged \n",
+ "54 haunted-aged \n",
+ "55 haunted-aged \n",
+ "56 haunted-aged \n",
+ "57 haunted-aged \n",
+ "58 haunted-aged \n",
+ "59 haunted-aged "
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "18\n",
+ " method audio_type ablation start_idx stop_idx rms \\\n",
+ "12 diffmst-16 pred_mix pan_left 5022137 5463137 0.005618 \n",
+ "13 diffmst-16 ref pan_left 3223724 3664724 0.008366 \n",
+ "14 sum pred_mix pan_left 5022137 5463137 0.006213 \n",
+ "16 diffmst-16 pred_mix pan_right 5022137 5463137 0.005376 \n",
+ "17 diffmst-16 ref pan_right 3223724 3664724 0.008894 \n",
+ "\n",
+ " crest_factor stereo_width stereo_imbalance barkspectrum net_AF_loss \\\n",
+ "12 0.014758 [0.30145487] [-0.8022489] 0.679761 0.446519 \n",
+ "13 0.013010 [0.7625234] [-0.97916067] 0.806224 0.446519 \n",
+ "14 0.020306 [0.] [0.] -0.543323 36.472347 \n",
+ "16 0.015142 [0.38029966] [0.79385364] 0.685591 0.332059 \n",
+ "17 0.013010 [0.7729033] [0.9811843] 0.794389 0.332059 \n",
+ "\n",
+ " file_name \n",
+ "12 by-my-side \n",
+ "13 by-my-side \n",
+ "14 by-my-side \n",
+ "16 by-my-side \n",
+ "17 by-my-side \n"
+ ]
+ }
+ ],
+ "source": [
+ "#create a dataframe with ablation = pan_left and ablation = pan_right\n",
+ "\n",
+ "df_pan = df[(df['ablation'] == 'pan_left') | (df['ablation'] == 'pan_right')]\n",
+ "#remove rows with method sum and audio_type =ref and not the ones with either sum and audio_type = ref\n",
+ "df_pan = df_pan[~((df_pan['method'] == 'sum') & (df_pan['audio_type'] != 'pred_mix'))]\n",
+ "print(len(df_pan))\n",
+ "print(df_pan.head())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " method audio_type ablation start_idx stop_idx rms \\\n",
+ "12 diffmst-16 pred_mix pan_left 5022137 5463137 0.005618 \n",
+ "13 diffmst-16 ref pan_left 3223724 3664724 0.008366 \n",
+ "14 sum pred_mix pan_left 5022137 5463137 0.006213 \n",
+ "16 diffmst-16 pred_mix pan_right 5022137 5463137 0.005376 \n",
+ "17 diffmst-16 ref pan_right 3223724 3664724 0.008894 \n",
+ "\n",
+ " crest_factor stereo_width stereo_imbalance barkspectrum net_AF_loss \\\n",
+ "12 0.014758 0.301455 -0.802249 0.679761 0.446519 \n",
+ "13 0.013010 0.762523 -0.979161 0.806224 0.446519 \n",
+ "14 0.020306 0.000000 0.000000 -0.543323 36.472347 \n",
+ "16 0.015142 0.380300 0.793854 0.685591 0.332059 \n",
+ "17 0.013010 0.772903 0.981184 0.794389 0.332059 \n",
+ "\n",
+ " file_name \n",
+ "12 by-my-side \n",
+ "13 by-my-side \n",
+ "14 by-my-side \n",
+ "16 by-my-side \n",
+ "17 by-my-side \n"
+ ]
+ }
+ ],
+ "source": [
+ "#the values for stereo imbalance and stereo width are list of size 1 saved as string. for ex, [2.34]. We need to convert them to float\n",
+ "df_pan['stereo_imbalance'] = df_pan['stereo_imbalance'].apply(lambda x: float(x[1:-1]))\n",
+ "df_pan['stereo_width'] = df_pan['stereo_width'].apply(lambda x: float(x[1:-1]))\n",
+ "print(df_pan.head())\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " method audio_type ablation start_idx stop_idx rms \\\n",
+ "12 diffmst-16 pred_mix pan_left 5022137 5463137 0.005618 \n",
+ "13 diffmst-16 ref pan_left 3223724 3664724 0.008366 \n",
+ "14 sum pred_mix pan_left 5022137 5463137 0.006213 \n",
+ "16 diffmst-16 pred_mix pan_right 5022137 5463137 0.005376 \n",
+ "17 diffmst-16 ref pan_right 3223724 3664724 0.008894 \n",
+ "\n",
+ " crest_factor stereo_width stereo_imbalance barkspectrum net_AF_loss \\\n",
+ "12 0.014758 0.301455 -0.802249 0.679761 0.446519 \n",
+ "13 0.013010 0.762523 -0.979161 0.806224 0.446519 \n",
+ "14 0.020306 0.000000 0.000000 -0.543323 36.472347 \n",
+ "16 0.015142 0.380300 0.793854 0.685591 0.332059 \n",
+ "17 0.013010 0.772903 0.981184 0.794389 0.332059 \n",
+ "\n",
+ " file_name audio_type_encoding method_encoding \n",
+ "12 by-my-side 0 0 \n",
+ "13 by-my-side 1 0 \n",
+ "14 by-my-side 0 1 \n",
+ "16 by-my-side 0 0 \n",
+ "17 by-my-side 1 0 \n"
+ ]
+ }
+ ],
+ "source": [
+ "#encode audio_type and method to numbers\n",
+ "from sklearn.preprocessing import LabelEncoder\n",
+ "le = LabelEncoder()\n",
+ "df_pan['audio_type_encoding'] = le.fit_transform(df_pan['audio_type'])\n",
+ "df_pan['method_encoding'] = le.fit_transform(df_pan['method'])\n",
+ "print(df_pan.head())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 89,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "#find the type of the element of first row in method_encoding\n",
+ "print(type(df_pan['audio_type'].iloc[0]))\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 123,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n",
+ "posx and posy should be finite values\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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DVu3buW1114jNjbWcoCQj48Pn3zyCatXr2bXrl1s376dzp07Z1tgaTAYuP6Ilqw2MzMzKVmyJGvWrLHEHhsbe18+NVFyLSIiImKnPN2NlC/pnm37vVupUNIdDzfrtuPbt28fp0+fxtfXl9WrV9OwYUOqVatGYmIiP/zwA2FhYcC14+vzomPHjrzyyivUr18fT09PPD09LQkxwC+//ML777/Pp59+yhNPPMGhQ4f4888/Lfdr1arF+PHjOXv2LMWKFeObb77B09OTsmXLcunSJfbs2UOdOnVYt24d//3vfy3TUO4lJdciIiIidsrJ0UDnp0ow4bNjdyzbuUUJnB0NVrVXoUIFRo8eTWxsLEFBQXTs2BGAp59+mlOnTlm940uVKlVwcXGxjID/W61atShbtiytW7fG1dWVxx57jEaNGvH7778D4O3tzejRo+nduzfu7u6UL18euDavf+bMmYSHh5Oamoq7uzvvvPOOVbHmlZJrERERETtlNDpQv2phnm3kc9vt+J5r7PO/nULyvldFUFAQS5YsyXbNbDaTlpbGzz//zODBg2/57PX7TAcFBREUFGT5evPmzZa6Tpw4gYuLC3Xq1LlpPQaDgdGjR99wfeDAgZa/P/300zz99NM3lKlRowbLly+/ZYz3inYLEREREbFjri5GXupQmtEvlqd8Sfds98qXdGf0i+WtPkDmVi5cuED9+vWpWLEiNWvWtKquiIgIevbsyahRozAYrBtht2cP5Mh1dHQ0bdu2JTQ0lJEjR+bq2djYWD744AN27NjBuXPn8Pb2JiQkhFdeeYUiRYrcpYhFRERE8s7VxUijGkUIrlKYyykmLidn4OnuiIebEWdHw107OMbHx4df/neEvbV69epFr169bFKXPXvgRq7//vtvXn755Wx7HubUX3/9RYcOHViyZAmurq40bdoUo9HIF198Qdu2bTl79uxdiFhERETEekajA64uRrwLOVOmhDvehZxxczHmyxMZH2QPVG8cPnyYrl27cvz48Tw9/+abb3LhwgUGDhzIunXrmDVrFt999x2dO3cmNjaWt956y8YRi4iIiMjD5IFIri9dusS0adPo1KkTJ0+exN/fP9d17Nmzh71791K2bFlefvlly3Wj0cioUaMoUaIE27Zt49ixO6/GFREREbnXMjIySE5O5ty5c0RFRXHu3DmSk5PJyMjdQTNy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jx5s3ryZv//+m5EjR3LmzBkcHR0ZMmQIjRo1Yvbs2cTGxvLXX39x+vRpGjduzFtvvXUfvyN5o5FrEREREWHfvn04ODiwbNkyNm7cSFpaGtu2bePixYs8//zzrF27ltKlS/P+++9ne27r1q1UrFiRpUuXsmHDBg4ePMjvv//OW2+9hY+PD3Pnzs1W/u2336ZOnTqsW7eOWbNmMWLECP7++2/g2tHzn376KWvXruX777+3HDv+INHItYiIiIhQp04dChUqxMKFCzlx4gTR0dEkJydTunRpateuDcBzzz3HsGHDsj3XunVrDhw4wOeff86JEyeIj48nOTmZQoUK3bSdn3/+mbfffhuAkiVLUq1aNX777TcAgoODcXZ2xtnZmdKlS3Pp0qW794LvEo1ci4iIiAibNm3itddew9XVlfbt21OnTh1KlCiBg0P2dNHRMfvY7IIFC5g8eTJFihTh+eefp1y5cpjN5lu28+97ZrOZjIwMAFxcXCzXDQbDbeuxV0quRURERISdO3fyzDPP0KFDB7y9vdmzZw8mk4no6GgiIyMBWLFiBfXr18/23E8//USXLl147rnnMBgM/PHHH2RmZuLo6GhJmq9Xr149li1bBsCpU6f49ddfqV69+l1/ffeKpoWIiIiICKGhobz++ut8++23ODs7U6NGDX7++WdKlSrFRx99RHR0NBUqVGDIkCHZnuvZsydjx45l3rx5uLu7U7NmTU6dOkXdunXx8/Oja9euTJ061VJ+5MiRvPXWW6xZswa4Nge7WLFi9/S13k1KrkVERESEgIAA1q1bl+PymzdvBsDf35/vvvvupmUWL158Q/lixYrx0Ucf3VB24MCB2b5esGBBjmOxJ5oWIiIiIiJiI0quRURERERsRMm1iIiIiIiNKLkWEREREbERLWgUEREReQCYTCbS0tJISkoiMTGRggULUqBAAZydnTEajfc7PPkfJdciIiIidi4lJYVt27Yxf/78bEeCBwQE0KNHDxo1aoSbm9t9jFCyaFqIiIiIiB1LSUlh5syZjBw5MltiDRAVFcXIkSOZOXMmKSkp9ynCnFm1atUNR6f/26FDh2jWrBldunRh8+bNzJs37x5FZztKrkVERETslMlkYtu2baxYseK25VasWMGPP/6IyWS6R5HdHZs3b6ZVq1YsXryY33//ncuXL9/vkHJN00JERERE7FRa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oaH069fvpuWHDRtGcHAwgwYNolOnTjg6OvLxxx9jNBpxcnLio48+4tKlS7Rr147FixfTrVs3m7yu24mIiODMmTMMGTIEgK5du1KsWDHCw8PvetsiIiIieWUwWzGZulKlStSqVYuFORhVfe655zh58iS//fZbXpt74Bw+fJjk5GTc3d3zfByryWQiNTWVkKZNiY2NzdEzA156iXnz5ukYcTtjMpnYv38/ANWrV8doNN7fgCRP1I/5h/oy/7B1X9ri97c8vHI8LSQ6Opo1a9bccP3s2bPMnDnzls+ZzWbOnDnDkSNHKFy4cN6ifMglJibSrHnzHE8NCQkJYd68eXc5KhERERH5txwn1yVLluTbb78lOjracs1gMHD27Fnmzp1722ezBseffPLJvEX5kEtKSqJv3745Sq5r1659z97EzJs3j1mzZt3y/rPPPsv48ePvSSwiIiIi9iDHybXRaGTkyJF89NFHlmt79uyhQIECVKpU6ZbPOTg44O7uzmOPPUbfvn2ti/YhlZSURJnSpXlz2DCmTJ58y3JFihRh9pw5FCtWjKioqLseV4cOHQgJCbnlfU9Pz7seg4iIiIg9yXFyDdCgQQMaNGhg+bpSpUpUrFiRBQsW2DwwyS7m9Gl69uxJsWLFmD59OjH/W7QI197ANG7ShEmTJlGkSJF7dophwYIFKViw4D1pS0RERORBkKvk+t8mTZqkbdHukYyMDFxcXGjdujWtWrXi8KFDHD16FFdXV+o/8QSurq4aKRYRERG5z6xKrtu1a2erOCSHnJ2dAahRsyY1rjveXERERETuvxwn1zt37rRJg8HBwTapR0RERETE3uQ4ue7duzcGg8GqxgwGA4cOHbKqDhERERERe5WraSE5OW/Gx8eHRx55hNTUVM6cOYPJZMJgMFCsWDHLlAYRERERkfwox8n1H3/8ccO1tLQ0XnzxRfbu3ctLL71Ely5dKFKkiOV+cnIyK1euZPr06fj4+PD555/bJGgREREREXtk1Z5tn332GXv27GHChAm88sor2RJrAHd3d7p3786UKVM4cOAAs2fPtipYERERERF7ZlVyvXr1anx8fGjbtu1tyz311FOUKFGC7777zprmRERERETsmlXJ9dmzZ/Hx8clR2cKFC3Px4kVrmhMRERERsWtWJddFixbl+PHjJCcn37ZcXFwcR48epXjx4tY0JyIi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bHb1fpn0S4mUG3Bt8aLRzTXbLiJeQbWp9NqrGN1cb7sLmK106NCBkJAQjh8/TkpKCm5ubpQrV85y39PT867HIA8+JdciIiJyTzk4OeFazIegeR9w/oef8G//rGVXkKxdRI5//Dnl+/fB6OZ6z+IqWLAgBQsWJDk52TLnunTp0vesfckflFyLiIjIPZeVYJds/9wNCbSjuzsVXn4RB2fn+xSdSN4puRYREZH7wsHJCW5xOqISa3lQabcQEREREREbUXItIiIiImIjSq5FRERERGxEybWIiIiIiI0ouRYRERERsREl1yIiIiIiNnJfk2tvb298fX3vZwgiIiIiIjZjs32uz5w5w5YtW/jzzz+5cuUKHh4elClThoYNG97ydKNZs2bZqnkRERERkfvO6uTaZDIxZcoUFi1ahMlkAsBsNmMwGAAwGAz85z//Yfjw4ThrQ3gRERERycesTq5ff/11vv32W8xmM8WKFeOxxx7D09OTxMREDh06xIULF1iyZAkJCQnMmDHDFjGLiIiIiNglq5Lr77//nm+++QYPDw/efvttWrVqle2+2Wzmq6++YsyYMXz77bc899xzNG3a1KqARURERETslVULGpctW4bBYCA8PPyGxBquTQl59tlnmTRpEmazmRUrVljTnIiIiIiIXbMquY6MjMTHx4cWLVrctlyLFi3w8fEhMjLSmuZEREREROyaVcl1UlISxYoVy1HZ4sWLExcXZ01zIiIiIiJ2zarkulChQpw6deqO5cxmM6dOneKRRx6xpjkREREREbtmVXJdo0YNEhISWLJkyW3LLV68mPj4eGrUqGFNcyIiIiIids2q5Lpr166YzWYmTJjAJ598wpUrV7Ldv3LlCh9//DHh4eEYDAa6du1qVbAiIiIiIvbMqq34goOD6datGwsXLmT69OnMnDmTMmXK4OnpyeXLl4mOjsZkMmE2m+natSvBwcG2iltERERExO5YfYjM6NGj8fPzY+7cuSQmJnLs2LFs9x955BH69evHCy+8YG1TIiIiIiJ2zerkGqBPnz48//zz/PLLL5w4cYLLly/j4eFB2bJlqVWrFq6urrZoRkRERETErtkkuQZwdnamfv361K9f31ZVioiIiIg8UGyWXF+6dImdO3dy4sQJkpKSePPNN7l69Sq//fYbdevWtVUzIiIiIiJ2y+rk2mw2M3v2bObNm0dq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8szuk2sPDw8SEhK4evXqTe+npqYC4O7unuu6v/32W4YNG0ZKSgpPPfUU77777l354WowGGxSr9Fo1A//fEJ9mT+oH/MP9WX+YYu+NBgMNopGHkZ2vxWfj48PABcuXLjp/ay51lnlcur9999n8ODBpKSk8PzzzzNz5kycnZ2tC1ZEREREHmp2n1xnTac4evToTe8fO3YsW7k7yczMZNiwYcyaNQsHBwdGjhzJ6NGjcdBhJSIiIiJiJbvPKJs0aQLAhg0bbrgXHx/Prl27cHFxITg4OEf1jRo1ii+//BI3Nzfef/99evToYctwRUREROQhZvfJdfPmzfHz82Pr1q0sWbLEcj01NZWRI0eSnJxMp06dKFKkiOVeeno6x48f5/jx46Snp1uur169mpUrV2I0Gvnwww9p2rTpPX0tIiIiIpK/2f2CRldXV6ZMmcKLL77ImDFjWLZsGf7+/uzbt4/z588TGBjIkCFDsj0TGxtrOVBm06ZN+Pv7YzKZeO+99wDw9vZm5cqVrFy58qZtNmzYkDZt2tzV1yUiIiIi+Y/dJ9cAderUYfny5cyZM4fdu3dz7Ngx/P396dSpE717977jyXpwbVuds2fPAteS73Xr1t2ybOHChZVci4iIiEiuPRDJNUDFihWZNWtWjsr6+/sTFRWV7dpjjz12wzUREREREVuy+znXIiIiIiIPCiXXIiIiIiI2ouRaRERERMRGlFyLiIiIiNiIkmsRERERERtRci0iIiIiYiNKrkVEREREbETJtYiIiIiIjSi5FhERERGxESXXIiIiIiI2ouRaRERERMRGlFyLiIiIiNiIkmsRERERERtRci0iIiIiYiNKrkVEREREbETJtYiIiIiIjSi5FhERERGxESXXIiIiIiI2ouRaRERERMRGlFyLiIiIiNiIkmsRERERERtRci0iIiIiYiNKrkVEREREbETJtYiIiIiIjSi5FhERERGxESXXIiIiIiI2ouRaRERERMRGlFyLiIiIiNiIkmsRERERERtRci0iIiIiYiNKrkVEREREbETJtYiIiIiIjSi5FhERERGxESXXIiIiIiI2ouRaRERERMRGlFyLiIiIiNiIkmsRERERERtRci0iIiIiYiNKrkVEREREbETJtYiIiIiIjSi5FhERERGxESXXIiIiIiI2ouRaRERERMRGlFyLiDxAKleuTEBAgM3r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Xj3atWtHYGAgLVu2LPc9X7p0iXXr1nHgwAESExPJysrCzs6OZs2a0aNHD4YNG0a1atW045OTk+natSv16tVj//79bNy4kbCwMM6dOweAoigMHjyYAQMGYGZmVuJ6N2/eJDQ0lJ07d5KYmEheXh6urq50796doKAgatSoUeKcqKgovv/+e44ePYpOp6NmzZq0adOGESNG4OfnV+57FUKIB8lMVVW1sishhKi4xMREhgwZwpUrV7C1tdUC6YSEBPLy8qhRowbff/89Xl5eDB06lKtXr5KQkICVlZUWhIWGhgJQUFDA1KlT2bZtGwB169alYcOGJCcno9PpsLCwYPr06bz00ksGddAH123btuXYsWMoisLVq1fx9fXliy++AGDdunXMmTOHwsJCbG1tcXNzQ6fTkZqaCkCfPn34/PPPsbCwMCh71qxZrF27FoDatWvj6upKeno6ly5dAqBnz57MmzcPKyure36Wdwuuvb29sba25tixY7i4uFCtWjXi4+MpLCzE1dWVuXPnEhwcTG5uLm5ubty8eZOLFy8C0Lt3b7788kvtOps3b2batGnUrl0bR0dHYmNjadSoEbVr1yY2Npb8/Hxq167NqlWr8PLy0s5TVZUpU6awfft2AJycnKhfv772HIuKirC0tGTJkiV07txZO2/q1Kn89NNP9O3bl/nz52vpx48fZ+zYsdy4cQMbGxsaN26MpaUlycnJZGZmAtC+fXtCQkK0dtEH13Xr1uU///kPW7ZsoWbNmri6upKUlMSNGzcAGDt2bIndSeLi4ggODiYxMREzMzPc3d0xNzfn/PnzFBQU4OHhQWhoKDVr1tTOmT9/PsuXLwegVq1auLi4cOnSJTIyMkq9jhBCPBRUIUSV9Prrr6uKoqiTJk1Sb968qaVnZGSoL774oqooijp69GgtfdOmTaqiKGqnTp1KlDV//nxVURT16aefVv/44w8tvaCgQA0JCVG9vLxUT09Pdf/+/QbnKYqiKoqitmjRQj18+LCqqqpaWFioXrt2TVVVVd23b5/q6empent7q2vWrFELCgq0cw8ePKj6+fmpiqKoCxYsMCj3u+++UxVFUb28vNS1a9eqhYWFWt4vv/yitmnTRlUURZ01a1bFH5wR/v7+qqIoalhYmJb2f//3f9r9+fj4qPv27TOou6enp6ooivrYY4+po0aNUi9duqSqqqoW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",
+ "text/plain": [
+ "