1 // --------------------------------------------------------------------------
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000
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
;
63 m_snderror
= wxSOUND_MEMERROR
;
67 m_snderror
= wxSOUND_NOERROR
;
73 m_internal
->m_input_enabled
= FALSE
;
74 m_internal
->m_output_enabled
= FALSE
;
76 m_waiting_for
= FALSE
;
78 if (!OpenDevice(wxSOUND_OUTPUT
))
84 wxSoundStreamWin::~wxSoundStreamWin()
87 if (m_production_started
)
95 // -----------------------------------------------------------------------
96 // _wxSoundHandlerWndProc: Window callback to handle buffer completion
97 // -----------------------------------------------------------------------
98 LRESULT APIENTRY _EXPORT
_wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
99 WPARAM wParam
, LPARAM lParam
)
101 wxSoundStreamWin
*sndwin
;
103 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
109 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
112 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
120 // -----------------------------------------------------------------------
121 // CreateSndWindow() creates an hidden window which will receive the sound
123 // -----------------------------------------------------------------------
125 void wxSoundStreamWin::CreateSndWindow()
127 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
131 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
132 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
133 wxGetInstance(), NULL
);
135 error
= GetLastError();
137 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
139 // Add this window to the sound handle list so we'll be able to redecode
140 // the "magic" number.
141 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
144 // -----------------------------------------------------------------------
145 // DestroySndWindow() destroys the hidden window
146 // -----------------------------------------------------------------------
148 void wxSoundStreamWin::DestroySndWindow()
150 if (m_internal
->m_sndWin
) {
151 ::DestroyWindow(m_internal
->m_sndWin
);
152 wxSoundHandleList
->DeleteObject((wxObject
*)this);
156 // -------------------------------------------------------------------------
157 // OpenDevice(int mode) initializes the windows driver for a "mode"
158 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
159 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
160 // is set, then the driver is opened for input operation. The two modes
162 // The initialization parameters (sample rate, ...) are taken from the
163 // m_sndformat object.
164 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
166 // -------------------------------------------------------------------------
167 bool wxSoundStreamWin::OpenDevice(int mode
)
169 wxSoundFormatPcm
*pcm
;
170 WAVEFORMATEX wformat
;
173 m_snderror
= wxSOUND_INVFRMT
;
177 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
179 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
180 wformat
.nChannels
= pcm
->GetChannels();
181 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
182 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
183 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
184 wformat
.wBitsPerSample
= pcm
->GetBPS();
187 // -----------------------------------
188 // Open the driver for Output operation
189 // -----------------------------------
190 if (mode
& wxSOUND_OUTPUT
) {
193 result
= waveOutOpen(&m_internal
->m_devout
,
194 WAVE_MAPPER
, &wformat
,
195 (DWORD
)m_internal
->m_sndWin
, 0,
198 if (result
!= MMSYSERR_NOERROR
) {
199 m_snderror
= wxSOUND_INVDEV
;
203 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
204 m_current_frag_out
= 0;
206 m_internal
->m_output_enabled
= TRUE
;
208 // -----------------------------------
209 // Open the driver for Input operation
210 // -----------------------------------
211 if (mode
& wxSOUND_INPUT
) {
214 result
= waveInOpen(&m_internal
->m_devin
,
215 WAVE_MAPPER
, &wformat
,
216 (DWORD
)m_internal
->m_sndWin
, 0,
219 if (result
!= MMSYSERR_NOERROR
) {
220 m_snderror
= wxSOUND_INVDEV
;
224 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
227 m_internal
->m_input_enabled
= TRUE
;
230 if (mode
& wxSOUND_OUTPUT
) {
231 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
236 if (mode
& wxSOUND_INPUT
) {
237 if (!AllocHeaders(wxSOUND_INPUT
)) {
246 // -------------------------------------------------------------------------
247 // CloseDevice() closes the driver handles and frees memory allocated for
249 // -------------------------------------------------------------------------
250 void wxSoundStreamWin::CloseDevice()
252 if (m_internal
->m_output_enabled
) {
253 FreeHeaders(wxSOUND_OUTPUT
);
254 m_internal
->m_output_enabled
= FALSE
;
255 waveOutClose(m_internal
->m_devout
);
258 if (m_internal
->m_input_enabled
) {
259 FreeHeaders(wxSOUND_INPUT
);
260 m_internal
->m_input_enabled
= FALSE
;
261 waveInClose(m_internal
->m_devin
);
265 // -------------------------------------------------------------------------
266 // AllocHeader(int mode)
268 // mode has the same mean as in OpenDevice() except that here the two flags
269 // must be exclusive.
270 // AllocHeader() initializes an element of an operation (this can be input
271 // or output). It means it allocates the sound header's memory block
272 // and "prepares" it (It is needed by Windows). At the same time, it sets
273 // private datas so we can the header's owner (See callback).
275 // It returns the new allocated block or NULL.
276 // -------------------------------------------------------------------------
277 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
279 wxSoundInfoHeader
*info
;
282 // Some memory allocation
283 info
= new wxSoundInfoHeader
;
284 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
285 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
286 if (!info
->m_h_data
|| !info
->m_h_header
) {
288 m_snderror
= wxSOUND_MEMERR
;
292 // Get the two pointers from the system
293 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
294 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
295 // Set the header's mode
297 // Set the parent of the header
298 info
->m_driver
= this;
302 header
= info
->m_header
;
303 // Initialize Windows variables
304 header
->lpData
= info
->m_data
;
305 header
->dwBufferLength
= GetBestSize();
306 header
->dwUser
= (DWORD
)info
;
307 header
->dwFlags
= WHDR_DONE
;
309 // "Prepare" the header
310 if (mode
== wxSOUND_INPUT
) {
313 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
316 if (result
!= MMSYSERR_NOERROR
) {
317 // If something goes wrong, free everything.
318 GlobalUnlock(info
->m_data
);
319 GlobalUnlock(info
->m_header
);
320 GlobalFree(info
->m_h_data
);
321 GlobalFree(info
->m_h_header
);
324 m_snderror
= wxSOUND_IOERROR
;
327 } else if (mode
== wxSOUND_OUTPUT
) {
330 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
333 if (result
!= MMSYSERR_NOERROR
) {
334 // If something goes wrong, free everything.
335 GlobalUnlock(info
->m_data
);
336 GlobalUnlock(info
->m_header
);
337 GlobalFree(info
->m_h_data
);
338 GlobalFree(info
->m_h_header
);
341 m_snderror
= wxSOUND_IOERROR
;
348 // -------------------------------------------------------------------------
349 // AllocHeaders(int mode)
351 // "mode" has the same mean as for OpenDevice() except that the two flags must
353 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
354 // queue. It uses AllocHeader() for each element.
356 // Once it has allocated all blocks, it returns TRUE and if an error occured
358 // -------------------------------------------------------------------------
359 bool wxSoundStreamWin::AllocHeaders(int mode
)
362 wxSoundInfoHeader
**headers
;
364 if (mode
== wxSOUND_OUTPUT
)
365 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
367 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
369 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
371 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
372 headers
[i
] = AllocHeader(mode
);
381 // -------------------------------------------------------------------------
382 // FreeHeader(int mode)
384 // "mode" has the same mean as for OpenDevice() except that the two flags must
386 // FreeHeader() frees a memory block and "unprepares" it.
387 // -------------------------------------------------------------------------
388 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
390 if (mode
== wxSOUND_OUTPUT
)
391 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
393 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
395 GlobalUnlock(header
->m_data
);
396 GlobalUnlock(header
->m_header
);
397 GlobalFree(header
->m_h_header
);
398 GlobalFree(header
->m_h_data
);
402 // -------------------------------------------------------------------------
403 // FreeHeaders(int mode)
405 // "mode" has the same mean as for OpenDevice() except that the two flags must
407 // FreeHeaders() frees all an operation queue once it has checked that
408 // all buffers have been terminated.
409 // -------------------------------------------------------------------------
410 void wxSoundStreamWin::FreeHeaders(int mode
)
413 wxSoundInfoHeader
***headers
;
415 if (mode
== wxSOUND_OUTPUT
)
416 headers
= &m_headers_play
;
418 headers
= &m_headers_rec
;
420 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
422 // We wait for the end of the buffer
423 WaitFor((*headers
)[i
]);
424 // Then, we free the header
425 FreeHeader((*headers
)[i
], mode
);
432 // -------------------------------------------------------------------------
433 // WaitFor(wxSoundInfoHeader *info)
435 // "info" is one element of an IO queue
436 // WaitFor() checks whether the specified block has been terminated.
437 // If it hasn't been terminated, it waits for its termination.
439 // NB: if it's a partially filled buffer it adds it to the Windows queue
440 // -------------------------------------------------------------------------
441 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
443 // If the buffer is finished, we return immediately
444 if (!info
->m_playing
) {
446 // We begun filling it: we must send it to the Windows queue
447 if (info
->m_position
!= 0) {
448 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
457 m_waiting_for
= TRUE
;
458 // Else, we wait for its termination
459 while (info
->m_playing
|| info
->m_recording
)
461 m_waiting_for
= FALSE
;
464 // -------------------------------------------------------------------------
465 // AddToQueue(wxSoundInfoHeader *info)
467 // For "info", see WaitFor()
468 // AddToQueue() sends the IO queue element to the Windows queue.
470 // Warning: in the current implementation, it partially assume we send the
471 // element in the right order. This is true in that implementation but if
472 // you use it elsewhere, be careful: it may shuffle all your sound datas.
473 // -------------------------------------------------------------------------
474 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
478 if (info
->m_mode
== wxSOUND_INPUT
) {
479 // Increment the input fragment pointer
480 result
= waveInAddBuffer(m_internal
->m_devin
,
481 info
->m_header
, sizeof(WAVEHDR
));
482 if (result
== MMSYSERR_NOERROR
)
483 info
->m_recording
= TRUE
;
486 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
487 result
= waveOutWrite(m_internal
->m_devout
,
488 info
->m_header
, sizeof(WAVEHDR
));
489 if (result
== MMSYSERR_NOERROR
)
490 info
->m_playing
= TRUE
;
497 // -------------------------------------------------------------------------
498 // ClearHeader(wxSoundInfoHeader *info)
500 // ClearHeader() reinitializes the parameters of "info" to their default
502 // -------------------------------------------------------------------------
503 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
505 info
->m_playing
= FALSE
;
506 info
->m_recording
= FALSE
;
507 info
->m_position
= 0;
508 info
->m_size
= GetBestSize();
511 // -------------------------------------------------------------------------
512 // wxSoundInfoHeader *NextFragmentOutput()
514 // NextFragmentOutput() looks for a free output block. It will always
515 // return you a non-NULL pointer but it may waits for an empty buffer a long
517 // -------------------------------------------------------------------------
518 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
520 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
521 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
523 if (m_headers_play
[m_current_frag_out
]->m_playing
)
524 WaitFor(m_headers_play
[m_current_frag_out
]);
526 if (m_current_frag_out
== m_output_frag_out
)
527 m_queue_filled
= TRUE
;
528 return m_headers_play
[m_current_frag_out
];
531 // -------------------------------------------------------------------------
532 // The behaviour of Write is documented in the global documentation.
533 // -------------------------------------------------------------------------
534 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
537 if (!m_internal
->m_output_enabled
) {
538 m_snderror
= wxSOUND_NOTSTARTED
;
544 wxSoundInfoHeader
*header
;
547 // Get a new output fragment
548 header
= NextFragmentOutput();
550 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
551 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
553 header
->m_position
+= to_copy
;
554 header
->m_size
-= to_copy
;
555 buffer
= (((const char *)buffer
) + to_copy
);
557 m_lastcount
+= to_copy
;
559 // If the fragment is full, we send it to the Windows queue.
560 if (header
->m_size
== 0)
561 if (!AddToQueue(header
)) {
562 m_snderror
= wxSOUND_IOERROR
;
569 // -------------------------------------------------------------------------
570 // NextFragmentInput is not functional.
571 // -------------------------------------------------------------------------
572 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
574 wxSoundInfoHeader
*header
;
576 // Queue pointer: reader
577 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
579 header
= m_headers_rec
[m_current_frag_in
];
580 // If the current buffer is in recording mode, we must wait for its
582 if (header
->m_recording
)
585 // We reached the writer position: the queue is full.
586 if (m_current_frag_in
== m_input_frag_in
)
587 m_queue_filled
= TRUE
;
592 // -------------------------------------------------------------------------
593 // The behaviour of Read is documented in the global documentation.
594 // -------------------------------------------------------------------------
595 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
597 wxSoundInfoHeader
*header
;
601 if (!m_internal
->m_input_enabled
)
605 header
= NextFragmentInput();
607 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
608 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
610 header
->m_position
+= to_copy
;
611 header
->m_size
-= to_copy
;
612 buffer
= (((char *)buffer
) + to_copy
);
614 m_lastcount
+= to_copy
;
616 if (header
->m_size
== 0) {
618 if (!AddToQueue(header
)) {
619 m_snderror
= wxSOUND_IOERROR
;
627 // -------------------------------------------------------------------------
628 // NotifyDoneBuffer(wxUint32 dev_handle)
630 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
631 // fragment finished. It reinitializes the parameters of the fragment and
632 // sends an event to the clients.
633 // -------------------------------------------------------------------------
634 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
636 wxSoundInfoHeader
*info
;
638 if (flag
== wxSOUND_OUTPUT
) {
639 if (!m_internal
->m_output_enabled
)
642 // Queue pointer: reader
643 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
644 info
= m_headers_play
[m_output_frag_out
];
645 // Clear header to tell the system the buffer is free now
647 m_queue_filled
= FALSE
;
649 // Try to requeue a new buffer.
650 OnSoundEvent(wxSOUND_OUTPUT
);
652 if (!m_internal
->m_input_enabled
)
655 // Recording completed
656 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
657 // Queue pointer: writer
658 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
660 OnSoundEvent(wxSOUND_INPUT
);
661 m_queue_filled
= FALSE
;
665 // -------------------------------------------------------------------------
667 // -------------------------------------------------------------------------
668 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
670 // TODO: detect best format
671 return wxSoundStream::SetSoundFormat(base
);
674 // -------------------------------------------------------------------------
676 // -------------------------------------------------------------------------
677 bool wxSoundStreamWin::StartProduction(int evt
)
682 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
683 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
686 if (!OpenDevice(evt
))
689 m_production_started
= TRUE
;
690 m_queue_filled
= FALSE
;
691 // Send a dummy event to start.
692 if (evt
& wxSOUND_OUTPUT
)
693 OnSoundEvent(wxSOUND_OUTPUT
);
695 if (evt
& wxSOUND_INPUT
) {
697 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
698 AddToQueue(m_headers_rec
[i
]);
700 waveInStart(m_internal
->m_devin
);
706 // -------------------------------------------------------------------------
708 // ------------------------------------------------------------------------
709 bool wxSoundStreamWin::StopProduction()
711 if (!m_production_started
) {
712 m_snderror
= wxSOUND_NOTSTARTED
;
716 m_snderror
= wxSOUND_NOERROR
;
717 m_production_started
= FALSE
;
722 // -------------------------------------------------------------------------
724 // -------------------------------------------------------------------------
725 bool wxSoundStreamWin::QueueFilled() const
727 return (!m_production_started
|| m_queue_filled
);
731 // --------------------------------------------------------------------------
733 // --------------------------------------------------------------------------
735 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
736 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
742 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
744 bool wxSoundWinModule::OnInit() {
745 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
749 void wxSoundWinModule::OnExit() {
750 delete wxSoundHandleList
;