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
)) {
102 m_snderror
= wxSOUND_NOERROR
; //next call to OpenDevice won't do this
103 if (!OpenDevice(wxSOUND_INPUT
))
110 wxSoundStreamWin::~wxSoundStreamWin()
113 if (m_production_started
)
121 // -----------------------------------------------------------------------
122 // _wxSoundHandlerWndProc: Window callback to handle buffer completion
123 // -----------------------------------------------------------------------
125 LRESULT APIENTRY _EXPORT
128 LRESULT WXDLLEXPORT APIENTRY _EXPORT
130 _wxSoundHandlerWndProc(HWND hWnd
, UINT message
,
131 WPARAM wParam
, LPARAM lParam
)
133 wxSoundStreamWin
*sndwin
;
135 sndwin
= wxFindSoundFromHandle((WXHWND
)hWnd
);
141 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
);
144 sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
);
152 // -----------------------------------------------------------------------
153 // CreateSndWindow() creates an hidden window which will receive the sound
155 // -----------------------------------------------------------------------
157 void wxSoundStreamWin::CreateSndWindow()
159 FARPROC proc
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
,
163 m_internal
->m_sndWin
= ::CreateWindow(wxCanvasClassName
, NULL
, 0,
164 0, 0, 0, 0, NULL
, (HMENU
) NULL
,
165 wxGetInstance(), NULL
);
167 error
= GetLastError();
169 ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
);
171 // Add this window to the sound handle list so we'll be able to redecode
172 // the "magic" number.
173 wxSoundHandleList
->Append((long)m_internal
->m_sndWin
, (wxObject
*)this);
176 // -----------------------------------------------------------------------
177 // DestroySndWindow() destroys the hidden window
178 // -----------------------------------------------------------------------
180 void wxSoundStreamWin::DestroySndWindow()
182 if (m_internal
->m_sndWin
) {
183 ::DestroyWindow(m_internal
->m_sndWin
);
184 wxSoundHandleList
->DeleteObject((wxObject
*)this);
188 // -------------------------------------------------------------------------
189 // OpenDevice(int mode) initializes the windows driver for a "mode"
190 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
191 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
192 // is set, then the driver is opened for input operation. The two modes
194 // The initialization parameters (sample rate, ...) are taken from the
195 // m_sndformat object.
196 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
198 // -------------------------------------------------------------------------
199 bool wxSoundStreamWin::OpenDevice(int mode
)
201 wxSoundFormatPcm
*pcm
;
202 WAVEFORMATEX wformat
;
205 m_snderror
= wxSOUND_INVFRMT
;
209 pcm
= (wxSoundFormatPcm
*)m_sndformat
;
211 wformat
.wFormatTag
= WAVE_FORMAT_PCM
;
212 wformat
.nChannels
= pcm
->GetChannels();
213 wformat
.nBlockAlign
= wformat
.nChannels
* pcm
->GetBPS() / 8;
214 wformat
.nSamplesPerSec
= pcm
->GetSampleRate();
215 wformat
.nAvgBytesPerSec
= wformat
.nSamplesPerSec
* wformat
.nBlockAlign
;
216 wformat
.wBitsPerSample
= pcm
->GetBPS();
219 // -----------------------------------
220 // Open the driver for Output operation
221 // -----------------------------------
222 if (mode
& wxSOUND_OUTPUT
) {
225 result
= waveOutOpen(&m_internal
->m_devout
,
226 WAVE_MAPPER
, &wformat
,
227 (DWORD
)m_internal
->m_sndWin
, 0,
230 if (result
!= MMSYSERR_NOERROR
) {
231 m_snderror
= wxSOUND_INVDEV
;
235 m_output_frag_out
= WXSOUND_MAX_QUEUE
-1;
236 m_current_frag_out
= 0;
238 m_internal
->m_output_enabled
= TRUE
;
240 // -----------------------------------
241 // Open the driver for Input operation
242 // -----------------------------------
243 if (mode
& wxSOUND_INPUT
) {
246 result
= waveInOpen(&m_internal
->m_devin
,
247 WAVE_MAPPER
, &wformat
,
248 (DWORD
)m_internal
->m_sndWin
, 0,
251 if (result
!= MMSYSERR_NOERROR
) {
252 m_snderror
= wxSOUND_INVDEV
;
256 m_current_frag_in
= WXSOUND_MAX_QUEUE
-1;
259 m_internal
->m_input_enabled
= TRUE
;
262 if (mode
& wxSOUND_OUTPUT
) {
263 if (!AllocHeaders(wxSOUND_OUTPUT
)) {
268 if (mode
& wxSOUND_INPUT
) {
269 if (!AllocHeaders(wxSOUND_INPUT
)) {
278 // -------------------------------------------------------------------------
279 // CloseDevice() closes the driver handles and frees memory allocated for
281 // -------------------------------------------------------------------------
282 void wxSoundStreamWin::CloseDevice()
284 if (m_internal
->m_output_enabled
) {
285 FreeHeaders(wxSOUND_OUTPUT
);
286 m_internal
->m_output_enabled
= FALSE
;
287 waveOutClose(m_internal
->m_devout
);
290 if (m_internal
->m_input_enabled
) {
291 FreeHeaders(wxSOUND_INPUT
);
292 m_internal
->m_input_enabled
= FALSE
;
293 waveInClose(m_internal
->m_devin
);
297 // -------------------------------------------------------------------------
298 // AllocHeader(int mode)
300 // mode has the same mean as in OpenDevice() except that here the two flags
301 // must be exclusive.
302 // AllocHeader() initializes an element of an operation (this can be input
303 // or output). It means it allocates the sound header's memory block
304 // and "prepares" it (It is needed by Windows). At the same time, it sets
305 // private datas so we can the header's owner (See callback).
307 // It returns the new allocated block or NULL.
308 // -------------------------------------------------------------------------
309 wxSoundInfoHeader
*wxSoundStreamWin::AllocHeader(int mode
)
311 wxSoundInfoHeader
*info
;
314 // Some memory allocation
315 info
= new wxSoundInfoHeader
;
316 info
->m_h_data
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, GetBestSize());
317 info
->m_h_header
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE
, sizeof(WAVEHDR
));
318 if (!info
->m_h_data
|| !info
->m_h_header
) {
320 m_snderror
= wxSOUND_MEMERROR
;
324 // Get the two pointers from the system
325 info
->m_data
= (char *)GlobalLock(info
->m_h_data
);
326 info
->m_header
= (WAVEHDR
*)GlobalLock(info
->m_h_header
);
327 // Set the header's mode
329 // Set the parent of the header
330 info
->m_driver
= this;
334 header
= info
->m_header
;
335 // Initialize Windows variables
336 header
->lpData
= info
->m_data
;
337 header
->dwBufferLength
= GetBestSize();
338 header
->dwUser
= (DWORD
)info
;
339 header
->dwFlags
= WHDR_DONE
;
341 // "Prepare" the header
342 if (mode
== wxSOUND_INPUT
) {
345 result
= waveInPrepareHeader(m_internal
->m_devin
, header
,
348 if (result
!= MMSYSERR_NOERROR
) {
349 // If something goes wrong, free everything.
350 GlobalUnlock(info
->m_data
);
351 GlobalUnlock(info
->m_header
);
352 GlobalFree(info
->m_h_data
);
353 GlobalFree(info
->m_h_header
);
356 m_snderror
= wxSOUND_IOERROR
;
359 } else if (mode
== wxSOUND_OUTPUT
) {
362 result
= waveOutPrepareHeader(m_internal
->m_devout
, header
,
365 if (result
!= MMSYSERR_NOERROR
) {
366 // If something goes wrong, free everything.
367 GlobalUnlock(info
->m_data
);
368 GlobalUnlock(info
->m_header
);
369 GlobalFree(info
->m_h_data
);
370 GlobalFree(info
->m_h_header
);
373 m_snderror
= wxSOUND_IOERROR
;
380 // -------------------------------------------------------------------------
381 // AllocHeaders(int mode)
383 // "mode" has the same mean as for OpenDevice() except that the two flags must
385 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
386 // queue. It uses AllocHeader() for each element.
388 // Once it has allocated all blocks, it returns TRUE and if an error occured
390 // -------------------------------------------------------------------------
391 bool wxSoundStreamWin::AllocHeaders(int mode
)
394 wxSoundInfoHeader
**headers
;
396 if (mode
== wxSOUND_OUTPUT
)
397 headers
= m_headers_play
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
399 headers
= m_headers_rec
= new wxSoundInfoHeader
*[WXSOUND_MAX_QUEUE
];
401 memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader
*));
403 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
404 headers
[i
] = AllocHeader(mode
);
413 // -------------------------------------------------------------------------
414 // FreeHeader(int mode)
416 // "mode" has the same mean as for OpenDevice() except that the two flags must
418 // FreeHeader() frees a memory block and "unprepares" it.
419 // -------------------------------------------------------------------------
420 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader
*header
, int mode
)
422 if (mode
== wxSOUND_OUTPUT
)
423 waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
));
425 waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
));
427 GlobalUnlock(header
->m_data
);
428 GlobalUnlock(header
->m_header
);
429 GlobalFree(header
->m_h_header
);
430 GlobalFree(header
->m_h_data
);
434 // -------------------------------------------------------------------------
435 // FreeHeaders(int mode)
437 // "mode" has the same mean as for OpenDevice() except that the two flags must
439 // FreeHeaders() frees all an operation queue once it has checked that
440 // all buffers have been terminated.
441 // -------------------------------------------------------------------------
442 void wxSoundStreamWin::FreeHeaders(int mode
)
445 wxSoundInfoHeader
***headers
;
447 if (mode
== wxSOUND_OUTPUT
)
448 headers
= &m_headers_play
;
450 headers
= &m_headers_rec
;
452 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) {
454 // We wait for the end of the buffer
455 WaitFor((*headers
)[i
]);
456 // Then, we free the header
457 FreeHeader((*headers
)[i
], mode
);
464 // -------------------------------------------------------------------------
465 // WaitFor(wxSoundInfoHeader *info)
467 // "info" is one element of an IO queue
468 // WaitFor() checks whether the specified block has been terminated.
469 // If it hasn't been terminated, it waits for its termination.
471 // NB: if it's a partially filled buffer it adds it to the Windows queue
472 // -------------------------------------------------------------------------
473 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader
*info
)
475 // If the buffer is finished, we return immediately
476 if (!info
->m_playing
) {
478 // We begun filling it: we must send it to the Windows queue
479 if (info
->m_position
!= 0) {
480 memset(info
->m_data
+ info
->m_position
, 0, info
->m_size
);
489 m_waiting_for
= TRUE
;
490 // Else, we wait for its termination
491 while (info
->m_playing
|| info
->m_recording
)
493 m_waiting_for
= FALSE
;
496 // -------------------------------------------------------------------------
497 // AddToQueue(wxSoundInfoHeader *info)
499 // For "info", see WaitFor()
500 // AddToQueue() sends the IO queue element to the Windows queue.
502 // Warning: in the current implementation, it partially assume we send the
503 // element in the right order. This is true in that implementation but if
504 // you use it elsewhere, be careful: it may shuffle all your sound datas.
505 // -------------------------------------------------------------------------
506 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader
*info
)
510 if (info
->m_mode
== wxSOUND_INPUT
) {
511 // Increment the input fragment pointer
512 result
= waveInAddBuffer(m_internal
->m_devin
,
513 info
->m_header
, sizeof(WAVEHDR
));
514 if (result
== MMSYSERR_NOERROR
)
515 info
->m_recording
= TRUE
;
518 } else if (info
->m_mode
== wxSOUND_OUTPUT
) {
519 result
= waveOutWrite(m_internal
->m_devout
,
520 info
->m_header
, sizeof(WAVEHDR
));
521 if (result
== MMSYSERR_NOERROR
)
522 info
->m_playing
= TRUE
;
529 // -------------------------------------------------------------------------
530 // ClearHeader(wxSoundInfoHeader *info)
532 // ClearHeader() reinitializes the parameters of "info" to their default
534 // -------------------------------------------------------------------------
535 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader
*info
)
537 info
->m_playing
= FALSE
;
538 info
->m_recording
= FALSE
;
539 info
->m_position
= 0;
540 info
->m_size
= GetBestSize();
543 // -------------------------------------------------------------------------
544 // wxSoundInfoHeader *NextFragmentOutput()
546 // NextFragmentOutput() looks for a free output block. It will always
547 // return you a non-NULL pointer but it may waits for an empty buffer a long
549 // -------------------------------------------------------------------------
550 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentOutput()
552 if (m_headers_play
[m_current_frag_out
]->m_playing
) {
553 m_current_frag_out
= (m_current_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
555 if (m_headers_play
[m_current_frag_out
]->m_playing
)
556 WaitFor(m_headers_play
[m_current_frag_out
]);
558 if (m_current_frag_out
== m_output_frag_out
)
559 m_queue_filled
= TRUE
;
560 return m_headers_play
[m_current_frag_out
];
563 // -------------------------------------------------------------------------
564 // The behaviour of Write is documented in the global documentation.
565 // -------------------------------------------------------------------------
566 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
)
569 if (!m_internal
->m_output_enabled
) {
570 m_snderror
= wxSOUND_NOTSTARTED
;
576 wxSoundInfoHeader
*header
;
579 // Get a new output fragment
580 header
= NextFragmentOutput();
582 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
583 memcpy(header
->m_data
+ header
->m_position
, buffer
, to_copy
);
585 header
->m_position
+= to_copy
;
586 header
->m_size
-= to_copy
;
587 buffer
= (((const char *)buffer
) + to_copy
);
589 m_lastcount
+= to_copy
;
591 // If the fragment is full, we send it to the Windows queue.
592 if (header
->m_size
== 0)
593 if (!AddToQueue(header
)) {
594 m_snderror
= wxSOUND_IOERROR
;
601 // -------------------------------------------------------------------------
602 // NextFragmentInput is not functional.
603 // -------------------------------------------------------------------------
604 wxSoundInfoHeader
*wxSoundStreamWin::NextFragmentInput()
606 wxSoundInfoHeader
*header
;
608 // Queue pointer: reader
609 m_current_frag_in
= (m_current_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
611 header
= m_headers_rec
[m_current_frag_in
];
612 // If the current buffer is in recording mode, we must wait for its
614 if (header
->m_recording
)
617 // We reached the writer position: the queue is full.
618 if (m_current_frag_in
== m_input_frag_in
)
619 m_queue_filled
= TRUE
;
624 // -------------------------------------------------------------------------
625 // The behaviour of Read is documented in the global documentation.
626 // -------------------------------------------------------------------------
627 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
)
629 wxSoundInfoHeader
*header
;
633 if (!m_internal
->m_input_enabled
)
637 header
= NextFragmentInput();
639 to_copy
= (len
> header
->m_size
) ? header
->m_size
: len
;
640 memcpy(buffer
, header
->m_data
+ header
->m_position
, to_copy
);
642 header
->m_position
+= to_copy
;
643 header
->m_size
-= to_copy
;
644 buffer
= (((char *)buffer
) + to_copy
);
646 m_lastcount
+= to_copy
;
648 if (header
->m_size
== 0) {
650 if (!AddToQueue(header
)) {
651 m_snderror
= wxSOUND_IOERROR
;
659 // -------------------------------------------------------------------------
660 // NotifyDoneBuffer(wxUint32 dev_handle)
662 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
663 // fragment finished. It reinitializes the parameters of the fragment and
664 // sends an event to the clients.
665 // -------------------------------------------------------------------------
666 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle
, int flag
)
668 wxSoundInfoHeader
*info
;
670 if (flag
== wxSOUND_OUTPUT
) {
671 if (!m_internal
->m_output_enabled
)
674 // Queue pointer: reader
675 m_output_frag_out
= (m_output_frag_out
+ 1) % WXSOUND_MAX_QUEUE
;
676 info
= m_headers_play
[m_output_frag_out
];
677 // Clear header to tell the system the buffer is free now
679 m_queue_filled
= FALSE
;
681 // Try to requeue a new buffer.
682 OnSoundEvent(wxSOUND_OUTPUT
);
684 if (!m_internal
->m_input_enabled
)
687 // Recording completed
688 m_headers_rec
[m_input_frag_in
]->m_recording
= FALSE
;
689 // Queue pointer: writer
690 m_input_frag_in
= (m_input_frag_in
+ 1) % WXSOUND_MAX_QUEUE
;
692 OnSoundEvent(wxSOUND_INPUT
);
693 m_queue_filled
= FALSE
;
697 // -------------------------------------------------------------------------
699 // -------------------------------------------------------------------------
700 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase
& base
)
702 // TODO: detect best format
703 return wxSoundStream::SetSoundFormat(base
);
706 // -------------------------------------------------------------------------
708 // -------------------------------------------------------------------------
709 bool wxSoundStreamWin::StartProduction(int evt
)
714 if ((m_internal
->m_output_enabled
&& (evt
& wxSOUND_OUTPUT
)) ||
715 (m_internal
->m_input_enabled
&& (evt
& wxSOUND_INPUT
)))
718 if (!OpenDevice(evt
))
721 m_production_started
= TRUE
;
722 m_queue_filled
= FALSE
;
723 // Send a dummy event to start.
724 if (evt
& wxSOUND_OUTPUT
)
725 OnSoundEvent(wxSOUND_OUTPUT
);
727 if (evt
& wxSOUND_INPUT
) {
729 for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++)
730 AddToQueue(m_headers_rec
[i
]);
732 waveInStart(m_internal
->m_devin
);
738 // -------------------------------------------------------------------------
740 // ------------------------------------------------------------------------
741 bool wxSoundStreamWin::StopProduction()
743 if (!m_production_started
) {
744 m_snderror
= wxSOUND_NOTSTARTED
;
748 m_snderror
= wxSOUND_NOERROR
;
749 m_production_started
= FALSE
;
754 // -------------------------------------------------------------------------
756 // -------------------------------------------------------------------------
757 bool wxSoundStreamWin::QueueFilled() const
759 return (!m_production_started
|| m_queue_filled
);
763 // --------------------------------------------------------------------------
765 // --------------------------------------------------------------------------
767 class WXDLLEXPORT wxSoundWinModule
: public wxModule
{
768 DECLARE_DYNAMIC_CLASS(wxSoundWinModule
)
774 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
)
776 bool wxSoundWinModule::OnInit() {
777 wxSoundHandleList
= new wxList(wxKEY_INTEGER
);
781 void wxSoundWinModule::OnExit() {
782 delete wxSoundHandleList
;