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 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
, wxSOUND_OUTPUT
);
91 wxFindSoundFromHandle((WXHWND
)hWnd
)->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
99 // -----------------------------------------------------------------------
100 // CreateSndWindow() creates an hidden window which will receive the sound
102 // -----------------------------------------------------------------------
104 void wxSoundStreamWin::CreateSndWindow()
106 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
110 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
111 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
112 wxGetInstance(), NULL
);
114 error
= GetLastError();
116 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
118 // Add this window to the sound handle list so we'll be able to redecode
119 // the "magic" number.
120 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
123 // -----------------------------------------------------------------------
124 // DestroySndWindow() destroys the hidden window
125 // -----------------------------------------------------------------------
127 void wxSoundStreamWin::DestroySndWindow()
129 if (m_internal
->m_sndWin
) {
130 ::DestroyWindow(m_internal
->m_sndWin
);
131 wxSoundHandleList
->DeleteObject((wxObject
*)this);
135 // -------------------------------------------------------------------------
136 // OpenDevice(int mode) initializes the windows driver for a "mode"
137 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
138 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
139 // is set, then the driver is opened for input operation. The two modes
141 // The initialization parameters (sample rate, ...) are taken from the
142 // m_sndformat object.
143 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
145 // -------------------------------------------------------------------------
146 bool wxSoundStreamWin::OpenDevice(int mode
)
148 wxSoundFormatPcm
*pcm
;
149 WAVEFORMATEX wformat
;
152 m_snderror
= wxSOUND_INVFRMT
;
156 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
158 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
159 wformat
.nChannels
= pcm
->GetChannels();
160 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
161 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
162 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
163 wformat
.wBitsPerSample
= pcm
->GetBPS();
166 // -----------------------------------
167 // Open the driver for Output operation
168 // -----------------------------------
169 if (mode
& wxSOUND_OUTPUT
) {
172 result
= waveOutOpen(&m_internal
->m_devout
,
173 WAVE_MAPPER
, &wformat
,
174 (DWORD
)m_internal
->m_sndWin
, 0,
177 if (result
!= MMSYSERR_NOERROR
) {
178 m_snderror
= wxSOUND_INVDEV
;
182 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
183 m_current_frag_out
= 0;
185 m_internal
->m_output_enabled
= TRUE
;
187 // -----------------------------------
188 // Open the driver for Input operation
189 // -----------------------------------
190 if (mode
& wxSOUND_INPUT
) {
193 result
= waveInOpen(&m_internal
->m_devin
,
194 WAVE_MAPPER
, &wformat
,
195 (DWORD
)m_internal
->m_sndWin
, 0,
198 if (result
!= MMSYSERR_NOERROR
) {
199 m_snderror
= wxSOUND_INVDEV
;
203 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
206 m_internal
->m_input_enabled
= TRUE
;
209 if (mode
& wxSOUND_OUTPUT
) {
210 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
215 if (mode
& wxSOUND_INPUT
) {
216 if (!AllocHeaders(wxSOUND_INPUT
)) {
225 // -------------------------------------------------------------------------
226 // CloseDevice() closes the driver handles and frees memory allocated for
228 // -------------------------------------------------------------------------
229 void wxSoundStreamWin::CloseDevice()
231 m_internal
->m_output_enabled
= FALSE
;
232 m_internal
->m_input_enabled
= FALSE
;
234 if (m_internal
->m_output_enabled
) {
235 waveOutReset(m_internal
->m_devout
);
236 FreeHeaders(wxSOUND_OUTPUT
);
237 waveOutClose(m_internal
->m_devout
);
240 if (m_internal
->m_input_enabled
) {
241 waveInReset(m_internal
->m_devin
);
242 FreeHeaders(wxSOUND_INPUT
);
243 waveInClose(m_internal
->m_devin
);
247 // -------------------------------------------------------------------------
248 // AllocHeader(int mode)
250 // mode has the same mean as in OpenDevice() except that here the two flags
251 // must be exclusive.
252 // AllocHeader() initializes an element of an operation (this can be input
253 // or output). It means it allocates the sound header's memory block
254 // and "prepares" it (It is needed by Windows). At the same time, it sets
255 // private datas so we can the header's owner (See callback).
257 // It returns the new allocated block or NULL.
258 // -------------------------------------------------------------------------
259 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
261 wxSoundInfoHeader
*info
;
264 // Some memory allocation
265 info
= new wxSoundInfoHeader
;
266 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
267 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
268 if (!info
->m_h_data
|| !info
->m_h_header
) {
270 m_snderror
= wxSOUND_MEMERR
;
274 // Get the two pointers from the system
275 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
276 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
277 // Set the header's mode
279 // Set the parent of the header
280 info
->m_driver
= this;
284 header
= info
->m_header
;
285 // Initialize Windows variables
286 header
->lpData
= info
->m_data
;
287 header
->dwBufferLength
= GetBestSize();
288 header
->dwUser
= (DWORD
)info
;
289 header
->dwFlags
= WHDR_DONE
;
292 // "Prepare" the header
293 if (mode
== wxSOUND_INPUT
) {
296 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
299 if (result
!= MMSYSERR_NOERROR
) {
300 // If something goes wrong, free everything.
301 GlobalUnlock(info
->m_data
);
302 GlobalUnlock(info
->m_header
);
303 GlobalFree(info
->m_h_data
);
304 GlobalFree(info
->m_h_header
);
307 m_snderror
= wxSOUND_IOERR
;
310 } else if (mode
== wxSOUND_OUTPUT
) {
313 result
= waveOutPrepareHeader(m_internal
->m_devout
, 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_IOERR
;
331 // -------------------------------------------------------------------------
332 // AllocHeaders(int mode)
334 // "mode" has the same mean as for OpenDevice() except that the two flags must
336 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
337 // queue. It uses AllocHeader() for each element.
339 // Once it has allocated all blocks, it returns TRUE and if an error occured
341 // -------------------------------------------------------------------------
342 bool wxSoundStreamWin::AllocHeaders(int mode
)
345 wxSoundInfoHeader
**headers
;
347 if (mode
== wxSOUND_OUTPUT
)
348 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
350 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
352 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
354 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
355 headers
[i
] = AllocHeader(mode
);
364 // -------------------------------------------------------------------------
365 // FreeHeader(int mode)
367 // "mode" has the same mean as for OpenDevice() except that the two flags must
369 // FreeHeader() frees a memory block and "unprepares" it.
370 // -------------------------------------------------------------------------
371 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
373 if (mode
== wxSOUND_OUTPUT
)
374 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
376 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
378 GlobalUnlock(header
->m_data
);
379 GlobalUnlock(header
->m_header
);
380 GlobalFree(header
->m_h_header
);
381 GlobalFree(header
->m_h_data
);
385 // -------------------------------------------------------------------------
386 // FreeHeaders(int mode)
388 // "mode" has the same mean as for OpenDevice() except that the two flags must
390 // FreeHeaders() frees all an operation queue once it has checked that
391 // all buffers have been terminated.
392 // -------------------------------------------------------------------------
393 void wxSoundStreamWin::FreeHeaders(int mode
)
396 wxSoundInfoHeader
***headers
;
398 if (mode
== wxSOUND_OUTPUT
)
399 headers
= &m_headers_play
;
401 headers
= &m_headers_rec
;
403 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
405 // We wait for the end of the buffer
406 WaitFor((*headers
)[i
]);
407 // Then, we free the header
408 FreeHeader((*headers
)[i
], mode
);
415 // -------------------------------------------------------------------------
416 // WaitFor(wxSoundInfoHeader *info)
418 // "info" is one element of an IO queue
419 // WaitFor() checks whether the specified block has been terminated.
420 // If it hasn't been terminated, it waits for its termination.
422 // NB: if it's a partially filled buffer it adds it to the Windows queue
423 // -------------------------------------------------------------------------
424 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
426 // If the buffer is finished, we return immediately
427 if (!info
->m_playing
) {
429 // We begun filling it: we must send it to the Windows queue
430 if (info
->m_position
!= 0) {
431 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
438 // Else, we wait for its termination
439 while (info
->m_playing
|| info
->m_recording
)
443 // -------------------------------------------------------------------------
444 // AddToQueue(wxSoundInfoHeader *info)
446 // For "info", see WaitFor()
447 // AddToQueue() sends the IO queue element to the Windows queue.
449 // Warning: in the current implementation, it partially assume we send the
450 // element in the right order. This is true in that implementation but if
451 // you use it elsewhere, be careful: it may shuffle all your sound datas.
452 // -------------------------------------------------------------------------
453 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
457 if (info
->m_mode
== wxSOUND_INPUT
) {
458 // Increment the input fragment pointer
459 result
= waveInAddBuffer(m_internal
->m_devin
,
460 info
->m_header
, sizeof(WAVEHDR
));
461 if (result
== MMSYSERR_NOERROR
)
462 info
->m_recording
= TRUE
;
465 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
466 result
= waveOutWrite(m_internal
->m_devout
,
467 info
->m_header
, sizeof(WAVEHDR
));
468 if (result
== MMSYSERR_NOERROR
)
469 info
->m_playing
= TRUE
;
476 // -------------------------------------------------------------------------
477 // ClearHeader(wxSoundInfoHeader *info)
479 // ClearHeader() reinitializes the parameters of "info" to their default
481 // -------------------------------------------------------------------------
482 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
484 info
->m_playing
= FALSE
;
485 info
->m_recording
= FALSE
;
486 info
->m_position
= 0;
487 info
->m_size
= GetBestSize();
490 // -------------------------------------------------------------------------
491 // wxSoundInfoHeader *NextFragmentOutput()
493 // NextFragmentOutput() looks for a free output block. It will always
494 // return you a non-NULL pointer but it may waits for an empty buffer a long
496 // -------------------------------------------------------------------------
497 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
499 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
500 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
502 if (m_headers_play
[m_current_frag_out
]->m_playing
)
503 WaitFor(m_headers_play
[m_current_frag_out
]);
505 if (m_current_frag_out
== m_output_frag_out
)
506 m_queue_filled
= TRUE
;
507 return m_headers_play
[m_current_frag_out
];
510 // -------------------------------------------------------------------------
511 // The behaviour of Write is documented in the global documentation.
512 // -------------------------------------------------------------------------
513 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
516 if (!m_internal
->m_output_enabled
)
520 wxSoundInfoHeader
*header
;
523 // Get a new output fragment
524 header
= NextFragmentOutput();
526 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
527 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
529 header
->m_position
+= to_copy
;
530 header
->m_size
-= to_copy
;
531 buffer
= (((const char *)buffer
) + to_copy
);
533 m_lastcount
+= to_copy
;
535 // If the fragment is full, we send it to the Windows queue.
536 if (header
->m_size
== 0)
537 if (!AddToQueue(header
)) {
538 m_snderror
= wxSOUND_IOERR
;
545 // -------------------------------------------------------------------------
546 // NextFragmentInput is not functional.
547 // -------------------------------------------------------------------------
548 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
550 wxSoundInfoHeader
*header
;
552 header
= m_headers_rec
[m_current_frag_in
];
553 if (header
->m_recording
)
556 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
558 if (m_current_frag_in
== m_input_frag_in
)
559 m_queue_filled
= TRUE
;
564 // -------------------------------------------------------------------------
565 // The behaviour of Read is documented in the global documentation.
566 // -------------------------------------------------------------------------
567 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
569 wxSoundInfoHeader
*header
;
573 if (!m_internal
->m_input_enabled
)
577 header
= NextFragmentInput();
579 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
580 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
582 header
->m_position
+= to_copy
;
583 header
->m_size
-= to_copy
;
584 buffer
= (((char *)buffer
) + to_copy
);
586 m_lastcount
+= to_copy
;
588 if (header
->m_size
== 0) {
590 if (!AddToQueue(header
)) {
591 m_snderror
= wxSOUND_IOERR
;
599 // -------------------------------------------------------------------------
600 // NotifyDoneBuffer(wxUint32 dev_handle)
602 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
603 // fragment finished. It reinitializes the parameters of the fragment and
604 // sends an event to the clients.
605 // -------------------------------------------------------------------------
606 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
608 wxSoundInfoHeader
*info
;
610 if (flag
== wxSOUND_OUTPUT
) {
611 if (!m_internal
->m_output_enabled
)
614 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
615 info
= m_headers_play
[m_output_frag_out
];
617 m_queue_filled
= FALSE
;
618 OnSoundEvent(wxSOUND_OUTPUT
);
620 if (!m_internal
->m_input_enabled
)
623 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
624 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
625 OnSoundEvent(wxSOUND_INPUT
);
626 m_queue_filled
= FALSE
;
630 // -------------------------------------------------------------------------
631 // -------------------------------------------------------------------------
632 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
634 return wxSoundStream::SetSoundFormat(base
);
637 // -------------------------------------------------------------------------
638 // -------------------------------------------------------------------------
639 bool wxSoundStreamWin::StartProduction(int evt
)
641 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
642 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
645 if (!OpenDevice(evt
))
648 m_production_started
= TRUE
;
649 m_queue_filled
= FALSE
;
650 // Send a dummy event to start.
651 if (evt
& wxSOUND_OUTPUT
)
654 if (evt
& wxSOUND_INPUT
) {
656 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
657 AddToQueue(m_headers_rec
[i
]);
659 waveInStart(m_internal
->m_devin
);
665 // -------------------------------------------------------------------------
666 // -------------------------------------------------------------------------
667 bool wxSoundStreamWin::StopProduction()
669 if (m_internal
->m_input_enabled
)
670 waveInStop(m_internal
->m_devin
);
672 m_production_started
= FALSE
;
677 // -------------------------------------------------------------------------
678 // -------------------------------------------------------------------------
679 bool wxSoundStreamWin::QueueFilled() const
681 return (!m_production_started
|| m_queue_filled
);
685 // --------------------------------------------------------------------------
687 // --------------------------------------------------------------------------
689 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
690 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
696 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
698 bool wxSoundWinModule::OnInit() {
699 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
703 void wxSoundWinModule::OnExit() {
704 delete wxSoundHandleList
;