{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "669bb5f3",
   "metadata": {},
   "source": [
    "<div align=\"center\">\n",
    "    \n",
    "# ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΡΗΤΗΣ\n",
    "# Τμήμα Επιστήμης Υπολογιστών\n",
    "\n",
    "## HY-215: Εφαρμοσμένα Μαθηματικά για Μηχανικούς\n",
    "## Εαρινό Εξάμηνο 2024-2025\n",
    "### Διδάσκων: Γ. Καφεντζής\n",
    "    \n",
    "## Ανάθεση: 11-3-2025\n",
    "## Παράδοση (ηλεκτρονικά, όπως αναφέρεται στην ιστοσελίδα του μαθήματος και στο τέλος του αρχείου αυτού): \n",
    "## Παρασκευή, 23-3-2025, 23:59:59\n",
    "<h1><center>2η Σειρά Εργαστηρίου</center></h1>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "601949cd",
   "metadata": {},
   "source": [
    "Σε κάθε άσκηση `Python`, εισάγουμε πρώτα τις βιβλιοθήκες που θα χρησιμοποιήσουμε: εδώ, θα χρειαστούμε τις `numpy`, `matplotlib`, `Ipython`, `pandas`, και `scipy`.\n",
    "\n",
    "Επιλέξτε το παρακάτω κελί κώδικα και πατήστε Shift + Enter, ώστε να το εκτελέσετε. \n",
    "\n",
    "Το ίδιο θα κάνετε με οποιοδήποτε κελί κώδικα. Αν τροποποιήσετε κάποιο κελί κώδικα, θα πρέπει να το ξανατρέξετε για να δείτε τα αποτελέσματά του."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e1593ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np                        # Μαθηματική/αριθμητική βιβλιοθήκη\n",
    "import scipy.io.wavfile as wav            # Βιβλιοθήκη ανάγνωσης/εγγραφής ήχου\n",
    "import matplotlib.pyplot as plt           # Βιβλιοθήκη απεικονίσεων\n",
    "from IPython.display import Audio         # Βιβλιοθήκη παιξίματος αρχείων ήχου\n",
    "import pandas as pd                       # Βιβλιοθήκη ανάγνωσης και επεξεργασίας αρχείων"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac9835e1",
   "metadata": {},
   "source": [
    "---\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a008d87",
   "metadata": {},
   "source": [
    "### Άσκηση 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00c50afa",
   "metadata": {},
   "source": [
    "Ας κάνουμε μια άσκηση για ζέσταμα πριν μπούμε σε πιο ενδιαφέρουσες ασκήσεις παρακάτω... \n",
    "\n",
    "Η τιμή κάποιων απλών ολοκληρωμάτων μπορεί να υπολογιστεί με αρκετή ακρίβεια με χρήση της `Python`. Ας δούμε πρώτα τι λέει η θεωρία, και συγκεκριμένα ας κάνουμε μια βόλτα από τον Απειροστικό Λογισμό Ι και να θυμηθούμε το"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19fb50d5",
   "metadata": {},
   "source": [
    "<center> <strong> <font size=5>Ολοκλήρωμα Riemann </strong> </center>\n",
    "<font size=4>\n",
    "<br>\n",
    "Ο Β. Riemann πρότεινε την προσέγγιση ενός ολοκληρώματος από τμηματικά σταθερές συναρτήσεις, των οποίων το αθροιστικό εμβαδό δίνει μια τιμή για το ολοκλήρωμα. Με μαθηματικά:\n",
    "\n",
    "$$ E = \\int_{a}^{b} f(t) dt = \\lim_{\\Delta t \\rightarrow 0} \\sum_{i=1}^{N} f(\\bar{t}_i) \\Delta t$$\n",
    "\n",
    "Τι μας λέει η παραπάνω σχέση; Μας λέει ότι η τιμή ενός ορισμένου ολοκληρώματος μπορεί να βρεθεί αν κάνουμε τα εξής:\n",
    "\n",
    "(i) Χωρίζουμε το διάστημα $[a,b]$ σε $N$ ίσα μέρη μήκους (διάρκειας) $\\Delta t$ το καθένα. Αυτή η *διαμέριση* του διαστήματος λέγεται *ομοιόμορφη*.\n",
    "\n",
    "(ii) Παίρνουμε ένα σημείο $\\bar{t}_i$ σε κάθε ένα από αυτά τα διαστήματα. Για παράδειγμα, το μέσο του διαστήματος $ \\Delta t $ ή όποιο άλλο θέλουμε.\n",
    "\n",
    "(iii) Υπολογίζουμε την τιμή της συνάρτησης $f(t)$ στα σημεία $\\bar{t}_i$ κάθε τμήματος της διαμέρισης.\n",
    "\n",
    "(iv) Έχουμε τώρα $N$ τιμές της $f(t)$, δηλ. τις $f(\\bar{t}_i)$, και πολλαπλασιαζόμενες μαζί με τα $N$ το πλήθος $\\Delta t$, τις προσθέτουμε όλες μαζί για να πάρουμε το αποτέλεσμα.\n",
    "\n",
    "Για να είναι ακριβές το αποτέλεσμα, πρέπει το $\\Delta t$ να είναι όσο μικρότερο γίνεται, δηλ. ιδανικά να ισχύει $\\Delta t \\rightarrow 0$, ώστε να υπάρχει ισότητα μεταξύ ολοκληρώματος και αθροίσματος.\n",
    "\n",
    "**Πρακτικά**, το παραπάνω μεταφράζεται στην προσέγγιση της επιφάνειας κάτω από τη συνάρτηση $f(t)$ από ορθογώνια παραλληλόγραμμα με \"ύψος\" $f(\\bar{t}_i)$ και \"πλάτος\" $\\Delta t$. Γνωρίζουμε από τη Γεωμετρία ότι κάθε παραλληλόγραμμο έχει επιφάνεια \n",
    "\n",
    "$$E_i = f(\\bar{t}_i)\\Delta t$$\n",
    "\n",
    "και η προσέγγιση του ολοκληρώματος, υποθέτοντας ότι έχουμε $N$ τέτοια παραλληλόγραμμα, θα είναι\n",
    "\n",
    "$$E \\approx \\sum_{i=1}^N E_i =\\sum_{i=1}^N f(\\bar{t}_i)\\Delta t $$\n",
    "\n",
    "Όταν το \"πλάτος\" $\\Delta t$ κάθε παραλληλογράμμου γίνεται όλο και μικρότερο, τόσο καλύτερη η προσέγγιση της επιφάνειας (δηλ. του ολοκληρώματος που ζητάμε) από το άθροισμα των επιφανειών των παραλληλογράμμων."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25ac32f8",
   "metadata": {},
   "source": [
    "Η διαδικασία φαίνεται στο παρακάτω σχήμα.\n",
    "\n",
    "![riemann](./riemann.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f90c4f4",
   "metadata": {},
   "source": [
    "I. Ας δούμε πως θα υλοιποιήσουμε αριθμητικά στη `Python` το ολοκλήρωμα Riemann\n",
    "\n",
    "$$\\int_{-\\infty}^{+\\infty}\\left(-\\frac{1}{3}\\right)e^{-2(t-2)}u(t-2)dt = \\int_2^{+\\infty}\\left(-\\frac{1}{3}\\right)e^{-2(t-2)}dt = -\\frac{1}{6}$$\n",
    "\n",
    "Έχουμε:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7999d317",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ορίζουμε τον άξονα του χρόνου από t=\"2 + κάτι μικρό\" (λόγω της βηματικής που ξεκινά από το t=2)\n",
    "# ως μια μεγάλη τιμή που προσομοιώνει το άπειρο, π.χ. την τιμή 1000, τμηματοποιώντας \n",
    "# τον ανά Dt = 0.001\n",
    "Dt = 0.001\n",
    "\n",
    "# άξονας του χρόνου \n",
    "t = np.arange(start=2+Dt, stop=1000, step=Dt)\n",
    "\n",
    "# Η συνάρτηση x(t) ορίζεται ως\n",
    "x = (-1/3) * np.exp(-2*(t-2))\n",
    "\n",
    "# Υπολογίζουμε με τον παρακάτω τρόπο το ολοκλήρωμα Riemann:\n",
    "apotelesma = Dt * sum(x)\n",
    "\n",
    "# και το τυπώνουμε:\n",
    "print(f'{apotelesma:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0596357e",
   "metadata": {},
   "source": [
    "Ας το επιβεβαιώσουμε:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4331380e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Επιβεβαίωση:\n",
    "print(f'{-1/6:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32f82fc8",
   "metadata": {},
   "source": [
    "II. Ας υπολογίσουμε τώρα το (διάσημο) ολοκλήρωμα\n",
    "\n",
    "$$ \\int_{0}^{\\pi /2} \\left(\\frac{t}{\\sin(t)}\\right)^2 dt = \\pi \\ln (2) $$\n",
    "\n",
    "Αρχικά πρέπει να προσέξουμε ότι η συνάρτηση μέσα στο ολοκλήρωμα δεν ορίζεται για $t=0$. Οπότε:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "055f4d00",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ορίζουμε τον άξονα του χρόνου από \"0 + κάτι_μικρό\" ως π/2 για να εξασφαλίσουμε ότι\n",
    "# δε μηδενίζεται ο παρονομαστής της συνάρτησής μας. Η τμηματοποίηση του άξονα είναι\n",
    "# ξανά ανά Dt που τώρα το θέτουμε ίσο με 0.00001.\n",
    "Dt = 1e-5 # Math notation: 1e-a = 10**(-a)\n",
    "\n",
    "t = np.arange(start=Dt, stop=np.pi/2, step=Dt)\n",
    "\n",
    "# Ορίζουμε τη συνάρτηση t/sin(t)\n",
    "f = (t/np.sin(t))\n",
    "\n",
    "# Υψώνουμε στο τετράγωνο\n",
    "f_sq = f**2\n",
    "\n",
    "# Υπολογίζουμε το ολοκλήρωμα Riemann όπως και πριν:\n",
    "apotelesma = Dt * sum(f_sq)\n",
    "print(f'{apotelesma:.4f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d81217c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Επιβεβαίωση:\n",
    "print(f'{np.pi*np.log(2):.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6b07605",
   "metadata": {},
   "source": [
    "III. Με βάση τα παραπάνω, επιβεβαιώστε αριθμητικά στην `Python` τα ολοκληρώματα:\n",
    "\n",
    "a. $$ \\int_{0}^{1} \\frac{\\ln(t)}{1+t}dt = -\\frac{\\pi^2}{12} $$\n",
    "\n",
    "\n",
    "b. $$ \\int_{1}^{2} \\frac{(t+2)(t-2)}{t^2} dt = -1 $$\n",
    "\n",
    "\n",
    "c. $$ \\int_{0}^{+\\infty} \\frac{t}{e^t - 1} dt = \\frac{\\pi^2}{6} $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f8c65cc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# a.\n",
    "Dt = 1e-5 \n",
    "t1 = # INSERT CODE\n",
    "f1 = # INSERT CODE\n",
    "apotelesma1 = # INSERT CODE\n",
    "print(f\"a. (Riemmann)   = {apotelesma1:.4f}\")\n",
    "print(f\"a. (Επιβεβαίωση): -pi**2/12 = {-np.pi**2/12:.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36685b99",
   "metadata": {},
   "outputs": [],
   "source": [
    "# b.\n",
    "Dt = 1e-5\n",
    "t2 = # INSERT CODE\n",
    "f2 = # INSERT CODE\n",
    "apotelesma2 = # INSERT CODE\n",
    "print(f\"b. (Riemmann) = {apotelesma2:.4f}\")\n",
    "print(f\"b. (Επιβεβαίωση): {-1:.4f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba47a3c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# c.\n",
    "Dt = 1e-5\n",
    "t3 = # INSERT CODE\n",
    "f3 = # INSERT CODE\n",
    "apotelesma3 = # INSERT CODE\n",
    "print(f\"c. (Riemmann)   = {apotelesma3:.4f}\")\n",
    "print(f\"c. (Επιβεβαίωση): pi^2/6 = {np.pi**2/6:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55fb2b49",
   "metadata": {},
   "source": [
    "---\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9daaf158",
   "metadata": {},
   "source": [
    "### 2η Άσκηση"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0277e747",
   "metadata": {},
   "source": [
    "Μόνο και μόνο με τις έννοιες της *ενέργειας* και των απλών πρότυπων σημάτων, όπως ο *τετραγωνικός παλμός*, που έχετε μάθει, μπορείτε να δείτε μερικές εφαρμογές όπως η \n",
    "<center><font size=6>\n",
    "Aνίχνευση δραστηριότητας ομιλίας - voice activity detection. \n",
    "</center>\n",
    "<font size=4>\n",
    "Αλγόριθμοι που υλοποιούν τέτοιες εφαρμογές υπάρχουν σε κάθε κινητό τηλέφωνο, όπως επίσης και σε συστήματα αναγνώρισης ομιλίας όπως η Siri, το Google Assistant, και η Amazon Alexa, αλλά και σε συστήματα επικοινωνιών όπως το Zoom, το Skype, και το Microsoft Teams.\n",
    "\n",
    "Μια *ιδιαίτερα* απλοϊκή υλοποίηση ενός τέτοιου αλγορίθμου είναι η εξής :\n",
    "- Διαβάζετε ένα αρχείο ομιλίας στην Python που σας δίνεται.\n",
    "- Παραθυροποιείτε με χρήση τετραγωνικού παλμού το σήμα σας, \"κόβοντας\" έτσι ένα τμήμα του.\n",
    "    - Θεωρητικά, αυτό αντιστοιχεί σε πολλαπλασιασμό του σήματος με ένα τετραγωνικό παλμό ($\\mathrm{rect}(\\cdot)$)\n",
    "    - Η παραθυροποίηση γίνεται πρακτικά έμμεσα, απομονώνοντας ένα κομμάτι του σήματος μέσω array slicing. \n",
    "        - Άρα πρακτικά δεν πολλαπλασιάζουμε κάποιο σήμα με τον τετραγωνικό παλμό, αλλά **θεωρούμε** ότι το κάνουμε για να καταλάβουμε περισσότερα όσον αφορά το χώρο της συχνότητας, που θα ακολουθήσει αργότερα στο μάθημα.\n",
    "        - Αν όμως το παράθυρό μας είναι ένας άλλος παλμός, π.χ. τριγωνικός, τότε **ναι**, γίνεται **ρητά** ο πολλαπλασιασμός του τμήματος του σήματος με τον τριγωνικό παλμό. \n",
    "- Για κάθε παραθυροποιημένο σήμα, υπολογίζετε την ενέργεια του από τη γνωστή σχέση της θεωρίας.\n",
    "- Ανάλογα με την τιμή της ενέργειας, αποφασίζετε αν το παραθυροποιημένο σήμα περιέχει ομιλία ή όχι.\n",
    "- Προχωράτε στο επόμενο παραθυροποιημένο σήμα και επαναλαμβάνετε, ως το τέλος του σήματος."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "913b10f8",
   "metadata": {},
   "source": [
    "Ερωτήματα που προκύπτουν από την ανάγνωση του παραπάνω αλγορίθμου:\n",
    "\n",
    "(α') Πόση είναι η διάρκεια του παραθυροποιημένου σήματος; Μια τιμή κοντά στα $0.03$ s $=30$ ms είναι καλή. Δεν υπάρχει βέλτιστη διάρκεια, υπάρχουν trade-offs είτε έχει μεγάλη είτε μικρή διάρκεια.\n",
    "\n",
    "(β') Πόση απόσταση ϑα έχουν τα διαδοχικά παραθυροποιημένα σήματα μεταξύ τους; Στην πράξη, θα πρέπει όχι απλά να είναι διαδοχικά, αλλά να έχουν και κάποιο *ποσοστό επικάλυψης*. Για τους δικούς μας σκοπούς, θα χρησιμοποιήσουμε $0\\%$ επικάλυψη, δηλ. τα παραθυροποιημένα σήματα θα είναι ακριβώς διαδοχικά, χωρίς \"κενά\" μεταξύ τους (αφού κάτι τέτοιο θα σήμαινε απώλεια εκτίμησης δραστηριότητας ομιλίας).\n",
    "\n",
    "(γ') Πώς αποφασίζω με ϐάση την ενέργεια αν το εκάστοτε παραθυροποιημένο σήμα είναι τμήμα ομιλίας; Αυτή είναι μια ερώτηση που δέχεται αρκετές απαντήσεις - προσεγγίσεις. Ξανά μια απλή προσέγγιση είναι ότι στην αρχή του σήματος, υπάρχουν μερικά δευτερόλεπτα σιωπής - μπορείτε να εκτιμήσετε την ενέργεια σε αυτά τα παραθυροποιημένα σήματα και να αποφασίζετε ϐάσει αυτής (με κάποιο στατιστικό κριτήριο, όπως για παράδειγμα \"*κάθε παραθυροποιημένο σήμα να έχει ενέργεια μεγαλύτερη του διπλάσιου της ενέργειας των παραθυροποιημένων σημάτων σιωπής*\") για τα επόμενα τμήματα ομιλίας."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2defde3c",
   "metadata": {},
   "source": [
    "Το παρακάτω σχήμα δείχνει την τμηματοποίηση και τον υπολογισμό ενέργειας για κάθε παράθυρο (frame) σήματος.\n",
    "\n",
    "![rectwin2](./rectwindows.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0b59d02",
   "metadata": {},
   "source": [
    "Αν και υπάρχει ξεχωριστή εξίσωση για τον υπολογισμό της ενέργειας ενός σήματος **διακριτού χρόνου**, όπως ένα .WAV αρχείο, θα χρησιμοποιήσουμε το ολοκλήρωμα Riemann για να υπολογίσουμε την ενέργεια, όπως είδατε στην προηγούμενη άσκηση (και θα δείτε και στο ακόλουθο toy example).\n",
    "\n",
    "Το σήμα ομιλίας που έχουμε ηχογραφήσει και σας δίνουμε είναι *ψηφιακό* και όταν το διαβάζετε στην `Python`, το μετατρέπετε σε *διακριτού χρόνου*, αποθηκεύοντας τα δείγματά του σε ένα διάνυσμα. \n",
    "\n",
    "Μέχρι τότε όμως, ας καταλάβουμε λίγο τι συμβαίνει με ένα πιο ελεγχόμενο ψευδο-παράδειγμα. Έστω ότι το σήμα ομιλίας διακριτού χρόνου που διαβάζετε είναι η ακολουθία από αριθμούς αποθηκευμένους στο διάνυσμα $x$ όπως παρακάτω. Ας το δούμε."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c51db25",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.asarray([1, 0.5, 1.2, -1.7, 2.4, -0.3, -1.1, 0.6, -0.9])  # τυχαίο σήμα - υποτίθεται ομιλίας\n",
    "print(f\"Το σήμα ομιλίας διακριτού χρόνου είναι το x = {x}\")      # βλέπουμε τις τιμές του\n",
    "plt.stem(np.arange(0,9), x)                                      # τυπώνουμε τις τιμές του\n",
    "plt.xlabel('Διακριτός χρόνος')                                   # ομορφαίνουμε\n",
    "plt.ylabel('Πλάτος')                                             # ομορφαίνουμε"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b32e6fe",
   "metadata": {},
   "source": [
    "Οι τιμές που βλέπετε αποτελούν **δείγματα** του σήματος ομιλίας συνεχούς χρόνου που καταγράφηκε. Τα δείγματα αυτά λήφθηκαν ανά $1/F_s$ δευτερόλεπτα πραγματικού χρόνου, με $F_s$ να είναι η συχνότητα δειγματοληψίας, που σας δίνεται έτοιμη κάθε φορά που ανοίγετε ένα αρχείο ήχου στην `Python`. Αν, για παράδειγμα, η συχνότητα δειγματοληψίας είναι $16000$ Hz, τότε το παραπάνω σήμα $9$ δειγμάτων προέρχεται από ένα σήμα συνεχούς χρόνου διάρκειας \n",
    "\n",
    "$$(9-1)/16000 = 8/16000 = 500 \\text{ μs} = 0.5 \\text{ ms} = 0.0005 \\text{ s}$$\n",
    "\n",
    "Πραγματικά πολύ μικρής διάρκειας. \n",
    "\n",
    "Για να φτάσουμε στα $30$ ms που αναφέρει παραπάνω η άσκηση, χρειαζόμαστε $481$ δείγματα - αφού $(481-1)/16000 = 0.03$ s $= 30$ ms. Ας συνεχίσουμε όμως σε αυτό το toy example και ας υποθέσουμε όμως ότι θέλουμε να υπολογίσουμε την ενέργεια ανά $187.5$ μs, δηλ. ανά μόλις $N=3$ δείγματα. Τότε θα χωρίσουμε το σήμα σε $N-$άδες (τριάδες, εν προκειμένω), που ουσιαστικά αυτό αντιστοιχεί σε διαδοχικούς πολλαπλασιασμούς με μη επικαλυπτόμενους τετραγωνικούς παλμούς μοναδιαίου πλάτους και διάρκειας $187.5$ μs, ήτοι διάρκειας $N$ (τριών) δειγμάτων. \n",
    "\n",
    "Έτσι προκύπτουν τρεις τριάδες, $(1, 0.5, 1.2), (−1.7, 2.4,−0.3), (−1.1, 0.6,−0.9)$, που καθεμιά είναι ένα παράθυρο σήματος, και θα υπολογίσουμε την ενέργειά καθεμιάς:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19775344",
   "metadata": {},
   "outputs": [],
   "source": [
    "Fs = 16000 # Έστω μια συχνότητα δειγματοληψίας\n",
    "Dt = 1/Fs # Περίοδος δειγματοληψίας\n",
    "N = 3 # Διάρκεια παραθύρου\n",
    "\n",
    "# Τμηματοποίηση σε διαδοχικές N-άδες και υπολογισμός ενέργειας τους\n",
    "x1 = x[0:N]\n",
    "x2 = x[N:2*N]\n",
    "x3 = x[2*N:3*N]\n",
    "\n",
    "E = [] # μια άδεια λίστα για να μαζεύουμε τις ενέργειες των Ν-άδων\n",
    "E.append(Dt * np.sum(x1**2)) # Riemann\n",
    "E.append(Dt * np.sum(x2**2)) # Riemann\n",
    "E.append(Dt * np.sum(x3**2)) # Riemann\n",
    "\n",
    "print(f\"E1 = {E[0]}\\nE2 = {E[1]}\\nE3 = {E[2]}\") \n",
    "# θα πρέπει να λάβετε: \n",
    "# E1 = 0.000168125, \n",
    "# E2 = 0.000546249, \n",
    "# E3 = 0.00014875"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "276ca0cc",
   "metadata": {},
   "source": [
    "Βλέπετε ότι κάθε τμήμα από το αρχικό σήμα έχει διαφορετική τιμή για την ενέργειά του. \n",
    "\n",
    "Ας επιστρέψουμε τώρα στο πλήρες, πραγματικό παράδειγμα, όπου θα υλοποιήσουμε πιο αυτοματοποιημένα τα παραπάνω, καθώς και την επιλογή της δραστηριότητας ομιλίας με βάση την ενέργεια κάθε τμήματος σήματος.\n",
    "\n",
    "Ας γράψουμε κώδικα που διαβάζει ένα σήμα φωνής και μας υπολογίζει ένα διάνυσμα της ενέργειάς του σε κάποιες χρονικές στιγμές που αντιστοιχούν στα κέντρα των παραθύρων που αναλύουμε.\n",
    "\n",
    "Η ιδέα είναι η εξής:\n",
    "\n",
    "- Το σήμα ομιλίας θα αποθηκευτεί σε ένα ``numpy`` array.\n",
    "- Θα το προσπελαύνουμε ανά μη επικαλυπτόμενα τμήματα, χρησιμοποιώντας array slicing, όπως στο παραπάνω toy example.\n",
    "- Για κάθε τμήμα, θα υπολογίζουμε την ενέργειά του.\n",
    "- Θα την αποθηκεύουμε σε ένα άλλο array, με τέτοιο τρόπο ώστε η $i-$οστή τιμή του array να αντιστοιχεί στο $i-$οστό τμήμα.\n",
    "- Μόλις μαζέψουμε όλες τις ενέργειας, θα αποφασίσουμε ένα κατώφλι (threshold) με το οποίο θα συγκρίνουμε αυτές τις τιμές για να ταξινομούμε κάθε frame που αντιστοιχεί στις υπολογισμένες ενέργειες στην κατηγορία \"speech\" ή \"non-speech\".\n",
    "\n",
    "Συμπληρώστε τον παρακάτω σκελετό."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f201635",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Φόρτωση αρχείου ήχου στην Python (Fs είναι η συχνότητα δειγματοληψίας)\n",
    "Fs, s = wav.read('speech.wav')\n",
    "\n",
    "# Βήμα στο χρόνο (κάθε πότε πήραμε δείγμα από την πραγματική ηχογράφηση? κάθε 1/Fs δευτερόλεπτα)\n",
    "Dt = 1/Fs\n",
    "\n",
    "# Κανονικοποίηση του σήματος σε πλάτη [-1,1] (τεχνική λεπτομέρεια, μην τη λάβετε υπ'όψη)\n",
    "s = s/np.max(np.abs(s))\n",
    "\n",
    "# θέλουμε παράθυρα 30 ms στο χρόνο, σε πόσα δείγματα αντιστοιχούν? Αν 1 δευτερόλεπτο αντιστοιχεί σε Fs δείγματα, πόσα δείγματα αντιστοιχούν σε 0.03 δευτερόλεπτα? Χρησιμοποιήστε τη round().\n",
    "T = # INSERT CODE\n",
    "\n",
    "# Συνολική διάρκεια σήματος\n",
    "L = len(s)\n",
    "\n",
    "# Πόσα παράθυρα διάρκειας T δειγμάτων που βρηκαμε παραπάνω, μη επικαλυπτόμενα, χωρούν σε όλο το σήμα διάρκειας L δειγμάτων? Χρησιμοποιήστε τη round().\n",
    "N = # INSERT CODE\n",
    "\n",
    "# Δέσμευση μνήμης - αντίστοιχο της calloc στη C\n",
    "Energy = np.zeros(N)\n",
    "Time = np.zeros(N)\n",
    "\n",
    "for i in range(1, N):\n",
    "    \n",
    "    # Χρησιμοποιώντας το δείκτη i, τη διάρκεια T, και τον τελεστή int(), κόψτε κάθε φορά ένα τμήμα του σήματος s διάρκειας T\n",
    "    windowed_speech = # INSERT CODE\n",
    "\n",
    "    # Υπολογίστε την ενέργεια του τμήματος σήματος windowed_speech (όπως δείξαμε παραπάνω)\n",
    "    Energy[i] = # INSERT CODE\n",
    "    \n",
    "    # Κέντρο του κάθε τμήματος (το δείγμα στη μέση του τμήματος, π.χ. ένα τμήμα διάρκειας 200 δειγμάτων έχει κέντρο το 100)\n",
    "    Time[i] = round(i*T+T/2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8af13e26",
   "metadata": {},
   "source": [
    "Ας επιλέξουμε κατώφλι απόφασης με βάση τα frames της σιωπής στην αρχή του αρχείου. Όπως είπαμε, τα πρώτα 2 δευτερόλεπτα της ηχογράφησης (δηλ. τα πρώτα $2\\cdot F_s$ δείγματα) έχουν σιωπή. Συμπληρώστε κατάλληλα παρακάτω."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eba1ead2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Κατώφλι απόφασης\n",
    "No_Silence_Frames = round(2*Fs/T)             # το πληθος των frames που αντιστοιχούν στα πρώτα 2 δευτερόλεπτα\n",
    "Threshold = Energy[:No_Silence_Frames].mean() # έστω ότι το κατώφλι είναι η μέση ενέργεια αυτών των πρώτων frames που αντιστοιχούν στα πρώτα 2 seconds\n",
    "\n",
    "# Δέσμευση μνήμης για το διάνυσμα απόφασης (διάνυσμα με μηδενικά)\n",
    "D = np.zeros(N)\n",
    "\n",
    "# Διατρέχετε το διάνυσμα της ενέργειας που υπολογίσαμε ανά frame στον προηγούμενο κώδικα και αποφασίζετε \n",
    "# - 1 αν η ενέργεια του i-οστού frame είναι > από το κατώφλι, \n",
    "# - 0 αν η αντίστοιχη ενέργεια για το i-οστό frame είναι < κατώφλι\n",
    "for i in range(N):\n",
    "    if # INSERT CODE:\n",
    "         D[i] = # INSERT CODE"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04968535",
   "metadata": {},
   "source": [
    "Τώρα μπορούμε να \"ενώσουμε\" τις εκτιμήσεις μας και να απεικονίσουμε το αποτέλεσμα."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "699ffc54",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Γράφημα\n",
    "plt.figure(figsize=(10,5))\n",
    "\n",
    "t = np.arange(start=0, stop=L*Dt, step=Dt)               # άξονας χρόνου με βήμα Dt\n",
    "n = np.arange(start=0, stop=L, step=1)                   # άξονας διακριτού χρόνου για κάθε δείγμα\n",
    "Din = np.interp(x=n, xp=Time, fp=D)                      # γραμμική παρεμβολή\n",
    "\n",
    "plt.plot(t, Din, color='tab:blue', linewidth=2)            # γράφημα απόφασης\n",
    "plt.plot(t, s/max(abs(s)), color='tab:red', linewidth=0.4) # γράφημα σήματος\n",
    "\n",
    "plt.title('Voice Activity Detector')                     # Ομορφαίνουμε\n",
    "plt.xlabel('Time (s)')                                   # --- \" -----\n",
    "plt.grid(linestyle=':')                                  # --- \" -----\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2a3aa7d7",
   "metadata": {},
   "source": [
    "Αν τα κάνετε όλα σωστά, θα πάρετε κάτι πολύ κοντά στο παρακάτω σχήμα:\n",
    "\n",
    "![vad2](./output.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc8145bc",
   "metadata": {},
   "source": [
    "α) Επαναλάβετε την παραπάνω άσκηση με χρήση της δικής σας φωνής! Ηχογραφήστε τον εαυτό σας, για 5-7 δευτερόλεπτα, λέγοντας μια τυχαία πρόταση μεγάλης διάρκειας αλλά <span style=\"font-size:1.5em;\">κρατώντας τα δυο πρώτα δευτερόλεπτα σιωπηλά</span>. \n",
    "\n",
    "Χρησιμοποιήστε συχνότητα δειγματοληψίας $16000$ Hz, και μονοκαναλική ηχογράφηση. Προτεινόμενο πρόγραμμα ηχογράφησης:\n",
    "<center><font size=6> Audacity (https://www.audacityteam.org/)<font size=4></center>\n",
    "Αν θέλετε, συμπεριλάβετε στην παράδοσή σας το αρχείο αυτό. Θα διαγραφεί μετά τη διόρθωση της άσκησης.\n",
    "\n",
    "Δείτε το βιντεοπαράδειγμα ηχογράφησης με το Audacity (το βίντεο δεν έχει ήχο) που συνοδεύει το εργαστήριο. Το παραγόμενο αρχείο πρέπει να μπει στον ίδιο κατάλογο με το παρόν .ipynb αρχείο."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fdd1953",
   "metadata": {},
   "source": [
    "<span style=\"font-size:1.5em;\">Αν δεν το καταφέρετε, χρησιμοποιήστε το αρχείο `speechsample.wav` που σας δίνεται.</span>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b608b4e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# INSERT CODE HERE\n",
    "\n",
    "# Αντιγράψτε τον προηγούμενο κώδικα και απλά προσαρμόστε τον στο δικό σας σήμα\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# Μην ξεχάσετε να τυπώσετε στην οθόνη το γράφημα που προκύπτει!\n",
    "\n",
    "# Κώδικας για γράφημα\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# END OF CODE"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb4a6c06",
   "metadata": {},
   "source": [
    "β) Κάντε το ίδιο για τα δυο αρχεία που σας δίνονται, ``male_speech.wav`` και ``female_speech.wav``."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "457b443b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# INSERT CODE HERE\n",
    "\n",
    "# Αντιγράψτε τον προηγούμενο κώδικα και απλά προσαρμόστε τον στο male_speech.wav\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# Μην ξεχάσετε να τυπώσετε στην οθόνη το γράφημα που προκύπτει!\n",
    "\n",
    "# Κώδικας για γράφημα\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# END OF CODE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2adc176f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# INSERT CODE HERE\n",
    "\n",
    "# Αντιγράψτε τον προηγούμενο κώδικα και απλά προσαρμόστε τον στο female_speech.wav\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# Μην ξεχάσετε να τυπώσετε στην οθόνη το γράφημα που προκύπτει!\n",
    "\n",
    "# Κώδικας για γράφημα\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "# END OF CODE"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c5b7d9c",
   "metadata": {},
   "source": [
    "---\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f654d46",
   "metadata": {},
   "source": [
    "### 3η Άσκηση"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45e2ade8",
   "metadata": {},
   "source": [
    "Αν θελήσουμε να δημιουργήσουμε στην `Python` ένα ημιτονοειδές σήμα συχνότητας $f_0=400$ Hz (νότα ΛΑ), διάρκειας $0.5$ sec (δηλ. $500$ ms), με μηδενική φάση μετατόπισης $\\phi$ και μοναδιαίου πλάτους $A=1$, με συχνότητα δειγματοληψίας $f_s=16000$ Hz (που αντιστοιχεί σε καλής ποιότητας ηχογράφηση) θα γράψουμε τις ακόλουθες εντολές:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f47a31e",
   "metadata": {},
   "outputs": [],
   "source": [
    "fs = 16000    # συχνότητα δειγματοληψίας (fixed)\n",
    "f0 = 400      # συχνότητα ημιτόνου\n",
    "dur = 500     # διάρκεια σε ms\n",
    "A = 1         # πλάτος ημιτόνου\n",
    "t = np.arange(start=0, stop=dur/1000, step = 1/fs)   # άξονας χρόνου\n",
    "signal = A*np.sin(2*np.pi*f0*t)                      # σήμα"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "afa32689",
   "metadata": {},
   "source": [
    "Για να ακούσουμε αυτό που φτιάξαμε, πληκτρολογούμε"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53683f9c",
   "metadata": {},
   "outputs": [],
   "source": [
    "Audio(signal, rate=fs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c98732f7",
   "metadata": {},
   "source": [
    "Στην άσκηση αυτή θα συνθέσουμε τμήμα από το πολύ γνωστό κομμάτι του Beethoven, Fur Elise (https://www.youtube.com/watch?v=q9bU12gXUyM), από νότες πλήκτρων πιάνου."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fc00373",
   "metadata": {},
   "source": [
    "(α') Με χρήση της βιβλιοθήκης `Pandas` μπορούμε να διαβάσουμε ένα CSV αρχείο `FurEliseData.csv` που μας δίνεται"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f8c76947",
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv('FurEliseData.csv')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99dcab79",
   "metadata": {},
   "source": [
    "ως ένα `DataFrame`, και να το τυπώσουμε."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be6675d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26543144",
   "metadata": {},
   "source": [
    "Όπως βλέπετε, υπάρχουν $4$ στήλες, `lefthand, righthand, ldur, rhdur` οι οποίες ανταποκρίνονται σε αριθμούς πλήκτρου πιάνου που παίζονται από το αριστερό χέρι (`lefthand`), με τις αντίστοιχες διάρκειές τους σε ms (`lhdur`), και σε αριθμούς πλήκτρου πιάνου που παίζονται με το δεξί χέρι (`righthand`), με τις αντίστοιχες επίσης διάρκειές τους σε ms (`rhdur`). Τα μηδενικά αντιστοιχούν σε παύσεις (δεν παίζουμε κάποιο πλήκτρο), αντίστοιχης διάρκειας.\n",
    "\n",
    "Αυτούς μπορούμε να τους εισάγουμε σε ξεχωριστούς πίνακες της Python ως εξής:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "931a6394",
   "metadata": {},
   "outputs": [],
   "source": [
    "lefthand = df[\"lefthand\"].values\n",
    "righthand = df[\"righthand\"].values\n",
    "lhdur = df[\"lhdur\"].values\n",
    "rhdur = df[\"rhdur\"].values\n",
    "\n",
    "print(f\"Τα πλήκτρα πιάνου του αριστερού χεριού είναι τα: \\n {lefthand}\")\n",
    "print(f\"Οι διάρκειες σε ms κάθε πλήκτρου του αριστερού χεριού είναι: \\n {lhdur} ms\")\n",
    "\n",
    "print(f\"Τα πλήκτρα πιάνου του δεξιού χεριού είναι τα: \\n {righthand}\")\n",
    "print(f\"Οι διάρκειες σε ms κάθε πλήκτρου του δεξιού χεριού είναι: \\n {rhdur} ms\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fdf17d7",
   "metadata": {},
   "source": [
    "(β') Μετατρέψτε από κάθε χέρι, κάθε αριθμό πλήκτρου πιάνου $n$ στην αντίστοιχη συνχότητα με τη σχέση\n",
    "<br>\n",
    "$$\\Large f(n) = f_\\mathrm{LA} \\cdot 2^{\\frac{n-49}{12}} = 440 \\cdot 2^{\\frac{n-49}{12}} $$\n",
    "Στην `Python` αυτό γίνεται εύκολα ως"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "370a169f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Μετατροπή πλήκτρου πιάνου σε νότα για το αριστερό χέρι\n",
    "freq_lh = 440 * (2 ** ((lefthand-49)/12))\n",
    "\n",
    "# Μετατροπή πλήκτρου πιάνου σε νότα για το αριστερό χέρι (συμπληρώστε)\n",
    "freq_rh = # INSERT CODE"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "724a9155",
   "metadata": {},
   "source": [
    "με αποτέλεσμα έναν πίνακα - διάνυσμα `freq` με τις αντίστοιχες συχνότητες. Οι παύσεις αναπαρίστανται στους πίνακες με τα πλήκτρα ως $(0)$. Θα μετατραπούν και αυτές - ενώ δεν πρέπει - όμως αυτό δε θα μας επηρεάσει, καθώς τις παύσεις θα τις χειριστούμε ξεχωριστά όπως θα δείτε παρακάτω."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39aaefc4",
   "metadata": {},
   "source": [
    "(γ') Οι πίνακες `lhdur, rhdur` περιέχουν τη διάρκεια κάθε νότας σε ms για το αριστερό και το δεξί χέρι, αντίστοιχα. \n",
    "\n",
    "Ακολουθώντας τον παρακάτω κώδικα, δημιουργήστε τα αντίστοιχα ημίτονα με τη σωστή διάρκεια. Για τις παύσεις, θα δημιουργήσουμε ένα μηδενικό διάνυσμα με την ανάλογη διάρκεια."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a4b7e24",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Πόσα πλήκτρα έχουμε στο αριστερό χέρι?\n",
    "N = # INSERT CODE\n",
    "\n",
    "# Συχνότητα δειγματοληψίας\n",
    "fs = 16000\n",
    "\n",
    "# Μια άδεια λίστα για να μαζεύουμε τα ημίτονα που θα φτιάχνουμε\n",
    "signal_left = []\n",
    "\n",
    "# Ξεκινάμε:\n",
    "for i in range(1, N):\n",
    "    \n",
    "    # Ο άξονας χρόνου για κάθε πλήκτρο:\n",
    "    t = np.arange(start=0, stop=lhdur[i]/1000, step=1/fs)\n",
    "    \n",
    "    if lefthand[i] == 0:\n",
    "        # αν έχουμε παύση, δημιουργύμε ένα μηδενικό διάνυσμα διάρκειας lhdur[i] ms:\n",
    "        signal_left = np.append(signal_left, np.zeros(len(t)))\n",
    "    else:\n",
    "        # φτιάξτε το αντίστοιχο ημίτονο με χρήση του διανύσματος freq_lh (χρησιμοποιήστε την np.sin() και το παράδειγμα στην αρχή της άσκησης)\n",
    "        new_sin = # INSERT CODE\n",
    "        # \"μαζέψτε\" όλα τα ημίτονα στη λίστα signal_left\n",
    "        signal_left = np.append(signal_left, new_sin)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c49effe",
   "metadata": {},
   "source": [
    "(δ') Η μεταβλητή `signal_left` εδώ δηλώνεται ως μία λίστα (https://www.w3schools.com/python/python_lists.asp) της `Python`, στην οποία μπορούμε να *προσαρτήσουμε* (append) οποιαδήποτε μεταβλητή οποιουδήποτε μεγέθους. Αυτό είναι βολικό γιατί οι νότες δεν έχουν όλες την ίδια διάρκεια, και ένας πίνακας $N\\times L$ θα ήταν δύσχρηστος μιας και το $L$ αλλάζει δυναμικά."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fdf6f2a8",
   "metadata": {},
   "source": [
    "(ε') Επαναλάβετε τη διαδικασία για το δεξί χέρι, δηλ. τις μεταβλητές `righthand, rhdur` και αποθηκεύστε τα αποτελέσματά σας σε ξεχωριστές μεταβλητές. Παρατηρήστε ότι το αριστερό χέρι παίζει λιγότερες νότες από το δεξί."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c70e7237",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Πόσα πλήκτρα έχουμε στο δεξί χέρι?\n",
    "N = # INSERT CODE\n",
    "\n",
    "# Συχνότητα δειγματοληψίας\n",
    "fs = 16000\n",
    "\n",
    "# Μια άδεια λίστα για να μαζεύουμε τα ημίτονα που θα φτιάχνουμε\n",
    "signal_right = []\n",
    "\n",
    "# Ξεκινάμε:\n",
    "for i in range(1, N):\n",
    "    \n",
    "    # Ο άξονας χρόνου για κάθε πλήκτρο:\n",
    "    t = np.arange(start=0, stop=rhdur[i]/1000, step=1/fs)\n",
    "    \n",
    "    if righthand[i] == 0:\n",
    "        # αν έχουμε παύση, δημιουργύμε ένα μηδενικό διάνυσμα διάρκειας rhdur[i] ms:\n",
    "        signal_right = np.append(signal_right, np.zeros(len(t)))\n",
    "    else:\n",
    "        # φτιάξτε το αντίστοιχο ημίτονο με χρήση του διανύσματος freq_rh  (χρησιμοποιήστε την np.sin() και το παράδειγμα στην αρχή της άσκησης)\n",
    "        new_sin = # INSERT CODE\n",
    "        # \"μαζέψτε\" όλα τα ημίτονα στη λίστα signal_right\n",
    "        signal_right = np.append(signal_right, new_sin)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cc79fa0",
   "metadata": {},
   "source": [
    "(ζ) Δημιουργήστε το συνολικό σήμα, αθροίζοντας τα δύο σήματα ως\n",
    "\n",
    "`sig = signal_left + signal_right`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a2b8eec",
   "metadata": {},
   "outputs": [],
   "source": [
    "sig = signal_left + signal_right"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e5f4a39",
   "metadata": {},
   "source": [
    "Αν τα έχετε κάνει όλα σωστά, η `Python` θα σας επιστρέψει σφάλμα, γιατί το ένα σήμα βγήκε λίγο μικρότερο από το άλλο, οπότε δε γίνεται να προστεθούν (να θυμάστε ότι οι πράξεις μεταξύ πινάκων πρέπει να τηρούν τους κανόνες διάστασης των πινάκων). Χρησιμοποιήστε την εντολή `len` και μετρήστε το μήκος του κάθε σήματος. Πόση είναι η διαφορά τους σε πλήθος τιμών;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "176bffda",
   "metadata": {},
   "outputs": [],
   "source": [
    "len_left = len(signal_left)\n",
    "print(f\"To μήκος αριστερού σήματος είναι {len_left} δείγματα.\")\n",
    "\n",
    "len_right = len(signal_right)\n",
    "print(f\"To μήκος δεξιού σήματος είναι {len_left} δείγματα.\")\n",
    "\n",
    "D = len_right - len_left\n",
    "print(f\"Το δεξί σήμα είναι μεγαλύτερο κατά {D} δείγματα.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ae59d8f",
   "metadata": {},
   "source": [
    "(η') Για μια γρήγορη διόρθωση στο παραπάνω πρόβλημα, χρησιμοποιήστε τις εντολές"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60b771a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "D = len(signal_right) - len(signal_left)\n",
    "signal_left = np.append(signal_left, np.zeros(D))\n",
    "sig = signal_left + signal_right"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7ae3981",
   "metadata": {},
   "source": [
    "όπου απλά συμπληρώνουμε με μηδενικά το μικρότερο διάνυσμα για να γίνει ίσου \"μεγέθους\" με το μεγαλύτερο."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fdb38f3",
   "metadata": {},
   "source": [
    "(θ') Μπορείτε τώρα να ακούσετε το σήμα σας!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8315c6c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "sig = signal_left + signal_right\n",
    "\n",
    "# Ακούμε (χαμηλώστε τον ήχο)\n",
    "Audio(sig, rate=fs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18094376",
   "metadata": {},
   "source": [
    "Να σημειωθεί ότι ένα **πραγματικό πλήκτρο πιάνου** δεν παράγει *ένα* ημίτονο, όπως κάναμε εμείς, αλλά ένα **άθροισμα από ημίτονα**, με συχνότητες σχεδόν ακέραιες πολλαπλάσιες της θεμελιώδους συχνότητας και διαφορετικά μεταξύ τους πλάτη. Με άλλα λόγια, παράγει - προσεγγιστικά - **μια σειρά Fourier**, με πεπερασμένο πλήθος όρων, την οποία θα μελετήσουμε σύντομα. Επίσης, σε ένα πραγματικό πιάνο, υπάρχουν πάρα πολλές λεπτομέρειες που μας χαρίζουν τον πλούσιο ήχο του, οι οποίες δεν είναι απλό να μοντελοποιηθούν.\n",
    "\n",
    "$\\underline{\\text{Σημείωση}}$: Δείτε παρακάτω το άρθρο με τίτλο \"Perceptual significance of inharmonicity and spectral envelope in the piano bass range\", των Galembo et al. \n",
    "\n",
    "Πηγαίνετε στη σελίδα $6$, εξίσωση $(7)$, για να καταλάβετε πόσο πολύπολοκο είναι να συνθέσει κανείς έναν ήχο πλήκτρου πιάνου που να μοιάζει με τον πραγματικό. Η απλή όψη της εξίσωσης είναι αρκετή για να σας πείσει! 😉 \n",
    "\n",
    "Link: https://www.researchgate.net/publication/225284530_Perceptual_relevance_of_inharmonicity_and_spectral_envelope_in_the_piano_bass_range"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8de9bf6",
   "metadata": {},
   "source": [
    "---\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99a1a411",
   "metadata": {},
   "source": [
    "### 4η Άσκηση"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b57828f",
   "metadata": {},
   "source": [
    "Όσοι/ες εκτελέσατε με επιτυχία την προηγούμενη άσκηση, θα παρατηρήσατε ότι ο ήχος που δημιουργήσατε είχε κάποια ηχητικά clicks/απότομες παύσεις κάθε ϕορά που άλλαζε μια νότα. \n",
    "\n",
    "Σίγουρα συμφωνείτε ότι είναι πολύ ενοχλητικό στο αυτί μας. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ee4f9cd",
   "metadata": {},
   "source": [
    "Ο λόγος εμφάνισης αυτών των clicks είναι ο εξής: όταν δημιουργείτε ένα ημίτονο μιας συγκεκριμένης συχνότητας και συγκεκριμένης διάρκειας, δε σημαίνει απαραίτητα ότι το ημίτονο ξεκινά και τερματίζει σε μηδενικό πλάτος. Ο τερματισμός ενός ημιτόνου σε τυχαίο πλάτος και η αρχή του επομένου από μηδενικό πλάτος δημιουργεί μια απότομη αλλαγή, μια ασυνέχεια/αλλαγή φάσης, η οποία ακούγεται σαν click. Δείτε το Σχήμα παρακάτω. \n",
    "\n",
    "![disc](./discontinuities.png)\n",
    "\n",
    "Σε κάθε παράδειγμα του σχήματος βλέπετε δυο ημίτονα διαφορετικής συχνότητας σαν αυτά που δημιουργήσατε στην προηγούμενη άσκηση, με το ένα να ακολουθεί το άλλο. Αν οι διάρκεια του πρώτου ημιτόνου είναι κατάλληλα επιλεγμένη, τότε η συνένωση της δεύτερης νότας γίνεται ομαλά, όπως στο Σχήμα 6(α). Όμοια και στο Σχήμα 6(β), όπου η συνένωση είναι ακόμα πιο ομαλή και μια τέτοια περίπτωση είναι ιδανική. Και στις δυο περιπτώσεις τα clicks είναι ανεπαίσθητα ή ανύπαρκτα. \n",
    "\n",
    "Όμως αυτό που στην πραγματικότητα κάνατε στην προηγούμενη άσκηση είναι το Σχήμα 6(γ), καθώς η διάρκεια της κάθε νότας δεν τερμάτιζε απαραίτητα σε σημείο τέτοιο ώστε η ένωση της με το ημίτονο της επόμενης νότας να σχηματίζει \"συνέχεια\" στο σήμα. Τις περισσότερες - αν όχι όλες - φορές, ένα ημίτονο που δημιουργούσατε τερμάτιζε σε στιγμή που το πλάτος του δεν ήταν μηδενικό, και αναγκαστικά δημιουργούνταν μια ασυνέχεια στο ηχητικό σήμα λόγω της προσθήκης της επόμενης νότας, που ξεκινούσε από το μηδέν (αφού ήταν συναρτήσεις $\\sin(\\cdot)$, αλλά το ίδιο ϑα συνέβαινε αν χρησιμοποιούσαμε $\\cos(\\cdot))$."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b938928",
   "metadata": {},
   "source": [
    "Μια εύκολη λύση αυτού του προβλήματος είναι ο πολλαπλασιασμός του ημιτόνου που μοντελοποιεί κάθε νότα με ένα σήμα παραθύρου, ίδιας διάρκειας με το ημίτονο που παριστάνει κάθε νότα. Γνωρίζουμε ήδη δυο τέτοια παράθυρα: τον τετραγωνικό παλμό και τον τριγωνικό παλμό. Ουσιαστικά, ο τετραγωνικός παλμός χρησιμοποιείται έμμεσα κάθε ϕορά που δημιουργείτε ένα ημίτονο συγκεκριμένης διάρκειας. Όμως μπορείτε να χρησιμοποιήσετε άλλα παράθυρα, τέτοια ώστε να αναγκάζουν το ημιτονοειδές σήμα να κατεβαίνει ομαλά στο μηδέν στα άκρα του, όπως στο Σχήμα παρακάτω, όπου χρησιμοποιούμε το γνωστό παράθυρο Hanning, που έχει κωδωνοειδή μορφή. \n",
    "\n",
    "![win](./windowing.png)\n",
    "\n",
    "Με αυτό τον τρόπο τα clicks μειώνονται σημαντικά, ενώ δημιουργούνται και κάποια \"εξωτικά\" εφέ στον ήχο που παράγεται."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4208225a",
   "metadata": {},
   "source": [
    "Η βιβλιοθήκη `NumPy` της `Python` έχει έτοιμες συναρτήσεις παραθύρων για να χρησιμοποιήσετε. Όλες καλούνται με τον ίδιο τρόπο:\n",
    "\n",
    "`window = np.window_type(duration)`\n",
    "\n",
    "με το window_type να είναι ένα από τα εξής:\n",
    "\n",
    "`hamming, hanning, bartlett, blackman`\n",
    "\n",
    "και τη μεταβλητή `duration` να είναι η διάρκεια του παραθύρου. \n",
    "\n",
    "Εκτελέστε την παρακάτω συνάρτηση που αναλαμβάνει να πολλαπλασιάζει τα ημίτονα με το εκάστοτε παράθυρο."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a844e220",
   "metadata": {},
   "outputs": [],
   "source": [
    "def piano_simulator(lhand, rhand, ldur, rdur, sr, window):\n",
    "    \"\"\" Η συνάρτηση αυτή υλοποιεί την προηγούμενη άσκηση \n",
    "    και πολλαπλασιάζει κάθε ημίτονο με ένα γνωστό παράθυρο \n",
    "    για να μειώσει τα clicks μεταξύ γειτονικών ημιτόνων\"\n",
    "    Η μεταβλητή sr είναι η συχνότητα δειγματοληψίας \"\"\"\n",
    "\n",
    "    if window == \"hanning\":\n",
    "        win = np.hanning\n",
    "    elif window == \"hamming\":\n",
    "        win = np.hamming\n",
    "    elif window == \"bartlett\":\n",
    "        win = np.bartlett\n",
    "    elif window == \"blackman\":\n",
    "        win = np.blackman\n",
    "    else:\n",
    "        print(f\"\\\"{window}\\\" was given as the window; please provide one of the following ones: \\\"hanning\\\", \\\"hamming\\\", \\\"bartlett\\\", \\\"blackman\\\".\")\n",
    "        return 0\n",
    "\n",
    "    # Το αριστερό χέρι πρώτα\n",
    "    signal_left = []\n",
    "\n",
    "    for i in range(1, len(lhand)):\n",
    "        t = np.arange(start=0, stop=ldur[i]/1000, step=1/sr)\n",
    "        if lhand[i] == 0:\n",
    "            signal_left = np.append(signal_left, np.zeros(len(t)))\n",
    "        else:\n",
    "            freq = 440 * (2 ** ((lhand[i] - 49) / 12))\n",
    "            # Η παραθυροποίηση γίνεται εδώ:\n",
    "            signal_left = np.append(signal_left, np.sin(2*np.pi*freq*t) * win(len(t)))\n",
    "\n",
    "    # Το δεξί χέρι μετά\n",
    "    signal_right = []\n",
    "\n",
    "    for i in range(1, len(rhand)):\n",
    "        t = np.arange(start=0, stop=rdur[i]/1000, step=1/sr)\n",
    "        if rhand[i] == 0:\n",
    "            signal_right = np.append(signal_right, np.zeros(len(t)))\n",
    "        else:\n",
    "            freq = 440 * (2 ** ((rhand[i] - 49) / 12))\n",
    "            # κι εδώ:\n",
    "            signal_right = np.append(signal_right, np.sin(2*np.pi*freq*t) * win(len(t)))\n",
    "\n",
    "    # Χειριζόμαστε τη διαφορά στις διάρκειες όπως στην προηγ. άσκηση\n",
    "    d = len(signal_right) - len(signal_left)\n",
    "    signal_left = np.append(signal_left, np.zeros(d))\n",
    "\n",
    "    return signal_left + signal_right"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8c9189f",
   "metadata": {},
   "source": [
    "Ας δούμε τι παράγει λοιπόν η παραπάνω συνάρτηση για καθένα από τα παράθυρα που εφαρμόζουμε."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74911403",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Συχνότητα δειγματοληψίας:\n",
    "fs = 16000\n",
    "\n",
    "# Φορτώνουμε ξανά τις μεταβλητές μας\n",
    "df = pd.read_csv('FurEliseData.csv')\n",
    "lefthand = df[\"lefthand\"].values\n",
    "righthand = df[\"righthand\"].values\n",
    "lhdur = df[\"lhdur\"].values\n",
    "rhdur = df[\"rhdur\"].values\n",
    "\n",
    "# Καλούμε τη συνάρτηση για τα 4 γνωστά παράθυρα:\n",
    "hann_out = # INSERT CODE\n",
    "hamm_out = # INSERT CODE\n",
    "bart_out = # INSERT CODE\n",
    "black_out = # INSERT CODE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4c397711",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Ας τα ακούσουμε διαδοχικά:\n",
    "Audio(hann_out, rate=fs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7d8da0b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "Audio(hamm_out, rate=fs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05751159",
   "metadata": {},
   "outputs": [],
   "source": [
    "Audio(bart_out, rate=fs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3ed8503",
   "metadata": {},
   "outputs": [],
   "source": [
    "Audio(black_out, rate=fs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "215f1d81",
   "metadata": {},
   "source": [
    "***\n",
    "***\n",
    "## Παραδοτέα:\n",
    "*** \n",
    "### Μετονομάστε το **συμπληρωμένο και με εκτελεσμένο κάθε κελί κώδικα** notebook σε \n",
    "\n",
    "## *HY215-Lab2-2025-CSDXXXX.ipynb*, \n",
    "\n",
    "### με *XXXX* ο αριθμός μητρώου σας. \n",
    "\n",
    "### Αν είστε με προσωρινό κωδικό λόγω μεταγραφής, τότε ονομάστε το αρχείο σας \n",
    "\n",
    "## *HY215-Lab2-2025-mtgXXXX.ipynb*, \n",
    "\n",
    "### με *XXXX* ο προσωρινός αριθμός μητρώου σας.\n",
    "\n",
    "### Αν είστε από άλλο τμήμα εκτός του CSD, τότε ονομάστε το αρχείο σας ως\n",
    "\n",
    "## *HY215-Lab2-2025-DPTXXXX.ipynb*, \n",
    "\n",
    "### με DTP ένα εκ των {MATH, PHYS} και *XXXX* ο αριθμός μητρώου σας. \n",
    "\n",
    "### Στείλτε το μετονομασμένο notebook ως επισυναπτόμενο αρχείο με e-mail στο csd4641@csd.uoc.gr . \n",
    "\n",
    "### Στο e-mail θα αναγράφετε:\n",
    "\n",
    "### - Τίτλος: [ΗΥ215] 2ο εργαστήριο Python\n",
    "\n",
    "### και στο σώμα του e-mail τα παρακάτω: \n",
    "\n",
    "### - Ονοματεπώνυμο\n",
    "### - ΑΜ\n",
    "\n",
    "#### **Σημείωση**: μπορείτε να κάνετε επαναπαραδόσεις του εργαστηρίου - αν χρειαστεί. Χρησιμοποιήστε τίτλο\n",
    "\n",
    "#### [ΗΥ215] Επαναπαράδοση 2ου εργαστηρίου Python\n",
    "\n",
    "#### και κρατήστε τα ίδια στοιχεία στο σώμα του e-mail."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfe46515",
   "metadata": {},
   "source": [
    "### ΠΡΟΣΕΞΤΕ: Το παραδοτέο αρχείο σας πρέπει να έχει extension .ipynb, και ΟΧΙ κάτι άλλο!!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Hyfe_TF26",
   "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.9.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
