Hilfe bei mehrdimensionalen Text-"Array" benötigt (Problem gelöst > Struct )

So, ich habe jetzt wirklich lange versucht, das Problem selber zu lösen, ab ich bin anscheinend zu blöde dazu. Vielleicht kann mir hier ja jemand helfen. Eins vorweg: Das Subjekt dieses Artikel soll nur mein Problem beschreiben. Ob mehrdimensionalen Text-"Arrays" übehaupt machbar oder die richtige Lösung sind? Keine Ahnung. Bin für jede Hilfe offen.

Ich habe mir ein Midipedal gebaut, dass die Sounds meines Gitarreneffektgerätes in der Reihenfolge abrufen soll, in der diese im gewählten Song vorkommen. Ich möchte zu den verschiedenen Parts der jeweiligen Songs jeweils eine Text-Info, eine Sound-Info und einen Midi-Befehl abspeichern.
Mit einem Knopfdruck soll dann zum nächsten Part gesprungen werden.

Für einen Song ist das ja kein Problem (wobei ich keine Ahnung habe, ob meine Lösung elegant ist... vermutlich eher nicht).

Momentan sieht es für einen Song in etwas so aus:

// Temporäre Arrays

char* partNames[] = {"1Strophe","2Bridge","3Refrain","4Strophe","5Solo"};
int partCounterMax = sizeof(partNames) / sizeof(int);

void setup() {
  Serial.begin(31250);
  Serial.println("Start: ");
}

void loop() {
        
  Serial.println("Parts: ");

  for (int zaehler=0; zaehler < partCounterMax ;zaehler=zaehler+1){
      Serial.println(partNames[zaehler]);
         
  }  
 }

Ist jetzt natürlich erst mal nur Testcode um zu zeigen, was ich aktuell mache und auch nur für eine der drei benötigten Infos. Lenkt aber glaube ich besser den Fokus auf mein Problem, als den kompletten Code des Pedals hier zu posten.

Jetzt ist meine Frage, wie ich das für mehrere Songs umsetze. Meine erste leichtsinnige Idee, war, dass man das doch einfach mit einem mehrdimensionalen Array umsetzt. Nur habe ich jetzt nach längerer lesen und diversen Tutorials festgestellt: Ich bin zu blöde, um das Array-Konstrukt von C zu verstehen. Es geht einfach nicht in meinem Kopf ein. Zumindest, sobald man etwas anderes als Zahlen verwenden will.

Die Struktur wäre in etwa sowas hier:

Song 1

  • Part1 Part2 Part3
  • Sound1 Sound2 Sound3
  • Midi-1 Midi-2 Midi-3

Song 2

  • Part1 Part2 Part3 Part4
  • Sound1 Sound2 Sound3 Sound4
  • Midi-1 Midi-2 Midi-3 Midi-4

Hat von Euch jemand eine Idee, wie ich es umsetzen kann? Wie, ist mir eigentlich komplette egal. Muss auch kein Array sein, wenn es was Besseres gibt. Ich bin für jeden Vorschlag offen, solange ich den umgesetzt oder ein Beispiel vor die Nase bekommen, dass dicht genug dran ist, um es für meine Zwecke anpassen zu können.

Da ich einen Teensy 3.2 einsetze ist der Speicherplatz nicht so ganz das Problem. Es werden insgesamt so 20 bis 30 Songs mit jeweils 4 bis 6 Parts werden. Textlänge sollte jeweils unter 20 Zeichen bleiben.

Hat jemand eine Idee?

Maximallänge von Song, Part, Sound, Midi ermitteln.

Zeile mit Song|Part1;part2;...|Sound1;Sound2;...|Midi1;Midi2;...\n (Zeilenvorschub)+\0 (Abschluß) = Zeilengröße

Char-Array mit 30 Zeilen zu je Zeilengröße. Schauen, ob das rein passt.

Pro Zeile aufdröseln mit strtok/strtok_r

Gruß Tommy

in5y372:
Hat jemand eine Idee?

Hast Du meinen letzten Beitrag in Deinem vorangegangenen Thema gelesen. Da hatte ich Dir eine Lösung vorgeschlagen, wäre sozusagen meine Idee :slight_smile:

Das ist der Nachteil, wenn man immer wieder einen neuen Thread beginnt.
Informationen gehen verloren und die TO wundern sich, dass nichts raus kommt.
Da haben sie aber selbst schuld.

Gruß Tommy

Interessant ist, dass jeder Song eine unterschiedliche Anzahl Parts hat. ca. (1 ... 6 ) Diese Anzahl bezieht sich dann pro Song jeweils auf Partname, Sound, und Midi

Partname ist ein char* ( auf 0..20 Zeichen )
Sound und Midi ist ? (das zeigt dein Beispiel noch nicht)

Es geht immer nur von einem Song zum nächsten, man muss also nicht direkt auf z.B. Song33 springen können, und von vornherein wissen, wieviele es gibt, wenn eine Kennung "kein weiterer Song" ausreicht.
Das fällt aber weiter unten einfach mit ab
Jeder Song wird vermutlich auch einen Titel oder Namen haben, das kommt nicht so ganz raus.

==========================================

Normale Menschen (also Leute wie ich) können nicht in mehreren Dimensionen denken.
Das Ganze als mehrdimensionale Arrays in C++ ist auch der Aufgabenstellung nicht angemessen, weil eine wesentliche Eigenschaft von C/C++ - Arrays ist, dass jedes Element gleich groß ist und die Anzahl Elemente jeder Dimension gleich ist.

Die vorläufig letzte Frage ist, wie du diese Daten im Teensy speichern und dort hineinbringen willst?
Als wie auch immer strukturierten Teil des Codes? Oder kommen die Daten unabhängig vom bearbeitenden Code zur Laufzeit dazu? Abder das kann man auch später festlegen, wenn man festhält, dass zur Laufzeit alles gleichzeitig im RAM ist.
Fangen wir mit einer Datendefinition an, die unterschiedliche Größen der Einzelelemente erlaubt und einfaches Springen zum jeweils nächsten Element der gleichen Ebene.

Deine bisherige Datendefinition-Variablen müssen erstmal eindeutig sein, zB indem ich sie hier mit einer vorangestellten Songnummer S01 ... S99 individualisiere. Wie gesagt, damit wir erstmal durch einfaches Hochladen des gesamten Sketches gleich alle Daten zur Verfügung haben.

const char* S01Name = "TestSong 01";
const char* S01partNames[] = {"1Strophe","2Bridge","3Refrain","4Strophe","5Solo"};
const int S01partCount = sizeof(S01partNames) / sizeof(char*); //  sollte 5 ergeben
const char* S01Midi[S01partCount] = {" ", " ", " " , " " , "  "};    //  TODO
const char* S01Sound[S01partCount] ; //  NOCH MEHR TODO

Dann brauchen wir eine Struktur, die uns Zugriff auf diese Daten gibt. z.B. sowas:

struct Song {
 const char * name;
 int partCount;
 const char ** partNames; // zeiger auf ein char* Array !   
 const char ** midis;
 const char**  sounds;
};
Song S01 {S01Name, S01partCount, S01partNames, S01Midi, S01Sound};
// das gleiche später für die anderen Songs

// Jetzt fehlt nur noch ein einziges eindimensionales Array um an alle Songs zu kommen 

Song mySongs[] {S01 /* ,S02, S03 usw */ }; 
const int songCount = sizeof (mySongs) / sizeof (Song); // z.Zt.  1

Deine Aufgabe ist jetzt, ausgehend von mySongs per Sketch auf den Namen des ersten Songs zu kommen und die Namen der Parts zu finden und auszugeben, und natürtlich zu erkennen, wie viele Songs mit jeweils wie vielen Parts es gibt... ( 1 mit 5 )

Dann solltest du dir Gedanken darum machen was ein sound und ein midi eines Songparts ist und wie man das im RAM speichert. char* könnte evtl. suboptimal sein...

Die ganzen Daten kann man natürlich auch aus einer csv oder XML (oder JSON) oder einer anderen Datei einlesen, aber das kriegen wir evtl. später. ( Mein Vorschlag, um dich erstmal von den unseligen mehrdimensionalen Arrays wegzukriegen, die wo kein normaler Mensch durchblickt, und die hier auch nicht passen.)

agmue:
Hast Du meinen letzten Beitrag in Deinem vorangegangenen Thema gelesen. Da hatte ich Dir eine Lösung vorgeschlagen, wäre sozusagen meine Idee :slight_smile:

Tommy56:
Das ist der Nachteil, wenn man immer wieder einen neuen Thread beginnt.
Informationen gehen verloren und die TO wundern sich, dass nichts raus kommt.
Da haben sie aber selbst schuld.

Gruß Tommy

Moin, den alten Thread habe ich nicht weiter verfolgt, weil ich da zu sehr auf CSV, XML und das externe Laden versteift war. Ist mit nem Teensy jetzt nicht mehr notwendig. Ich wollte noch mal ergebnissoffen ohne zuviel Vorgaben fragen. Außerdem bezieht sich der alte Thread ja eher auf das Einlesen und parsen einer CSV-Datei. Jetzt "kämpfe" ich ja nur noch mit einer sinnvollen Array-Struktur. Also gleiches Projekt aber anderes Thema :-).

Es werden insgesamt so 20 bis 30 Songs mit jeweils 4 bis 6 Parts werden. Textlänge sollte jeweils unter 20 Zeichen bleiben.

also zunächst wohl ein Array mit 30 Einträgen.

je nach dem wie du die part, sound, midi brauchst würde sich da ein struct anbieten, für midi brauchst ja kein char array oder?

Dann alle 4 Variabeln in ein array of struct mit 30 Elementen.

nicht schön, aber einfach im zugriff ala

track[1].song
track[1].partname[2]
track[1].sound[2]
track[1].midi[2]

definiere also mal grundsätzlich die Datentypen für

song
partname
sound
midi

in5y372:
Jetzt "kämpfe" ich ja nur noch mit einer sinnvollen Array-Struktur.

Aus meinem Vorschlag die Datenstruktur:

class Setlist {
    char song[MAXCOL];
    char part[MAXCOL];
    char sound[MAXCOL];
    char midi[MAXCOL];

Wobei die Idee, neue Daten von einer SD-Karte einzulesen, grundsätzlich richtig ist. Solange die Struktur der Daten auf der SD-Karte per Programm zu interpretieren ist, läßt sich das realisieren. Verschiedene Formate haben Vor- und Nachteile, das muß man abwägen. XML ist kein schlechter Ansatz, solange Du nicht einen allgemeinen Parser bauen willst, sondern Dich auf Dein spezielles Anliegen konzentrierst.

In meinem Vorschlag fehlt jegliche Fehlererkennung und -behandlung, das wäre noch zu machen.

Ich kenne übrigens eine Datenbank, die variable Feldlängen verwendet. Leider ist mir noch keine Bibliothek im Arduino-Umfeld begegenet, die das unterstützt, denn das wäre vermutlich die für Dich optimale Lösung.

PS: @noiasca, wir haben parallel geschrieben :slight_smile:

wollt's editieren, aber ich machs nun extra

struct myMonster     //achtung da sind 20+6*20+6+6=152 bytes pro instanz!!!
{
  char song[20];
  char partname[6][20];
  uint16_t sound[6];
  uint16_t midi[6];
};

myMonster title[5] // reduziert für Arduino Uno
{
  { "Abba\0",
    {"arrival\0", "the name of the\0"},
    {1, 2},
    {3, 4}
  },
  { "Bee Gees\0",
    {"islands in the\0", "nightfever\0", "by\0"},
    {5, 6, 8},
    {7, 8, 9}
  }
};


void output(byte index, byte part)
{
  Serial.println(title[index].song);
  Serial.println(title[index].partname[part]);
  Serial.println(title[index].sound[part]);
  Serial.println(title[index].midi[part]);
}


void setup() {
  Serial.begin(115200);
  Serial.println(F("Data-Missuse"));

  output(0, 1);
  output(1, 2);
}

void loop() {
}

Ausgabe:
Data-Missuse
Abba
the name of the
2
4
Bee Gees
by
8
9

rein äugisch ist es ok.

Hui, ne Menge vielversprechende Ansätze. Struct ist noch neu für mich. Da muss ich mich erst mal einlesen. Der Vorschlag von michael_x ist ja so umfangreich, dass ich mir den auch erst mal genauer zu Gemüte führen muss. Klingt sehr interessant (auch wenn ich ihn noch nicht so ganz durchdrungen habe :slight_smile: ). Der Vorschlag mit dem strtok/strtok_r scheint mir am einfachsten umzusetzen zu sein. Werde ich als erstes mal versuchen.

Was mir beim durchlesen von michael_x Vorschlag für mein Projekt aufgefallen ist, ist, dass sich viele Texte bei meinem Projekt wiederholen. Als Parts kommen überwiegen die Wörter "Strophe", Refrain", "Solo", "Bridge" und dergl. vor. Sound soll der Name des entsprechenden Sound am Effektgerät sein. Das ist auch auf etwa 8 bis 10 Wörter beschränkt.

Ich stelle mir gerade vor, dass ich die Wörter jeweils in ein Array schiebe, und dann in ein zweidimensionales Int-Array einfach nur die Positionen der Wörter sowie den jeweiligen Midi-Befehl schreibe. Wenn ich nicht 100mal das gleiche Wort ablege könnte es ja auch durchaus dem Speicherpltz zuträglich sein.

Schon mal herzlichen Dank für die Hilfe. Habe jetzt erst mal für die nächste Woche genügend Denkanstöße, mit denen ich weiter laufen kann.

Update (noiascas Post kam rein, wärend ich das oben getippt habe): Wow, das ist doch mal eine Vorlage. Danke schön :slight_smile:

gib bescheid wenn du Vorschläge und in meinem Fall #8 verstanden hast.

Ich habe jetzt noch eine Version, wo man die Song-Titel von den Song-Parts separat hält. So wie man es eigentlich auch in einer DB machen würde und eine 1:n Verknüpfung von einer Tabelle "Songs" auf eine Tabelle "Parts" macht.

Ich zeigs aber erst, wenn du das mit Struct grundsätzlich verstanden hast.

Struct ist noch neu für mich. Da muss ich mich erst mal einlesen

Tu das. Dann bist du auch weg von mehrdimensionalen Arrays.
Ein Element einer struct kann ein Array oder ein Zeiger auf ein anderes Array sein. Im zweiten Fall können diese Arrays unterschiedlich groß sein bei verschiedenen struct Instanzen.
Gleiche structs (mit gleichem Aufbau und Größe) kann man dann wieder zu einem Array zusammenfassen.

Ich zeigs aber erst, wenn du das mit Struct grundsätzlich verstanden hast.

Das gilt auch für mein

michael_x 23.11. 22:11:
Deine Aufgabe ist jetzt, ausgehend von mySongs per Sketch auf den Namen des ersten Songs zu kommen und die Namen der Parts zu finden und auszugeben, und natürtlich zu erkennen, wie viele Songs mit jeweils wie vielen Parts es gibt... ( 1 mit 5 )

Über diese Hausaufgabe können wir gern diskutieren, wenn es unüberwindliche Probleme mit const char ** oder so gibt bevor sowas ähnliches aus deinem Sketch rauskommt:

TestSong 01 with 5 parts
  Parts:	1Strophe 	 2Bridge 	 3Refrain 	 4Strophe 	 5Solo 	 
End after 1 Songs

Viel Spaß mit struct

Das von mir verwendete class unterscheidet sich von struct nur durch private und public, also die Sichtbarkeit von Variablen und Methoden. Da darf man sich nicht irritieren lassen.

Stimmt.
Leute denen struct komplett neu und unbekannt ist, lernen nichts verkehrtes oder überholtes, wenn sie sich erstmal damit vertraut machen. Dass man Datenstrukturen auch Methoden hinzufügen kann, kriegen wir später.

Da darf man sich nicht irritieren lassen

Nie, sehr richtig.

Aber anregen schon. :wink:

@michael_x: Ich berichte da halt aus eigener Erfahrung, weil ich manche Erklärungen fälschlicherweise verworfen hatte, bis ich dann hier im Forum durch Mitlesen auf den richtigen Weg gebracht wurde. Daran habe ich mich erinnert.

War also nicht als Kritik gemeint. Schritt für Schritt, anders geht es nicht :slight_smile:

War also nicht als Kritik gemeint

Von mir auch nicht. :slight_smile:
Genaugenommen ist es mir auch zu mühsam, den anderen Thread nochmal genau anzusehen, nachdem ich mitbekommen habe, dass es dort mehr drum ging, dynamisch eine Datei in den RAM einzulesen.
Dies hier ist ja erstmal die Vorstufe dazu, den erforderlichen RAM sinnvoll zu strukturieren...

Moin, das struct von noiasca macht so ziemlich genau das, was ich brauche :-). Ich habe das jetzt mal in mein Test-Sketch eingebaut, aus "sound" auch noch mal ein char gemacht und mit 40 Test-Einträgen versehen. Paßt alles in den Speicher des Teensy :-). Das baue ich dann mal in mein Pedal-Sketch ein. Nichts desto trotz werde ich bezüglich des structs noch mal ein bisschen durchs Netz wildern um mir das noch ein wenig mehr anzulesen. Anwenden ist ja nur der halbe Weg zum verstehen :-). Kann aber ein paar Tage dauern, bis ich mich wieder melde. Zeit ist gerade etwas knapp. Wenn ich fertig bin poste ich hier mal ein Bild oder Video von dem Teil :-).

Vielden Dank für die Hilfe.

@noiasca: Kannst du vieleich noch ein Wort dazu verlieren, welche Vorteile man hat wenn man die Song-Titel von den Song-Parts separat hält? (Da gehts mir noch gar nicht darum, wie du das umgesetzt hast :slight_smile: )

ein Wort???

Speicherbedarfreduzierung

So, hier kommt wie versprochen ein Link auf das fertige Pedal. Funktioniert alles prima und der Speicher reicht auch üppig aus. Da kann ich noch ne Menge Songs schreiben, bis der voll ist. Werde bei Gelegenheit vielleicht das UI noch etwas schöner machen :-). Nach mal nen herzlichen Dank für die Hilfe :slight_smile:

Zum fertigen Pedal...

Danke für die Rückmeldung!