module-waveout: Adapted to updated API
[profile/ivi/pulseaudio-panda.git] / src / modules / module-waveout.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2006 Lennart Poettering
5   Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <windows.h>
28 #include <mmsystem.h>
29
30 #include <pulse/mainloop-api.h>
31
32 #include <pulse/xmalloc.h>
33 #include <pulse/timeval.h>
34 #include <pulse/rtclock.h>
35
36 #include <pulsecore/sink.h>
37 #include <pulsecore/source.h>
38 #include <pulsecore/module.h>
39 #include <pulsecore/modargs.h>
40 #include <pulsecore/sample-util.h>
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/thread.h>
44 #include <pulsecore/thread-mq.h>
45
46 #include "module-waveout-symdef.h"
47
48 PA_MODULE_AUTHOR("Pierre Ossman");
49 PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source");
50 PA_MODULE_VERSION(PACKAGE_VERSION);
51 PA_MODULE_USAGE(
52     "sink_name=<name for the sink> "
53     "source_name=<name for the source> "
54     "device=<device number> "
55     "record=<enable source?> "
56     "playback=<enable sink?> "
57     "format=<sample format> "
58     "channels=<number of channels> "
59     "rate=<sample rate> "
60     "fragments=<number of fragments> "
61     "fragment_size=<fragment size> "
62     "channel_map=<channel map>");
63
64 #define DEFAULT_SINK_NAME "wave_output"
65 #define DEFAULT_SOURCE_NAME "wave_input"
66
67 #define WAVEOUT_MAX_VOLUME 0xFFFF
68
69 struct userdata {
70     pa_sink *sink;
71     pa_source *source;
72     pa_core *core;
73     pa_usec_t poll_timeout;
74
75     pa_thread *thread;
76     pa_thread_mq thread_mq;
77     pa_rtpoll *rtpoll;
78
79     uint32_t fragments, fragment_size;
80
81     uint32_t free_ofrags, free_ifrags;
82
83     DWORD written_bytes;
84     int sink_underflow;
85
86     int cur_ohdr, cur_ihdr;
87     WAVEHDR *ohdrs, *ihdrs;
88
89     HWAVEOUT hwo;
90     HWAVEIN hwi;
91     pa_module *module;
92
93     CRITICAL_SECTION crit;
94 };
95
96 static const char* const valid_modargs[] = {
97     "sink_name",
98     "source_name",
99     "device",
100     "record",
101     "playback",
102     "fragments",
103     "fragment_size",
104     "format",
105     "rate",
106     "channels",
107     "channel_map",
108     NULL
109 };
110
111 static void do_write(struct userdata *u) {
112     uint32_t free_frags;
113     pa_memchunk memchunk;
114     WAVEHDR *hdr;
115     MMRESULT res;
116     void *p;
117
118     if (!u->sink)
119         return;
120
121     if (!PA_SINK_IS_LINKED(u->sink->state))
122         return;
123
124     EnterCriticalSection(&u->crit);
125     free_frags = u->free_ofrags;
126     LeaveCriticalSection(&u->crit);
127
128     if (!u->sink_underflow && (free_frags == u->fragments))
129         pa_log_debug("WaveOut underflow!");
130
131     while (free_frags) {
132         hdr = &u->ohdrs[u->cur_ohdr];
133         if (hdr->dwFlags & WHDR_PREPARED)
134             waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
135
136         hdr->dwBufferLength = 0;
137         while (hdr->dwBufferLength < u->fragment_size) {
138             size_t len;
139
140             len = u->fragment_size - hdr->dwBufferLength;
141
142             pa_sink_render(u->sink, len, &memchunk);
143
144             pa_assert(memchunk.memblock);
145             pa_assert(memchunk.length);
146
147             if (memchunk.length < len)
148                 len = memchunk.length;
149
150             p = pa_memblock_acquire(memchunk.memblock);
151             memcpy(hdr->lpData + hdr->dwBufferLength, (char*) p + memchunk.index, len);
152             pa_memblock_release(memchunk.memblock);
153
154             hdr->dwBufferLength += len;
155
156             pa_memblock_unref(memchunk.memblock);
157             memchunk.memblock = NULL;
158         }
159
160         /* Insufficient data in sink buffer? */
161         if (hdr->dwBufferLength == 0) {
162             u->sink_underflow = 1;
163             break;
164         }
165
166         u->sink_underflow = 0;
167
168         res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
169         if (res != MMSYSERR_NOERROR)
170             pa_log_error("Unable to prepare waveOut block: %d", res);
171
172         res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR));
173         if (res != MMSYSERR_NOERROR)
174             pa_log_error("Unable to write waveOut block: %d", res);
175
176         u->written_bytes += hdr->dwBufferLength;
177
178         EnterCriticalSection(&u->crit);
179         u->free_ofrags--;
180         LeaveCriticalSection(&u->crit);
181
182         free_frags--;
183         u->cur_ohdr++;
184         u->cur_ohdr %= u->fragments;
185     }
186 }
187
188 static void do_read(struct userdata *u) {
189     uint32_t free_frags;
190     pa_memchunk memchunk;
191     WAVEHDR *hdr;
192     MMRESULT res;
193     void *p;
194
195     if (!u->source)
196         return;
197
198     if (!PA_SOURCE_IS_LINKED(u->source->state))
199         return;
200
201     EnterCriticalSection(&u->crit);
202     free_frags = u->free_ifrags;
203     u->free_ifrags = 0;
204     LeaveCriticalSection(&u->crit);
205
206     if (free_frags == u->fragments)
207         pa_log_debug("WaveIn overflow!");
208
209     while (free_frags) {
210         hdr = &u->ihdrs[u->cur_ihdr];
211         if (hdr->dwFlags & WHDR_PREPARED)
212             waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
213
214         if (hdr->dwBytesRecorded) {
215             memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded);
216             pa_assert(memchunk.memblock);
217
218             p = pa_memblock_acquire(memchunk.memblock);
219             memcpy((char*) p, hdr->lpData, hdr->dwBytesRecorded);
220             pa_memblock_release(memchunk.memblock);
221
222             memchunk.length = hdr->dwBytesRecorded;
223             memchunk.index = 0;
224
225             pa_source_post(u->source, &memchunk);
226             pa_memblock_unref(memchunk.memblock);
227         }
228
229         res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
230         if (res != MMSYSERR_NOERROR)
231             pa_log_error("Unable to prepare waveIn block: %d", res);
232
233         res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR));
234         if (res != MMSYSERR_NOERROR)
235             pa_log_error("Unable to add waveIn block: %d", res);
236
237         free_frags--;
238         u->cur_ihdr++;
239         u->cur_ihdr %= u->fragments;
240     }
241 }
242
243 static void thread_func(void *userdata) {
244     struct userdata *u = userdata;
245
246     pa_assert(u);
247     pa_assert(u->sink || u->source);
248
249     pa_log_debug("Thread starting up");
250
251     if (u->core->realtime_scheduling)
252         pa_make_realtime(u->core->realtime_priority);
253
254     pa_thread_mq_install(&u->thread_mq);
255
256     for (;;) {
257         int ret;
258
259         if (PA_SINK_IS_OPENED(u->sink->thread_info.state) ||
260             PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
261
262             if (u->sink->thread_info.rewind_requested)
263                 pa_sink_process_rewind(u->sink, 0);
264
265             if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
266                 do_write(u);
267             if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
268                 do_read(u);
269
270             pa_rtpoll_set_timer_relative(u->rtpoll, u->poll_timeout);
271         } else
272             pa_rtpoll_set_timer_disabled(u->rtpoll);
273
274         /* Hmm, nothing to do. Let's sleep */
275         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
276             goto fail;
277
278         if (ret == 0)
279             goto finish;
280     }
281
282 fail:
283     /* If this was no regular exit from the loop we have to continue
284      * processing messages until we received PA_MESSAGE_SHUTDOWN */
285     pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
286     pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
287
288 finish:
289     pa_log_debug("Thread shutting down");
290 }
291
292 static void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
293     struct userdata *u = (struct userdata *)inst;
294
295     if (msg != WOM_DONE)
296         return;
297
298     EnterCriticalSection(&u->crit);
299     u->free_ofrags++;
300     pa_assert(u->free_ofrags <= u->fragments);
301     LeaveCriticalSection(&u->crit);
302 }
303
304 static void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
305     struct userdata *u = (struct userdata *)inst;
306
307     if (msg != WIM_DATA)
308         return;
309
310     EnterCriticalSection(&u->crit);
311     u->free_ifrags++;
312     pa_assert(u->free_ifrags <= u->fragments);
313     LeaveCriticalSection(&u->crit);
314 }
315
316 static pa_usec_t sink_get_latency(struct userdata *u) {
317     uint32_t free_frags;
318     MMTIME mmt;
319     pa_assert(u);
320     pa_assert(u->sink);
321
322     memset(&mmt, 0, sizeof(mmt));
323     mmt.wType = TIME_BYTES;
324     if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR)
325         return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &u->sink->sample_spec);
326     else {
327         EnterCriticalSection(&u->crit);
328         free_frags = u->free_ofrags;
329         LeaveCriticalSection(&u->crit);
330
331         return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size, &u->sink->sample_spec);
332     }
333 }
334
335 static pa_usec_t source_get_latency(struct userdata *u) {
336     pa_usec_t r = 0;
337     uint32_t free_frags;
338     pa_assert(u);
339     pa_assert(u->source);
340
341     EnterCriticalSection(&u->crit);
342     free_frags = u->free_ifrags;
343     LeaveCriticalSection(&u->crit);
344
345     r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &u->source->sample_spec);
346
347     return r;
348 }
349
350 static int process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
351     struct userdata *u;
352
353     if (pa_sink_isinstance(o)) {
354         u = PA_SINK(o)->userdata;
355
356         switch (code) {
357
358             case PA_SINK_MESSAGE_GET_LATENCY: {
359                 pa_usec_t r = 0;
360                 if (u->hwo)
361                     r = sink_get_latency(u);
362                 *((pa_usec_t*) data) = r;
363                 return 0;
364             }
365
366         }
367
368         return pa_sink_process_msg(o, code, data, offset, chunk);
369     }
370
371     if (pa_source_isinstance(o)) {
372         u = PA_SOURCE(o)->userdata;
373
374         switch (code) {
375
376             case PA_SOURCE_MESSAGE_GET_LATENCY: {
377                 pa_usec_t r = 0;
378                 if (u->hwi)
379                     r = source_get_latency(u);
380                 *((pa_usec_t*) data) = r;
381                 return 0;
382             }
383
384         }
385
386         return pa_source_process_msg(o, code, data, offset, chunk);
387     }
388
389     return -1;
390 }
391
392 static void sink_get_volume_cb(pa_sink *s) {
393     struct userdata *u = s->userdata;
394     DWORD vol;
395     pa_volume_t left, right;
396
397     if (waveOutGetVolume(u->hwo, &vol) != MMSYSERR_NOERROR)
398         return;
399
400     left = PA_CLAMP_VOLUME((vol & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
401     right = PA_CLAMP_VOLUME(((vol >> 16) & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
402
403     /* Windows supports > 2 channels, except for volume control */
404     if (s->real_volume.channels > 2)
405         pa_cvolume_set(&s->real_volume, s->real_volume.channels, (left + right)/2);
406
407     s->real_volume.values[0] = left;
408     if (s->real_volume.channels > 1)
409         s->real_volume.values[1] = right;
410 }
411
412 static void sink_set_volume_cb(pa_sink *s) {
413     struct userdata *u = s->userdata;
414     DWORD vol;
415
416     vol = s->real_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM;
417     if (s->real_volume.channels > 1)
418         vol |= (s->real_volume.values[1] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
419
420     if (waveOutSetVolume(u->hwo, vol) != MMSYSERR_NOERROR)
421         return;
422 }
423
424 static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
425     wf->wFormatTag = WAVE_FORMAT_PCM;
426
427     if (ss->channels > 2) {
428         pa_log_error("More than two channels not supported.");
429         return -1;
430     }
431
432     wf->nChannels = ss->channels;
433
434     switch (ss->rate) {
435     case 8000:
436     case 11025:
437     case 22005:
438     case 44100:
439         break;
440     default:
441         pa_log_error("Unsupported sample rate.");
442         return -1;
443     }
444
445     wf->nSamplesPerSec = ss->rate;
446
447     if (ss->format == PA_SAMPLE_U8)
448         wf->wBitsPerSample = 8;
449     else if (ss->format == PA_SAMPLE_S16NE)
450         wf->wBitsPerSample = 16;
451     else {
452         pa_log_error("Unsupported sample format.");
453         return -1;
454     }
455
456     wf->nBlockAlign = wf->nChannels * wf->wBitsPerSample/8;
457     wf->nAvgBytesPerSec = wf->nSamplesPerSec * wf->nBlockAlign;
458
459     wf->cbSize = 0;
460
461     return 0;
462 }
463
464 int pa__get_n_used(pa_module *m) {
465     struct userdata *u;
466     pa_assert(m);
467     pa_assert(m->userdata);
468     u = (struct userdata *)m->userdata;
469
470     return (u->sink ? pa_sink_used_by(u->sink) : 0) +
471            (u->source ? pa_source_used_by(u->source) : 0);
472 }
473
474 int pa__init(pa_module *m) {
475     struct userdata *u = NULL;
476     HWAVEOUT hwo = INVALID_HANDLE_VALUE;
477     HWAVEIN hwi = INVALID_HANDLE_VALUE;
478     WAVEFORMATEX wf;
479     int nfrags, frag_size;
480     pa_bool_t record = TRUE, playback = TRUE;
481     unsigned int device;
482     pa_sample_spec ss;
483     pa_channel_map map;
484     pa_modargs *ma = NULL;
485     unsigned int i;
486
487     pa_assert(m);
488     pa_assert(m->core);
489
490     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
491         pa_log("failed to parse module arguments.");
492         goto fail;
493     }
494
495     if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
496         pa_log("record= and playback= expect boolean argument.");
497         goto fail;
498     }
499
500     if (!playback && !record) {
501         pa_log("neither playback nor record enabled for device.");
502         goto fail;
503     }
504
505     device = WAVE_MAPPER;
506     if (pa_modargs_get_value_u32(ma, "device", &device) < 0) {
507         pa_log("failed to parse device argument");
508         goto fail;
509     }
510
511     nfrags = 5;
512     frag_size = 8192;
513     if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
514         pa_log("failed to parse fragments arguments");
515         goto fail;
516     }
517
518     ss = m->core->default_sample_spec;
519     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_WAVEEX) < 0) {
520         pa_log("failed to parse sample specification");
521         goto fail;
522     }
523
524     if (ss_to_waveformat(&ss, &wf) < 0)
525         goto fail;
526
527     u = pa_xmalloc(sizeof(struct userdata));
528
529     if (record) {
530         if (waveInOpen(&hwi, device, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
531             pa_log("failed to open waveIn");
532             goto fail;
533         }
534         if (waveInStart(hwi) != MMSYSERR_NOERROR) {
535             pa_log("failed to start waveIn");
536             goto fail;
537         }
538         pa_log_debug("Opened waveIn subsystem.");
539     }
540
541     if (playback) {
542         if (waveOutOpen(&hwo, device, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
543             pa_log("failed to open waveOut");
544             goto fail;
545         }
546         pa_log_debug("Opened waveOut subsystem.");
547     }
548
549     InitializeCriticalSection(&u->crit);
550
551     if (hwi != INVALID_HANDLE_VALUE) {
552         pa_source_new_data data;
553         pa_source_new_data_init(&data);
554         data.driver = __FILE__;
555         data.module = m;
556         pa_source_new_data_set_sample_spec(&data, &ss);
557         pa_source_new_data_set_channel_map(&data, &map);
558         pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
559         u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
560         pa_source_new_data_done(&data);
561
562         pa_assert(u->source);
563         u->source->userdata = u;
564         pa_source_set_description(u->source, "Windows waveIn PCM");
565         u->source->parent.process_msg = process_msg;
566     } else
567         u->source = NULL;
568
569     if (hwo != INVALID_HANDLE_VALUE) {
570         pa_sink_new_data data;
571         pa_sink_new_data_init(&data);
572         data.driver = __FILE__;
573         data.module = m;
574         pa_sink_new_data_set_sample_spec(&data, &ss);
575         pa_sink_new_data_set_channel_map(&data, &map);
576         pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
577         u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
578         pa_sink_new_data_done(&data);
579
580         pa_assert(u->sink);
581         u->sink->get_volume = sink_get_volume_cb;
582         u->sink->set_volume = sink_set_volume_cb;
583         u->sink->userdata = u;
584         pa_sink_set_description(u->sink, "Windows waveOut PCM");
585         u->sink->parent.process_msg = process_msg;
586     } else
587         u->sink = NULL;
588
589     pa_assert(u->source || u->sink);
590     pa_modargs_free(ma);
591
592     u->core = m->core;
593     u->hwi = hwi;
594     u->hwo = hwo;
595
596     u->fragments = nfrags;
597     u->free_ifrags = u->fragments;
598     u->free_ofrags = u->fragments;
599     u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss));
600
601     u->written_bytes = 0;
602     u->sink_underflow = 1;
603
604     u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 10, &ss);
605
606     u->cur_ihdr = 0;
607     u->cur_ohdr = 0;
608     u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
609     pa_assert(u->ihdrs);
610     u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
611     pa_assert(u->ohdrs);
612     for (i = 0;i < u->fragments;i++) {
613         u->ihdrs[i].dwBufferLength = u->fragment_size;
614         u->ohdrs[i].dwBufferLength = u->fragment_size;
615         u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size);
616         pa_assert(u->ihdrs);
617         u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
618         pa_assert(u->ohdrs);
619     }
620
621     u->module = m;
622     m->userdata = u;
623
624     /* Read mixer settings */
625     if (u->sink)
626         sink_get_volume_cb(u->sink);
627
628     u->rtpoll = pa_rtpoll_new();
629     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
630     if (!(u->thread = pa_thread_new("waveout-source", thread_func, u))) {
631         pa_log("Failed to create thread.");
632         goto fail;
633     }
634
635     if (u->sink) {
636         pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
637         pa_sink_set_rtpoll(u->sink, u->rtpoll);
638         pa_sink_put(u->sink);
639     }
640     if (u->source) {
641         pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
642         pa_source_set_rtpoll(u->source, u->rtpoll);
643         pa_source_put(u->source);
644     }
645
646     return 0;
647
648 fail:
649     if (ma)
650         pa_modargs_free(ma);
651
652     pa__done(m);
653
654     return -1;
655 }
656
657 void pa__done(pa_module *m) {
658     struct userdata *u;
659     unsigned int i;
660
661     pa_assert(m);
662     pa_assert(m->core);
663
664     if (!(u = m->userdata))
665         return;
666
667     if (u->sink)
668         pa_sink_unlink(u->sink);
669     if (u->source)
670         pa_source_unlink(u->source);
671
672     pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
673     if (u->thread)
674       pa_thread_free(u->thread);
675     pa_thread_mq_done(&u->thread_mq);
676
677     if (u->sink)
678         pa_sink_unref(u->sink);
679     if (u->source)
680         pa_source_unref(u->source);
681
682     if (u->rtpoll)
683         pa_rtpoll_free(u->rtpoll);
684
685     if (u->hwi != INVALID_HANDLE_VALUE) {
686         waveInReset(u->hwi);
687         waveInClose(u->hwi);
688     }
689
690     if (u->hwo != INVALID_HANDLE_VALUE) {
691         waveOutReset(u->hwo);
692         waveOutClose(u->hwo);
693     }
694
695     for (i = 0;i < u->fragments;i++) {
696         pa_xfree(u->ihdrs[i].lpData);
697         pa_xfree(u->ohdrs[i].lpData);
698     }
699
700     pa_xfree(u->ihdrs);
701     pa_xfree(u->ohdrs);
702
703     DeleteCriticalSection(&u->crit);
704
705     pa_xfree(u);
706 }