relativ einfaches metronom
-
Heilige Scheiße. Ich muss llllllllllll leider rechtgeben - und rate dir, dir schnellstens ein Grundlagenbuch anzuschaffen. So wie das jetzt aussieht...
Ich bin zwar ein Multithreading-Noob, aber so stelle ich mir das vor:
#include <thread> #include <memory> #include <mutex> #include <iostream> #include "windows.h" struct Metronom { using duration_type = std::chrono::milliseconds; private: std::intmax_t mBPM; mutable std::mutex mBPMMutex; std::unique_ptr<std::thread> mThread; void _tickLoop() { for(;;) { if( !mBPMMutex.try_lock() ) // kk in Erinnerung: "... resourcenfressende Endlosschleife" continue; // Hier könnte man noch ein this_thread::sleep_for einfügen if( !mBPM ) break; duration_type dur{ duration_type::period::den / mBPM * 60 }; mBPMMutex.unlock(); Beep(800, 100); std::this_thread::sleep_for( dur ); } } public: Metronom(): mBPM{} {} ~Metronom() { if( mThread ) { bpm(0); mThread->join(); } } void bpm( std::intmax_t f ) { mBPMMutex.lock(); mBPM = f; mBPMMutex.unlock(); if( !mThread ) mThread.reset( new std::thread{ [this]{ _tickLoop(); } } ); } std::intmax_t bpm() const { std::lock_guard<std::mutex> _{mBPMMutex}; return mBPM; } }; #include <iostream> int main() { Metronom m; std::intmax_t period; while( std::cin >> period && period ) m.bpm( period ); }
-
Fehlt da nicht der Ausgleich, wenn sleep nicht exakt korrekt lange schläft? Das Metronom soll ja nicht irgendwann Abweichungen hervorbringen bzw. die wenn wieder ausgleichen.
Ich würde mir einfach die Anfangszeit merken und immer von dieser Basis aus mit Vielfachen die nächsten Zeitpunkte bestimmen. Dann arbeitet man entweder mit Sleep (angepasst auf Differenz von aktuellem Zeitpunkt zu nächstem auf Anfangszeit basierenden Zeitpunkt) oder hält ne Endlosschleife, die bei größtmöglicher Nähe zum Zeitpunkt den Beep auslöst (ein Sleep(10 /* ms*/); um nicht unnötig Prozessauslastung zu erzeugen reicht da auch völlig).
-
Fehlt da nicht der Ausgleich, wenn sleep nicht exakt korrekt lange schläft? Das Metronom soll ja nicht irgendwann Abweichungen hervorbringen.
Hui, das habe ich nicht berücksichtigt.
-
Ich würde mir einfach die Anfangszeit merken und immer von dieser Basis aus mit Vielfachen die nächsten Zeitpunkte bestimmen.
Genau so hab ich das bei mir umgesetzt falls ihr es nicht gemerkt habt, entschuldigt nochmal meine Strukturierung, aber ich wüsste nicht wie das besser gehen soll.
EDIT:
Es ist auch irgendwie leicht unfair, denn im Editor ist der Text anders strukturiert als nachher in der Vorschau, das macht es nicht einfach.EDIT: Ich füg mal Kommentare ein, dann gleicht das die Unstrukturiertheit etwas aus. und warum " Heilige Scheisse"? Ist es wirklich so schlimm?
Was tun
EDIT: Ich merke gerade, mein Programm ist hochgradig uneffizient, CPU-Auslastung 50%, für ein 100KB .exe, das ist ja schlimmer als ein Fourieranalysen-benchmark
-
So, habe mir den Code Mal angeschaut. Sieht doch ganz okay aus, vom Stil abgesehen.
Also für sauberen Codestil kannst Du sicherlich was googlen. Ich würde nicht mehrere Statements zwischen {} ohne Zeilenumbruch machen und der {}-Block sollte bündig zum Befehl darüber sein. Oder man macht eben so was wie
do{, aber bitte nicht mischen, wie Du es tust. Mehr als eine Leerzeile ist Käse und nach public: oder private: macht man auch keine Leerzeile üblicherweise. Außerdem bitte gleichmäßig einrücken, Du hast manchmal 4 Leerzeichen und manchmal eins, das ist Käse.Zu der CPU-Auslastung: Einfach ein Sleep(10); oder so in die innere Schleife stecken.
-
Womit programmierst du denn? Wenn ich z.B. Visual Studio zum Programmieren nutze, dann habe ich keine Probleme mit Einrückungen - auch nicht, wenn ich den Code kopiere.
Der Code sieht schon etwas lesbarer aus, aber ist noch immer nicht schön.
Mal ein Beispiel:
while(true) { do{ QueryPerformanceCounter(&t2); //t2 wird permanent aktualisiert elapsedTime = (t2.QuadPart - t1.QuadPart)*1000000 /frequency.QuadPart; //(60000000/oldbpm) ist das delay in microsekunden, die das programm warten muss, 0.997 ist ein selbstermittleter korrekturfaktor, i ist der zähler, der das Delay nach //jedem tick vervielfacht }while(elapsedTime<((60000000/oldbpm))*0.997*i); Beep(440,100); i++; //zähler wird erhöht, damit später wieder gewartet werden kann wait++; // dient dazu, eine BPM-rate eine weile lang zu halten if(oldbpm<bpm) {oldbpm+=1;QueryPerformanceCounter(&t1);i=1;wait=0;} //ist-wert demm soll-wert mit einer linearen rampe angleichen else if(oldbpm>bpm) {oldbpm-=1;QueryPerformanceCounter(&t1);i=1;wait=0;} else if(wait>=oldbpm*hold/60){wait=0;readsequence();}// wenn die BPM lange genug gehalten wurde, nächste zeile lesen, neue bpm und hold werte benutzen }Besser:
while (true) { do { QueryPerformanceCounter(&t2); //t2 wird permanent aktualisiert elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000000 / frequency.QuadPart; /* (60000000/oldbpm) ist das delay in microsekunden, die das programm warten muss, 0.997 ist ein selbstermittleter korrekturfaktor, i ist der zähler, der das Delay nach //jedem tick vervielfacht */ } while (elapsedTime < ((60000000 / oldbpm)) * 0.997 * i); Beep(440, 100); i++; //zähler wird erhöht, damit später wieder gewartet werden kann wait++; // dient dazu, eine BPM-rate eine weile lang zu halten if (oldbpm < bpm) //ist-wert demm soll-wert mit einer linearen rampe angleichen { oldbpm += 1; QueryPerformanceCounter(&t1); i = 1; wait = 0; } else if (oldbpm > bpm) { oldbpm -= 1; QueryPerformanceCounter(&t1); i = 1; wait = 0; } else if (wait >= oldbpm * hold / 60) // wenn die BPM lange genug gehalten wurde, nächste zeile lesen, neue bpm und hold werte benutzen { wait = 0; readsequence(); } }Natürlich ist einiges davon Geschmackssache, aber es sollte dir ein Anhaltspunkt geben, wie Code lesbarer wird.
-
Mal eine Frage:
Welche Bücher und vorallem welche Programmiersprache würdet ihr mir ans Herz legen, wenn es in Richtung hardwarenahe Programmierung und Echtzeitaudioanalyse geht? Assembler soll da sehr effizient sein, da es keine unnötigen Kapriolen springt, aber eine Ganze Software nur in Assembler?
Ich hab noch nie in Assembler programmiert, muss sich toll anfühlen, wenn man weiß, wie das Bit beim Namen heißt, und was genau im Hintergrund passiert.
Ich habe schon einen Assemblercode gefunden, wo Fourieranalysen implementiert wurde, allerdings will ich NOCH nicht genauer reinschauen, sonst verzettel ich mich hier wieder.
Grob gesagt: Mein Ziel ist es ein Metronom auf Hardware umzusetzen, das auf bestimmtes Spielverhalten mit Taktänderungen reagiert.
Hohes Ziel, hab schon ein sehr gutes kostenloses Buch gefunden zum Thema Digital Signal Processing, allerdings Ebook: www.dspguide.com
Bin dabei es durchzuarbeiten, mit Aufgaben lösen zur Vertiefung.Ihr wisst bestimmt, in welche Richtung ich gehen muss, um mir unnötige Mühe und Ärger zu ersparen?
-
amrosik schrieb:
Mal eine Frage:
Welche Bücher und vorallem welche Programmiersprache würdet ihr mir ans Herz legen, wenn es in Richtung hardwarenahe Programmierung und Echtzeitaudioanalyse geht? Assembler soll da sehr effizient sein, da es keine unnötigen Kapriolen springt, aber eine Ganze Software nur in Assembler?
In Assembler würde ich nur empfehlen wenn du sicher bist, dass das nötig ist. Das ist sehr unkomfortabel und solange du nicht sehr gut darin bist, wird ein Compiler meist besseren Mascheienencode erzeugen. Für welchen Prozessor soll das denn überhaupt sein?
Prinzipiell ist es durchaus empfehlenswert sich mal etwas mit Assembler zu beschäftigen, weil man dann viel besser versteht was der Compiler überhaupt macht, wie der Stack funktioniert, wie Funktionsaufrufe funktionieren usw. Wirklich in Assembler Programmieren würde ich heute nur noch in einzelnen Subroutinen wenn es um die Ausnutzung von Spezialbefehlen auf spezieller Hardware geht.
-
Ups da habe ich versehentlich den Beitrag zweimal abgeschickt. Kann man Beiträge irgendwie löschen?
-
Nein, aber ersetze den Text in deinem letzten einfach durch ein paar Leerzeichen. Das versteht schon jeder.
-
amrosik schrieb:
Mal eine Frage:
Welche Bücher und vorallem welche Programmiersprache würdet ihr mir ans Herz legen, wenn es in Richtung hardwarenahe Programmierung und Echtzeitaudioanalyse geht? Assembler soll da sehr effizient sein, da es keine unnötigen Kapriolen springt, aber eine Ganze Software nur in Assembler?
Benutz C++ wnen es dir gefällt. Der Compiler wird aller Wahrscheinlichkeit besser als du wissen, wie effizienter assembler aussieht.
-
Der Compiler wird aller Wahrscheinlichkeit besser als du wissen, wie effizienter assembler aussieht.
Bis dahin wird sich das aller Wahrscheinlichkeit ändern.
guckt euch mal das Tier an:
http://elm-chan.org/works/akilcd/report_e.html
Hier wurde eine FFT direkt in Assembler auf einem ATmega8 umgesetzt.
-
amrosik schrieb:
Bis dahin wird sich das aller Wahrscheinlichkeit ändern.
Nein. Compiler werden eher besser als schlechter. Zwischen Assembler können und effizient Assembler anwenden liegen ein paar Jahre Erfahrung (und jede Menge tabellen auswnedig lernen die der COmpiler natürlich alle kennt).
-
was sind diese Tabellen genau? Fallunterscheidungen?
-
Nein. Compiler werden eher besser als schlechter.
Anfänger werden üblicherweise anders motiviert als so.