]>
Commit | Line | Data |
---|---|---|
503aa33d GL |
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 | ||
0662cd32 | 382 | wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len) |
503aa33d GL |
383 | { |
384 | m_lastcount = 0; | |
385 | if (!m_internal->m_output_enabled) | |
386 | return *this; | |
387 | ||
388 | while (len > 0) { | |
389 | wxSoundInfoHeader *header; | |
0662cd32 | 390 | wxUint32 to_copy; |
503aa33d GL |
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 | ||
0662cd32 | 426 | wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len) |
503aa33d GL |
427 | { |
428 | wxSoundInfoHeader *header; | |
0662cd32 | 429 | wxUint32 to_copy; |
503aa33d GL |
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 | } |