Trackermodul-Engine (sehr einfach)

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Erstmal wieder ein Update:

Die Sache mit den Notenwerten in einem Pattern...
Ich würde optional für jedes im Track vorkommende Sample eine Tabelle für den Pitch anlegen, die auf
die Gesamttabelle verweist, aber nur wenn dadurch tatsächlich Speicher gespart wird. Die jeweilige
Tabelle würde angekündigt durch ihre Länge - wenn sich keine Ersparnis ergeben sollte hat sie die
Länge 0 und die Pitch-Werte im Pattern werden mit der Bitbreite geschrieben, die die gesamt-Tabelle
erfordert. Ansonsten wird über die Tabelle für den Track auf die gesamt-Tabelle indiziert.
Versteht vielleicht jetzt keiner, aber für mich ist das mal gut, festzuhalten.


Irgendwie empfinde ich DosBox als monolithisch. Es hängt nur von der Version ab, welche Features es gibt.
Und beim Programmieren ganz praktisch dass man bei fatalen Abstürzen nicht neu booten muss.
VIelleicht ein bisschen blöd in und für einen Emulator zu programmieren, aber immerhin läuft es auch
auf echter Hardware, und so sehe ich eigentlich keinen Frevel darin, in DosBox zu programmieren...
Zumindest in meinem Rahmen, wo das Programmieren vielleicht CPU nah ist, aber nicht allzusehr
hardwarespezifisch.


Gerne schreibe ich hier mal die Formatbeschreibung rein, bin ja schon bald fertig in Gedanken.


Ursprünglich wollte ich auch den "Speed-Adder" für jedes Sample-Abspielen neu berechnen.
Könnte ich natürlich auch machen. Andererseits sind die Tabellen pro Sample nicht allzu lang.
Schlagzeug-Samples werden nur einen Tabelleneintrag haben. Durchschnittliche Melodie-
Samples vielleicht 10. Der Gedanke kam ja daher, dass, wenn ich wie du eine Frequenz-
tabelle mit 96 Einträge hätte, diese immer da ist und Speicherplatz belegt, egal ob
diese Noten überhaupt gespielt werden.
Aber eine Tabelle pro Sample wollte ich sowieso machen. Um in den Patterns Bits zu sparen.
Dann kann ich aber eben direkt in diese Tabellen pro Sample die "Speed-Adder" reinmachen.
16 Bit Vorkomma finde ich irgendwie unnütz, kein Sample geht mit fünfstelligen Sprüngen
in der Mixroutine voran. Es würden wahrscheinlich 4 oder 5 Bit Vorkomma genügen, und
dann muss ich sehen ob 12 oder 11 Bit Nachkomma genügen. Wie auch immer man das
im Player dann rechnet, vermutlich mit Bitshifting. Oder einfach 8 Bit Vorkomma, 16 Bit Nachkomma.

Pitching ohne Längenänderung geht im einfachsten Fall indem man das Sample in kleine Fenster
unterteilt, die man wiederholt abspielt und dabei in sich überblendet... Hört sich aber nicht
so toll an.


Mittelweg: Wenn das was ich hier alles schreibe, also die Patternkodierung, am Ende schnell und ohne
zu viel Programmoverhead funktioniert, dann ist doch alles gut und wunderbar. Bis jetzt denke ich ja
auch, dass der Player, also die Mischroutinen, aufgrund der Einfachheit des Formats, trotz der 8 Kanäle,
schneller ist als einige bekannte MOD-Player. Wird sich zeigen.


Schleife:
Hmm...? Bei mir würden alle Samples des Moduls im Speicher liegen.
Vielleicht führt es zu weit, wenn ich dich frage, wie deine Mix-Routine funktioniert.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

So, hier mal ne Formatbeschreibung, vielleicht etwas lückenhaft, aber im Prinzip sollte alles drin sein.
Auf die Sache mit der weiteren "Verschachtelung" der Samples beim Pitchen hab ich mal zugunsten
weniger Frickelei und womöglich besserer Geschwindigkeit beim Pattern-Header lesen verzichtet.



Header(DWORD): ZSTM

Anzahl Samples: Byte

Sequencer-Einträge: WORD


Samples
-------

je Sample:

WORD: Länge
WORD: Schleifenanfang (Schleifenende ist immer auch Sample-Ende)
Ist Schleifenanfang = Länge, dann gibt es keine Schleife

Pitch-Tabelle:
Nur so viel Einträge, auf wieviel verschiedenen Tonhöhen das
Sample auch gespielt wird.
BYTE: Anzahl der Einträge
XX BYTE, WORD: Tabelleneinträge Vorkomma, Nachkomma Pitch



Sequencer
---------

WORDS:
Direkter Offset in die Patterndaten, die auf 64KB begrenzt sind.


Patternstruktur:

Header
------
WORD: Patterngröße in Byte (eigentlich nicht notwendig)
BYTE: Anzahl Zeilen - 1, da 0 keinen Sinn ergibt, 256 aber wohl

BYTE: Zeilen pro Viertelnote
BYTE: BPM
(Anstelle dieser beiden Werte könnte man, da es auf 16kHz Mixfrequenz
festgelegt sein soll, auch direkt die Anzahl Byte hinterlegen, die der Player
pro Zeile im Soundpuffer füllen soll)

BYTE: Flags, welche der 8 Tracks aktiv sind (welche nicht leer sind)

Informationen
-------------

pro Track:


Sample-Nummer-Tabelle:
Die Anzahl der gespeicherten Samples bzw. das letzte Sample
bestimmt die Bitbreite dieser Tabelle.
X BIT: Länge der Tabelle
X BIT x Länge der Tabelle: Sample-Nummern

Hat die Tabelle keine Einträge, so ist dies deshalb, weil
die Kompression nicht effektiv wäre. In diesem Fall werden
im Track die Samplenummern direkt Indiziert, mit entsprechender
Bitbreite.


Volume-Tabelle:
3 BIT: Länge der Tabelle - 1
3 BIT x Länge der Tabelle: Volume-Werte


Patternzeile
------------

X Bit: Flags, auf welchem Track ein Ereignis stattfindet.
Es werden nur die Tracks berücksichtigt, die im Header als
aktiv geflagt wurden.

pro geflagtem Ereignis:

Samplenummer: X BIT
Hat die entsprechende Tabelle genau einen Eintrag, gibt es hier
keine Daten, sprich 0 Bit. Hat die Tabelle zwei Einträge, ist hier
1 Bit zu lesen. Hat die Tabelle drei Einträge, 2 Bit usw.
Hat die Tabelle gar keinen Eintrag, sind hier so viele Bits
zu lesen, wie die Indizierung in die insgesamt geladenen Samples
erfordert.
Eine Anmerkung: Samplenummer 0 wird der Player so umsetzen,
dass er bei Änderung der Note in diesen anderen Ton hineingleitet,
ohne das Sample von vorn zu starten.


Volume: X BIT
Länge der Tabelle von 1: Nichts zu lesen, es gilt der eine Eintrag
der Tabelle als Laustärkewert.
Länge der Tabelle von 2: 1 Bit zu lesen, es gilt der entsprechend
in der Tabelle indizierte Wert als Lautstärke. Usw...


Note/Pitch: X BIT
Indizierung in die entsprechende Tabelle des durch Samplenummer bestimmten
Samples, mit so viel Bit wie die Pitch Tabelle des Samples es erfordert,
bei nur einem Eintrag nichts zu lesen.


Zum Abschluss der Zeile:
1 BIT:
0 = keine Leerzeile folgt
1 = es folgen Leerzeilen, Anzahl definiert mit 3 Bit (-1, 0 macht keinen Sinn)

Folgen mehr als 8 Leerzeilen, so ergeben sich eine ganze Zeile 0-Flags und wieder Leerzeilen.
Das scheint mir der beste Kompromiss zu sein, nur weil vielleicht ein paar mal ein Pattern
mit 32 Leerzeilen kommen sollte möchte ich keine 5 Bit ranhängen, von denen im Schnitt
nur 1-2 genutzt werden.


Sampledaten
-----------

Einfach alle am Stück. 8 Bit Signed.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1145
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von DOSferatu »

zatzen hat geschrieben:Die Sache mit den Notenwerten in einem Pattern...
Ja, wir gehen ja von unterschiedlichen Ansätzen aus:
Dein ZSTM basiert auf Samples.
Mein ISM basiert auf digitale Klangsynthese - Samples sind bei mir die "optionale Zugabe".
Und bei mir gehört die Frequenztabelle der Noten nicht in das Musikfile, sondern sind inhärenter Bestandteil des Players.
Und für 96 Word-Werte brauche ich 192 Bytes - damit kann ich leben.
(Anm.: Die Tabelle wird trotzdem berechnet. Man kann (als Programmierer, der den Player nutzt) angeben, welche Frequenz der Kammerton A haben soll (davon ausgehend werden die anderen Noten angepaßt). - Standardmäßig hat er natürlich die Frequenz 440 Hz. (Es gibt Orchester, die absichtlich leicht abweichende Frequenzen nutzen - hatte da mal im Netz recherchiert. Wußte das vorher auch nicht.)
zatzen hat geschrieben:Irgendwie empfinde ich DosBox als monolithisch. Es hängt nur von der Version ab, welche Features es gibt.
Naja, es ist ein Emulator - und das IST natürlich gewissermaßen monolithisch. Andererseits aber auch wieder nicht. Man kann ja die Geschwindigkeit regeln (und es regelt sie selbst dynamisch nach), man kann einige Grafikkartentypen einstellen (supportet übrigens nicht alle Features, die meine reale ET4000 hat), die Soundkarte, die Tastenbelegung... Und wenn man es SO HERUM betrachtet, ist es eigentlich das am wenigsten monolithische Ding, das ein "PC" sein kann.

Mit monolithisch meinte ich nicht die "Stabilität" gegen Abstürze - sondern die Unveränderlichkeit und "Singularität" eines (DES) Monolithen - die bei Konsolen auftritt. Jede Konsole des gleichen Typs ist auf genau eine Geschwindigkeit getaktet (bzw. wenn nicht, dann bei allen nicht), hat genau die eine CPU, den einen Grafikchip, Soundchip und andere Hardware eingebaut. Dadurch braucht ein Programmierer da nichts abzufragen und keine bestimmten "Unregelmäßigkeiten" berücksichtigen, sondern kann (bzw. könnte) so hardwarenah wie er will programmieren - weil die Hardware unveränderlich festgelegt ist. Sonst würden ja nicht alle Spiele der Konsole laufen - und das gäbe einen Aufschrei der Empörung der Käufer.
Bei einem PC dagegen ist man von Anfang angewöhnt, daß er ein Hardwarepuzzle ist, er war schon von vornherein so ausgelegt. Daher beschwert sich auch niemand (außer sehr dumme Leute), wenn auf seinem PC/seiner Hardwarekonfiguration mal etwas nicht oder nicht so richtig läuft. Weil natürlich jeder einsieht (und einsehen muß), daß ein Programmierer (vor allem ein Spieleprogrammierer, der gewissen Wert auf Effizienz legen muß - außer er will nur ganz simple Spiele machen) nicht JEDEN seltsamen Schnickschnack, der in einem PC eingebaut sein kann, berücksichtigen kann.

Schließlich kann kein Entwickler alle Motherboards und alle mögliche Peripherie in jeder Kombination testen. Außerdem gibt es immer neue - zwar beim PC abwärtskompatible - CPU-Generationen. Berücksichtigt man hier nur die 8086, dann laufen zwar alle Programme, aber man ignoriert natürlich alle neueren Features späterer CPU-Generationen.

Und wenn die Entwickler von Betriebssystemen sich irgendwann auf einen "Mindeststandard" einigen und alles was darunter ist, nicht mehr unterstützen, dann braucht man selbst mindestens diese Hardwarekonfiguration, um das Betriebssystem nutzen zu können. Hat man diese nicht, dann muß man entweder ein anderes OS nehmen, selbst ein OS schreiben oder ohne OS "auf die blanke Maschine" coden. Die Hardware-Abwärtskompatibilität hat es jahre-/jahrzehntelang ermöglicht, daß auch auf neueren OS/Maschinen Programme für ältere Konfigurationen liefen.
(Seit dem "Sprung" auf 64bit (bzw den "nativen 64bit Mode) ist dies nicht mehr gegeben. 16bit wird in diesem Mode ausgeklammert.)
zatzen hat geschrieben:Und beim Programmieren ganz praktisch dass man bei fatalen Abstürzen nicht neu booten muss.
Das stimmt. Andererseits bootet mein 486er in ca. 20 Sekunden, Windows braucht - selbst beim Hochfahren aus dem Ruhezustand, und obwohl es auf 'ner wesentlich schnelleren Kiste läuft - dafür schon länger...

Mir ist es natürlich egal und recht, wenn jemand in DOSbox programmiert. Für mich wäre nur wichtig, daß am Ende auch immer der Gegentest auf der realen Hardware steht. Wenn es nur auf einem Emulator läuft, auf der realen Hardware nicht, dann hat man irgend etwas falsch gemacht. Grund ist: Der Emulator ist Software, die geändert werden kann und die NACH der Hardwarevorlage kam. Somit kann der Emulator immer weiter an die reale Hardware "angeglichen" werden - andersherum aber nicht. D.h. wenn man für "alte DOS-Rechner" programmiert, muß man "mit dem klarkommen, was da ist". Jedes zusätzliche Feature, die ein Emulator bietet, die reale Maschine aber nicht hat, ist so gesehen eigentlich schon ein Fehler.
zatzen hat geschrieben:VIelleicht ein bisschen blöd in und für einen Emulator zu programmieren, aber immerhin läuft es auch
auf echter Hardware, und so sehe ich eigentlich keinen Frevel darin, in DosBox zu programmieren...
Solange es auf der echten Hardware läuft, ist das alles legitim. Ich bin mir fast sicher, daß selbst die letzten Spiele (und vor allem Demos) für den C64 auch schon "crosscompiliert" wurden.
zatzen hat geschrieben:Zumindest in meinem Rahmen, wo das Programmieren vielleicht CPU nah ist, aber nicht allzusehr hardwarespezifisch.
Ich versuche auch immer nur dieses. Allzu hardwarespezifisch programmiere ich nur so kleine private Tests (wie z.B. Dinge, die die ET4000 kann usw.). Aber da das Endziel bei mir ja Spiele sind - und Spiele auch von anderen Leuten gespielt werden sollten als nur von mir (so jedenfalls der Plan), versuche ich hier immer, das "kleinste gemeinsame Vielfache" zu finden.

Allerdings bin ich im Assembler-Bereich inzwischen beim 386er "angekommen", d.h. ich setze oft schon statt wie früher 286er jetzt bei meinen größeren Projekten einen 386er als Mindeststandard voraus. 286er und darunter haben keine FS und GS Segmentregister und keine 32-Bit-Erweiterung der Standardregister. Hauptsächlich diese Sachen - neben den oft recht nützlichen zusätzlichen Befehlen, die es ab 386er gibt - haben mich irgendwann zu einer gewissen Dekadenz veranlaßt, diese Sachen auch zu benutzen. Natürlich mit dem Nachteil, daß auf Maschinen unter 386er die entsprechenden Dinge nicht laufen. Aber mit dem Vorteil, daß die erreichte Performance ab 386er wesentlich erhöht werden kann.
zatzen hat geschrieben:Gerne schreibe ich hier mal die Formatbeschreibung rein, bin ja schon bald fertig in Gedanken.
Ja, ich habe mich leider noch nicht ganz auf ein endgültiges Format festgelegt. Das Format im Computer/im Speicher ist klar. Aber das Format, in dem ich die Musikfiles abspeichere, ist noch "STC" (Subject-To-Change). Ich bin da einfach noch nicht so ganz zufrieden damit.
Früher war ich auch bei Files immer der totale "Bitfrickler", habe jeden kleinen Rest vollgestopft. Inzwischen versuche ich hier eine Balance zwischen zu komplizierten Daten (und damit komplizierter Lese-/Schreib-/Anzeige-/Abspielroutinen) zu finden und anderseits Performance. Bei manchen Dingen lasse ich absichtlich mal ein Bit oder Byte aus oder frei.
Das hat 3 Gründe:
1. Die entsprechenden Routinen können einfacher sein. (Ich kann auch komplexe Routinen bauen - diese brauchen aber auch mehr RAM.)
2. Absichtlich freigelassene (oder nicht ganz bis zur maximalen Reichweite ausgenutzte) Bits, Bytes, Words usw, bei denen die "übrigen" Werte dann =0 gesetzt werden müssen, lassen Spielraum für spätere Erweiterungen und neue, abwärtskompatible Versionen, die kein komplettes Neuschreiben der Lese-/Schreib-/Anzeige-/Abspielroutinen erfordern.
3. Es dient etwas der Sicherheit (im Sinne von Datenkonsistenz). Wird jedes Bit bis zum Ende befüllt, gibt es quasi keine "falschen Daten" mehr - d.h. jede Byte-(und Bit-)kombination in einem File kann prinzipiell als "gültige Daten" funktionieren. Somit bräuchte man nur ein Textfile oder Bild nehmen, die entsprechende Header-ID davorsetzen und die Filenamen-Erweiterung anpassen und hätte damit ein in sich "gültiges" - aber natürlich nicht wirklich funktionierendes - File geschaffen.
Dagegen Werte, die gültige Reichweiten überschreiten oder enthalten, können aber gut zur Prüfung fungieren. Beispielsweise kann ein File mit einer neuen Version (das neue Features enthält) nicht einfach einer alten Lese-/Schreib-/Anzeige/Abspielroutine vorgesetzt werden, die diese Version nicht kennt. Aber umgekehrt kann ein File einer alten Version so durchaus Routinen einer neuen Version angeboten werden, die dann neue und alte Version nutzen kann. NATÜRLICH kann so etwas auch einfach mit einem "Versionsfeld" im File geregelt werden. War aber die alte Version nicht ausreichend "erweiterbar", so muß ihr Format für neue Features inkompatibel verändert werden, so daß Routinen für neue Versionen, die abwärtskompativbel zu alten Versionen bleiben sollen, dann vollständig Code für beide/mehrere Formate enthalten muß, der dann je nach Version angesprungen wird. War dagegen die alte Version an einigen Stellen "genullt" bzw. "nicht bis zum Ende ausgenutzt", so braucht eine neue Routine quasi nicht vollständig abgeändert werden, sondern kann einfach ganz normal für neue Versionen programmiert werden - Features, die in alten Versionen nicht vorhanden waren, machen so den neuen Routinen keine Probleme - sie sind ja so quasi nur "ausgeschaltet"/ungenutzt.

Ich könnte mir bei manchen meiner alten Formate, die so unerweiterbar waren, manchmal heute noch in den Hintern treten, weil ich dadurch in ihrer Nutzung so unflexibel bin. Ich freue mich dagegen jedesmal, wenn ich in meinen alten Formaten "ungenutzte" Bereiche finde, die ein einfaches Upgrade bei Erhaltung der Abwärtskompatibilität möglich machen.

Ich weiß nicht, ob ich das zu abstrakt erklärt habe.
zatzen hat geschrieben:Ursprünglich wollte ich auch den "Speed-Adder" für jedes Sample-Abspielen neu berechnen.
Könnte ich natürlich auch machen. Andererseits sind die Tabellen pro Sample nicht allzu lang.
Naja, ich hatte von Anfang an schon vor, Sounddaten für frei wählbare Abspielfrequenzen zu generieren;
hauptsächlich aus Hardwaregründen. Ist z.B. der Rechner zu langsam, kann man die Frequenz heruntersetzen.
Dadurch hat man zwar niedrigere Klangqualität - aber immer noch besser, als den Sound ganz abschalten zu
müssen oder mit stotterndem Sound leben zu müssen (was natürlich auch alles andere Stottern ließe - Grafik,
Steuerung... - je nachdem wie es programmiert ist).
Außerdem supporte ich ja auch immer PC-Speaker (im "digitalen Mode") mit. Aber der Speaker klingt eigentlich
am besten bei so 12 bis 16 kHz. Darunter rauscht es schon, darüber ist die mögliche Amplitude zu klein
(bedingt dadurch, wie der Trick funktioniert, mit dem man den "digital" kriegt) - was dann zwar immer saubereren,
aber leider auch immer leiseren Sound zur Folge hat. Weil man für hohe Frequenzen aber auch den Timer-Ticker
hochsetzen muß (was auf langsamen Maschinen aber schon zum Overkill ausarten kann), sollen auch niedrigere
Frequenzen möglich sein.
Andererseits will ich natürlich jemandem, der sich extra eine 100 MHz Mördermaschine mit einer Soundblaster 2.0
eingebaut hat, nicht von seinem 44 KHz Klangerlebnis abbringen, auch wenn natürlich die Art, wie ich Sound
erzeuge - 4bit pro Stimme usw. - wahrscheinlich nicht unbedingt 44 kHz rechtfertigt. Aber diese Entscheidung
will ich dem Nutzer überlassen.

Ich weiß, in mancher Hinsicht widerspreche ich damit teilweise dem "wenig Speicher, viel Performance" -
Paradigma, das ich mir selbst auferlegt habe. Ich tue dies aber rückwirkend aufgrund schlechter Erfahrungen,
die ich gemacht habe. Es gibt heute immer noch Code, den ich mal vor Jahren/Jahrzehnten geschrieben habe,
und den ich immer noch weil er eben nützlich ist. Aber weil ich damals nicht an mögliche Erweiterbarkeit
gedacht habe und das ganze sich zu festen "Codeblocks" verbacken hat, die ich am liebsten gar nicht mehr
anfassen will, solange sie ihren Dienst tun, bin ich nun auf manches von dem Zeug angewiesen - auch wenn
ich heute vieles von dem gern anders machen würde. Aber ich werde einfach nie fertig: Immer, wenn ich an
einem Ende etwas neues geschaffen habe, kommen mir manche anderen Codes schon alt und überholt vor.

Beispiel: Ich habe ja diese Steuerroutinen (GS2) und jetzt diesen Player (ISM). Das Grafische wird von meinem
Arcade01 abgedeckt - Meine Level-/Sprite-Routinen, die so viele Features haben und viele "konsolenmäßige"
Dinge ermöglichen. Aber die sind von 2004, also knapp 12 Jahre alt. Inzwischen würde ich da einiges anders
machen, den Anteil an ASM erhöhen usw... - Aber dann fange ich ja wieder von vorne an! Und wenn ich damit
fertig wäre, käme mir als nächstes wohl wieder meine IGBIOS, dann die GS2 und dann wieder die ISM veraltet
vor und die müßte ich dann auch schon wieder neu machen...
Alles schön und gut - irgendwann hat man vielleicht die besten Routinen des Jahrhunderts da - aber immer noch
kein neues Spiel...
Und in letzter Zeit programmiere ich schon wieder seit Wochen gar nicht mehr und seit Monaten nichts mehr
richtiges (also nur so Kleinkram) - weil ständig müde und kaputt.
Und selbst wenn ich mit der Programmierung mal "fertig werde" (oder sagen wir: mal hinlänglich zufrieden mit
dem Zeug bin, um es als einigermaßen brauchbar anzusehen), wäre es immer noch kein Spiel! Denn dann fängt
die Arbeit am Spiel ja erst an: Grafiken zeichnen/pixeln, Sounds/Musiken komponieren/programmieren, Steuerung
und Verhalten definieren... -
und, ach! Ein Menüsystem braucht's ja auch noch... Eigentlich ja nicht, kann man ja auch alles "hardcoden"...
aber das habe ich auch bei Xpyderz gedacht - und im Laufe der Zeit brauchte ich immer mehr Features und das
als "einfach und so nebenher" gedachte Menüsystem hat sich zu einem riesigen Monster (aus Spaghetticode)
entwickelt... Das könnte ich nirgends mehr einsetzen, wenn ich das in ein neues Spiel einbaue, hab ich keinen
Platz mehr für Grafik, Sound und anderes Zeug...

Ja - die Sorgen eines (ehemaligen) Programmierers - - - der heute genug weiß, um ein ziemlich eindrucksvolles
(DOS-)Spiel zu bauen, aber keine Zeit und Kraft mehr hat. Und zu den Zeiten, als Zeit und Kraft nicht das Problem
waren, war es die damalige Stümperhaftigkeit. Da habe ich mehr Spiele gebaut - die aber schon damals kaum was hermachten...
zatzen hat geschrieben:Schlagzeug-Samples werden nur einen Tabelleneintrag haben. Durchschnittliche Melodie-
Samples vielleicht 10. Der Gedanke kam ja daher, dass, wenn ich wie du eine Frequenz-
tabelle mit 96 Einträge hätte, diese immer da ist und Speicherplatz belegt, egal ob
diese Noten überhaupt gespielt werden.
Aber eine Tabelle pro Sample wollte ich sowieso machen. Um in den Patterns Bits zu sparen.
Dann kann ich aber eben direkt in diese Tabellen pro Sample die "Speed-Adder" reinmachen.
Ja, na wie gesagt, "feststehende" 192 Bytes lassen mir bei PC-Programmierung keine grauen Haare wachsen -
selbst wenn ich nur den Heap benutze,ist das für mich vertretbar.
Ein Beispiel: Nehmen wir an, ich würde 10 verschiedene Samples nutzen und jedes hat eine Notenreichweite
von 10 Tönen. Dann hätte ich (wenn ich WORDS nutzen würde), auch 200 sample-intern gespeicherte Words,
nur daß ich diese dann einzeln und "indirekt" aus den Samples "rauskratzen" würde, wenn ich sie damit
abspielen würde, während ich bei einer fixen Frequenztabelle immer "wüßte", wo die liegt.

Du hast natürlich damit Recht: Es entfiele die Anpassung (Berechnung) durch den Player, weil die "Adder"
schon außerhalb der Abspielroutinen (schon beim Erstellen des Sounddatenfiles) für die Samples berechnet
werden würden. Aber da liegt eben der Unterschied zwischen unseren Vorgehensweisen:
1. Ich will nicht nur Samples, sondern vor allem digitale Klangsynthese (und Samples nur als "zweite Wahl").
2. Ich will flexible Abspielfrequenzen - d.h. dafür müßte ich sowieso die Adder berechnen.

Wieso ich den Kram so "offen" und "flexibel" haben will, liegt daran, daß es ja nicht ein alleinstehender
Musikplayer werden soll, sondern in Spielen für die BGM und SFX genutzt werden soll - und daß der Plan vorsieht,
nicht nur einfach fortlaufende Musiken als Untermalung abzuspielen, sondern auch abhängig vom Spielgeschehen
Soundeffekte zu generieren (die nicht ausschließlich fest abgespielte Samples sein müssen) und sogar im
besten Fall sogar in die Musik vom Spiel aus "einzugreifen". (Ich erinnere mich an diese einzigartig klassische
Idee von Monkey Island 2, in Woodtick (diesem Dorf aus Schiffswracks), wo sich die Musik an jeder Lokation
geändert hat, aber nicht abrupt von einer zur anderen gewechselt ist, sondern mit Übergängen fließend
ineinander wechselte, mit gleichem "Grundtakt", um quasi "den Flow" beizubehalten...)
"Kommunikation" zwischen Grafik, Sound, Steuerung - das sind alles Dinge, die ich mir für ein Spiel wünsche.
Dinge wie diese erklären vielleicht auch, wieso ich keinen "Game-Maker" bauen will. Es wäre für den Nutzer
dann viel zu umständlich und würde interne Kenntnisse der Sachen erfordern, um solche Dinge umzusetzen -
da kann er auch gleich richtig programmieren, anstatt extra eine zusätzliche "Pseudosprache" zu erlernen,
mit der er außerhalb des Makers quasi nichts anfangen kann...
zatzen hat geschrieben:16 Bit Vorkomma finde ich irgendwie unnütz, kein Sample geht mit fünfstelligen Sprüngen
in der Mixroutine voran. Es würden wahrscheinlich 4 oder 5 Bit Vorkomma genügen, und
dann muss ich sehen ob 12 oder 11 Bit Nachkomma genügen. Wie auch immer man das
im Player dann rechnet, vermutlich mit Bitshifting. Oder einfach 8 Bit Vorkomma, 16 Bit Nachkomma.
Das stimmt. Aber ich berechne die Adder ja sowieso und nutze "verdrehte" 32bit-Register zur Ausgabe.
Verdreht = ich habe Vorkomma und Nachkomma vertauscht.
Ich weiß nicht, ob das jemand anders auch schonmal so gemacht hat - vielleicht sollte ich mir die Idee
patentieren lassen... (jk).
Erklärung: ADDER ist mit vertauschtem Vorkomma/Nachkomma. (Ich nehme EBP, weil... naja, ich nutze alle Register die da sind... Und BP bzw EBP war halt noch frei. SI,DI und BX brauch ich anderweitig...)
Ich addiere so: add EBP,ADDER;adc BP,0;
Wieso mach ich das? Naja, BP sind die unteren 16bit von EBP. Auf die oberen 16 kann man nicht einzeln zugreifen. So hab ich zwar 2 Addierbefehle, aber in BP steht nun automatisch immer gleich die Position im Sample (oder in der Welle).
Der zweite Addierbefehl dient nur dazu, den eventuellen Übertrag an der Grenze zwischen Bit15 und Bit16 durchzuführen (denn eigentlich muß man sich diese 32bit ja andersrum denken).
Was passiert, wenn die unteren 16bit von EBP vom ersten Addierbefehl einen Überlauf kriegen?
Garnix! Samples dürfen bei mir nicht >64 kB werden und auf Erreichen der Länge wird sowieso immer geprüft.
Bei Wellen ist es noch einfacher. Die enden ja gar nicht, sondern wrappen bei 32. Also kommt danach für Wellen noch AND BP,31
(Ja, die Wellen sind intern eigentlich auch Samples.)
zatzen hat geschrieben:Pitching ohne Längenänderung geht im einfachsten Fall indem man das Sample in kleine Fenster
unterteilt, die man wiederholt abspielt und dabei in sich überblendet...
Hatte mir so etwas schon gedacht.
zatzen hat geschrieben:Hört sich aber nicht so toll an.
Und genau das hatte ich befürchtet, daher lasse ich es sein. Denn diesen "einfachsten Fall" würde ich vielleicht hinkriegen, aber es würde "brummen" (in der "Fenster-Frequenz") und den "komplizierten Fall" (also so wie es die professionellen Leute machen, die sowas angleichen), aber a) würd ich das in ASM wohl schlecht hinkriegen b) verstehe ich die Formeln dafür schon quasi gar nicht und c) selbst wenn ich es umsetzen könnte, wäre es wohl der reinste Performancekiller, d.h. schätzungsweise 80% der Performance, die ISM braucht, wäre dann für das Sample-Pitchen...

Übrigens, ein Vorteil von Klangsynthese gegenüber Samples ist: kein Pitchen nötig...
zatzen hat geschrieben:Mittelweg: Wenn das was ich hier alles schreibe, also die Patternkodierung, am Ende schnell und ohne
zu viel Programmoverhead funktioniert, dann ist doch alles gut und wunderbar. Bis jetzt denke ich ja
auch, dass der Player, also die Mischroutinen, aufgrund der Einfachheit des Formats, trotz der 8 Kanäle,
schneller ist als einige bekannte MOD-Player. Wird sich zeigen.
Ja. Bewiesen ist, was auf der Maschine funktioniert.
Auf meinem 486er (DX4, 133 MHz) braucht ISM für das Generieren des 4-stimmigen Stücks (Ente und so), was ich mal geschickt habe, für 1 kB Sounddaten im Schnitt geringfügig mehr als 1 Millisekunde (und das, wo nebenher der 14 kHz-Ticker-IRQ läuft, für Abspielen per PC-Speaker, d.h. er wird auch noch 14000x pro Sekunde bei der Arbeit unterbrochen.)
Hochgerechnet bedeutet das, daß er für alle 16 Stimmen 4 Millisekunden für das Füllen von 1kB Soundpuffer braucht. Damit kann ich leben. Ich würde nur gerne mal wissen, wie die Performance auf einem 386er ist. Abgesehen davon, daß der ja langsamer getaktet ist, brauchen die Befehle da auch mehr Takte.
zatzen hat geschrieben:Schleife:
Hmm...? Bei mir würden alle Samples des Moduls im Speicher liegen.
Vielleicht führt es zu weit, wenn ich dich frage, wie deine Mix-Routine funktioniert.
Ja, bei mir liegen die auch alle im Speicher. Ich hatte mich jetzt gefragt: Meinst Du, eine Schleife um ein Musikstück herum (also dieselbe Musiksequenz/Tonfolge mehrmals spielen) oder meinst Du das Wiederholen des Samples - also das bekannte: Sample anschlagen, bis zum Ende spielen und dann wiederholen - entweder vom Samplestart oder aus der Mitte des Samples, um as "anschlagen" nicht mehr mitzunehmen... - So machen es ja quasi die MODs auch.

Du hattest ja das mit den Schleifen angesprochen - ich hatte nur darauf reagiert.
Meine Mix-Routine... Naja. Eigentlich ist das kein Hexenwerk.
Das Soundpufferstück wird zu Anfang genullt.
Ich hole aus Welle oder Sample den Wert (4bit) nach AL, OR Lautstärke (auch 4bit) auch nach AL, dann XLAT.
Habe eine 256 Byte Multiplikationstabelle, die vorberechnet ist und die den Wert*Lautstärke berechnet und gleich "signed" macht. So erhalte ich dann einen Wert zwischen -8 und +7 aus dieser Tabelle (also 248...007) und den addiere ich zum Puffer. Dann addiere ich Position in Welle oder Sample mit dem obengenannten EBP Zeugs und vermindere ECX um 1. (in ECX steht bei mir die Länge in "Ticks", die der Ton gespielt wird). Wird 0 erreicht, wird rausgesprungen und zum nächsten "Befehl" gegangen.
Außerdem gibt es noch einen Adder, der ESI addiert, das ist für die Position im Puffer, natürlich auch immer um 1 erhöht werden muß. Ich erhöhe den aber nicht um 1, sondern um $FFFF0001. Das ist ein Trick. Bei jeder Stimme setze ich zu Anfang die oberen 16bit auf Pufferlänge-1, die unteren 16bit auf die Position im Puffer. Durch die o.g. 32bit Addition wird CARRY immer =1, außer, wenn ich die "Pufferstück-Länge" erreicht habe, das ist der einzige Fall, wo CARRY=0 wird und ich aus der Stimme insgesamt "rausspringe".
Meine Routine arbeitet so, daß ich jede Stimme einzeln so lange ihr Zeug zum Puffer "addieren" lasse, bis bei der
ADD ESI,$FFFF0001 Addition CARRY=0 wird.
Am Ende habe ich einen Soundpuffer mit aufaddierten Werten, die schon "zentriert" sind, allerdings "signed" und zu leise.
Am Ende läuft über den gesamten Puffer nochmal eine Ersetzungsroutine, die mit XLAT und (dynamischer) Tabelle die Werte "normalisiert". Nur wenn sich Mastervolume oder Anzahl benutzter Stimmen ändert, wird diese Tabelle intern neu berechnet.
Diese Tabelle normalisiert dann die Pufferwerte auf Werte 0 bis 255, mit Zentrum 128.

Das ist, im Groben vereinfacht, die Art, wie mein "Mixer" arbeitet. Da ich nicht mit Patterns, die aus mehreren Stimmen bestehen, arbeite, sondern mit quasi "unabhängig funktionierenden" Stimmen arbeite (die aber - innerhalb ihres "Devices" - auch miteinander kommunizieren können), muß ich auf diese Weise vorgehen.

In Wirklichkeit sind die Schleifen für Klangsynthese noch geringfügig "komplexer":
Es gibt ja immerhin noch die Hüllkurve. ATTACK, DECAY, SUSTAIN und RELEASE sind bei mir einzelne Subroutinen (damit die Schleifen klein und schnell sind). Außerdem sind es insgesamt eigentlich 20 Subroutinen, erstens, weil es 2 RELEASEs gibt (HARD/SOFT-RELEASE), also ATTACK, DECAY, SUSTAIN, HRELEASE,SRELEASE und weil jede dieser 5 noch mit 4 Konfigurationen Kombiniert werden kann: TONE,NOISE,TONE+PORTAMENTO,NOISE+PORTAMENTO.
Auch alles, um das Ding möglichst schnell zu kriegen.

Dann kommen noch 4 Schleifen für Samples hinzu (bei Samples natürlich keine Hüllkurve)
SAMPLE_EVEN,SAMPLE_ODD,SAMPLE_EVEN_RELEASE,SAMPLE_ODD_RELEASE.
Bei Samples gibt es nur eine Art Release. Das EVEN und ODD muß ich vielleicht erklären:
Wie alles andere werden auch Samples bei mir nur mit 4bit abgespielt, somit auch nur 4bit gespeichert. Der erste Instinkt verleitet einen natürlich gleich dazu, Samples in aufeinanderfolgenden Nybbles im Speicher abzulegen.... Aber dann bräuchte man ständig eine Routine, die das letzte bit der Position rausrotiert, um die Position zu halbieren und anhand des CARRY zu entscheiden, ob das obere oder untere Nybble benutzt wird. Das kam für mich nicht in Frage.

Somit entschied ich mich für einen ungewöhnlicheren Weg:
Samples werden in voller Länge mit 4bit gespeichert. Um keinen Speicher zu verschwenden, habe ich eigens dafür eine 4bit-Speicherverwaltung entwickelt, die den vorhandenen Samplespeicher wie 2 unabhängige "nebeneinanderliegende" 4bit-Speicher behandelt. Selbst die "Header", die einem Sample im Speicher vorangestellt werden, sind 4 Bit (7 Nybbles insgesamt).

Der Abspielroutine muß natürlich gesagt werden, ob das Sample im "unteren Nybble" (EVEN) oder "oberen Nybble" (ODD) liegt. Danach entscheidet sie, wie die Daten ausgelesen werden,
entweder:
mov AL,ES:[DI]; shl AL,4;
oder:
mov AL,ES:[DI]; and AL,$F0;

(der Wert muß immer "oben" sein, "unten" wird danach die Lautstäre mit OR AL,Lautstärke eingesetzt, danach wieder XLAT, usw...

Die beiden Befehle oben (entweder shl AL,4 oder and AL,$F0) sind der einzige Unterschied zwischen den Abspielsubroutinen für Samples. Daher habe ich diese NICHT doppelt gemacht, sondern vor der Schleife wird abhängig vom EVEN/ODD-Bit des Samples der Code modifiziert, d.h. mit einem Befehl entweder die Bytefolge für shl AL,4; oder für and AL,$F0; in den Code kopiert... - ja, ich weiß: Ich bin ein Freak...

Weil es insgesamt 22 Subroutinen für verschiedene "Zustände" (siehe oben) sind, ist ISM natürlich vergleichsweise groß geworden, zusätzlich kommen ja auch noch die ganzen "Befehle" dazu.
Außerdem gibt es einige Flags für Zustände (für jede Stimme), z.B. das Flag zum Aufaddieren für Transponieren.
Das ist auch der Grund, wieso bei mir da nicht zusätzlich ein Anstieg eingegeben werden muß. Zum Beispiel:

Code: Alles auswählen

C-5   10
TRN   20
A-6   15
Es wird C-5 für 10 Ticks gespielt, dann wird 20 Ticks lang bis zu A-6 transponiert, dann wird noch A-6 für 15 Ticks gespielt.
Der TRN-Befehl macht überhaupt nichts, außer die Länge hochzuaddieren (also 20). Der Grund dafür ist, daß man auch mehrere TRN-Befehle hintereinandersetzen kann (ohne Note dazwischen) und es funktioniert trotzdem.
Erst wenn wieder etwas "spielbares" (eine Note) erscheint, wird ausgewertet, ob das Bit für Transponieren gesetzt ist und dann ein zusätzlicher Adder berechnet, der zwischen der Frequenz C-5 und A-6 zum Frequenzadder zu addieren ist, damit es für 20 Ticks reicht, also ein Adder-Adder sozusagen. Klingt das nicht krumm, muß der nicht logarithmisch sein? Nein, muß er nicht:Das doppelte Addieren macht es von selbst logarithmisch.

(Anmerkung: Bei Figurensteuerung hab ich das auf C64 schon so gemacht: Damit Sprünge nicht wie ein ""Dreieck" aussehen, sondern "sinusförmig" sind, gibt es auch so einen "Doppel-Adder" - schon kann ich mir Tabellen, Multiplikationen oder gar Sinusfunktionen sparen... Außerdem kann man auf die Art ganz leicht die Sprunghöhe variieren, ohne für alle Höhen zusätzliche Sinustabellen oder ähnliches zu bemühen...)

Der Adder-Adder wird dann automatisch positiv oder negativ für hoch-/runtertransponieren. Er ist intern 48bit vorzeichenbehaftet. Das muß er sein: Seine Nachkommastellen sind nochmal um 16 bit nach hinten versetzt zum normalen Adder, damit auch der AdderAdder weiche Übergänge machen kann (bei "ganzzahligem" AdderAdder käme die transponierte Frequenz nicht immer genau genug an der "Zielnote" an.

Die ganze Transponiererei ist also ein Trick: Kommt ein Notenbefehl und Transponieren ist AN, dann wird die Note nicht gespielt, der Player holt nur die Note (Für die AdderAdder-Berechnung) und geht wieder in den Musikdaten einen Befehl zurück, dann berechnet er den Kram und geht in die alternative Abspielroutine für Transponieren. Ist diese fertig gespielt (d.h. der Tick-Zähler wieder auf 0 runter, wird normal zum nächsten Befehl gesprungen - der dann die nächste Note ist.
Das ist auch der Grund, wieso bei Notenbefehlen eine Länge von 0 durchaus sinnvoll sein kann: Wenn hoch-/runtertransponiert werden soll, aber die Noten am Ende nicht gehalten werden sollen, kann die Note auf Länge 0 gesetzt werden - dann ist die Länge nur die des TRN-Befehls und die Note dient nur zur Angabe der Zielfrequenz.

Was kann ich noch über den Mixer sagen? Achja, daß ich die "Noise" extra gemacht habe, liegt daran, daß "Noise" keine Wellenform, sondern dynamisch ist (sonst würde es nicht wie Noise klingen). Hier ist die Schleife etwas anders gestaltet: Bei jedem Überlauf der Nachkommastelle des "Frequenztickers" wird zu einer Subroutine verzweigt, die wie ein 16bit-Zufallsgenerator funktioniert, mit dem Bias $0421. (Habe verschiedene getestet. Dieses deckt alle möglichen 65536 Werte ab und erzeugt wenig "Moiré"). (Ich multipliziere hier natürlich nicht, sondern addiere.)
Somit kann ich also "blaues Rauschen" in verschiedenen Frequenzen erzeugen. Auf die Art wird die "Percussion" in dem "Enten"-Stück erzeugt. Wenn mir nur eine gescheite Idee zum Umsetzen der Filter käme (bin auch mit Registern schon am Limit und will so wenig wie möglich "Wechsel" haben, andererseits aber auch keine riesigen 8kB-Tabellen) - dann könnte ich vor allem das Rauschen noch etwas modifizieren, damit es nicht nur so "mittel" ist, sondern auch gescheit "explodieren und zischen" kann. Andererseits könnte man hierfür natürlich auch mal ein Sample bemühen...

Achja, noch zur Erwähnung: Wenn der Soundpuffer voll ist, generiert ISM nicht weiter, sondern merkt sich bei jeder Stimme ihren derzeitigen Zustand (z.B. eine erst halb gespielte Note). Beim Wiedereintritt (d.h. dem nächsten Aufruf von ISM, wenn Soundkarte oder PC-Speaker-Subroutine meldet, daß der Puffer wieder fertig gelesen ist) wird dann an der gleichen Stelle weitergemacht.
Das ganze Ding ist so ausgelegt, daß Doublebuffering,Triplebuffering und mehr ohne Weiteres möglich ist. Man gibt nur die Position und Größe des Soundpuffers an, sowie die Größe des Subpuffers, der pro Aufruf von ISM gefüllt werden soll. Den Rest macht ISM.

Funktioniert bei Doublebuffering so, daß ISM zuerst A+B-Teil des Puffers füllt, erst dann wird Abspielen per Speaker/SB gestartet (für den A-Teil). Sobald das Ende des A-Teils erreicht ist, sendet SB den berühmten IRQ (z.B. 5 oder 7), da lasse ich dann einen "Pufferanforderung" Wert um 1 erhöhen (bei Speaker mach ich das manuell).

Der Wert sollte eigentlich nie größer als 1 werden, falls doch, sollte man Triplebuffering in Erwägung ziehen. Der Puffer sollte aber groß genug gewählt werden, damit Figurensteuerung und Grafikberechnung nicht länger dauern als das Abspielen eines Subpuffers.

So, ist nun der Wert >=1, dann wird von ISM ein neuer A-Teil generiert (SB/Speaker sind ja nach dem setzen des "Puffer abgespielt" gleich in den B-Teil weitergegangen und lesen da weiter). In der Zeit kann ISM den A-Teil wieder mit neuen Daten überschreiben. Ist der B-Teil fertig gelesen, folgt ein neues "brauche Pufferdaten" Signal, SB/Speaker fangen wieder oben beim A-Teil an (der inzwischen schon eine Weile fertig neu-generiert sein sollte) und ISM kann den B-Teil überschreiben. Ich nehme an, daß das sowieso jeder so macht. Ohne mind. Doublebuffering stottert Sound und flackert Grafik...

Muß aber sagen, daß selbst bei Singlebuffern diese Millisekunde nur ein kurzes "TIcken" macht. Aber so ist das nicht "sauber", daher Mehrfachpuffern Pflicht.

Wenn ich den ganzen Murks mal fertigkriege, werde ich eine "Standard-Hauptschleife" für Spiele basteln, die diese ganzen Sachen selbst berücksichtigt und auch die Kommunikation zwischen den einzelnen Teilen organisiert. Man könnte das auch alles jedesmal selbst neu machen, die Schnittstellen habe ich mehr oder weniger "standardisiert", so daß es hier eigentlich kaum Fragen geben sollte, wie der Kram zu benutzen ist... Und wie schon erwähnt, werde ich wohl auch gleich so ein "Standardframe" in den Musikdaten selbst bauen, das die Organisation/Sortierung/Priorisierung von Soundeffekten (d.h. Verteilung auf die Stimmen) selbst vornimmt. Befehle dafür sind ja bereits vorgesehen.

OK, genug für heute.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Ich programmiere generell nicht so viel, und so würde es mir genügen,
wenn ich eine vernünftige Version bei dieser Patternspeicherung habe.
Das ganze ist im Grunde eine ziemlich simple Sache, weil die Kommandos
Nur aus Samplenummer, Note und Lautstärke bestehen.
Wenn ich das mal über die Bühne gebracht habe und neben dem Converter
eine Unit habe die das decodieren kann (und diese wird nicht sehr groß sein),
dann ist das Thema abgehandelt und es kann vergessen werden was der Computer
im Hintergrund macht wenn er die Zeilen reinlädt.
Ich verwende ja durchaus in der Datei auch ganze Bytes und Words, lediglich
die Patterns werden Bitweise gelesen, weil es dort einfach eine ziemliche
Verschwendung wäre, Daten, die im Schnitt nur 2 Bit belegen, mit einem
ganzen Byte zu speichern.

Die Bit-Tiefe von digitalem Audio und die Samplefrequenz haben nur indirekt
einen Qualitativen Zusammenhang. 44.1kHz mit 4 Bit wird "frischer" klingen
als 14kHz mit 4 Bit. Allerdings würde ich sagen dass 22.05 kHz genügen.
Meine geplanten 16kHz könnten teils etwas stumpf klingen, aber das ist für
mich der beste Mittelweg zwischen Klangqualität und Rechenzeit.
so macht es auch keinen Sinn, Samples mit mehr als 16kHz Grundfrequenz zu
verwenden, was wiederum für Speicherersparnis sorgen kann.

Aber wie du schon geschrieben hast, du schreibst Routinen, schreibst Jahre
später etwas, wofür du diese brauchst, aber mittlerweile sind sie veraltet.
Ich mache das eher so, dass ich etwas fertig mache, ohne große Möglichkeit
auf Erweiterung, und dann verwende ich das so. Aber eben auch, weil ich
das Programmieren eher nebenbei mache, und trotz aller Liebe zum Erfinden
von Dateiformaten da eher amateurhaft rangehe, da nicht erweiterbar.
Aber bisher hat das funktioniert - die Musik von Kotzman II habe ich damals
in einem eigenen Tracker gemacht. Der war zwar ziemlich beschränkt, aber
ich war zufrieden. Dagegen werden die Tracker-Routinen, die ich jetzt gerade
bastle, regelrecht revolutionär sein.
Es ist einfach für mich jetzt ganz klar, wie die Patterns codiert werden
müssen, und so viel Aufwand ist das nicht, könnte man evtl. an ein bis
zwei Tagen niedergeschrieben haben.

Bevor ich ein Spiel mache, gibts erstmal einen schönen Player für Soundblaster.
Ich spiele ja selbst schon ziemlich lange nicht mehr, da muss mir für ein
Spiel schon was einfallen, was mich selbser hinterm Ofen hervorlockt.

Im Gegensatz zu dir habe ich ja nur Samples, das ist einfach Tracker-immanent.
Ja, und ich will meine Sache "zu" und unflexibel machen, damit der Player möglichst
knapp gehalten werden kann. Wohlgemerkt, die Player-Routinen - die Patternlese-
Routine werden natürlich etwas komplexer sein als bei normalen Tracker-Playern.

SFX bei spielen würde ich dann einfach als Samples reinbringen, ohne dass sie
von den Patterns gesteuert werden. Denkbar wären allerdings kleine melodische
Dinge wie Triumphmusik oder Trommelwirbel was auch immer, wenn irgendwas
spezielles passiert, und was dann angesprungen wird.
Soetwas wie iMuse bei Lucas Arts Spielen geht mit meiner Methode natürlich nicht
so schön, allerdings könnte man nach Ablauf eines Patterns je nach Spielsituation
in eine andere Sequencerposition wechseln.

Die Beschreibung deiner Player-Routinen geht mir gerade noch nicht so ein, weil
ich lange keinen Mixer mehr programmiert habe und momentan noch zu sehr auf die
Patterns konzentriert bin. Aber es sind wertvolle Infos die ich dann später
gebrauchen kann, und deswegen fragte ich ja auch danach.

Aber das mit dem Puffer würde ich auch so machen, ist ja eigentlich klar.
Ich habe ja bereits schonmal ein Mischroutine geschrieben, allerdings nur
für große Samples aus dem XMS, ohne Pitching.


Ok, im letzten Post habe ich etwas verfrüht eine Art Formatbeschreibung kundgegeben.
Werde ich nochmal machen wenn es wirklich fertig ist und es die ersten tatsächlichen
Files gibt...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

So, ich hab mal ein bisschen programmiert.

Dieses Pattern eines kleinen Testsongs (Ausgabe des Readers von einem codierten Pattern, aus einer Datei):

Code: Alles auswählen

  0 |01 00E000 .|18 004333 .|23 016544 1|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  1 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  2 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  3 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  4 |02 009800 .|25 0160CC .|23 016544 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  5 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  6 |.. ...... .|18 004333 .|23 016544 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  7 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  8 |.. ...... .|24 010AA7 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
  9 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 10 |.. ...... .|25 0160CC .|23 016544 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 11 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 12 |03 009000 .|18 004333 .|23 016544 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 13 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 14 |04 011800 .|18 004333 .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 15 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 16 |.. ...... .|.. ...... .|23 019104 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 17 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 18 |.. ...... .|24 010AA7 .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 19 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 20 |05 00A000 .|20 0160CC .|23 019104 2|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 21 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 22 |.. ...... .|20 0160CC 1|23 019104 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 23 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 24 |.. ...... .|19 0160CC .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 25 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 26 |.. ...... .|25 0160CC .|23 019104 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 27 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 28 |.. ...... .|19 014CFF .|23 019104 1|26 01BEAF .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 29 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 30 |.. ...... .|25 0160CC .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 31 |06 00A000 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 32 |.. ...... .|18 004333 .|23 010BA5 1|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 33 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 34 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 35 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 36 |.. ...... .|25 0160CC .|23 010BA5 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 37 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 38 |.. ...... .|18 004333 .|23 010BA5 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 39 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 40 |.. ...... .|24 010AA7 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 41 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 42 |.. ...... .|25 0160CC .|23 010BA5 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 43 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 44 |.. ...... .|18 004333 .|23 010BA5 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 45 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 46 |.. ...... .|18 004333 .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 47 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 48 |.. ...... .|25 0160CC .|23 010BA5 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 49 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 50 |.. ...... .|24 010AA7 .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 51 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 52 |07 009000 .|20 0160CC .|23 010BA5 2|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 53 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 54 |.. ...... .|20 0160CC 1|23 010BA5 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 55 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 56 |.. ...... .|19 0160CC .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 57 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 58 |.. ...... .|20 0160CC .|23 010BA5 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 59 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 60 |.. ...... .|25 0160CC .|23 010BA5 1|26 01BEAF .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 61 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 62 |.. ...... .|19 014CFF .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 63 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 64 |08 008000 .|18 004333 .|23 016544 1|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 65 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 66 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 67 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 68 |09 00A000 .|25 0160CC .|23 016544 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 69 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 70 |.. ...... .|18 004333 .|23 016544 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 71 |10 013000 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 72 |11 009000 .|24 010AA7 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 73 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 74 |.. ...... .|25 0160CC .|23 016544 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 75 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 76 |12 00A000 .|18 004333 .|23 016544 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 77 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 78 |.. ...... .|18 004333 .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 79 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 80 |.. ...... .|.. ...... .|23 019104 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 81 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 82 |13 013000 .|24 010AA7 .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 83 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 84 |14 00A000 .|20 0160CC .|23 019104 2|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 85 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 86 |.. ...... .|20 0160CC 1|23 019104 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 87 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 88 |15 009000 .|19 0160CC .|.. ...... .|26 021333 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 89 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 90 |.. ...... .|25 0160CC .|23 019104 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 91 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 92 |16 00B000 .|19 014CFF .|23 019104 1|26 01BEAF .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 93 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 94 |.. ...... .|25 0160CC .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 95 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 96 |17 0077DD .|18 004333 .|23 015137 1|26 014EA2 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 97 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 98 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
 99 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
100 |.. ...... .|25 0160CC .|23 015137 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
101 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
102 |.. ...... .|18 004333 .|23 015137 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
103 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
104 |.. ...... .|24 010AA7 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
105 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
106 |.. ...... .|25 0160CC .|23 015137 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
107 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
108 |.. ...... .|18 004333 .|23 015137 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
109 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
110 |.. ...... .|18 004333 .|.. ...... .|26 014EA2 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
111 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
112 |.. ...... .|.. ...... .|23 019104 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
113 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
114 |.. ...... .|24 010AA7 .|.. ...... .|26 014EA2 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
115 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
116 |.. ...... .|20 0160CC .|23 019104 2|26 014EA2 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
117 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
118 |.. ...... .|20 0160CC 1|23 019104 1|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
119 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
120 |.. ...... .|19 0160CC .|.. ...... .|26 014EA2 .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
121 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
122 |.. ...... .|20 0160CC .|23 019104 2|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
123 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
124 |.. ...... .|25 0160CC .|23 019104 1|26 01BEAF .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
125 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
126 |.. ...... .|19 014CFF .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
127 |.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|.. ...... .|
belegt 119 Byte inkl. der erforderlichen Tabellen. Ich muss das mal per Hand nachrechnen ob da nicht
vielleicht noch mehr (weniger) geht. Möglicherweise hab ich noch nen Fehler drin.
Immerhin braucht es schonmal weniger Bytes als Events drin vorkommen.
Statt Notenwerten stehen hier direkt die Festkomma-Schrittweiten-Werte für 16kHz, 8 Bit Vorkomma,
16 Bit Nachkomma. Links davon die Samplenummer, rechts, wenn > 0, die Lautstärke im Sinne
von SHR (bzw. SAR) von 0 bis 7, d.h. ich beschränke mich auf Halbierungen.

Der Code um eine Zeile einzulesen hält sich auch noch in Grenzen, finde ich:

Code: Alles auswählen

procedure read_row;
  label skip, skip2;
  var
    track: byte;
    tblpos: byte;
begin
  fillchar(singlerow, sizeof(showpattern) * 8, 0);

  if skipcount > 0 then
    dec(skipcount)
  else
    skipcount := readbits(2);
  if skipcount > 0 then goto skip2;


  for track := 0 to 7 do
  if trk_active[track] then (* nur aktive Kanaele abfragen *)
  if readbits(1) = 1 then (* Wenn Event auf Kanal stattfindet *)
  begin

     (* Repeat abfragen, falls genutzt *)
     if do_repeat[track] then
       if readbits(1) = 1 then
       begin
         singlerow[track].smp := lastentry[track].smp;
         singlerow[track].fixvor := lastentry[track].fixvor;
         singlerow[track].fixnach := lastentry[track].fixnach;
         goto skip;
       end;

     (* Sampnr *)
     if sampnr_table_len[track] = 0 then
       singlerow[track].smp := readbits(bitsneeded[zsmheader.sample_entries])
     else
       singlerow[track].smp
         := sampnr_table[track, readbits(bitsneeded[sampnr_table_len[track]])];
     lastentry[track].smp := singlerow[track].smp;

     (* Pitch *)
     tblpos := readbits(bitsneeded[pitch_table_length[singlerow[track].smp]]);
     singlerow[track].fixvor := pitch_tables[singlerow[track].smp, tblpos].vor;
     singlerow[track].fixnach := pitch_tables[singlerow[track].smp, tblpos].nach;

     lastentry[track].fixvor := singlerow[track].fixvor;
     lastentry[track].fixnach := singlerow[track].fixnach;

   skip:

     singlerow[track].vol
      := volume_table[track, readbits(bitsneeded[volume_table_len[track]])];

  end;

  skip2:

end;
Natürlich aus dem Zusammenhang gerissen. Aber nur mal um die Größe der Routine zu zeigen,
die jede Zeile aufgerufen werden muss.
Alles noch optimierungs- und vereinfachungs-würdig, aber es funktioniert schonmal.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Der Converter ist soweit fertig.
Habe es noch etwas vereinfacht (kein Leerzeilen-Counter sondern nur ein Flag-Bit für jede Leerzeile,
optional, nur wenn es insgesamt was bringt. Kommt dann speichermäßig aufs gleich raus bzw. je nach
Situation ist es sogar kompakter (immer nur 1 Bit statt mehrere Bits für einen Counter), und natürlich
einfacher zu programmieren. Dann noch ein paar Fehler beseitigt... Zudem habe ich mich entschlossen,
keine Sample-Schleifen zu unterstützen (nutze ich in der Praxis sowieso selten), damit ich beim Player
mal etwas zügiger vorankomme. Das Kompressionsverhältnis ist in etwa geblieben und kann je nachdem
dem sehr effektiv sein, z.B. 25 Byte für ein Pattern mit nur einer Spur, die aber komplett gefüllt ist.
Verglichen mit der Kompression eines DDMF liege ich bei ca. 20 Prozent, und dass ein Pattern mit sagen
wir mal 100 Byte effizienter codiert ist als eines mit 2048 Byte (soviel hätte ich wenn ich's einach dumm
speichern würde) sollte klar sein.
Überhaupt scheint die Kompression im Schnitt ein Event in knapp ein Byte unterzubringen.

Dann geht's mal an den Player...

Hättet ihr nen Musikwunsch mittleren muskalischen Anspruchs den ich für die Präsentation des Players
mal nachtrackern könnte? Möglichst ohne viel Text und Gesang, sonst ist das blöd bzw. ohne riesige
Samples nicht machbar.
mov ax, 13h
int 10h

while vorne_frei do vor;
duncan
617K-Frei-Haber
Beiträge: 310
Registriert: Fr 19. Dez 2014, 00:45
Wohnort: Markgräflerland

Re: Trackermodul-Engine (sehr einfach)

Beitrag von duncan »

Hi,

dies voraus: Ich habe genau 0% Ahnung, wovon ihr hier schreibt - aber ich wollte mal ein Kompliment loslassen über das Ausmass an Arbeit, welche in diesen Beiträgen steckt. Phänomenal, in welcher Bandbreite hier im Forum Interessen gegeben sind - danke dafür!

Gruss ein unwissender duncan
You live and learn. Or you don´t live long. (Lazarus Long)
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3218
Registriert: Mi 24. Mai 2006, 20:29

Re: Trackermodul-Engine (sehr einfach)

Beitrag von Dosenware »

Schließe mich mal an :like:

Was den Musikwunsch angeht:
Ich weiß zwar nicht was ein "mittlerer muskalischer Anspruch" ist, aber
https://www.youtube.com/watch?v=qeOYhC66EoM
Fänd ich cool...
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Danke für den Musikwunsch...
Allerdings sind Metal-E-Gitarren schwer durch Samples nachzubilden.
Und in dem Stück kommen auch viele Vibratos und Portamentos vor,
die sieht mein Player nicht vor... Es ist, bezogen auf die Umsetzbarkeit
in einem 8 Kanal Tracker schon eher ein gehobener Anspruch.

Vielleicht habt ihr noch mehr Wünsche, dass ich mir was aussuchen kann.
Ich selber hatte schon an "Theme from a Caravan" von Mike Batt gedacht,
das wäre aber eine Mammut-Aufgabe...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3218
Registriert: Mi 24. Mai 2006, 20:29

Re: Trackermodul-Engine (sehr einfach)

Beitrag von Dosenware »

Was wäre denn einfacher, elektronische Musik? Das wiederholen immer gleicher Töne in unterschiedlichen Kombinationen?

ich vermute mal https://www.youtube.com/watch?v=ah0_E-zF5JU ist dann eher einfach (ich mag die Version, irgendwie Arcadig)

https://www.youtube.com/watch?v=jzUWdd1PQvw auch noch im Umsetzbaren bereich?

Oder: http://www.remix64.com/track/galantine/ ... sters-mix/ <- sehr geil
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Ich glaube da würde ich am ehesten "Great Giana Sisters (Great Terminator Sisters mix)" umsetzen.
Das würde allerdings ziemlich Lo-Fi klingen in meinem Format, mehr in die Richtung wie z.B. bei
Nintendo (NES) Tetris die Musik, da ist ja auch viel berühmte Musik drin. Es würde aber bei mir
doch etwas naturgetreuer klingen, weil ich eben nicht nur wie ein NES ein paar Wellenformen wie
Rechteck, Dreick, Sägezahn habe, sondern eben Samples, die jeden Klang repräsentieren können,
aber eben, weil mein Format auch auf Kompaktheit der Daten abzielt, von der Klangqualität etwas
beschränkt sind.

Ich gucke aber auch selber mal mein CD-Regal durch, vielleicht mache ich mal eine Conversion eines
bekannten Easy Listening Stücks. Der Punkt ist nur, ich muss die Klänge irgendwo her holen, deswegen
geht soetwas wie "das ich - dorn" schlecht, welches sich zu einem Großteil durch ungewöhnliche Klangfarben
definiert, die ich so nicht parat habe und die man als ziemlich lange Samples mit reinbringen müsste,
wenn man sie denn hätte.

Für alle, die nicht wissen worum es hier überhaupt geht:
Schon seit geraumer Zeit gibt es Tracker, das sind Musikprogramme die meist mit Samples als
Klangfarben arbeiten und wo man die Noten in eine Art Matrix, die Patterns, einprogrammiert.
Entsprechende Musikdaten finden sich zu Hauf z.B. auf http://www.modarchive.org .
Nun unterstützen diese Format so gut wie alle etliche Sonderbefehle, nämlich Effekte, womit
man das Abspielen der Samples noch beeinflussen kann, jenseits des simplen Abspielens auf
verschiedenen Noten.
Aber das programmiertechnisch umzusetzen ist mir aus gewissen technischen Gründen zu aufwendig.
Also habe ich mich entschieden, einen Player zu schreiben der eben nur Samples auf verschiedenen
Noten abspielen kann und noch die Lautstärke verändern kann, mehr aber nicht. Zudem habe ich
mir gleichzeitig ein eigenes Format ausgedacht, bei dem die Patterndaten wesentlich kompakter
gespeichert werden (und darum geht es fast ausschliesslich in diesem Thread). Denn wenn man
mal als Beispiel ein klassisches 8-Kanal-MOD nimmt vom Amiga (eigentlich sinds 4 Kanäle, aber
es gibt eben auch 8er, und mein Format unterstützt auch 8 Kanäle), dann verbraucht ein Pattern
2048 Byte und je nach Geschwindigkeit und Abstimmung auf die Möglichkeit von 16tel oder aber
32tel Noten, vergehen pro Pattern vielleicht 3-6 Sekunden. Den günstigeren Fall, 6 Sekunden, mal
vorausgesetzt, hätten wir bei einem 4-minütigen Song also schon 2048 * 10 * 4 = 81920 Byte Patterndaten.
Mein Format würde da möglicherweise mit lediglich 5 KB zu Buche schlagen. Die 77 KB Unterschied
könnte man viel besser in weitere Samples investieren.
Gewissermaßen könnte man sagen, mein Format und mein Player seien "lame".
Es gibt aber auch zwei Vorteile oder Rechtfertigungen, zum einen, dass die Patterns im Gegensatz
zu den meisten klassischen Formaten einen meist wirklich vernachlässigbaren Teil des Speicherplatzes
ausmachen werden, und zum anderen, dass die Player-Routinen durch ihre Einfachheit möglicherweise
sehr schnell sind.
Früher habe ich gerne mal bei eigener Musik wohl mehr aus Verlegenheit, weil die Effekte da sind,
Vibrato, Tremolo oder lange Portamentos benutzt. Mittlerweile merke ich aber, dass ich das kaum
noch tue und meistens nur für Triller mal direkte Notenwechsel ohne Neustart des Samples mache.
Für mich ist also das eigene "Sparformat" keine bedeutende Einschränkung.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Musik aus Spielen wäre nicht schlecht, vielleicht mache ich etwas aus Monkey Island.
Die verhält sich teilweise rhythmisch und melodisch ziemlich einfach.
Einfach mal sehen... Oder was einfacheres aus der Klassik.
Ich wollte nur schonmal nach Ideen fragen, Um zwischendurch mal was anderes zu
machen als programmieren, deshalb.
Der Player ist noch nicht fertig, gerade mal der Converter.

Was natürlich auch geht, ein bekanntes und beliebtes Trackerstück (seien es 4 oder 8 Kanäle)
einfach in mein Format wandeln, wenn es trotz des weglassens der Effekte trotzdem noch richtig
klingt. Oder ich finde bei meinen eigenen Sachen etwas hörenswertes, allerdings habe ich meistens
viel Samplematerial in den Modulen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3218
Registriert: Mi 24. Mai 2006, 20:29

Re: Trackermodul-Engine (sehr einfach)

Beitrag von Dosenware »

der eben nur Samples auf verschiedenen
Noten abspielen kann und noch die Lautstärke verändern kann
Klingt als würden Pianos ganz gut abgebildet werden können: https://www.youtube.com/watch?v=-ZJDNSp1QJA

Ansonsten könntest du dir Classic Videogames Radio mal anhören, da findest du sicher etwas.
Benutzeravatar
Heilbar
DOS-Übermensch
Beiträge: 1014
Registriert: Mi 5. Mär 2014, 07:25
Wohnort: Hamburg

Re: Trackermodul-Engine (sehr einfach)

Beitrag von Heilbar »

zatzen hat geschrieben:Musik aus Spielen wäre nicht schlecht, vielleicht mache ich etwas aus Monkey Island.
Die verhält sich teilweise rhythmisch und melodisch ziemlich einfach.
Einfach mal sehen... Oder was einfacheres aus der Klassik.
Ich wollte nur schonmal nach Ideen fragen, Um zwischendurch mal was anderes zu
machen als programmieren, deshalb.
Der Player ist noch nicht fertig, gerade mal der Converter.

Was natürlich auch geht, ein bekanntes und beliebtes Trackerstück (seien es 4 oder 8 Kanäle)
einfach in mein Format wandeln, wenn es trotz des weglassens der Effekte trotzdem noch richtig
klingt. Oder ich finde bei meinen eigenen Sachen etwas hörenswertes, allerdings habe ich meistens
viel Samplematerial in den Modulen.
Monkey ist echt cool zum nach spielen....sowohl Gitarre als auch Klavier....ich meine wenn du diese Instrumente zur Verfügung hast ....ich hab die Grund-Melodie auch schon auf einer F-Flöte gefiedelt....macht aber jedes mal Spaß.
Im klassischen Bereich....wäre Musik aus dem Mittelalter zu empfehlen...Crusaders? Noch besser aber Bretonische Gesänge aus dem Französischen (Karl Marx Region)...da kann mal so richtig das Mitspielfieber ausbrechen :-) Und man ist nah an den Komponier-Ideen der Spieleprogrammierer unsere Retrogemeinde.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 520
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Trackermodul-Engine (sehr einfach)

Beitrag von zatzen »

Habe noch etwas an dem Converter gewerkelt und mich doch wieder für Sample-Schleifen entschieden.
Wie ich die im Player umsetze und wie schnell das funktioniert sehe ich dann, und vielleicht kann mir
ja auch jemand helfen. Aber der Gedanke kam mir gestern, als ich an alte Nintendo Musik gedacht habe -
es wäre ja blöd wenn ich für diese simplen Wellenform-Synthesizer klanglich völlig redundante Samples
benutzen müsste, nur damit sie ausreichend lang sind... Und Speicherplatz verschwenden.

Das funktioniert ja alles mit Samples. Also wenn ich was nachtrackern will dann finde ich schon irgendwo
passende Klänge, es fragt sich nur wie glaubwürdig das letztlich klingt, von der Klangqualität her wird es
sowieso etwas beschränkt sein.

Von Monkey Island gibt es ja sogar von der Amiga-Version die Musik quasi im Tracker-Format, finde ich
aber nicht so gelungen, zumindest die vom ersten Teil nicht.

Im Moment verfolge ich noch die Idee, möglichst kleine Module mit meinem Format umzusetzen,
d.h. mit wenig Sample-Daten. Nur dann lohnt sich das mit der Pattern-Kompression wirklich.
Ich habe gerade nochmal ein altes 4-Kanal MOD von mir durch den Converter geschickt:
1855 Byte Patterndaten insgesamt. Das MOD hat 33KB Patterns... Der Song dauert etwa 5 Minuten.
Wenn man das mal hochrechnet kommt man da hin, dass ich ungefähr so viel Patterndaten in 64KB
unterbringen könnte wie in einem MOD 1 MB entsprechen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Antworten