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 char 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 if (!OpenDevice(wxSOUND_OUTPUT
))
77 wxSoundStreamWin::~wxSoundStreamWin()
79 if (m_production_started
)
86 LRESULT APIENTRY _EXPORT
_wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
87 WPARAM wParam
, LPARAM lParam
)
89 wxSoundStreamWin
*sndwin
;
91 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
97 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
100 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
108 // -----------------------------------------------------------------------
109 // CreateSndWindow() creates an hidden window which will receive the sound
111 // -----------------------------------------------------------------------
113 void wxSoundStreamWin::CreateSndWindow()
115 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
119 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
120 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
121 wxGetInstance(), NULL
);
123 error
= GetLastError();
125 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
127 // Add this window to the sound handle list so we'll be able to redecode
128 // the "magic" number.
129 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
132 // -----------------------------------------------------------------------
133 // DestroySndWindow() destroys the hidden window
134 // -----------------------------------------------------------------------
136 void wxSoundStreamWin::DestroySndWindow()
138 if (m_internal
->m_sndWin
) {
139 ::DestroyWindow(m_internal
->m_sndWin
);
140 wxSoundHandleList
->DeleteObject((wxObject
*)this);
144 // -------------------------------------------------------------------------
145 // OpenDevice(int mode) initializes the windows driver for a "mode"
146 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
147 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
148 // is set, then the driver is opened for input operation. The two modes
150 // The initialization parameters (sample rate, ...) are taken from the
151 // m_sndformat object.
152 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
154 // -------------------------------------------------------------------------
155 bool wxSoundStreamWin::OpenDevice(int mode
)
157 wxSoundFormatPcm
*pcm
;
158 WAVEFORMATEX wformat
;
161 m_snderror
= wxSOUND_INVFRMT
;
165 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
167 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
168 wformat
.nChannels
= pcm
->GetChannels();
169 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
170 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
171 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
172 wformat
.wBitsPerSample
= pcm
->GetBPS();
175 // -----------------------------------
176 // Open the driver for Output operation
177 // -----------------------------------
178 if (mode
& wxSOUND_OUTPUT
) {
181 result
= waveOutOpen(&m_internal
->m_devout
,
182 WAVE_MAPPER
, &wformat
,
183 (DWORD
)m_internal
->m_sndWin
, 0,
186 if (result
!= MMSYSERR_NOERROR
) {
187 m_snderror
= wxSOUND_INVDEV
;
191 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
192 m_current_frag_out
= 0;
194 m_internal
->m_output_enabled
= TRUE
;
196 // -----------------------------------
197 // Open the driver for Input operation
198 // -----------------------------------
199 if (mode
& wxSOUND_INPUT
) {
202 result
= waveInOpen(&m_internal
->m_devin
,
203 WAVE_MAPPER
, &wformat
,
204 (DWORD
)m_internal
->m_sndWin
, 0,
207 if (result
!= MMSYSERR_NOERROR
) {
208 m_snderror
= wxSOUND_INVDEV
;
212 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
215 m_internal
->m_input_enabled
= TRUE
;
218 if (mode
& wxSOUND_OUTPUT
) {
219 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
224 if (mode
& wxSOUND_INPUT
) {
225 if (!AllocHeaders(wxSOUND_INPUT
)) {
234 // -------------------------------------------------------------------------
235 // CloseDevice() closes the driver handles and frees memory allocated for
237 // -------------------------------------------------------------------------
238 void wxSoundStreamWin::CloseDevice()
240 if (m_internal
->m_output_enabled
) {
241 m_internal
->m_output_enabled
= FALSE
;
242 waveOutReset(m_internal
->m_devout
);
243 FreeHeaders(wxSOUND_OUTPUT
);
244 waveOutClose(m_internal
->m_devout
);
247 if (m_internal
->m_input_enabled
) {
248 m_internal
->m_input_enabled
= FALSE
;
249 waveInReset(m_internal
->m_devin
);
250 FreeHeaders(wxSOUND_INPUT
);
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
;
300 // "Prepare" the header
301 if (mode
== wxSOUND_INPUT
) {
304 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
307 if (result
!= MMSYSERR_NOERROR
) {
308 // If something goes wrong, free everything.
309 GlobalUnlock(info
->m_data
);
310 GlobalUnlock(info
->m_header
);
311 GlobalFree(info
->m_h_data
);
312 GlobalFree(info
->m_h_header
);
315 m_snderror
= wxSOUND_IOERR
;
318 } else if (mode
== wxSOUND_OUTPUT
) {
321 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
324 if (result
!= MMSYSERR_NOERROR
) {
325 // If something goes wrong, free everything.
326 GlobalUnlock(info
->m_data
);
327 GlobalUnlock(info
->m_header
);
328 GlobalFree(info
->m_h_data
);
329 GlobalFree(info
->m_h_header
);
332 m_snderror
= wxSOUND_IOERR
;
339 // -------------------------------------------------------------------------
340 // AllocHeaders(int mode)
342 // "mode" has the same mean as for OpenDevice() except that the two flags must
344 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
345 // queue. It uses AllocHeader() for each element.
347 // Once it has allocated all blocks, it returns TRUE and if an error occured
349 // -------------------------------------------------------------------------
350 bool wxSoundStreamWin::AllocHeaders(int mode
)
353 wxSoundInfoHeader
**headers
;
355 if (mode
== wxSOUND_OUTPUT
)
356 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
358 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
360 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
362 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
363 headers
[i
] = AllocHeader(mode
);
372 // -------------------------------------------------------------------------
373 // FreeHeader(int mode)
375 // "mode" has the same mean as for OpenDevice() except that the two flags must
377 // FreeHeader() frees a memory block and "unprepares" it.
378 // -------------------------------------------------------------------------
379 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
381 if (mode
== wxSOUND_OUTPUT
)
382 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
384 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
386 GlobalUnlock(header
->m_data
);
387 GlobalUnlock(header
->m_header
);
388 GlobalFree(header
->m_h_header
);
389 GlobalFree(header
->m_h_data
);
393 // -------------------------------------------------------------------------
394 // FreeHeaders(int mode)
396 // "mode" has the same mean as for OpenDevice() except that the two flags must
398 // FreeHeaders() frees all an operation queue once it has checked that
399 // all buffers have been terminated.
400 // -------------------------------------------------------------------------
401 void wxSoundStreamWin::FreeHeaders(int mode
)
404 wxSoundInfoHeader
***headers
;
406 if (mode
== wxSOUND_OUTPUT
)
407 headers
= &m_headers_play
;
409 headers
= &m_headers_rec
;
411 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
413 // We wait for the end of the buffer
414 WaitFor((*headers
)[i
]);
415 // Then, we free the header
416 FreeHeader((*headers
)[i
], mode
);
423 // -------------------------------------------------------------------------
424 // WaitFor(wxSoundInfoHeader *info)
426 // "info" is one element of an IO queue
427 // WaitFor() checks whether the specified block has been terminated.
428 // If it hasn't been terminated, it waits for its termination.
430 // NB: if it's a partially filled buffer it adds it to the Windows queue
431 // -------------------------------------------------------------------------
432 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
434 // If the buffer is finished, we return immediately
435 if (!info
->m_playing
) {
437 // We begun filling it: we must send it to the Windows queue
438 if (info
->m_position
!= 0) {
439 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
446 // Else, we wait for its termination
447 while (info
->m_playing
|| info
->m_recording
)
451 // -------------------------------------------------------------------------
452 // AddToQueue(wxSoundInfoHeader *info)
454 // For "info", see WaitFor()
455 // AddToQueue() sends the IO queue element to the Windows queue.
457 // Warning: in the current implementation, it partially assume we send the
458 // element in the right order. This is true in that implementation but if
459 // you use it elsewhere, be careful: it may shuffle all your sound datas.
460 // -------------------------------------------------------------------------
461 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
465 if (info
->m_mode
== wxSOUND_INPUT
) {
466 // Increment the input fragment pointer
467 result
= waveInAddBuffer(m_internal
->m_devin
,
468 info
->m_header
, sizeof(WAVEHDR
));
469 if (result
== MMSYSERR_NOERROR
)
470 info
->m_recording
= TRUE
;
473 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
474 result
= waveOutWrite(m_internal
->m_devout
,
475 info
->m_header
, sizeof(WAVEHDR
));
476 if (result
== MMSYSERR_NOERROR
)
477 info
->m_playing
= TRUE
;
484 // -------------------------------------------------------------------------
485 // ClearHeader(wxSoundInfoHeader *info)
487 // ClearHeader() reinitializes the parameters of "info" to their default
489 // -------------------------------------------------------------------------
490 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
492 info
->m_playing
= FALSE
;
493 info
->m_recording
= FALSE
;
494 info
->m_position
= 0;
495 info
->m_size
= GetBestSize();
498 // -------------------------------------------------------------------------
499 // wxSoundInfoHeader *NextFragmentOutput()
501 // NextFragmentOutput() looks for a free output block. It will always
502 // return you a non-NULL pointer but it may waits for an empty buffer a long
504 // -------------------------------------------------------------------------
505 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
507 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
508 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
510 if (m_headers_play
[m_current_frag_out
]->m_playing
)
511 WaitFor(m_headers_play
[m_current_frag_out
]);
513 if (m_current_frag_out
== m_output_frag_out
)
514 m_queue_filled
= TRUE
;
515 return m_headers_play
[m_current_frag_out
];
518 // -------------------------------------------------------------------------
519 // The behaviour of Write is documented in the global documentation.
520 // -------------------------------------------------------------------------
521 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
524 if (!m_internal
->m_output_enabled
)
528 wxSoundInfoHeader
*header
;
531 // Get a new output fragment
532 header
= NextFragmentOutput();
534 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
535 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
537 header
->m_position
+= to_copy
;
538 header
->m_size
-= to_copy
;
539 buffer
= (((const char *)buffer
) + to_copy
);
541 m_lastcount
+= to_copy
;
543 // If the fragment is full, we send it to the Windows queue.
544 if (header
->m_size
== 0)
545 if (!AddToQueue(header
)) {
546 m_snderror
= wxSOUND_IOERR
;
553 // -------------------------------------------------------------------------
554 // NextFragmentInput is not functional.
555 // -------------------------------------------------------------------------
556 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
558 wxSoundInfoHeader
*header
;
560 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
562 header
= m_headers_rec
[m_current_frag_in
];
563 if (header
->m_recording
)
566 if (m_current_frag_in
== m_input_frag_in
)
567 m_queue_filled
= TRUE
;
572 // -------------------------------------------------------------------------
573 // The behaviour of Read is documented in the global documentation.
574 // -------------------------------------------------------------------------
575 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
577 wxSoundInfoHeader
*header
;
581 if (!m_internal
->m_input_enabled
)
585 header
= NextFragmentInput();
587 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
588 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
590 header
->m_position
+= to_copy
;
591 header
->m_size
-= to_copy
;
592 buffer
= (((char *)buffer
) + to_copy
);
594 m_lastcount
+= to_copy
;
596 if (header
->m_size
== 0) {
598 if (!AddToQueue(header
)) {
599 m_snderror
= wxSOUND_IOERR
;
607 // -------------------------------------------------------------------------
608 // NotifyDoneBuffer(wxUint32 dev_handle)
610 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
611 // fragment finished. It reinitializes the parameters of the fragment and
612 // sends an event to the clients.
613 // -------------------------------------------------------------------------
614 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
616 wxSoundInfoHeader
*info
;
618 if (flag
== wxSOUND_OUTPUT
) {
619 if (!m_internal
->m_output_enabled
)
622 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
623 info
= m_headers_play
[m_output_frag_out
];
625 m_queue_filled
= FALSE
;
626 OnSoundEvent(wxSOUND_OUTPUT
);
628 if (!m_internal
->m_input_enabled
)
631 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
632 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
633 OnSoundEvent(wxSOUND_INPUT
);
634 m_queue_filled
= FALSE
;
638 // -------------------------------------------------------------------------
639 // -------------------------------------------------------------------------
640 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
642 return wxSoundStream::SetSoundFormat(base
);
645 // -------------------------------------------------------------------------
646 // -------------------------------------------------------------------------
647 bool wxSoundStreamWin::StartProduction(int evt
)
649 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
650 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
653 if (!OpenDevice(evt
))
656 m_production_started
= TRUE
;
657 m_queue_filled
= FALSE
;
658 // Send a dummy event to start.
659 if (evt
& wxSOUND_OUTPUT
)
662 if (evt
& wxSOUND_INPUT
) {
664 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
665 AddToQueue(m_headers_rec
[i
]);
667 waveInStart(m_internal
->m_devin
);
673 // -------------------------------------------------------------------------
674 // -------------------------------------------------------------------------
675 bool wxSoundStreamWin::StopProduction()
677 if (m_internal
->m_input_enabled
)
678 waveInStop(m_internal
->m_devin
);
680 m_production_started
= FALSE
;
685 // -------------------------------------------------------------------------
686 // -------------------------------------------------------------------------
687 bool wxSoundStreamWin::QueueFilled() const
689 return (!m_production_started
|| m_queue_filled
);
693 // --------------------------------------------------------------------------
695 // --------------------------------------------------------------------------
697 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
698 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
704 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
706 bool wxSoundWinModule::OnInit() {
707 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
711 void wxSoundWinModule::OnExit() {
712 delete wxSoundHandleList
;