c++ - Latenz Töne ausgeben

elektro2

Cadet 4th Year
Registriert
Feb. 2014
Beiträge
67
Liebes Forum,

endlich habe ich meine Klausur "Objektorientiertes Programmieren " hinter mit und wollte nun mal, da ich musikbegeistert bin,
ein kleines Klavier programmieren. Leider habe ich Latenz-Probleme. Wenn ich eine Ton spiele, ist dieser ca. 0,4 Sekunden später zu hören. Ich fände es super, wenn mir jemand einen Tip zur Beseitigung dieser geben kann.

Liebe Grüße!

Code:
#include <iostream>
#include <Windows.h>
#include <conio.h>

using namespace std;

int main()
{
	char a;

	while (1)
	{
		a = _getch();
		if (a == 'a')
		{
			Beep(800, 800);

		}

		if (a == 's')
		{
			Beep(1000, 800);
		}
	}
	cin.get();

	system("Pause");
}
 
Zuletzt bearbeitet:
Scheint so, als ob man lieber z.b Piano Töne von c2 bis c3 als Wave benutzt und diese über c++ steuert
Ergänzung ()

Ich habe eine Funktion Playsound() gefunden , die nur unter WIndows funktioniert. Diese wäre echt super, wenn ich mit piano samples arbeite. Wie es mit der Latenz aussieht wird sich später zeigen;)
Ergänzung ()

Juhuuu es hat geklappt.. ich kann jetzt mit verschiedenen Tasten die Komplette Tonleiter spielen( alles Wav files ).

Meine Frage nun: ( Könnte kompliziert werden)

Ich möchte einen Beat laufen lassen. Während dessen soll man die Töne spielen können.

Ich habe es schon mit zwei verschachtelten while-Schleifen Probiert, in der ersten der Beat, und in der zweiten wird der Ton abgefragt. Beide Schleifen laufen while(1).

Das Problem ist, dass der Beat erst durchläuft und mann danach erst die Töne spielen kann. Der Compiler wartet sogesehen, bis die erste While schleife vollendet ist und geht danach erst in die zweite.

Hab ihr eine Ahnung :)
Danke im Voraus !
 
Das nennt sich Threading ... da du das noch nicht kennst (sowie einige grundlegenden Prinzipien) schlage ich vor dass du dir erstmal eine Menge dazu durchliest ;)
Begriffe wie Neben/Gleichläufigkeit bringen dich bei der Suche gleich etwas voran.

mfg,
Max
 
Also blockt die Funktion, die den Ton ausgeben soll, so lange, bis er fertig ist? Dann hast du ein Problem. Multithreading hilft da auch nur bedingt, weil man ohne genaue Kontrolle über die Samples garantiert keine gute Synchronisation hinbekommt.

Ansonsten schau dir mal bei Zeiten die Audio-Funktionen von SDL2 an, die sind für sowas gut geeignet und plattformunabhängig. Allerdings etwas mehr low-level.
Aktuell ist auch blöderweise deren Webseite down, hoffentlich ändert sich das bald...
 
Zuletzt bearbeitet:
@VikingGe
Nope ... es wird einfach nur versucht, wie vom Prinzip her absolut korrekt, beide while Schleifen nacheinander abzuarbeiten.
Threads sind hierbei alternativlos.

mfg,
Max
 
Hey :) Es wäre super, sozusagen eine funktion zu erschaffen, die neben der Main ausgeführt wird und einfach nur den 2 minütigen beat abspielt. Und in der normalen Main eine While(1) schleife, die die ganze Zeit die Töne abfragt.

Die Töne kann ich über die Tastatur sogesehen schon ohne Latenz abspielen.. höchstens 35 ms aber das ist zu verkraften

Ist c++ für sowas nicht zu berauchen? also würdet ihr mir lieber eine andere Sprache empfehlen?
 
Zuletzt bearbeitet:
Wie sieht denn das Programm jetzt überhaupt konkret aus?

Natürlich geht sowas auch ohne Threads - Samples vom Beat in nen Buffer schreiben, Klaviertöne draufaddieren, Buffer abschicken, fertig. Deswegen ja auch die Idee mit SDL2, IIRC gibts da auch einen einfachen Mixer, mit dem man sowas auf 2-3 Funktionscalls reduzieren kann.

Komplizierter wird die Angelegenheit ohnehin, egal ob nun mit Multithreading oder ohne.

Edit:
Es wäre super, sozusagen eine funktion zu erschaffen, die neben der Main ausgeführt wird
Wobei das natürlich nichts weiter wäre als ein zweiter Thread, siehe Antwort von max_1234.
 
Ich schicks kurz

Problem: Der Beat in der ersten While schleife läuft 2 Minuten durch. Danach geht der Compiler erst in die zweite Schleife

Code:
#include <iostream>
#include <Windows.h>
#include <MMSystem.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>

#pragma comment(lib, "Winmm.lib")

using namespace std;


int main()
{
	
	int eingabe;
	cout << " Synth and Beat generator " << endl;
	cout << " You wanna have a Beat? " << endl << "type in '1' for a Beat...." << endl << "type in '0' for no Beat, " << endl << "begin with Enter" << endl;
	cin >> eingabe;
	

	
	while (1){
		if (eingabe==1)
			PlaySound(TEXT("beat.wav"), NULL, SND_SYNC);

		while (1)
		{


			char a = 'f';
			a = _getch();

			if (a == 'a')


				PlaySound(TEXT("D3Synth.wav"), NULL, SND_ASYNC);

			if (a == 's')
				PlaySound(TEXT("E3Synth.wav"), NULL, SND_ASYNC);

			if (a == 'd')
				PlaySound(TEXT("F3Synth.wav"), NULL, SND_ASYNC);

			if (a == 'f')
				PlaySound(TEXT("G3Synth.wav"), NULL, SND_ASYNC);

			if (a == 'g')
				PlaySound(TEXT("A3Synth.wav"), NULL, SND_ASYNC);

			if (a == 'h')
				PlaySound(TEXT("d2.wav"), NULL, SND_ASYNC);


		}
	}

	system("Pause");
}
Ergänzung ()

Code:
#include <iostream>
#include <Windows.h>
#include <MMSystem.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>

#pragma comment(lib, "Winmm.lib")


using namespace std;

// Soll den Beat ( Länge 2 Minuten laufen lassen)

void task1(){
	int eingabe;
	cout << " Synth and Beat generator " << endl;
	cout << " You wanna have a Beat? " << endl << "type in '1' for a Beat...." << endl << "type in '0' for no Beat, " << endl << "begin with Enter" << endl;
	cin >> eingabe;

	if (eingabe == 1)
		PlaySound(TEXT("beat.wav"), NULL, SND_SYNC);
	else return;

}

// Während der Beat läuft, sollen die Töne abgefragt werden durch Tastatur-eingabe

void task2()
{

	while (1)
	{


		char a = 'f';
		a = _getch();

		if (a == 'a')


			PlaySound(TEXT("D3Synth.wav"), NULL, SND_ASYNC);

		if (a == 's')
			PlaySound(TEXT("E3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'd')
			PlaySound(TEXT("F3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'f')
			PlaySound(TEXT("G3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'g')
			PlaySound(TEXT("A3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'h')
			PlaySound(TEXT("d2.wav"), NULL, SND_ASYNC);
    }


}


int main()
{
	 // Klappt nicht... der Beat läuft, aber die zweite Funktion Task 2 wird nicht aufgerufen 
	
	thread t1(task1);
	t1.join();
	thread t2(task2);
	
	
    cin.get();

	return 0;
}
Ergänzung ()

Hat niemand eine Ahnung :/ --.. Ich habe es mit den Threads probiert aber irgendwie klappt das nicht so recht. Es macht mich verrückt, dass eine so leichte Denkweise so schwer umzusetzen ist .. aber ich bin ambitioniert das heute noch irgendwie hinzubekommen.
 
Zuletzt bearbeitet:
Und genau deswegen ist Multithreading vielleicht nicht das, womit man jemanden unmittelbar nach einer OOP-Klausur konfrontieren muss. :D
Code:
thread t1(task1);
t1.join();

Der Konstruktor startet zwar den Thread, aber mit t1.join wartest du darauf, bis er fertig wird. Der restliche Code läuft dann nur, wenn der Thread schon beendet ist. Das Joinen muss also woanders passieren.
Außerdem joinst du Thread 2 nicht.

Weiterhin fragst du jetzt in beiden Threads die Eingabe auf der Konsole ab. Das... wird nichts, denn
a) möglicherweise sind die Funktionen da nicht threadsicher
b) selbst wenn sie es sind, kommen die Requests nicht in einer festen Reihenfolge an. Ich habe keine Ahnung, was _getch machtl, aber wenn es auch auf eine Eingabe wartet, dann kann es sein, dass du
- dem User sagst, er soll den Beat aktivieren
- dann aber die eingegebene Zahl in dem anderen Thread auswertest, weil der Funktionsaufruf rein zufällig eher kam
- und dann erst die eigentliche Eingabe auswertest.

Also:
- Benutzereingaben nur im Hauptthread
- und task2 am besten auch im Hauptthread, sonst macht der nichts anderes als auf die anderen beiden Threads zu warten.
 
Danke für deine Antwort.. ich habe den COde nun mal umgeschrieben aber irgendwie klappt es immer noch nicht so richtig.. Ich habe jetzt mal keinen join angelegt. Also vom Prinzip her müsste das Programm doch genau das machen, was ich mir vorstelle. Den Beat laufen lassen ( Den Thread starten ) und danach ganz normal die While schleife in der Main funktion behandeln. Keine Ahnung, was ich noch ändern kann.

Der Compiler führt parallel die while schleife nicht aus

Code:
#include <iostream>
#include <Windows.h>
#include <MMSystem.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>

#pragma comment(lib, "Winmm.lib")


using namespace std;

// Soll den Beat ( Länge 2 Minuten laufen lassen)

void task1(int a){

         if (a == 1)
		PlaySound(TEXT("beat.wav"), NULL, SND_SYNC);
	     else return;

}



int main()
{
	int eingabe;
	cout << " Synth and Beat generator " << endl;
	cout << " You wanna have a Beat? " << endl <<
        "type in '1' for a Beat...." << endl << "type in '0' for no   Beat, " << endl << "begin with Enter" 
       << endl;
	cin >> eingabe;

	thread t1(task1, eingabe);
	

	// Während der Beat läuft, sollen die Töne abgefragt werden durch Tastatur-eingabe
	while (1)
	{

		char a = 'f';
		a = _getch();

		if (a == 'a')


			PlaySound(TEXT("D3Synth.wav"), NULL, SND_ASYNC);

		if (a == 's')
			PlaySound(TEXT("E3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'd')
			PlaySound(TEXT("F3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'f')
			PlaySound(TEXT("G3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'g')
			PlaySound(TEXT("A3Synth.wav"), NULL, SND_ASYNC);

		if (a == 'h')
			PlaySound(TEXT("d2.wav"), NULL, SND_ASYNC);
	}

	
	cin.get();

	return 0;
}
 
Zuletzt bearbeitet:
Deine Fehlerbeschreibung ist sehr dünn aber was mir auf Anhib auffällt:
if (a == 1)
Das prüft NICHT, ob die Taste 1 gedrückt wurde! Die Taste 1 ist der Ascii-Buchstabe 1 und hat den Wert 0x31.
Du willst also eigtl prüfen if (a == '1') wie du es ja in der while auch schon machst.

Mal ganz Generell: Die Tasten per while zu checken ist ganz häßlich und definitiv nicht so wie man soetwas programmieren sollte, denn deine CPU ist jetzt zu 100% mit der while beschäftigt - selbst wenn du das Programm nicht verwendest.
Ein Tastendruck sollte einen Event auslösen der dann den Ton spielt.
Aber das ist eher ein Tipp für die Zukunft.
 
Es ist möglich, dass das formell nicht so gut ist, aber einzeln klappt alles. Der Beat läuft durch und ich kann auch die Töne per Tastatur spielen, aber alles gleichzeitig klappt nicht. :/ aber danke für den Tip. Das merke ich mir auf jeden Fall für die nächsten Projekte.

Trotzdem ist es mir ein Rätsel, warum dieses Programm nicht so klappt, wie ich es erwartet habe.

Oder eher gesagt... warum läuft der Thread mit dem Beat nicht "parallel" mit der While schleife in der main. Der Beat ist doch jetzt in einer Thread funktion
Ergänzung ()

Neuer Stand der Dinge... Der Thread funktioniert! Die While(1) schleife auch !
Der Beat spielt und dabei wird die ganze zeit " Test " geschrieben ..

es muss an den Eingaben in der Schleife liegen

Code:
#include <iostream>
#include <Windows.h>
#include <MMSystem.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>

#pragma comment(lib, "Winmm.lib")


using namespace std;

// Soll den Beat ( Länge 2 Minuten laufen lassen)

void task1(char a){
	
	
	if (a == '1')
		PlaySound(TEXT("beat.wav"), NULL, SND_SYNC);
	else return;

}



int main()
{
	char eingabe;
	cout << " Synth and Beat generator " << endl;
	cout << " You wanna have a Beat? " << endl << "type in '1' for a Beat...." << endl << "type in '0' for no Beat, " << endl << "begin with Enter" << endl;
	cin >> eingabe;

	thread t1(task1, eingabe);
	

	// Während der Beat läuft, sollen die Töne abgefragt werden durch Tastatur-eingabe
	while (1)
	{

		cout << "test";
	}

	
	cin.get();

	return 0;
}
 
Zuletzt bearbeitet:
Prüf doch erstmal, ob die Eingabe mit deinem while(1) überhaupt parallel funktioniert zum thread.
Also zB in der while:
if (a == 'a')
cout << "a\n";
else if (a == 'x')
cout << "x\n";

Dann würde ich erwarten das du Musik hörst und a bzw x geprinted werden aber sonst nix passiert.

Dann wüsstest du, dass eben doch nicht die Tasteneingabe das Problem ist sondern etwas anderes: Die Funktion PlaySound lässt sich evtl nicht parallel aufrufen mit mehreren Sounds.

Das könntest du dann auch nochmal anders bestätigen:
Thread1 spielt deine 2min wav und statt der while(1) spielst du ohne ifs direkt eine ANDERE wav. Nur wenn beide gleichzeitig spielen ist diese Funktion überhaupt mehrfach gleichzeitig aufrufbar um Töne zu überlagern.
 
Zuletzt bearbeitet:
Also, für die Funktion PlaySound() brauchst du eigentlichen keinen 2. Thread.

https://msdn.microsoft.com/en-us/library/aa909766.aspx

fdwSound

Flags for playing the sound. The following table shows the possible values.

[...]

SND_ASYNC

The sound is played asynchronously and PlaySound returns immediately after beginning the sound. To terminate an asynchronously played waveform sound, call PlaySound with pszSound set to NULL.
 
hey .. da bin ich wieder :)

Das wird leider alles nichts. Die Funktion Playsound() unterstützt wohl nur ein Wav file zur Laufzeit... Ich zerbreche mir gerade den Kopf daran.
Es muss doch eine leichte Möglichkeit geben. Auch gerade an die, die zB. kleinere Spiele programmieren. Stellt Euch vor, Ihr müsst die Hintergrundmusik immer ausschalten, bevor Ihr einen " Action Sound " startet und wenn dieser vorbei ist, könnt ihr erst wieder Eure Hintergrundmusik einschalten.
 
Zuletzt bearbeitet:
Dann müßtest du vielleicht mit der DirectSound API (oder etwas vergleichbarem) rumhantieren. Die Sounds müssen wohl irgendwie gemixt werden. Google einfach mal "C++ play multiple sounds".
 
Es muss doch eine leichte Möglichkeit geben. Auch gerade an die, die zB. kleinere Spiele programmieren. Stellt Euch vor, Ihr müsst die Hintergrundmusik immer ausschalten, bevor Ihr einen " Action Sound " startet und wenn dieser vorbei ist, könnt ihr erst wieder Eure Hintergrundmusik einschalten.
Natürlich geht es parallel aber eben nicht in 2 Zeilen C++ ohne externe Bibliotheken. Die Api an sich ist absichtlich sehr schwach was so Multimedia angeht, weil C++ Programme auch auf winzige µC passen sollen die die nötige HW garnicht haben. Es gibt aber genug Erweiterungen damit man auch mit C++ genau so Multimedia-Programme schreiben kann wie mit anderen Sprachen.
DirectSound wäre eine (Plattformabhängige) Möglichkeit. Da ich persönlich in den letzten Jahren immer mehr mit QT mache und mehr und mehr begeistert bin wie einfach und elegant das alles zu benutzen ist empfehle ich das einfach mal: http://stackoverflow.com/questions/4473608/how-to-play-sound-with-qt
Oder google nach play sound qt
 
Zuletzt bearbeitet:
Für eine richtige Lösung benötigst du zwei Dinge:
1. Eine Sound-API
2. Multithreading

Für 2 würde ich die Boost-Libs empfehlen, da sie ab C++11 sowieso Standard sind. Beii 1 gibt es wesentlich mehr Möglichkeiten. Die WinApi bietet hier eine Lösung, für die du keine zusätzlichen Libs (außer win32, das eh bei windows mitgeliefert wird) benötigst. Ansonsten gäbe es OpenAl, DirectSound, WASAPI, ASIO, PortAudio usw.

Aber das würde auf jeden Fall vorraussetzen, dass du dich ein bisschen einliest. Mit 2-3 Zeilen ist es da nicht mehr getan.
 
Zurück
Oben