Autor |
Beitrag |
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Di 12.07.11 13:37
Zur zeit benutzte ich ein statisches Array
Delphi-Quelltext 1:
| Mygrid : Array [0..15, 0..maxlen] of byte; |
schöner währe ein dynamisches da die Entgrösse ja eigentich nicht feststeht.
Delphi-Quelltext 1:
| Mygrid : Array [0..15] of Array of Byte; |
Will ich nun an einer bestimmten Stelle was einfügen/entfernen muss alles verschoben werden
und die Werte mussen neu berechnet werden
Nun lese ich immer wieder, nimm gleich TList<type> für sowas. Angeschaut, gelesen und Bahnhof verstanden.
Grund, mir schwebt etwas in der Form vor wie eine Liste, nur wie umsetzten?
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| Trackindex(0-15) Eventindex ZeitWert integer Event,(+Werte)=drei bytes 0 0 0 92,48,96 0 1 0 92,60,96 0 2 96 91,67,64 0 3 96 90,76,32 0 4 192 82,48,64 0 5 0 82,60,64 0 6 0 81,67,64 0 7 0 80,76,64 |
Beim einfügen/entfernen in einem normalen Array müsste ich nun durch irritieren um die Zeitwerte und das Event dazu finden. Vom neuberechnen der Zeitwerte mal abgesehen.
Habe ich aber ein Eventindex so bräuchte ich nur den index löschen und die Werte währen auch weg bzw will ich was einfügen(insert) hätte ich den Eventindex und könnte an der Stelle was neues einfügen.
Nun zum eigentlichen, wie müsste den so eine TList erstellt werden das sie in etwa so aussieht.
Es kann natürlich auch sein das mein ganzer Ansatz dazu vollkommen falsch ist und ich mich an etwas festbeisse was vielleicht gar nicht Notwendig ist.
Nach tagelangen Suchen und ausprobieren komm ich einfach auf keine vernünftige Lösung dafür.
Hoffe ihr könnt wieder mal helfen.
Gruss Alf
PS: Es geht nicht um das auslesen von MidiDaten, sondern um MidiNoten selbst zu erstellen(zeichnen).
Sollte es etwas konfus sein hier mal ein link dazu
www.omega-art.com/midi/mfiles.html
www.camx.de/kurs_kap3.html
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Narses
Beiträge: 10181
Erhaltene Danke: 1254
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Di 12.07.11 15:11
Moin!
ALF hat folgendes geschrieben : | Nach tagelangen Suchen und ausprobieren komm ich einfach auf keine vernünftige Lösung dafür. |
IMHO gehst du da ganz falsch ran. Du willst auf dem Zieldatenprotokoll auch die Bearbeitung aufsetzen und deshalb wird das so schnell so komplex.
Du solltest das etwas abstrakter angehen, z.B. ein Objekt TMidiEvent (evtl. auch als Basisobjekt für abgeleitete Klassen) definieren und erstmal alle möglichen MIDI-Ereignisse als Datentypen anlegen. Dann gehst du hin und schreibst die Ereignisse in eine Liste pro MIDI-Track. So kannst du auf Ereignisbasis die Tracks bearbeiten. Tipp: du könntest dem TMidiEvent eine Eigenschaft .PreDelay verpassen, so dass die Midi-Ereignisse eine Kette bilden, die sich selbst timed. Das ist zwar relativ effizient, aber auch etwas tricky beim Bestimmen der Einfügepositionen in anderen Tracks, wenn dort Ereignisse nicht exakt synchron abgelegt sind. Ein anderer Ansatz könnte ein festes, internes Zeitraster sein.
Wenn du dann ein Midi-File aus der Trackliste erzeugen willst, schreibst du erst im Midi-Protokoll die Bytestreams. Und zu diesem Zeitpunkt sind die einzelnen Objektgrößen im Zielformat klar, so dass es keine Probleme mehr geben sollte.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
Für diesen Beitrag haben gedankt: ALF
|
|
Bergmann89
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: Di 12.07.11 15:32
Hey,
wenn du noch D7 benutzt (wie das in deinem Profil steht) dann kannst du eh keine typisierten Listen nehmen. Die gibts erst später. Also enweder castest du jedesmal wenn du etwas aus der Liste liest, oder du benutzt ein Template:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| unit uMidiEvents;
interface
uses Classes, Contnrs, Windows, SysUtils;
type TMidiEvent = class(TIObject) end;
TListItem = TMidiEvent;
{$I ObjectListTemplate.pas}
TTMidiEventList = class(TObjectListTemplate);
implementation
{$I ObjectListTemplate.pas}
end. |
MfG Bergmann.
Einloggen, um Attachments anzusehen!
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Di 12.07.11 20:15
Danke für die Anworten.
Ich glaub, ich hab wieder mal falsch formuliert.
Was die Midi-Events betrifft gibt es da weniger Probleme. Mir geht es um das Notenschreiben. Speziell Note-on, Note-off und deren Timewert. Solange alle Noten intereinanderkommen null problemo, dann währe das Array ja effektiv(einfügen und delete) anhand der Zeitwerte wo welche Note steht.
So wie ich es jetzt mache.
Delphi-Quelltext 1: 2:
| maxlen = 10000 NotenTracks: Array [0..15, 0..maxlen] of byte | funct ja auch wenn alle Noten hintereinander gezeichnet werden.
Nun gibt es ja auch Akkorde in der Musik und da fängt das Problem mit dem Array an.
also muss ne andere Lösung her.
Narses hat folgendes geschrieben : |
IMHO gehst du da ganz falsch ran. |
Vermute ich auch, hab mich vielleicht festgefahren mit meinem Array.
Narses hat folgendes geschrieben : | Du willst auf dem Zieldatenprotokoll auch die Bearbeitung aufsetzen und deshalb wird das so schnell so komplex. |
Jein. Ich lege zwar die Daten schon im Originalformat im Array ab, so das das abspielen nicht noch mal konvertiert werden muss.
Narses hat folgendes geschrieben : | Du solltest das etwas abstrakter angehen, z.B. ein Objekt TMidiEvent (evtl. auch als Basisobjekt für abgeleitete Klassen) definieren und erstmal alle möglichen MIDI-Ereignisse als Datentypen anlegen. |
Das ist ja kein Problem, im Prinzip hab ich so schon.
Narses hat folgendes geschrieben : | ...Dann gehst du hin und schreibst die Ereignisse in eine Liste pro MIDI-Track. |
Mehr mach ich mit meinem Array(16Tracks) ja auch nicht, für jeden Track und deren Noten.
Narses hat folgendes geschrieben : | So kannst du auf Ereignisbasis die Tracks bearbeiten. Tipp: du könntest dem TMidiEvent eine Eigenschaft .PreDelay verpassen, so dass die Midi-Ereignisse eine Kette bilden, die sich selbst timed. Das ist zwar relativ effizient, aber auch etwas tricky beim Bestimmen der Einfügepositionen in anderen Tracks, wenn dort Ereignisse nicht exakt synchron abgelegt sind. Ein anderer Ansatz könnte ein festes, internes Zeitraster sein. |
fehlt mir jedweiliger Ansatz dafür
Wie schon gesagt, wahrscheinlich zu sehr auf das Array von mir fixiert!
@Bergman89:
Profil geändert:wink:
Zur Zeit also totale leeeeere, um ne Lösung für das zwischenspeichern der selbstertellten Noten zu finden.
Ansätze ja - aber lande immer bei meinem Array. einfach nur blöd zur Zeit.
Gruss Alf
Einloggen, um Attachments anzusehen!
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
hansa
Beiträge: 3079
Erhaltene Danke: 9
|
Verfasst: Di 12.07.11 21:01
Bergmann89 hat folgendes geschrieben : | Hey,
..dann kannst du eh keine typisierten Listen nehmen. Die gibts erst später. |
Wieso das ? Wenn irgendwas nicht richtig passt, dann steckt man das besser direkt in eine TObjectList und fertig. Die kann man sortieren und und...
_________________ Gruß
Hansa
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Di 12.07.11 22:24
hansa hat folgendes geschrieben : | Wieso das ? Wenn irgendwas nicht richtig passt, dann steckt man das besser direkt in eine TObjectList und fertig. Die kann man sortieren und und... |
Er bezog das auf D7 mit TList<Typ>. Das gab es da noch nicht. Darum hat er ja den DL angehangen.
TList<Type>, TObjektList hab ich zwar schon gelesen. Problem bei mir, wie setzte ich es um?
Hab nicht mal nen Ansatz bezogen auf mein Problem. Auch die gefunden Beispiele geben mir da nix, weil ich die nicht in meinem Kontext bekomme
Gruss Alf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Do 14.07.11 15:57
Jo, ich pushe zwar nicht gern, mach es aber mal.
Dank Narses und noch weitere suche hab ich entlich mal ein "simples" Beispiel gefunden, wie man mit Objektlisten umgeht.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
| type TEvent = class(TObject) Track: Integer; Deltatime: Integer; Event: Byte; Wert1: Byte; Wert2: Byte; end; ... ... TrackEvent: TObjectList; ... ...
TrackEvent:= TObjectList.Create;
TrackEvent.Free; ... ... procedure WriteEvent(Track, DTime: Integer; Event, aWert1, bWert2: Byte); var newEvent: TEvent; begin
newEvent:= TEvent.Create; newEvent.Track:= CurrentTrack; newEvent.Deltatime:= DTime); newEvent.Event:= Event; newEvent.Wert1:= aWert1; newEvent.Wert2:= bWert2;
TrackEvent.Add(newEvent); end; |
Also add, delete kein Problem. Insert etwas komplizierter und Editieren einer Note noch komplizierter
Hoffe also das für den Anfang, ich es so richtig verstanden habe.
Allerdings bleibt erstmal ein Wunsch offen.
Würde die Zuordnung zum Track nicht in diesem Objekt machen, sondern die Events zum Track zuordnen so wie
Delphi-Quelltext 1:
| Tracks: Array[0..15, 0..maxevents] of Byte. |
So wie es ist , ist es ja ein Chaos, weil alles in einer Liste ist.
Währe schön für nen Hinweis oder wie das Objekt, Classe aussehen musste.
Gruss Alf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Bergmann89
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: Do 14.07.11 18:36
Hey,
ich versteh dein Problem iwie nicht. Du hast mehrere Tracks, also brauchst du ne Klasse die deine Tracks verwaltet. Dann hat jeder Track mehrere Events, also brauchst du noch ne Klasse die deine Events verwaltet. Ich würd das so in der Art machen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| TEvent = class(TObject) private fDeltaTime: Integer; fEvent, Value1, Value2: Byte; public
end;
TTrack = class(TObject) private fEventList: TObjectList<TEvent>; public procedure AddEvent(event: TEvent); procedure DelEvent(ID: Integer); end;
TTrackManager = class(TObjekt) private fTrackList: TObjectList<TTrack>; public constructor Create(TrackCount: Integer); end; |
So hast du doch die Daten schön verpackt. Was ich nicht verstehe: Warum muss ein TEvent den Track wissen auf dem es liegt? Das kann doch dem Event egal sein, oder? Oder ich hab das Problem nicht richtig verstanden ^^
MfG Bergmann.
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
Zuletzt bearbeitet von Bergmann89 am Do 14.07.11 20:05, insgesamt 1-mal bearbeitet
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Do 14.07.11 19:56
Bergmann89 hat folgendes geschrieben : | Was ich nicht verstehe: Warum muss ein TEvent den Track wissen auf dem es liegt? Das kann doch dem Event egal sein, oder? Oder ich hab das Problem nicht richtig verstanden ^^
|
Jo, dem Event ist es egal. Ich habe Track nur mit reingenommen, weil ich keine andere Lösung hab um dieses Event zuzuordnen.
Für Deine Hilfe bin ich auch sehr Dankbar, nur verstehen tu ich es nicht
Brauch leider immer sehr lange bis ich so was ins Kleinhirn bekomme. Noch nie damit was gemacht und meine bildliche Vorstellung ist immer dieses blöde Array.
Nun vergleich ich mein kleines Beispiel mit Deins!
Das ist ja nun wieder ganz was anderes, omg.
Wozu diese beiden Prozeduren in TTrack, müssen die sein oder können die sein oder weil man es so macht?
Warum 2x Class(TObject) und einmal nicht? ist TTrackManager nicht eine classe? und und
Schon steh ich da ich alter Torr und bin so schlau wie zuvor.
Peinlich .
Gruss Alf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Bergmann89
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: Do 14.07.11 20:18
Hey,
der TTrackManager muss natürlich eine class(TObject) sein, hab ich mich vertippt, sry (habs oben auch angepasst).
Das mit den Methoden bei TTrack kann man so machen, muss man aber nicht. Man könnte auch TTrack von TObjektList erben lassen: TTrack = class(TObjectList) oder man macht ein property auf fEventList um auf die Liste zuzugreifen: property EventList: TObjectList<TEvent> read fEventList; Das mit den extra Methoden ist zwar ein bischen mehr Aufwand, aber es ist übersichtlicher, wenn man die Klasse nutzen will (zumindest seh ich das so).
Du hattest ja das 2D-Array für die Tracks: Tracks: Array[0..15, 0..maxevents] of Byte; Das ist aber sehr unübersichtlich, also hab ich das Ganze in die 3 Klassen zerlegt. [0..15] ist der TTrackManager der verwaltet alle Tracks (egal ob es 15, 20 oder 1000 sind). Das [0..maxevents] wird durch TTrack dargestellt. Auch hier wieder mit beliebig vielen Events, da die Liste dynamisch erweiterbar ist. Letzendlich wird nur noch das Event (bei dir der of Byte-Teil) in der Klasse TEvent gespeichert. Natürlich mit ein paar Zusatzinfos. Das meine Lösung auch zweidimensional ist sieht man daran, das eine Liste (fTrackList) ein Objekt verwaltet was wieder eine Liste besitzt (fEventList). Und ne Liste is intern auch nix anderes als ein Array, nur das die ganze Verwaltung (hinzufügen, löschen, ...) schon vorgefertigt ist. Und ein Array mal noch ein Array ist nun mal ein 2D-Array
Ich hoffe die Ausführungen konnten ein wenig Licht in das Dunkel bringen ^^
MfG Bergmann
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
Für diesen Beitrag haben gedankt: ALF
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Do 14.07.11 20:35
Jo, nun hab ichs verstanden und damit auch ne bildliche Vorstellung
Bergmann89 Danke
Mach mich run ans umsetzten. Mal sehen wie weit ich komme!
Gruss Alf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Narses
Beiträge: 10181
Erhaltene Danke: 1254
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 15.07.11 01:33
Moin!
Also so richtig gut sieht mir das Klassenmodell noch nicht aus, das scheint nicht wirklich die Realität abzubilden. Ich mach nochmal einen anderen Vorschlag.
Vergessen wir mal kurz komplett alles, was wir über Midi-Files wissen und schauen uns an, wie ein, sagen wir mal " Lied", aufgebaut ist: - Zu irgendeinem Zeitpunkt, nennen wir ihn mal 0, startet das Lied, und zwar mit einem oder mehreren gleichzeitig erklingenden Tönen
- Diese Töne haben eine bestimmte Dauer, die wollen wir mal in Millisekunden (ms) messen
- Wenn alle Starttöne ihre Dauer durch haben, kommt entweder eine Pause (nix zu hören) oder wieder ein oder mehrere Töne, usw.
Man könnte also ein Lied als Liste von Tönen (oder Tongruppen = Akkorde) definieren, die einen Startzeitpunkt (in ms bezogen auf den Liedstart = 0) und eine Dauer (ebenfalls in ms) haben. Pausen werden nicht erfasst sondern ergeben sich implizit dadurch, dass diese Intervalle nicht in der Ton-Liste des Liedes enthalten sind. Interessant ist, dass diese Ton-Liste im Speicher noch nicht einmal sortiert sein muss, um trotzdem korrekt ein Lied abzubilden. Der einfacheren Verarbeitung halber könnte man die Töne aber z.B. aufsteigend nach Startzeitpunkt sortieren.
Mit dieser Datenstruktur ist es relativ einfach, ein Lied zu "erstellen": man fügt einfach irgendwo ein Objekt mit den Eigenschaften Startzeitpunkt/Tonhöhe/Dauer in diese Liste ein und ist fertig. Diese Informationen könnte man relativ leicht aus einer GUI vom Benutzer entgegennehmen, wenn man z.B. eine Dauer als Vorgabe einstellt, als Y-Koordinate eine Klaviatur zeigt und aus der X-Koordinate des Mausklicks den Startzeitpunkt ableitet.
Schön, jetzt haben wir also unser Lied in dieser Liste. Wie macht man daraus nun ein MidiFile im korrekten Bytecode?! Konzept: - Wir gehen der Einfachheit halber mal nur von einem Track aus. Wenn man das mit einem Track kann, dann geht das mit 15 weiteren genauso.
- Zunächstmal benötigen wir passende Objekte für die Midi-Ereignistypen: NoteOn und NoteOff mit den Eigenschaften Ereigniszeitpunkt und Tonhöhe
- Diese Objekte "wissen", wie man "sich selbst" im Midi-Bytecode darstellt
- Weiterhin brauchen wir noch eine Liste, die diese Objekte verwaltet und nach Ereigniszeitpunkt sortieren kann (oder auch gleich hält), nennen wir sie mal MidiTrack
- Jetzt nehmen wir unsere Lied-Liste, gehen einfach die darin enthaltenen Objekte durch und erstellen passende NoteOn- (zum Startzeitpunkt) und NoteOff-Objekte (Startzeit+Dauer) in der MidiTrack-Liste (spannend ist: bis jetzt ist immer noch überhaupt keinerlei Sortierung notwendig )
- Wenn die MidiTrack-Liste gefüllt ist (oder anders gesagt, das Lied abgearbeitet ist), sortieren wir die darin enthaltenen Midi-Ereignisse nach Zeitpunkt (falls die Liste das nicht schon beim Einfügen gemacht hat)
- Abschließend gehen wir nun diese MidiTrack-Liste durch und lassen die darin enthaltenen Ereigniss-Objekte sich selbst in den Zieldatenstrom schreiben (-> das MidiFile). Dabei merken wir uns den letzten Ereigniszeitpunkt: ist der Zeitpunkt des nächsten Ereignisses der Gleiche, einfach weiter schreiben lassen, sonst für den Differenz-Zeitraum den Delay-Bytecode selbst erzeugen und schreiben
- Das Ganze noch etwas mit dem MidiFile-Header und anderem Beiwerk dekoriert, fertig.
Und damit das nicht so trocken ist, hier noch etwas Deklaration als Vorschlag (ich habe "nur" D7pro, deshalb mal darauf zugeschnitten):
Delphi-Quelltext 1: 2: 3:
| TLied = class(TObjectList) end; | Jetzt die Objekte für ein Lied: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
| TBasiston = class(TObject) private FStartzeit: Integer; FDauer: Integer; public property Startzeit: Integer read FStartzeit write FStartzeit; property Dauer: Integer read FDauer write FDauer; procedure WriteToMidiTrack(AMidiTrack: TMidiTrack); virtual; abstract; end;
TTon = class(TBasiston) private FTonhoehe: Integer; public property Tonhoehe: Integer read FTonhoehe write FTonhoehe; procedure WriteToMidiTrack(AMidiTrack: TMidiTrack); override; end;
TIntArray = array of Integer;
TAkkord = class(TBasiston) private FTonhoehen: TIntArray; function GetCount: Integer; procedure SetCount(const Value: Integer); function GetTonhoehe(const Index: Integer): Integer; procedure SetTonhoehe(const Index, Value: Integer); public property Count: Integer read GetCount write SetCount; property Tonhoehen[const Index: Integer]: Integer read GetTonhoehe write SetTonhoehe; procedure WriteToMidiTrack(AMidiTrack: TMidiTrack); override; end; | Jetzt der MidiTrack: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| TMidiTrack = class(TObjectList) private FTrackNr: Integer; protected procedure SortiereNachStartzeit; public property TrackNr: Integer read FTrackNr write FTrackNr; procedure WriteToStream(AStream: TStream); end; | Und noch die Midi-Ereignisse (nur die Grundlegenden): Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| TMidiEvent = class(TObject) private FZeitpunkt: Integer; public property Zeitpunkt: Integer read FZeitpunkt write FZeitpunkt; procedure WriteToStream(AStream: TStream); virtual; abstract; end;
TNoteEvent = class(TMidiEvent) private FTonhoehe: Integer; public property Tonhoehe: Integer read FTonhoehe write FTonhoehe; end;
TNoteOn = class(TNoteEvent) public procedure WriteToStream(AStream: TStream); override; end;
TNoteOff = class(TNoteEvent) public procedure WriteToStream(AStream: TStream); override; end; | Dieses Konzept sollte dein Grundproblem, wie man ein Lied "malt", relativ gut kapseln. Schau mal drüber, ob du damit was anfangen kannst.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Fr 15.07.11 17:44
Ok, bitte nicht gleich alles auf einmal. Darum erst mal der Reihe nach.
Bergmann89Ich hab mal CP gemacht. Schanlls natürlich nicht.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102:
| type TEvent = class(TObject) Track: Integer; Deltatime: integer; Event: Byte; Wert1: Byte; Wert2: Byte; end;
type TTrack = class(TObject) private fEventList: TObjectList<TEvent>; public procedure AddEvent(event: TEvent); procedure DelEvent(ID: Integer); end;
type TTrackManager = class(TObject) private fTrackList: TObjectList<TTrack>; public constructor Create(TrackCount: Integer); end;
type TForm1 = class(TForm) ... ... private myTracks: TTrack; myTrackManager: TTrackManager; var Form1: TForm1;
implementation
{$R *.dfm}
constructor TTrackManager.Create(TrackCount: Integer); var i: Integer; begin inherited Create; end;
procedure TTrack.AddEvent(event: TEvent); begin
fEventList.Add(event); end;
procedure TTrack.DelEvent(ID: Integer); begin end;
procedure TForm1.FormCreate(Sender: TObject); begin myTracks:= TTrack.Create; myTrackManager:= TTrackManager.Create(2) ; end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin myTrackManager.Free; myTracks.Free; end;
procedure TForm1.Button1Click(Sender: TObject); var newEvent: TEvent; begin newEvent:= TEvent.Create; try newEvent.Track:= 0; newEvent.Deltatime:= 196; newEvent.Event:= $90; newEvent.Wert1:= $27; newEvent.Wert2:= $7f; mytrackmanager.fTrackList.Items[0].AddEvent(newEvent); finally end; end; | kein Durchblick sorry, MyTrackManager, MyTracks = Nil
zumal wo bleibt den, die eigentliche Liste(TObjectList)?
Narses möchte jetzt erst mal das vom Bergmann89 verstehen.
Dann Verstehe ich Deins wesentlich besser Deins ist natürlich Spitze!
Zitat: | Interessant ist, dass diese Ton-Liste im Speicher noch nicht einmal sortiert sein muss, um trotzdem korrekt ein Lied abzubilden. |
hab ich bei meiner kleinen Liste(TObjektliste) auch festgestellt
Das da noch was passieren muss(sortieren und abspielbar machen) ist mir auch klar. Du hast ja schon gesagt:
Zitat: | Vergessen wir mal kurz komplett alles, was wir über Midi-Files wissen und schauen uns an, wie ein, sagen wir mal "Lied", aufgebaut ist: |
und da ist mein Problem, Ich wollte ebend alles schon in der eigentlichen MidiForm, MeinFehler
Ich gehe mal davon aus, wenn ich Bergann89 seins verstanden habe, dürfte ich mit Deinem Vorschlag nur halb soviel schwierigkeiten haben(ausnahme werden die Berechnungen werden(delaytime)). Aber bis dahin muss ja erst mal das andere funcen.
Ich Danke erst mal, für eure Ausdauer mit mir.
EDIT:
QNarses, hab mit Deinem Vorschlag angefangen. komischerweise komme ich damit besser zurecht.
Ausser die Proceduren(klar die können ja noch nicht fertig sein)
Hab allerdings noch zwei hinzugefügt EditNote und DeleteNote.
Wahrscheinlich komm ich nicht mit:
fEventList: TObjectList<TEvent>;
fTrackList: TObjectList<TTrack>;
zurecht. Muss das nicht auch Createt werden bevor ich darauf zugreifen kann?
Gruss ALf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Narses
Beiträge: 10181
Erhaltene Danke: 1254
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 15.07.11 22:44
Moin!
ALF hat folgendes geschrieben : | Narses möchte jetzt erst mal das vom Bergmann89 verstehen. |
Wie du möchtest. Allerdings will ich dich trotzdem noch auf zwei, IMHO wesentliche Dinge, hinweisen, die dort nicht umgesetzt sind und dich deshalb eher Zeit kosten, als Nutzen bringen:
Du hast weiter oben dieses MidiEvent-Objekt eingeführt:
ALF hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| type TEvent = class(TObject) Track: Integer; Deltatime: Integer; Event: Byte; Wert1: Byte; Wert2: Byte; end; | |
Das Midi-Format ist AFAIR relativ in seinen Zeitbezügen aufgebaut. Das kommt vermutlich daher, dass man die Ereignisse von einem live eingespielten Ereignisstrom so einfach ablegen kann. Allerdings willst du einen anderen Weg gehen: du willst den Ereignisstrom designen, und da wirst du mit einem relativen Ansatz (genau das drückt Deltatime ja wohl aus) große Probleme haben. Mein Tipp: stell das auf ein absolutes Zeitraster um und mach erst in einem zweiten Schritt die Konversion auf relative Angaben. Du brichst dir beim Bearbeiten sonst noch die Finger...
Verabschiede dich als Speicherkontainer vom dem Array für den MidiBytecode und nimm Stream-Klassen, die verwalten ihre Größe selbst - das kann das Array nicht! Warum sich zusätzlich Arbeit aufhalsen.
cu
Narses
//EDIT:
ALF hat folgendes geschrieben : | Narses, hab mit Deinem Vorschlag angefangen. komischerweise komme ich damit besser zurecht. |
Das liegt am absoluten Zeitraster.
ALF hat folgendes geschrieben : | Wahrscheinlich komm ich nicht mit:
fEventList: TObjectList<TEvent>;
fTrackList: TObjectList<TTrack>;
zurecht. Muss das nicht auch Createt werden bevor ich darauf zugreifen kann? |
So ist es, ich bin mal davon ausgegangen, dass das klar ist.
//EDIT2: Also, falls das wirklich nicht klar ist die Klassen sind alle noch nicht vollständig ausdifferenziert, das ist sehr roh und nicht c&p-fähig.
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
Bergmann89
Beiträge: 1742
Erhaltene Danke: 72
Win7 x64, Ubuntu 11.10
Delphi 7 Personal, Lazarus/FPC 2.2.4, C, C++, C# (Visual Studio 2010), PHP, Java (Netbeans, Eclipse)
|
Verfasst: Fr 15.07.11 23:30
Hey,
wie Narses schon gesagt hat müssen die ganzen Variablen eines Objekts im Constructor initialisiert un im Destructor ggf freigegeben werden. Bsp.:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| constructor TTrackManager.Create(TrackCount: Integer); var i: Integer; begin inherited Create; fTrackList := TObjectList.Create(True); for i := 0 to TrackCount-1 do fTrackList.Add(TTrack.Create); end;
destructor TTrackManager.Destroy; begin fTrackList.Free; inherited Destroy; end; |
Bei Narses Lösung musst du die Listen nicht extra anlegen, da seine Objekte von den Listen erben un die dadurch im Konstructor erzeugt werden. Hast du schonmal richtig mit Klassen gearbeitet? Wenn nicht solltest du dir vlt erstmal ein Tutorial über die Möglichkeiten und Grenzen von Klassen durchlesen (und natürlich verstehen) eh du dir selbst eine Klassenstruktur aufbaust.
MfG Bergmann
_________________ Ich weiß nicht viel, lern aber dafür umso schneller^^
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Sa 16.07.11 01:17
Bergmann89 hat folgendes geschrieben : | Hast du schonmal richtig mit Klassen gearbeitet? Wenn nicht solltest du dir vlt erstmal ein Tutorial über die Möglichkeiten und Grenzen von Klassen durchlesen (und natürlich verstehen) eh du dir selbst eine Klassenstruktur aufbaust.
|
jo hab ich 1mal type TScanThread = class(TThread) ewig her.
Tuts lese ich, leider in vielen Fällen sehr abstrakt gehalten und wenn ich die Theorie verstanden habe, scheiterts an der Praxis Und damit man sieht das es nicht nur CP ist:
Delphi-Quelltext 1: 2:
| fTrackList := TObjectList.Create(True); fTrackList:= TObjectList<TTrack>.Create(True); |
Dabei reicht ja manchmal ein simples Beispiel aus. Nur die, die ich finde, bekomme ich meist nicht in meinem Kontext, mit Aussnahme meinem kleinem Beispiel oben, wo ich auch lange suchen musste.
Das ftracklist createt werden muss hab ich vermutet, aber ebend nur vermutet!
Das da ein destructor rein muss hatt ich vermutet, aber ebend nur vermutet!
Aber das evtl Wissen heist bei mir leider noch nicht das können(selbst wenn ich es in einem andern Kontext schon mal gemacht habe).
Egal, jetzt funct auch Deins
@Narses:
DeltaTime hab ich nur aus Midi übernommen. Egentlich gibt es ja kein StartTime oder StopTime, es ist nur ein Zeitwert vor einem xbeliebigen Event. Es spielt also keine Rolle was für ein Event danach folgt. Auch wenn mann in diesem Fall bei NoteOn/NoteOff davon reden könnte. Was aber im "running status" schon nicht mehr stimmt.
Was den Zeitraster betrifft, verwende ich ihn ja schon, sonst könnt ich keine Note Zeichnen(zwischenspeichern), abspielen bzw wieder ins Grid zurückzeichnen
Was nun das graphische Zwischenspeichern betrifft, mit den Objeklisten, ist absolut genial!
Weil hier, wie Du schon sagst, die Reienfolge keine rolle spielt.
Nur, muss daraus jetzt auch die Reihenfolge erstellt werden(real) das ich es mal abspielen kann, was ich Design hab
also muss ja, der, die Track(s) ebenfalls schon fertig sein. Dies klappte ja bei der altbacken Methode von mir. Nur keine akkorde.
In der Theorie ist mir ja alles klar. Aaaaber die praktische Umsetztung in Code omg.
Mal sehen, wie weit ich jetzt mit der modernen komme, ohne Hilfe.
Wobei ich jetzt schon weiss, dass das Umwandeln zum abspielen, nen haufen Mathe ist omg!
THX an euch.
Gruss Alf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Narses
Beiträge: 10181
Erhaltene Danke: 1254
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Sa 16.07.11 11:09
Moin!
ALF hat folgendes geschrieben : | DeltaTime hab ich nur aus Midi übernommen. Egentlich gibt es ja kein StartTime oder StopTime, es ist nur ein Zeitwert vor einem xbeliebigen Event. |
Ich war ja schon drauf und dran gestern noch eine funktionsfähige Demo zu schreiben, aber ich bin dann leider doch an dem Midi-Timing gescheitert... Irgendwie kriege ich das mit der Bedeutung der Ticks und dem Wert im Header nicht auf die Reihe. Hast du verstanden, wie man daraus z.B. Millisekunden macht?
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Sa 16.07.11 13:35
Ich soll was beschreiben ! Ich kanns versuchen.
Eine 1/4note ist fix nicht veränderbar, errechnet sich aus 60000000microsec/120bpm = 500000, wurde mal festgelegt. allerdings werden die 1/4noten mit 480000 geschrieben Warum keine ahnung
Verändere ich den bpm Wert, erhalte ich die zu speichernde Geschwindigkeit für den Header, in diesem Fall währe es auch 500000 microsec.
Beim lesen des Headers, wird nun 6000000/500000 = 120, So hatt man wieder die eigentliche Geschwindikeit bpm.
Nun muss beim abspielen die sogenannten Wartezei errechnet werden bis man weiterlesen kann im Track.
timeWait:=((( 120 / (6000000/50000)) * Noteleange); in diesem Fall nehemen wir die 1/4Note
Notenlänge ist der Timewert vor einem Event!!!! eigentlich DeltaTime oder bei Dir StartTime, Stoptime. aber vorsicht mit diesen 2 Begriffen.
und nun das ganze mit gettickcount in einem thread ablaufen lassen
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| Tick:= Gettickcount + timwait; while timewait > 0 do begin application.pocessmassage timewait:= tick - Gettickcount end |
ist das abgelaufen wird das nächste Event gelesen und sofort ausgeführt, sofort der nächste Timwert gelesen und wieder an den thread übergeben usw bis das Songende erreicht ist.
Ich wünschte mir, das profesioneller zu beschreiben. Ich kann es aber nicht anders beschreiben sorry!
Gruss Alf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
Narses
Beiträge: 10181
Erhaltene Danke: 1254
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: So 17.07.11 23:55
Moin!
ALF hat folgendes geschrieben : | Ich wünschte mir, das profesioneller zu beschreiben. Ich kann es aber nicht anders beschreiben sorry! |
Danke, kein Problem, hat schon gereicht.
Die entscheidende Erkenntnis ist, dass die MidiTicks eine relative Zeiteinheit sind, die nur ein Verhältnis der Notenwerte untereinander beschreibt. Das eigentliche "Timing" (also die Umsetzung in tatsächliche Zeitwerte) wird erst mit dem Metaevent $51 = Mikrosekunden pro Viertelnote gesetzt. Falls noch einer auf der Suche nach den Timing-Details im MidiFile-Format sein sollte: hier ist das ganz brauchbar erklärt. Weiterhin hilft dieses Tool extrem bei den ersten Gehversuchen.
So, jetzt aber zur Sache. Ich habe mal eine Funktions-Demo meines Konzepts weiter oben gemacht. Damit sieht die Erstellung eines MidiFiles so aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| var Lied: TLied; MidiFile: TMidiFile; begin Lied := TLied.Create; try Lied.Add(TTon.Create(72, 0, 480)); Lied.Add(TTon.Create(74, 480, 480)); Lied.Add(TTon.Create(76, 960, 480)); Lied.Add(TTon.Create(77, 1440, 480)); Lied.Add(TTon.Create(79, 1920, 960)); Lied.Add(TTon.Create(79, 2880, 960)); Lied.Add(TAkkord.Create(72, 4000, 1920)); MidiFile := TMidiFile.Create; try MidiFile.AddTrack(TMidiTrack.CreateFromLied(Lied)); MidiFile.WriteToFile(ExtractFilePath(Application.ExeName)+'Test.mid'); ShowMessage('OK'); finally MidiFile.Free; end; finally Lied.Free; end; | Im Anhang die zugehörige Unit mit den Klassen. Viel Spaß beim Experimentieren.
cu
Narses
//EDIT: StartTicks korrigiert.
Einloggen, um Attachments anzusehen!
_________________ There are 10 types of people - those who understand binary and those who don´t.
Zuletzt bearbeitet von Narses am Mo 18.07.11 15:28, insgesamt 1-mal bearbeitet
Für diesen Beitrag haben gedankt: ALF
|
|
ALF
Beiträge: 1085
Erhaltene Danke: 53
WinXP, Win7, Win10
Delphi 7 Enterprise, XE
|
Verfasst: Mo 18.07.11 14:36
Hi, Narses Dank für Deine Mühen und Ausdauer mit mir. Die links kenne ich, hab ich alle als Favoriten bei mir, selbst die engl obwohl ich kein engl kann. Würde mir gern das ganze anschauen, was Du sozusagen übernacht gemacht hast, um zu sehen wie ich es bei mir umsetzten kann. Hab ja im Prinzip alles schon da zum auslesen, nur nicht vom zeichnen zum abspielbaren File, also Musik erstellen. Mich interresiert nähmlich das berechnen! Musik ist ja nur Mathe mit Emotion
Mal sehen ob ich klar komme
Allerdings gleich ne frage dazu!
Delphi-Quelltext 1: 2: 3: 4:
| Lied.Add(TTon.Create(72, 480, 480)); Lied.Add(TTon.Create(74, 960, 480)); |
Fange ich also bei meinem 'Grid bei startposition null an steht dann da
Delphi-Quelltext 1: 2:
| Lied.Add(TTon.Create(72, 0, 480)); Lied.Add(TTon.Create(74, 480, 480)); | usw.
Da ich wieder im rechnen blöd bin, was für ein integerwert würde den da stehen wenn die letzte Note erst nach 6 oder 9 Minuten kommt??? Währe das nicht schon > FFFF7F??
Gruss ALf
_________________ Wenn jeder alles kann oder wüsste und keiner hätt' ne Frage mehr, omg, währe dieses Forum leer!
|
|
|