Added wxSound for Windows support (successful WAV playback on VC++ 5)
[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/msw/private.h>
11 #include <wx/module.h>
12 #include <string.h>
13 #include "sndbase.h"
14 #include "sndwin.h"
15 #include "sndpcm.h"
16
17 #include <windows.h>
18 #include <mmsystem.h>
19
20 typedef struct _wxSoundInternal wxSoundInternal;
21 typedef struct _wxSoundInfoHeader wxSoundInfoHeader;
22
23 extern char wxCanvasClassName[];
24
25 wxList *wxSoundHandleList = NULL;
26
27 static inline wxSoundStreamWin *wxFindSoundFromHandle(WXHWND hWnd)
28 {
29 wxNode *node = wxSoundHandleList->Find((long)hWnd);
30 if (!node)
31 return NULL;
32 return (wxSoundStreamWin *)node->Data();
33 }
34
35 struct _wxSoundInternal {
36 HWND m_sndWin;
37 HWAVEIN m_devin;
38 HWAVEOUT m_devout;
39 bool m_output_enabled, m_input_enabled;
40 };
41
42 struct _wxSoundInfoHeader {
43 HGLOBAL m_h_header, m_h_data;
44 char *m_data;
45 WAVEHDR *m_header;
46 int m_mode;
47 bool m_playing, m_recording;
48 wxUint32 m_position, m_size;
49
50 wxSoundStreamWin *m_driver;
51 };
52
53 #define WXSOUND_MAX_QUEUE 128
54
55 wxSoundStreamWin::wxSoundStreamWin()
56 {
57 wxSoundFormatPcm pcm;
58
59 m_production_started = FALSE;
60 m_internal = new wxSoundInternal;
61 m_snderror = wxSOUND_NOERR;
62
63 // Setup defaults
64 CreateSndWindow();
65 SetSoundFormat(pcm);
66
67 if (!OpenDevice(wxSOUND_OUTPUT))
68 return;
69
70 CloseDevice();
71 }
72
73 wxSoundStreamWin::~wxSoundStreamWin()
74 {
75 if (m_production_started)
76 StopProduction();
77 DestroySndWindow();
78
79 delete m_internal;
80 }
81
82 LRESULT APIENTRY _EXPORT _wxSoundHandlerWndProc(HWND hWnd, UINT message,
83 WPARAM wParam, LPARAM lParam)
84 {
85 switch (message) {
86 case MM_WOM_DONE: {
87 wxFindSoundFromHandle((WXHWND)hWnd)->NotifyDoneBuffer(wParam);
88 break;
89 }
90 default:
91 break;
92 }
93 return (LRESULT)0;
94 }
95
96 void wxSoundStreamWin::CreateSndWindow()
97 {
98 FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc,
99 wxGetInstance());
100 int error;
101
102 m_internal->m_sndWin = ::CreateWindow(wxCanvasClassName, NULL, 0,
103 0, 0, 0, 0, NULL, (HMENU) NULL,
104 wxGetInstance(), NULL);
105
106 error = GetLastError();
107 wxPrintf("%d\n", error);
108
109 ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
110
111 wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this);
112 }
113
114 void wxSoundStreamWin::DestroySndWindow()
115 {
116 if (m_internal->m_sndWin) {
117 ::DestroyWindow(m_internal->m_sndWin);
118 wxSoundHandleList->DeleteObject((wxObject *)this);
119 }
120 }
121
122 bool wxSoundStreamWin::OpenDevice(int mode)
123 {
124 wxSoundFormatPcm *pcm;
125 WAVEFORMATEX wformat;
126
127 if (!m_sndformat) {
128 m_snderror = wxSOUND_INVFRMT;
129 return FALSE;
130 }
131
132 pcm = (wxSoundFormatPcm *)m_sndformat;
133
134 wformat.wFormatTag = WAVE_FORMAT_PCM;
135 wformat.nChannels = pcm->GetChannels();
136 wformat.nBlockAlign = pcm->GetBPS() / 8 * wformat.nChannels;
137 wformat.nAvgBytesPerSec = pcm->GetBytesFromTime(1);
138 wformat.nSamplesPerSec = pcm->GetSampleRate();
139 wformat.wBitsPerSample = pcm->GetBPS();
140 wformat.cbSize = 0;
141
142 if (mode & wxSOUND_OUTPUT) {
143 MMRESULT result;
144
145 result = waveOutOpen(&m_internal->m_devout,
146 WAVE_MAPPER, &wformat,
147 (DWORD)m_internal->m_sndWin, 0,
148 CALLBACK_WINDOW);
149
150 if (result != MMSYSERR_NOERROR) {
151 m_snderror = wxSOUND_INVDEV;
152 return FALSE;
153 }
154
155 m_output_frag_out = WXSOUND_MAX_QUEUE-1;
156 m_current_frag_out = 0;
157
158 m_internal->m_output_enabled = TRUE;
159 }
160 if (mode & wxSOUND_INPUT) {
161 MMRESULT result;
162
163 result = waveInOpen(&m_internal->m_devin,
164 WAVE_MAPPER, &wformat,
165 (DWORD)m_internal->m_sndWin, 0,
166 CALLBACK_WINDOW);
167
168 if (result != MMSYSERR_NOERROR) {
169 m_snderror = wxSOUND_INVDEV;
170 return FALSE;
171 }
172
173 m_input_frag_in = WXSOUND_MAX_QUEUE-1;
174 m_current_frag_in = 0;
175
176 m_internal->m_input_enabled = TRUE;
177 }
178
179 if (!AllocHeaders(mode)) {
180 CloseDevice();
181 return FALSE;
182 }
183 return TRUE;
184 }
185
186
187 void wxSoundStreamWin::CloseDevice()
188 {
189 if (m_internal->m_output_enabled) {
190 FreeHeaders(wxSOUND_OUTPUT);
191 waveOutReset(m_internal->m_devout);
192 waveOutClose(m_internal->m_devout);
193 }
194
195 if (m_internal->m_input_enabled) {
196 FreeHeaders(wxSOUND_INPUT);
197 waveInReset(m_internal->m_devin);
198 waveInClose(m_internal->m_devin);
199 }
200
201 m_internal->m_output_enabled = FALSE;
202 m_internal->m_input_enabled = FALSE;
203 }
204
205 wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
206 {
207 wxSoundInfoHeader *info;
208 WAVEHDR *header;
209
210 info = new wxSoundInfoHeader;
211 info->m_h_data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize());
212 info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
213 if (!info->m_h_data || !info->m_h_header) {
214 delete info;
215 m_snderror = wxSOUND_MEMERR;
216 return NULL;
217 }
218
219 info->m_data = (char *)GlobalLock(info->m_h_data);
220 info->m_header = (WAVEHDR *)GlobalLock(info->m_h_header);
221 info->m_mode = mode;
222 info->m_driver = this;
223 ClearHeader(info);
224
225 header = info->m_header;
226
227 header->lpData = info->m_data;
228 header->dwBufferLength = GetBestSize();
229 header->dwUser = (DWORD)info;
230 header->dwFlags = WHDR_DONE;
231
232 if (mode == wxSOUND_INPUT) {
233 MMRESULT result;
234
235 result = waveInPrepareHeader(m_internal->m_devin, header,
236 sizeof(WAVEHDR));
237
238 if (result != MMSYSERR_NOERROR) {
239 GlobalUnlock(info->m_data);
240 GlobalUnlock(info->m_header);
241 GlobalFree(info->m_h_data);
242 GlobalFree(info->m_h_header);
243 delete info;
244
245 m_snderror = wxSOUND_IOERR;
246 return NULL;
247 }
248 } else if (mode == wxSOUND_OUTPUT) {
249 MMRESULT result;
250
251 result = waveOutPrepareHeader(m_internal->m_devout, header,
252 sizeof(WAVEHDR));
253
254 if (result != MMSYSERR_NOERROR) {
255 GlobalUnlock(info->m_data);
256 GlobalUnlock(info->m_header);
257 GlobalFree(info->m_h_data);
258 GlobalFree(info->m_h_header);
259 delete info;
260
261 m_snderror = wxSOUND_IOERR;
262 return NULL;
263 }
264 }
265 return info;
266 }
267
268 bool wxSoundStreamWin::AllocHeaders(int mode)
269 {
270 int i;
271 wxSoundInfoHeader **headers;
272
273 if (mode == wxSOUND_OUTPUT)
274 headers = m_headers_play = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
275 else
276 headers = m_headers_rec = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
277
278 memset(headers, 0, WXSOUND_MAX_QUEUE*sizeof(wxSoundInfoHeader *));
279
280 for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
281 headers[i] = AllocHeader(mode);
282 if (!headers[i]) {
283 FreeHeaders(mode);
284 return FALSE;
285 }
286 }
287 return TRUE;
288 }
289
290 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
291 {
292 if (mode == wxSOUND_OUTPUT)
293 waveOutUnprepareHeader(m_internal->m_devout, header->m_header, sizeof(WAVEHDR));
294 else
295 waveInUnprepareHeader(m_internal->m_devin, header->m_header, sizeof(WAVEHDR));
296
297 GlobalUnlock(header->m_data);
298 GlobalUnlock(header->m_header);
299 GlobalFree(header->m_h_header);
300 GlobalFree(header->m_h_data);
301 delete header;
302 }
303
304 void wxSoundStreamWin::FreeHeaders(int mode)
305 {
306 int i;
307 wxSoundInfoHeader ***headers;
308
309 if (mode == wxSOUND_OUTPUT)
310 headers = &m_headers_play;
311 else
312 headers = &m_headers_rec;
313
314 for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
315 if ((*headers)[i]) {
316 WaitFor((*headers)[i]);
317 FreeHeader((*headers)[i], mode);
318 }
319 }
320 delete[] (*headers);
321 (*headers) = NULL;
322 }
323
324 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
325 {
326 if (info->m_position != 0) {
327 memset(info->m_data + info->m_position, 0, info->m_size);
328 AddToQueue(info);
329 }
330
331 if (!info->m_playing && !info->m_recording)
332 return;
333
334 while (info->m_playing || info->m_recording)
335 wxYield();
336 }
337
338 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info)
339 {
340 MMRESULT result;
341
342 if (info->m_mode == wxSOUND_INPUT) {
343 m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE;
344 result = waveInAddBuffer(m_internal->m_devin,
345 info->m_header, sizeof(WAVEHDR));
346 if (result == MMSYSERR_NOERROR)
347 info->m_recording = TRUE;
348 else
349 return FALSE;
350 } else if (info->m_mode == wxSOUND_OUTPUT) {
351 result = waveOutWrite(m_internal->m_devout,
352 info->m_header, sizeof(WAVEHDR));
353 if (result == MMSYSERR_NOERROR)
354 info->m_playing = TRUE;
355 else
356 return FALSE;
357 }
358 return TRUE;
359 }
360
361 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
362 {
363 info->m_playing = FALSE;
364 info->m_recording = FALSE;
365 info->m_position = 0;
366 info->m_size = GetBestSize();
367 }
368
369 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
370 {
371 if (m_headers_play[m_current_frag_out]->m_playing) {
372 m_current_frag_out = (m_current_frag_out + 1) % WXSOUND_MAX_QUEUE;
373
374 if (m_headers_play[m_current_frag_out]->m_playing)
375 WaitFor(m_headers_play[m_current_frag_out]);
376 }
377 if (m_current_frag_out == m_output_frag_out)
378 m_queue_filled = TRUE;
379 return m_headers_play[m_current_frag_out];
380 }
381
382 wxSoundStream& wxSoundStreamWin::Write(const void *buffer, size_t len)
383 {
384 m_lastcount = 0;
385 if (!m_internal->m_output_enabled)
386 return *this;
387
388 while (len > 0) {
389 wxSoundInfoHeader *header;
390 size_t to_copy;
391
392 header = NextFragmentOutput();
393
394 to_copy = (len > header->m_size) ? header->m_size : len;
395 memcpy(header->m_data + header->m_position, buffer, to_copy);
396
397 header->m_position += to_copy;
398 header->m_size -= to_copy;
399 buffer = (((const char *)buffer) + to_copy);
400 len -= to_copy;
401 m_lastcount += to_copy;
402
403 if (header->m_size == 0)
404 if (!AddToQueue(header)) {
405 m_snderror = wxSOUND_IOERR;
406 return *this;
407 }
408 }
409 return *this;
410 }
411
412 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
413 {
414 wxSoundInfoHeader *header;
415
416 // TODO //
417 header = m_headers_rec[m_current_frag_in];
418 WaitFor(header);
419
420 if (m_current_frag_in == m_input_frag_in)
421 m_queue_filled = TRUE;
422
423 return header;
424 }
425
426 wxSoundStream& wxSoundStreamWin::Read(void *buffer, size_t len)
427 {
428 wxSoundInfoHeader *header;
429 size_t to_copy;
430
431 m_lastcount = 0;
432 if (!m_internal->m_input_enabled)
433 return *this;
434
435 while (len > 0) {
436 header = NextFragmentInput();
437
438 to_copy = (len > header->m_size) ? header->m_size : len;
439 memcpy(buffer, header->m_data + header->m_position, to_copy);
440
441 header->m_position += to_copy;
442 header->m_size -= to_copy;
443 buffer = (((char *)buffer) + to_copy);
444 len -= to_copy;
445 m_lastcount += to_copy;
446
447 if (header->m_size == 0) {
448 ClearHeader(header);
449 if (!AddToQueue(header)) {
450 m_snderror = wxSOUND_IOERR;
451 return *this;
452 }
453 }
454 }
455 return *this;
456 }
457
458 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle)
459 {
460 wxSoundInfoHeader *info;
461
462 if (dev_handle == (wxUint32)m_internal->m_devout) {
463 m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE;
464 info = m_headers_play[m_output_frag_out];
465 ClearHeader(info);
466 m_queue_filled = FALSE;
467 OnSoundEvent(wxSOUND_OUTPUT);
468 } else {
469 m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE;
470 OnSoundEvent(wxSOUND_INPUT);
471 m_queue_filled = FALSE;
472 }
473 }
474
475 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
476 {
477 return wxSoundStream::SetSoundFormat(base);
478 }
479
480 bool wxSoundStreamWin::StartProduction(int evt)
481 {
482 if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) ||
483 (m_internal->m_input_enabled && (evt & wxSOUND_INPUT)))
484 CloseDevice();
485
486 if (!OpenDevice(evt))
487 return FALSE;
488
489 m_production_started = TRUE;
490 m_queue_filled = FALSE;
491 // Send a dummy event to start.
492 if (evt & wxSOUND_OUTPUT)
493 OnSoundEvent(evt);
494
495 if (evt & wxSOUND_INPUT) {
496 int i;
497 for (i=0;i<WXSOUND_MAX_QUEUE;i++)
498 AddToQueue(m_headers_rec[i]);
499 }
500
501 return TRUE;
502 }
503
504 bool wxSoundStreamWin::StopProduction()
505 {
506 m_production_started = FALSE;
507 CloseDevice();
508 return TRUE;
509 }
510
511 bool wxSoundStreamWin::QueueFilled() const
512 {
513 return (!m_production_started || m_queue_filled);
514 }
515
516
517 // --------------------------------------------------------------------------
518 // wxSoundWinModule
519 // --------------------------------------------------------------------------
520
521 class WXDLLEXPORT wxSoundWinModule : public wxModule {
522 DECLARE_DYNAMIC_CLASS(wxSoundWinModule)
523 public:
524 bool OnInit();
525 void OnExit();
526 };
527
528 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule, wxModule)
529
530 bool wxSoundWinModule::OnInit() {
531 wxSoundHandleList = new wxList(wxKEY_INTEGER);
532 return TRUE;
533 }
534
535 void wxSoundWinModule::OnExit() {
536 delete wxSoundHandleList;
537 }