D
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);
}