// Happy Birthday melody for 8 relays (C to C high) // Note mapping: out1=C, out2=D, out3=E, out4=F, out5=G, out6=A, out7=B, out8=C high tempo = 2000; // quarter note duration in ms noteIdx = -1; lastTime = 0; lastMetro = 0; metroPeriod = 500; dur = 0; playing = 1; songLen = 31; note = 0; dur_out = 0; note_out = 0; metro = 0; func all_off() { out(1) = 0; out(2) = 0; out(3) = 0; out(4) = 0; out(5) = 0; out(6) = 0; out(7) = 0; return 0; } func set_note(note, &dur_out, ¬e_out) { // default rest note_out = 0; dur_out = 500; switch (note) { case 0: { note_out = 5; dur_out = 400; break; } case 1: { note_out = 0; dur_out = 100; break; } case 2: { note_out = 5; dur_out = 500; break; } case 3: { note_out = 6; dur_out = 1000; break; } case 4: { note_out = 5; dur_out = 1000; break; } case 5: { note_out = 1; dur_out = 1000; break; } case 6: { note_out = 7; dur_out = 1900; break; } case 7: { note_out = 0; dur_out = 100; break; } case 8: { note_out = 5; dur_out = 400; break; } case 9: { note_out = 0; dur_out = 100; break; } case 10: { note_out = 5; dur_out = 500; break; } case 11: { note_out = 6; dur_out = 1000; break; } case 12: { note_out = 5; dur_out = 1000; break; } case 13: { note_out = 2; dur_out = 1000; break; } case 14: { note_out = 1; dur_out = 1900; break; } case 15: { note_out = 0; dur_out = 100; break; } case 16: { note_out = 5; dur_out = 400; break; } case 17: { note_out = 0; dur_out = 100; break; } case 18: { note_out = 5; dur_out = 500; break; } case 19: { note_out = 3; dur_out = 1000; break; } case 20: { note_out = 1; dur_out = 1000; break; } case 21: { note_out = 7; dur_out = 1000; break; } case 22: { note_out = 6; dur_out = 1900; break; } case 23: { note_out = 0; dur_out = 100; break; } case 24: { note_out = 4; dur_out = 400; break; } case 25: { note_out = 0; dur_out = 100; break; } case 26: { note_out = 4; dur_out = 500; break; } case 27: { note_out = 3; dur_out = 1000; break; } case 28: { note_out = 1; dur_out = 1000; break; } case 29: { note_out = 2; dur_out = 1000; break; } case 30: { note_out = 1; dur_out = 2000; break; } default: { break; } } return 0; } // Init all_off(); out(8) = 0; // Main playback loop every(50) { tnow = now(); if (lastMetro == 0) { lastMetro = tnow; } if (tnow - lastMetro >= metroPeriod) { if (tnow - lastMetro >= metroPeriod) { metro = !metro; out(8) = metro; lastMetro = lastMetro + metroPeriod; } } if (p_edge(in(1))) { playing = !playing; if (!playing) { all_off(); logn("Stopped by button"); } else { logn("Started by button"); noteIdx = 0; lastTime = tnow; lastMetro = tnow; metro = 0; out(8) = 0; } } if (p_edge(in(2))) { tempo = tempo - 200; if (tempo < 1000) { tempo = 2000; } logn("Tempo: ", tempo); } if (playing) { if (lastTime == 0) { lastTime = tnow; } if (tnow - lastTime >= dur) { prevDur = dur; noteIdx = noteIdx + 1; if (noteIdx >= songLen) { noteIdx = 0; } note = 0; set_note(noteIdx, &dur, ¬e); all_off(); if (note > 0 && note <= 8) { out(note) = 1; } if (prevDur == 0) { lastTime = tnow; } else { lastTime = lastTime + prevDur; } } } } // Control via MQTT if needed onmqtt("play_script") { if (msg_is_number) { playing = msg_value; if (!playing) { all_off(); logn("Stopped"); } else { logn("Playing"); noteIdx = 0; lastTime = now(); lastMetro = lastTime; metro = 0; out(8) = 0; } } } // Publish status occasionally every(10000, "status") { publish("music", "playing", playing, "note", noteIdx, "tempo", tempo); }