ser-standardizer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. ser_standardizer-0.1.0/LICENSE +21 -0
  2. ser_standardizer-0.1.0/PKG-INFO +166 -0
  3. ser_standardizer-0.1.0/README.md +116 -0
  4. ser_standardizer-0.1.0/pyproject.toml +51 -0
  5. ser_standardizer-0.1.0/setup.cfg +4 -0
  6. ser_standardizer-0.1.0/src/ser_standardizer/__init__.py +1 -0
  7. ser_standardizer-0.1.0/src/ser_standardizer/api.py +83 -0
  8. ser_standardizer-0.1.0/src/ser_standardizer/features/__init__.py +3 -0
  9. ser_standardizer-0.1.0/src/ser_standardizer/features/extractor.py +97 -0
  10. ser_standardizer-0.1.0/src/ser_standardizer/features/preprocessing.py +92 -0
  11. ser_standardizer-0.1.0/src/ser_standardizer/loaders/__init__.py +21 -0
  12. ser_standardizer-0.1.0/src/ser_standardizer/loaders/crema_d.py +125 -0
  13. ser_standardizer-0.1.0/src/ser_standardizer/loaders/emodb.py +121 -0
  14. ser_standardizer-0.1.0/src/ser_standardizer/loaders/emouerj.py +72 -0
  15. ser_standardizer-0.1.0/src/ser_standardizer/loaders/iemocap.py +140 -0
  16. ser_standardizer-0.1.0/src/ser_standardizer/loaders/msp_improv.py +147 -0
  17. ser_standardizer-0.1.0/src/ser_standardizer/loaders/msp_podcast.py +118 -0
  18. ser_standardizer-0.1.0/src/ser_standardizer/loaders/ravdess.py +111 -0
  19. ser_standardizer-0.1.0/src/ser_standardizer/main.py +40 -0
  20. ser_standardizer-0.1.0/src/ser_standardizer.egg-info/PKG-INFO +166 -0
  21. ser_standardizer-0.1.0/src/ser_standardizer.egg-info/SOURCES.txt +23 -0
  22. ser_standardizer-0.1.0/src/ser_standardizer.egg-info/dependency_links.txt +1 -0
  23. ser_standardizer-0.1.0/src/ser_standardizer.egg-info/entry_points.txt +2 -0
  24. ser_standardizer-0.1.0/src/ser_standardizer.egg-info/requires.txt +12 -0
  25. ser_standardizer-0.1.0/src/ser_standardizer.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vinicius Suaiden
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: ser-standardizer
3
+ Version: 0.1.0
4
+ Summary: A tool for standardizing Speech Emotion Recognition (SER) datasets and extracting acoustic features.
5
+ Author-email: Vinicius Suaiden <vinicius.suaiden@usp.br>, Miguel Arjona Ramirez <maramire@usp.br>, Wesley Beccaro <wesleybeccaro@usp.br>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Vinicius Suaiden
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ViniciusSuaiden/ser-standardizer
29
+ Project-URL: Repository, https://github.com/ViniciusSuaiden/ser-standardizer
30
+ Project-URL: Issues, https://github.com/ViniciusSuaiden/ser-standardizer/issues
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
35
+ Requires-Python: >=3.8
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: pandas>=1.3.0
39
+ Requires-Dist: audb>=1.0.0
40
+ Provides-Extra: features
41
+ Requires-Dist: numpy<3.0,>=1.21.0; extra == "features"
42
+ Requires-Dist: soundfile>=0.12.0; extra == "features"
43
+ Requires-Dist: scipy>=1.10.0; extra == "features"
44
+ Requires-Dist: ipython>=9.0.0; extra == "features"
45
+ Requires-Dist: ipywidgets>=8.1.8; extra == "features"
46
+ Requires-Dist: noisereduce>=3.0.3; extra == "features"
47
+ Requires-Dist: tqdm>=4.60.0; extra == "features"
48
+ Requires-Dist: opensmile>=2.4.0; extra == "features"
49
+ Dynamic: license-file
50
+
51
+ # SER-Standardizer
52
+
53
+ 🇺🇸 English | [🇧🇷 Português](README.pt-br.md)
54
+
55
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
56
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/downloads/)
57
+
58
+ **A modular infrastructure for dataset standardization and feature extraction in Speech Emotion Recognition (SER).**
59
+
60
+ **SER-Standardizer** is a Python package built to unify the formatting of multiple speech databases, mitigating metadata heterogeneity. Its main goal is to facilitate *cross-corpus training* and generalization testing of machine learning models.
61
+
62
+ Beyond the standardization pipeline, the tool provides an extension module for acoustic feature extraction (spectral and prosodic) by integrating the **openSMILE** framework.
63
+
64
+ ## 📋 Currently Supported Datasets
65
+
66
+ The tool currently supports loading and standardizing the following databases:
67
+
68
+ * **CREMA-D** | **IEMOCAP** | **EmoUERJ** (PT-BR) | **MSP-IMPROV** | **MSP-PODCAST** | **RAVDESS** | **EmoDB** (DE)
69
+
70
+ Per-dataset download and directory-layout guides are available under [`docs/en/`](docs/en/).
71
+
72
+ ## 🚀 Installation
73
+
74
+ ### Prerequisites
75
+ * Python 3.8 or higher
76
+ * Libraries listed in `pyproject.toml` (installed automatically).
77
+
78
+ ### Installation from source
79
+ Clone this repository to install it with `pip` (read more about it below):
80
+ ```
81
+ git clone https://github.com/ViniciusSuaiden/ser-standardizer.git
82
+ cd ser-standardizer
83
+ ```
84
+
85
+ The library was designed with flexibility in mind, offering two installation modes to optimize the use of computational resources:
86
+
87
+ #### 1. Base Installation (Lightweight Metadata)
88
+ Installs only the essential packages (`pandas`, `audb`) for tabular standardization and filtering.
89
+ ```bash
90
+ pip install .
91
+ ```
92
+ #### 2. Full Installation (With Signal Processing)
93
+ Installs the heavy dependencies required for the feature extraction and audio manipulation module (opensmile, noisereduce).
94
+ ```bash
95
+ pip install '.[features]'
96
+ ```
97
+ ### 💻 How to Use
98
+ The workflow is split into three logical steps:
99
+
100
+ **1. Preprocessing (CLI)**
101
+
102
+ After installation, the `ser-std` command becomes available in your terminal.
103
+ To standardize a specific dataset:
104
+ ```bash
105
+ # Example: crema_d
106
+ ser-std --dataset crema_d --input_dir /path/to/crema
107
+ ```
108
+ The standardized `.csv` file is written to the user's home folder, with a specific name for each database.
109
+
110
+ **2. Data Handling and Filtering (Python)**
111
+
112
+ After preprocessing, use the library to load, filter and manipulate the audio directly in your code or Jupyter Notebook.
113
+ ```python
114
+ import ser_standardizer as ser
115
+
116
+ # Loads multiple standardized datasets into a single DataFrame
117
+ df = ser.load_datasets(["crema_d", "ravdess", "iemocap"])
118
+
119
+ # Filters by dataset, emotion and gender
120
+ df_target = ser.filters(
121
+ df,
122
+ datasets=['ravdess', 'iemocap'],
123
+ emotions=['anger', 'sad'],
124
+ genders=['female']
125
+ )
126
+ ```
127
+ **3. Acoustic Feature Extraction (Requires [features])**
128
+
129
+ The extraction module automates digital signal processing. It includes the option to apply a Voice Activity Detection (VAD) mask based on RMS energy to extract features strictly from the effective phonation segments.
130
+ ```python
131
+ # Batch extraction via openSMILE
132
+ # Supported 'feature_set' values: 'eGeMAPS' or 'ComParE'
133
+ features_df = ser.extract_features(
134
+ df_target,
135
+ feature_set='eGeMAPS',
136
+ feature_level='functionals', # 'functionals' (1 vector per utterance) or 'llds' (per frame)
137
+ use_vad=False, # VAD (silence removal) is only allowed with feature_level='llds'
138
+ sr=16000 # Sampling rate
139
+ )
140
+
141
+ # The result preserves the original indices, making concatenation easy
142
+ import pandas as pd
143
+ final_dataset = pd.concat([df_target, features_df], axis=1)
144
+
145
+ # Group-wise normalization (z-score) — removes corpus/speaker offsets.
146
+ # Pass the grouping column (e.g., 'dataset' or 'speaker_id').
147
+ X_corpus = ser.normalize(features_df, df_target['dataset'])
148
+ X_speaker = ser.normalize(features_df, df_target['speaker_id'])
149
+
150
+ # --- Extra Utilities ---
151
+ # Listen to the audio in a Jupyter Notebook
152
+ ser.listen(df_target, index=42)
153
+
154
+ # Load the raw audio as a NumPy array (e.g., to feed neural networks)
155
+ audio_array = ser.load_audio(df_target, index=42)
156
+
157
+ # Load a batch of audio with zero-padding
158
+ batch = ser.load_batch(df_target, begin=0, end=32)
159
+ ```
160
+
161
+ ### ✍️ Authors
162
+ Vinicius Suaiden - USP - vinicius.suaiden@usp.br
163
+
164
+ Miguel Arjona Ramirez - USP - maramire@usp.br
165
+
166
+ Wesley Beccaro - USP - wesleybeccaro@usp.br
@@ -0,0 +1,116 @@
1
+ # SER-Standardizer
2
+
3
+ 🇺🇸 English | [🇧🇷 Português](README.pt-br.md)
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/downloads/)
7
+
8
+ **A modular infrastructure for dataset standardization and feature extraction in Speech Emotion Recognition (SER).**
9
+
10
+ **SER-Standardizer** is a Python package built to unify the formatting of multiple speech databases, mitigating metadata heterogeneity. Its main goal is to facilitate *cross-corpus training* and generalization testing of machine learning models.
11
+
12
+ Beyond the standardization pipeline, the tool provides an extension module for acoustic feature extraction (spectral and prosodic) by integrating the **openSMILE** framework.
13
+
14
+ ## 📋 Currently Supported Datasets
15
+
16
+ The tool currently supports loading and standardizing the following databases:
17
+
18
+ * **CREMA-D** | **IEMOCAP** | **EmoUERJ** (PT-BR) | **MSP-IMPROV** | **MSP-PODCAST** | **RAVDESS** | **EmoDB** (DE)
19
+
20
+ Per-dataset download and directory-layout guides are available under [`docs/en/`](docs/en/).
21
+
22
+ ## 🚀 Installation
23
+
24
+ ### Prerequisites
25
+ * Python 3.8 or higher
26
+ * Libraries listed in `pyproject.toml` (installed automatically).
27
+
28
+ ### Installation from source
29
+ Clone this repository to install it with `pip` (read more about it below):
30
+ ```
31
+ git clone https://github.com/ViniciusSuaiden/ser-standardizer.git
32
+ cd ser-standardizer
33
+ ```
34
+
35
+ The library was designed with flexibility in mind, offering two installation modes to optimize the use of computational resources:
36
+
37
+ #### 1. Base Installation (Lightweight Metadata)
38
+ Installs only the essential packages (`pandas`, `audb`) for tabular standardization and filtering.
39
+ ```bash
40
+ pip install .
41
+ ```
42
+ #### 2. Full Installation (With Signal Processing)
43
+ Installs the heavy dependencies required for the feature extraction and audio manipulation module (opensmile, noisereduce).
44
+ ```bash
45
+ pip install '.[features]'
46
+ ```
47
+ ### 💻 How to Use
48
+ The workflow is split into three logical steps:
49
+
50
+ **1. Preprocessing (CLI)**
51
+
52
+ After installation, the `ser-std` command becomes available in your terminal.
53
+ To standardize a specific dataset:
54
+ ```bash
55
+ # Example: crema_d
56
+ ser-std --dataset crema_d --input_dir /path/to/crema
57
+ ```
58
+ The standardized `.csv` file is written to the user's home folder, with a specific name for each database.
59
+
60
+ **2. Data Handling and Filtering (Python)**
61
+
62
+ After preprocessing, use the library to load, filter and manipulate the audio directly in your code or Jupyter Notebook.
63
+ ```python
64
+ import ser_standardizer as ser
65
+
66
+ # Loads multiple standardized datasets into a single DataFrame
67
+ df = ser.load_datasets(["crema_d", "ravdess", "iemocap"])
68
+
69
+ # Filters by dataset, emotion and gender
70
+ df_target = ser.filters(
71
+ df,
72
+ datasets=['ravdess', 'iemocap'],
73
+ emotions=['anger', 'sad'],
74
+ genders=['female']
75
+ )
76
+ ```
77
+ **3. Acoustic Feature Extraction (Requires [features])**
78
+
79
+ The extraction module automates digital signal processing. It includes the option to apply a Voice Activity Detection (VAD) mask based on RMS energy to extract features strictly from the effective phonation segments.
80
+ ```python
81
+ # Batch extraction via openSMILE
82
+ # Supported 'feature_set' values: 'eGeMAPS' or 'ComParE'
83
+ features_df = ser.extract_features(
84
+ df_target,
85
+ feature_set='eGeMAPS',
86
+ feature_level='functionals', # 'functionals' (1 vector per utterance) or 'llds' (per frame)
87
+ use_vad=False, # VAD (silence removal) is only allowed with feature_level='llds'
88
+ sr=16000 # Sampling rate
89
+ )
90
+
91
+ # The result preserves the original indices, making concatenation easy
92
+ import pandas as pd
93
+ final_dataset = pd.concat([df_target, features_df], axis=1)
94
+
95
+ # Group-wise normalization (z-score) — removes corpus/speaker offsets.
96
+ # Pass the grouping column (e.g., 'dataset' or 'speaker_id').
97
+ X_corpus = ser.normalize(features_df, df_target['dataset'])
98
+ X_speaker = ser.normalize(features_df, df_target['speaker_id'])
99
+
100
+ # --- Extra Utilities ---
101
+ # Listen to the audio in a Jupyter Notebook
102
+ ser.listen(df_target, index=42)
103
+
104
+ # Load the raw audio as a NumPy array (e.g., to feed neural networks)
105
+ audio_array = ser.load_audio(df_target, index=42)
106
+
107
+ # Load a batch of audio with zero-padding
108
+ batch = ser.load_batch(df_target, begin=0, end=32)
109
+ ```
110
+
111
+ ### ✍️ Authors
112
+ Vinicius Suaiden - USP - vinicius.suaiden@usp.br
113
+
114
+ Miguel Arjona Ramirez - USP - maramire@usp.br
115
+
116
+ Wesley Beccaro - USP - wesleybeccaro@usp.br
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ser-standardizer"
7
+ version = "0.1.0"
8
+ description = "A tool for standardizing Speech Emotion Recognition (SER) datasets and extracting acoustic features."
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Vinicius Suaiden", email = "vinicius.suaiden@usp.br" },
12
+ { name = "Miguel Arjona Ramirez", email = "maramire@usp.br" },
13
+ { name = "Wesley Beccaro", email = "wesleybeccaro@usp.br" },
14
+ ]
15
+ license = { file = "LICENSE" }
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ ]
22
+ dependencies = [
23
+ "pandas>=1.3.0",
24
+ "audb>=1.0.0"
25
+ ]
26
+ requires-python = ">=3.8"
27
+
28
+ [project.optional-dependencies]
29
+ features = [
30
+ "numpy>=1.21.0,<3.0",
31
+ "soundfile>=0.12.0",
32
+ "scipy>=1.10.0",
33
+ "ipython>=9.0.0",
34
+ "ipywidgets>=8.1.8",
35
+ "noisereduce>=3.0.3",
36
+ "tqdm>=4.60.0",
37
+ "opensmile>=2.4.0"
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://github.com/ViniciusSuaiden/ser-standardizer"
42
+ Repository = "https://github.com/ViniciusSuaiden/ser-standardizer"
43
+ Issues = "https://github.com/ViniciusSuaiden/ser-standardizer/issues"
44
+
45
+ [project.scripts]
46
+ ser-std = "ser_standardizer.main:main"
47
+
48
+ [tool.setuptools.packages.find]
49
+ where = ["src"]
50
+ include = ["ser_standardizer*"]
51
+ namespaces = false
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ from .api import load_datasets, load_audio, load_batch, listen, filters, normalize, extract_features
@@ -0,0 +1,83 @@
1
+ import pandas as pd
2
+ import os
3
+ from pathlib import Path
4
+
5
+ def load_datasets(datasets):
6
+ if isinstance(datasets, str):
7
+ target_datasets = [datasets]
8
+ else:
9
+ target_datasets = datasets
10
+
11
+ dataframes = []
12
+ DATA_DIR = os.path.join(Path.home(), ".ser_standardizer_data")
13
+
14
+ for name in target_datasets:
15
+ file_path = os.path.join(DATA_DIR, f"process_{name}.csv")
16
+ if not file_path or not os.path.exists(file_path):
17
+ raise FileNotFoundError(
18
+ f"O dataset '{name}' não foi encontrado. "
19
+ f"Certifique-se de ter rodado: ser-std --dataset {name} ..."
20
+ )
21
+ df = pd.read_csv(file_path)
22
+ dataframes.append(df)
23
+
24
+ if not dataframes:
25
+ raise ValueError("Nenhum dataset válido foi fornecido.")
26
+
27
+ return pd.concat(dataframes, ignore_index=True)
28
+
29
+ def filters(df, datasets=None, emotions=None, genders=None, languages=None, speakers=None):
30
+ mask = pd.Series([True] * len(df), index=df.index)
31
+
32
+ def to_list(val):
33
+ if isinstance(val, str):
34
+ return [val]
35
+ return val
36
+
37
+ if datasets:
38
+ target = [x.lower() for x in to_list(datasets)]
39
+ mask = mask & df['dataset'].str.lower().isin(target)
40
+
41
+ if emotions:
42
+ target = [x.lower() for x in to_list(emotions)]
43
+ mask = mask & df['emotion'].str.lower().isin(target)
44
+
45
+ if genders:
46
+ target = [x.lower() for x in to_list(genders)]
47
+ mask = mask & df['gender'].str.lower().isin(target)
48
+
49
+ if languages:
50
+ target = [x.lower() for x in to_list(languages)]
51
+ mask = mask & df['language'].str.lower().isin(target)
52
+
53
+ if speakers:
54
+ target = [x.lower() for x in to_list(speakers)]
55
+ mask = mask & df['speaker_id'].str.lower().isin(target)
56
+
57
+ print(f"Filtro aplicado. Restaram {len(df[mask])} amostras.")
58
+ return df[mask].copy()
59
+
60
+ def normalize(features, group, eps=1e-8):
61
+ """Aplica z-score por grupo em `features` (ex.: por dataset ou locutor)."""
62
+ g = group.reindex(features.index)
63
+ out = features.groupby(g).transform(
64
+ lambda c: (c - c.mean()) / (c.std(ddof=0) + eps)
65
+ )
66
+ return out.fillna(0.0)
67
+
68
+ try:
69
+ from .features.preprocessing import listen, load_audio, load_batch
70
+ from .features.extractor import extract_features
71
+ FEATURES_AVAILABLE = True
72
+
73
+ except ImportError:
74
+ FEATURES_AVAILABLE = False
75
+
76
+ def _missing_deps_error(*args, **kwargs):
77
+ raise ImportError(
78
+ "Esta função requer o submódulo de extração acústica.\n"
79
+ "Para utilizá-la, reinstale o pacote com as dependências completas:\n\n"
80
+ " pip install '.[features]'"
81
+ )
82
+
83
+ listen = load_audio = load_batch = extract_features = _missing_deps_error
@@ -0,0 +1,3 @@
1
+ # Módulo opcional (instale com `pip install "ser-standardizer[features]"`).
2
+ # Sem imports aqui: os submódulos são importados sob demanda em api.py,
3
+ # dentro de try/except ImportError.
@@ -0,0 +1,97 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from tqdm.auto import tqdm
4
+ import opensmile
5
+ from .preprocessing import load_audio, remove_silence
6
+
7
+ def extract_features(df, feature_set='eGeMAPS', feature_level='functionals', use_vad=False, sr=16000):
8
+ """
9
+ Extrai características acústicas utilizando o openSMILE.
10
+
11
+ Args:
12
+ df: DataFrame contendo a coluna 'file_path'.
13
+ feature_set: 'eGeMAPS' ou 'ComParE' (padrões validados na literatura).
14
+ feature_level: 'functionals' (estatísticas globais por arquivo) ou 'llds' (nível de frame).
15
+ use_vad: Se True, remove silêncios baseados na energia RMS.
16
+ ATENÇÃO: Permitido apenas quando feature_level='llds'.
17
+ sr: Taxa de amostragem esperada.
18
+
19
+ Returns:
20
+ Um novo pd.DataFrame contendo as features.
21
+ Para 'functionals', mantém o índice original.
22
+ Para 'llds', adiciona o índice original ('file_idx') para cruzamento de metadados.
23
+ """
24
+
25
+ if use_vad and feature_level.lower() == 'functionals':
26
+ raise ValueError(
27
+ "Erro de Metodologia: A aplicação de VAD no áudio bruto antes da extração "
28
+ "de funcionais corrompe as métricas temporais padronizadas do openSMILE "
29
+ "(como taxa de fala e proporção de pausas) e introduz artefatos no espectro. \n"
30
+ "-> Para extrair 'functionals' padronizados, defina use_vad=False. \n"
31
+ "-> O uso de use_vad=True é permitido apenas para extração no nível de frame ('llds')."
32
+ )
33
+
34
+ if feature_set.lower() == 'egemaps':
35
+ fset = opensmile.FeatureSet.eGeMAPSv02
36
+ elif feature_set.lower() == 'compare':
37
+ fset = opensmile.FeatureSet.ComParE_2016
38
+ else:
39
+ raise ValueError("Conjunto não suportado. Escolha 'eGeMAPS' ou 'ComParE'.")
40
+
41
+ # 2. Definir o Nível da Extração
42
+ if feature_level.lower() == 'functionals':
43
+ flevel = opensmile.FeatureLevel.Functionals
44
+ level_name = "Functionals"
45
+ elif feature_level.lower() in ['lld', 'llds']:
46
+ flevel = opensmile.FeatureLevel.LowLevelDescriptors
47
+ level_name = "LowLevelDescriptors"
48
+ else:
49
+ raise ValueError("Nível não suportado. Escolha 'functionals' ou 'llds'.")
50
+
51
+ print(f"Inicializando openSMILE ({feature_set} - {level_name})...")
52
+ smile = opensmile.Smile(
53
+ feature_set=fset,
54
+ feature_level=flevel,
55
+ )
56
+
57
+ features_list = []
58
+
59
+ for idx in tqdm(df.index, desc=f"Extraindo características ({level_name})"):
60
+ file_path = df.loc[idx, 'file_path']
61
+
62
+ try:
63
+ if use_vad:
64
+ audio = load_audio(df, idx, sr=sr)
65
+ audio_clean = remove_silence(audio, sr=sr)
66
+
67
+ if len(audio_clean) == 0:
68
+ audio_clean = np.zeros(sr)
69
+
70
+ feat_df = smile.process_signal(audio_clean, sr)
71
+ else:
72
+ feat_df = smile.process_file(file_path)
73
+
74
+ if flevel == opensmile.FeatureLevel.Functionals:
75
+ feat_series = feat_df.iloc[0].rename(idx)
76
+ features_list.append(feat_series)
77
+ else:
78
+ feat_df['file_idx'] = idx
79
+ features_list.append(feat_df)
80
+
81
+ except Exception as e:
82
+ print(f"Erro na extração do índice {idx}: {e}")
83
+ if flevel == opensmile.FeatureLevel.Functionals:
84
+ features_list.append(pd.Series(dtype=float, name=idx))
85
+ else:
86
+ features_list.append(pd.DataFrame({'file_idx': [idx]}))
87
+
88
+ print("Consolidando matriz de features...")
89
+
90
+ if flevel == opensmile.FeatureLevel.Functionals:
91
+ result_df = pd.DataFrame(features_list)
92
+ else:
93
+ result_df = pd.concat(features_list)
94
+ if 'file_idx' in result_df.columns:
95
+ result_df = result_df.set_index('file_idx', append=True)
96
+
97
+ return result_df
@@ -0,0 +1,92 @@
1
+ import soundfile as sf
2
+ import scipy.signal
3
+ import numpy as np
4
+ from IPython.display import Audio, display
5
+ from tqdm.auto import tqdm
6
+ import noisereduce as nr
7
+
8
+ def scipy_load(path, target_sr=None):
9
+
10
+ audio, native_sr = sf.read(path)
11
+
12
+ if audio.ndim > 1:
13
+ audio = np.mean(audio, axis=1)
14
+
15
+ if target_sr is not None and native_sr != target_sr:
16
+ num_samples = round(len(audio) * float(target_sr) / native_sr)
17
+ audio = scipy.signal.resample(audio, num_samples)
18
+ return audio, target_sr
19
+
20
+ return audio, native_sr
21
+
22
+ def remove_silence(audio, sr=16000, frame_length=2048, hop_length=512, threshold=0.25):
23
+ audio_clean = nr.reduce_noise(y=audio, sr=sr)
24
+
25
+ n_frames = 1 + (len(audio_clean) - frame_length) // hop_length
26
+ if n_frames <= 0:
27
+ return audio_clean
28
+
29
+ rms = np.array([
30
+ np.sqrt(np.mean(audio_clean[i*hop_length : i*hop_length + frame_length] ** 2))
31
+ for i in range(n_frames)
32
+ ])
33
+
34
+ max_rms = np.max(rms)
35
+ if max_rms == 0:
36
+ return np.array([])
37
+
38
+ rms_norm = rms / max_rms
39
+ speech_mask = rms_norm > threshold
40
+
41
+ sample_mask = np.zeros(len(audio_clean), dtype=bool)
42
+ for i, active in enumerate(speech_mask):
43
+ if active:
44
+ start = i * hop_length
45
+ end = min(start + hop_length, len(audio_clean))
46
+ sample_mask[start:end] = True
47
+
48
+ return audio_clean[sample_mask]
49
+
50
+ def listen(df, index):
51
+ if index not in df.index:
52
+ print(f"Aviso: Não há nenhum áudio disponível com o índice {index}.")
53
+ return
54
+
55
+ path = df.loc[index, 'file_path']
56
+ emotion = df.loc[index, 'emotion']
57
+
58
+ print(f"Tocando áudio {index} | Emoção: {emotion}")
59
+ display(Audio(filename=path))
60
+
61
+ def load_audio(df, index, sr=16000):
62
+ if index not in df.index:
63
+ raise IndexError("Índice não encontrado no Dataset.")
64
+ path = df.loc[index, 'file_path']
65
+ audio, _ = scipy_load(path, target_sr=sr)
66
+ return audio
67
+
68
+ def load_batch(df, begin, end, sr=16000, max_1000=True):
69
+ qtd = end - begin
70
+ if qtd > 1000 and max_1000:
71
+ raise ValueError(f"Você tentou carregar {qtd} áudios. O limite é 1000. Defina max_1000=False se tiver certeza.")
72
+
73
+ batch = []
74
+ subset_indices = df.index[begin:end]
75
+
76
+ for idx in tqdm(subset_indices, desc="Carregando áudios"):
77
+ try:
78
+ audio = load_audio(df, idx, sr)
79
+ batch.append(audio)
80
+ except Exception as e:
81
+ print(f"Erro ao carregar índice {idx}: {e}")
82
+ batch.append(None)
83
+
84
+ max_len = max(len(x) for x in batch if x is not None)
85
+ padded_batch = np.zeros((len(batch), max_len), dtype=np.float32)
86
+
87
+ for i, audio in enumerate(batch):
88
+ if audio is not None:
89
+ padded_batch[i, :audio.shape[0]] = audio
90
+
91
+ return padded_batch
92
+
@@ -0,0 +1,21 @@
1
+ from .crema_d import process as process_crema
2
+ from .iemocap import process as process_iemocap
3
+ from .msp_improv import process as process_msp
4
+ from .msp_podcast import process as process_msp_podcast
5
+ from .emouerj import process as process_emouerj
6
+ from .ravdess import process as process_ravdess
7
+ from .emodb import process as process_emodb
8
+
9
+ # Dicionário que mapeia o nome do dataset (string) para a função de processamento
10
+ DATASET_LOADERS = {
11
+ 'crema_d': process_crema,
12
+ 'iemocap': process_iemocap,
13
+ 'msp_improv': process_msp,
14
+ 'msp_podcast': process_msp_podcast,
15
+ 'emouerj': process_emouerj,
16
+ 'ravdess': process_ravdess,
17
+ 'emodb': process_emodb
18
+ }
19
+
20
+ def get_available_datasets():
21
+ return list(DATASET_LOADERS.keys())