// File: sinewave.cxx // Michael Main - Nov 21, 2004 - main@colorado.edu // To include the right libraries: // Compile with: bgi++ studinfo.cxx -o studinfo //----------------------------------------------------------------------------- // Standard C++ Include Facilities: #include // Provides cout for debugging #include // Provides strcpy -- is this right for TCHAR? #include // Provides malloc #include // Provides FILE and size_t for jpeglib #include // Provides the Win32 API #include // Provides message cracker macros (p. 96 Win32 Prog) #include // Provides the _T macro for Visual C++ #include // Provides LPPICTURE #include using namespace std; //----------------------------------------------------------------------------- const double M_PI = 3.14159265; //----------------------------------------------------------------------------- // NOTES: // Many items in this file are still tenative. Those items are marked with // the word TODO. For example: // TODO: Does the waveformat need to be static? // TODO: What if the waveOutOpen function fails? // TODO: Cleanup the programming style //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // CONSTANTS AND PROTOTYPES const int FREQ_MIN = 20; // Minimum frequency in Hz const int FREQ_MAX = 5000; // Maximum frequency in Hz const int FREQ_INIT = 440; // Initial frequency in Hz const int SAMPLE_RATE = 11025; // Samples/sec for the waveform const int BSIZE = 4096; // Size of a waveform buffer // The main message callback functions for the main window and subwindows. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // For main wnd // Prototypes for each message handler function from App B of Win32 Programming. static BOOL OnCreate(HWND, LPCREATESTRUCT); static void OnDestroy(HWND); static void OnScroll(HWND, HWND hctl, UINT code, int pos); // A function to set a header for a waveform static void SetHeader(WAVEHDR& header, CHAR* pBuffer); // This function is called once the first time that Sound is called. static void StartSound( ); // Fill the buffer with a sinewave of the specified frequency static void FillBuffer(char pBuffer[], int iFreq); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Global handles for objects that will reside in the application. // This seems to be a common way of making all objects available to // all the handlers, although a better way might be to set these // as properties of the application. HINSTANCE hInstanceGlobal; // The first parameter to WinMain HWND hwndScroll; // A static control for the scroll bar HWND hwndMain; // A handle to that main window HWAVEOUT hWaveOut; // A handle to the sound card WAVEHDR header1, header2; // Two headers for sound buffers char buffer1[BSIZE], buffer2[BSIZE]; // Two sound buffers int iFreq = FREQ_INIT; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // The WinMain function. int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS WndClass; // The class for the main window that we create MSG Message; // An event message obtained by GetMessage in event loop // Set hInstanceGlobal to this application's first parameter: hInstanceGlobal = hInstance; // Set up the properties of a WinProg window class and register them: WndClass.style = 0; // Default appearance WndClass.cbClsExtra = 0; // Not sure yet WndClass.cbWndExtra = 0; // Not sure yet WndClass.lpfnWndProc = WndProc; // A message-handler function we'll write WndClass.hInstance = hInstance; // HANDLE for this program WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Set mouse image WndClass.hIcon = 0; // HANDLE for an icon (or 0 for default) WndClass.lpszMenuName = 0; // A string to identify the menu (not used) WndClass.lpszClassName = _T("WinProg"); // A string name for the main window WndClass.hbrBackground = CreateSolidBrush(RGB(0,228,168)); // Background col RegisterClass(&WndClass); // Register the window before we start using it. // Create the main window (an object of WinProg class that we registered). // The purpose of the _T macro is to allow the code to be portable between // 8-bit characters and 16-bit wide unicode characters. hwndMain = CreateWindow(_T("WinProg"), // What kind of window _T("Sinewave"), // Title at top of the window WS_OVERLAPPEDWINDOW, // One of several possible styles 0, 0, 220, 80, // top left width height NULL, // HANDLE to the parent window NULL, // HANDLE to the menu hInstance, // HANDLE to this program NULL // This is "lpParam" -- not sure ); ShowWindow (hwndMain, nCmdShow); // Make visible with nCmdShow controls UpdateWindow (hwndMain); // Flush output buffer // Get the sound started. // Warning: This must not be done until after hwndMain has been set! StartSound( ); // The MESSAGE LOOP, which stops when a WM_QUIT message arrives. while (GetMessage(&Message, NULL, 0, 0)) { TranslateMessage(&Message); DispatchMessage(&Message); } return (int) Message.wParam; // From the WM_QUIT message } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // The message-handler functions LRESULT CALLBACK WndProc (HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam) { switch (uiMessage) { HANDLE_MSG(hwnd, WM_HSCROLL, OnScroll); HANDLE_MSG(hwnd, WM_CREATE, OnCreate); HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy); case MM_WOM_OPEN: FillBuffer(buffer1, FREQ_INIT); FillBuffer(buffer2, FREQ_INIT); waveOutWrite(hWaveOut, &header1, sizeof(WAVEHDR)); waveOutWrite(hWaveOut, &header2, sizeof(WAVEHDR)); return TRUE; case MM_WOM_DONE: FillBuffer(((PWAVEHDR)lParam)->lpData, iFreq); waveOutWrite(hWaveOut, (PWAVEHDR) lParam, sizeof(WAVEHDR)); return TRUE; case MM_WOM_CLOSE: return TRUE; } return DefWindowProc(hwnd, uiMessage, wParam, lParam); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Handler for WM_CREATE messages. This function is called once when the // application starts. I'm not certain what the return value means. static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT cs) { //WNDPROC old; // The WndProc for scrollbar before subclassing. // CREATE a static control to hold a scroll bar: Page 704 of Win32 Programming hwndScroll = CreateWindow(_T("scrollbar"), // What type of child window NULL, // Title (not applicable) WS_CHILD // STYLES | WS_VISIBLE | SBS_HORZ, 10, 10, 200, 20, // top left width height hwnd, // HANDLE to the parent window NULL, // HANDLE to the menu hInstanceGlobal, // HANDLE to this program NULL // This is lpParam -- not sure ); // Set the scrollbar's range and initial position SetScrollRange(hwndScroll, SB_CTL, FREQ_MIN, FREQ_MAX, FALSE); SetScrollPos(hwndScroll, SB_CTL, FREQ_INIT, TRUE); return TRUE; // Question: What is the meaning of the return value? } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Handler for WM_DESTROY messages. static void OnDestroy(HWND hwnd) { PostQuitMessage(0); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static void OnScroll(HWND hwnd, HWND hctl, UINT code, int pos) { switch(code) { case SB_PAGELEFT: iFreq /= 2; break; case SB_PAGERIGHT: iFreq *= 2; break; case SB_LINELEFT: iFreq -= 1; break; case SB_LINERIGHT: iFreq += 1; break; case SB_THUMBTRACK: iFreq = pos; break; } iFreq = max(FREQ_MIN, iFreq); iFreq = min(FREQ_MAX, iFreq); SetScrollPos(hctl, SB_CTL, iFreq, TRUE); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static void SetHeader(WAVEHDR& header, char* pBuffer) { header.lpData = pBuffer; header.dwBufferLength = BSIZE; header.dwBytesRecorded = 0; header.dwUser = 0; header.dwFlags = 0; header.dwLoops = 1; header.lpNext = NULL; header.reserved = 0; waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR)); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static void StartSound( ) { static WAVEFORMATEX waveformat; // Does this need to be static? waveformat.wFormatTag = WAVE_FORMAT_PCM; waveformat.nChannels = 1; waveformat.nSamplesPerSec = SAMPLE_RATE; waveformat.nAvgBytesPerSec = SAMPLE_RATE; waveformat.nBlockAlign = 1; waveformat.wBitsPerSample = 8; waveformat.cbSize = 0; waveOutOpen( &hWaveOut, // Handle to the waveout WAVE_MAPPER, // Choose preferred sound card if there are many &waveformat, // That struct we just set (DWORD) hwndMain, // Handle to the window that will get messages 0, // Not used when previous argument is a window CALLBACK_WINDOW // Indicates that argument #4 is a window ); SetHeader(header1, buffer1); SetHeader(header2, buffer2); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- static void FillBuffer(char pBuffer[], int iFreq) // This function fills the buffer of bytes with a sine wave form with a // frequency (in Hertz) given by iFreq (which should be no more than half of // the SAMPLE_RATE. { static double fAngle = 0; // Starting phase of the sine wave from 0 to 2*PI int i; // Loop counter from 0 to number of samples for (i = 0; i < BSIZE; ++i) { pBuffer[i] = (char) (127 + 127 * sin(fAngle)); fAngle += 2 * M_PI * iFreq / SAMPLE_RATE; if (fAngle > 2 * M_PI) fAngle -= 2 * M_PI; } } //-----------------------------------------------------------------------------