/* Enhanced NCSA Mosaic from Spyglass "Guitar" Copyright 1994 Spyglass, Inc. All Rights Reserved Author(s): Albert Lee alee@spyglass.com */ #include "all.h" #ifdef FEATURE_SOUND_PLAYER #include // Multimedia extensions #include "contmenu.h" #include "history.h" static void Handle_Stop(HWND hDlg); #define TIMER_INTERVAL 100 #define TIMER_ID 1 #define WM_STOP_SOUND WM_USER + 100 extern HWND hwndModeless; extern struct hash_table gSoundCache; extern int device_capability; static BOOL bUserAlerted = FALSE; static int available_device = 0; HBITMAP hPlayUp_Bitmap = NULL, hPlayDown_Bitmap = NULL; HBITMAP hStopUp_Bitmap = NULL, hStopDown_Bitmap = NULL; HBITMAP hPauseUp_Bitmap = NULL, hPauseDown_Bitmap = NULL; void formatTime(int seconds,int cbFormatID,char *szTemp,int cbTemp) { char szNumber[64]; HTFormatNumber(seconds / 10, seconds % 10,szNumber,sizeof(szNumber)); GTR_formatmsg(cbFormatID,szTemp,cbTemp,szNumber); } //******************************************* // Owner-draw button stuff //******************************************* HBITMAP Create_UpButton(HDC hDC, RECT *pRect) { HBRUSH hFace, hBkgnd; HPEN hDark, hHighlight; RECT btnRect; HDC hTemp; HBITMAP hBitmap; COLORREF white; hBitmap = CreateCompatibleBitmap(hDC, pRect->right - pRect->left, pRect->bottom - pRect->top); hTemp = CreateCompatibleDC(hDC); SelectObject(hTemp, hBitmap); // Initialize the background hBkgnd = CreateSolidBrush(GetSysColor(COLOR_WINDOWFRAME)); FillRect(hTemp, pRect, hBkgnd); DeleteObject(hBkgnd); white = RGB(255, 255, 255); SetPixel(hTemp, pRect->left, pRect->top, white); SetPixel(hTemp, pRect->left, pRect->bottom - 1, white); SetPixel(hTemp, pRect->right - 1, pRect->top, white); SetPixel(hTemp, pRect->right - 1, pRect->bottom - 1, white); btnRect = *pRect; InflateRect(&btnRect, -1, -1); hFace = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); FillRect(hTemp, &btnRect, hFace); btnRect.bottom--; hHighlight = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); SelectObject(hTemp, hHighlight); MoveToEx(hTemp, btnRect.left, btnRect.bottom - 1, NULL); LineTo(hTemp, btnRect.left, btnRect.top); LineTo(hTemp, btnRect.right - 1, btnRect.top); MoveToEx(hTemp, btnRect.left + 1, btnRect.bottom - 2, NULL); LineTo(hTemp, btnRect.left + 1, btnRect.top + 1); LineTo(hTemp, btnRect.right - 2, btnRect.top + 1); hDark = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); SelectObject(hTemp, hDark); MoveToEx(hTemp, btnRect.left + 2, btnRect.bottom - 1, NULL); LineTo(hTemp, btnRect.right - 2, btnRect.bottom - 1); LineTo(hTemp, btnRect.right - 2, btnRect.top + 1); MoveToEx(hTemp, btnRect.left + 1, btnRect.bottom, NULL); LineTo(hTemp, btnRect.right - 1, btnRect.bottom); LineTo(hTemp, btnRect.right - 1, btnRect.top); DeleteObject(hDark); DeleteDC(hTemp); DeleteObject(hFace); DeleteObject(hHighlight); return (hBitmap); } HBITMAP Create_DownButton(HDC hDC, RECT *pRect) { HBRUSH hFace, hBkgnd; HPEN hDark; RECT btnRect; HDC hTemp; HBITMAP hBitmap; COLORREF white; hBitmap = CreateCompatibleBitmap(hDC, pRect->right - pRect->left, pRect->bottom - pRect->top); hTemp = CreateCompatibleDC(hDC); SelectObject(hTemp, hBitmap); // Initialize the background hBkgnd = CreateSolidBrush(GetSysColor(COLOR_WINDOWFRAME)); FillRect(hTemp, pRect, hBkgnd); DeleteObject(hBkgnd); btnRect = *pRect; white = RGB(255, 255, 255); SetPixel(hTemp, pRect->left, pRect->top, white); SetPixel(hTemp, pRect->left, pRect->bottom - 1, white); SetPixel(hTemp, pRect->right - 1, pRect->top, white); SetPixel(hTemp, pRect->right - 1, pRect->bottom - 1, white); InflateRect(&btnRect, -1, -1); hFace = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); FillRect(hTemp, &btnRect, hFace); btnRect.bottom--; hDark = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW)); SelectObject(hTemp, hDark); MoveToEx(hTemp, btnRect.left, btnRect.bottom, NULL); LineTo(hTemp, btnRect.left, btnRect.top); LineTo(hTemp, btnRect.right, btnRect.top); MoveToEx(hTemp, btnRect.left + 1, btnRect.bottom, NULL); LineTo(hTemp, btnRect.left + 1, btnRect.top + 1); LineTo(hTemp, btnRect.right, btnRect.top + 1); DeleteObject(hDark); DeleteDC(hTemp); DeleteObject(hFace); return (hBitmap); } void PaintPlayBitmap(HDC hMemDC,int xstart, int ystart, int height, int width ) { BOOL bFirst = TRUE; for (;;) { MoveToEx(hMemDC, xstart, ystart, NULL); LineTo(hMemDC, xstart, ystart + height - 1); if (!bFirst || height != 1) { // Draw again xstart++; MoveToEx(hMemDC, xstart, ystart, NULL); LineTo(hMemDC, xstart, ystart + height - 1); } bFirst = FALSE; if (height == 1) break; height = height - 2; xstart++; ystart++; } } void PaintStopBitmap(HDC hMemDC,int xstart, int ystart, int height, int width ) { int i; for (i = 0; i < height; i++) { MoveToEx(hMemDC, xstart, ystart, NULL); LineTo(hMemDC, xstart, ystart + height - 1); xstart++; } } void PaintPauseBitmap(HDC hMemDC,int xstart, int ystart, int height, int width ) { int i; for (i = 0; i < width; i++) { MoveToEx(hMemDC, xstart, ystart, NULL); LineTo(hMemDC, xstart, ystart + height - 1); xstart++; } xstart += (width - 1); for (i = 0; i < width; i++) { MoveToEx(hMemDC, xstart, ystart, NULL); LineTo(hMemDC, xstart, ystart + height - 1); xstart++; } } #define BITMAP_STOP 0x0000 #define BITMAP_PLAY 0x0001 #define BITMAP_PAUSE 0x0002 void PutBitmap_W(HDC hDC, HBITMAP hBitmap, RECT * pRect, BOOL bUp, DWORD dwBitmapType) { HDC hMemDC; TEXTMETRIC tm; int height, ystart, xstart, halfheight, width; HBITMAP hOld; BOOL bFirst = TRUE; HPEN hPen; hMemDC = CreateCompatibleDC(hDC); SelectObject(hMemDC, GetStockObject(SYSTEM_FONT)); GetTextMetrics(hMemDC, &tm); // Make sure that the height is odd height = tm.tmHeight - tm.tmDescent; if (height % 2 == 0) height++; halfheight = height / 2; width = height / 3; ystart = ((pRect->bottom - pRect->top) / 2) - halfheight; xstart = ((pRect->right - pRect->left) / 2) - halfheight; if (!bUp) { ystart += 1; xstart += 1; } hOld = SelectObject(hMemDC, hBitmap); hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT)); SelectObject(hMemDC, hPen); switch (dwBitmapType) { case BITMAP_STOP: PaintStopBitmap(hMemDC,xstart,ystart,height,width); break; case BITMAP_PLAY: PaintPlayBitmap(hMemDC,xstart,ystart,height,width); break; case BITMAP_PAUSE: PaintPauseBitmap(hMemDC,xstart,ystart,height,width); break; } DeleteObject(hPen); SelectObject(hMemDC, hOld); DeleteDC(hMemDC); } void PutPlayBitmap(HDC hDC, HBITMAP hBitmap, RECT *pRect, BOOL bUp) { // call worker function PutBitmap_W(hDC,hBitmap,pRect,bUp,BITMAP_PLAY); } void PutStopBitmap(HDC hDC, HBITMAP hBitmap, RECT *pRect, BOOL bUp) { // call worker function PutBitmap_W(hDC,hBitmap,pRect,bUp,BITMAP_STOP); } void PutPauseBitmap(HDC hDC, HBITMAP hBitmap, RECT *pRect, BOOL bUp) { // call worker function PutBitmap_W(hDC,hBitmap,pRect,bUp,BITMAP_PAUSE); } void DrawBitmap(HDC hDC, HBITMAP hBitmap, int width, int height) { HDC hMemDC; hMemDC = CreateCompatibleDC(hDC); SelectObject(hMemDC, hBitmap); BitBlt(hDC, 0, 0, width, height, hMemDC, 0, 0, SRCCOPY); DeleteDC(hMemDC); } void Prepare_Buttons(HWND hDlg) { HDC hDC; HWND hwnd; RECT rect; if (!hPlayUp_Bitmap) { hwnd = GetDlgItem(hDlg, RES_SP_PLAY); hDC = GetDC(hwnd); GetClientRect(hwnd, &rect); hPlayUp_Bitmap = Create_UpButton(hDC, &rect); PutPlayBitmap(hDC, hPlayUp_Bitmap, &rect, TRUE); hPlayDown_Bitmap = Create_DownButton(hDC, &rect); PutPlayBitmap(hDC, hPlayDown_Bitmap, &rect, FALSE); ReleaseDC(hwnd, hDC); } if (!hStopUp_Bitmap) { hwnd = GetDlgItem(hDlg, RES_SP_STOP); hDC = GetDC(hwnd); GetClientRect(hwnd, &rect); hStopUp_Bitmap = Create_UpButton(hDC, &rect); PutStopBitmap(hDC, hStopUp_Bitmap, &rect, TRUE); hStopDown_Bitmap = Create_DownButton(hDC, &rect); PutStopBitmap(hDC, hStopDown_Bitmap, &rect, FALSE); ReleaseDC(hwnd, hDC); } if (!hPauseUp_Bitmap) { hwnd = GetDlgItem(hDlg, RES_SP_PAUSE); hDC = GetDC(hwnd); GetClientRect(hwnd, &rect); hPauseUp_Bitmap = Create_UpButton(hDC, &rect); PutPauseBitmap(hDC, hPauseUp_Bitmap, &rect, TRUE); hPauseDown_Bitmap = Create_DownButton(hDC, &rect); PutPauseBitmap(hDC, hPauseDown_Bitmap, &rect, FALSE); ReleaseDC(hwnd, hDC); } } static void DrawButtons(DRAWITEMSTRUCT *dis) { RECT rect; GetClientRect(dis->hwndItem, &rect); switch(dis->CtlID) { case RES_SP_PLAY: if (dis->itemState & ODS_SELECTED) DrawBitmap(dis->hDC, hPlayDown_Bitmap, rect.right, rect.bottom); else DrawBitmap(dis->hDC, hPlayUp_Bitmap, rect.right, rect.bottom); break; case RES_SP_STOP: if (dis->itemState & ODS_SELECTED) DrawBitmap(dis->hDC, hStopDown_Bitmap, rect.right, rect.bottom); else DrawBitmap(dis->hDC, hStopUp_Bitmap, rect.right, rect.bottom); break; case RES_SP_PAUSE: if (dis->itemState & ODS_SELECTED) DrawBitmap(dis->hDC, hPauseDown_Bitmap, rect.right, rect.bottom); else DrawBitmap(dis->hDC, hPauseUp_Bitmap, rect.right, rect.bottom); break; } if (dis->itemState & ODS_FOCUS) { InflateRect(&rect, -6, -5); if (dis->itemState & ODS_SELECTED) OffsetRect(&rect, 1, 1); DrawFocusRect(dis->hDC, &rect); } return; } void SoundPlayer_RecreateButtons(void) { HWND hDlg; hDlg = SoundPlayer_GetNextWindow(TRUE); if (!hDlg) return; SoundPlayer_FreeBitmaps(); hPlayUp_Bitmap = NULL; hStopUp_Bitmap = NULL; hPauseUp_Bitmap = NULL; Prepare_Buttons(hDlg); // force redraw of all dialogs do { InvalidateRect(hDlg, NULL, TRUE); hDlg = SoundPlayer_GetNextWindow(FALSE); } while (hDlg); } //******************************************* // Sound stuff //******************************************* void Prepare_Wave(HWND hDlg) { char temp[100]; struct SoundInfo *si; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); // Assume the worst si->bMemoryError = TRUE; si->hWaveFormat = GlobalAlloc(GHND, sizeof(PCMWAVEFORMAT)); if (!si->hWaveFormat) return; si->hWaveHeader = GlobalAlloc(GHND, sizeof(WAVEHDR)); if (!si->hWaveHeader) return; si->wh = (WAVEHDR *) GlobalLock(si->hWaveHeader); si->pwf = (PCMWAVEFORMAT *) GlobalLock(si->hWaveFormat); si->pwf->wf.wFormatTag = WAVE_FORMAT_PCM; si->pwf->wf.nChannels= (WORD) si->channels; si->pwf->wf.nSamplesPerSec = (DWORD) si->sample_rate; si->pwf->wf.nAvgBytesPerSec = (DWORD) si->sample_rate * si->channels; // Windows requires the data to be played be in globally allocated memory si->hWaveData = GlobalAlloc(GHND, si->buf_size); if (!si->hWaveData) return; si->pWaveData = GlobalLock(si->hWaveData); memcpy(si->pWaveData, si->buf, si->buf_size); if (si->size == SIZE_WORD && !(device_capability == DEVICE_8BIT)) si->pwf->wf.nAvgBytesPerSec *= 2; si->pwf->wf.nBlockAlign = (WORD) (si->size * (int) si->channels); si->pwf->wBitsPerSample = (WORD) (8 * si->size); // Setup window handles si->hwnd = hDlg; si->hwndPos = GetDlgItem(hDlg, RES_SP_POS); si->hwndScroll = GetDlgItem(hDlg, RES_SP_SCROLLBAR); // Create a fake tw for error reporting si->tw = GTR_MALLOC(sizeof(struct Mwin)); if (!si->tw) return; memset(si->tw, 0, sizeof(struct Mwin)); si->tw->hWndFrame = hDlg; // Total time is in tenths of seconds (for example, 39.9 sec == 399 total_time) si->total_time = 10 * si->loc / si->size / si->sample_rate; formatTime(si->total_time,RES_STRING_SOUND2,temp,sizeof(temp)); SetDlgItemText(hDlg, RES_SP_LENGTH, temp); // One line up or line down represents a tenth of a second SetScrollRange(si->hwndScroll, SB_CTL, 0, si->total_time, FALSE); SetScrollPos(si->hwndScroll, SB_CTL, 0, TRUE); // No error si->bMemoryError = FALSE; } void CALLBACK WaveProc(HWAVE hWave, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { static HWND hwnd = NULL; switch(uMsg) { case WOM_OPEN: hwnd = (HWND) dwInstance; break; case WOM_DONE: PostMessage(hwnd, WM_STOP_SOUND, 0, 0); break; } } static void Handle_Play(HWND hDlg) { int pos; int offset; MMRESULT err; struct SoundInfo *si; enum GuitError errNum = MMSYSERR_NOMEM; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); if (si->hwo || si->bMemoryError) return; if (available_device == 0) { // don't report errors if we are hidden (playing background sounds) if (!si->fHidden) { ERR_ReportError(si->tw, errNoSoundDevice, "", ""); } return; } err = waveOutOpen((LPHWAVEOUT) &si->hwo, WAVE_MAPPER, (LPWAVEFORMATEX) si->pwf, (DWORD) WaveProc, (DWORD) si->hwnd, CALLBACK_FUNCTION); if ( !err ) { // Start playing the current piece FROM where the scrollbar is currently positioned pos = GetScrollPos(si->hwndScroll, SB_CTL); // Restart from the beginning if at end if (pos == si->total_time) { pos = 0; SetScrollPos(si->hwndScroll, SB_CTL, 0, TRUE); } si->scroll_offset = pos; // pos now contains the current position in tenths of seconds. From this value, we can // deduce where we shoud start playing from. offset = (si->total_time ?(si->loc * pos / si->total_time) :0); if (offset % 4) offset = offset + (4 - (offset % 4)); XX_DMsg(DBG_MM, ("Handle_Play: Scroll position returned %d\n", pos)); XX_DMsg(DBG_MM, ("Handle_Play: Total time of play (.1 secs) %d\n", si->total_time)); XX_DMsg(DBG_MM, ("Handle_Play: Audio data size is %d\n", si->loc)); XX_DMsg(DBG_MM, ("Handle_Play: Offset = data size * position / total time = %d\n", offset)); si->bPaused = FALSE; si->wh->lpData = (LPBYTE) ((LPBYTE) si->pWaveData + offset); si->wh->dwBufferLength = si->loc - offset; err = waveOutPrepareHeader(si->hwo, si->wh, sizeof(WAVEHDR)); if ( !err ) err = waveOutWrite(si->hwo, si->wh, sizeof(WAVEHDR)); } if (err) { XX_DMsg(DBG_MM, ("Handle_Play: waveOut API call failed\n")); switch (err) { case MMSYSERR_BADDEVICEID: // The specified device ID is out of range. errNum = errNoSoundDevice; break; case MMSYSERR_ALLOCATED: // The specified resource is already allocated. errNum = errDeviceBusy; break; case MMSYSERR_NOMEM: // The system is unable to allocate or lock memory. errNum = errNoSoundMemory; break; case WAVERR_BADFORMAT: // The system attempted to open with an unsupported wave format errNum = errInvalidSoundFormat; break; } if (!si->fHidden) { // only report errors if we are not hidden ERR_ReportError(si->tw, errNum, "", ""); } return; } // Start a timer to update the progress if (!si->bTimerOn) { si->bTimerOn = TRUE; SetTimer(hDlg, TIMER_ID, TIMER_INTERVAL, NULL); } } static void Handle_Timer(HWND hDlg) { MMTIME mmt; int currentpos; char temp[100]; int pos; struct SoundInfo *si; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); pos = GetScrollPos(si->hwndScroll, SB_CTL); // Get the current playing position mmt.wType = TIME_BYTES; waveOutGetPosition(si->hwo, &mmt, sizeof(mmt)); // Not all devices may support the requested type. currentpos = si->scroll_offset; // in tenths of seconds switch(mmt.wType) { case TIME_MS: currentpos += (mmt.u.ms / 100); break; case TIME_SAMPLES: // // Time elapsed in 1/10 secs = 10 * No. of samples taken so far / Samples per second currentpos += (si->sample_rate ?(10 * mmt.u.sample / si->sample_rate) :0); break; case TIME_BYTES: // // Time elapsed in 1/10 secs = total time * No. of bytes processed so far / Total bytes currentpos += (si->loc ?(si->total_time * mmt.u.cb / si->loc) :0); break; } formatTime(currentpos,RES_STRING_SOUND1,temp,sizeof(temp)); SetWindowText(si->hwndPos, temp); SetScrollPos(si->hwndScroll, SB_CTL, currentpos, TRUE); } static void Handle_Pause(HWND hDlg) { struct SoundInfo *si; int pos; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); if (si->bMemoryError) return; if (!si->hwo && si->bPausedAndMoved) { XX_DMsg(DBG_MM, ("Handle_Pause: Resuming play after thumb was moved while paused\n")); si->bPausedAndMoved = FALSE; Handle_Play(hDlg); return; } pos = GetScrollPos(si->hwndScroll, SB_CTL); if (pos == 0) return; if (si->bPaused) { XX_DMsg(DBG_MM, ("Handle_Pause: Restarting paused play\n")); Handle_Play(hDlg); } else if (si->hwo) { XX_DMsg(DBG_MM, ("Handle_Pause: Pausing play\n")); Handle_Stop(hDlg); si->bPaused = TRUE; } } static void Handle_Stop(HWND hDlg) { struct SoundInfo *si; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); if (!si->hwo && si->bMemoryError) return; XX_DMsg(DBG_MM, ("Handle_Stop: Stopping play\n")); if (si->bTimerOn) { si->bTimerOn = FALSE; KillTimer(hDlg, TIMER_ID); } si->bReset = TRUE; waveOutReset(si->hwo); waveOutUnprepareHeader(si->hwo, si->wh, sizeof(WAVEHDR)); waveOutClose(si->hwo); si->hwo = NULL; si->bPaused = FALSE; } static void Handle_ScrollBar(HWND hDlg, HWND hCtl, UINT wNotify, int thumbpos) { int pos; char temp[100]; struct SoundInfo *si; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); pos = GetScrollPos(si->hwndScroll, SB_CTL); switch(wNotify) { case SB_LINEUP: if (pos == 0) break; pos = max(0, pos - 1); formatTime(pos,RES_STRING_SOUND1,temp,sizeof(temp)); SetScrollPos(si->hwndScroll, SB_CTL, pos, TRUE); SetWindowText(si->hwndPos, temp); UpdateWindow(si->hwndPos); if (si->hwo && !si->bPaused) { XX_DMsg(DBG_MM, ("Handle_ScrollBar: Rewound 0.1 secs while playing\n")); si->bInterrupted = TRUE; Handle_Stop(hDlg); } else XX_DMsg(DBG_MM, ("Handle_ScrollBar: Rewound 0.1 secs\n")); break; case SB_LINEDOWN: if (pos == si->total_time) break; pos = min(si->total_time, pos + 1); formatTime(pos,RES_STRING_SOUND1,temp,sizeof(temp)); SetScrollPos(si->hwndScroll, SB_CTL, pos, TRUE); SetWindowText(si->hwndPos, temp); UpdateWindow(si->hwndPos); if (si->hwo && !si->bPaused) { XX_DMsg(DBG_MM, ("Handle_ScrollBar: Fastforwarded 0.1 secs while playing\n")); si->bInterrupted = TRUE; Handle_Stop(hDlg); } else XX_DMsg(DBG_MM, ("Handle_ScrollBar: Fastforwarded 0.1 secs\n")); break; case SB_PAGEUP: if (pos == 0) break; pos = max(0, pos - 10); formatTime(pos,RES_STRING_SOUND1,temp,sizeof(temp)); SetScrollPos(si->hwndScroll, SB_CTL, pos, TRUE); SetWindowText(si->hwndPos, temp); UpdateWindow(si->hwndPos); if (si->hwo && !si->bPaused) { XX_DMsg(DBG_MM, ("Handle_ScrollBar: Rewound 1 sec while playing\n")); si->bInterrupted = TRUE; Handle_Stop(hDlg); } else XX_DMsg(DBG_MM, ("Handle_ScrollBar: Rewound 1 sec\n")); break; case SB_PAGEDOWN: if (pos == si->total_time) break; pos = min(si->total_time, pos + 10); formatTime(pos,RES_STRING_SOUND1,temp,sizeof(temp)); SetScrollPos(si->hwndScroll, SB_CTL, pos, TRUE); SetWindowText(si->hwndPos, temp); UpdateWindow(si->hwndPos); if (si->hwo && !si->bPaused) { XX_DMsg(DBG_MM, ("Handle_ScrollBar: Fastforwarded 1 sec while playing\n")); si->bInterrupted = TRUE; Handle_Stop(hDlg); } else XX_DMsg(DBG_MM, ("Handle_ScrollBar: Fastforwarded 1 sec\n")); break; case SB_THUMBTRACK: formatTime(thumbpos,RES_STRING_SOUND1,temp,sizeof(temp)); SetScrollPos(si->hwndScroll, SB_CTL, thumbpos, TRUE); SetWindowText(si->hwndPos, temp); UpdateWindow(si->hwndPos); if (si->hwo && !si->bPaused) { XX_DMsg(DBG_MM, ("Handle_ScrollBar: Thumb dragged while playing\n")); si->bInterrupted = TRUE; Handle_Stop(hDlg); } else XX_DMsg(DBG_MM, ("Handle_ScrollBar: Thumb dragged\n")); break; case SB_ENDSCROLL: // // If we were interrupted because the user moved the scrollbar while playing, resume // playing. if (si->bInterrupted) { XX_DMsg(DBG_MM, ("Handle_ScrollBar: End scroll. Resuming interrupted play\n")); si->bInterrupted = FALSE; Handle_Play(hDlg); } else if (si->bPaused) { // We were not interrupted but the scrollbar has moved while paused. In order to play // correctly from the new position, we need to stop the playing altogether. XX_DMsg(DBG_MM, ("Handle_ScrollBar: End scroll. Thumb moved while paused\n")); Handle_Stop(hDlg); si->bPausedAndMoved = TRUE; } break; default: break; } } static void Handle_Destroy(HWND hDlg) { struct SoundInfo *si; int i; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); if (si == NULL) return; SetWindowLong(hDlg, DWL_USER, 0); if (si->fsOrig) { if (!si->bNoDeleteFile) { if (!si->fDCached) remove(si->fsOrig); // remove the file if not cached to disk } GTR_FREE(si->fsOrig); } if (si->hwo) { si->bReset = TRUE; waveOutReset(si->hwo); waveOutUnprepareHeader(si->hwo, si->wh, sizeof(WAVEHDR)); waveOutClose(si->hwo); } if (si->hWaveFormat) { GlobalUnlock(si->hWaveFormat); GlobalFree(si->hWaveFormat); } if (si->hWaveHeader) { GlobalUnlock(si->hWaveHeader); GlobalFree(si->hWaveHeader); } if (si->hWaveData) { GlobalUnlock(si->hWaveData); GlobalFree(si->hWaveData); } if (si->buf) GTR_FREE(si->buf); if (si->tw) GTR_FREE(si->tw); if (si->hPlay) { DeleteObject(si->hPlay); } if (si->hStop) { DeleteObject(si->hStop); } if (si->hPause) { DeleteObject(si->hPause); } if (si->fHidden) { // if we are hidden, then we are playing in background and parent // window needs to know if we finish. Send it a message letting // it know we're done. if (si->tw_refer) { SendMessage(si->tw_refer->hWndFrame,WM_AU_BGSOUND_COMPLETED, (WPARAM) hDlg,0); } } i = Hash_FindByData(&gSoundCache, NULL, NULL, si); Hash_DeleteIndexedEntry(&gSoundCache, i); GTR_FREE(si); } static void ResetPosition(HWND hDlg) { struct SoundInfo *si; char szTime[64]; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); GTR_formatmsg(RES_STRING_SOUND3,szTime,sizeof(szTime)); SetWindowText(si->hwndPos, szTime); SetScrollPos(si->hwndScroll, SB_CTL, 0, TRUE); } static void SaveFile(HWND hDlg) { char path[_MAX_PATH + 1]; char tempFile[_MAX_PATH + 1]; char baseFile[_MAX_PATH + 1]; struct SoundInfo *si; char *pExtension; int filter; si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); path[0] = 0; PREF_GetTempPath(_MAX_PATH, path); switch(si->type) { case SOUND_AU: x_get_good_filename(baseFile, _MAX_PATH - strlen(path), si->szURL, HTAtom_for("audio/basic")); strcpy(tempFile, baseFile); // Lose the extension pExtension = strchr(tempFile, '.'); if (pExtension) *pExtension = '\0'; if ((filter = DlgSaveAs_RunDialog(hDlg, NULL, tempFile, 6, RES_STRING_SAVEAS)) < 0) return; if (!strstr(tempFile, ".AU")) { if (filter == 1) strcat(tempFile, ".AU"); } break; case SOUND_AIFF: x_get_good_filename(baseFile, _MAX_PATH - strlen(path), si->szURL, HTAtom_for("audio/x-aiff")); strcpy(tempFile, baseFile); // Lose the extension pExtension = strchr(tempFile, '.'); if (pExtension) *pExtension = '\0'; if ((filter = DlgSaveAs_RunDialog(hDlg, NULL, tempFile, 7, RES_STRING_SAVEAS)) < 0) return; if (!strstr(tempFile, ".AIF")) { if (filter == 1) strcat(tempFile, ".AIF"); } break; default: return; } // Save the file now CopyFile(si->fsOrig, tempFile, FALSE); } DCL_DlgProc(SoundPlayerProc) { struct SoundInfo *si; char temp[128]; HICON hIcon; PAINTSTRUCT ps; int menuID; #if 0 struct Mwin *tw; HMENU hMenu; HWND hwnd; #endif // used in vaiour ifdef's below switch(uMsg) { HANDLE_MSG(hDlg, WM_HSCROLL, Handle_ScrollBar); case WM_INITDIALOG: hwndModeless = hDlg; SetWindowLong(hDlg, DWL_USER, lParam); Prepare_Buttons(hDlg); Prepare_Wave(hDlg); break; case WM_COMMAND: if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) { // Message from a menu item or accelerator menuID = (int) LOWORD(wParam); #ifdef FEATURE_HIDDEN_NOT_HIDDEN if ((menuID >= RES_MENU_CHILD__FIRST__) && (menuID <= RES_MENU_CHILD__LAST__)) { TW_ActivateWindowFromList(menuID, -1, NULL); return TRUE; } #endif // FEATURE_HIDDEN_NOT_HIDDEN switch(menuID) { case RES_MENU_ITEM_CLOSE: PostMessage(hDlg, WM_CLOSE, 0, 0); break; case RES_MENU_ITEM_SAVEAS: SaveFile(hDlg); break; case RES_MENU_ITEM_EXIT: PostMessage(wg.hWndHidden, WM_CLOSE, 0, 0); return TRUE; #ifdef FEATURE_HIDDEN_NOT_HIDDEN case RES_MENU_CHILD_MOREWINDOWS: DlgSelectWindow_RunDialog(hDlg); return TRUE; #endif // FEATURE_HIDDEN_NOT_HIDDEN #ifdef FEATURE_SPYGLASS_HOTLIST case RES_MENU_ITEM_GLOBALHISTORY: DlgHOT_RunDialog(TRUE); return TRUE; case RES_MENU_ITEM_HOTLIST: DlgHOT_RunDialog(FALSE); return TRUE; #endif // FEATURE_SPYGLASS_HOTLIST case RES_MENU_ITEM_ADDCURRENTTOHOTLIST: // For a sound player, URL and title are the same si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); if (!HotList_Add(si->szURL, si->szURL)) ERR_ReportError(si->tw, errHotListItemNotAdded, NULL, NULL); return TRUE; #ifdef OLD_HELP case RES_MENU_ITEM_HELPPAGE: tw = TW_FindTopmostWindow(); OpenHelpWindow(tw->hWndFrame); TW_RestoreWindow(tw->hWndFrame); return TRUE; #endif #ifdef OLD_ABOUT_BOX case RES_MENU_ITEM_ABOUTBOX: DlgAbout_RunDialog(hDlg); return TRUE; #endif #ifdef FEATURE_WINDOWS_MENU case RES_MENU_ITEM_NEWWINDOW: GTR_NewWindow(NULL, NULL, 0, 0, 0, NULL, NULL); return TRUE; case RES_MENU_ITEM_CASCADEWINDOWS: TW_CascadeWindows(); return TRUE; case RES_MENU_ITEM_TILEWINDOWS: TW_TileWindows(); return TRUE; case RES_MENU_ITEM_SWITCHWINDOW: hwnd = TW_GetNextWindow(hDlg); if (hwnd) TW_RestoreWindow(hwnd); return TRUE; #endif default: break; } } switch(LOWORD(wParam)) { case RES_SP_PLAY: if (HIWORD(wParam) == BN_CLICKED) Handle_Play(hDlg); break; case RES_SP_STOP: if (HIWORD(wParam) == BN_CLICKED) { Handle_Stop(hDlg); ResetPosition(hDlg); } break; case RES_SP_PAUSE: if (HIWORD(wParam) == BN_CLICKED) Handle_Pause(hDlg); break; case IDCANCEL: DestroyWindow(hDlg); break; } break; case WM_TIMER: Handle_Timer(hDlg); break; case WM_ACTIVATE: if (LOWORD(wParam) == WA_INACTIVE) hwndModeless = NULL; else hwndModeless = hDlg; break; case WM_ERASEBKGND: if (IsIconic(hDlg)) return TRUE; return FALSE; case WM_QUERYDRAGICON: hIcon = LoadIcon(wg.hInstance, MAKEINTRESOURCE(RES_ICO_FRAME)); return (LONG) hIcon; case WM_PAINT: if (IsIconic(hDlg)) { BeginPaint(hDlg, &ps); DefWindowProc(hDlg, WM_ICONERASEBKGND, (WPARAM) ps.hdc, 0); hIcon = LoadIcon(wg.hInstance, MAKEINTRESOURCE(RES_ICO_FRAME)); DrawIcon(ps.hdc, 0, 0, hIcon); EndPaint(hDlg, &ps); return TRUE; } return FALSE; case WM_ENABLE: if (wParam && !IsWindowEnabled(hDlg)) { if (!TW_EnableModalChild(hDlg)) return FALSE; /* Let Windows enable us */ else return TRUE; /* Do not become enabled since child was enabled */ } return FALSE; case WM_SETCURSOR: /* If the window is currently disabled, we need to give the activation to the window which disabled this window */ if ((!IsWindowEnabled(hDlg)) && ((GetKeyState(VK_LBUTTON) & 0x8000) || (GetKeyState(VK_RBUTTON) & 0x8000))) { TW_EnableModalChild(hDlg); } return (FALSE); #ifdef FEATURE_HIDDEN_NOT_HIDDEN case WM_INITMENU: hMenu = GetMenu(hDlg); TW_CreateWindowList(hDlg, hMenu, NULL); return TRUE; #endif // FEATURE_HIDDEN_NOT_HIDDEN case WM_DRAWITEM: DrawButtons((LPDRAWITEMSTRUCT) lParam); return TRUE; case WM_SYSCOLORCHANGE: /* System color changed, so the button bitmaps need to be recreated */ return 0; case WM_CLOSE: DestroyWindow(hDlg); break; case WM_DESTROY: Handle_Destroy(hDlg); hwndModeless = NULL; break; case WM_STOP_SOUND: si = (struct SoundInfo *) GetWindowLong(hDlg, DWL_USER); if (!si->bReset) { // If we didn't stop the playing by calling waveOutReset, then // this means the sound play has been finished. Stop the playing. Handle_Stop(hDlg); formatTime(si->total_time,RES_STRING_SOUND1,temp,sizeof(temp)); SetWindowText(si->hwndPos, temp); SetScrollPos(si->hwndScroll, SB_CTL, si->total_time, TRUE); XX_DMsg(DBG_MM, ("Playing finished.\n")); } si->bReset = FALSE; // if window is hidden, then as soon as we finish playing we should // automatically loop or close the dialog, rather than hanging around if (si->fHidden) { BOOL fPlayAgain = FALSE; // we need to loop if loops remaining is > 1, or -1 (infinite) if (si->nLoopsRemaining > 1) { fPlayAgain = TRUE; si->nLoopsRemaining--; } else if (si->nLoopsRemaining == -1) { fPlayAgain = TRUE; } if (fPlayAgain) { // play the sound again ResetPosition(hDlg); Handle_Play(hDlg); } else { // kill the dialog PostMessage( hDlg, WM_COMMAND, MAKEWPARAM(IDCANCEL,BN_CLICKED), (LPARAM) 0 ); } } break; case WM_ENTERIDLE: main_EnterIdle(hDlg, wParam); return 0; default: break; } return FALSE; } void GetSoundCapability(void) { WAVEOUTCAPS wc; // Use the first available device waveOutGetDevCaps(0, &wc, sizeof(wc)); if ((wc.dwFormats & WAVE_FORMAT_1M16) || (wc.dwFormats & WAVE_FORMAT_1S16) || (wc.dwFormats & WAVE_FORMAT_2M16) || (wc.dwFormats & WAVE_FORMAT_2S16) || (wc.dwFormats & WAVE_FORMAT_4M16) || (wc.dwFormats & WAVE_FORMAT_4S16)) { device_capability = DEVICE_16BIT; } else device_capability = DEVICE_8BIT; } void CreateSoundPlayer(struct SoundInfo *si, const char *pszURL) { HWND hwnd; int newleft, newtop,nCmdShow; struct Mwin *tw; RECT rect; tw = TW_FindTopmostWindow(); // There will ALWAYS be at least one tw if (tw && IsIconic(tw->hWndFrame)) { ShowWindow(tw->hWndFrame, SW_RESTORE); } hwnd = CreateDialogParam(wg.hInstance, MAKEINTRESOURCE(RES_SP_DIALOG), wg.hWndHidden, SoundPlayerProc, (LPARAM) si); if (!hwnd) return; if (tw) { GetWindowRect(tw->hWndFrame, &rect); newleft = rect.left + GetSystemMetrics(SM_CXSIZE) + GetSystemMetrics(SM_CXFRAME); newtop = rect.top + GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYFRAME); SetWindowPos(hwnd, NULL, newleft, newtop, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } SetWindowText(hwnd, (char *) MB_GetWindowNameFromURL((unsigned char *) pszURL)); // show or hide the dialog depending on if this should be hidden or not. // Normally the dialog will be visible but if we are playing background // sounds for a web page, we don't want the dialog to show up. if (si->fHidden) { nCmdShow = SW_HIDE; } else { nCmdShow = SW_SHOW; } ShowWindow(hwnd,nCmdShow); // start playing once window is up PostMessage( hwnd, WM_COMMAND, MAKEWPARAM(RES_SP_PLAY,BN_CLICKED), (LPARAM) 0 ); available_device = waveOutGetNumDevs(); // (don't display error if we are hidden, playing background sound) if ((available_device == 0) && (!bUserAlerted) && (!si->fHidden)) { // Can't play sound on this machine. Tell user. bUserAlerted = TRUE; ERR_ReportError(si->tw, errNoSoundDevice, "", ""); return; } // If there was a memory error, display the error here // (don't display error if we are hidden, playing background sound) if ((si->bMemoryError) && (!bUserAlerted) && (!si->fHidden)) { bUserAlerted = TRUE; ERR_ReportError(si->tw, errNoSoundMemory, "", ""); } } void SoundPlayer_FreeBitmaps(void) { if (hPlayUp_Bitmap) DeleteObject(hPlayUp_Bitmap); if (hPlayDown_Bitmap) DeleteObject(hPlayDown_Bitmap); if (hStopUp_Bitmap) DeleteObject(hStopUp_Bitmap); if (hStopDown_Bitmap) DeleteObject(hStopDown_Bitmap); if (hPauseUp_Bitmap) DeleteObject(hPauseUp_Bitmap); if (hPauseDown_Bitmap) DeleteObject(hPauseDown_Bitmap); } #endif