delay funktioniert nicht richtig

Hallo,
ich versuche zwei Sketche zu verbinden (LCD und Piezo) dabei funktioniert das delay nicht richtig. Es wird immer nur ein delay Wert angenommen. Ich habe schon viel ausprobiert komme aber zu keinem Ergebnis. Deshalb wende ich mich an euch.

Ich hänge den zusammengeführten mal Sketch an.

Im Prinzip soll ein bestimmtes Muster auf dem LCD und zur selben Zeit eine längere Tonabfolge ablaufen.

Muster_Ton.ino (1.01 KB)

Ich versuche mal auf Deutsch

Welches 'delay' meinen sie ?

tonePause = 200 das wird nicht andern, alle Tonen sind gleich in wie lange Sie dauern

delay(50); //funktioniert nicht in der gewünschten Geschwindigkeit
}

Program wartet 50 milliseconden. Also Ihren Program macht

Piezo Muzik Warten LCD sachen Wartem ---> und wieder

Aber ich glaube was Sie suchen ist: http://arduino.cc/en/Tutorial/BlinkWithoutDelay

Sie mochten zwei sachen zu gleich machen, das ist moglich mit dieser Link.

pulsarus: Im Prinzip soll ein bestimmtes Muster auf dem LCD und zur selben Zeit eine längere Tonabfolge ablaufen.

Sorry, dass ich mir den angehängten Sketch in dem Fall erst gar nicht ansehe: So etwas mußt Du komplett OHNE DELAY programmieren!

Im Prinzip mußt Du Intervalle bilden, nach denen etwas passieren soll. Und dann laufend die aktuelle Zeit abfragen, z.B. mit millis(). Und wenn das Zeitintervall überschritten ist, passiert was. Wenn das Intervall zum Setzen eines neuen LCD-Musters abgelaufen ist, kommt ein neues LCD-Muster. Wenn das Intervall zum Setzen eines neuen Tones abgelaufen ist, kommt ein neuer Ton.

Danke für die schnellen Antworten. Gibt es dafür vielleicht einen Beispielsketch. Ich habe das mit millis nicht so richtig verstanden. muss ich dann jede Note (bzw.Ton) einzeln ansprechen und die Pause auch?

pulsarus: Gibt es dafür vielleicht einen Beispielsketch. Ich habe das mit millis nicht so richtig verstanden. muss ich dann jede Note (bzw.Ton) einzeln ansprechen und die Pause auch?

Ja, Du musst für jedes Event jedes einzelnen Tasks einzeln festlegen, wann das Event wieder zuende ist und das nächste Event starten soll.

Wenn Du also einen Task "SpieleNoten()" programmierst, dann mußt Du beim Einschalten einer Note festlegen, wann sie wieder abgeschaltet werden soll, und beim Abschalten der Note mußt Du festlegen, wann die nächste Note wieder starten soll.

Ich habe Dir als Beispiel für Multitasking mit Arduino einen Beispiel-Sketch gemacht, der gleichzeitig drei Tasks abarbeitet: task_led(); ==> Blinken der Pin-13 LED im Halbsekundentakt task_tone(); ==> Simulation des Abspielens einer ziemlich atonalen "Melodie" aus verschiedenen Noten task_serial(); ==> gibt im Sekundentakt ein "H" auf dem seriellen Monitor aus

Statt dass wirklich eine Melodie gespielt wird, habe ich eine Art Simulation gemacht, mit Ausgabe auf dem seriellen Monitor wenn eine Note gespielt und wieder abgeschaltet wird. Das geht dann nicht so auf die Ohren. Damit man die Abspielsimulation per Auge verfogen kann, ist die "Notenlänge" dabei 1, 2, 3, 4, 5 Sekunden lang.

Die "Melodie" habe ich in so eine Datenstruktur gegeben: char melodie[]="C1D2E4F5A4D2C4"; Immer ein Zeichen benennt die zu spielende Note, ein weiteres Zeichen die Notenlänge in Sekunden.

Wenn Du tatsächlich Noten abspielen möchtest, brauchst Du nur die Ausgaben der Simulation zu ersetzen, also den tone-Befehl statt: Serial.print("\r\nNote: ");Serial.print(melodie[notencounter]); Serial.print(" Dauer: ");Serial.println(melodie[notencounter+1]); und den notone-Befehl statt: Serial.println("\r\nNote aus");

Wenn die Noten zehnmal schneller gespielt werden sollen, könntest Du die Notendauer als Angabe in Zehntelsekunden interpretieren, dazu muß nur der Faktor der Notenlängendauer von 1000 auf 100 ms herabgesetzt werden: nextpause=millis()+(melodie[notencounter+1]-byte('0'))*100; ==> *100 statt *1000 Dann wird's mit dem Verfolgen der Simulation per Auge aber schwierig, das wäre dann mehr etwas für eine tatsächliche akustische Wiedergabe, vielleicht mit einer etwas gefälligeren Melodie.

Die "Pause" zwischen zwei Noten habe ich fest auf 300 ms eingestellt, das kann man natürlich variieren oder diesen Wert auch in die Melodie-Datenstruktur mit einbinden: else nextnote=millis()+300;

Nach Ablauf wiederholt sich die Melodie mit einer Pausenzeit von 10 Sekunden: Serial.println("\r\nMelodie startet neu nach 10 Sekunden."); nextnote=millis()+10000;

Im ganzen Sketch ist kein einziges delay() drin.

Und auch nur deshalb funktioniert diese Art von Multitasking ("kooperatives Multitasking"), so daß quasi mehrere Aufgaben gleichzeitig ablaufen können, und jeder Task erledigt Aufgaben in einem eigenen Takt, der die Takte der übrigen Tasks nicht beeinträchtigt.

// Simulation Multitasking by jurs for German Arduino Forum

#define LEDPIN 13

// Nur Simulation, es werden keine Noten gespielt!
// Fiktive Melodie, Note-C 1s, Note-D 2s, Note-E 4s etc.
char melodie[]="C1D2E4F5A4D2C4";

boolean halbSekundenTakt()
// liefert true falls seit dem letzten Aufruf ein neues Halbsekunden-Intervall begonnen hat
{
  static long alterWert;
  if (millis() - alterWert < 500) return(false);
  while (millis() - alterWert >= 500) alterWert+=500;
  return(true);  
}

boolean SekundenTakt()
// liefert true falls seit dem letzten Aufruf ein neues Sekunden-Intervall begonnen hat
{
  static long alterWert;
  if (millis() - alterWert < 1000) return(false);
  while (millis() - alterWert >= 1000) alterWert+=1000;
  return(true);  
}

void task_serial()
{
  // im Sekundentakt ein "H" seriellen Monitor ausgeben
  if (SekundenTakt())
    Serial.print('H');
}


void task_led()
{
  // Im Halbsekundentakt die LED ein- und ausschalten
  if (halbSekundenTakt())
    digitalWrite(LEDPIN,!digitalRead(LEDPIN));
}

void task_tone()
// Simulation: Abspielen einer Melodie,
// die Simulation gibt Notenbeginn und Notenende auf dem seriellen Monitor aus
{
  static byte notencounter;
  static boolean note_an;
  static long nextnote, nextpause;
  if (millis()>nextnote)
  {
    if (!note_an) 
    {
      Serial.print("\r\nNote: ");Serial.print(melodie[notencounter]);
      Serial.print(" Dauer: ");Serial.println(melodie[notencounter+1]);
      nextpause=millis()+(melodie[notencounter+1]-byte('0'))*1000;
      note_an=true;
    }  
  }
  if (millis()>nextpause)
  {
    if (note_an) 
    {
      Serial.println("\r\nNote aus");
      notencounter+=2; // notencounter weiter
      if (notencounter>=strlen(melodie)) 
      {
        notencounter=0; // melodie zuende, notencounter reset
        Serial.println("\r\nMelodie startet neu nach 10 Sekunden.");
        nextnote=millis()+10000;
      } 
      else nextnote=millis()+300; // Feste Pause von 300 ms zwischen den Noten
      note_an=false; // spielen der Note is abgeschaltet
    }  
  }
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(LEDPIN,OUTPUT);
}


void loop() {
  // put your main code here, to run repeatedly: 
  task_led();
  task_tone();
  task_serial();
}

Danke für die umfassende sehr hilfreiche Antwort!!! Das probiere ich jetzt aus...

pulsarus: Danke für die umfassende sehr hilfreiche Antwort!!! Das probiere ich jetzt aus...

Das Timing für die Noten kannst Du natürlich auch anders machen, als jedesmal mit millis() zu rechnen.

Insbesondere wenn Du Melodien mit unterschiedlichem Tempo / BPM abspielen möchtest, bietet es sich vielleicht an, sich vorher die Dauern von Viertelnote, Halbenote, Sechzehntelnote nur einmal vor dem Start der Melodie anhand der BPM-Angabe auszuechnen (also nicht vor jeder Note), und die Notendauer in der Melodie als Vielfaches der kürzesten Note anzugeben.

Für diese kürzeste Notendauer (z.B. Sechzehntelnote) machst Du Dir dann einen Takt, so wie in den Funktionen für das Blinken der LED und der "H" Ausgabe vorgemacht in den Funktionen halbSekundenTakt(), SekundenTakt(), nur mit kleinerem Millisekundenwert. Vielleicht als Funktion "SechzehntelNotenTakt()".

Und in der Abspielfunktion setzt Du bei Start einer Note oder einer Pause einen Zähler auf den notwendigen Wert, also für eine Sechzehntelnote/-pause auf 1, bei einer Achtelnote/-pause auf 2, bei einer Viertelnote/-pause auf 4 etc. Und bei jedem auftretenden Sechzehntelnoten-Takt wird der Zähler um 1 heruntergezählt. Ist der Zähler bei 0 wird bei Abspielen einer Note die nächste Pause gestartet, beim Abspielen einer Pause die nächste Note. So daß Du beim Abspielen selbst gar nicht mehr mit millis() hantierst, sondern nur noch mit einem Zähler, der Vielfache der kürzesten Notenlänge zählt.

Es gibt ja immer beliebig viele Möglichkeiten, wenn man Programme schreibt.

Ja, das ist eine gute Idee.

Wie kann ich char melodie[]="C1D2E4F5A4D2C4"; in int melodie[] = {55,78,1109, 659,4978,4978} umwandeln, damit ich die Notenwerte in Zahlen angeben kann?

pulsarus: Wie kann ich char melodie[]="C1D2E4F5A4D2C4"; in int melodie[] = {55,78,1109, 659,4978,4978} umwandeln, damit ich die Notenwerte in Zahlen angeben kann?

Wie ist denn "int melodie[] = {55,78,1109, 659,4978,4978} " zu interpretieren?

Wo sind die Noten? Wo sind die Notenlängen in den Zahlen?

Mal kurz in Deinen Quellcode geschaut: Sind das nur Tonhöhen und Du möchtest eine Melodie nur mit immer derselben Notenlänge und Pausenlänge spielen? Das dürfte dann ja ein ziemlich monotones Gepiepe werden!

Warum möchtest Du Notenwerte in Zahlen angeben, Noten sind doch normalerweise Buchstaben (c, d, e, f, g, a, h, c), da würde sich doch eher eine Codierung der Melodie als String anbieten? Wenn ein Tonumfang von vielen Oktaven notwendig wird, codiert man die Oktave mit 2 Zeichen, dann ist "1c" das c aus der ersten Oktave, "2c" das c aus der zweiten Oktave, "3c" das c aus der dritten Oktave. Und dahinter dann die Länge, z.B. 0 - ganze Note 1 - ganze Note punktiert 2 - halbe Note 3 - halbe Note punktiert 4 - viertel Note 5 - viertel Note punktiert 6 - achtel Note 7 - achtel Note punktiert 9 - sechzehntel Note a - sechzehntel Note punktiert b - zweiunddreißigstel Note c - zweiunddreißigstel Note punktiert

Dann wäre eine Folge wie "1c23e5" 1c2 = spiele aus der ersten Oktave, Note C, mit Dauer einer halben Note 3e5 = spiele aus der dritten Oktave, Note E, mit der Dauer einer Viertelnote punktiert

Wenn Du jetzt die Melodie sagen wir mal auf einem Notenblatt vorliegen hast, müßtest Du sie ja irgendwie in eine computergerechte Form bringen, ohne dass das Umschreiben auf die Controller-Melodie Datenstruktur zu aufwändig wird.

Und die grundlegende Datenstruktur müßtest Du Dir zu Anfang überlegen und nicht erst zum Schluß. Wie das dann aus der Datenstruktur heraus gespielt wird, ist einfach, Du schaust einfach die Oktave und Note nach und schlägst in einem Array nach, welche Tonhöhe es ist. Dadurch ergibt sich, was die tone() Funktion als Parameter übergeben bekommt.

Ich wollte, wenn ich mit den Tonhöhen arbeiten könnte, eine andere "musikalische" Struktur aufbauen und so z.B. die Fibonacci Zahlenreihe oder andere mathematische Verhältnisse vertonen (verpiepsen) oder einfach auch nur Zufallszahlen.

Das Konvertieren von char zu int sieht ja etwas kompliziert aus (atoi?).

pulsarus: Ich wollte, wenn ich mit den Tonhöhen arbeiten könnte, eine andere "musikalische" Struktur aufbauen und so z.B. die Fibonacci Zahlenreihe oder andere mathematische Verhältnisse vertonen (verpiepsen) oder einfach auch nur Zufallszahlen.

Das stelle ich mir schwierig zu vertonen vor, da der mathematische Zahlenbereich von minus Unendlich bis plus Unendlich reicht, das menschliche Hörvermögen allenfalls von 20 bis 20000 Hertz. Kling recht experimentell. Ich habe ehrlich gesagt nur eine recht schwache Vorstellung davon, was Dir dabei vorschwebt.

pulsarus: Das Konvertieren von char zu int sieht ja etwas kompliziert aus (atoi?).

Überhaupt nicht. Anbei mal die Logik zum Dekodieren eines Strings, in dem Oktave und Ton zu einer int-Tonhöhe dekodiert werden.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  char melodie[]="1C2E3F4G5A6H4C3C4E3F3G5A6H3D4E3C";
  int tonhoehe;
  for (int i=0;i

[/code]

Nimm aber anstelle

static long alterWert

unbedingt

static unsigned long alterWert;

Merke: Variablen für millis() und micros() Werte IMMER als unsigned long deklarieren. Zusammen mit der richtigen Abfrage gibt es dann auch kein Problem mit dem Überlauf.

Erst mal vielen Dank für die Antworten. In der Tat ist dieses kleine Arduinoprojekt sehr experimentell.

Ich habe jetzt versucht den Code so umzuschreiben, dass man die Töne hören kann. Allerdings kommt nur ein ganz leises "Gefiepse" aus dem Piezo, ganz anders als in meinem obigen Code. (Die Funktionen task_led und task_serial habe ich vorerst ausgeschaltet.) Was habe ich falsch gemacht?

#define piezoPin A5
char melodie[]="C1D2E4F5A4D2C4";

boolean halbSekundenTakt()
// liefert true falls seit dem letzten Aufruf ein neues Halbsekunden-Intervall begonnen hat
{
  static long alterWert;
  if (millis() - alterWert < 500) return(false);
  while (millis() - alterWert >= 500) alterWert+=500;
  return(true);  
}

boolean SekundenTakt()
{
  static unsigned long alterWert;
  if (millis() - alterWert < 1000) return(false);
  while (millis() - alterWert >= 1000) alterWert+=1000;
  return(true);  
}
void task_tone()
{
  static byte notencounter;
  static boolean note_an;
  static long nextnote, nextpause;
  if (millis()>nextnote)
  {
    if (!note_an) 
    {
      tone(piezoPin,melodie[notencounter]);
      tone(piezoPin,melodie[notencounter+1]);
      nextpause=millis()+(melodie[notencounter+1]-byte('0'))*1000;
      note_an=true;
    }  
  }
  if (millis()>nextpause)
  {
    if (note_an) 
    {
      noTone(piezoPin);
      notencounter+=2; // notencounter weiter
      if (notencounter>=strlen(melodie)) 
      {
        notencounter=0; // melodie zuende, notencounter reset
        Serial.println("\r\nMelodie startet neu nach 10 Sekunden.");
        nextnote=millis()+10000;
      } 
      else nextnote=millis()+300; // Feste Pause von 300 ms zwischen den Noten
      note_an=false; // spielen der Note is abgeschaltet
    }  
  }
}


void setup() {
}


void loop() {
  task_tone();
}

Ich habe jetzt versucht den Code so umzuschreiben, dass man die Töne hören kann. Allerdings kommt nur ein ganz leises "Gefiepse" aus dem Piezo, ganz anders als in meinem obigen Code... Was habe ich falsch gemacht?

In dem was du im ersten Post angehängt hast, benutzt du tone(pin, freq, duration), hier die mit 2 Parametern... Kommt jetzt das noTone() zu früh ???

Sonst ist es eher Hardware ...

pulsarus: Ich habe jetzt versucht den Code so umzuschreiben, dass man die Töne hören kann. Allerdings kommt nur ein ganz leises "Gefiepse" aus dem Piezo, ganz anders als in meinem obigen Code.

Du darfst natürlich nicht einfach die ASCII-Codes der Buchstaben als Tonhöhe nehmen: tone(piezoPin,melodie[notencounter]); tone(piezoPin,melodie[notencounter+1]); sondern mußt Angaben wie "2C" (2. Oktave , Note C) schon erst noch in eine Tonhöhe umrechnen.

Ich hab's jetzt mal auf das Abspielen von einfachen Melodien umgeschrieben. Jeder Ton der Melodie wird jetzt mit vier Bytes in einem String definiert: - Oktave - Note - Notendauer - Komma Leerzeichen dürfen nicht vorkommen.

Falls statt einer Note eine Pause gespielt werden soll, für Oktave und Note einfach "P" setzen.

Wenn die Melodie schneller oder langsamer abgespielt werden soll, kann der Wert für BPM (Beats per Minute) verändert werden.

Die zweite, auskommentierte Melodie ist übrigens eine Tonleiter aus Viertelnoten.

#define piezoPin A5
char melodie[]="2d5,2d6,2c4,2d6,2d6,2g6,2a4,2a6,2g6,2a6,2a6,2d6,2f2,PP4,2f4,2e6,2d6,2e6,2f6,2a6,2c5,2h6,2a6,2h6,2c6,2g5";
// char melodie[]="1c4,1d4,1e4,1f4,1g4,1a4,1h4,2c4,2d4,2e4,2f4,2g4,2a4,2h4,3c4,3d4,3e4,3f4,3g4,3a4,3h4,4c4,4d4,4e4,4f4,4g4,4a4,4h4,5c4,5d4,5e4,5f4,5g4,5a4,5h4,6c4,6d4,6e4,6f4,6g4,6a4,6h4";

#define BPM 120
#define SECONDS_PER_MINUTE 60
int getNotenDauer(int beatsPerMinute, char notendauer)
{
  long factor;
  switch (notendauer)
  {
    case '0': factor=1024;break; // ganze Note
    case '1': factor=1536;break; // ganze Note punktiert
    case '2': factor=512;break; // halbe Note
    case '3': factor=768;break; // halbe Note punktiert
    case '4': factor=256;break; // viertel Note 
    case '5': factor=384;break; // viertel Note punktiet
    case '6': factor=128;break; // achtel Note
    case '7': factor=192;break; // achtel Note punktiert
    case '8': factor=64;break;  // sechzehntel Note
    case '9': factor=96;break;  // sechzehntel Note punktiert
    case 'a': factor=32;break;  // 32stel Note
    case 'b': factor=48;break;  // 32stel Note punktiert
    default: {Serial.print("Error Tondauer ");Serial.println(notendauer);}
  }  
  return (4*factor*SECONDS_PER_MINUTE/beatsPerMinute);
}


int getTonHoehe(char oktave, char note)
{
  int tonhoehe;
  switch (note)
  {
    case 'c': tonhoehe=330;break;
    case 'd': tonhoehe=349;break;
    case 'e': tonhoehe=370;break;
    case 'f': tonhoehe=392;break;
    case 'g': tonhoehe=415;break;
    case 'a': tonhoehe=440;break;
    case 'h': tonhoehe=466;break;
    default: {Serial.print("Error Ton ");Serial.println(note);}
  }
  switch (oktave)
  {
    case '1': break;
    case '2': tonhoehe*=2;break;
    case '3': tonhoehe*=4;break;
    case '4': tonhoehe*=8;break;
    case '5': tonhoehe*=16;break;
    case '6': tonhoehe*=32;break;
    default: {Serial.print("Error Oktave ");Serial.println(oktave);}
  }
 return(tonhoehe); 
}  


boolean halbSekundenTakt()
// liefert true falls seit dem letzten Aufruf ein neues Halbsekunden-Intervall begonnen hat
{
  static long alterWert;
  if (millis() - alterWert < 500) return(false);
  while (millis() - alterWert >= 500) alterWert+=500;
  return(true);  
}

boolean SekundenTakt()
{
  static unsigned long alterWert;
  if (millis() - alterWert < 1000) return(false);
  while (millis() - alterWert >= 1000) alterWert+=1000;
  return(true);  
}
void task_tone()
{
  static int notencounter;
  static boolean note_an;
  static long nextnote, nextpause;
  if (millis()>nextnote)
  {
    if (!note_an) 
    {
      if (melodie[notencounter]!='P')
      {
        tone (piezoPin,getTonHoehe(melodie[notencounter], melodie[notencounter+1]));
//        Serial.println(getTonHoehe(melodie[notencounter], melodie[notencounter+1]));
      }
      else
      {
        noTone(piezoPin); // Pause statt Note  
//        Serial.println("Pause");
      }
      nextpause=millis()+getNotenDauer(BPM,melodie[notencounter+2]);
//      Serial.println(getNotenDauer(BPM,melodie[notencounter+2]));
      Serial.println();
      note_an=true;
    }  
  }
  if (millis()>nextpause)
  {
    if (note_an) 
    {
      noTone(piezoPin);
      notencounter+=4; // notencounter weiter
      if (notencounter>=strlen(melodie)) 
      {
        notencounter=0; // melodie zuende, notencounter reset
        Serial.println("\r\nMelodie startet neu nach 10 Sekunden.");
        nextnote=millis()+10000;
      } 
      else nextnote=millis()+100; // Feste Pause von 100 ms zwischen den Noten
      note_an=false; // spielen der Note is abgeschaltet
    }  
  }
}


void setup() {
  Serial.begin(9600);
}


void loop() {
  task_tone();
}

Das ist natürlich ein selbsterfundenes Datenformat für die Melodie, und Halbtöne sind auch nicht dabei. Wenn Du fertige Melodien per Download ziehen oder Software erzeugen möchtest, wäre natürlich ein Standardformat wie MIDI oder RTTTL (Nokia Klingeltonformat aus alten Handy-Zeiten) besser geeignet. Aber rein für das Spielen von Zufallstönen und computergenerierten Tönen ist es ja egal, ob das Datenformat auch von irgendeiner anderen Software unterstützt wird oder nicht.

Ich hatte jetzt einfach von der pitches.h die Tonhöhen meiner "Melodie" rausgesucht und die Noten abgeschrieben. Vielleicht kann ich auch gleich diese Library in den Code einbinden? und habe so alle Noten und Tonhöhen zur Verfügung? MIDI oderRTTTL brauche ich nicht, obwohl ich das RTTL Format für ein anderes Projekt sehr interessant finde! Danke für den Tip. Ich werde jetzt Deinen Vorschlag ausprobieren und gleich noch meinen LCD -Code mit einfügen.

pulsarus: Ich hatte jetzt einfach von der pitches.h die Tonhöhen meiner "Melodie" rausgesucht und die Noten abgeschrieben. Vielleicht kann ich auch gleich diese Library in den Code einbinden? und habe so alle Noten und Tonhöhen zur Verfügung?

Den Code kannst Du umbauen und erweitern wie Du lustig bist.

Wenn Du zusätzlich zu den 7 Tönen in der Oktave noch die 5 Halbtöne haben möchtest: Baue sie ein!

Mein Vorschlag für die Notation der Melodie wäre dann, den Kleinbuchstaben für die Note und den Großbuchstaben für die um einen Halbton erhöhte Note zu verwenden c = Note c C = Note cis d = Note d D = Note dis e = Note e f = Note f F = Note fis g = Note g G = Note gis a = Note a A = Note ais h = Note h

Aber das kannst Du natürlich machen wie Du möchtest.

Auch ob Du jetzt die Tonhöhen für acht Oktaven komplett mit Tonwerten füllst und als Konstanten ins Programm packst oder nicht, ist alleine Deine Entscheidung.

Da die Tonhöhe sich in jeder Oktave verdoppelt, halte ich es in meinem Beispielcode so, dass ich die Tonhöhe nur für eine einzige Oktave definiere und dann nach Bedarf für andere Oktaven ausrechne: Eine Oktave höher ==> Tonhöhe der Grundoktave mal 2 Zwei Oktaven höher ==> Tonhöhe der Grundoktave mal 4 Drei Oktaven höher ==> Tonhöhe der Grundoktave mal 8 Vier Oktaven höher ==> Tonhöhe der Grundoktave mal 16 Fünf Oktaven höher ==> Tonhöhe der Grundoktave mal 32 Dadurch, dass ich mit den gerundeten und relativ niedrigen Zahlen der unteren Oktave beginne, kommen in den oberen Oktaven kleinere Fehler zustande, vielleicht wäre es geschickter, stattdessen die Tonhöhe der obersten Oktave exakt festzulegen und dann runterzurechnen.

Wie gesagt, ist ja nur Beispielcode, was Du im Endeffekt brauchst (keine Ahnung was das genau ist), machst Du eben selbst. Nur darf eben kein delay() im Code beim Abspielen der Melodie vorkommen, wenn Du nebenbei noch andere Aufgaben mit dem Controller erledigen möchtest (in denen aber ebenfalls kein delay() verwendet werden darf).

Und wenn Dir das Prinzip aus Oktaven und Noten für Dein Programm gar nicht gefällt, und Du verschiedene Tondauern gar nicht brauchst, dann spielst Du eben nur Töne verschiedener Tonhöhen als Tonfolgen ab, nach mathematischer Berechnung oder Zufall, wie Du möchtest.

Eine Frage hätte ich noch zum Code. Was bedeutet

}
return (4*factor*SECONDS_PER_MINUTE/beatsPerMinute);
}

Ansonsten ist mir soweit alles klar und der Code funktioniert super!!! Habe eine Menge gelernt und versuche es jetzt anzuwenden. Vielen Dank dafür!

pulsarus: Eine Frage hätte ich noch zum Code. Was bedeutet

}
return (4*factor*SECONDS_PER_MINUTE/beatsPerMinute);
}

In der Zeile rechne ich den Rückgabewert der Funktion "Notenlänge in Millisekunden" aus, also wie viele Millisekunden die Note erklingen muß, bevor sie wieder abgeschaltet wird.

Und zwar erfolgt die Berechnung einerseits anhand der Notenlänge, wie sie auf dem Notenblatt steht bzw. im Melodie-String codiert ist (ganze, halbe, Viertelnote, Achtelnote etc.), und andererseits anhand der Vorgabe, wie schnell das Stück gespielt werden soll.

Das folgende nach bestem Wissen und Gewissen, ich bin kein Musiker und mein Musikunterricht in der Schule ist Jahrzehnte her: Früher hatte man für die Abspielgeschwindigkeit immer nur allgemeine Bezeichnungen wie Grave, Lento, Adagio, Allegro, Presto. Mit der Einführung des Metronoms kam dann die Digitalisierung: Man hat den Tick-Tock Takt eines Metronoms auf Viertelnoten eingestellt und so beim Einüben von Stücken die Spielgeschwindigkeit vorgegeben. Dieser Metronom-Takt für Viertelnoten nennt sich heute neudeutsch "Beats per Minute", abgekürzt BPM. 120 Viertelnoten in einer Minute wäre eine Spielgeschwindigkeit von 120 BPM. Die Dauer einer Viertelnote wäre also 60 Sekunden geteilt durch 120 Noten = 60s/120 = 0,5s

Die Dauer einer ganzen Note erhält man durch den Faktor "4" in der Formel, denn eine ganze Note ist viermal so lang wie eine Viertelnote. Bei 120 BPM also 2s.

Und durch "factor" rechne ich in Millisekunden um, allerdings mit einer kleinen Toleranz: Ich habe so getan, als wenn eine Sekunde nicht 1000 ms enthält, sondern 1024 ms. Dadurch komme ich beim Herunterrechnen auf 16tel und 32tel Noten auf ganze Zahlen (1000 ist nicht ohne Rest durch 16 und 32 teilbar, 1024 dagegen sehr wohl) Bezug nehmen und vermeide, dass der Faktor schon ein gerundeter Wert ist und dann in der Formel nochmal aus einem gerundeten Wert wieder ein gerundeter Wert errechnet wird. Aber dafür stimmen die Verhältnisse zwischen den Noten, insbesondere sind Achtelnoten exakt doppelt so lang wie Sechzehntelnoten, und Sechzehntelnoten exakt doppelt so lang wie Zweiunddreißigstelnoten, mit nur der Rundungsgenauigkeit von +/-1ms.

Im Endeffekt sind "echte" 120 BPM allerdings ein klein wenig schneller als die von mir für 120 BPM (und beliebige andere Werte) ausgerechneten Notendauern.

Das ist eben nur mal schnell hingeschrieben, aber immerhin unterstützt der Code bereits verschiedene Abspielgeschwindigkeiten, durch die Konstante BPM im Code. D.h. wenn nur schneller gespielt werden soll, kann die festgelegte Melodie gleichbleiben, man ändert nur den Wert für BPM.

Und BPM braucht keine Konstante zu sein. Dadurch, dass der Wert bereits an die Berechnungsfunktion übergeben und von dieser wie eine Variable behandelt wird: nextpause=millis()+getNotenDauer(BPM,melodie[notencounter+2]); ist das Programm quasi schon darauf vorbereitet, dass BPM statt einer Konstanten auch eine Variable sein kann, die zur Laufzeit des Programms geändert wird. Für Deine Experimentalmusik könntest Du also auch einen Task machen, in dem immer der BPM-Wert alle paar Sekunden geändert wird, und dann ändert sich alle paar Sekunden das Abspieltempo.

Allerdings gibt es da auch Grenzen, also länger als 50 Tage darf ein Ton bei meinem Beispiel-Sketch nicht dauern, da läuft zwischendurch der millis()-Timer über. Also das Orgelstück Organ2/ASLSP von John Cage könntest Du noch in dem Tempo spielen, wie das Stück uraufgeführt wurde. Aber mit der Darbietung in der Sankt Burchardi-Kirche in Halberstadt als langsamstes und längstandauerndes Musikstück der Welt in einer Gesamtlänge von 639 Jahren, da steigt mein Programm-Sketch vorher aus, so experimentell langsam kann mein Sketch keine Noten spielen, mit Notenwechseln nach Monaten und Jahren bemessen: http://de.wikipedia.org/wiki/ORGAN%C2%B2/ASLSP http://www.aslsp.org/de/

pulsarus: Ansonsten ist mir soweit alles klar und der Code funktioniert super!!! Habe eine Menge gelernt und versuche es jetzt anzuwenden. Vielen Dank dafür!

Ich schaue mir ja selbst auch immer gerne Programmierkniffe aus fremden Codes ab. Und so Dinge wie geeignete Datenstrukturen, um z.B. Noten aus einem Notenblatt in einen Melodie-String umzusetzen, der dann wiederum in Töne zum Abspielen umgesetzt wird, kann man im Endeffekt nicht nur beim Spielen von Noten gebrauchen, sonder auch in anderen Fällen, wenn die reale Welt auf computergerechte Datenstrukturen umgesetzt werden soll.

Das ist eine sehr ausführliche und gut anschauliche Erklärung. Vielen Dank.

Über 50 Tage möchte ich keinen Ton spielen. Trotzdem noch eine kurze Frage dazu: Ich dachte den Überlauf kann man vermeiden, wenn mann statt long - unsigned long nimmt?