| 1 | // -------------------------------------------------------------------------- |
| 2 | // Name: sndwin.cpp |
| 3 | // Purpose: |
| 4 | // Date: 08/11/1999 |
| 5 | // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000 |
| 6 | // CVSID: $Id$ |
| 7 | // -------------------------------------------------------------------------- |
| 8 | #ifdef __GNUG__ |
| 9 | #pragma implementation "sndwin.cpp" |
| 10 | #endif |
| 11 | |
| 12 | #include "wx/wxprec.h" |
| 13 | |
| 14 | #ifdef __WINDOWS__ |
| 15 | |
| 16 | #ifndef WX_PRECOMP |
| 17 | #include "wx/defs.h" |
| 18 | #include "wx/app.h" |
| 19 | #include "wx/string.h" |
| 20 | #endif |
| 21 | |
| 22 | #include "wx/module.h" |
| 23 | #include "wx/msw/private.h" |
| 24 | |
| 25 | // ------------------------------------------------------------------------- |
| 26 | // MMedia headers |
| 27 | // ------------------------------------------------------------------------- |
| 28 | |
| 29 | #include "wx/mmedia/sndbase.h" |
| 30 | #include "wx/mmedia/sndwin.h" |
| 31 | #include "wx/mmedia/sndpcm.h" |
| 32 | |
| 33 | // ------------------------------------------------------------------------- |
| 34 | // System headers |
| 35 | // ------------------------------------------------------------------------- |
| 36 | |
| 37 | #include <windows.h> |
| 38 | #include <mmsystem.h> |
| 39 | |
| 40 | // ------------------------------------------------------------------------- |
| 41 | // External definitions, forward, ... |
| 42 | // ------------------------------------------------------------------------- |
| 43 | |
| 44 | typedef struct _wxSoundInternal wxSoundInternal; |
| 45 | typedef struct _wxSoundInfoHeader wxSoundInfoHeader; |
| 46 | |
| 47 | extern const wxChar *wxCanvasClassName; |
| 48 | |
| 49 | wxList *wxSoundHandleList = NULL; |
| 50 | |
| 51 | static inline wxSoundStreamWin *wxFindSoundFromHandle(WXHWND hWnd) |
| 52 | { |
| 53 | wxNode *node = wxSoundHandleList->Find((long)hWnd); |
| 54 | if (!node) |
| 55 | return NULL; |
| 56 | return (wxSoundStreamWin *)node->Data(); |
| 57 | } |
| 58 | |
| 59 | struct _wxSoundInternal { |
| 60 | HWND m_sndWin; |
| 61 | HWAVEIN m_devin; |
| 62 | HWAVEOUT m_devout; |
| 63 | bool m_output_enabled, m_input_enabled; |
| 64 | }; |
| 65 | |
| 66 | struct _wxSoundInfoHeader { |
| 67 | HGLOBAL m_h_header, m_h_data; |
| 68 | char *m_data; |
| 69 | WAVEHDR *m_header; |
| 70 | int m_mode; |
| 71 | bool m_playing, m_recording; |
| 72 | wxUint32 m_position, m_size; |
| 73 | |
| 74 | wxSoundStreamWin *m_driver; |
| 75 | }; |
| 76 | |
| 77 | #define WXSOUND_MAX_QUEUE 10 |
| 78 | |
| 79 | wxSoundStreamWin::wxSoundStreamWin() |
| 80 | { |
| 81 | wxSoundFormatPcm pcm; |
| 82 | |
| 83 | m_production_started = FALSE; |
| 84 | m_internal = new wxSoundInternal; |
| 85 | if (!m_internal) { |
| 86 | m_snderror = wxSOUND_MEMERROR; |
| 87 | m_internal = NULL; |
| 88 | return; |
| 89 | } |
| 90 | m_snderror = wxSOUND_NOERROR; |
| 91 | |
| 92 | // Setup defaults |
| 93 | CreateSndWindow(); |
| 94 | SetSoundFormat(pcm); |
| 95 | |
| 96 | m_internal->m_input_enabled = FALSE; |
| 97 | m_internal->m_output_enabled = FALSE; |
| 98 | |
| 99 | m_waiting_for = FALSE; |
| 100 | |
| 101 | if (!OpenDevice(wxSOUND_OUTPUT)) { |
| 102 | m_snderror = wxSOUND_NOERROR; //next call to OpenDevice won't do this |
| 103 | if (!OpenDevice(wxSOUND_INPUT)) |
| 104 | return; |
| 105 | } |
| 106 | |
| 107 | CloseDevice(); |
| 108 | } |
| 109 | |
| 110 | wxSoundStreamWin::~wxSoundStreamWin() |
| 111 | { |
| 112 | if (m_internal) { |
| 113 | if (m_production_started) |
| 114 | StopProduction(); |
| 115 | DestroySndWindow(); |
| 116 | |
| 117 | delete m_internal; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | // ----------------------------------------------------------------------- |
| 122 | // _wxSoundHandlerWndProc: Window callback to handle buffer completion |
| 123 | // ----------------------------------------------------------------------- |
| 124 | /* |
| 125 | LRESULT APIENTRY _EXPORT |
| 126 | */ |
| 127 | |
| 128 | LRESULT WXDLLEXPORT APIENTRY _EXPORT |
| 129 | |
| 130 | _wxSoundHandlerWndProc(HWND hWnd, UINT message, |
| 131 | WPARAM wParam, LPARAM lParam) |
| 132 | { |
| 133 | wxSoundStreamWin *sndwin; |
| 134 | |
| 135 | sndwin = wxFindSoundFromHandle((WXHWND)hWnd); |
| 136 | if (!sndwin) |
| 137 | return (LRESULT)0; |
| 138 | |
| 139 | switch (message) { |
| 140 | case MM_WOM_DONE: |
| 141 | sndwin->NotifyDoneBuffer(wParam, wxSOUND_OUTPUT); |
| 142 | break; |
| 143 | case MM_WIM_DATA: |
| 144 | sndwin->NotifyDoneBuffer(wParam, wxSOUND_INPUT); |
| 145 | break; |
| 146 | default: |
| 147 | break; |
| 148 | } |
| 149 | return (LRESULT)0; |
| 150 | } |
| 151 | |
| 152 | // ----------------------------------------------------------------------- |
| 153 | // CreateSndWindow() creates an hidden window which will receive the sound |
| 154 | // events |
| 155 | // ----------------------------------------------------------------------- |
| 156 | |
| 157 | void wxSoundStreamWin::CreateSndWindow() |
| 158 | { |
| 159 | FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc, |
| 160 | wxGetInstance()); |
| 161 | int error; |
| 162 | |
| 163 | m_internal->m_sndWin = ::CreateWindow(wxCanvasClassName, NULL, 0, |
| 164 | 0, 0, 0, 0, NULL, (HMENU) NULL, |
| 165 | wxGetInstance(), NULL); |
| 166 | |
| 167 | error = GetLastError(); |
| 168 | |
| 169 | ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc); |
| 170 | |
| 171 | // Add this window to the sound handle list so we'll be able to redecode |
| 172 | // the "magic" number. |
| 173 | wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this); |
| 174 | } |
| 175 | |
| 176 | // ----------------------------------------------------------------------- |
| 177 | // DestroySndWindow() destroys the hidden window |
| 178 | // ----------------------------------------------------------------------- |
| 179 | |
| 180 | void wxSoundStreamWin::DestroySndWindow() |
| 181 | { |
| 182 | if (m_internal->m_sndWin) { |
| 183 | ::DestroyWindow(m_internal->m_sndWin); |
| 184 | wxSoundHandleList->DeleteObject((wxObject *)this); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // ------------------------------------------------------------------------- |
| 189 | // OpenDevice(int mode) initializes the windows driver for a "mode" |
| 190 | // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set, |
| 191 | // the driver is opened for output operation, and if the bit "wxSOUND_INPUT" |
| 192 | // is set, then the driver is opened for input operation. The two modes |
| 193 | // aren't exclusive. |
| 194 | // The initialization parameters (sample rate, ...) are taken from the |
| 195 | // m_sndformat object. |
| 196 | // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO |
| 197 | // queue. |
| 198 | // ------------------------------------------------------------------------- |
| 199 | bool wxSoundStreamWin::OpenDevice(int mode) |
| 200 | { |
| 201 | wxSoundFormatPcm *pcm; |
| 202 | WAVEFORMATEX wformat; |
| 203 | |
| 204 | if (!m_sndformat) { |
| 205 | m_snderror = wxSOUND_INVFRMT; |
| 206 | return FALSE; |
| 207 | } |
| 208 | |
| 209 | pcm = (wxSoundFormatPcm *)m_sndformat; |
| 210 | |
| 211 | wformat.wFormatTag = WAVE_FORMAT_PCM; |
| 212 | wformat.nChannels = pcm->GetChannels(); |
| 213 | wformat.nBlockAlign = wformat.nChannels * pcm->GetBPS() / 8; |
| 214 | wformat.nSamplesPerSec = pcm->GetSampleRate(); |
| 215 | wformat.nAvgBytesPerSec = wformat.nSamplesPerSec * wformat.nBlockAlign; |
| 216 | wformat.wBitsPerSample = pcm->GetBPS(); |
| 217 | wformat.cbSize = 0; |
| 218 | |
| 219 | // ----------------------------------- |
| 220 | // Open the driver for Output operation |
| 221 | // ----------------------------------- |
| 222 | if (mode & wxSOUND_OUTPUT) { |
| 223 | MMRESULT result; |
| 224 | |
| 225 | result = waveOutOpen(&m_internal->m_devout, |
| 226 | WAVE_MAPPER, &wformat, |
| 227 | (DWORD)m_internal->m_sndWin, 0, |
| 228 | CALLBACK_WINDOW); |
| 229 | |
| 230 | if (result != MMSYSERR_NOERROR) { |
| 231 | m_snderror = wxSOUND_INVDEV; |
| 232 | return FALSE; |
| 233 | } |
| 234 | |
| 235 | m_output_frag_out = WXSOUND_MAX_QUEUE-1; |
| 236 | m_current_frag_out = 0; |
| 237 | |
| 238 | m_internal->m_output_enabled = TRUE; |
| 239 | } |
| 240 | // ----------------------------------- |
| 241 | // Open the driver for Input operation |
| 242 | // ----------------------------------- |
| 243 | if (mode & wxSOUND_INPUT) { |
| 244 | MMRESULT result; |
| 245 | |
| 246 | result = waveInOpen(&m_internal->m_devin, |
| 247 | WAVE_MAPPER, &wformat, |
| 248 | (DWORD)m_internal->m_sndWin, 0, |
| 249 | CALLBACK_WINDOW); |
| 250 | |
| 251 | if (result != MMSYSERR_NOERROR) { |
| 252 | m_snderror = wxSOUND_INVDEV; |
| 253 | return FALSE; |
| 254 | } |
| 255 | |
| 256 | m_current_frag_in = WXSOUND_MAX_QUEUE-1; |
| 257 | m_input_frag_in = 0; |
| 258 | |
| 259 | m_internal->m_input_enabled = TRUE; |
| 260 | } |
| 261 | |
| 262 | if (mode & wxSOUND_OUTPUT) { |
| 263 | if (!AllocHeaders(wxSOUND_OUTPUT)) { |
| 264 | CloseDevice(); |
| 265 | return FALSE; |
| 266 | } |
| 267 | } |
| 268 | if (mode & wxSOUND_INPUT) { |
| 269 | if (!AllocHeaders(wxSOUND_INPUT)) { |
| 270 | CloseDevice(); |
| 271 | return FALSE; |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | return TRUE; |
| 276 | } |
| 277 | |
| 278 | // ------------------------------------------------------------------------- |
| 279 | // CloseDevice() closes the driver handles and frees memory allocated for |
| 280 | // IO queues. |
| 281 | // ------------------------------------------------------------------------- |
| 282 | void wxSoundStreamWin::CloseDevice() |
| 283 | { |
| 284 | if (m_internal->m_output_enabled) { |
| 285 | FreeHeaders(wxSOUND_OUTPUT); |
| 286 | m_internal->m_output_enabled = FALSE; |
| 287 | waveOutClose(m_internal->m_devout); |
| 288 | } |
| 289 | |
| 290 | if (m_internal->m_input_enabled) { |
| 291 | FreeHeaders(wxSOUND_INPUT); |
| 292 | m_internal->m_input_enabled = FALSE; |
| 293 | waveInClose(m_internal->m_devin); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | // ------------------------------------------------------------------------- |
| 298 | // AllocHeader(int mode) |
| 299 | // |
| 300 | // mode has the same mean as in OpenDevice() except that here the two flags |
| 301 | // must be exclusive. |
| 302 | // AllocHeader() initializes an element of an operation (this can be input |
| 303 | // or output). It means it allocates the sound header's memory block |
| 304 | // and "prepares" it (It is needed by Windows). At the same time, it sets |
| 305 | // private datas so we can the header's owner (See callback). |
| 306 | // |
| 307 | // It returns the new allocated block or NULL. |
| 308 | // ------------------------------------------------------------------------- |
| 309 | wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode) |
| 310 | { |
| 311 | wxSoundInfoHeader *info; |
| 312 | WAVEHDR *header; |
| 313 | |
| 314 | // Some memory allocation |
| 315 | info = new wxSoundInfoHeader; |
| 316 | info->m_h_data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize()); |
| 317 | info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); |
| 318 | if (!info->m_h_data || !info->m_h_header) { |
| 319 | delete info; |
| 320 | m_snderror = wxSOUND_MEMERROR; |
| 321 | return NULL; |
| 322 | } |
| 323 | |
| 324 | // Get the two pointers from the system |
| 325 | info->m_data = (char *)GlobalLock(info->m_h_data); |
| 326 | info->m_header = (WAVEHDR *)GlobalLock(info->m_h_header); |
| 327 | // Set the header's mode |
| 328 | info->m_mode = mode; |
| 329 | // Set the parent of the header |
| 330 | info->m_driver = this; |
| 331 | // Clean it up |
| 332 | ClearHeader(info); |
| 333 | |
| 334 | header = info->m_header; |
| 335 | // Initialize Windows variables |
| 336 | header->lpData = info->m_data; |
| 337 | header->dwBufferLength = GetBestSize(); |
| 338 | header->dwUser = (DWORD)info; |
| 339 | header->dwFlags = WHDR_DONE; |
| 340 | |
| 341 | // "Prepare" the header |
| 342 | if (mode == wxSOUND_INPUT) { |
| 343 | MMRESULT result; |
| 344 | |
| 345 | result = waveInPrepareHeader(m_internal->m_devin, header, |
| 346 | sizeof(WAVEHDR)); |
| 347 | |
| 348 | if (result != MMSYSERR_NOERROR) { |
| 349 | // If something goes wrong, free everything. |
| 350 | GlobalUnlock(info->m_data); |
| 351 | GlobalUnlock(info->m_header); |
| 352 | GlobalFree(info->m_h_data); |
| 353 | GlobalFree(info->m_h_header); |
| 354 | delete info; |
| 355 | |
| 356 | m_snderror = wxSOUND_IOERROR; |
| 357 | return NULL; |
| 358 | } |
| 359 | } else if (mode == wxSOUND_OUTPUT) { |
| 360 | MMRESULT result; |
| 361 | |
| 362 | result = waveOutPrepareHeader(m_internal->m_devout, header, |
| 363 | sizeof(WAVEHDR)); |
| 364 | |
| 365 | if (result != MMSYSERR_NOERROR) { |
| 366 | // If something goes wrong, free everything. |
| 367 | GlobalUnlock(info->m_data); |
| 368 | GlobalUnlock(info->m_header); |
| 369 | GlobalFree(info->m_h_data); |
| 370 | GlobalFree(info->m_h_header); |
| 371 | delete info; |
| 372 | |
| 373 | m_snderror = wxSOUND_IOERROR; |
| 374 | return NULL; |
| 375 | } |
| 376 | } |
| 377 | return info; |
| 378 | } |
| 379 | |
| 380 | // ------------------------------------------------------------------------- |
| 381 | // AllocHeaders(int mode) |
| 382 | // |
| 383 | // "mode" has the same mean as for OpenDevice() except that the two flags must |
| 384 | // be exclusive. |
| 385 | // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation |
| 386 | // queue. It uses AllocHeader() for each element. |
| 387 | // |
| 388 | // Once it has allocated all blocks, it returns TRUE and if an error occured |
| 389 | // it returns FALSE. |
| 390 | // ------------------------------------------------------------------------- |
| 391 | bool wxSoundStreamWin::AllocHeaders(int mode) |
| 392 | { |
| 393 | int i; |
| 394 | wxSoundInfoHeader **headers; |
| 395 | |
| 396 | if (mode == wxSOUND_OUTPUT) |
| 397 | headers = m_headers_play = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE]; |
| 398 | else |
| 399 | headers = m_headers_rec = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE]; |
| 400 | |
| 401 | memset(headers, 0, WXSOUND_MAX_QUEUE*sizeof(wxSoundInfoHeader *)); |
| 402 | |
| 403 | for (i=0;i<WXSOUND_MAX_QUEUE;i++) { |
| 404 | headers[i] = AllocHeader(mode); |
| 405 | if (!headers[i]) { |
| 406 | FreeHeaders(mode); |
| 407 | return FALSE; |
| 408 | } |
| 409 | } |
| 410 | return TRUE; |
| 411 | } |
| 412 | |
| 413 | // ------------------------------------------------------------------------- |
| 414 | // FreeHeader(int mode) |
| 415 | // |
| 416 | // "mode" has the same mean as for OpenDevice() except that the two flags must |
| 417 | // be exclusive. |
| 418 | // FreeHeader() frees a memory block and "unprepares" it. |
| 419 | // ------------------------------------------------------------------------- |
| 420 | void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode) |
| 421 | { |
| 422 | if (mode == wxSOUND_OUTPUT) |
| 423 | waveOutUnprepareHeader(m_internal->m_devout, header->m_header, sizeof(WAVEHDR)); |
| 424 | else |
| 425 | waveInUnprepareHeader(m_internal->m_devin, header->m_header, sizeof(WAVEHDR)); |
| 426 | |
| 427 | GlobalUnlock(header->m_data); |
| 428 | GlobalUnlock(header->m_header); |
| 429 | GlobalFree(header->m_h_header); |
| 430 | GlobalFree(header->m_h_data); |
| 431 | delete header; |
| 432 | } |
| 433 | |
| 434 | // ------------------------------------------------------------------------- |
| 435 | // FreeHeaders(int mode) |
| 436 | // |
| 437 | // "mode" has the same mean as for OpenDevice() except that the two flags must |
| 438 | // be exclusive. |
| 439 | // FreeHeaders() frees all an operation queue once it has checked that |
| 440 | // all buffers have been terminated. |
| 441 | // ------------------------------------------------------------------------- |
| 442 | void wxSoundStreamWin::FreeHeaders(int mode) |
| 443 | { |
| 444 | int i; |
| 445 | wxSoundInfoHeader ***headers; |
| 446 | |
| 447 | if (mode == wxSOUND_OUTPUT) |
| 448 | headers = &m_headers_play; |
| 449 | else |
| 450 | headers = &m_headers_rec; |
| 451 | |
| 452 | for (i=0;i<WXSOUND_MAX_QUEUE;i++) { |
| 453 | if ((*headers)[i]) { |
| 454 | // We wait for the end of the buffer |
| 455 | WaitFor((*headers)[i]); |
| 456 | // Then, we free the header |
| 457 | FreeHeader((*headers)[i], mode); |
| 458 | } |
| 459 | } |
| 460 | delete[] (*headers); |
| 461 | (*headers) = NULL; |
| 462 | } |
| 463 | |
| 464 | // ------------------------------------------------------------------------- |
| 465 | // WaitFor(wxSoundInfoHeader *info) |
| 466 | // |
| 467 | // "info" is one element of an IO queue |
| 468 | // WaitFor() checks whether the specified block has been terminated. |
| 469 | // If it hasn't been terminated, it waits for its termination. |
| 470 | // |
| 471 | // NB: if it's a partially filled buffer it adds it to the Windows queue |
| 472 | // ------------------------------------------------------------------------- |
| 473 | void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info) |
| 474 | { |
| 475 | // If the buffer is finished, we return immediately |
| 476 | if (!info->m_playing) { |
| 477 | |
| 478 | // We begun filling it: we must send it to the Windows queue |
| 479 | if (info->m_position != 0) { |
| 480 | memset(info->m_data + info->m_position, 0, info->m_size); |
| 481 | AddToQueue(info); |
| 482 | } |
| 483 | } |
| 484 | |
| 485 | if (m_waiting_for) { |
| 486 | // PROBLEM // |
| 487 | return; |
| 488 | } |
| 489 | m_waiting_for = TRUE; |
| 490 | // Else, we wait for its termination |
| 491 | while (info->m_playing || info->m_recording) |
| 492 | wxYield(); |
| 493 | m_waiting_for = FALSE; |
| 494 | } |
| 495 | |
| 496 | // ------------------------------------------------------------------------- |
| 497 | // AddToQueue(wxSoundInfoHeader *info) |
| 498 | // |
| 499 | // For "info", see WaitFor() |
| 500 | // AddToQueue() sends the IO queue element to the Windows queue. |
| 501 | // |
| 502 | // Warning: in the current implementation, it partially assume we send the |
| 503 | // element in the right order. This is true in that implementation but if |
| 504 | // you use it elsewhere, be careful: it may shuffle all your sound datas. |
| 505 | // ------------------------------------------------------------------------- |
| 506 | bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info) |
| 507 | { |
| 508 | MMRESULT result; |
| 509 | |
| 510 | if (info->m_mode == wxSOUND_INPUT) { |
| 511 | // Increment the input fragment pointer |
| 512 | result = waveInAddBuffer(m_internal->m_devin, |
| 513 | info->m_header, sizeof(WAVEHDR)); |
| 514 | if (result == MMSYSERR_NOERROR) |
| 515 | info->m_recording = TRUE; |
| 516 | else |
| 517 | return FALSE; |
| 518 | } else if (info->m_mode == wxSOUND_OUTPUT) { |
| 519 | result = waveOutWrite(m_internal->m_devout, |
| 520 | info->m_header, sizeof(WAVEHDR)); |
| 521 | if (result == MMSYSERR_NOERROR) |
| 522 | info->m_playing = TRUE; |
| 523 | else |
| 524 | return FALSE; |
| 525 | } |
| 526 | return TRUE; |
| 527 | } |
| 528 | |
| 529 | // ------------------------------------------------------------------------- |
| 530 | // ClearHeader(wxSoundInfoHeader *info) |
| 531 | // |
| 532 | // ClearHeader() reinitializes the parameters of "info" to their default |
| 533 | // value. |
| 534 | // ------------------------------------------------------------------------- |
| 535 | void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info) |
| 536 | { |
| 537 | info->m_playing = FALSE; |
| 538 | info->m_recording = FALSE; |
| 539 | info->m_position = 0; |
| 540 | info->m_size = GetBestSize(); |
| 541 | } |
| 542 | |
| 543 | // ------------------------------------------------------------------------- |
| 544 | // wxSoundInfoHeader *NextFragmentOutput() |
| 545 | // |
| 546 | // NextFragmentOutput() looks for a free output block. It will always |
| 547 | // return you a non-NULL pointer but it may waits for an empty buffer a long |
| 548 | // time. |
| 549 | // ------------------------------------------------------------------------- |
| 550 | wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput() |
| 551 | { |
| 552 | if (m_headers_play[m_current_frag_out]->m_playing) { |
| 553 | m_current_frag_out = (m_current_frag_out + 1) % WXSOUND_MAX_QUEUE; |
| 554 | |
| 555 | if (m_headers_play[m_current_frag_out]->m_playing) |
| 556 | WaitFor(m_headers_play[m_current_frag_out]); |
| 557 | } |
| 558 | if (m_current_frag_out == m_output_frag_out) |
| 559 | m_queue_filled = TRUE; |
| 560 | return m_headers_play[m_current_frag_out]; |
| 561 | } |
| 562 | |
| 563 | // ------------------------------------------------------------------------- |
| 564 | // The behaviour of Write is documented in the global documentation. |
| 565 | // ------------------------------------------------------------------------- |
| 566 | wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len) |
| 567 | { |
| 568 | m_lastcount = 0; |
| 569 | if (!m_internal->m_output_enabled) { |
| 570 | m_snderror = wxSOUND_NOTSTARTED; |
| 571 | return *this; |
| 572 | } |
| 573 | |
| 574 | |
| 575 | while (len > 0) { |
| 576 | wxSoundInfoHeader *header; |
| 577 | wxUint32 to_copy; |
| 578 | |
| 579 | // Get a new output fragment |
| 580 | header = NextFragmentOutput(); |
| 581 | |
| 582 | to_copy = (len > header->m_size) ? header->m_size : len; |
| 583 | memcpy(header->m_data + header->m_position, buffer, to_copy); |
| 584 | |
| 585 | header->m_position += to_copy; |
| 586 | header->m_size -= to_copy; |
| 587 | buffer = (((const char *)buffer) + to_copy); |
| 588 | len -= to_copy; |
| 589 | m_lastcount += to_copy; |
| 590 | |
| 591 | // If the fragment is full, we send it to the Windows queue. |
| 592 | if (header->m_size == 0) |
| 593 | if (!AddToQueue(header)) { |
| 594 | m_snderror = wxSOUND_IOERROR; |
| 595 | return *this; |
| 596 | } |
| 597 | } |
| 598 | return *this; |
| 599 | } |
| 600 | |
| 601 | // ------------------------------------------------------------------------- |
| 602 | // NextFragmentInput is not functional. |
| 603 | // ------------------------------------------------------------------------- |
| 604 | wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput() |
| 605 | { |
| 606 | wxSoundInfoHeader *header; |
| 607 | |
| 608 | // Queue pointer: reader |
| 609 | m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE; |
| 610 | |
| 611 | header = m_headers_rec[m_current_frag_in]; |
| 612 | // If the current buffer is in recording mode, we must wait for its |
| 613 | // completion. |
| 614 | if (header->m_recording) |
| 615 | WaitFor(header); |
| 616 | |
| 617 | // We reached the writer position: the queue is full. |
| 618 | if (m_current_frag_in == m_input_frag_in) |
| 619 | m_queue_filled = TRUE; |
| 620 | |
| 621 | return header; |
| 622 | } |
| 623 | |
| 624 | // ------------------------------------------------------------------------- |
| 625 | // The behaviour of Read is documented in the global documentation. |
| 626 | // ------------------------------------------------------------------------- |
| 627 | wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len) |
| 628 | { |
| 629 | wxSoundInfoHeader *header; |
| 630 | wxUint32 to_copy; |
| 631 | |
| 632 | m_lastcount = 0; |
| 633 | if (!m_internal->m_input_enabled) |
| 634 | return *this; |
| 635 | |
| 636 | while (len > 0) { |
| 637 | header = NextFragmentInput(); |
| 638 | |
| 639 | to_copy = (len > header->m_size) ? header->m_size : len; |
| 640 | memcpy(buffer, header->m_data + header->m_position, to_copy); |
| 641 | |
| 642 | header->m_position += to_copy; |
| 643 | header->m_size -= to_copy; |
| 644 | buffer = (((char *)buffer) + to_copy); |
| 645 | len -= to_copy; |
| 646 | m_lastcount += to_copy; |
| 647 | |
| 648 | if (header->m_size == 0) { |
| 649 | ClearHeader(header); |
| 650 | if (!AddToQueue(header)) { |
| 651 | m_snderror = wxSOUND_IOERROR; |
| 652 | return *this; |
| 653 | } |
| 654 | } |
| 655 | } |
| 656 | return *this; |
| 657 | } |
| 658 | |
| 659 | // ------------------------------------------------------------------------- |
| 660 | // NotifyDoneBuffer(wxUint32 dev_handle) |
| 661 | // |
| 662 | // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound |
| 663 | // fragment finished. It reinitializes the parameters of the fragment and |
| 664 | // sends an event to the clients. |
| 665 | // ------------------------------------------------------------------------- |
| 666 | void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle, int flag) |
| 667 | { |
| 668 | wxSoundInfoHeader *info; |
| 669 | |
| 670 | if (flag == wxSOUND_OUTPUT) { |
| 671 | if (!m_internal->m_output_enabled) |
| 672 | return; |
| 673 | |
| 674 | // Queue pointer: reader |
| 675 | m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE; |
| 676 | info = m_headers_play[m_output_frag_out]; |
| 677 | // Clear header to tell the system the buffer is free now |
| 678 | ClearHeader(info); |
| 679 | m_queue_filled = FALSE; |
| 680 | if (!m_waiting_for) |
| 681 | // Try to requeue a new buffer. |
| 682 | OnSoundEvent(wxSOUND_OUTPUT); |
| 683 | } else { |
| 684 | if (!m_internal->m_input_enabled) |
| 685 | return; |
| 686 | |
| 687 | // Recording completed |
| 688 | m_headers_rec[m_input_frag_in]->m_recording = FALSE; |
| 689 | // Queue pointer: writer |
| 690 | m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE; |
| 691 | if (!m_waiting_for) |
| 692 | OnSoundEvent(wxSOUND_INPUT); |
| 693 | m_queue_filled = FALSE; |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | // ------------------------------------------------------------------------- |
| 698 | // SetSoundFormat() |
| 699 | // ------------------------------------------------------------------------- |
| 700 | bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base) |
| 701 | { |
| 702 | // TODO: detect best format |
| 703 | return wxSoundStream::SetSoundFormat(base); |
| 704 | } |
| 705 | |
| 706 | // ------------------------------------------------------------------------- |
| 707 | // StartProduction() |
| 708 | // ------------------------------------------------------------------------- |
| 709 | bool wxSoundStreamWin::StartProduction(int evt) |
| 710 | { |
| 711 | if (!m_internal) |
| 712 | return FALSE; |
| 713 | |
| 714 | if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) || |
| 715 | (m_internal->m_input_enabled && (evt & wxSOUND_INPUT))) |
| 716 | CloseDevice(); |
| 717 | |
| 718 | if (!OpenDevice(evt)) |
| 719 | return FALSE; |
| 720 | |
| 721 | m_production_started = TRUE; |
| 722 | m_queue_filled = FALSE; |
| 723 | // Send a dummy event to start. |
| 724 | if (evt & wxSOUND_OUTPUT) |
| 725 | OnSoundEvent(wxSOUND_OUTPUT); |
| 726 | |
| 727 | if (evt & wxSOUND_INPUT) { |
| 728 | int i; |
| 729 | for (i=0;i<WXSOUND_MAX_QUEUE;i++) |
| 730 | AddToQueue(m_headers_rec[i]); |
| 731 | |
| 732 | waveInStart(m_internal->m_devin); |
| 733 | } |
| 734 | |
| 735 | return TRUE; |
| 736 | } |
| 737 | |
| 738 | // ------------------------------------------------------------------------- |
| 739 | // StopProduction() |
| 740 | // ------------------------------------------------------------------------ |
| 741 | bool wxSoundStreamWin::StopProduction() |
| 742 | { |
| 743 | if (!m_production_started) { |
| 744 | m_snderror = wxSOUND_NOTSTARTED; |
| 745 | return FALSE; |
| 746 | } |
| 747 | |
| 748 | m_snderror = wxSOUND_NOERROR; |
| 749 | m_production_started = FALSE; |
| 750 | CloseDevice(); |
| 751 | return TRUE; |
| 752 | } |
| 753 | |
| 754 | // ------------------------------------------------------------------------- |
| 755 | // QueueFilled() |
| 756 | // ------------------------------------------------------------------------- |
| 757 | bool wxSoundStreamWin::QueueFilled() const |
| 758 | { |
| 759 | return (!m_production_started || m_queue_filled); |
| 760 | } |
| 761 | |
| 762 | |
| 763 | // -------------------------------------------------------------------------- |
| 764 | // wxSoundWinModule |
| 765 | // -------------------------------------------------------------------------- |
| 766 | |
| 767 | class WXDLLEXPORT wxSoundWinModule : public wxModule { |
| 768 | DECLARE_DYNAMIC_CLASS(wxSoundWinModule) |
| 769 | public: |
| 770 | bool OnInit(); |
| 771 | void OnExit(); |
| 772 | }; |
| 773 | |
| 774 | IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule, wxModule) |
| 775 | |
| 776 | bool wxSoundWinModule::OnInit() { |
| 777 | wxSoundHandleList = new wxList(wxKEY_INTEGER); |
| 778 | return TRUE; |
| 779 | } |
| 780 | |
| 781 | void wxSoundWinModule::OnExit() { |
| 782 | delete wxSoundHandleList; |
| 783 | } |
| 784 | |
| 785 | #endif |
| 786 | // __WINDOWS__ |