Letzte Änderungen: 12.03.2009

Zur Hauptseite kurs9


Das Tempo/PPQ/Darstellung von Zeit

Eine 1/4-Note ist bei 120 BPM 500 Millisekunden lang. Diese Formel muss man sich einfach merken, sie wird immer wieder benötigt.

Angenommen der Sequencer arbeitet intern mit 768PPQ (Pulses per Quarter) bedeutet dies:

Die Sequenceruhr läuft also intern mit Nachkommastellen um auch eine Feineinstellung des Tempos (z.B. 120,1300 BPM) zu ermöglichen - wer's brauch. Somit kann das Tempo auch während des Abspielens/Aufnahme des Songs intern (durch Tempoänderungsbefehle), durch den Benutzer oder durch externe Synchronisation geändert werden.

Die wichtige Formel für die Sequenceruhr ist also: Additionswert zur Sequenceruhr pro Pulse ist das momentanes Tempo/120.

Die MIDI-Events selber übernehmen nur den Integer-Wert der Sequenceruhr, somit entsteht eben das PPQ Raster - quasi bereits eine feine Quantisierung

Bei (z.B. 768 ppq) muss man vom Betriebssystem entweder einen Interrupt oder einen präzisen Timer zu Verfügung gestellt bekommen, der 1536 pro Sekunde aufgerufen werden kann. Dieser Timer muss äusserst genau und konstant sein, da ansonsten der Song "wackelt" oder auseinanderlaufen kann. Der Amiga bot mit seinem CIA Timer einen guten Timer-Chip. Hier kann eventuell ein Problem mit Multitasking-Betriebssystemen entstehen, falls diese irgendwie den Timer kurzzeitig anhalten (z.B. beim Festplatten-Zugriff). Das ist natürlich von OS zu OS unterschiedlich.

Hinweis: 384ppq sollten aber das Minimum darstellen

Diese PPQ Zahlen von 384 und 768 sind übrigens ein Vielfaches der festdefinierten MIDI-Clock von (24 ppq). Somit lässt sich später der Sequencer auch einfach extern synchronisieren, da sich alle Sequencer/MIDI-Geräte an diese PPQ Zahlen halten (müssen).


Die Quantisierung

Ein Sequencer sollte grundsätzlich alle "Effekte" (z.B. das Transponieren von Noten auf einem Track) in Echtzeit durchführen. Somit ist ein Sequencer also nondestruktiv, d.h. die ursprünglich eingespielten Midi-Events bleiben immer erhalten, es sei denn der Benutzer ändert die Daten ganz bewusst (destruktiv).

Die Quantisierung von MIDI-Events (normalerweise nur die Noten) ist eigentlich ganz simpel:

Quantisierung/Timestamp-Raster

Die Quantisierung betrifft denTimestamp-Wert (also der einzige Wert den alle Events gemeinsam haben) geändert. Mit Hilfe der Quantisierung "verschiebt" man diesen Timestamp-Wert nach links oder rechts - je nach Timestamp-Wert und dem vom Benutzer gewünschten Quantisierung-Wert.

Beispiel:

Eine Note wurde bei 120 BPM (Beats per minute) und 768ppq (768 pulses per quarter) mit einem Timestamp-Wert von 600 aufgenommen. Nun stellt der Benutzer für dieses Pattern einen Quantisierungswert von 1/8-Note ein, das bedeutet alle Timestamp der MIDI-Events werden exakt auf dieses Quantisierungsraster verschoben. Das "Verschieben" ändert natürlich nicht den ursprünglichen Timestamp-Wert sondern wird in Echtzeit bei der Berechnung der nächsten Alarmwerte errechnet. Dabei werden die Events exakt auf die Quantisierungsrasterpositionen verschoben. Der neue Timestamp-Wert für die jeweiligen Events richtet sich danach welchem Quantisierungspunkt das Event am nächsten liegt.

Die Berechnung der unterschiedlichen Raster ist eigentlich recht einfach (eine 1/4-Note ist bei 120BPM übrigens 500ms lang,also 1/4 Note=768 Pulse=500ms bei 120 BPM). Die Berechnung des nächsten Quantisierungspunktes für diesen Wert:

Die Rasterpunkte bei 1/8 Note (also 384ppq) Quantisierung sind: 0 (Der Songstart) -> 384 -> 768 -> 1152 ... immer + 384

Das Event befindet sich also zwischen den Quantisierungspositionen 384 (links Quantisierung )und 768(rechts Quantisierung) . Nun muss noch berechnet werden in welche Richtung die Position verschoben werden soll. Das berechnet man wie einfach wie folgt:

UQWORD
Quantize (UQWORD time,UQWORD quant)
{
UQWORD links, rechts;

time=600; // TimeStamp (in Pulses) des Event
quant=384; // 1/8

links = time / quant; // = 1 es handelt sich um Integerzahlen !
links *= quant; // = 384
rechts = links + quant; // 768

if ((time - links) <= (rechts - time)) // Berechnung auf welchen Punkt verschoben werden soll
return(links);
else
return(rechts);
}

Komplexere Quantisierungarten

Dies ist die einfachste Art der Quantisierung. Es gibt noch weitere Arten der Quantisierung bei denen diese beiden Quantisierungspunkte nicht so einfach errechnet werden, sondern über s.g. Groove-Muster oder Rhythmen "errechnet" werden, um in die Quantisierung eine gewollte Abweichung einzurechnen. Das macht man um der Quantisierung ein etwas menschlicheres Feeling zu verleihen, denn kein Mensch kann perfekt auf die Quantisierungspunkte einspielen.

Camouflage z.B. kann auch bestehenden MIDI-Files ein Quantisierungsmuster "filtern", um dieses Muster in eigene Songs einzubauen, das macht besonders bei Drumspuren Sinn - die Groovequantisierung. Mann könnte sogar aus MP3/CD-Songs ein Rhythmus filtern, um diesen dann in eigenen MIDI-Songs zu benutzen.

Ebenso gibts es die Rangequantisierung in der die Events nur quantisiert werden, wenn das Event in einem bestimmbaren Bereich der Quantisierungpunkte liegt.Man kann auch einen bestimmten Zufallswert in die Quantisierungspunkte einrechnen lassen, um ein gewisses Live-Feeling zu erzeugen. Die Notenlänge ändert sich durch eine Quantisierung nicht, ausser der Benutzer legt eine Quantisierung für die Endpunkte der Noten fest.


Die "Darstellung" von Zeit

Intern werden die Events alle also mit einem einfachen Pulse-Zähler (entweder 32-Bit oder 64-Bit) verwaltet und sortiert auf den Tracks/Pattern angeordnet. Beim Abspielen muss nun einfach nur ständig das nächste Event - also das Event, welches der aktuellen Sequenceruhr am nächsten liegt - "berechnet" werden. Das gelingt ganz einfach, indem man für jeden Track und für jedes Pattern eben einen Zeiger auf das "nächste" Event setzt, so dass der Abspielprocess einfach diese Zeiger durchgeht und überprüft ob diese Events gesendet werden müssen. Der Zeiger wird dann ggf. weitergesetzt, intern sind ja alle Events zeitlich sortiert.

Da der Benutzer nicht viel mit einem Pulse-Zähler anfangen kann, kann dieser Pulse-Zähler (also der Timestamp-Wert) in lesbare, verständliche Werte umgerechnet werden.

1. Die Darstellung im Taktformat (z.B. 2-2-1-3)

Diese Darstellung wandelt dem Timestamp-Wert in (Takt, Viertel, Rasterwert, Ticks/Pulse) um. Diese Darstellung ist am verständlichsten, da man einen Songs meistens sowieso in Taktbereiche unterteilt. Der Rasterwert (z.B. 1/16) dient zur weiteren Rasterung. Die übriggebliebenen Timestamp-Pulse werden dann eben als Ticks dargestellt-der kleinste Wert innerhalb des Sequencers.

Die Umwandlung von Timestamp-Werten erfolgt durch einfache Teiler.

Bei der Berechnung muss man jedoch Taktänderungen, die vor dem Timestamp-Wert liegen können (z.B. weil der Musiker von 4/4 auf 3/4 gewechselt ist) berücksichtigen, Tempoänderungen ändern natürlich keine Taktdarstellungen !

Beispiel: In einem Song mit der Taktart 4/4 und einem Raster von 1/16 bedeutet 2-2-1-3, 2. Takt (=4x4*768 Pulse)+2. Viertel(=1x 768 Pulse)+1. Rasterpunkt(=0 Pulse)+3 Ticks -> 3843 Pulse. Das Event besitzt einen Timestamp-Wert von 3843.

Das Tempo spielt übrigens bei dieser Darstellung keine Rolle.

2. Die Darstellung SMPTE oder Uhr-Format

SMPTE zeigt den Timestamp-Wert in <Stunden> <Minuten> <Sekunden> <Frames/Bilder> an.

Die Uhrzeit zeigt den Timestamp-Wert in <Stunden> <Minuten> <Sekunden> <Millisekunden> an.

Diese Darstellung wird für die Audio/Video-Synchronisation benötigt. Den Timestamp-Wert eines Events kann man ebenfalls einfach mit Teilern in diese Formate konvertieren - denn: Eine 1/4-Note ist bei 120 BPM 500 Millisekunden lang.

Bei der Berechnung muss man jedoch Tempoänderungen, die vor dem Timestamp-Wert liegen können (z.B. weil der Musiker von 120 BPM auf 130 BPM gewechselt ist) berücksichtigen, Taktwechsel ändern natürlich keine Uhrdarstellungen !

 

Am besten man programmiert erst einmal einfach Konvertierungsroutinen (einfach Teilerroutinen), die übrigens von alle Routinen gemeinsam benutzt werden können. Intern gilt das Zeitlineal ja für alle Events.