Added makefile.g95
[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 128
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);
89 break;
90 }
91 default:
92 break;
93 }
94 return (LRESULT)0;
95 }
96
97 // -----------------------------------------------------------------------
98 // CreateSndWindow() creates an hidden window which will receive the sound
99 // events
100 // -----------------------------------------------------------------------
101
102 void wxSoundStreamWin::CreateSndWindow()
103 {
104 FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc,
105 wxGetInstance());
106 int error;
107
108 m_internal->m_sndWin = ::CreateWindow(wxCanvasClassName, NULL, 0,
109 0, 0, 0, 0, NULL, (HMENU) NULL,
110 wxGetInstance(), NULL);
111
112 error = GetLastError();
113
114 ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
115
116 // Add this window to the sound handle list so we'll be able to redecode
117 // the "magic" number.
118 wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this);
119 }
120
121 // -----------------------------------------------------------------------
122 // DestroySndWindow() destroys the hidden window
123 // -----------------------------------------------------------------------
124
125 void wxSoundStreamWin::DestroySndWindow()
126 {
127 if (m_internal->m_sndWin) {
128 ::DestroyWindow(m_internal->m_sndWin);
129 wxSoundHandleList->DeleteObject((wxObject *)this);
130 }
131 }
132
133 // -------------------------------------------------------------------------
134 // OpenDevice(int mode) initializes the windows driver for a "mode"
135 // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
136 // the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
137 // is set, then the driver is opened for input operation. The two modes
138 // aren't exclusive.
139 // The initialization parameters (sample rate, ...) are taken from the
140 // m_sndformat object.
141 // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
142 // queue.
143 // -------------------------------------------------------------------------
144 bool wxSoundStreamWin::OpenDevice(int mode)
145 {
146 wxSoundFormatPcm *pcm;
147 WAVEFORMATEX wformat;
148
149 if (!m_sndformat) {
150 m_snderror = wxSOUND_INVFRMT;
151 return FALSE;
152 }
153
154 pcm = (wxSoundFormatPcm *)m_sndformat;
155
156 wformat.wFormatTag = WAVE_FORMAT_PCM;
157 wformat.nChannels = pcm->GetChannels();
158 wformat.nBlockAlign = pcm->GetBPS() / 8 * wformat.nChannels;
159 wformat.nAvgBytesPerSec = pcm->GetBytesFromTime(1);
160 wformat.nSamplesPerSec = pcm->GetSampleRate();
161 wformat.wBitsPerSample = pcm->GetBPS();
162 wformat.cbSize = 0;
163
164 // -----------------------------------
165 // Open the driver for Output operation
166 // -----------------------------------
167 if (mode & wxSOUND_OUTPUT) {
168 MMRESULT result;
169
170 result = waveOutOpen(&m_internal->m_devout,
171 WAVE_MAPPER, &wformat,
172 (DWORD)m_internal->m_sndWin, 0,
173 CALLBACK_WINDOW);
174
175 if (result != MMSYSERR_NOERROR) {
176 m_snderror = wxSOUND_INVDEV;
177 return FALSE;
178 }
179
180 m_output_frag_out = WXSOUND_MAX_QUEUE-1;
181 m_current_frag_out = 0;
182
183 m_internal->m_output_enabled = TRUE;
184 }
185 // -----------------------------------
186 // Open the driver for Input operation
187 // -----------------------------------
188 if (mode & wxSOUND_INPUT) {
189 MMRESULT result;
190
191 result = waveInOpen(&m_internal->m_devin,
192 WAVE_MAPPER, &wformat,
193 (DWORD)m_internal->m_sndWin, 0,
194 CALLBACK_WINDOW);
195
196 if (result != MMSYSERR_NOERROR) {
197 m_snderror = wxSOUND_INVDEV;
198 return FALSE;
199 }
200
201 m_input_frag_in = WXSOUND_MAX_QUEUE-1;
202 m_current_frag_in = 0;
203
204 m_internal->m_input_enabled = TRUE;
205 }
206
207 if (!AllocHeaders(mode)) {
208 CloseDevice();
209 return FALSE;
210 }
211 return TRUE;
212 }
213
214 // -------------------------------------------------------------------------
215 // CloseDevice() closes the driver handles and frees memory allocated for
216 // IO queues.
217 // -------------------------------------------------------------------------
218 void wxSoundStreamWin::CloseDevice()
219 {
220 if (m_internal->m_output_enabled) {
221 FreeHeaders(wxSOUND_OUTPUT);
222 waveOutReset(m_internal->m_devout);
223 waveOutClose(m_internal->m_devout);
224 }
225
226 if (m_internal->m_input_enabled) {
227 FreeHeaders(wxSOUND_INPUT);
228 waveInReset(m_internal->m_devin);
229 waveInClose(m_internal->m_devin);
230 }
231
232 m_internal->m_output_enabled = FALSE;
233 m_internal->m_input_enabled = FALSE;
234 }
235
236 // -------------------------------------------------------------------------
237 // AllocHeader(int mode)
238 //
239 // mode has the same mean as in OpenDevice() except that here the two flags
240 // must be exclusive.
241 // AllocHeader() initializes an element of an operation (this can be input
242 // or output). It means it allocates the sound header's memory block
243 // and "prepares" it (It is needed by Windows). At the same time, it sets
244 // private datas so we can the header's owner (See callback).
245 //
246 // It returns the new allocated block or NULL.
247 // -------------------------------------------------------------------------
248 wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
249 {
250 wxSoundInfoHeader *info;
251 WAVEHDR *header;
252
253 // Some memory allocation
254 info = new wxSoundInfoHeader;
255 info->m_h_data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize());
256 info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
257 if (!info->m_h_data || !info->m_h_header) {
258 delete info;
259 m_snderror = wxSOUND_MEMERR;
260 return NULL;
261 }
262
263 // Get the two pointers from the system
264 info->m_data = (char *)GlobalLock(info->m_h_data);
265 info->m_header = (WAVEHDR *)GlobalLock(info->m_h_header);
266 // Set the header's mode
267 info->m_mode = mode;
268 // Set the parent of the header
269 info->m_driver = this;
270 // Clean it up
271 ClearHeader(info);
272
273 header = info->m_header;
274 // Initialize Windows variables
275 header->lpData = info->m_data;
276 header->dwBufferLength = GetBestSize();
277 header->dwUser = (DWORD)info;
278 header->dwFlags = WHDR_DONE;
279
280
281 // "Prepare" the header
282 if (mode == wxSOUND_INPUT) {
283 MMRESULT result;
284
285 result = waveInPrepareHeader(m_internal->m_devin, header,
286 sizeof(WAVEHDR));
287
288 if (result != MMSYSERR_NOERROR) {
289 // If something goes wrong, free everything.
290 GlobalUnlock(info->m_data);
291 GlobalUnlock(info->m_header);
292 GlobalFree(info->m_h_data);
293 GlobalFree(info->m_h_header);
294 delete info;
295
296 m_snderror = wxSOUND_IOERR;
297 return NULL;
298 }
299 } else if (mode == wxSOUND_OUTPUT) {
300 MMRESULT result;
301
302 result = waveOutPrepareHeader(m_internal->m_devout, header,
303 sizeof(WAVEHDR));
304
305 if (result != MMSYSERR_NOERROR) {
306 // If something goes wrong, free everything.
307 GlobalUnlock(info->m_data);
308 GlobalUnlock(info->m_header);
309 GlobalFree(info->m_h_data);
310 GlobalFree(info->m_h_header);
311 delete info;
312
313 m_snderror = wxSOUND_IOERR;
314 return NULL;
315 }
316 }
317 return info;
318 }
319
320 // -------------------------------------------------------------------------
321 // AllocHeaders(int mode)
322 //
323 // "mode" has the same mean as for OpenDevice() except that the two flags must
324 // be exclusive.
325 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
326 // queue. It uses AllocHeader() for each element.
327 //
328 // Once it has allocated all blocks, it returns TRUE and if an error occured
329 // it returns FALSE.
330 // -------------------------------------------------------------------------
331 bool wxSoundStreamWin::AllocHeaders(int mode)
332 {
333 int i;
334 wxSoundInfoHeader **headers;
335
336 if (mode == wxSOUND_OUTPUT)
337 headers = m_headers_play = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
338 else
339 headers = m_headers_rec = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
340
341 memset(headers, 0, WXSOUND_MAX_QUEUE*sizeof(wxSoundInfoHeader *));
342
343 for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
344 headers[i] = AllocHeader(mode);
345 if (!headers[i]) {
346 FreeHeaders(mode);
347 return FALSE;
348 }
349 }
350 return TRUE;
351 }
352
353 // -------------------------------------------------------------------------
354 // FreeHeader(int mode)
355 //
356 // "mode" has the same mean as for OpenDevice() except that the two flags must
357 // be exclusive.
358 // FreeHeader() frees a memory block and "unprepares" it.
359 // -------------------------------------------------------------------------
360 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
361 {
362 if (mode == wxSOUND_OUTPUT)
363 waveOutUnprepareHeader(m_internal->m_devout, header->m_header, sizeof(WAVEHDR));
364 else
365 waveInUnprepareHeader(m_internal->m_devin, header->m_header, sizeof(WAVEHDR));
366
367 GlobalUnlock(header->m_data);
368 GlobalUnlock(header->m_header);
369 GlobalFree(header->m_h_header);
370 GlobalFree(header->m_h_data);
371 delete header;
372 }
373
374 // -------------------------------------------------------------------------
375 // FreeHeaders(int mode)
376 //
377 // "mode" has the same mean as for OpenDevice() except that the two flags must
378 // be exclusive.
379 // FreeHeaders() frees all an operation queue once it has checked that
380 // all buffers have been terminated.
381 // -------------------------------------------------------------------------
382 void wxSoundStreamWin::FreeHeaders(int mode)
383 {
384 int i;
385 wxSoundInfoHeader ***headers;
386
387 if (mode == wxSOUND_OUTPUT)
388 headers = &m_headers_play;
389 else
390 headers = &m_headers_rec;
391
392 for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
393 if ((*headers)[i]) {
394 // We wait for the end of the buffer
395 WaitFor((*headers)[i]);
396 // Then, we free the header
397 FreeHeader((*headers)[i], mode);
398 }
399 }
400 delete[] (*headers);
401 (*headers) = NULL;
402 }
403
404 // -------------------------------------------------------------------------
405 // WaitFor(wxSoundInfoHeader *info)
406 //
407 // "info" is one element of an IO queue
408 // WaitFor() checks whether the specified block has been terminated.
409 // If it hasn't been terminated, it waits for its termination.
410 //
411 // NB: if it's a partially filled buffer it adds it to the Windows queue
412 // -------------------------------------------------------------------------
413 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
414 {
415 // We begun filling it: we must send it to the Windows queue
416 if (info->m_position != 0) {
417 memset(info->m_data + info->m_position, 0, info->m_size-info->m_position);
418 AddToQueue(info);
419 }
420
421 // If the buffer is finished, we return immediately
422 if (!info->m_playing && !info->m_recording)
423 return;
424
425 // Else, we wait for its termination
426 while (info->m_playing || info->m_recording)
427 wxYield();
428 }
429
430 // -------------------------------------------------------------------------
431 // AddToQueue(wxSoundInfoHeader *info)
432 //
433 // For "info", see WaitFor()
434 // AddToQueue() sends the IO queue element to the Windows queue.
435 //
436 // Warning: in the current implementation, it partially assume we send the
437 // element in the right order. This is true in that implementation but if
438 // you use it elsewhere, be careful: it may shuffle all your sound datas.
439 // -------------------------------------------------------------------------
440 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info)
441 {
442 MMRESULT result;
443
444 if (info->m_mode == wxSOUND_INPUT) {
445 // Increment the input fragment pointer
446 m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE;
447 result = waveInAddBuffer(m_internal->m_devin,
448 info->m_header, sizeof(WAVEHDR));
449 if (result == MMSYSERR_NOERROR)
450 info->m_recording = TRUE;
451 else
452 return FALSE;
453 } else if (info->m_mode == wxSOUND_OUTPUT) {
454 result = waveOutWrite(m_internal->m_devout,
455 info->m_header, sizeof(WAVEHDR));
456 if (result == MMSYSERR_NOERROR)
457 info->m_playing = TRUE;
458 else
459 return FALSE;
460 }
461 return TRUE;
462 }
463
464 // -------------------------------------------------------------------------
465 // ClearHeader(wxSoundInfoHeader *info)
466 //
467 // ClearHeader() reinitializes the parameters of "info" to their default
468 // value.
469 // -------------------------------------------------------------------------
470 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
471 {
472 info->m_playing = FALSE;
473 info->m_recording = FALSE;
474 info->m_position = 0;
475 info->m_size = GetBestSize();
476 }
477
478 // -------------------------------------------------------------------------
479 // wxSoundInfoHeader *NextFragmentOutput()
480 //
481 // NextFragmentOutput() looks for a free output block. It will always
482 // return you a non-NULL pointer but it may waits for an empty buffer a long
483 // time.
484 // -------------------------------------------------------------------------
485 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
486 {
487 if (m_headers_play[m_current_frag_out]->m_playing) {
488 m_current_frag_out = (m_current_frag_out + 1) % WXSOUND_MAX_QUEUE;
489
490 if (m_headers_play[m_current_frag_out]->m_playing)
491 WaitFor(m_headers_play[m_current_frag_out]);
492 }
493 if (m_current_frag_out == m_output_frag_out)
494 m_queue_filled = TRUE;
495 return m_headers_play[m_current_frag_out];
496 }
497
498 // -------------------------------------------------------------------------
499 // The behaviour of Write is documented in the global documentation.
500 // -------------------------------------------------------------------------
501 wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
502 {
503 m_lastcount = 0;
504 if (!m_internal->m_output_enabled)
505 return *this;
506
507 while (len > 0) {
508 wxSoundInfoHeader *header;
509 wxUint32 to_copy;
510
511 // Get a new output fragment
512 header = NextFragmentOutput();
513
514 to_copy = (len > header->m_size) ? header->m_size : len;
515 memcpy(header->m_data + header->m_position, buffer, to_copy);
516
517 header->m_position += to_copy;
518 header->m_size -= to_copy;
519 buffer = (((const char *)buffer) + to_copy);
520 len -= to_copy;
521 m_lastcount += to_copy;
522
523 // If the fragment is full, we send it to the Windows queue.
524 if (header->m_size == 0)
525 if (!AddToQueue(header)) {
526 m_snderror = wxSOUND_IOERR;
527 return *this;
528 }
529 }
530 return *this;
531 }
532
533 // -------------------------------------------------------------------------
534 // NextFragmentInput is not functional.
535 // -------------------------------------------------------------------------
536 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
537 {
538 wxSoundInfoHeader *header;
539
540 // TODO //
541 header = m_headers_rec[m_current_frag_in];
542 WaitFor(header);
543
544 if (m_current_frag_in == m_input_frag_in)
545 m_queue_filled = TRUE;
546
547 return header;
548 }
549
550 // -------------------------------------------------------------------------
551 // The behaviour of Read is documented in the global documentation.
552 // -------------------------------------------------------------------------
553 wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
554 {
555 wxSoundInfoHeader *header;
556 wxUint32 to_copy;
557
558 m_lastcount = 0;
559 if (!m_internal->m_input_enabled)
560 return *this;
561
562 while (len > 0) {
563 header = NextFragmentInput();
564
565 to_copy = (len > header->m_size) ? header->m_size : len;
566 memcpy(buffer, header->m_data + header->m_position, to_copy);
567
568 header->m_position += to_copy;
569 header->m_size -= to_copy;
570 buffer = (((char *)buffer) + to_copy);
571 len -= to_copy;
572 m_lastcount += to_copy;
573
574 if (header->m_size == 0) {
575 ClearHeader(header);
576 if (!AddToQueue(header)) {
577 m_snderror = wxSOUND_IOERR;
578 return *this;
579 }
580 }
581 }
582 return *this;
583 }
584
585 // -------------------------------------------------------------------------
586 // NotifyDoneBuffer(wxUint32 dev_handle)
587 //
588 // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
589 // fragment finished. It reinitializes the parameters of the fragment and
590 // sends an event to the clients.
591 // -------------------------------------------------------------------------
592 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle)
593 {
594 wxSoundInfoHeader *info;
595
596 if (dev_handle == (wxUint32)m_internal->m_devout) {
597 m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE;
598 info = m_headers_play[m_output_frag_out];
599 ClearHeader(info);
600 m_queue_filled = FALSE;
601 OnSoundEvent(wxSOUND_OUTPUT);
602 } else {
603 m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE;
604 OnSoundEvent(wxSOUND_INPUT);
605 m_queue_filled = FALSE;
606 }
607 }
608
609 // -------------------------------------------------------------------------
610 // -------------------------------------------------------------------------
611 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
612 {
613 return wxSoundStream::SetSoundFormat(base);
614 }
615
616 // -------------------------------------------------------------------------
617 // -------------------------------------------------------------------------
618 bool wxSoundStreamWin::StartProduction(int evt)
619 {
620 if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) ||
621 (m_internal->m_input_enabled && (evt & wxSOUND_INPUT)))
622 CloseDevice();
623
624 if (!OpenDevice(evt))
625 return FALSE;
626
627 m_production_started = TRUE;
628 m_queue_filled = FALSE;
629 // Send a dummy event to start.
630 if (evt & wxSOUND_OUTPUT)
631 OnSoundEvent(evt);
632
633 if (evt & wxSOUND_INPUT) {
634 int i;
635 for (i=0;i<WXSOUND_MAX_QUEUE;i++)
636 AddToQueue(m_headers_rec[i]);
637 }
638
639 return TRUE;
640 }
641
642 // -------------------------------------------------------------------------
643 // -------------------------------------------------------------------------
644 bool wxSoundStreamWin::StopProduction()
645 {
646 m_production_started = FALSE;
647 CloseDevice();
648 return TRUE;
649 }
650
651 // -------------------------------------------------------------------------
652 // -------------------------------------------------------------------------
653 bool wxSoundStreamWin::QueueFilled() const
654 {
655 return (!m_production_started || m_queue_filled);
656 }
657
658
659 // --------------------------------------------------------------------------
660 // wxSoundWinModule
661 // --------------------------------------------------------------------------
662
663 class WXDLLEXPORT wxSoundWinModule : public wxModule {
664 DECLARE_DYNAMIC_CLASS(wxSoundWinModule)
665 public:
666 bool OnInit();
667 void OnExit();
668 };
669
670 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule, wxModule)
671
672 bool wxSoundWinModule::OnInit() {
673 wxSoundHandleList = new wxList(wxKEY_INTEGER);
674 return TRUE;
675 }
676
677 void wxSoundWinModule::OnExit() {
678 delete wxSoundHandleList;
679 }