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 128
56 wxSoundStreamWin::wxSoundStreamWin()
60 m_production_started
= FALSE
;
61 m_internal
= new wxSoundInternal
;
62 m_snderror
= wxSOUND_NOERR
;
68 if (!OpenDevice(wxSOUND_OUTPUT
))
74 wxSoundStreamWin::~wxSoundStreamWin()
76 if (m_production_started
)
83 LRESULT APIENTRY _EXPORT
_wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
84 WPARAM wParam
, LPARAM lParam
)
88 wxFindSoundFromHandle((WXHWND
)hWnd
)->NotifyDoneBuffer(wParam
);
97 // -----------------------------------------------------------------------
98 // CreateSndWindow() creates an hidden window which will receive the sound
100 // -----------------------------------------------------------------------
102 void wxSoundStreamWin::CreateSndWindow()
104 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
108 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
109 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
110 wxGetInstance(), NULL
);
112 error
= GetLastError();
114 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
116 // Add this window to the sound handle list so we'll be able to redecode
117 // the "magic" number.
118 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
121 // -----------------------------------------------------------------------
122 // DestroySndWindow() destroys the hidden window
123 // -----------------------------------------------------------------------
125 void wxSoundStreamWin::DestroySndWindow()
127 if (m_internal
->m_sndWin
) {
128 ::DestroyWindow(m_internal
->m_sndWin
);
129 wxSoundHandleList
->DeleteObject((wxObject
*)this);
133 // -------------------------------------------------------------------------
134 // OpenDevice(int mode) initializes the windows driver for a "mode"
135 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
136 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
137 // is set, then the driver is opened for input operation. The two modes
139 // The initialization parameters (sample rate, ...) are taken from the
140 // m_sndformat object.
141 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
143 // -------------------------------------------------------------------------
144 bool wxSoundStreamWin::OpenDevice(int mode
)
146 wxSoundFormatPcm
*pcm
;
147 WAVEFORMATEX wformat
;
150 m_snderror
= wxSOUND_INVFRMT
;
154 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
156 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
157 wformat
.nChannels
= pcm
->GetChannels();
158 wformat
.nBlockAlign
= pcm
->GetBPS() / 8 * wformat
.nChannels
;
159 wformat
.nAvgBytesPerSec
= pcm
->GetBytesFromTime(1);
160 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
161 wformat
.wBitsPerSample
= pcm
->GetBPS();
164 // -----------------------------------
165 // Open the driver for Output operation
166 // -----------------------------------
167 if (mode
& wxSOUND_OUTPUT
) {
170 result
= waveOutOpen(&m_internal
->m_devout
,
171 WAVE_MAPPER
, &wformat
,
172 (DWORD
)m_internal
->m_sndWin
, 0,
175 if (result
!= MMSYSERR_NOERROR
) {
176 m_snderror
= wxSOUND_INVDEV
;
180 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
181 m_current_frag_out
= 0;
183 m_internal
->m_output_enabled
= TRUE
;
185 // -----------------------------------
186 // Open the driver for Input operation
187 // -----------------------------------
188 if (mode
& wxSOUND_INPUT
) {
191 result
= waveInOpen(&m_internal
->m_devin
,
192 WAVE_MAPPER
, &wformat
,
193 (DWORD
)m_internal
->m_sndWin
, 0,
196 if (result
!= MMSYSERR_NOERROR
) {
197 m_snderror
= wxSOUND_INVDEV
;
201 m_input_frag_in
= WXSOUND_MAX_QUEUE
-1;
202 m_current_frag_in
= 0;
204 m_internal
->m_input_enabled
= TRUE
;
207 if (!AllocHeaders(mode
)) {
214 // -------------------------------------------------------------------------
215 // CloseDevice() closes the driver handles and frees memory allocated for
217 // -------------------------------------------------------------------------
218 void wxSoundStreamWin::CloseDevice()
220 if (m_internal
->m_output_enabled
) {
221 FreeHeaders(wxSOUND_OUTPUT
);
222 waveOutReset(m_internal
->m_devout
);
223 waveOutClose(m_internal
->m_devout
);
226 if (m_internal
->m_input_enabled
) {
227 FreeHeaders(wxSOUND_INPUT
);
228 waveInReset(m_internal
->m_devin
);
229 waveInClose(m_internal
->m_devin
);
232 m_internal
->m_output_enabled
= FALSE
;
233 m_internal
->m_input_enabled
= FALSE
;
236 // -------------------------------------------------------------------------
237 // AllocHeader(int mode)
239 // mode has the same mean as in OpenDevice() except that here the two flags
240 // must be exclusive.
241 // AllocHeader() initializes an element of an operation (this can be input
242 // or output). It means it allocates the sound header's memory block
243 // and "prepares" it (It is needed by Windows). At the same time, it sets
244 // private datas so we can the header's owner (See callback).
246 // It returns the new allocated block or NULL.
247 // -------------------------------------------------------------------------
248 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
250 wxSoundInfoHeader
*info
;
253 // Some memory allocation
254 info
= new wxSoundInfoHeader
;
255 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
256 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
257 if (!info
->m_h_data
|| !info
->m_h_header
) {
259 m_snderror
= wxSOUND_MEMERR
;
263 // Get the two pointers from the system
264 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
265 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
266 // Set the header's mode
268 // Set the parent of the header
269 info
->m_driver
= this;
273 header
= info
->m_header
;
274 // Initialize Windows variables
275 header
->lpData
= info
->m_data
;
276 header
->dwBufferLength
= GetBestSize();
277 header
->dwUser
= (DWORD
)info
;
278 header
->dwFlags
= WHDR_DONE
;
281 // "Prepare" the header
282 if (mode
== wxSOUND_INPUT
) {
285 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
288 if (result
!= MMSYSERR_NOERROR
) {
289 // If something goes wrong, free everything.
290 GlobalUnlock(info
->m_data
);
291 GlobalUnlock(info
->m_header
);
292 GlobalFree(info
->m_h_data
);
293 GlobalFree(info
->m_h_header
);
296 m_snderror
= wxSOUND_IOERR
;
299 } else if (mode
== wxSOUND_OUTPUT
) {
302 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
305 if (result
!= MMSYSERR_NOERROR
) {
306 // If something goes wrong, free everything.
307 GlobalUnlock(info
->m_data
);
308 GlobalUnlock(info
->m_header
);
309 GlobalFree(info
->m_h_data
);
310 GlobalFree(info
->m_h_header
);
313 m_snderror
= wxSOUND_IOERR
;
320 // -------------------------------------------------------------------------
321 // AllocHeaders(int mode)
323 // "mode" has the same mean as for OpenDevice() except that the two flags must
325 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
326 // queue. It uses AllocHeader() for each element.
328 // Once it has allocated all blocks, it returns TRUE and if an error occured
330 // -------------------------------------------------------------------------
331 bool wxSoundStreamWin::AllocHeaders(int mode
)
334 wxSoundInfoHeader
**headers
;
336 if (mode
== wxSOUND_OUTPUT
)
337 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
339 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
341 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
343 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
344 headers
[i
] = AllocHeader(mode
);
353 // -------------------------------------------------------------------------
354 // FreeHeader(int mode)
356 // "mode" has the same mean as for OpenDevice() except that the two flags must
358 // FreeHeader() frees a memory block and "unprepares" it.
359 // -------------------------------------------------------------------------
360 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
362 if (mode
== wxSOUND_OUTPUT
)
363 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
365 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
367 GlobalUnlock(header
->m_data
);
368 GlobalUnlock(header
->m_header
);
369 GlobalFree(header
->m_h_header
);
370 GlobalFree(header
->m_h_data
);
374 // -------------------------------------------------------------------------
375 // FreeHeaders(int mode)
377 // "mode" has the same mean as for OpenDevice() except that the two flags must
379 // FreeHeaders() frees all an operation queue once it has checked that
380 // all buffers have been terminated.
381 // -------------------------------------------------------------------------
382 void wxSoundStreamWin::FreeHeaders(int mode
)
385 wxSoundInfoHeader
***headers
;
387 if (mode
== wxSOUND_OUTPUT
)
388 headers
= &m_headers_play
;
390 headers
= &m_headers_rec
;
392 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
394 // We wait for the end of the buffer
395 WaitFor((*headers
)[i
]);
396 // Then, we free the header
397 FreeHeader((*headers
)[i
], mode
);
404 // -------------------------------------------------------------------------
405 // WaitFor(wxSoundInfoHeader *info)
407 // "info" is one element of an IO queue
408 // WaitFor() checks whether the specified block has been terminated.
409 // If it hasn't been terminated, it waits for its termination.
411 // NB: if it's a partially filled buffer it adds it to the Windows queue
412 // -------------------------------------------------------------------------
413 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
415 // We begun filling it: we must send it to the Windows queue
416 if (info
->m_position
!= 0) {
417 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
-info
->m_position
);
421 // If the buffer is finished, we return immediately
422 if (!info
->m_playing
&& !info
->m_recording
)
425 // Else, we wait for its termination
426 while (info
->m_playing
|| info
->m_recording
)
430 // -------------------------------------------------------------------------
431 // AddToQueue(wxSoundInfoHeader *info)
433 // For "info", see WaitFor()
434 // AddToQueue() sends the IO queue element to the Windows queue.
436 // Warning: in the current implementation, it partially assume we send the
437 // element in the right order. This is true in that implementation but if
438 // you use it elsewhere, be careful: it may shuffle all your sound datas.
439 // -------------------------------------------------------------------------
440 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
444 if (info
->m_mode
== wxSOUND_INPUT
) {
445 // Increment the input fragment pointer
446 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
447 result
= waveInAddBuffer(m_internal
->m_devin
,
448 info
->m_header
, sizeof(WAVEHDR
));
449 if (result
== MMSYSERR_NOERROR
)
450 info
->m_recording
= TRUE
;
453 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
454 result
= waveOutWrite(m_internal
->m_devout
,
455 info
->m_header
, sizeof(WAVEHDR
));
456 if (result
== MMSYSERR_NOERROR
)
457 info
->m_playing
= TRUE
;
464 // -------------------------------------------------------------------------
465 // ClearHeader(wxSoundInfoHeader *info)
467 // ClearHeader() reinitializes the parameters of "info" to their default
469 // -------------------------------------------------------------------------
470 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
472 info
->m_playing
= FALSE
;
473 info
->m_recording
= FALSE
;
474 info
->m_position
= 0;
475 info
->m_size
= GetBestSize();
478 // -------------------------------------------------------------------------
479 // wxSoundInfoHeader *NextFragmentOutput()
481 // NextFragmentOutput() looks for a free output block. It will always
482 // return you a non-NULL pointer but it may waits for an empty buffer a long
484 // -------------------------------------------------------------------------
485 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
487 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
488 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
490 if (m_headers_play
[m_current_frag_out
]->m_playing
)
491 WaitFor(m_headers_play
[m_current_frag_out
]);
493 if (m_current_frag_out
== m_output_frag_out
)
494 m_queue_filled
= TRUE
;
495 return m_headers_play
[m_current_frag_out
];
498 // -------------------------------------------------------------------------
499 // The behaviour of Write is documented in the global documentation.
500 // -------------------------------------------------------------------------
501 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
504 if (!m_internal
->m_output_enabled
)
508 wxSoundInfoHeader
*header
;
511 // Get a new output fragment
512 header
= NextFragmentOutput();
514 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
515 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
517 header
->m_position
+= to_copy
;
518 header
->m_size
-= to_copy
;
519 buffer
= (((const char *)buffer
) + to_copy
);
521 m_lastcount
+= to_copy
;
523 // If the fragment is full, we send it to the Windows queue.
524 if (header
->m_size
== 0)
525 if (!AddToQueue(header
)) {
526 m_snderror
= wxSOUND_IOERR
;
533 // -------------------------------------------------------------------------
534 // NextFragmentInput is not functional.
535 // -------------------------------------------------------------------------
536 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
538 wxSoundInfoHeader
*header
;
541 header
= m_headers_rec
[m_current_frag_in
];
544 if (m_current_frag_in
== m_input_frag_in
)
545 m_queue_filled
= TRUE
;
550 // -------------------------------------------------------------------------
551 // The behaviour of Read is documented in the global documentation.
552 // -------------------------------------------------------------------------
553 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
555 wxSoundInfoHeader
*header
;
559 if (!m_internal
->m_input_enabled
)
563 header
= NextFragmentInput();
565 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
566 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
568 header
->m_position
+= to_copy
;
569 header
->m_size
-= to_copy
;
570 buffer
= (((char *)buffer
) + to_copy
);
572 m_lastcount
+= to_copy
;
574 if (header
->m_size
== 0) {
576 if (!AddToQueue(header
)) {
577 m_snderror
= wxSOUND_IOERR
;
585 // -------------------------------------------------------------------------
586 // NotifyDoneBuffer(wxUint32 dev_handle)
588 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
589 // fragment finished. It reinitializes the parameters of the fragment and
590 // sends an event to the clients.
591 // -------------------------------------------------------------------------
592 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
)
594 wxSoundInfoHeader
*info
;
596 if (dev_handle
== (wxUint32
)m_internal
->m_devout
) {
597 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
598 info
= m_headers_play
[m_output_frag_out
];
600 m_queue_filled
= FALSE
;
601 OnSoundEvent(wxSOUND_OUTPUT
);
603 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
604 OnSoundEvent(wxSOUND_INPUT
);
605 m_queue_filled
= FALSE
;
609 // -------------------------------------------------------------------------
610 // -------------------------------------------------------------------------
611 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
613 return wxSoundStream::SetSoundFormat(base
);
616 // -------------------------------------------------------------------------
617 // -------------------------------------------------------------------------
618 bool wxSoundStreamWin::StartProduction(int evt
)
620 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
621 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
624 if (!OpenDevice(evt
))
627 m_production_started
= TRUE
;
628 m_queue_filled
= FALSE
;
629 // Send a dummy event to start.
630 if (evt
& wxSOUND_OUTPUT
)
633 if (evt
& wxSOUND_INPUT
) {
635 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
636 AddToQueue(m_headers_rec
[i
]);
642 // -------------------------------------------------------------------------
643 // -------------------------------------------------------------------------
644 bool wxSoundStreamWin::StopProduction()
646 m_production_started
= FALSE
;
651 // -------------------------------------------------------------------------
652 // -------------------------------------------------------------------------
653 bool wxSoundStreamWin::QueueFilled() const
655 return (!m_production_started
|| m_queue_filled
);
659 // --------------------------------------------------------------------------
661 // --------------------------------------------------------------------------
663 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
664 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
670 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
672 bool wxSoundWinModule::OnInit() {
673 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
677 void wxSoundWinModule::OnExit() {
678 delete wxSoundHandleList
;