Sound - Ausgabe eines Signaltonens per Soundkarte mit beliebiger Frequenz
-
Also, mit dem Befehl
Beep (Frequenz, ms)
lässt sich eine Piep-Ton über den PC-Speaker ausgeben.Wie kann nun solch ein Piep-Ton, mit unterschiedlicher Frequenz über
die Lautsprecherboxen (die an der Soundkarte angeschlossen sind) ausgeben?Ich hoffe jemand kann mir helfen.
-
programmierst du ein Musikprogramm
-
Naja, so was ähnliches.
Aber es ist mir wirklich sehr wichtig.
Kannst du mir helfen?
-
Hat sich erledigt.
Hab mich in den Newsgroups umgeschaut und hab was geeignetes gefunden:
http://www.marc-pullmann.de/sinus/DSSineWave.cpp
-
Ich pack das mal in die FAQ und poste den Code direkt hier, falls es die Datei mal nicht mehr gibt:
#include "stdafx.h" #include <tchar.h> #include <stdio.h> #include <mmreg.h> #include <dsound.h> #include <math.h> #define WND_WIDTH 500 #define WND_HEIGHT 200 //*** Strukturen struct memArea { //beschreibt einen Speicherbereich durch einen Zeiger darauf und die Größe des Bereichs in Bytes //wird von der Funktion createSineWave() als Rückgabetyp verwendet void *pointer; DWORD size; }; //*** Variablen im Zusammenhang mit DirectSound *** LPDIRECTSOUND lpDirectSound = NULL; LPDIRECTSOUNDBUFFER lpDSBSecondary = NULL; BOOL dsInitialized; BOOL dsBufferCreated; BOOL dsBufferFilled; //*** Funktionen im Zusammenhang mit DirectSound *** BOOL initDirectSound(HWND); BOOL createStaticBuffer(DWORD, int); BOOL fillStaticBufferFromMem(void *, DWORD); void playStaticBuffer(); void dsStop(); void dsSetVolume(int); //*** andere Variablen int status = 0; //0: nicht los //1: gerade wird abgespielt //2: gerade wird abgespielt und die Frequenz wurde geändert //*** andere Funktionen LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); memArea createSineWave(unsigned int); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = _T("[AppName]"); int xPos, yPos; //anfängliche Fensterposition int xRes, yRes; //Bildschirmauflösung WNDCLASSEX wndclass; //Struktur für die Fensterattribute //Bildschrimauflösung ermitteln xRes = GetSystemMetrics(SM_CXSCREEN); yRes = GetSystemMetrics(SM_CYSCREEN); //Fenster zentrieren xPos = (int) ((xRes - WND_WIDTH) / 2); yPos = (int) ((yRes - WND_HEIGHT) / 2); wndclass.cbSize = sizeof(WNDCLASSEX); wndclass.style = CS_OWNDC; //eigener Gerätekontext wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); //Hintergrundfarbe wie 3D-Elemente wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); RegisterClassEx(&wndclass); //Fensterklasse registrieren //Hauptfenster HWND hwnd = CreateWindowEx(WS_EX_STATICEDGE, szAppName, _T("SineWave"), WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, xPos, yPos, WND_WIDTH, WND_HEIGHT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow); //Fenster anzeigen UpdateWindow(hwnd); //Innenbereich neu zeichnen //Nachrichtenschleife MSG msg; while (GetMessage(&msg, NULL, 0, 0) == TRUE) { //Nachrichten empfangen; NULL, 0, 0: alle Nachrichten; TRUE: falls nicht WM_QUIT-Nachricht (Programm beenden) kommt TranslateMessage(&msg); //Nachricht konvertieren DispatchMessage(&msg); //Nachricht verteilen } return 0; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HDC hdc; //Gerätekontext PAINTSTRUCT ps; //Struktur zur Definition des zu zeichnenden Bereichs static SCROLLINFO si = {0}; //für Scrollbars static HWND hBtnPlay, hScrFreq, hScrVol, hStFreq, hStVol; HFONT hfont; //Schriftart für Buttons etc. HINSTANCE hInstance; char text[32]; static unsigned int frequency = 100; static unsigned int volume = 100; memArea ma; switch (message) { case WM_CREATE: hInstance = (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE); hBtnPlay = CreateWindow(_T("BUTTON"), _T("Play / Stop"), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 150, 70, 80, 30, hwnd, (HMENU) 1, hInstance, NULL); hScrFreq = CreateWindow(_T("SCROLLBAR"), _T("Frequency"), SBS_HORZ | WS_CHILD | WS_VISIBLE, 40, 20, 300, 20, hwnd, (HMENU) 2, hInstance, NULL); hScrVol = CreateWindow(_T("SCROLLBAR"), _T("Volume"), SBS_HORZ | WS_CHILD | WS_VISIBLE, 40, 130, 300, 20, hwnd, (HMENU) 3, hInstance, NULL); hStFreq = CreateWindow(_T("STATIC"), _T("Frequency: 100 Hz"), SS_LEFT | WS_CHILD | WS_VISIBLE, 350, 20, 100, 20, hwnd, (HMENU) 4, hInstance, NULL); hStVol = CreateWindow(_T("STATIC"), _T("Volume"), SS_LEFT | WS_CHILD | WS_VISIBLE, 350, 130, 100, 20, hwnd, (HMENU) 5, hInstance, NULL); //Schriftart setzen hfont = (HFONT) GetStockObject(DEFAULT_GUI_FONT); SendMessage(hBtnPlay, WM_SETFONT, (WPARAM) hfont, MAKELPARAM(TRUE, 0)); SendMessage(hStFreq, WM_SETFONT, (WPARAM) hfont, MAKELPARAM(TRUE, 0)); SendMessage(hStVol, WM_SETFONT, (WPARAM) hfont, MAKELPARAM(TRUE, 0)); //Scrollbars konfigurieren si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_POS | SIF_RANGE; si.nMin = 50; si.nMax = 1000; si.nPos = frequency; SetScrollInfo(hScrFreq, SB_CTL, &si,TRUE); si.nMin = 0; //[DSBVOLUME_MIN/MAX geht nicht, da negative Werte!] si.nMax = 100; si.nPos = volume; //[negative Werte evtl. abfragbar mit GetScrollInfo? s.u.] SetScrollInfo(hScrVol, SB_CTL, &si,TRUE); //DirectSound initialisieren dsInitialized = initDirectSound(hwnd); //Abbrechen, falls gescheitert if (!dsInitialized) { MessageBox(hwnd, _T("DirectSoundCreate or SetCooperativeLevel failed"), _T("Error"), MB_OK | MB_ICONSTOP); SendMessage(hwnd, WM_DESTROY, wParam, lParam); } return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); EndPaint(hwnd, &ps); return 0; case WM_COMMAND: if (wParam == 1) { switch (status) { case 0: ma = createSineWave(frequency); if (ma.pointer == NULL) MessageBox(hwnd, _T("VitualAlloc failed"), _T("Error"), MB_OK | MB_ICONSTOP); else { dsBufferCreated = createStaticBuffer(ma.size, volume); if (!dsBufferCreated) MessageBox(hwnd, _T("CreateSoundBuffer (secondary) failed"), _T("Error"), MB_OK | MB_ICONSTOP); else { dsBufferFilled = fillStaticBufferFromMem(ma.pointer, ma.size); if (!dsBufferFilled) MessageBox(hwnd, _T("Secondary buffer could not be filled"), _T("Error"), MB_OK | MB_ICONSTOP); else { playStaticBuffer(); status = 1; } } } break; case 1: dsStop(); VirtualFree(ma.pointer, ma.size, MEM_DECOMMIT); status = 0; break; case 2: dsStop(); VirtualFree(ma.pointer, ma.size, MEM_DECOMMIT); ma = createSineWave(frequency); if (ma.pointer == NULL) MessageBox(hwnd, _T("VitualAlloc failed"), _T("Error"), MB_OK | MB_ICONSTOP); else { dsBufferCreated = createStaticBuffer(ma.size, volume); if (!dsBufferCreated) MessageBox(hwnd, _T("CreateSoundBuffer (secondary) failed"), _T("Error"), MB_OK | MB_ICONSTOP); else { dsBufferFilled = fillStaticBufferFromMem(ma.pointer, ma.size); if (!dsBufferFilled) MessageBox(hwnd, _T("Secondary buffer could not be filled"), _T("Error"), MB_OK | MB_ICONSTOP); else { playStaticBuffer(); status = 1; } } } break; } } return 0; case WM_HSCROLL: switch (LOWORD(wParam)) { case SB_THUMBTRACK: case SB_THUMBPOSITION: if ((HWND) lParam == hScrFreq) { frequency = HIWORD(wParam); _snprintf(text, 30, "Frequency: %u Hz", frequency); SetWindowText(GetDlgItem(hwnd, 4), _T(text)); if (status == 1) status = 2; } //[Abfrage mit GetScrollInfo geht nicht!] if ((HWND) lParam == hScrVol) { volume = HIWORD(wParam); if (status > 0) dsSetVolume(volume); } si.fMask = SIF_POS; si.nPos = HIWORD(wParam); SetScrollInfo((HWND) lParam, SB_CTL, &si, TRUE); break; } return 0; case WM_DESTROY: //DirectSound-Objekte löschen if (lpDSBSecondary != NULL) lpDSBSecondary->Release(); if (lpDirectSound != NULL) lpDirectSound->Release(); PostQuitMessage(0); return 0; default: return DefWindowProc(hwnd, message, wParam, lParam); } } /***********************************************************************************************/ BOOL initDirectSound(HWND hwnd) { //erstellt ein DirectSound-Objekt, aber noch *keinen* sekundären Puffer HRESULT hResult; //DirectSound-Objekt erstellen hResult = DirectSoundCreate(NULL, &lpDirectSound, NULL); if (hResult != DS_OK) return FALSE; //Cooperative Level auf "normal" setzen hResult = lpDirectSound->SetCooperativeLevel(hwnd, DSSCL_NORMAL); if (hResult != DS_OK) return FALSE; return TRUE; } BOOL createStaticBuffer(DWORD size, int volume) { //löscht den eventuell vorhandenen sekundären Puffer und legt einen neuen Puffer der angegebenen Größe mit der aktuellen Lautstärke an HRESULT hResult; WAVEFORMATEX waveFormat = {0}; DSBUFFERDESC dsBufferDesc = {0}; //vorhandenen Puffer löschen if (lpDSBSecondary != NULL) lpDSBSecondary->Release(); //Ausgabeformat für sekundären Puffer waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = 1; waveFormat.nSamplesPerSec = 44100; waveFormat.wBitsPerSample = 16; waveFormat.nBlockAlign = 2; waveFormat.nAvgBytesPerSec = 88200; //sekundären Puffer anlegen dsBufferDesc.dwSize = sizeof (dsBufferDesc); dsBufferDesc.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS; dsBufferDesc.dwBufferBytes = size; dsBufferDesc.dwReserved = 0; dsBufferDesc.lpwfxFormat = &waveFormat; hResult = lpDirectSound->CreateSoundBuffer(&dsBufferDesc, &lpDSBSecondary, NULL); if (hResult != DS_OK) return FALSE; else dsSetVolume(volume); return TRUE; } memArea createSineWave(unsigned int frequency) { //erzeugt *eine* Sinusschwingung der angegebenen Frequenz //dazu wird ein Speicherbereich reserviert, dessen Position und Größe in der memArea-Struktur zurückgegeben wird //DER SPEICHER MUSS AUSSERHALB DIESER FUNKTION WIEDER FREIGEGEBEN WERDEN, SOBALD ER UND DIE DATEN NICHT MEHR GEBRAUCHT WERDEN! memArea ma; int samples = 44100 / frequency; //44100 Hz Samplefrequenz ist fest vorgegeben int i; //Speicher reservieren ma.size = 2 * samples; //Größe des zu reservierenden Speicherbereichs; 2 Bytes pro Sample (16 bit Auflösung) sind fest vorgegeben ma.pointer = VirtualAlloc(NULL, ma.size, MEM_COMMIT, PAGE_READWRITE); //Sinusschwingung erzeugen if (ma.pointer != NULL) { for (i = 0; i < samples; i++) { *((short *) ma.pointer + i) = (short) (sin(((double) i / (double) samples) * 6.283185) * 32767.0); } } return ma; } BOOL fillStaticBufferFromMem(void *source, DWORD bytes) { //schreibt Daten ("bytes" Bytes) von "source" in den (statischen) sekundären Puffer HRESULT hResult; void *audioPtr1, *audioPtr2; DWORD audioBytes1, audioBytes2; DWORD act_bytes; //Bereich des Puffers (hier: ganzer Puffer) sperren hResult = lpDSBSecondary->Lock(0, 0, &audioPtr1, &audioBytes1, &audioPtr2, &audioBytes2, DSBLOCK_ENTIREBUFFER); //Lock-Größe wird sowieso ignoriert if (hResult != DS_OK) return FALSE; //Daten kopieren if (bytes <= audioBytes1) act_bytes = bytes; else act_bytes = audioBytes1; CopyMemory(audioPtr1, source, act_bytes); //Pufferbereich entsperren hResult = lpDSBSecondary->Unlock(audioPtr1, act_bytes, audioPtr2, 0); if (hResult != DS_OK) return FALSE; return TRUE; } void playStaticBuffer() { //spielt den sekundären Puffer im Loop-Modus ab lpDSBSecondary->SetCurrentPosition(0); //vom Anfang des Puffers an abspielen lpDSBSecondary->Play(0, 0, DSBPLAY_LOOPING); } void dsStop() { lpDSBSecondary->Stop(); } void dsSetVolume(int volume) { int v = DSBVOLUME_MIN + (int) (((double) volume / 100.0) * (double) (DSBVOLUME_MAX - DSBVOLUME_MIN)); lpDSBSecondary->SetVolume(v); }