Bruke Python til å manipulere og spille av lyd

Læringsmål denne uken

Denne uken skal vi jobbe med lyd i Python. Vi skal se at lyd egentlig bare er bølger, og at vi enkelt kan representere slike bølger ved hjelp av en liste (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.

Installere bibliotekene vi trenger

Denne uken trenger vi noen biblioteker som ikke er standard i Python:

Linkene over inneholder instrukser for å installere disse bibliotekene, og det bør gå greit å installere disse både på Windows, Mac og Linux gitt at du har pip installert først.

Hvis du har Python3 installert slik at du vanligvis kjører python3 program.py i terminalen når du kjører Python-programmer, har du sannsynligvis også pip tilgjengelig, og kan installere en pakke slik:

# Installer simpleaudio ved å skrive dette i terminalen:
python3 -m pip install simpleaudio
# Tilsvarende for numpy:
python3 -m pip install numpy
# Tilsvarende for matplotlib:
python3 -m pip install matplotlib

NB: Noen ganger (kanskje bare på linux?) kan det være nødvendig med sudo først, altså sudo python3 -m pip install ...

NB 2: Hvis du kjører Python3 med kommandoen python, så må du bytte ut python3 med python i instruksene over.

NB 3: Hvis du er vant med å installere pakker via Conda, skal det gå fint som alternativ til pip.

Hvis installasjonene fungerer skal du kunne lage en python-fil med disse tre linjene og kjøre den uten å få noen feilmeldinger:

import numpy
import simpleaudio
import matplotlib

Hvis du står fast, send gjerne en e-post til ivargry@ifi.uio.no. Beskriv hva du har prøvd og hvor du står fast.

Kort introduksjon

Bruke simpleaudio i Python

Hvis du har fått til å installere simpleaudio og NumPy ved å følge instruksene over, er du klar for denne ukens opplegg.

For å gjøre ting enklere for oss, ønsker vi å ha en funksjon som kan ta en liste som parameter og spille av lyden som den listen representerer. Dette krever noen kall til numpy (for å omgjøre listen til bytes som maskinen kan spilles av) og til simpleaudio. Lag en fil lyd.py hvor du starter med denne koden:

import simpleaudio
import numpy as np

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

I kallet til simpleaudio.play_buffer spesifiserer vi at lyden vår har "sample-rate" 44100, som betyr at vi har 44100 verdier (samples) i sekundet. Simpleaudio vil da prosessere og spille av listen vi sender inn i en slik hastighet at det blir spilt av 44100 verdier i sekundet.

Teste ut simpleaudio med støy

Vi lager en funksjon tilfeldig_lyd(antall_sekunder) som lager en lyd som skal vare i antall_sekunder der hver lyd-verdi er et tilfeldig tall mellom 0 og 32000. Funksjonen skal lage returnere en liste som representerer lyden.

PS: En lyd som skal vare i et sekund må ha 44100 verdier, en lyd som skal vare i 2 sekunder må ha det dobbelte av det, osv.

# Legg denne linjen øverst i filen
from random import randint

def tilfeldig_lyd(antall_sekunder):
    lyd = []
    for i in range(44100 * antall_sekunder):
        lyd.append(randint(0, 32767))

    return lyd

spill_lyd(tilfeldig_lyd(4))

OBS! Det kan være lurt å skru ned volumet en del før man spiller av disse lydene.

Oppgave: Lag en funksjon som returnerer en lyd (liste) der de første 50 verdiene er 0, de neste 50 verdiene er 32000, de neste 50 er 0, osv ... Hvordan høres lyden ut?

Spille rene toner

Lyden vi lagde i forrige oppgave ser omtrent slik ut hvis vi hadde plottet den som en graf:

plott

Lyden er faktisk en ren A (i musikkverden). Hvis du spiller en A på en gitar, vil strengen vibrere 440 ganger i sekundet (440 bølgetopper/daler i sekundet). Det har lyden du lagde også.

Lyden du lagde høres derimot litt hakkete ut. Det er fordi det ikke er helt naturlige bølger (som du ser på plottet).

Oppgave: Lag en funksjon lyd_trekant(antall_sekunder) som returnerer en lyd som ser slik ut:

plott

Høres denne lyden bedre ut enn den "firkantede"?

Enda bedre lyd (perfekt sinus-kurve)

Den "trekantede" lyden var mer behagelig å høre på enn den firkantede. Lyd i naturen er stort sett mer lik naturlige bølger (som bølger på et hav), og har mye mykere svingninger:

plott

En slik lyd kan genereres slik:

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

Spille toner med en gitt frekvens

I koden over ganger vi en del tall med 440, og får dermed en lyd som svinger 440 ganger i sekundet (akkurat hvordan matematikken fungerer her trenger du ikke tenke på, annet enn at det magiske tallet 440 fører til det blir 440 bølgetopper/-bunner i sekundet).

Oppgave: Endre koden slik at funksjonen tar enda et parameter som er antall svingninger i sekundet og bruk det parameteret i stedet for 440. Denne funksjonen som tar to parametere vil du trenge i obligen denne uken.