Werkplaats / De werkplaats
In jouw eigen tempo
← Multimodaal & generatief
MODULE 03 · LES 09

Knip audio in overlappende frames

Verbind digitale samples met tijd en maak zelfstandige frames met een precies behandelde staart.

60 min1 checkpoints
DIT GA JE ONTDEKKEN
  • Je berekent sampletijden en onderscheidt vensterlengte, sprong en overlap.
  • Je maakt begrensde framekopieën en kiest bewust tussen een afgebroken staart en nulvulling.

Een spreker kan hetzelfde woord snel of langzaam uitspreken. Voor een computer begint dat geluid als een reeks getallen, zonder vakjes rond letters of klanken. Voordat je een model kunt laten leren, moet je weten welke tijd en welk stukje geluid bij ieder getal horen.

Stel je een doorlopende geluidslijn voor waarvan je op vaste momenten een foto neemt. Iedere foto levert één sample: een gemeten amplitude. In deze module gebruiken we zelfgemaakte toonachtige audio, zodat we de berekeningen kunnen controleren zonder opnamen of een grote spraakdataset.

We bouwen eerst de meetlat en de knipschaar. De meetlat verbindt sampleposities met tijd; de knipschaar maakt korte frames. Een frame is nog geen herkende klank en heeft ook geen automatisch vaststaande letter als label.

1. Geef samples een tijdschaal

De samplefrequentie f_s is het aantal samples per seconde, in hertz. Bij 8000 samples per seconde ligt er 1/8000 seconde tussen twee opeenvolgende meetpunten. We noemen die tijdsafstand Δt.

Δt=1fs
sample_rate = 8000
print(round(1/sample_rate,6))
# 0.000125

Sample nul krijgt tijd nul. Sample drie ligt drie van zulke stapjes verder, dus op 0,000375 seconde. Het laatste meetpunt van vier samples ligt daardoor eerder dan de eindgrens van het tijdvak waarin die vier samples zijn genomen.

times = np.arange(4)/sample_rate
print(times[0],times[-1])
# 0.0 0.000375

Een verwisseling van samplefrequentie verandert de interpretatie van het geluid. Dezelfde getallen afspelen alsof ze tweemaal zo snel zijn bemonsterd, maakt de tijdsduur half zo lang. Een samplefrequentie aanpassen is daarom iets anders dan het signaal werkelijk opnieuw bemonsteren.

De frequentie van een toon is weer een ander getal: die beschrijft hoeveel golfbewegingen er per seconde zijn. Verwar bijvoorbeeld een toon van 500 Hz niet met een samplefrequentie van 8000 Hz. De samplefrequentie bepaalt de tijdsmeetlat waarmee je die golf observeert.

VERKEN HET ZELFEen frame is een klein venster op de audio

Dezelfde dertien samples kunnen overlappende frames opleveren. Verander de vensterlengte en sprong; onderzoek apart wat er met de staart gebeurt.

-10104812Sample-index n
Aantal frames
6
Nulwaarden toegevoegd
1
Staart niet opgenomen
0
De waarden in frame 1; de oorspronkelijke audio blijft gelijk
Index nAmplitudeHerkomst
00Audio
10,25Audio
20,5Audio
30,75Audio

De staart is bedekt. Nulvulling voegt geen nieuwe gemeten audio toe. De frames overlappen: sommige samples worden meermaals gebruikt.

Een sample is één digitale amplitudewaarde. Padding gebruikt het minimale aantal frames dat de staart bedekt; ontbrekende waarden zijn nul. De grafiek toont sample-indexen, geen seconden.

2. Knip korte, eventueel overlappende frames

Een enkel sample zegt weinig over de frequentie-inhoud. Daarom bekijken we korte stukjes met M samples: frames. De hop h bepaalt hoeveel samples je verder schuift tussen twee opeenvolgende frames; bij h kleiner dan M overlappen ze.

Neem vijf oefenwaarden en frames van vier samples. Met hop twee begint frame nul op positie nul. Zonder opvullen past een tweede volledig frame niet meer: vanaf positie twee zijn slechts drie samples beschikbaar.

samples = np.array([0,.25,.5,.75,1])
frames = frame_audio(samples,4,2)
print(frames.tolist())
# [[0.0, 0.25, 0.5, 0.75]]

Voor N samples, N minstens M en alleen volledige frames, telt de volgende formule de startposities die nog passen. Het vloerteken betekent afronden naar beneden. De één telt het eerste frame mee.

K=1+⌊N−Mh⌋

Met pad_end=True behouden we ook de staart en vullen we ontbrekende samples met nul. Gebruik het minimale aantal frames dat die staart bedekt, met minstens één frame. In ons voorbeeld komt er één frame bij; we plakken geen extra volledig leeg frame achter het signaal.

frames = frame_audio(samples,4,2,True)
for row in frames:
    print(row.tolist())
# [0.0, 0.25, 0.5, 0.75]
# [0.5, 0.75, 1.0, 0.0]

Zonder padding geeft audio korter dan M een ValueError. Met padding mag datzelfde korte signaal één aangevuld frame vormen. Padding verzint geen gemeten geluid: de toegevoegde nullen zijn een bewuste rekenkeuze en kunnen latere kenmerken beïnvloeden.

Maak frame_audio met zelfstandige floatrijen. Behoud de oorspronkelijke samples en controleer vóór frameallocatie dat het aantal cellen maximaal 65.536 is. De overige oefengrenzen staan precies in de checkpoint; gebruik de gegeven validators in plaats van stilzwijgend waarden af te knippen.

01
CHECKPOINTKnip frames en behandel de staart

Wat neem je mee?

  • Samplefrequentie koppelt getallen aan tijd; toonfrequentie beschrijft een golf.
  • Framelengte en hop bepalen verschillende eigenschappen van de knipbewerking.
  • Padding behoudt de staart met expliciete nullen, zonder nieuwe meetinformatie.

Hierna maak je zichtbaar welke frequenties in zo'n frame aanwezig zijn.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-29.
DE VOLGENDE STAPMaak vermogens- en melkenmerken
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 1
01
Knip frames en behandel de staart

Schrijf frame_audio(samples,size=128,hop=128,pad_end=False). Geef een zelfstandige floatmatrix. Gebruik startposities j*hop. Zonder padding: alleen volledige frames, count=1+(N-size)//hop; een korter signaal geeft ValueError. Met padding: minimaal één frame en count=1+ceil(max(0,N-size)/hop); ontbrekende samples zijn nul. N is 1–16384, size is Python-int 4–512, hop Python-int 1–size, pad_end exact bool. Gebruik de gegeven arrayvalidator: reële eindige amplitudes binnen [-2,2], geen bool/complex. Weiger meer dan 65536 framecellen vóór allocatie. Behoud invoer; ongeldige invoer geeft ValueError.

Een hint

Werk eerst het handvoorbeeld uit; controleer daarna de vormen, grenzen en het behoud van de invoer.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

# Jouw functies

def frame_audio(samples,size=128,hop=128,pad_end=False):
    samples=_sp_array(samples,1);size=_sp_int(size,4,512);hop=_sp_int(hop,1,size)
    if len(samples)>16384 or type(pad_end) is not bool:raise ValueError('Begrensde audio en een expliciete bool vereist')
    if len(samples)<size and not pad_end:raise ValueError('Audio is korter dan één volledig frame')
    remaining=max(0,len(samples)-size)
    count=1+(remaining+hop-1)//hop if pad_end else 1+remaining//hop
    if count*size>65536:raise ValueError('Te veel framecellen vóór allocatie')
    frames=np.zeros((count,size))
    for index in range(count):
        part=samples[index*hop:index*hop+size];frames[index,:len(part)]=part
    return frames

# Gegeven helpers — eerdere methoden en invoergrenzen

def _sp_int(value,minimum,maximum):
    if type(value) is not int or not minimum<=value<=maximum:raise ValueError('Geheel getal buiten de oefengrens')
    return value

def _sp_array(value,dimensions,max_abs=2):
    raw=np.asarray(value)
    if raw.ndim!=dimensions or raw.dtype.kind not in 'iuf' or not raw.size or raw.size>65536:
        raise ValueError('Een niet-lege reële array met maximaal 65536 cellen vereist')
    result=raw.astype(float,copy=True)
    if not np.isfinite(result).all() or np.max(np.abs(result))>max_abs:raise ValueError('Eindige waarden binnen de oefengrens vereist')
    return result

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.