Obligatorisk innlevering uke 5

Frist for innlevering: 29.09. kl 23:59

Spørsmål eller kommentarer til oppgaveteksten sendes til ivargry@ifi.uio.no.

Denne uken skal vi jobbe med lyd i Python. I gruppetimen har vi sett at lyd egentlig bare er bølger, og at vi enkelt kan representere slike bølger ved hjelp av en liste i python (der hvert element representerer bølgehøyden på et gitt tidsrom).

For å gjøre denne innleveringen trenger du å ha numpy og simpleaudio installert på pc-en din (dette skal vi ha installert i gruppetimen).

NB: Det er viktig at du har fulgt undervisningsopplegget og gjort oppgavene knyttet til lyd der før du gjør denne obligatoriske oppgaven. Oppgavene her fortsetter på konseptene og koden som blir introdusert i undervisningen.

Prekode

Lag en fil lyd.py hvor du importerer numpy og simpleaudio slik:

import simpleaudio
import numpy as np

Alle oppgavene i denne obligen kan gjøres i filen lyd.py.

Som del av ukesoppgavene (og livekoding) har vi skrevet en funksjon for å spille av en tone med en gitt frekvens i et gitt antall sekunder:

Legg denne funksjonen, som vi kaller lag_tone i lyd.py:

def lag_tone(antall_sekunder, antall_svingninger_i_sekundet):
    lyd = []
    for i in range(int(44100 * antall_sekunder)):
        lyd.append(16000 * (1 + np.sin(antall_svingninger_i_sekundet * i/44100 * 2 * np.pi)))
    return lyd

Du trenger også funksjonen spill_lyd, som vi skrev på gruppetimen:

def spill_lyd(lydliste):
    lyd = np.array(lydliste).astype(np.int16)
    lydobjekt = simpleaudio.play_buffer(lyd, 1, 2, 44100)
    lydobjekt.wait_done()

Test at du får spilt av en tone (NB: det kan være lurt å ikke ha på for høyt volum første gang du prøver, i tilfelle lyden er høy/ubehagelig):

spill_lyd(lag_tone(3, 440))

Oppgave 1: Lese noter fra fil

Vi ønsker nå lage et lite program som kan spille musikk som er skrevet ved hjelp av tekst i en fil. Sangen vår ser slik ut (se under). Hver linje inneholder navnet på en note og varigheten til noten (hvor lenge den skal spilles).

Kopier innholdet her og lagre i en fil sang.txt:

E 0.3
- 0.1
E 0.3
- 0.1
E 0.7
- 0.1
E 0.3
- 0.1
E 0.3
- 0.1
E 0.7
- 0.1
E 0.3
- 0.1
G 0.3
- 0.1
C 0.5
- 0.1
D 0.2
E 1.5
- 0.1
F 0.3
- 0.1
F 0.3
- 0.1
F 0.5
- 0.1
F 0.2
F 0.3
- 0.1
E 0.3
- 0.1
E 0.3
- 0.1
E 0.1
- 0.1
E 0.1
- 0.1
E 0.3
- 0.1
D 0.3
- 0.1
D 0.3
- 0.1
E 0.3
- 0.1
D 0.7
- 0.1
G 0.8

PS: Pass på at du ikke får noen ekstra tomme linjer til slutt når du lagrer filen.

Hver note tilsvarer en frekvens (antall svingninger i sekundet). For eksempel tilsvarer A frekvensen 440.

Vi kan bruke følgende ordbok for å representere hvilke noter som tilsvarer hvilke frekvenser:

noter = {
    "A": 440,
    "G": 392,
    "F": 349,
    "E": 330,
    "D": 294,
    "B": 247,
    "C": 261,
    "-": 0
}

PS: Vi bruker "-" til å representere en stille tone (pause). Denne har frekvens 0, og vil ikke gi noen lyd.

Lag en funksjon les_sang_fra_fil som tar filnavn som parameter, leser sangen fra filen og returnerer en liste der hvert element i listen er en liste bestående av to elementer: frekvens og varighet. Denne listen som returneres vil representere sangen i filen på et format som vi kan bruke til å spille av sangen senere.

Test funksjonen les_sang_fra_fil på filen sang.txt. Print listen som returneres, og sjekk at den starter slik:

[[330, 0.3], [0, 0.1], [330, 0.3], [0, 0.1],  ...

Oppgave 2:

Lag en funksjon lag_sang_fra_noter som tar en liste av den typen les_sang_fra_fil returnerer som parameter, og returnerer en liste som kan sendes rett inn til spill_sang for å bli spilt av.

Test funksjonen med sangen du lagret i sang.txt, og spill av lyden som blir generert. Kjenner du igjen hvilken sang det er?

Oppgave 3:

Lag en funksjon fade_ut som tar en komplett lyd (en liste av typen som kan sendes inn til spill_lyd) og fader den ut (senker volumet gradvis).

Volum er rett og slett hvor høye verdier det er i lyden. Du kan derfor fade sangen ut ved å gange verdiene med gradvis lavere og lavere tall. Det er ulike måter å gjøre dette på. Prøv deg frem til du får noe du er fornøyd med.

Oppgave 4

Lag en funksjon forenkle_lyd som tar en lyd (en liste) og endrer hver verdi i lyden slik:

Test funksjonen på lyden generert fra sangen i oppgave 2. Før du hører på resultatet, gjett om du tror du kommer til å kjenne igjen sangen eller ikke.

Oppgave 5 (valgfri konkurranse)

Skriv koden for denne oppgaven i en fil med navn konkurranse.py og lever den filen for å delta i konkurransen.

Her finner du en fil med et lydsignal som inneholder hemmelig kode. Koden er vanskelig/umulig å høre pga støy og andre problemer med lyden. Last ned filen og legg den i samme mappe som konkurranse.py ligger. Prekoden under viser deg hvordan du spiller av lyden.

Konkurransen går ut på å skrive kode som endrer lyden slik at du klarer å høre hva den hemmelige koden er. Jo renere og penere lyd du klarer å få, jo bedre. Det er altså to mål i konkurransen:

  1. Få tak i den hemmelige koden
  2. Lag en ny lyd som er så ren og pen som mulig (lite støy og andre artifakter). Vi vil høre på de ulike bidragene i neste gruppetime og sammen kåre en vinner.

Start med følgende prekode prekode og følg instruksjonene i kommentarene:


import simpleaudio
import numpy as np
import matplotlib.pyplot as plt
import pickle

def spill_lyd(lydliste):
    lyd = np.array(lydliste).astype(np.int16)
    lydobjekt = simpleaudio.play_buffer(lyd, 1, 2, 44100)
    lydobjekt.wait_done()

def les_lyd_fra_fil():
    lyd = pickle.load(open("kode.pickle", "rb"))
    return lyd


hemmelig_kode = les_lyd_fra_fil()
# hemmelig_kode er nå en liste som inneholder lyden du skal jobbe med

# Start gjerne med å spille av hemmelig_kode for å se hva du har å jobbe med:
spill_lyd(hemmelig_kode)

# Det kan være lurt å plotte lyden for å se etter mulige problemer og ting du kan gjøre for å forbedre signalet
plt.plot(hemmelig_kode)
plt.show()

# tips: Det kan være lurt å zoome inn på deler av plottet for å studere det nærmere

def fiks_lyd(lyd):
    # Implementer ditt bidrag til konkurransen i denne funksjonen
    # denne funksjonen kan kalle andre funksjoner (om du vil dele opp koden din),
    # men den skal returnere en ny liste som er den korrigerte lyden
    # det er denne lyden du vil bli vurdert på
    pass


Et hint: Støy i lyd kommer ofte i form av enkeltverdier som er mye høyere eller lavere enn verdier i nærheten:

[1, 2, 3, 4, 1000, 5, 4, 3, 2, 1, 0, 1, 2, 3]

Her er typisk tallet 1000 støy, og lyden vil høres bedre ut om man hadde byttet ut 1000 med f. eks verdien før eller etter (eller gjennomsnittet/medianen av verdiene rundt).

Krav til innlevering

Hvordan levere oppgaven

Kommenter på følgende spørsmål i kommentarfeltet i Devilry. Spørsmålene skal besvares.

For å levere:

  1. Logg inn på Devilry.
  2. Lever alle .py-filene , og husk å svare på spørsmålene i kommentarfeltet.
  3. Husk å trykke lever/add delivery og sjekk deretter at innleveringen din er komplett. Du kan levere flere ganger, men alle filer må være med i hver innlevering.
  4. Den obligatoriske innleveringen er minimum av hva du bør ha programmert i løpet av en uke. Du finner flere oppgaver for denne uken på semestersiden.