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_MEMERR
;
67 m_snderror
= wxSOUND_NOERR
;
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 LRESULT APIENTRY _EXPORT
_wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
96 WPARAM wParam
, LPARAM lParam
)
98 wxSoundStreamWin
*sndwin
;
100 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
106 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
109 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
117 // -----------------------------------------------------------------------
118 // CreateSndWindow() creates an hidden window which will receive the sound
120 // -----------------------------------------------------------------------
122 void wxSoundStreamWin::CreateSndWindow()
124 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
128 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
129 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
130 wxGetInstance(), NULL
);
132 error
= GetLastError();
134 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
136 // Add this window to the sound handle list so we'll be able to redecode
137 // the "magic" number.
138 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
141 // -----------------------------------------------------------------------
142 // DestroySndWindow() destroys the hidden window
143 // -----------------------------------------------------------------------
145 void wxSoundStreamWin::DestroySndWindow()
147 if (m_internal
->m_sndWin
) {
148 ::DestroyWindow(m_internal
->m_sndWin
);
149 wxSoundHandleList
->DeleteObject((wxObject
*)this);
153 // -------------------------------------------------------------------------
154 // OpenDevice(int mode) initializes the windows driver for a "mode"
155 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
156 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
157 // is set, then the driver is opened for input operation. The two modes
159 // The initialization parameters (sample rate, ...) are taken from the
160 // m_sndformat object.
161 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
163 // -------------------------------------------------------------------------
164 bool wxSoundStreamWin::OpenDevice(int mode
)
166 wxSoundFormatPcm
*pcm
;
167 WAVEFORMATEX wformat
;
170 m_snderror
= wxSOUND_INVFRMT
;
174 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
176 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
177 wformat
.nChannels
= pcm
->GetChannels();
178 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
179 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
180 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
181 wformat
.wBitsPerSample
= pcm
->GetBPS();
184 // -----------------------------------
185 // Open the driver for Output operation
186 // -----------------------------------
187 if (mode
& wxSOUND_OUTPUT
) {
190 result
= waveOutOpen(&m_internal
->m_devout
,
191 WAVE_MAPPER
, &wformat
,
192 (DWORD
)m_internal
->m_sndWin
, 0,
195 if (result
!= MMSYSERR_NOERROR
) {
196 m_snderror
= wxSOUND_INVDEV
;
200 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
201 m_current_frag_out
= 0;
203 m_internal
->m_output_enabled
= TRUE
;
205 // -----------------------------------
206 // Open the driver for Input operation
207 // -----------------------------------
208 if (mode
& wxSOUND_INPUT
) {
211 result
= waveInOpen(&m_internal
->m_devin
,
212 WAVE_MAPPER
, &wformat
,
213 (DWORD
)m_internal
->m_sndWin
, 0,
216 if (result
!= MMSYSERR_NOERROR
) {
217 m_snderror
= wxSOUND_INVDEV
;
221 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
224 m_internal
->m_input_enabled
= TRUE
;
227 if (mode
& wxSOUND_OUTPUT
) {
228 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
233 if (mode
& wxSOUND_INPUT
) {
234 if (!AllocHeaders(wxSOUND_INPUT
)) {
243 // -------------------------------------------------------------------------
244 // CloseDevice() closes the driver handles and frees memory allocated for
246 // -------------------------------------------------------------------------
247 void wxSoundStreamWin::CloseDevice()
249 if (m_internal
->m_output_enabled
) {
250 FreeHeaders(wxSOUND_OUTPUT
);
251 m_internal
->m_output_enabled
= FALSE
;
252 waveOutClose(m_internal
->m_devout
);
255 if (m_internal
->m_input_enabled
) {
256 FreeHeaders(wxSOUND_INPUT
);
257 m_internal
->m_input_enabled
= FALSE
;
258 waveInClose(m_internal
->m_devin
);
262 // -------------------------------------------------------------------------
263 // AllocHeader(int mode)
265 // mode has the same mean as in OpenDevice() except that here the two flags
266 // must be exclusive.
267 // AllocHeader() initializes an element of an operation (this can be input
268 // or output). It means it allocates the sound header's memory block
269 // and "prepares" it (It is needed by Windows). At the same time, it sets
270 // private datas so we can the header's owner (See callback).
272 // It returns the new allocated block or NULL.
273 // -------------------------------------------------------------------------
274 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
276 wxSoundInfoHeader
*info
;
279 // Some memory allocation
280 info
= new wxSoundInfoHeader
;
281 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
282 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
283 if (!info
->m_h_data
|| !info
->m_h_header
) {
285 m_snderror
= wxSOUND_MEMERR
;
289 // Get the two pointers from the system
290 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
291 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
292 // Set the header's mode
294 // Set the parent of the header
295 info
->m_driver
= this;
299 header
= info
->m_header
;
300 // Initialize Windows variables
301 header
->lpData
= info
->m_data
;
302 header
->dwBufferLength
= GetBestSize();
303 header
->dwUser
= (DWORD
)info
;
304 header
->dwFlags
= WHDR_DONE
;
306 // "Prepare" the header
307 if (mode
== wxSOUND_INPUT
) {
310 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
313 if (result
!= MMSYSERR_NOERROR
) {
314 // If something goes wrong, free everything.
315 GlobalUnlock(info
->m_data
);
316 GlobalUnlock(info
->m_header
);
317 GlobalFree(info
->m_h_data
);
318 GlobalFree(info
->m_h_header
);
321 m_snderror
= wxSOUND_IOERR
;
324 } else if (mode
== wxSOUND_OUTPUT
) {
327 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
330 if (result
!= MMSYSERR_NOERROR
) {
331 // If something goes wrong, free everything.
332 GlobalUnlock(info
->m_data
);
333 GlobalUnlock(info
->m_header
);
334 GlobalFree(info
->m_h_data
);
335 GlobalFree(info
->m_h_header
);
338 m_snderror
= wxSOUND_IOERR
;
345 // -------------------------------------------------------------------------
346 // AllocHeaders(int mode)
348 // "mode" has the same mean as for OpenDevice() except that the two flags must
350 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
351 // queue. It uses AllocHeader() for each element.
353 // Once it has allocated all blocks, it returns TRUE and if an error occured
355 // -------------------------------------------------------------------------
356 bool wxSoundStreamWin::AllocHeaders(int mode
)
359 wxSoundInfoHeader
**headers
;
361 if (mode
== wxSOUND_OUTPUT
)
362 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
364 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
366 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
368 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
369 headers
[i
] = AllocHeader(mode
);
378 // -------------------------------------------------------------------------
379 // FreeHeader(int mode)
381 // "mode" has the same mean as for OpenDevice() except that the two flags must
383 // FreeHeader() frees a memory block and "unprepares" it.
384 // -------------------------------------------------------------------------
385 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
387 if (mode
== wxSOUND_OUTPUT
)
388 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
390 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
392 GlobalUnlock(header
->m_data
);
393 GlobalUnlock(header
->m_header
);
394 GlobalFree(header
->m_h_header
);
395 GlobalFree(header
->m_h_data
);
399 // -------------------------------------------------------------------------
400 // FreeHeaders(int mode)
402 // "mode" has the same mean as for OpenDevice() except that the two flags must
404 // FreeHeaders() frees all an operation queue once it has checked that
405 // all buffers have been terminated.
406 // -------------------------------------------------------------------------
407 void wxSoundStreamWin::FreeHeaders(int mode
)
410 wxSoundInfoHeader
***headers
;
412 if (mode
== wxSOUND_OUTPUT
)
413 headers
= &m_headers_play
;
415 headers
= &m_headers_rec
;
417 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
419 // We wait for the end of the buffer
420 WaitFor((*headers
)[i
]);
421 // Then, we free the header
422 FreeHeader((*headers
)[i
], mode
);
429 // -------------------------------------------------------------------------
430 // WaitFor(wxSoundInfoHeader *info)
432 // "info" is one element of an IO queue
433 // WaitFor() checks whether the specified block has been terminated.
434 // If it hasn't been terminated, it waits for its termination.
436 // NB: if it's a partially filled buffer it adds it to the Windows queue
437 // -------------------------------------------------------------------------
438 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
440 // If the buffer is finished, we return immediately
441 if (!info
->m_playing
) {
443 // We begun filling it: we must send it to the Windows queue
444 if (info
->m_position
!= 0) {
445 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
454 m_waiting_for
= TRUE
;
455 // Else, we wait for its termination
456 while (info
->m_playing
|| info
->m_recording
)
458 m_waiting_for
= FALSE
;
461 // -------------------------------------------------------------------------
462 // AddToQueue(wxSoundInfoHeader *info)
464 // For "info", see WaitFor()
465 // AddToQueue() sends the IO queue element to the Windows queue.
467 // Warning: in the current implementation, it partially assume we send the
468 // element in the right order. This is true in that implementation but if
469 // you use it elsewhere, be careful: it may shuffle all your sound datas.
470 // -------------------------------------------------------------------------
471 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
475 if (info
->m_mode
== wxSOUND_INPUT
) {
476 // Increment the input fragment pointer
477 result
= waveInAddBuffer(m_internal
->m_devin
,
478 info
->m_header
, sizeof(WAVEHDR
));
479 if (result
== MMSYSERR_NOERROR
)
480 info
->m_recording
= TRUE
;
483 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
484 result
= waveOutWrite(m_internal
->m_devout
,
485 info
->m_header
, sizeof(WAVEHDR
));
486 if (result
== MMSYSERR_NOERROR
)
487 info
->m_playing
= TRUE
;
494 // -------------------------------------------------------------------------
495 // ClearHeader(wxSoundInfoHeader *info)
497 // ClearHeader() reinitializes the parameters of "info" to their default
499 // -------------------------------------------------------------------------
500 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
502 info
->m_playing
= FALSE
;
503 info
->m_recording
= FALSE
;
504 info
->m_position
= 0;
505 info
->m_size
= GetBestSize();
508 // -------------------------------------------------------------------------
509 // wxSoundInfoHeader *NextFragmentOutput()
511 // NextFragmentOutput() looks for a free output block. It will always
512 // return you a non-NULL pointer but it may waits for an empty buffer a long
514 // -------------------------------------------------------------------------
515 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
517 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
518 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
520 if (m_headers_play
[m_current_frag_out
]->m_playing
)
521 WaitFor(m_headers_play
[m_current_frag_out
]);
523 if (m_current_frag_out
== m_output_frag_out
)
524 m_queue_filled
= TRUE
;
525 return m_headers_play
[m_current_frag_out
];
528 // -------------------------------------------------------------------------
529 // The behaviour of Write is documented in the global documentation.
530 // -------------------------------------------------------------------------
531 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
534 if (!m_internal
->m_output_enabled
)
538 wxSoundInfoHeader
*header
;
541 // Get a new output fragment
542 header
= NextFragmentOutput();
544 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
545 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
547 header
->m_position
+= to_copy
;
548 header
->m_size
-= to_copy
;
549 buffer
= (((const char *)buffer
) + to_copy
);
551 m_lastcount
+= to_copy
;
553 // If the fragment is full, we send it to the Windows queue.
554 if (header
->m_size
== 0)
555 if (!AddToQueue(header
)) {
556 m_snderror
= wxSOUND_IOERR
;
563 // -------------------------------------------------------------------------
564 // NextFragmentInput is not functional.
565 // -------------------------------------------------------------------------
566 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
568 wxSoundInfoHeader
*header
;
570 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
572 header
= m_headers_rec
[m_current_frag_in
];
573 if (header
->m_recording
)
576 if (m_current_frag_in
== m_input_frag_in
)
577 m_queue_filled
= TRUE
;
582 // -------------------------------------------------------------------------
583 // The behaviour of Read is documented in the global documentation.
584 // -------------------------------------------------------------------------
585 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
587 wxSoundInfoHeader
*header
;
591 if (!m_internal
->m_input_enabled
)
595 header
= NextFragmentInput();
597 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
598 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
600 header
->m_position
+= to_copy
;
601 header
->m_size
-= to_copy
;
602 buffer
= (((char *)buffer
) + to_copy
);
604 m_lastcount
+= to_copy
;
606 if (header
->m_size
== 0) {
608 if (!AddToQueue(header
)) {
609 m_snderror
= wxSOUND_IOERR
;
617 // -------------------------------------------------------------------------
618 // NotifyDoneBuffer(wxUint32 dev_handle)
620 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
621 // fragment finished. It reinitializes the parameters of the fragment and
622 // sends an event to the clients.
623 // -------------------------------------------------------------------------
624 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
626 wxSoundInfoHeader
*info
;
628 if (flag
== wxSOUND_OUTPUT
) {
629 if (!m_internal
->m_output_enabled
)
632 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
633 info
= m_headers_play
[m_output_frag_out
];
635 m_queue_filled
= FALSE
;
637 OnSoundEvent(wxSOUND_OUTPUT
);
639 if (!m_internal
->m_input_enabled
)
642 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
643 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
645 OnSoundEvent(wxSOUND_INPUT
);
646 m_queue_filled
= FALSE
;
650 // -------------------------------------------------------------------------
651 // -------------------------------------------------------------------------
652 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
654 // TODO: detect best format
655 return wxSoundStream::SetSoundFormat(base
);
658 // -------------------------------------------------------------------------
659 // -------------------------------------------------------------------------
660 bool wxSoundStreamWin::StartProduction(int evt
)
665 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
666 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
669 if (!OpenDevice(evt
))
672 m_production_started
= TRUE
;
673 m_queue_filled
= FALSE
;
674 // Send a dummy event to start.
675 if (evt
& wxSOUND_OUTPUT
)
676 OnSoundEvent(wxSOUND_OUTPUT
);
678 if (evt
& wxSOUND_INPUT
) {
680 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
681 AddToQueue(m_headers_rec
[i
]);
683 waveInStart(m_internal
->m_devin
);
689 // -------------------------------------------------------------------------
690 // -------------------------------------------------------------------------
691 bool wxSoundStreamWin::StopProduction()
693 if (!m_production_started
)
696 m_production_started
= FALSE
;
701 // -------------------------------------------------------------------------
702 // -------------------------------------------------------------------------
703 bool wxSoundStreamWin::QueueFilled() const
705 return (!m_production_started
|| m_queue_filled
);
709 // --------------------------------------------------------------------------
711 // --------------------------------------------------------------------------
713 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
714 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
720 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
722 bool wxSoundWinModule::OnInit() {
723 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
727 void wxSoundWinModule::OnExit() {
728 delete wxSoundHandleList
;