1 // --------------------------------------------------------------------------
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000
7 // --------------------------------------------------------------------------
9 #pragma implementation "sndwin.cpp"
12 #include "wx/wxprec.h"
19 #include "wx/string.h"
22 #include "wx/module.h"
23 #include "wx/msw/private.h"
25 // -------------------------------------------------------------------------
27 // -------------------------------------------------------------------------
29 #include "wx/mmedia/sndbase.h"
30 #include "wx/mmedia/sndwin.h"
31 #include "wx/mmedia/sndpcm.h"
33 // -------------------------------------------------------------------------
35 // -------------------------------------------------------------------------
40 // -------------------------------------------------------------------------
41 // External definitions, forward, ...
42 // -------------------------------------------------------------------------
44 typedef struct _wxSoundInternal wxSoundInternal
;
45 typedef struct _wxSoundInfoHeader wxSoundInfoHeader
;
47 extern const wxChar
*wxCanvasClassName
;
49 wxList
*wxSoundHandleList
= NULL
;
51 static inline wxSoundStreamWin
*wxFindSoundFromHandle(WXHWND hWnd
)
53 wxNode
*node
= wxSoundHandleList
->Find((long)hWnd
);
56 return (wxSoundStreamWin
*)node
->Data();
59 struct _wxSoundInternal
{
63 bool m_output_enabled
, m_input_enabled
;
66 struct _wxSoundInfoHeader
{
67 HGLOBAL m_h_header
, m_h_data
;
71 bool m_playing
, m_recording
;
72 wxUint32 m_position
, m_size
;
74 wxSoundStreamWin
*m_driver
;
77 #define WXSOUND_MAX_QUEUE 10
79 wxSoundStreamWin::wxSoundStreamWin()
83 m_production_started
= FALSE
;
84 m_internal
= new wxSoundInternal
;
86 m_snderror
= wxSOUND_MEMERROR
;
90 m_snderror
= wxSOUND_NOERROR
;
96 m_internal
->m_input_enabled
= FALSE
;
97 m_internal
->m_output_enabled
= FALSE
;
99 m_waiting_for
= FALSE
;
101 if (!OpenDevice(wxSOUND_OUTPUT
))
107 wxSoundStreamWin::~wxSoundStreamWin()
110 if (m_production_started
)
118 // -----------------------------------------------------------------------
119 // _wxSoundHandlerWndProc: Window callback to handle buffer completion
120 // -----------------------------------------------------------------------
122 LRESULT APIENTRY _EXPORT
125 LRESULT WXDLLEXPORT APIENTRY _EXPORT
127 _wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
128 WPARAM wParam
, LPARAM lParam
)
130 wxSoundStreamWin
*sndwin
;
132 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
138 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
141 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
149 // -----------------------------------------------------------------------
150 // CreateSndWindow() creates an hidden window which will receive the sound
152 // -----------------------------------------------------------------------
154 void wxSoundStreamWin::CreateSndWindow()
156 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
160 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
161 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
162 wxGetInstance(), NULL
);
164 error
= GetLastError();
166 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
168 // Add this window to the sound handle list so we'll be able to redecode
169 // the "magic" number.
170 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
173 // -----------------------------------------------------------------------
174 // DestroySndWindow() destroys the hidden window
175 // -----------------------------------------------------------------------
177 void wxSoundStreamWin::DestroySndWindow()
179 if (m_internal
->m_sndWin
) {
180 ::DestroyWindow(m_internal
->m_sndWin
);
181 wxSoundHandleList
->DeleteObject((wxObject
*)this);
185 // -------------------------------------------------------------------------
186 // OpenDevice(int mode) initializes the windows driver for a "mode"
187 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
188 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
189 // is set, then the driver is opened for input operation. The two modes
191 // The initialization parameters (sample rate, ...) are taken from the
192 // m_sndformat object.
193 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
195 // -------------------------------------------------------------------------
196 bool wxSoundStreamWin::OpenDevice(int mode
)
198 wxSoundFormatPcm
*pcm
;
199 WAVEFORMATEX wformat
;
202 m_snderror
= wxSOUND_INVFRMT
;
206 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
208 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
209 wformat
.nChannels
= pcm
->GetChannels();
210 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
211 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
212 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
213 wformat
.wBitsPerSample
= pcm
->GetBPS();
216 // -----------------------------------
217 // Open the driver for Output operation
218 // -----------------------------------
219 if (mode
& wxSOUND_OUTPUT
) {
222 result
= waveOutOpen(&m_internal
->m_devout
,
223 WAVE_MAPPER
, &wformat
,
224 (DWORD
)m_internal
->m_sndWin
, 0,
227 if (result
!= MMSYSERR_NOERROR
) {
228 m_snderror
= wxSOUND_INVDEV
;
232 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
233 m_current_frag_out
= 0;
235 m_internal
->m_output_enabled
= TRUE
;
237 // -----------------------------------
238 // Open the driver for Input operation
239 // -----------------------------------
240 if (mode
& wxSOUND_INPUT
) {
243 result
= waveInOpen(&m_internal
->m_devin
,
244 WAVE_MAPPER
, &wformat
,
245 (DWORD
)m_internal
->m_sndWin
, 0,
248 if (result
!= MMSYSERR_NOERROR
) {
249 m_snderror
= wxSOUND_INVDEV
;
253 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
256 m_internal
->m_input_enabled
= TRUE
;
259 if (mode
& wxSOUND_OUTPUT
) {
260 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
265 if (mode
& wxSOUND_INPUT
) {
266 if (!AllocHeaders(wxSOUND_INPUT
)) {
275 // -------------------------------------------------------------------------
276 // CloseDevice() closes the driver handles and frees memory allocated for
278 // -------------------------------------------------------------------------
279 void wxSoundStreamWin::CloseDevice()
281 if (m_internal
->m_output_enabled
) {
282 FreeHeaders(wxSOUND_OUTPUT
);
283 m_internal
->m_output_enabled
= FALSE
;
284 waveOutClose(m_internal
->m_devout
);
287 if (m_internal
->m_input_enabled
) {
288 FreeHeaders(wxSOUND_INPUT
);
289 m_internal
->m_input_enabled
= FALSE
;
290 waveInClose(m_internal
->m_devin
);
294 // -------------------------------------------------------------------------
295 // AllocHeader(int mode)
297 // mode has the same mean as in OpenDevice() except that here the two flags
298 // must be exclusive.
299 // AllocHeader() initializes an element of an operation (this can be input
300 // or output). It means it allocates the sound header's memory block
301 // and "prepares" it (It is needed by Windows). At the same time, it sets
302 // private datas so we can the header's owner (See callback).
304 // It returns the new allocated block or NULL.
305 // -------------------------------------------------------------------------
306 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
308 wxSoundInfoHeader
*info
;
311 // Some memory allocation
312 info
= new wxSoundInfoHeader
;
313 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
314 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
315 if (!info
->m_h_data
|| !info
->m_h_header
) {
317 m_snderror
= wxSOUND_MEMERROR
;
321 // Get the two pointers from the system
322 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
323 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
324 // Set the header's mode
326 // Set the parent of the header
327 info
->m_driver
= this;
331 header
= info
->m_header
;
332 // Initialize Windows variables
333 header
->lpData
= info
->m_data
;
334 header
->dwBufferLength
= GetBestSize();
335 header
->dwUser
= (DWORD
)info
;
336 header
->dwFlags
= WHDR_DONE
;
338 // "Prepare" the header
339 if (mode
== wxSOUND_INPUT
) {
342 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
345 if (result
!= MMSYSERR_NOERROR
) {
346 // If something goes wrong, free everything.
347 GlobalUnlock(info
->m_data
);
348 GlobalUnlock(info
->m_header
);
349 GlobalFree(info
->m_h_data
);
350 GlobalFree(info
->m_h_header
);
353 m_snderror
= wxSOUND_IOERROR
;
356 } else if (mode
== wxSOUND_OUTPUT
) {
359 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
362 if (result
!= MMSYSERR_NOERROR
) {
363 // If something goes wrong, free everything.
364 GlobalUnlock(info
->m_data
);
365 GlobalUnlock(info
->m_header
);
366 GlobalFree(info
->m_h_data
);
367 GlobalFree(info
->m_h_header
);
370 m_snderror
= wxSOUND_IOERROR
;
377 // -------------------------------------------------------------------------
378 // AllocHeaders(int mode)
380 // "mode" has the same mean as for OpenDevice() except that the two flags must
382 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
383 // queue. It uses AllocHeader() for each element.
385 // Once it has allocated all blocks, it returns TRUE and if an error occured
387 // -------------------------------------------------------------------------
388 bool wxSoundStreamWin::AllocHeaders(int mode
)
391 wxSoundInfoHeader
**headers
;
393 if (mode
== wxSOUND_OUTPUT
)
394 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
396 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
398 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
400 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
401 headers
[i
] = AllocHeader(mode
);
410 // -------------------------------------------------------------------------
411 // FreeHeader(int mode)
413 // "mode" has the same mean as for OpenDevice() except that the two flags must
415 // FreeHeader() frees a memory block and "unprepares" it.
416 // -------------------------------------------------------------------------
417 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
419 if (mode
== wxSOUND_OUTPUT
)
420 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
422 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
424 GlobalUnlock(header
->m_data
);
425 GlobalUnlock(header
->m_header
);
426 GlobalFree(header
->m_h_header
);
427 GlobalFree(header
->m_h_data
);
431 // -------------------------------------------------------------------------
432 // FreeHeaders(int mode)
434 // "mode" has the same mean as for OpenDevice() except that the two flags must
436 // FreeHeaders() frees all an operation queue once it has checked that
437 // all buffers have been terminated.
438 // -------------------------------------------------------------------------
439 void wxSoundStreamWin::FreeHeaders(int mode
)
442 wxSoundInfoHeader
***headers
;
444 if (mode
== wxSOUND_OUTPUT
)
445 headers
= &m_headers_play
;
447 headers
= &m_headers_rec
;
449 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
451 // We wait for the end of the buffer
452 WaitFor((*headers
)[i
]);
453 // Then, we free the header
454 FreeHeader((*headers
)[i
], mode
);
461 // -------------------------------------------------------------------------
462 // WaitFor(wxSoundInfoHeader *info)
464 // "info" is one element of an IO queue
465 // WaitFor() checks whether the specified block has been terminated.
466 // If it hasn't been terminated, it waits for its termination.
468 // NB: if it's a partially filled buffer it adds it to the Windows queue
469 // -------------------------------------------------------------------------
470 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
472 // If the buffer is finished, we return immediately
473 if (!info
->m_playing
) {
475 // We begun filling it: we must send it to the Windows queue
476 if (info
->m_position
!= 0) {
477 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
486 m_waiting_for
= TRUE
;
487 // Else, we wait for its termination
488 while (info
->m_playing
|| info
->m_recording
)
490 m_waiting_for
= FALSE
;
493 // -------------------------------------------------------------------------
494 // AddToQueue(wxSoundInfoHeader *info)
496 // For "info", see WaitFor()
497 // AddToQueue() sends the IO queue element to the Windows queue.
499 // Warning: in the current implementation, it partially assume we send the
500 // element in the right order. This is true in that implementation but if
501 // you use it elsewhere, be careful: it may shuffle all your sound datas.
502 // -------------------------------------------------------------------------
503 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
507 if (info
->m_mode
== wxSOUND_INPUT
) {
508 // Increment the input fragment pointer
509 result
= waveInAddBuffer(m_internal
->m_devin
,
510 info
->m_header
, sizeof(WAVEHDR
));
511 if (result
== MMSYSERR_NOERROR
)
512 info
->m_recording
= TRUE
;
515 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
516 result
= waveOutWrite(m_internal
->m_devout
,
517 info
->m_header
, sizeof(WAVEHDR
));
518 if (result
== MMSYSERR_NOERROR
)
519 info
->m_playing
= TRUE
;
526 // -------------------------------------------------------------------------
527 // ClearHeader(wxSoundInfoHeader *info)
529 // ClearHeader() reinitializes the parameters of "info" to their default
531 // -------------------------------------------------------------------------
532 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
534 info
->m_playing
= FALSE
;
535 info
->m_recording
= FALSE
;
536 info
->m_position
= 0;
537 info
->m_size
= GetBestSize();
540 // -------------------------------------------------------------------------
541 // wxSoundInfoHeader *NextFragmentOutput()
543 // NextFragmentOutput() looks for a free output block. It will always
544 // return you a non-NULL pointer but it may waits for an empty buffer a long
546 // -------------------------------------------------------------------------
547 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
549 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
550 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
552 if (m_headers_play
[m_current_frag_out
]->m_playing
)
553 WaitFor(m_headers_play
[m_current_frag_out
]);
555 if (m_current_frag_out
== m_output_frag_out
)
556 m_queue_filled
= TRUE
;
557 return m_headers_play
[m_current_frag_out
];
560 // -------------------------------------------------------------------------
561 // The behaviour of Write is documented in the global documentation.
562 // -------------------------------------------------------------------------
563 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
566 if (!m_internal
->m_output_enabled
) {
567 m_snderror
= wxSOUND_NOTSTARTED
;
573 wxSoundInfoHeader
*header
;
576 // Get a new output fragment
577 header
= NextFragmentOutput();
579 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
580 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
582 header
->m_position
+= to_copy
;
583 header
->m_size
-= to_copy
;
584 buffer
= (((const char *)buffer
) + to_copy
);
586 m_lastcount
+= to_copy
;
588 // If the fragment is full, we send it to the Windows queue.
589 if (header
->m_size
== 0)
590 if (!AddToQueue(header
)) {
591 m_snderror
= wxSOUND_IOERROR
;
598 // -------------------------------------------------------------------------
599 // NextFragmentInput is not functional.
600 // -------------------------------------------------------------------------
601 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
603 wxSoundInfoHeader
*header
;
605 // Queue pointer: reader
606 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
608 header
= m_headers_rec
[m_current_frag_in
];
609 // If the current buffer is in recording mode, we must wait for its
611 if (header
->m_recording
)
614 // We reached the writer position: the queue is full.
615 if (m_current_frag_in
== m_input_frag_in
)
616 m_queue_filled
= TRUE
;
621 // -------------------------------------------------------------------------
622 // The behaviour of Read is documented in the global documentation.
623 // -------------------------------------------------------------------------
624 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
626 wxSoundInfoHeader
*header
;
630 if (!m_internal
->m_input_enabled
)
634 header
= NextFragmentInput();
636 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
637 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
639 header
->m_position
+= to_copy
;
640 header
->m_size
-= to_copy
;
641 buffer
= (((char *)buffer
) + to_copy
);
643 m_lastcount
+= to_copy
;
645 if (header
->m_size
== 0) {
647 if (!AddToQueue(header
)) {
648 m_snderror
= wxSOUND_IOERROR
;
656 // -------------------------------------------------------------------------
657 // NotifyDoneBuffer(wxUint32 dev_handle)
659 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
660 // fragment finished. It reinitializes the parameters of the fragment and
661 // sends an event to the clients.
662 // -------------------------------------------------------------------------
663 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
665 wxSoundInfoHeader
*info
;
667 if (flag
== wxSOUND_OUTPUT
) {
668 if (!m_internal
->m_output_enabled
)
671 // Queue pointer: reader
672 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
673 info
= m_headers_play
[m_output_frag_out
];
674 // Clear header to tell the system the buffer is free now
676 m_queue_filled
= FALSE
;
678 // Try to requeue a new buffer.
679 OnSoundEvent(wxSOUND_OUTPUT
);
681 if (!m_internal
->m_input_enabled
)
684 // Recording completed
685 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
686 // Queue pointer: writer
687 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
689 OnSoundEvent(wxSOUND_INPUT
);
690 m_queue_filled
= FALSE
;
694 // -------------------------------------------------------------------------
696 // -------------------------------------------------------------------------
697 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
699 // TODO: detect best format
700 return wxSoundStream::SetSoundFormat(base
);
703 // -------------------------------------------------------------------------
705 // -------------------------------------------------------------------------
706 bool wxSoundStreamWin::StartProduction(int evt
)
711 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
712 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
715 if (!OpenDevice(evt
))
718 m_production_started
= TRUE
;
719 m_queue_filled
= FALSE
;
720 // Send a dummy event to start.
721 if (evt
& wxSOUND_OUTPUT
)
722 OnSoundEvent(wxSOUND_OUTPUT
);
724 if (evt
& wxSOUND_INPUT
) {
726 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
727 AddToQueue(m_headers_rec
[i
]);
729 waveInStart(m_internal
->m_devin
);
735 // -------------------------------------------------------------------------
737 // ------------------------------------------------------------------------
738 bool wxSoundStreamWin::StopProduction()
740 if (!m_production_started
) {
741 m_snderror
= wxSOUND_NOTSTARTED
;
745 m_snderror
= wxSOUND_NOERROR
;
746 m_production_started
= FALSE
;
751 // -------------------------------------------------------------------------
753 // -------------------------------------------------------------------------
754 bool wxSoundStreamWin::QueueFilled() const
756 return (!m_production_started
|| m_queue_filled
);
760 // --------------------------------------------------------------------------
762 // --------------------------------------------------------------------------
764 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
765 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
771 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
773 bool wxSoundWinModule::OnInit() {
774 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
778 void wxSoundWinModule::OnExit() {
779 delete wxSoundHandleList
;