1 // --------------------------------------------------------------------------
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
7 // --------------------------------------------------------------------------
11 #include <wx/module.h>
12 #include <wx/msw/private.h>
21 typedef struct _wxSoundInternal wxSoundInternal
;
22 typedef struct _wxSoundInfoHeader wxSoundInfoHeader
;
24 extern const wxChar
*wxCanvasClassName
;
26 wxList
*wxSoundHandleList
= NULL
;
28 static inline wxSoundStreamWin
*wxFindSoundFromHandle(WXHWND hWnd
)
30 wxNode
*node
= wxSoundHandleList
->Find((long)hWnd
);
33 return (wxSoundStreamWin
*)node
->Data();
36 struct _wxSoundInternal
{
40 bool m_output_enabled
, m_input_enabled
;
43 struct _wxSoundInfoHeader
{
44 HGLOBAL m_h_header
, m_h_data
;
48 bool m_playing
, m_recording
;
49 wxUint32 m_position
, m_size
;
51 wxSoundStreamWin
*m_driver
;
54 #define WXSOUND_MAX_QUEUE 10
56 wxSoundStreamWin::wxSoundStreamWin()
60 m_production_started
= FALSE
;
61 m_internal
= new wxSoundInternal
;
62 m_snderror
= wxSOUND_NOERR
;
68 m_internal
->m_input_enabled
= FALSE
;
69 m_internal
->m_output_enabled
= FALSE
;
71 m_waiting_for
= FALSE
;
73 if (!OpenDevice(wxSOUND_OUTPUT
))
79 wxSoundStreamWin::~wxSoundStreamWin()
81 if (m_production_started
)
88 LRESULT APIENTRY _EXPORT
_wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
89 WPARAM wParam
, LPARAM lParam
)
91 wxSoundStreamWin
*sndwin
;
93 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
99 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
102 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
110 // -----------------------------------------------------------------------
111 // CreateSndWindow() creates an hidden window which will receive the sound
113 // -----------------------------------------------------------------------
115 void wxSoundStreamWin::CreateSndWindow()
117 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
121 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
122 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
123 wxGetInstance(), NULL
);
125 error
= GetLastError();
127 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
129 // Add this window to the sound handle list so we'll be able to redecode
130 // the "magic" number.
131 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
134 // -----------------------------------------------------------------------
135 // DestroySndWindow() destroys the hidden window
136 // -----------------------------------------------------------------------
138 void wxSoundStreamWin::DestroySndWindow()
140 if (m_internal
->m_sndWin
) {
141 ::DestroyWindow(m_internal
->m_sndWin
);
142 wxSoundHandleList
->DeleteObject((wxObject
*)this);
146 // -------------------------------------------------------------------------
147 // OpenDevice(int mode) initializes the windows driver for a "mode"
148 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
149 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
150 // is set, then the driver is opened for input operation. The two modes
152 // The initialization parameters (sample rate, ...) are taken from the
153 // m_sndformat object.
154 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
156 // -------------------------------------------------------------------------
157 bool wxSoundStreamWin::OpenDevice(int mode
)
159 wxSoundFormatPcm
*pcm
;
160 WAVEFORMATEX wformat
;
163 m_snderror
= wxSOUND_INVFRMT
;
167 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
169 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
170 wformat
.nChannels
= pcm
->GetChannels();
171 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
172 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
173 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
174 wformat
.wBitsPerSample
= pcm
->GetBPS();
177 // -----------------------------------
178 // Open the driver for Output operation
179 // -----------------------------------
180 if (mode
& wxSOUND_OUTPUT
) {
183 result
= waveOutOpen(&m_internal
->m_devout
,
184 WAVE_MAPPER
, &wformat
,
185 (DWORD
)m_internal
->m_sndWin
, 0,
188 if (result
!= MMSYSERR_NOERROR
) {
189 m_snderror
= wxSOUND_INVDEV
;
193 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
194 m_current_frag_out
= 0;
196 m_internal
->m_output_enabled
= TRUE
;
198 // -----------------------------------
199 // Open the driver for Input operation
200 // -----------------------------------
201 if (mode
& wxSOUND_INPUT
) {
204 result
= waveInOpen(&m_internal
->m_devin
,
205 WAVE_MAPPER
, &wformat
,
206 (DWORD
)m_internal
->m_sndWin
, 0,
209 if (result
!= MMSYSERR_NOERROR
) {
210 m_snderror
= wxSOUND_INVDEV
;
214 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
217 m_internal
->m_input_enabled
= TRUE
;
220 if (mode
& wxSOUND_OUTPUT
) {
221 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
226 if (mode
& wxSOUND_INPUT
) {
227 if (!AllocHeaders(wxSOUND_INPUT
)) {
236 // -------------------------------------------------------------------------
237 // CloseDevice() closes the driver handles and frees memory allocated for
239 // -------------------------------------------------------------------------
240 void wxSoundStreamWin::CloseDevice()
242 if (m_internal
->m_output_enabled
) {
243 FreeHeaders(wxSOUND_OUTPUT
);
244 m_internal
->m_output_enabled
= FALSE
;
245 waveOutClose(m_internal
->m_devout
);
248 if (m_internal
->m_input_enabled
) {
249 FreeHeaders(wxSOUND_INPUT
);
250 m_internal
->m_input_enabled
= FALSE
;
251 waveInClose(m_internal
->m_devin
);
255 // -------------------------------------------------------------------------
256 // AllocHeader(int mode)
258 // mode has the same mean as in OpenDevice() except that here the two flags
259 // must be exclusive.
260 // AllocHeader() initializes an element of an operation (this can be input
261 // or output). It means it allocates the sound header's memory block
262 // and "prepares" it (It is needed by Windows). At the same time, it sets
263 // private datas so we can the header's owner (See callback).
265 // It returns the new allocated block or NULL.
266 // -------------------------------------------------------------------------
267 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
269 wxSoundInfoHeader
*info
;
272 // Some memory allocation
273 info
= new wxSoundInfoHeader
;
274 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
275 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
276 if (!info
->m_h_data
|| !info
->m_h_header
) {
278 m_snderror
= wxSOUND_MEMERR
;
282 // Get the two pointers from the system
283 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
284 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
285 // Set the header's mode
287 // Set the parent of the header
288 info
->m_driver
= this;
292 header
= info
->m_header
;
293 // Initialize Windows variables
294 header
->lpData
= info
->m_data
;
295 header
->dwBufferLength
= GetBestSize();
296 header
->dwUser
= (DWORD
)info
;
297 header
->dwFlags
= WHDR_DONE
;
299 // "Prepare" the header
300 if (mode
== wxSOUND_INPUT
) {
303 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
306 if (result
!= MMSYSERR_NOERROR
) {
307 // If something goes wrong, free everything.
308 GlobalUnlock(info
->m_data
);
309 GlobalUnlock(info
->m_header
);
310 GlobalFree(info
->m_h_data
);
311 GlobalFree(info
->m_h_header
);
314 m_snderror
= wxSOUND_IOERR
;
317 } else if (mode
== wxSOUND_OUTPUT
) {
320 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
323 if (result
!= MMSYSERR_NOERROR
) {
324 // If something goes wrong, free everything.
325 GlobalUnlock(info
->m_data
);
326 GlobalUnlock(info
->m_header
);
327 GlobalFree(info
->m_h_data
);
328 GlobalFree(info
->m_h_header
);
331 m_snderror
= wxSOUND_IOERR
;
338 // -------------------------------------------------------------------------
339 // AllocHeaders(int mode)
341 // "mode" has the same mean as for OpenDevice() except that the two flags must
343 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
344 // queue. It uses AllocHeader() for each element.
346 // Once it has allocated all blocks, it returns TRUE and if an error occured
348 // -------------------------------------------------------------------------
349 bool wxSoundStreamWin::AllocHeaders(int mode
)
352 wxSoundInfoHeader
**headers
;
354 if (mode
== wxSOUND_OUTPUT
)
355 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
357 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
359 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
361 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
362 headers
[i
] = AllocHeader(mode
);
371 // -------------------------------------------------------------------------
372 // FreeHeader(int mode)
374 // "mode" has the same mean as for OpenDevice() except that the two flags must
376 // FreeHeader() frees a memory block and "unprepares" it.
377 // -------------------------------------------------------------------------
378 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
380 if (mode
== wxSOUND_OUTPUT
)
381 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
383 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
385 GlobalUnlock(header
->m_data
);
386 GlobalUnlock(header
->m_header
);
387 GlobalFree(header
->m_h_header
);
388 GlobalFree(header
->m_h_data
);
392 // -------------------------------------------------------------------------
393 // FreeHeaders(int mode)
395 // "mode" has the same mean as for OpenDevice() except that the two flags must
397 // FreeHeaders() frees all an operation queue once it has checked that
398 // all buffers have been terminated.
399 // -------------------------------------------------------------------------
400 void wxSoundStreamWin::FreeHeaders(int mode
)
403 wxSoundInfoHeader
***headers
;
405 if (mode
== wxSOUND_OUTPUT
)
406 headers
= &m_headers_play
;
408 headers
= &m_headers_rec
;
410 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
412 // We wait for the end of the buffer
413 WaitFor((*headers
)[i
]);
414 // Then, we free the header
415 FreeHeader((*headers
)[i
], mode
);
422 // -------------------------------------------------------------------------
423 // WaitFor(wxSoundInfoHeader *info)
425 // "info" is one element of an IO queue
426 // WaitFor() checks whether the specified block has been terminated.
427 // If it hasn't been terminated, it waits for its termination.
429 // NB: if it's a partially filled buffer it adds it to the Windows queue
430 // -------------------------------------------------------------------------
431 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
433 // If the buffer is finished, we return immediately
434 if (!info
->m_playing
) {
436 // We begun filling it: we must send it to the Windows queue
437 if (info
->m_position
!= 0) {
438 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
447 m_waiting_for
= TRUE
;
448 // Else, we wait for its termination
449 while (info
->m_playing
|| info
->m_recording
)
451 m_waiting_for
= FALSE
;
454 // -------------------------------------------------------------------------
455 // AddToQueue(wxSoundInfoHeader *info)
457 // For "info", see WaitFor()
458 // AddToQueue() sends the IO queue element to the Windows queue.
460 // Warning: in the current implementation, it partially assume we send the
461 // element in the right order. This is true in that implementation but if
462 // you use it elsewhere, be careful: it may shuffle all your sound datas.
463 // -------------------------------------------------------------------------
464 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
468 if (info
->m_mode
== wxSOUND_INPUT
) {
469 // Increment the input fragment pointer
470 result
= waveInAddBuffer(m_internal
->m_devin
,
471 info
->m_header
, sizeof(WAVEHDR
));
472 if (result
== MMSYSERR_NOERROR
)
473 info
->m_recording
= TRUE
;
476 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
477 result
= waveOutWrite(m_internal
->m_devout
,
478 info
->m_header
, sizeof(WAVEHDR
));
479 if (result
== MMSYSERR_NOERROR
)
480 info
->m_playing
= TRUE
;
487 // -------------------------------------------------------------------------
488 // ClearHeader(wxSoundInfoHeader *info)
490 // ClearHeader() reinitializes the parameters of "info" to their default
492 // -------------------------------------------------------------------------
493 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
495 info
->m_playing
= FALSE
;
496 info
->m_recording
= FALSE
;
497 info
->m_position
= 0;
498 info
->m_size
= GetBestSize();
501 // -------------------------------------------------------------------------
502 // wxSoundInfoHeader *NextFragmentOutput()
504 // NextFragmentOutput() looks for a free output block. It will always
505 // return you a non-NULL pointer but it may waits for an empty buffer a long
507 // -------------------------------------------------------------------------
508 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
510 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
511 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
513 if (m_headers_play
[m_current_frag_out
]->m_playing
)
514 WaitFor(m_headers_play
[m_current_frag_out
]);
516 if (m_current_frag_out
== m_output_frag_out
)
517 m_queue_filled
= TRUE
;
518 return m_headers_play
[m_current_frag_out
];
521 // -------------------------------------------------------------------------
522 // The behaviour of Write is documented in the global documentation.
523 // -------------------------------------------------------------------------
524 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
527 if (!m_internal
->m_output_enabled
)
531 wxSoundInfoHeader
*header
;
534 // Get a new output fragment
535 header
= NextFragmentOutput();
537 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
538 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
540 header
->m_position
+= to_copy
;
541 header
->m_size
-= to_copy
;
542 buffer
= (((const char *)buffer
) + to_copy
);
544 m_lastcount
+= to_copy
;
546 // If the fragment is full, we send it to the Windows queue.
547 if (header
->m_size
== 0)
548 if (!AddToQueue(header
)) {
549 m_snderror
= wxSOUND_IOERR
;
556 // -------------------------------------------------------------------------
557 // NextFragmentInput is not functional.
558 // -------------------------------------------------------------------------
559 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
561 wxSoundInfoHeader
*header
;
563 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
565 header
= m_headers_rec
[m_current_frag_in
];
566 if (header
->m_recording
)
569 if (m_current_frag_in
== m_input_frag_in
)
570 m_queue_filled
= TRUE
;
575 // -------------------------------------------------------------------------
576 // The behaviour of Read is documented in the global documentation.
577 // -------------------------------------------------------------------------
578 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
580 wxSoundInfoHeader
*header
;
584 if (!m_internal
->m_input_enabled
)
588 header
= NextFragmentInput();
590 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
591 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
593 header
->m_position
+= to_copy
;
594 header
->m_size
-= to_copy
;
595 buffer
= (((char *)buffer
) + to_copy
);
597 m_lastcount
+= to_copy
;
599 if (header
->m_size
== 0) {
601 if (!AddToQueue(header
)) {
602 m_snderror
= wxSOUND_IOERR
;
610 // -------------------------------------------------------------------------
611 // NotifyDoneBuffer(wxUint32 dev_handle)
613 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
614 // fragment finished. It reinitializes the parameters of the fragment and
615 // sends an event to the clients.
616 // -------------------------------------------------------------------------
617 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
619 wxSoundInfoHeader
*info
;
621 if (flag
== wxSOUND_OUTPUT
) {
622 if (!m_internal
->m_output_enabled
)
625 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
626 info
= m_headers_play
[m_output_frag_out
];
628 m_queue_filled
= FALSE
;
630 OnSoundEvent(wxSOUND_OUTPUT
);
632 if (!m_internal
->m_input_enabled
)
635 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
636 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
638 OnSoundEvent(wxSOUND_INPUT
);
639 m_queue_filled
= FALSE
;
643 // -------------------------------------------------------------------------
644 // -------------------------------------------------------------------------
645 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
647 return wxSoundStream::SetSoundFormat(base
);
650 // -------------------------------------------------------------------------
651 // -------------------------------------------------------------------------
652 bool wxSoundStreamWin::StartProduction(int evt
)
654 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
655 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
658 if (!OpenDevice(evt
))
661 m_production_started
= TRUE
;
662 m_queue_filled
= FALSE
;
663 // Send a dummy event to start.
664 if (evt
& wxSOUND_OUTPUT
)
665 OnSoundEvent(wxSOUND_OUTPUT
);
667 if (evt
& wxSOUND_INPUT
) {
669 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
670 AddToQueue(m_headers_rec
[i
]);
672 waveInStart(m_internal
->m_devin
);
678 // -------------------------------------------------------------------------
679 // -------------------------------------------------------------------------
680 bool wxSoundStreamWin::StopProduction()
682 m_production_started
= FALSE
;
687 // -------------------------------------------------------------------------
688 // -------------------------------------------------------------------------
689 bool wxSoundStreamWin::QueueFilled() const
691 return (!m_production_started
|| m_queue_filled
);
695 // --------------------------------------------------------------------------
697 // --------------------------------------------------------------------------
699 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
700 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
706 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
708 bool wxSoundWinModule::OnInit() {
709 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
713 void wxSoundWinModule::OnExit() {
714 delete wxSoundHandleList
;