{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3cd8cd7a-cc83-48b8-b472-b92434f1d157",
   "metadata": {},
   "source": [
    "# ALUNOS:\n",
    "- Vasco Alves - nº estudante 2022228207\n",
    "\n",
    "# OBJECTIVO\n",
    "Este projeto visa a análise nos domínios da frequência\n",
    "e do tempo de sinais áudio com o objetivo de identificar os dígitos\n",
    "em inglês entre 0 e 9. \n",
    "\n",
    "# DESCRIÇÃO DOS DADOS:\n",
    "Os dados fornecidos correspondem a sinais de voz emitidos\n",
    "por 60 participantes (cada uma das subdiretorias na pasta data contém os sinais\n",
    "correspondentes a 1 participante). Cada participante repetiu 50 vezes cada um\n",
    "dos dígitos, ou seja, cada uma das 60 pastas contém 500 sinais de áudio em\n",
    "formato ‘.wav’. Cada sinal de áudio foi adquirido a uma taxa de amostragem\n",
    "de 48000 Hz em modo mono-canal. Mais detalhes sobre os dados podem ser\n",
    "obtidos nas ligações seguintes.  \n",
    "- https://github.com/soerenab/AudioMNIST \n",
    "- https://www.kaggle.com/datasets/sripaadsrinivasan/audio-mnist \n",
    "- https://arxiv.org/abs/1807.03418.  \n",
    "\n",
    "# ENTREGAS:\n",
    "- Meta 1: 09/abr/2026, 23:59 \n",
    "- Meta 2: 14/mai/2026, 23:59 \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "fa208422-a7fe-40ad-8abf-8d1fd5618fc1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Feito\n"
     ]
    }
   ],
   "source": [
    "# Imports\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import sympy\n",
    "from scipy.io import wavfile\n",
    "import os\n",
    "import random\n",
    "print(\"Feito\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b2fd9d6-c045-44ab-9a2f-21add414913d",
   "metadata": {},
   "source": [
    "# Meta 1 Análise Temporal e de Frequência "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "802ea743-e63f-44d7-8cfd-21d1de3ce96d",
   "metadata": {},
   "source": [
    "## Exercicio 1\n",
    "Crie uma estrutura de dados (por exemplo, uma tabela) e guarde as seguintes informações relativas a cada áudio: \n",
    "\n",
    "- a. diretório do áudio; \n",
    "- b. nome do ficheiro (com extensão); \n",
    "- c. participante; \n",
    "- d. dígito; \n",
    "- e. número da repetição. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "043391e2-1364-4675-8de7-3e0a5732d908",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total de registos: 30000\n",
      "  dir         nome  participante  digito  repeticao\n",
      "0  01   0_01_0.wav             1       0          0\n",
      "1  01   0_01_1.wav             1       0          1\n",
      "2  01  0_01_10.wav             1       0         10\n",
      "3  01  0_01_11.wav             1       0         11\n",
      "4  01  0_01_12.wav             1       0         12\n"
     ]
    }
   ],
   "source": [
    "# a pasta 'data' deve estar na mesma pasta que o notebook\n",
    "pastas = [d for d in os.listdir('data') if os.path.isdir(os.path.join('data', d))]\n",
    "mega_dados_xpto = []\n",
    "for p in pastas:\n",
    "    caminho_pasta = os.path.join('data', p)\n",
    "    # ficheiros .wav dentro de cada pasta de participante\n",
    "    ficheiros = [f for f in os.listdir(caminho_pasta) if f.endswith('.wav')]\n",
    "    for f in ficheiros:\n",
    "        # nome do ficheiro: -> digito_participante_repeticao.wav \n",
    "        # removemos a extensão .wav e dividimos pelo underscore\n",
    "        # para obter os dados\n",
    "        nome_sem_extensao = f.replace('.wav', '')\n",
    "        partes = nome_sem_extensao.split('_')\n",
    "        if len(partes) == 3:\n",
    "            digito, participante, repeticao = partes\n",
    "            mega_dados_xpto.append([p, f, int(participante), int(digito), int(repeticao)])\n",
    "\n",
    "# Eu gosto de DataFrames\n",
    "cols = [\"dir\", \"nome\", \"participante\", \"digito\", \"repeticao\"]\n",
    "dados_df = pd.DataFrame(mega_dados_xpto, columns=cols)\n",
    "\n",
    "# Se o número de registos não for 30000, está errado\n",
    "assert(len(dados_df) == 60 * 50 * 10)\n",
    "print(f\"Total de registos: {len(dados_df)}\")\n",
    "print(dados_df.head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49a20a43-746b-4ab7-ba55-6cd692fb26d9",
   "metadata": {},
   "source": [
    "## Exercicio 2\n",
    "Desenvolva código para importação dos sinais de áudio e guarde o valor da taxa de amostragem e os sinais na estrutura de dados criada no ponto 1. Nota: pode utilizar a função audioread do MATLAB. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "cc62f558-27a8-4977-8912-5ca249a36756",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Colunas adicionadas com sucesso!\n",
      "          nome sampling_rate\n",
      "0   0_01_0.wav         48000\n",
      "1   0_01_1.wav         48000\n",
      "2  0_01_10.wav         48000\n",
      "3  0_01_11.wav         48000\n",
      "4  0_01_12.wav         48000\n"
     ]
    }
   ],
   "source": [
    "# novas colunas vazias\n",
    "dados_df['signal'] = None\n",
    "dados_df['sampling_rate'] = None\n",
    "for idx, row in dados_df.iterrows():\n",
    "    caminho_completo = os.path.join('data', row['dir'], row['nome'])  \n",
    "    try:\n",
    "        sr, signal = wavfile.read(caminho_completo)\n",
    "        dados_df.at[idx, 'signal'] = signal\n",
    "        dados_df.at[idx, 'sampling_rate'] = sr      \n",
    "    except Exception as e:\n",
    "        print(f\"wav não existe?? - {caminho_completo} - {e}\")\n",
    "print(\"Colunas adicionadas com sucesso!\")\n",
    "print(dados_df[['nome', 'sampling_rate']].head())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0500156e-d179-4c92-94a4-42fa19f280c9",
   "metadata": {},
   "source": [
    "## Exercicio 3\n",
    "Reproduza e represente graficamente um exemplo dos sinais importados, identificando o dígito e o número da repetição a que cada um corresponde, e indicando o eixo horizontal com o tempo em segundos, como apresentado no exemplo da Figura 2.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "4ddc829a-2b7f-43c5-b66b-da9d0765acfc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 663140 characters]",
      "text/plain": [
       "<Figure size 1500x2000 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from matplotlib.ticker import FormatStrFormatter\n",
    "# escolher um participante e uma repeticao aleatoriamente\n",
    "sujeito_aleatorio = random.choice(dados_df['participante'].unique())\n",
    "rep_aleatoria = random.choice(dados_df['repeticao'].unique())\n",
    "\n",
    "# criar a figura com 5 linhas e 2 colunas\n",
    "fig, axes = plt.subplots(5, 2, figsize=(15, 20))\n",
    "fig.suptitle(f'Gráficos dos Áudios Originais (Participante {sujeito_aleatorio} (Repetição {rep_aleatoria})', fontsize=16)\n",
    "\n",
    "# iterar pelos digitos de 0 a 9\n",
    "for digito in range(10):\n",
    "    selecao = dados_df[(dados_df['participante'] == sujeito_aleatorio) & \n",
    "                      (dados_df['repeticao'] == rep_aleatoria) & \n",
    "                      (dados_df['digito'] == digito)]\n",
    "    \n",
    "    if not selecao.empty:\n",
    "        # NOTE(vasco): o formato wav vem em inteiros de 16 bits\n",
    "        # é necessario converter para floats para ficar\n",
    "        # como está na figura\n",
    "        sinal = selecao.iloc[0]['signal'] / pow(2, 15) # 15 porque sao inteiros positivos\n",
    "        sr = selecao.iloc[0]['sampling_rate']\n",
    "        \n",
    "        # calcular o vetor de tempo em segundos\n",
    "        tempo = np.linspace(0, len(sinal) / sr, num=len(sinal))\n",
    "        ax = axes[digito // 2, digito % 2]\n",
    "        \n",
    "        # plot do sinal\n",
    "        ax.plot(tempo, sinal, color='steelblue', linewidth=0.5)\n",
    "        ax.set_title(f'Dígito {digito}; Repetição {rep_aleatoria}')\n",
    "        ax.set_xlabel('Tempo (s)')\n",
    "        ax.set_ylabel('Amplitude')\n",
    "        # eu queria que fosse exatamente igual à imagem!!!\n",
    "        ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))\n",
    "        ax.grid(alpha=0.2)\n",
    "\n",
    "# ajustar o layout para nao sobrepor titulos\n",
    "plt.tight_layout(rect=[0, 0.03, 1, 0.95])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58068c6a-eb41-4912-a0c7-2c50de304407",
   "metadata": {},
   "source": [
    "## Exercicio 4\n",
    "Uma estratégia que pode melhorar a diferenciação entre dígitos é fazer uma etapa de pré-processamento para garantir que todos os ficheiros têm a mesma duração e intervalo de amplitude. \n",
    "\n",
    "Para isso deverá implementar os seguintes passos:\n",
    "\n",
    "- Retirar o “silêncio” inicial dos sinais, de forma a garantir que todos começam exatamente ao mesmo tempo (por exemplo, através do estudo da Energia do sinal ao longo do tempo). \n",
    "- Normalizar a amplitude com base na amplitude máxima e mínima das amostras. Isto garante que os problemas usuais da gravação do som (por exemplo, a distância da pessoa ao microfone) não interferem na análise. \n",
    "- Adicionar (ou retirar) silêncio no fim dos ficheiros para garantir que todos têm a mesma duração total.\n",
    "\n",
    "Guarde os sinais após o pré-processamento na estrutura de dados criada no ponto 1. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8188faf8-ddc1-4178-9806-fd33196e7aa8",
   "metadata": {},
   "outputs": [],
   "source": [
    "def preprocessamento(signal, sr, target_dur=1.0):\n",
    "    # retirar o silêncio inicial baseado na energia\n",
    "    # calculamos a energia em janelas pequenas\n",
    "    window_size = int(sr * 0.02) # janelas de 20ms\n",
    "    energy = np.array([\n",
    "        np.sum(signal[i:i+window_size]**2) \n",
    "        for i in range(0, len(signal), window_size)\n",
    "    ])\n",
    "    \n",
    "    # definimos um threshold (ex: 5% da energia máxima)\n",
    "    threshold = 0.05 * np.max(energy)\n",
    "    \n",
    "    # encontrar o primeiro bloco que ultrapassa o threshold\n",
    "    start_index = 0\n",
    "    for i, e in enumerate(energy):\n",
    "        if e > threshold:\n",
    "            start_index = i * window_size\n",
    "            break\n",
    "            \n",
    "    # cortamos o sinal desde o início do som\n",
    "    trimmed_signal = signal[start_index:]\n",
    "    \n",
    "    # normalizar a amplitude (min-max para o intervalo [-1, 1])\n",
    "    # primeiro convertemos para float se ainda não for\n",
    "    trimmed_signal = trimmed_signal.astype(np.float32)\n",
    "    \n",
    "    max_val = np.max(np.abs(trimmed_signal))\n",
    "    if max_val > 0:\n",
    "        normalized_signal = trimmed_signal / max_val\n",
    "    else:\n",
    "        normalized_signal = trimmed_signal\n",
    "\n",
    "    # ajustar a duração (padding ou cropping no fim)\n",
    "    target_samples = int(target_dur * sr)\n",
    "    current_samples = len(normalized_signal)\n",
    "    \n",
    "    if current_samples > target_samples:\n",
    "        # se for maior, cortamos o fim\n",
    "        final_signal = normalized_signal[:target_samples]\n",
    "    else:\n",
    "        # se for menor, adicionamos silêncio (zeros) no fim\n",
    "        padding = np.zeros(target_samples - current_samples)\n",
    "        final_signal = np.concatenate((normalized_signal, padding))\n",
    "        \n",
    "    return final_signal\n",
    "\n",
    "# duraçao ideal de 1 segundo (arbitrario)\n",
    "duracao_ideal = 1.0\n",
    "dados_df['signal_preproc'] = dados_df.apply(\n",
    "    lambda row: preprocessamento(row['signal'], row['sampling_rate'], duracao_ideal), \n",
    "    axis=1\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2ca2fe8-6080-42d8-85da-586287c9ce04",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.14.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
