]> git.saurik.com Git - wxWidgets.git/blob - utils/wxMMedia2/lib/sndwin.cpp
Small change to SetFont() logic.
[wxWidgets.git] / utils / wxMMedia2 / lib / sndwin.cpp
1 // --------------------------------------------------------------------------
2 // Name: sndwin.cpp
3 // Purpose:
4 // Date: 08/11/1999
5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
6 // CVSID: $Id$
7 // --------------------------------------------------------------------------
8 #include <wx/wxprec.h>
9
10 #include <wx/app.h>
11 #include <wx/module.h>
12 #include <wx/msw/private.h>
13 #include <string.h>
14 #include "sndbase.h"
15 #include "sndwin.h"
16 #include "sndpcm.h"
17
18 #include <windows.h>
19 #include <mmsystem.h>
20
21 typedef struct _wxSoundInternal wxSoundInternal;
22 typedef struct _wxSoundInfoHeader wxSoundInfoHeader;
23
24 extern char wxCanvasClassName[];
25
26 wxList *wxSoundHandleList = NULL;
27
28 static inline wxSoundStreamWin *wxFindSoundFromHandle(WXHWND hWnd)
29 {
30 wxNode *node = wxSoundHandleList->Find((long)hWnd);
31 if (!node)
32 return NULL;
33 return (wxSoundStreamWin *)node->Data();
34 }
35
36 struct _wxSoundInternal {
37 HWND m_sndWin;
38 HWAVEIN m_devin;
39 HWAVEOUT m_devout;
40 bool m_output_enabled, m_input_enabled;
41 };
42
43 struct _wxSoundInfoHeader {
44 HGLOBAL m_h_header, m_h_data;
45 char *m_data;
46 WAVEHDR *m_header;
47 int m_mode;
48 bool m_playing, m_recording;
49 wxUint32 m_position, m_size;
50
51 wxSoundStreamWin *m_driver;
52 };
53
54 #define WXSOUND_MAX_QUEUE 10
55
56 wxSoundStreamWin::wxSoundStreamWin()
57 {
58 wxSoundFormatPcm pcm;
59
60 m_production_started = FALSE;
61 m_internal = new wxSoundInternal;
62 m_snderror = wxSOUND_NOERR;
63
64 // Setup defaults
65 CreateSndWindow();
66 SetSoundFormat(pcm);
67
68 if (!OpenDevice(wxSOUND_OUTPUT))
69 return;
70
71 CloseDevice();
72 }
73
74 wxSoundStreamWin::~wxSoundStreamWin()
75 {
76 if (m_production_started)
77 StopProduction();
78 DestroySndWindow();
79
80 delete m_internal;
81 }
82
83 LRESULT APIENTRY _EXPORT _wxSoundHandlerWndProc(HWND hWnd, UINT message,
84 WPARAM wParam, LPARAM lParam)
85 {
86 switch (message) {
87 case MM_WOM_DONE:
88 wxFindSoundFromHandle((WXHWND)hWnd)->NotifyDoneBuffer(wParam, wxSOUND_OUTPUT);
89 break;
90 case MM_WIM_DATA:
91 wxFindSoundFromHandle((WXHWND)hWnd)->NotifyDoneBuffer(wParam, wxSOUND_INPUT);
92 break;
93 default:
94 break;
95 }
96 return (LRESULT)0;
97 }
98
99 // -----------------------------------------------------------------------
100 // CreateSndWindow() creates an hidden window which will receive the sound
101 // events
102 // -----------------------------------------------------------------------
103
104 void wxSoundStreamWin::CreateSndWindow()
105 {
106 FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc,
107 wxGetInstance());
108 int error;
109
110 m_internal->m_sndWin = ::CreateWindow(wxCanvasClassName, NULL, 0,
111 0, 0, 0, 0, NULL, (HMENU) NULL,
112 wxGetInstance(), NULL);
113
114 error = GetLastError();
115
116 ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
117
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);
121 }
122
123 // -----------------------------------------------------------------------
124 // DestroySndWindow() destroys the hidden window
125 // -----------------------------------------------------------------------
126
127 void wxSoundStreamWin::DestroySndWindow()
128 {
129 if (m_internal->m_sndWin) {
130 ::DestroyWindow(m_internal->m_sndWin);
131 wxSoundHandleList->DeleteObject((wxObject *)this);
132 }
133 }
134
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
140 // aren't exclusive.
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
144 // queue.
145 // -------------------------------------------------------------------------
146 bool wxSoundStreamWin::OpenDevice(int mode)
147 {
148 wxSoundFormatPcm *pcm;
149 WAVEFORMATEX wformat;
150
151 if (!m_sndformat) {
152 m_snderror = wxSOUND_INVFRMT;
153 return FALSE;
154 }
155
156 pcm = (wxSoundFormatPcm *)m_sndformat;
157
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();
164 wformat.cbSize = 0;
165
166 // -----------------------------------
167 // Open the driver for Output operation
168 // -----------------------------------
169 if (mode & wxSOUND_OUTPUT) {
170 MMRESULT result;
171
172 result = waveOutOpen(&m_internal->m_devout,
173 WAVE_MAPPER, &wformat,
174 (DWORD)m_internal->m_sndWin, 0,
175 CALLBACK_WINDOW);
176
177 if (result != MMSYSERR_NOERROR) {
178 m_snderror = wxSOUND_INVDEV;
179 return FALSE;
180 }
181
182 m_output_frag_out = WXSOUND_MAX_QUEUE-1;
183 m_current_frag_out = 0;
184
185 m_internal->m_output_enabled = TRUE;
186 }
187 // -----------------------------------
188 // Open the driver for Input operation
189 // -----------------------------------
190 if (mode & wxSOUND_INPUT) {
191 MMRESULT result;
192
193 result = waveInOpen(&m_internal->m_devin,
194 WAVE_MAPPER, &wformat,
195 (DWORD)m_internal->m_sndWin, 0,
196 CALLBACK_WINDOW);
197
198 if (result != MMSYSERR_NOERROR) {
199 m_snderror = wxSOUND_INVDEV;
200 return FALSE;
201 }
202
203 m_current_frag_in = WXSOUND_MAX_QUEUE-1;
204 m_input_frag_in = 0;
205
206 m_internal->m_input_enabled = TRUE;
207 }
208
209 if (mode & wxSOUND_OUTPUT) {
210 if (!AllocHeaders(wxSOUND_OUTPUT)) {
211 CloseDevice();
212 return FALSE;
213 }
214 }
215 if (mode & wxSOUND_INPUT) {
216 if (!AllocHeaders(wxSOUND_INPUT)) {
217 CloseDevice();
218 return FALSE;
219 }
220 }
221
222 return TRUE;
223 }
224
225 // -------------------------------------------------------------------------
226 // CloseDevice() closes the driver handles and frees memory allocated for
227 // IO queues.
228 // -------------------------------------------------------------------------
229 void wxSoundStreamWin::CloseDevice()
230 {
231 m_internal->m_output_enabled = FALSE;
232 m_internal->m_input_enabled = FALSE;
233
234 if (m_internal->m_output_enabled) {
235 waveOutReset(m_internal->m_devout);
236 FreeHeaders(wxSOUND_OUTPUT);
237 waveOutClose(m_internal->m_devout);
238 }
239
240 if (m_internal->m_input_enabled) {
241 waveInReset(m_internal->m_devin);
242 FreeHeaders(wxSOUND_INPUT);
243 waveInClose(m_internal->m_devin);
244 }
245 }
246
247 // -------------------------------------------------------------------------
248 // AllocHeader(int mode)
249 //
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).
256 //
257 // It returns the new allocated block or NULL.
258 // -------------------------------------------------------------------------
259 wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
260 {
261 wxSoundInfoHeader *info;
262 WAVEHDR *header;
263
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) {
269 delete info;
270 m_snderror = wxSOUND_MEMERR;
271 return NULL;
272 }
273
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
278 info->m_mode = mode;
279 // Set the parent of the header
280 info->m_driver = this;
281 // Clean it up
282 ClearHeader(info);
283
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;
290
291
292 // "Prepare" the header
293 if (mode == wxSOUND_INPUT) {
294 MMRESULT result;
295
296 result = waveInPrepareHeader(m_internal->m_devin, header,
297 sizeof(WAVEHDR));
298
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);
305 delete info;
306
307 m_snderror = wxSOUND_IOERR;
308 return NULL;
309 }
310 } else if (mode == wxSOUND_OUTPUT) {
311 MMRESULT result;
312
313 result = waveOutPrepareHeader(m_internal->m_devout, header,
314 sizeof(WAVEHDR));
315
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);
322 delete info;
323
324 m_snderror = wxSOUND_IOERR;
325 return NULL;
326 }
327 }
328 return info;
329 }
330
331 // -------------------------------------------------------------------------
332 // AllocHeaders(int mode)
333 //
334 // "mode" has the same mean as for OpenDevice() except that the two flags must
335 // be exclusive.
336 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
337 // queue. It uses AllocHeader() for each element.
338 //
339 // Once it has allocated all blocks, it returns TRUE and if an error occured
340 // it returns FALSE.
341 // -------------------------------------------------------------------------
342 bool wxSoundStreamWin::AllocHeaders(int mode)
343 {
344 int i;
345 wxSoundInfoHeader **headers;
346
347 if (mode == wxSOUND_OUTPUT)
348 headers = m_headers_play = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
349 else
350 headers = m_headers_rec = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
351
352 memset(headers, 0, WXSOUND_MAX_QUEUE*sizeof(wxSoundInfoHeader *));
353
354 for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
355 headers[i] = AllocHeader(mode);
356 if (!headers[i]) {
357 FreeHeaders(mode);
358 return FALSE;
359 }
360 }
361 return TRUE;
362 }
363
364 // -------------------------------------------------------------------------
365 // FreeHeader(int mode)
366 //
367 // "mode" has the same mean as for OpenDevice() except that the two flags must
368 // be exclusive.
369 // FreeHeader() frees a memory block and "unprepares" it.
370 // -------------------------------------------------------------------------
371 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
372 {
373 if (mode == wxSOUND_OUTPUT)
374 waveOutUnprepareHeader(m_internal->m_devout, header->m_header, sizeof(WAVEHDR));
375 else
376 waveInUnprepareHeader(m_internal->m_devin, header->m_header, sizeof(WAVEHDR));
377
378 GlobalUnlock(header->m_data);
379 GlobalUnlock(header->m_header);
380 GlobalFree(header->m_h_header);
381 GlobalFree(header->m_h_data);
382 delete header;
383 }
384
385 // -------------------------------------------------------------------------
386 // FreeHeaders(int mode)
387 //
388 // "mode" has the same mean as for OpenDevice() except that the two flags must
389 // be exclusive.
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)
394 {
395 int i;
396 wxSoundInfoHeader ***headers;
397
398 if (mode == wxSOUND_OUTPUT)
399 headers = &m_headers_play;
400 else
401 headers = &m_headers_rec;
402
403 for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
404 if ((*headers)[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);
409 }
410 }
411 delete[] (*headers);
412 (*headers) = NULL;
413 }
414
415 // -------------------------------------------------------------------------
416 // WaitFor(wxSoundInfoHeader *info)
417 //
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.
421 //
422 // NB: if it's a partially filled buffer it adds it to the Windows queue
423 // -------------------------------------------------------------------------
424 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
425 {
426 // If the buffer is finished, we return immediately
427 if (!info->m_playing) {
428
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);
432 AddToQueue(info);
433 }
434
435 return;
436 }
437
438 // Else, we wait for its termination
439 while (info->m_playing || info->m_recording)
440 wxYield();
441 }
442
443 // -------------------------------------------------------------------------
444 // AddToQueue(wxSoundInfoHeader *info)
445 //
446 // For "info", see WaitFor()
447 // AddToQueue() sends the IO queue element to the Windows queue.
448 //
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)
454 {
455 MMRESULT result;
456
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;
463 else
464 return FALSE;
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;
470 else
471 return FALSE;
472 }
473 return TRUE;
474 }
475
476 // -------------------------------------------------------------------------
477 // ClearHeader(wxSoundInfoHeader *info)
478 //
479 // ClearHeader() reinitializes the parameters of "info" to their default
480 // value.
481 // -------------------------------------------------------------------------
482 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
483 {
484 info->m_playing = FALSE;
485 info->m_recording = FALSE;
486 info->m_position = 0;
487 info->m_size = GetBestSize();
488 }
489
490 // -------------------------------------------------------------------------
491 // wxSoundInfoHeader *NextFragmentOutput()
492 //
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
495 // time.
496 // -------------------------------------------------------------------------
497 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
498 {
499 if (m_headers_play[m_current_frag_out]->m_playing) {
500 m_current_frag_out = (m_current_frag_out + 1) % WXSOUND_MAX_QUEUE;
501
502 if (m_headers_play[m_current_frag_out]->m_playing)
503 WaitFor(m_headers_play[m_current_frag_out]);
504 }
505 if (m_current_frag_out == m_output_frag_out)
506 m_queue_filled = TRUE;
507 return m_headers_play[m_current_frag_out];
508 }
509
510 // -------------------------------------------------------------------------
511 // The behaviour of Write is documented in the global documentation.
512 // -------------------------------------------------------------------------
513 wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
514 {
515 m_lastcount = 0;
516 if (!m_internal->m_output_enabled)
517 return *this;
518
519 while (len > 0) {
520 wxSoundInfoHeader *header;
521 wxUint32 to_copy;
522
523 // Get a new output fragment
524 header = NextFragmentOutput();
525
526 to_copy = (len > header->m_size) ? header->m_size : len;
527 memcpy(header->m_data + header->m_position, buffer, to_copy);
528
529 header->m_position += to_copy;
530 header->m_size -= to_copy;
531 buffer = (((const char *)buffer) + to_copy);
532 len -= to_copy;
533 m_lastcount += to_copy;
534
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;
539 return *this;
540 }
541 }
542 return *this;
543 }
544
545 // -------------------------------------------------------------------------
546 // NextFragmentInput is not functional.
547 // -------------------------------------------------------------------------
548 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
549 {
550 wxSoundInfoHeader *header;
551
552 header = m_headers_rec[m_current_frag_in];
553 if (header->m_recording)
554 WaitFor(header);
555
556 m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE;
557
558 if (m_current_frag_in == m_input_frag_in)
559 m_queue_filled = TRUE;
560
561 return header;
562 }
563
564 // -------------------------------------------------------------------------
565 // The behaviour of Read is documented in the global documentation.
566 // -------------------------------------------------------------------------
567 wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
568 {
569 wxSoundInfoHeader *header;
570 wxUint32 to_copy;
571
572 m_lastcount = 0;
573 if (!m_internal->m_input_enabled)
574 return *this;
575
576 while (len > 0) {
577 header = NextFragmentInput();
578
579 to_copy = (len > header->m_size) ? header->m_size : len;
580 memcpy(buffer, header->m_data + header->m_position, to_copy);
581
582 header->m_position += to_copy;
583 header->m_size -= to_copy;
584 buffer = (((char *)buffer) + to_copy);
585 len -= to_copy;
586 m_lastcount += to_copy;
587
588 if (header->m_size == 0) {
589 ClearHeader(header);
590 if (!AddToQueue(header)) {
591 m_snderror = wxSOUND_IOERR;
592 return *this;
593 }
594 }
595 }
596 return *this;
597 }
598
599 // -------------------------------------------------------------------------
600 // NotifyDoneBuffer(wxUint32 dev_handle)
601 //
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)
607 {
608 wxSoundInfoHeader *info;
609
610 if (flag == wxSOUND_OUTPUT) {
611 if (!m_internal->m_output_enabled)
612 return;
613
614 m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE;
615 info = m_headers_play[m_output_frag_out];
616 ClearHeader(info);
617 m_queue_filled = FALSE;
618 OnSoundEvent(wxSOUND_OUTPUT);
619 } else {
620 if (!m_internal->m_input_enabled)
621 return;
622
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;
627 }
628 }
629
630 // -------------------------------------------------------------------------
631 // -------------------------------------------------------------------------
632 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
633 {
634 return wxSoundStream::SetSoundFormat(base);
635 }
636
637 // -------------------------------------------------------------------------
638 // -------------------------------------------------------------------------
639 bool wxSoundStreamWin::StartProduction(int evt)
640 {
641 if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) ||
642 (m_internal->m_input_enabled && (evt & wxSOUND_INPUT)))
643 CloseDevice();
644
645 if (!OpenDevice(evt))
646 return FALSE;
647
648 m_production_started = TRUE;
649 m_queue_filled = FALSE;
650 // Send a dummy event to start.
651 if (evt & wxSOUND_OUTPUT)
652 OnSoundEvent(evt);
653
654 if (evt & wxSOUND_INPUT) {
655 int i;
656 for (i=0;i<WXSOUND_MAX_QUEUE;i++)
657 AddToQueue(m_headers_rec[i]);
658
659 waveInStart(m_internal->m_devin);
660 }
661
662 return TRUE;
663 }
664
665 // -------------------------------------------------------------------------
666 // -------------------------------------------------------------------------
667 bool wxSoundStreamWin::StopProduction()
668 {
669 if (m_internal->m_input_enabled)
670 waveInStop(m_internal->m_devin);
671
672 m_production_started = FALSE;
673 CloseDevice();
674 return TRUE;
675 }
676
677 // -------------------------------------------------------------------------
678 // -------------------------------------------------------------------------
679 bool wxSoundStreamWin::QueueFilled() const
680 {
681 return (!m_production_started || m_queue_filled);
682 }
683
684
685 // --------------------------------------------------------------------------
686 // wxSoundWinModule
687 // --------------------------------------------------------------------------
688
689 class WXDLLEXPORT wxSoundWinModule : public wxModule {
690 DECLARE_DYNAMIC_CLASS(wxSoundWinModule)
691 public:
692 bool OnInit();
693 void OnExit();
694 };
695
696 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule, wxModule)
697
698 bool wxSoundWinModule::OnInit() {
699 wxSoundHandleList = new wxList(wxKEY_INTEGER);
700 return TRUE;
701 }
702
703 void wxSoundWinModule::OnExit() {
704 delete wxSoundHandleList;
705 }