1 // --------------------------------------------------------------------------
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000
8 // --------------------------------------------------------------------------
10 #include "wx/wxprec.h"
17 #include "wx/string.h"
20 #include "wx/module.h"
21 #include "wx/msw/private.h"
23 // -------------------------------------------------------------------------
25 // -------------------------------------------------------------------------
27 #include "wx/mmedia/sndbase.h"
28 #include "wx/mmedia/sndwin.h"
29 #include "wx/mmedia/sndpcm.h"
31 // -------------------------------------------------------------------------
33 // -------------------------------------------------------------------------
38 // -------------------------------------------------------------------------
39 // External definitions, forward, ...
40 // -------------------------------------------------------------------------
42 typedef struct _wxSoundInternal wxSoundInternal
;
43 typedef struct _wxSoundInfoHeader wxSoundInfoHeader
;
45 extern const wxChar
*wxCanvasClassName
;
47 wxList
*wxSoundHandleList
= NULL
;
49 static inline wxSoundStreamWin
*wxFindSoundFromHandle(WXHWND hWnd
)
51 wxObjectList::compatibility_iterator node
= wxSoundHandleList
->Find((long)hWnd
);
54 return (wxSoundStreamWin
*)node
->GetData();
57 struct _wxSoundInternal
{
61 bool m_output_enabled
, m_input_enabled
;
64 struct _wxSoundInfoHeader
{
65 HGLOBAL m_h_header
, m_h_data
;
69 bool m_playing
, m_recording
;
70 wxUint32 m_position
, m_size
;
72 wxSoundStreamWin
*m_driver
;
75 #define WXSOUND_MAX_QUEUE 10
77 wxSoundStreamWin::wxSoundStreamWin()
81 m_production_started
= false;
82 m_internal
= new wxSoundInternal
;
84 m_snderror
= wxSOUND_MEMERROR
;
88 m_snderror
= wxSOUND_NOERROR
;
94 m_internal
->m_input_enabled
= false;
95 m_internal
->m_output_enabled
= false;
97 m_waiting_for
= false;
99 if (!OpenDevice(wxSOUND_OUTPUT
)) {
100 m_snderror
= wxSOUND_NOERROR
; //next call to OpenDevice won't do this
101 if (!OpenDevice(wxSOUND_INPUT
))
108 wxSoundStreamWin::~wxSoundStreamWin()
111 if (m_production_started
)
119 // -----------------------------------------------------------------------
120 // _wxSoundHandlerWndProc: Window callback to handle buffer completion
121 // -----------------------------------------------------------------------
122 LRESULT APIENTRY _EXPORT
124 _wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
125 WPARAM wParam
, LPARAM
WXUNUSED(lParam
))
127 wxSoundStreamWin
*sndwin
;
129 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
135 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
138 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
146 // -----------------------------------------------------------------------
147 // CreateSndWindow() creates an hidden window which will receive the sound
149 // -----------------------------------------------------------------------
151 void wxSoundStreamWin::CreateSndWindow()
153 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
155 // NB: class name must be kept in sync with wxCanvasClassName in
157 m_internal
->m_sndWin
= ::CreateWindow(wxT("wxWindowClass"), NULL
, 0,
158 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
159 wxGetInstance(), NULL
);
163 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
165 // Add this window to the sound handle list so we'll be able to redecode
166 // the "magic" number.
167 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
170 // -----------------------------------------------------------------------
171 // DestroySndWindow() destroys the hidden window
172 // -----------------------------------------------------------------------
174 void wxSoundStreamWin::DestroySndWindow()
176 if (m_internal
->m_sndWin
) {
177 ::DestroyWindow(m_internal
->m_sndWin
);
178 wxSoundHandleList
->DeleteObject((wxObject
*)this);
182 // -------------------------------------------------------------------------
183 // OpenDevice(int mode) initializes the windows driver for a "mode"
184 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
185 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
186 // is set, then the driver is opened for input operation. The two modes
188 // The initialization parameters (sample rate, ...) are taken from the
189 // m_sndformat object.
190 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
192 // -------------------------------------------------------------------------
193 bool wxSoundStreamWin::OpenDevice(int mode
)
195 wxSoundFormatPcm
*pcm
;
196 WAVEFORMATEX wformat
;
199 m_snderror
= wxSOUND_INVFRMT
;
203 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
205 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
206 wformat
.nChannels
= pcm
->GetChannels();
207 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
208 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
209 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
210 wformat
.wBitsPerSample
= pcm
->GetBPS();
213 // -----------------------------------
214 // Open the driver for Output operation
215 // -----------------------------------
216 if (mode
& wxSOUND_OUTPUT
) {
219 result
= waveOutOpen(&m_internal
->m_devout
,
220 WAVE_MAPPER
, &wformat
,
221 (DWORD
)m_internal
->m_sndWin
, 0,
224 if (result
!= MMSYSERR_NOERROR
) {
225 m_snderror
= wxSOUND_INVDEV
;
229 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
230 m_current_frag_out
= 0;
232 m_internal
->m_output_enabled
= true;
234 // -----------------------------------
235 // Open the driver for Input operation
236 // -----------------------------------
237 if (mode
& wxSOUND_INPUT
) {
240 result
= waveInOpen(&m_internal
->m_devin
,
241 WAVE_MAPPER
, &wformat
,
242 (DWORD
)m_internal
->m_sndWin
, 0,
245 if (result
!= MMSYSERR_NOERROR
) {
246 m_snderror
= wxSOUND_INVDEV
;
250 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
253 m_internal
->m_input_enabled
= true;
256 if (mode
& wxSOUND_OUTPUT
) {
257 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
262 if (mode
& wxSOUND_INPUT
) {
263 if (!AllocHeaders(wxSOUND_INPUT
)) {
272 // -------------------------------------------------------------------------
273 // CloseDevice() closes the driver handles and frees memory allocated for
275 // -------------------------------------------------------------------------
276 void wxSoundStreamWin::CloseDevice()
278 if (m_internal
->m_output_enabled
) {
279 FreeHeaders(wxSOUND_OUTPUT
);
280 m_internal
->m_output_enabled
= false;
281 waveOutClose(m_internal
->m_devout
);
284 if (m_internal
->m_input_enabled
) {
285 FreeHeaders(wxSOUND_INPUT
);
286 m_internal
->m_input_enabled
= false;
287 waveInClose(m_internal
->m_devin
);
291 // -------------------------------------------------------------------------
292 // AllocHeader(int mode)
294 // mode has the same mean as in OpenDevice() except that here the two flags
295 // must be exclusive.
296 // AllocHeader() initializes an element of an operation (this can be input
297 // or output). It means it allocates the sound header's memory block
298 // and "prepares" it (It is needed by Windows). At the same time, it sets
299 // private datas so we can the header's owner (See callback).
301 // It returns the new allocated block or NULL.
302 // -------------------------------------------------------------------------
303 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
305 wxSoundInfoHeader
*info
;
308 // Some memory allocation
309 info
= new wxSoundInfoHeader
;
310 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
311 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
312 if (!info
->m_h_data
|| !info
->m_h_header
) {
314 m_snderror
= wxSOUND_MEMERROR
;
318 // Get the two pointers from the system
319 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
320 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
321 // Set the header's mode
323 // Set the parent of the header
324 info
->m_driver
= this;
328 header
= info
->m_header
;
329 // Initialize Windows variables
330 header
->lpData
= info
->m_data
;
331 header
->dwBufferLength
= GetBestSize();
332 header
->dwUser
= (DWORD
)info
;
333 header
->dwFlags
= WHDR_DONE
;
335 // "Prepare" the header
336 if (mode
== wxSOUND_INPUT
) {
339 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
342 if (result
!= MMSYSERR_NOERROR
) {
343 // If something goes wrong, free everything.
344 GlobalUnlock(info
->m_data
);
345 GlobalUnlock(info
->m_header
);
346 GlobalFree(info
->m_h_data
);
347 GlobalFree(info
->m_h_header
);
350 m_snderror
= wxSOUND_IOERROR
;
353 } else if (mode
== wxSOUND_OUTPUT
) {
356 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
359 if (result
!= MMSYSERR_NOERROR
) {
360 // If something goes wrong, free everything.
361 GlobalUnlock(info
->m_data
);
362 GlobalUnlock(info
->m_header
);
363 GlobalFree(info
->m_h_data
);
364 GlobalFree(info
->m_h_header
);
367 m_snderror
= wxSOUND_IOERROR
;
374 // -------------------------------------------------------------------------
375 // AllocHeaders(int mode)
377 // "mode" has the same mean as for OpenDevice() except that the two flags must
379 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
380 // queue. It uses AllocHeader() for each element.
382 // Once it has allocated all blocks, it returns true and if an error occurred
384 // -------------------------------------------------------------------------
385 bool wxSoundStreamWin::AllocHeaders(int mode
)
388 wxSoundInfoHeader
**headers
;
390 if (mode
== wxSOUND_OUTPUT
)
391 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
393 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
395 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
397 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
398 headers
[i
] = AllocHeader(mode
);
407 // -------------------------------------------------------------------------
408 // FreeHeader(int mode)
410 // "mode" has the same mean as for OpenDevice() except that the two flags must
412 // FreeHeader() frees a memory block and "unprepares" it.
413 // -------------------------------------------------------------------------
414 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
416 if (mode
== wxSOUND_OUTPUT
)
417 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
419 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
421 GlobalUnlock(header
->m_data
);
422 GlobalUnlock(header
->m_header
);
423 GlobalFree(header
->m_h_header
);
424 GlobalFree(header
->m_h_data
);
428 // -------------------------------------------------------------------------
429 // FreeHeaders(int mode)
431 // "mode" has the same mean as for OpenDevice() except that the two flags must
433 // FreeHeaders() frees all an operation queue once it has checked that
434 // all buffers have been terminated.
435 // -------------------------------------------------------------------------
436 void wxSoundStreamWin::FreeHeaders(int mode
)
439 wxSoundInfoHeader
***headers
;
441 if (mode
== wxSOUND_OUTPUT
)
442 headers
= &m_headers_play
;
444 headers
= &m_headers_rec
;
446 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
448 // We wait for the end of the buffer
449 WaitFor((*headers
)[i
]);
450 // Then, we free the header
451 FreeHeader((*headers
)[i
], mode
);
458 // -------------------------------------------------------------------------
459 // WaitFor(wxSoundInfoHeader *info)
461 // "info" is one element of an IO queue
462 // WaitFor() checks whether the specified block has been terminated.
463 // If it hasn't been terminated, it waits for its termination.
465 // NB: if it's a partially filled buffer it adds it to the Windows queue
466 // -------------------------------------------------------------------------
467 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
469 // If the buffer is finished, we return immediately
470 if (!info
->m_playing
) {
472 // We begun filling it: we must send it to the Windows queue
473 if (info
->m_position
!= 0) {
474 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
483 m_waiting_for
= true;
484 // Else, we wait for its termination
485 while (info
->m_playing
|| info
->m_recording
)
487 m_waiting_for
= false;
490 // -------------------------------------------------------------------------
491 // AddToQueue(wxSoundInfoHeader *info)
493 // For "info", see WaitFor()
494 // AddToQueue() sends the IO queue element to the Windows queue.
496 // Warning: in the current implementation, it partially assume we send the
497 // element in the right order. This is true in that implementation but if
498 // you use it elsewhere, be careful: it may shuffle all your sound datas.
499 // -------------------------------------------------------------------------
500 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
504 if (info
->m_mode
== wxSOUND_INPUT
) {
505 // Increment the input fragment pointer
506 result
= waveInAddBuffer(m_internal
->m_devin
,
507 info
->m_header
, sizeof(WAVEHDR
));
508 if (result
== MMSYSERR_NOERROR
)
509 info
->m_recording
= true;
512 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
513 result
= waveOutWrite(m_internal
->m_devout
,
514 info
->m_header
, sizeof(WAVEHDR
));
515 if (result
== MMSYSERR_NOERROR
)
516 info
->m_playing
= true;
523 // -------------------------------------------------------------------------
524 // ClearHeader(wxSoundInfoHeader *info)
526 // ClearHeader() reinitializes the parameters of "info" to their default
528 // -------------------------------------------------------------------------
529 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
531 info
->m_playing
= false;
532 info
->m_recording
= false;
533 info
->m_position
= 0;
534 info
->m_size
= GetBestSize();
537 // -------------------------------------------------------------------------
538 // wxSoundInfoHeader *NextFragmentOutput()
540 // NextFragmentOutput() looks for a free output block. It will always
541 // return you a non-NULL pointer but it may waits for an empty buffer a long
543 // -------------------------------------------------------------------------
544 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
546 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
547 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
549 if (m_headers_play
[m_current_frag_out
]->m_playing
)
550 WaitFor(m_headers_play
[m_current_frag_out
]);
552 if (m_current_frag_out
== m_output_frag_out
)
553 m_queue_filled
= true;
554 return m_headers_play
[m_current_frag_out
];
557 // -------------------------------------------------------------------------
558 // The behaviour of Write is documented in the global documentation.
559 // -------------------------------------------------------------------------
560 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
563 if (!m_internal
->m_output_enabled
) {
564 m_snderror
= wxSOUND_NOTSTARTED
;
570 wxSoundInfoHeader
*header
;
573 // Get a new output fragment
574 header
= NextFragmentOutput();
576 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
577 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
579 header
->m_position
+= to_copy
;
580 header
->m_size
-= to_copy
;
581 buffer
= (((const char *)buffer
) + to_copy
);
583 m_lastcount
+= to_copy
;
585 // If the fragment is full, we send it to the Windows queue.
586 if (header
->m_size
== 0)
587 if (!AddToQueue(header
)) {
588 m_snderror
= wxSOUND_IOERROR
;
595 // -------------------------------------------------------------------------
596 // NextFragmentInput is not functional.
597 // -------------------------------------------------------------------------
598 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
600 wxSoundInfoHeader
*header
;
602 // Queue pointer: reader
603 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
605 header
= m_headers_rec
[m_current_frag_in
];
606 // If the current buffer is in recording mode, we must wait for its
608 if (header
->m_recording
)
611 // We reached the writer position: the queue is full.
612 if (m_current_frag_in
== m_input_frag_in
)
613 m_queue_filled
= true;
618 // -------------------------------------------------------------------------
619 // The behaviour of Read is documented in the global documentation.
620 // -------------------------------------------------------------------------
621 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
623 wxSoundInfoHeader
*header
;
627 if (!m_internal
->m_input_enabled
)
631 header
= NextFragmentInput();
633 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
634 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
636 header
->m_position
+= to_copy
;
637 header
->m_size
-= to_copy
;
638 buffer
= (((char *)buffer
) + to_copy
);
640 m_lastcount
+= to_copy
;
642 if (header
->m_size
== 0) {
644 if (!AddToQueue(header
)) {
645 m_snderror
= wxSOUND_IOERROR
;
653 // -------------------------------------------------------------------------
654 // NotifyDoneBuffer(wxUint32 dev_handle)
656 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
657 // fragment finished. It reinitializes the parameters of the fragment and
658 // sends an event to the clients.
659 // -------------------------------------------------------------------------
660 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32
WXUNUSED(dev_handle
), int flag
)
662 wxSoundInfoHeader
*info
;
664 if (flag
== wxSOUND_OUTPUT
) {
665 if (!m_internal
->m_output_enabled
)
668 // Queue pointer: reader
669 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
670 info
= m_headers_play
[m_output_frag_out
];
671 // Clear header to tell the system the buffer is free now
673 m_queue_filled
= false;
675 // Try to requeue a new buffer.
676 OnSoundEvent(wxSOUND_OUTPUT
);
678 if (!m_internal
->m_input_enabled
)
681 // Recording completed
682 m_headers_rec
[m_input_frag_in
]->m_recording
= false;
683 // Queue pointer: writer
684 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
686 OnSoundEvent(wxSOUND_INPUT
);
687 m_queue_filled
= false;
691 // -------------------------------------------------------------------------
693 // -------------------------------------------------------------------------
694 bool wxSoundStreamWin::SetSoundFormat(const wxSoundFormatBase
& base
)
696 // TODO: detect best format
697 return wxSoundStream::SetSoundFormat(base
);
700 // -------------------------------------------------------------------------
702 // -------------------------------------------------------------------------
703 bool wxSoundStreamWin::StartProduction(int evt
)
708 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
709 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
712 if (!OpenDevice(evt
))
715 m_production_started
= true;
716 m_queue_filled
= false;
717 // Send a dummy event to start.
718 if (evt
& wxSOUND_OUTPUT
)
719 OnSoundEvent(wxSOUND_OUTPUT
);
721 if (evt
& wxSOUND_INPUT
) {
723 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
724 AddToQueue(m_headers_rec
[i
]);
726 waveInStart(m_internal
->m_devin
);
732 // -------------------------------------------------------------------------
734 // ------------------------------------------------------------------------
735 bool wxSoundStreamWin::StopProduction()
737 if (!m_production_started
) {
738 m_snderror
= wxSOUND_NOTSTARTED
;
742 m_snderror
= wxSOUND_NOERROR
;
743 m_production_started
= false;
748 // -------------------------------------------------------------------------
750 // -------------------------------------------------------------------------
751 bool wxSoundStreamWin::QueueFilled() const
753 return (!m_production_started
|| m_queue_filled
);
757 // --------------------------------------------------------------------------
759 // --------------------------------------------------------------------------
761 class wxSoundWinModule
: public wxModule
{
762 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
768 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
770 bool wxSoundWinModule::OnInit() {
771 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
775 void wxSoundWinModule::OnExit() {
776 delete wxSoundHandleList
;