einfaches metronom
-
Vollkommen präzise kannst du das prinzipiell nicht machen, Windows ist kein Echtzeitsystem.
Du kannst es in der Praxis nur bis auf ein paar Millisekunden genau hinbekommen, was aus meiner Sicht für ein Metronom eigentlich ausreichend sein sollte.
ja, Millisekunden sind gut genug. Ich hab einen Code umgesetzt der mit QueryCounter arbeitet, das geht dann in Microsekundenauflösung genau. Jedoch ist das nur der Takthread.
-
Was bedeutet "den User im Auge behalten"? Eine Kamera? Ein Knopf? Ein Rad vielleicht?
Bei einem Mikrokontroller bieten sich Interrupts an, die automatisch feuern sobald eine bestimmte Zeit abgelaufen ist oder eine Taste gedrückt wird. Schneller wirds nicht.
Multithreading ist völliger Overkill für deine Anwendung.Wie sieht dein Metronom aus? Ist das ein Stück Ding mit Zeiger und Skala oder ein Lautsprecher der ticken kann?
-
Genaugenommen braucht man daher 2 Threads
Nein, braucht es nicht. das Betriebssystem bietet Timer an.
Was tun?
Erstmal programmieren lernen.
-
"den User im Auge behalten" bedeutet, dass ich während das Metronom rumtickt, eine Eingabe machen kann, z.B. den Takt ändern kann per simpler Eingabe eines Integers.
Ich will das Metronom nicht erst stoppen damit es sich Zeit nimmt meine Eingabe zu lesen. Interrupt bedeutet doch, dass es in dem Moment nicht mehr weitertickt.EDIT: nochmal etwas genauer:
1. Die Eingabe soll möglich sein, während das Metronom weitertickt.
2. Wenn die Eingabe erfolgt, soll der nächste Tick nicht einfach direkt anfangen, sondern entsprechend lange warten, z.B.:bei 60 bpm liegt zwischen jedem Tick (etwa) 1 Sekunde (weil der Tick auch eine Dauer hat). Wenn es einmal getickt hat, und z.B. in der Mitte zum nächsten Tick eine Taktänderung per Eingabe auf 30 bpm stattfindet, dann soll noch eine Zeit lang bis zum nächsten Tick gewartet werden, nämlich so viel wie bei 30 bpm nötig ist minus die Zeit die schon seit dem letzten Tick vergangen ist, also insgesamt 1,5 Sekunden.
Zweck ist es, die Taktveränderungen möglichst weich anhören zu lassen.
-
amrosik schrieb:
Ein Metronom, das präzise taktet, und gleichzeitig den User im Auge behält.
Genaugenommen braucht man daher 2 Threads:
Der erste kümmert sich um den User, schaut nach, ob er Taktänderungen wünscht,
die Taktänderungen werden auf einem Speicherbereich abgelegt.
Der zweite Thread guckt in diesem Speicherbereich nach neuen Einträgen.Was den Speicher angeht, hab ich mir überlegt, um zu vermeiden, dass der Taktthread zufällig gleichzeitig auf den selben Speicher zugreift, worauf grade der User drauf schreibt , dass man einfach 2 Speicherbereiche einführt.
Die beiden Speicherstellen werden mit Flags markiert:
...Du kannst nicht einfach von mehreren Threads aus auf ein gemeinsames Objekt oder Speicherlokation zugreifen, wobei mindestens ein Thread schreibend zugreift. Das ergibt undefiniertes Verhalten. Du musst das entweder mit "atomics" machen oder mit "mutexes". Mit den Mutexes und Locks bekommst du auch timed_wait-Funktionen, die so lange warten -- ohne das extra CPU-Zyklen verbraten werden -- bis entweder ein bestimmtes Ereignis eingetreten ist oder die Zeit abgelaufen ist. Das ist vielleicht nützlich in Deinem Fall.
Für die Kommunikation zwischen zwei Threads, kannst du auch "Kanäle" verwenden. Das muss man sich allerdings selbst aus einer Liste, nem Mutex und ein paar Condition-Variablen bauen. So ein Kanal ist dann asynchron.
amrosik schrieb:
Es sei schonmal gesagt: Ich habe keine Ahnung von Threading.
Merkt man.
Mussu dich wirklich mal mehr einlesen, bevor du alles falsch machst.amrosik schrieb:
Meiner Meinung nach müssten dazu 2 tatsächlich parallele Prozesse laufen.
Der Umstieg von threads auf getrennte Prozesse bringt gar nichts bzgl Parallelität. Wenn dein Rechner mehrere Threads tatsächlich parallel ausführen kann, dann tut er das auch. Ist ja heute üblich so.
amrosik schrieb:
Kann man 2 Programme auf Windows miteinander kommunizieren lassen?
Geht bestimmt irgendwie. Ist aber in deinem Fall überflüssig. Vergiss das mit den verschiedenen Prozessen.
amrosik schrieb:
Das Endziel wäre, das ganze auf einer eigenen Hardware umzusetzen, also kein Windows. Ist das dann so, dass man solche Multithreading Bibliotheken die eigentlich für Windows gemacht sind, dort nicht mehr einsetzen kann. Dann braucht man extra MultithreadingBibliotheken die für dem Microcontroller mitgeliefert wurden?
Bei den größeren MCUs bekommst du ein kleines Betriebssystem zum laufen, was dir dann task switching bietet. MCUs bieten typischerweise keine echte Nebenläufigkeit. Bei den kleineren MCUs programmierst du direkt die Timer, die dann einen Interrupt auslösen. So ein Interrupt veranlasst dann die CPU, die Arbeit zu unterbrechen und eine bestimmte Subroutine, die du definierst, anzuspringen. Und das ist eher die Regel. Zumindest kenn ich kaum Leute, die auf kleinen MCUs versuchen, irgend ein Betriebssystem zum Laufen zu bringen. Da programmiert man eher auf dem "rohen Metall".
amrosik schrieb:
Und wenn man schon grade dabei ist eigene Hardware einzusetzen, kann man dann auch tatsächlich 2 parallele Prozesse umsetzen? Dann bräuchte man 2 Microcontroller die auf den selben Speicher zugreifen.
Vergiss es. Völlig falscher Ansatz. Eine MCU reicht. Die Dinger haben fast alle programmierbare Timer für sowas.
-
Du willst doch, laut Threadtitel, ein "einfaches metronom" programmieren.
Also tu das auch, ohne groß über Multithreading etc. zu grübeln.
Mit Beep, Sleep und GetAsyncKeyState hast du schon mal eine gute Grundlage, mit der du auf den ersten Blick weit kommenkönntest.
-
#include "stdafx.h" #include <iostream> #include <ctime> #include "windows.h" #include <stdio.h> #include <stdlib.h> using namespace std; class metronom{ private: LARGE_INTEGER frequency; LARGE_INTEGER t1, t2; double elapsedTime; double correction; public: metronom(); void tick(double); void estimatecorrection(void); }; int _tmain(int argc, _TCHAR* argv[]) { metronom m; m.estimatecorrection(); m.tick(120); Sleep(5000); return 0; } void metronom::tick(double bpm){ while(true){ QueryPerformanceCounter(&t1); QueryPerformanceFrequency(&frequency); while(true){ QueryPerformanceCounter(&t2); elapsedTime = (t2.QuadPart - t1.QuadPart)*1000 / frequency.QuadPart; if(elapsedTime/1000>=(60/bpm-correction/1000)){cout<<"tick"<<"\n";break;} } } }; metronom::metronom(){correction=0;}; void metronom::estimatecorrection(){ bool compare; double meassure[10]; double mean=0; for(int i=0; i<10;i++){ QueryPerformanceCounter(&t1); compare=(elapsedTime/1000>=(60/70-0,9)); //vergleichbare operationen cout<<"tick"<<"\n"; //vergleichbarer dreck QueryPerformanceCounter(&t2); QueryPerformanceFrequency(&frequency); meassure[i] = (t2.QuadPart - t1.QuadPart)*1000 / frequency.QuadPart; mean+=meassure[i]; } correction=mean/10; };naja,das hier wäre schonmal ein Ansatz, jetzt fehlt nur noch der tatsächliche Sound.
Aber zu frieden bin ich damit wiegesagt nicht.
Aber Entschuldigung, jetzt weiß ich erst was ihr mit Timern und Interrupts meint. Ich dachte da erst an was anderes. Aber wie ist das dann genau mit der Tonabspielung und der Takteingabe? Das Programm kann doch nicht einfach während der User etwas eintippt, aufeinmal ne andere Routine ansteuern? da fühlt man sich doch verarscht?
Bzw was passiert wenn grade mitten im Abspielen eines Ton der Timer das Interrupt auslöst?EDIT:
Wichtig sind auch die Prioritäten, also wie lange das Programm in welcher Subroutine verweilt. Da muss man die Timer justieren.
Kann man das so machen, dass beim Eintippen ein Hadrwareinterrupt ausgelöst wird, der die Timer so einstellt, dass kurzfristig die Verweilsdauer in der Taktabfragenroutine eine größere Priorität bekommt, und in der restlichen Zeit die Tickroutine die große Priorität hat?
-
Wie gefällt dir meine folgende Umsetzung?
#include <windows.h> int main() { int freq = 262, dur = 100, pause = 1000; while(!GetAsyncKeyState(VK_RETURN)) { if(GetAsyncKeyState(VK_INSERT)) pause += 100; else if(GetAsyncKeyState(VK_DELETE) && pause>100) pause -= 100; Beep(freq, dur); Sleep(pause); } return 0; }
-
auch nicht schlecht, aber eine diskrete Eingabe eines Starttaktes ist mir trotzdem wichtig. Danach könnte man das scrittweise increase und decrease verwenden um resourcen zu sparen.
-
Nun gut, den User die Dauer von
pausebestimmen zu lassen ist hoffentlich kein Problem
-
der Starttakt kann von Musikpassage zu Musikpassage varrieren, zusätzlich kommt noch der Skill des Users am Instrument. Je besser, oder je fauler, umso weniger wird bei kleineren Takten geübt.
EDIT: außerdem hab ich grade deinen Code mit einem Online metronom verglichen: Beim Defaulttempo, also pause=1000 --> 60 bpm hat der code einen starken versatz.
Da hält mein code länger durch, allerdings weil da eine korrekturfunktion drin ist.
-
Kann man diese Interrupttimer am PC umsetzen?
wie schafft man es z.B., eine abgebrochene Routine nacher an der selben Stelle fortzuführen? und wie löst man die oben genannten probleme?
-
Habe ja nicht behauptet, dass mein Code besser wäre als deiner

Wollte nur mal deine Meinung dazu wissen, ob das irgendwie deinen Vorstellungen entspricht.
Und das mit dem Versatz habe ich beim kurzen Test nicht bemerkt.Einen Interrupttimer umsetzen "kann" man sicher, ist aber imo ziemlich kompliziert.
Und wie gesagt, unter "einfaches metronom" wäre dies dann sicherlich nicht mehr einzuordnen.
-
ich weiß, "einfach" war da eher trotzig gemeint, etwa wie: "ich will doch EINFACH nur ein metronom, dass wenigstens userfreundlich ist!"
-
ist es möglich assembler und c++ zu hybridisieren? und dann den interrupthandler per assembler zu machen? oder kann interrupthandler nur in einer puren sprache umsetzen? könnt ihr mir dazu referenzen geben?
ich hab jetzt schonmal eine seite gefunden die ich am durchlesen bin:
http://www.drdobbs.com/implementing-interrupt-service-routines/184401485?pgno=1
-
Ohje, da geht so viel durcheinander. Erstmal: Prozessoren sind schnell. Sehr schnell. Du kannst dein Metronom abspielen, auf Eingaben reagieren und nebenbei Pi auf ein paar Millionen Stellen berechnen, gar kein Problem.
Die Soundausgabe wird nicht vom Prozessor gemacht, sondern der Prozessor tut wave-Daten in ein Stück Speicher und sagt der Soundkarte "mach mal". Daher spielt der Sound weiter, obwohl der Prozessor was anderes tut. Auf einem Mikrokontroller wäre es ein DMA-Controller oder PDCA oder wie die sonst heißen. Das sind extra Chips die das Datenfüttern übernehmen, damit die CPU sinnvolle Dinge tun kann.Ein Interrupthandler ist eine einfache Funktion. Der Prozessor springt beim setzen eines Pins an eine Stelle im Speicher, das nennt sich dann Interrupt. Der Code an der Stelle im Speicher ist der Interrupthandler. Den kannst du in Assembler, C, C++, JavaScript oder sonstwas schreiben (was sind pure Sprachen?). Zum "Hybridisieren" sei inline assembly erwähnt. Unter Windows hast du aber soweit ich weiß keinen Zugriff auf diesen lowlevel Kram. Dafür gibt es Timer, die dasselbe in komfortabel tun. Bei einem Mikroprozessor steht in der Doku wie man dem Prozessor sagt bei welchem Pin er wo hin springen soll. Dann verbindet man noch den Interruptpin mit der Uhr oder dem Taktzähler und schon läuft der Interrupthandler alle x Millisekunden.
Ich würde dir raten zuerst dein Problem zu definieren. Was soll wann passieren, wie sieht die Ein- und Ausgabe aus? Welcher Prozessor soll es sein?
Btw du kannst auch einfach die Zeit speichern:start = getTime(); while (true){ playTick(); //wird trotz Störungen alle delay Zeiteinheiten ausgeführt sleep(getTime() - start + delay); start += delay; }Was du hier sehen sollst, ist, dass playTick() keine Zeit verliert, sondern alle "delay" Zeiteinheiten tickt. Sollte der Prozessor beschäftigt sein tickt er später, das lässt sich nicht vermeiden, aber der nächste Tick stimmt wieder.
Aber insgesamt würde ich sagen das ist noch zu hoch für dich. Mikroprozessorprogrammierung ist nicht einfach, und wenn man die Sprache nicht kann wird das nichts.
knivil schrieb:
Was tun?
Erstmal programmieren lernen.
Willst du nicht hören, stimmt aber leider.
Und nachdem du C kannst kaufst du dir einen Atmel AVR irgendwas und lässt eine LED blinken. Und danach lässt du die LED per Interrupt blinken, was man per Taster an- und abstellen kann. Und dann kannst du mit Sound anfangen. Das dauert so 3-6 Monate. Wenn dir das zu lange dauert gibts eine gute Alternative.
-
Kann man diese Interrupttimer am PC umsetzen?
Alles schon beantwortet.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx
-
also den code von nwp3 hatte ich jetzt nicht die gelegenheit zu probieren, aber so wie ich mir das vorstelle summieren sich die störungen auf. das sleep wird immer länger und länger, weil start nur mit + delay aktualisiert, während gettime schneller wächst. egal,
aber ihr habt recht, ich verzettel mich hier nur, wenn ich so wirre weitermache, bzw. habe mich schon längst verzettelt.