153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#include <windows.h>
2653a5a1b3Sopenharmony_ci#include <mmsystem.h>
2753a5a1b3Sopenharmony_ci#include <mmreg.h>
2853a5a1b3Sopenharmony_ci#include <string.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3153a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3253a5a1b3Sopenharmony_ci#include <pulse/util.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#include <pulsecore/sink.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/source.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
4353a5a1b3Sopenharmony_ci
4453a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Pierre Ossman");
4553a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Windows waveOut Sink/Source");
4653a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
4753a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
4853a5a1b3Sopenharmony_ci    "sink_name=<name for the sink> "
4953a5a1b3Sopenharmony_ci    "source_name=<name for the source> "
5053a5a1b3Sopenharmony_ci    "output_device=<device number for the sink> "
5153a5a1b3Sopenharmony_ci    "output_device_name=<name of the output device> "
5253a5a1b3Sopenharmony_ci    "input_device=<device number for the source> "
5353a5a1b3Sopenharmony_ci    "input_device_name=<name of the input device> "
5453a5a1b3Sopenharmony_ci    "record=<enable source?> "
5553a5a1b3Sopenharmony_ci    "playback=<enable sink?> "
5653a5a1b3Sopenharmony_ci    "format=<sample format> "
5753a5a1b3Sopenharmony_ci    "rate=<sample rate> "
5853a5a1b3Sopenharmony_ci    "channels=<number of channels> "
5953a5a1b3Sopenharmony_ci    "channel_map=<channel map> "
6053a5a1b3Sopenharmony_ci    "fragments=<number of fragments> "
6153a5a1b3Sopenharmony_ci    "fragment_size=<fragment size>"
6253a5a1b3Sopenharmony_ci    "device=<device number - deprecated>"
6353a5a1b3Sopenharmony_ci    "device_name=<name of the device - deprecated>");
6453a5a1b3Sopenharmony_ci
6553a5a1b3Sopenharmony_ci#define DEFAULT_SINK_NAME "wave_output"
6653a5a1b3Sopenharmony_ci#define DEFAULT_SOURCE_NAME "wave_input"
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci#define WAVEOUT_MAX_VOLUME 0xFFFF
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_cistruct userdata {
7153a5a1b3Sopenharmony_ci    pa_sink *sink;
7253a5a1b3Sopenharmony_ci    pa_source *source;
7353a5a1b3Sopenharmony_ci    pa_core *core;
7453a5a1b3Sopenharmony_ci    pa_usec_t poll_timeout;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    pa_thread *thread;
7753a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
7853a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    uint32_t fragments, fragment_size;
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_ci    uint32_t free_ofrags, free_ifrags;
8353a5a1b3Sopenharmony_ci
8453a5a1b3Sopenharmony_ci    DWORD written_bytes;
8553a5a1b3Sopenharmony_ci    int sink_underflow;
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_ci    int cur_ohdr, cur_ihdr;
8853a5a1b3Sopenharmony_ci    WAVEHDR *ohdrs, *ihdrs;
8953a5a1b3Sopenharmony_ci
9053a5a1b3Sopenharmony_ci    HWAVEOUT hwo;
9153a5a1b3Sopenharmony_ci    HWAVEIN hwi;
9253a5a1b3Sopenharmony_ci    pa_module *module;
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_ci    CRITICAL_SECTION crit;
9553a5a1b3Sopenharmony_ci};
9653a5a1b3Sopenharmony_ci
9753a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
9853a5a1b3Sopenharmony_ci    "sink_name",
9953a5a1b3Sopenharmony_ci    "source_name",
10053a5a1b3Sopenharmony_ci    "output_device",
10153a5a1b3Sopenharmony_ci    "output_device_name",
10253a5a1b3Sopenharmony_ci    "input_device",
10353a5a1b3Sopenharmony_ci    "input_device_name",
10453a5a1b3Sopenharmony_ci    "record",
10553a5a1b3Sopenharmony_ci    "playback",
10653a5a1b3Sopenharmony_ci    "fragments",
10753a5a1b3Sopenharmony_ci    "fragment_size",
10853a5a1b3Sopenharmony_ci    "format",
10953a5a1b3Sopenharmony_ci    "rate",
11053a5a1b3Sopenharmony_ci    "channels",
11153a5a1b3Sopenharmony_ci    "channel_map",
11253a5a1b3Sopenharmony_ci    "device",
11353a5a1b3Sopenharmony_ci    "device_name",
11453a5a1b3Sopenharmony_ci    NULL
11553a5a1b3Sopenharmony_ci};
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_cistatic void do_write(struct userdata *u) {
11853a5a1b3Sopenharmony_ci    uint32_t free_frags;
11953a5a1b3Sopenharmony_ci    pa_memchunk memchunk;
12053a5a1b3Sopenharmony_ci    WAVEHDR *hdr;
12153a5a1b3Sopenharmony_ci    MMRESULT res;
12253a5a1b3Sopenharmony_ci    void *p;
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    if (!u->sink)
12553a5a1b3Sopenharmony_ci        return;
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_LINKED(u->sink->state))
12853a5a1b3Sopenharmony_ci        return;
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    EnterCriticalSection(&u->crit);
13153a5a1b3Sopenharmony_ci    free_frags = u->free_ofrags;
13253a5a1b3Sopenharmony_ci    LeaveCriticalSection(&u->crit);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    if (!u->sink_underflow && (free_frags == u->fragments))
13553a5a1b3Sopenharmony_ci        pa_log_debug("WaveOut underflow!");
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci    while (free_frags) {
13853a5a1b3Sopenharmony_ci        hdr = &u->ohdrs[u->cur_ohdr];
13953a5a1b3Sopenharmony_ci        if (hdr->dwFlags & WHDR_PREPARED)
14053a5a1b3Sopenharmony_ci            waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci        hdr->dwBufferLength = 0;
14353a5a1b3Sopenharmony_ci        while (hdr->dwBufferLength < u->fragment_size) {
14453a5a1b3Sopenharmony_ci            size_t len;
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_ci            len = u->fragment_size - hdr->dwBufferLength;
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci            pa_sink_render(u->sink, len, &memchunk);
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci            pa_assert(memchunk.memblock);
15153a5a1b3Sopenharmony_ci            pa_assert(memchunk.length);
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci            if (memchunk.length < len)
15453a5a1b3Sopenharmony_ci                len = memchunk.length;
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci            p = pa_memblock_acquire(memchunk.memblock);
15753a5a1b3Sopenharmony_ci            memcpy(hdr->lpData + hdr->dwBufferLength, (char*) p + memchunk.index, len);
15853a5a1b3Sopenharmony_ci            pa_memblock_release(memchunk.memblock);
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci            hdr->dwBufferLength += len;
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci            pa_memblock_unref(memchunk.memblock);
16353a5a1b3Sopenharmony_ci            memchunk.memblock = NULL;
16453a5a1b3Sopenharmony_ci        }
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci        /* Underflow detection */
16753a5a1b3Sopenharmony_ci        if (hdr->dwBufferLength == 0) {
16853a5a1b3Sopenharmony_ci            u->sink_underflow = 1;
16953a5a1b3Sopenharmony_ci            break;
17053a5a1b3Sopenharmony_ci        }
17153a5a1b3Sopenharmony_ci        u->sink_underflow = 0;
17253a5a1b3Sopenharmony_ci
17353a5a1b3Sopenharmony_ci        res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
17453a5a1b3Sopenharmony_ci        if (res != MMSYSERR_NOERROR)
17553a5a1b3Sopenharmony_ci            pa_log_error("Unable to prepare waveOut block: %d", res);
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci        res = waveOutWrite(u->hwo, hdr, sizeof(WAVEHDR));
17853a5a1b3Sopenharmony_ci        if (res != MMSYSERR_NOERROR)
17953a5a1b3Sopenharmony_ci            pa_log_error("Unable to write waveOut block: %d", res);
18053a5a1b3Sopenharmony_ci
18153a5a1b3Sopenharmony_ci        u->written_bytes += hdr->dwBufferLength;
18253a5a1b3Sopenharmony_ci
18353a5a1b3Sopenharmony_ci        EnterCriticalSection(&u->crit);
18453a5a1b3Sopenharmony_ci        u->free_ofrags--;
18553a5a1b3Sopenharmony_ci        LeaveCriticalSection(&u->crit);
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci        free_frags--;
18853a5a1b3Sopenharmony_ci        u->cur_ohdr++;
18953a5a1b3Sopenharmony_ci        u->cur_ohdr %= u->fragments;
19053a5a1b3Sopenharmony_ci    }
19153a5a1b3Sopenharmony_ci}
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_cistatic void do_read(struct userdata *u) {
19453a5a1b3Sopenharmony_ci    uint32_t free_frags;
19553a5a1b3Sopenharmony_ci    pa_memchunk memchunk;
19653a5a1b3Sopenharmony_ci    WAVEHDR *hdr;
19753a5a1b3Sopenharmony_ci    MMRESULT res;
19853a5a1b3Sopenharmony_ci    void *p;
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci    if (!u->source)
20153a5a1b3Sopenharmony_ci        return;
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    if (!PA_SOURCE_IS_LINKED(u->source->state))
20453a5a1b3Sopenharmony_ci        return;
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci    EnterCriticalSection(&u->crit);
20753a5a1b3Sopenharmony_ci    free_frags = u->free_ifrags;
20853a5a1b3Sopenharmony_ci    u->free_ifrags = 0;
20953a5a1b3Sopenharmony_ci    LeaveCriticalSection(&u->crit);
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci    if (free_frags == u->fragments)
21253a5a1b3Sopenharmony_ci        pa_log_debug("WaveIn overflow!");
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci    while (free_frags) {
21553a5a1b3Sopenharmony_ci        hdr = &u->ihdrs[u->cur_ihdr];
21653a5a1b3Sopenharmony_ci        if (hdr->dwFlags & WHDR_PREPARED)
21753a5a1b3Sopenharmony_ci            waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci        if (hdr->dwBytesRecorded) {
22053a5a1b3Sopenharmony_ci            memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded);
22153a5a1b3Sopenharmony_ci            pa_assert(memchunk.memblock);
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci            p = pa_memblock_acquire(memchunk.memblock);
22453a5a1b3Sopenharmony_ci            memcpy((char*) p, hdr->lpData, hdr->dwBytesRecorded);
22553a5a1b3Sopenharmony_ci            pa_memblock_release(memchunk.memblock);
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci            memchunk.length = hdr->dwBytesRecorded;
22853a5a1b3Sopenharmony_ci            memchunk.index = 0;
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci            pa_source_post(u->source, &memchunk);
23153a5a1b3Sopenharmony_ci            pa_memblock_unref(memchunk.memblock);
23253a5a1b3Sopenharmony_ci        }
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci        res = waveInPrepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
23553a5a1b3Sopenharmony_ci        if (res != MMSYSERR_NOERROR)
23653a5a1b3Sopenharmony_ci            pa_log_error("Unable to prepare waveIn block: %d", res);
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci        res = waveInAddBuffer(u->hwi, hdr, sizeof(WAVEHDR));
23953a5a1b3Sopenharmony_ci        if (res != MMSYSERR_NOERROR)
24053a5a1b3Sopenharmony_ci            pa_log_error("Unable to add waveIn block: %d", res);
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ci        free_frags--;
24353a5a1b3Sopenharmony_ci        u->cur_ihdr++;
24453a5a1b3Sopenharmony_ci        u->cur_ihdr %= u->fragments;
24553a5a1b3Sopenharmony_ci    }
24653a5a1b3Sopenharmony_ci}
24753a5a1b3Sopenharmony_ci
24853a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
24953a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    pa_assert(u);
25253a5a1b3Sopenharmony_ci    pa_assert(u->sink || u->source);
25353a5a1b3Sopenharmony_ci
25453a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci    if (u->core->realtime_scheduling)
25753a5a1b3Sopenharmony_ci        pa_thread_make_realtime(u->core->realtime_priority);
25853a5a1b3Sopenharmony_ci
25953a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
26053a5a1b3Sopenharmony_ci
26153a5a1b3Sopenharmony_ci    for (;;) {
26253a5a1b3Sopenharmony_ci        int ret;
26353a5a1b3Sopenharmony_ci        bool need_timer = false;
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci        if (u->sink) {
26653a5a1b3Sopenharmony_ci            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
26753a5a1b3Sopenharmony_ci                pa_sink_process_rewind(u->sink, 0);
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci            if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
27053a5a1b3Sopenharmony_ci                do_write(u);
27153a5a1b3Sopenharmony_ci                need_timer = true;
27253a5a1b3Sopenharmony_ci            }
27353a5a1b3Sopenharmony_ci        }
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
27653a5a1b3Sopenharmony_ci            do_read(u);
27753a5a1b3Sopenharmony_ci            need_timer = true;
27853a5a1b3Sopenharmony_ci        }
27953a5a1b3Sopenharmony_ci
28053a5a1b3Sopenharmony_ci        if (need_timer)
28153a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_relative(u->rtpoll, u->poll_timeout);
28253a5a1b3Sopenharmony_ci        else
28353a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_disabled(u->rtpoll);
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci        /* Hmm, nothing to do. Let's sleep */
28653a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
28753a5a1b3Sopenharmony_ci            goto fail;
28853a5a1b3Sopenharmony_ci
28953a5a1b3Sopenharmony_ci        if (ret == 0)
29053a5a1b3Sopenharmony_ci            goto finish;
29153a5a1b3Sopenharmony_ci    }
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_cifail:
29453a5a1b3Sopenharmony_ci    /* If this was no regular exit from the loop we have to continue
29553a5a1b3Sopenharmony_ci     * processing messages until we received PA_MESSAGE_SHUTDOWN */
29653a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
29753a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_cifinish:
30053a5a1b3Sopenharmony_ci    pa_log_debug("Thread shutting down");
30153a5a1b3Sopenharmony_ci}
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_cistatic void CALLBACK chunk_done_cb(HWAVEOUT hwo, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
30453a5a1b3Sopenharmony_ci    struct userdata *u = (struct userdata*) inst;
30553a5a1b3Sopenharmony_ci
30653a5a1b3Sopenharmony_ci    if (msg == WOM_OPEN)
30753a5a1b3Sopenharmony_ci        pa_log_debug("WaveOut subsystem opened.");
30853a5a1b3Sopenharmony_ci    if (msg == WOM_CLOSE)
30953a5a1b3Sopenharmony_ci        pa_log_debug("WaveOut subsystem closed.");
31053a5a1b3Sopenharmony_ci    if (msg != WOM_DONE)
31153a5a1b3Sopenharmony_ci        return;
31253a5a1b3Sopenharmony_ci
31353a5a1b3Sopenharmony_ci    EnterCriticalSection(&u->crit);
31453a5a1b3Sopenharmony_ci    u->free_ofrags++;
31553a5a1b3Sopenharmony_ci    pa_assert(u->free_ofrags <= u->fragments);
31653a5a1b3Sopenharmony_ci    LeaveCriticalSection(&u->crit);
31753a5a1b3Sopenharmony_ci}
31853a5a1b3Sopenharmony_ci
31953a5a1b3Sopenharmony_cistatic void CALLBACK chunk_ready_cb(HWAVEIN hwi, UINT msg, DWORD_PTR inst, DWORD param1, DWORD param2) {
32053a5a1b3Sopenharmony_ci    struct userdata *u = (struct userdata*) inst;
32153a5a1b3Sopenharmony_ci
32253a5a1b3Sopenharmony_ci    if (msg == WIM_OPEN)
32353a5a1b3Sopenharmony_ci        pa_log_debug("WaveIn subsystem opened.");
32453a5a1b3Sopenharmony_ci    if (msg == WIM_CLOSE)
32553a5a1b3Sopenharmony_ci        pa_log_debug("WaveIn subsystem closed.");
32653a5a1b3Sopenharmony_ci    if (msg != WIM_DATA)
32753a5a1b3Sopenharmony_ci        return;
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    EnterCriticalSection(&u->crit);
33053a5a1b3Sopenharmony_ci    u->free_ifrags++;
33153a5a1b3Sopenharmony_ci    pa_assert(u->free_ifrags <= u->fragments);
33253a5a1b3Sopenharmony_ci    LeaveCriticalSection(&u->crit);
33353a5a1b3Sopenharmony_ci}
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_cistatic pa_usec_t sink_get_latency(struct userdata *u) {
33653a5a1b3Sopenharmony_ci    uint32_t free_frags;
33753a5a1b3Sopenharmony_ci    MMTIME mmt;
33853a5a1b3Sopenharmony_ci    pa_assert(u);
33953a5a1b3Sopenharmony_ci    pa_assert(u->sink);
34053a5a1b3Sopenharmony_ci
34153a5a1b3Sopenharmony_ci    memset(&mmt, 0, sizeof(mmt));
34253a5a1b3Sopenharmony_ci    mmt.wType = TIME_BYTES;
34353a5a1b3Sopenharmony_ci    if (waveOutGetPosition(u->hwo, &mmt, sizeof(mmt)) == MMSYSERR_NOERROR)
34453a5a1b3Sopenharmony_ci        return pa_bytes_to_usec(u->written_bytes - mmt.u.cb, &u->sink->sample_spec);
34553a5a1b3Sopenharmony_ci    else {
34653a5a1b3Sopenharmony_ci        EnterCriticalSection(&u->crit);
34753a5a1b3Sopenharmony_ci        free_frags = u->free_ofrags;
34853a5a1b3Sopenharmony_ci        LeaveCriticalSection(&u->crit);
34953a5a1b3Sopenharmony_ci
35053a5a1b3Sopenharmony_ci        return pa_bytes_to_usec((u->fragments - free_frags) * u->fragment_size, &u->sink->sample_spec);
35153a5a1b3Sopenharmony_ci    }
35253a5a1b3Sopenharmony_ci}
35353a5a1b3Sopenharmony_ci
35453a5a1b3Sopenharmony_cistatic pa_usec_t source_get_latency(struct userdata *u) {
35553a5a1b3Sopenharmony_ci    pa_usec_t r = 0;
35653a5a1b3Sopenharmony_ci    uint32_t free_frags;
35753a5a1b3Sopenharmony_ci    pa_assert(u);
35853a5a1b3Sopenharmony_ci    pa_assert(u->source);
35953a5a1b3Sopenharmony_ci
36053a5a1b3Sopenharmony_ci    EnterCriticalSection(&u->crit);
36153a5a1b3Sopenharmony_ci    free_frags = u->free_ifrags;
36253a5a1b3Sopenharmony_ci    LeaveCriticalSection(&u->crit);
36353a5a1b3Sopenharmony_ci
36453a5a1b3Sopenharmony_ci    r += pa_bytes_to_usec((free_frags + 1) * u->fragment_size, &u->source->sample_spec);
36553a5a1b3Sopenharmony_ci
36653a5a1b3Sopenharmony_ci    return r;
36753a5a1b3Sopenharmony_ci}
36853a5a1b3Sopenharmony_ci
36953a5a1b3Sopenharmony_cistatic int process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
37053a5a1b3Sopenharmony_ci    struct userdata *u;
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    if (pa_sink_isinstance(o)) {
37353a5a1b3Sopenharmony_ci        u = PA_SINK(o)->userdata;
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci        switch (code) {
37653a5a1b3Sopenharmony_ci
37753a5a1b3Sopenharmony_ci            case PA_SINK_MESSAGE_GET_LATENCY: {
37853a5a1b3Sopenharmony_ci                pa_usec_t r = 0;
37953a5a1b3Sopenharmony_ci                if (u->hwo)
38053a5a1b3Sopenharmony_ci                    r = sink_get_latency(u);
38153a5a1b3Sopenharmony_ci                *((int64_t*) data) = (int64_t)r;
38253a5a1b3Sopenharmony_ci                return 0;
38353a5a1b3Sopenharmony_ci            }
38453a5a1b3Sopenharmony_ci
38553a5a1b3Sopenharmony_ci        }
38653a5a1b3Sopenharmony_ci
38753a5a1b3Sopenharmony_ci        return pa_sink_process_msg(o, code, data, offset, chunk);
38853a5a1b3Sopenharmony_ci    }
38953a5a1b3Sopenharmony_ci
39053a5a1b3Sopenharmony_ci    if (pa_source_isinstance(o)) {
39153a5a1b3Sopenharmony_ci        u = PA_SOURCE(o)->userdata;
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_ci        switch (code) {
39453a5a1b3Sopenharmony_ci
39553a5a1b3Sopenharmony_ci            case PA_SOURCE_MESSAGE_GET_LATENCY: {
39653a5a1b3Sopenharmony_ci                pa_usec_t r = 0;
39753a5a1b3Sopenharmony_ci                if (u->hwi)
39853a5a1b3Sopenharmony_ci                    r = source_get_latency(u);
39953a5a1b3Sopenharmony_ci                *((int64_t*) data) = (int64_t)r;
40053a5a1b3Sopenharmony_ci                return 0;
40153a5a1b3Sopenharmony_ci            }
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_ci        }
40453a5a1b3Sopenharmony_ci
40553a5a1b3Sopenharmony_ci        return pa_source_process_msg(o, code, data, offset, chunk);
40653a5a1b3Sopenharmony_ci    }
40753a5a1b3Sopenharmony_ci
40853a5a1b3Sopenharmony_ci    return -1;
40953a5a1b3Sopenharmony_ci}
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_cistatic void sink_get_volume_cb(pa_sink *s) {
41253a5a1b3Sopenharmony_ci    struct userdata *u = s->userdata;
41353a5a1b3Sopenharmony_ci    WAVEOUTCAPS caps;
41453a5a1b3Sopenharmony_ci    DWORD vol;
41553a5a1b3Sopenharmony_ci    pa_volume_t left, right;
41653a5a1b3Sopenharmony_ci
41753a5a1b3Sopenharmony_ci    if (waveOutGetDevCaps((UINT_PTR) u->hwo, &caps, sizeof(caps)) != MMSYSERR_NOERROR)
41853a5a1b3Sopenharmony_ci        return;
41953a5a1b3Sopenharmony_ci    if (!(caps.dwSupport & WAVECAPS_VOLUME))
42053a5a1b3Sopenharmony_ci        return;
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_ci    if (waveOutGetVolume(u->hwo, &vol) != MMSYSERR_NOERROR)
42353a5a1b3Sopenharmony_ci        return;
42453a5a1b3Sopenharmony_ci
42553a5a1b3Sopenharmony_ci    left = PA_CLAMP_VOLUME((vol & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
42653a5a1b3Sopenharmony_ci    if (caps.dwSupport & WAVECAPS_LRVOLUME)
42753a5a1b3Sopenharmony_ci        right = PA_CLAMP_VOLUME(((vol >> 16) & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
42853a5a1b3Sopenharmony_ci    else
42953a5a1b3Sopenharmony_ci        right = left;
43053a5a1b3Sopenharmony_ci
43153a5a1b3Sopenharmony_ci    /* Windows supports > 2 channels, except for volume control */
43253a5a1b3Sopenharmony_ci    if (s->real_volume.channels > 2)
43353a5a1b3Sopenharmony_ci        pa_cvolume_set(&s->real_volume, s->real_volume.channels, (left + right)/2);
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_ci    s->real_volume.values[0] = left;
43653a5a1b3Sopenharmony_ci    if (s->real_volume.channels > 1)
43753a5a1b3Sopenharmony_ci        s->real_volume.values[1] = right;
43853a5a1b3Sopenharmony_ci}
43953a5a1b3Sopenharmony_ci
44053a5a1b3Sopenharmony_cistatic void sink_set_volume_cb(pa_sink *s) {
44153a5a1b3Sopenharmony_ci    struct userdata *u = s->userdata;
44253a5a1b3Sopenharmony_ci    WAVEOUTCAPS caps;
44353a5a1b3Sopenharmony_ci    DWORD vol;
44453a5a1b3Sopenharmony_ci
44553a5a1b3Sopenharmony_ci    if (waveOutGetDevCaps((UINT_PTR) u->hwo, &caps, sizeof(caps)) != MMSYSERR_NOERROR)
44653a5a1b3Sopenharmony_ci        return;
44753a5a1b3Sopenharmony_ci    if (!(caps.dwSupport & WAVECAPS_VOLUME))
44853a5a1b3Sopenharmony_ci        return;
44953a5a1b3Sopenharmony_ci
45053a5a1b3Sopenharmony_ci    if (s->real_volume.channels == 2 && caps.dwSupport & WAVECAPS_LRVOLUME) {
45153a5a1b3Sopenharmony_ci        vol = (s->real_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM)
45253a5a1b3Sopenharmony_ci            | (s->real_volume.values[1] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
45353a5a1b3Sopenharmony_ci    } else {
45453a5a1b3Sopenharmony_ci        vol = (pa_cvolume_avg(&(s->real_volume)) * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM)
45553a5a1b3Sopenharmony_ci            | (pa_cvolume_avg(&(s->real_volume)) * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
45653a5a1b3Sopenharmony_ci    }
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci    if (waveOutSetVolume(u->hwo, vol) != MMSYSERR_NOERROR)
45953a5a1b3Sopenharmony_ci        return;
46053a5a1b3Sopenharmony_ci}
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_cistatic DWORD channel_position_to_wavefmt(pa_channel_position_t channel) {
46353a5a1b3Sopenharmony_ci    switch(channel) {
46453a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_MONO:
46553a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_FRONT_LEFT:
46653a5a1b3Sopenharmony_ci            return SPEAKER_FRONT_LEFT;
46753a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_FRONT_RIGHT:
46853a5a1b3Sopenharmony_ci            return SPEAKER_FRONT_RIGHT;
46953a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_FRONT_CENTER:
47053a5a1b3Sopenharmony_ci            return SPEAKER_FRONT_CENTER;
47153a5a1b3Sopenharmony_ci
47253a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_REAR_LEFT:
47353a5a1b3Sopenharmony_ci            return SPEAKER_BACK_LEFT;
47453a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_REAR_RIGHT:
47553a5a1b3Sopenharmony_ci            return SPEAKER_BACK_RIGHT;
47653a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_REAR_CENTER:
47753a5a1b3Sopenharmony_ci            return SPEAKER_BACK_CENTER;
47853a5a1b3Sopenharmony_ci
47953a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_LFE:
48053a5a1b3Sopenharmony_ci            return SPEAKER_LOW_FREQUENCY;
48153a5a1b3Sopenharmony_ci
48253a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
48353a5a1b3Sopenharmony_ci            return SPEAKER_FRONT_LEFT_OF_CENTER;
48453a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
48553a5a1b3Sopenharmony_ci            return SPEAKER_FRONT_RIGHT_OF_CENTER;
48653a5a1b3Sopenharmony_ci
48753a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_SIDE_LEFT:
48853a5a1b3Sopenharmony_ci            return SPEAKER_SIDE_LEFT;
48953a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_SIDE_RIGHT:
49053a5a1b3Sopenharmony_ci            return SPEAKER_SIDE_RIGHT;
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_CENTER:
49353a5a1b3Sopenharmony_ci            return SPEAKER_TOP_CENTER;
49453a5a1b3Sopenharmony_ci
49553a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
49653a5a1b3Sopenharmony_ci            return SPEAKER_TOP_FRONT_LEFT;
49753a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
49853a5a1b3Sopenharmony_ci            return SPEAKER_TOP_FRONT_RIGHT;
49953a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_FRONT_CENTER:
50053a5a1b3Sopenharmony_ci            return SPEAKER_TOP_FRONT_CENTER;
50153a5a1b3Sopenharmony_ci
50253a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
50353a5a1b3Sopenharmony_ci            return SPEAKER_TOP_BACK_LEFT;
50453a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
50553a5a1b3Sopenharmony_ci            return SPEAKER_TOP_BACK_RIGHT;
50653a5a1b3Sopenharmony_ci        case PA_CHANNEL_POSITION_TOP_REAR_CENTER:
50753a5a1b3Sopenharmony_ci            return SPEAKER_TOP_BACK_CENTER;
50853a5a1b3Sopenharmony_ci
50953a5a1b3Sopenharmony_ci        default:
51053a5a1b3Sopenharmony_ci            return 0;
51153a5a1b3Sopenharmony_ci    }
51253a5a1b3Sopenharmony_ci}
51353a5a1b3Sopenharmony_ci
51453a5a1b3Sopenharmony_cistatic int ss_to_waveformat(pa_sample_spec *ss, pa_channel_map *map, PWAVEFORMATEXTENSIBLE wf) {
51553a5a1b3Sopenharmony_ci    wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
51653a5a1b3Sopenharmony_ci    wf->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
51753a5a1b3Sopenharmony_ci
51853a5a1b3Sopenharmony_ci    wf->Format.nChannels = ss->channels;
51953a5a1b3Sopenharmony_ci    wf->Format.nSamplesPerSec = ss->rate;
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci    wf->dwChannelMask = 0;
52253a5a1b3Sopenharmony_ci    for (int i = 0; i < map->channels; i++) {
52353a5a1b3Sopenharmony_ci        DWORD thisSpeaker = channel_position_to_wavefmt(map->map[i]);
52453a5a1b3Sopenharmony_ci        if (thisSpeaker == 0 || (wf->dwChannelMask & thisSpeaker)) {
52553a5a1b3Sopenharmony_ci            pa_log_error("Invalid channel map: unknown or duplicated channel %d.", map->map[i]);
52653a5a1b3Sopenharmony_ci            return -1;
52753a5a1b3Sopenharmony_ci        }
52853a5a1b3Sopenharmony_ci        wf->dwChannelMask |= thisSpeaker;
52953a5a1b3Sopenharmony_ci    }
53053a5a1b3Sopenharmony_ci
53153a5a1b3Sopenharmony_ci    if (ss->format == PA_SAMPLE_U8) {
53253a5a1b3Sopenharmony_ci        wf->Format.wBitsPerSample = 8;
53353a5a1b3Sopenharmony_ci        wf->Samples.wValidBitsPerSample = 8;
53453a5a1b3Sopenharmony_ci    } else if (ss->format == PA_SAMPLE_S16LE) {
53553a5a1b3Sopenharmony_ci        wf->Format.wBitsPerSample = 16;
53653a5a1b3Sopenharmony_ci        wf->Samples.wValidBitsPerSample = 16;
53753a5a1b3Sopenharmony_ci    } else if (ss->format == PA_SAMPLE_S24LE) {
53853a5a1b3Sopenharmony_ci        wf->Format.wBitsPerSample = 24;
53953a5a1b3Sopenharmony_ci        wf->Samples.wValidBitsPerSample = 24;
54053a5a1b3Sopenharmony_ci    } else if (ss->format == PA_SAMPLE_S32LE) {
54153a5a1b3Sopenharmony_ci        wf->Format.wBitsPerSample = 32;
54253a5a1b3Sopenharmony_ci        wf->Samples.wValidBitsPerSample = 32;
54353a5a1b3Sopenharmony_ci    } else {
54453a5a1b3Sopenharmony_ci        pa_log_error("Unsupported sample format, only u8, s16le, s24le, and s32le are supported.");
54553a5a1b3Sopenharmony_ci        return -1;
54653a5a1b3Sopenharmony_ci    }
54753a5a1b3Sopenharmony_ci
54853a5a1b3Sopenharmony_ci    wf->Format.nBlockAlign = wf->Format.nChannels * wf->Format.wBitsPerSample/8;
54953a5a1b3Sopenharmony_ci    wf->Format.nAvgBytesPerSec = wf->Format.nSamplesPerSec * wf->Format.nBlockAlign;
55053a5a1b3Sopenharmony_ci
55153a5a1b3Sopenharmony_ci    wf->Format.cbSize = 22;
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_ci    return 0;
55453a5a1b3Sopenharmony_ci}
55553a5a1b3Sopenharmony_ci
55653a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) {
55753a5a1b3Sopenharmony_ci    struct userdata *u;
55853a5a1b3Sopenharmony_ci    pa_assert(m);
55953a5a1b3Sopenharmony_ci    pa_assert(m->userdata);
56053a5a1b3Sopenharmony_ci    u = (struct userdata*) m->userdata;
56153a5a1b3Sopenharmony_ci
56253a5a1b3Sopenharmony_ci    return (u->sink ? pa_sink_used_by(u->sink) : 0) +
56353a5a1b3Sopenharmony_ci           (u->source ? pa_source_used_by(u->source) : 0);
56453a5a1b3Sopenharmony_ci}
56553a5a1b3Sopenharmony_ci
56653a5a1b3Sopenharmony_ciint pa__init(pa_module *m) {
56753a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
56853a5a1b3Sopenharmony_ci    HWAVEOUT hwo = INVALID_HANDLE_VALUE;
56953a5a1b3Sopenharmony_ci    HWAVEIN hwi = INVALID_HANDLE_VALUE;
57053a5a1b3Sopenharmony_ci    WAVEFORMATEXTENSIBLE wf;
57153a5a1b3Sopenharmony_ci    WAVEOUTCAPS pwoc;
57253a5a1b3Sopenharmony_ci    WAVEINCAPS pwic;
57353a5a1b3Sopenharmony_ci    MMRESULT result;
57453a5a1b3Sopenharmony_ci    int nfrags, frag_size;
57553a5a1b3Sopenharmony_ci    bool record = true, playback = true;
57653a5a1b3Sopenharmony_ci    unsigned int input_device;
57753a5a1b3Sopenharmony_ci    unsigned int output_device;
57853a5a1b3Sopenharmony_ci    pa_sample_spec ss;
57953a5a1b3Sopenharmony_ci    pa_channel_map map;
58053a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
58153a5a1b3Sopenharmony_ci    const char *input_device_name = NULL;
58253a5a1b3Sopenharmony_ci    const char *output_device_name = NULL;
58353a5a1b3Sopenharmony_ci    unsigned int i;
58453a5a1b3Sopenharmony_ci
58553a5a1b3Sopenharmony_ci    pa_assert(m);
58653a5a1b3Sopenharmony_ci    pa_assert(m->core);
58753a5a1b3Sopenharmony_ci
58853a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
58953a5a1b3Sopenharmony_ci        pa_log("failed to parse module arguments.");
59053a5a1b3Sopenharmony_ci        goto fail;
59153a5a1b3Sopenharmony_ci    }
59253a5a1b3Sopenharmony_ci
59353a5a1b3Sopenharmony_ci    /* Check whether deprecated arguments have been used. */
59453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value(ma, "device", NULL) != NULL || pa_modargs_get_value(ma, "device_name", NULL) != NULL) {
59553a5a1b3Sopenharmony_ci        pa_log("device and device_name are no longer supported. Please use input_device, input_device_name, output_device and output_device_name.");
59653a5a1b3Sopenharmony_ci        goto fail;
59753a5a1b3Sopenharmony_ci    }
59853a5a1b3Sopenharmony_ci
59953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
60053a5a1b3Sopenharmony_ci        pa_log("record= and playback= expect boolean argument.");
60153a5a1b3Sopenharmony_ci        goto fail;
60253a5a1b3Sopenharmony_ci    }
60353a5a1b3Sopenharmony_ci
60453a5a1b3Sopenharmony_ci    if (!playback && !record) {
60553a5a1b3Sopenharmony_ci        pa_log("neither playback nor record enabled for device.");
60653a5a1b3Sopenharmony_ci        goto fail;
60753a5a1b3Sopenharmony_ci    }
60853a5a1b3Sopenharmony_ci
60953a5a1b3Sopenharmony_ci    /* Set the output_device to be opened. If set output_device_name is used,
61053a5a1b3Sopenharmony_ci     * else output_device if set and lastly WAVE_MAPPER is the default */
61153a5a1b3Sopenharmony_ci    output_device = WAVE_MAPPER;
61253a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "output_device", &output_device) < 0) {
61353a5a1b3Sopenharmony_ci        pa_log("failed to parse output_device argument");
61453a5a1b3Sopenharmony_ci        goto fail;
61553a5a1b3Sopenharmony_ci    }
61653a5a1b3Sopenharmony_ci    if ((output_device_name = pa_modargs_get_value(ma, "output_device_name", NULL)) != NULL) {
61753a5a1b3Sopenharmony_ci        unsigned int num_output_devices = waveOutGetNumDevs();
61853a5a1b3Sopenharmony_ci        for (i = 0; i < num_output_devices; i++) {
61953a5a1b3Sopenharmony_ci            if (waveOutGetDevCaps(i, &pwoc, sizeof(pwoc)) == MMSYSERR_NOERROR)
62053a5a1b3Sopenharmony_ci                if (strcmp(output_device_name, pwoc.szPname) == 0)
62153a5a1b3Sopenharmony_ci                    break;
62253a5a1b3Sopenharmony_ci        }
62353a5a1b3Sopenharmony_ci        if (i < num_output_devices)
62453a5a1b3Sopenharmony_ci            output_device = i;
62553a5a1b3Sopenharmony_ci        else {
62653a5a1b3Sopenharmony_ci            pa_log("output_device not found: %s", output_device_name);
62753a5a1b3Sopenharmony_ci            goto fail;
62853a5a1b3Sopenharmony_ci        }
62953a5a1b3Sopenharmony_ci    }
63053a5a1b3Sopenharmony_ci    if (waveOutGetDevCaps(output_device, &pwoc, sizeof(pwoc)) == MMSYSERR_NOERROR)
63153a5a1b3Sopenharmony_ci        output_device_name = pwoc.szPname;
63253a5a1b3Sopenharmony_ci    else
63353a5a1b3Sopenharmony_ci        output_device_name = "unknown";
63453a5a1b3Sopenharmony_ci
63553a5a1b3Sopenharmony_ci    /* Set the input_device to be opened. If set input_device_name is used,
63653a5a1b3Sopenharmony_ci     * else input_device if set and lastly WAVE_MAPPER is the default */
63753a5a1b3Sopenharmony_ci    input_device = WAVE_MAPPER;
63853a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "input_device", &input_device) < 0) {
63953a5a1b3Sopenharmony_ci        pa_log("failed to parse input_device argument");
64053a5a1b3Sopenharmony_ci        goto fail;
64153a5a1b3Sopenharmony_ci    }
64253a5a1b3Sopenharmony_ci    if ((input_device_name = pa_modargs_get_value(ma, "input_device_name", NULL)) != NULL) {
64353a5a1b3Sopenharmony_ci        unsigned int num_input_devices = waveInGetNumDevs();
64453a5a1b3Sopenharmony_ci        for (i = 0; i < num_input_devices; i++) {
64553a5a1b3Sopenharmony_ci            if (waveInGetDevCaps(i, &pwic, sizeof(pwic)) == MMSYSERR_NOERROR)
64653a5a1b3Sopenharmony_ci                if (strcmp(input_device_name, pwic.szPname) == 0)
64753a5a1b3Sopenharmony_ci                    break;
64853a5a1b3Sopenharmony_ci        }
64953a5a1b3Sopenharmony_ci        if (i < num_input_devices)
65053a5a1b3Sopenharmony_ci            input_device = i;
65153a5a1b3Sopenharmony_ci        else {
65253a5a1b3Sopenharmony_ci            pa_log("input_device not found: %s", input_device_name);
65353a5a1b3Sopenharmony_ci            goto fail;
65453a5a1b3Sopenharmony_ci        }
65553a5a1b3Sopenharmony_ci    }
65653a5a1b3Sopenharmony_ci    if (waveInGetDevCaps(input_device, &pwic, sizeof(pwic)) == MMSYSERR_NOERROR)
65753a5a1b3Sopenharmony_ci        input_device_name = pwic.szPname;
65853a5a1b3Sopenharmony_ci    else
65953a5a1b3Sopenharmony_ci        input_device_name = "unknown";
66053a5a1b3Sopenharmony_ci
66153a5a1b3Sopenharmony_ci
66253a5a1b3Sopenharmony_ci    nfrags = 5;
66353a5a1b3Sopenharmony_ci    frag_size = 8192;
66453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
66553a5a1b3Sopenharmony_ci        pa_log("failed to parse fragments arguments");
66653a5a1b3Sopenharmony_ci        goto fail;
66753a5a1b3Sopenharmony_ci    }
66853a5a1b3Sopenharmony_ci
66953a5a1b3Sopenharmony_ci    ss = m->core->default_sample_spec;
67053a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_WAVEEX) < 0) {
67153a5a1b3Sopenharmony_ci        pa_log("failed to parse sample specification");
67253a5a1b3Sopenharmony_ci        goto fail;
67353a5a1b3Sopenharmony_ci    }
67453a5a1b3Sopenharmony_ci
67553a5a1b3Sopenharmony_ci    if (ss_to_waveformat(&ss, &map, &wf) < 0)
67653a5a1b3Sopenharmony_ci        goto fail;
67753a5a1b3Sopenharmony_ci
67853a5a1b3Sopenharmony_ci    u = pa_xmalloc(sizeof(struct userdata));
67953a5a1b3Sopenharmony_ci
68053a5a1b3Sopenharmony_ci    if (record) {
68153a5a1b3Sopenharmony_ci        result = waveInOpen(&hwi, input_device, &wf, 0, 0, WAVE_FORMAT_DIRECT | WAVE_FORMAT_QUERY);
68253a5a1b3Sopenharmony_ci        if (result != MMSYSERR_NOERROR) {
68353a5a1b3Sopenharmony_ci            pa_log_warn("Sample spec not supported by WaveIn, falling back to default sample rate.");
68453a5a1b3Sopenharmony_ci            ss.rate = wf.Format.nSamplesPerSec = m->core->default_sample_spec.rate;
68553a5a1b3Sopenharmony_ci        }
68653a5a1b3Sopenharmony_ci        result = waveInOpen(&hwi, input_device, &wf, (DWORD_PTR) chunk_ready_cb, (DWORD_PTR) u, CALLBACK_FUNCTION);
68753a5a1b3Sopenharmony_ci        if (result != MMSYSERR_NOERROR) {
68853a5a1b3Sopenharmony_ci            char errortext[MAXERRORLENGTH];
68953a5a1b3Sopenharmony_ci            pa_log("Failed to open WaveIn.");
69053a5a1b3Sopenharmony_ci            if (waveInGetErrorText(result, errortext, sizeof(errortext)) == MMSYSERR_NOERROR)
69153a5a1b3Sopenharmony_ci                pa_log("Error: %s", errortext);
69253a5a1b3Sopenharmony_ci            goto fail;
69353a5a1b3Sopenharmony_ci        }
69453a5a1b3Sopenharmony_ci        if (waveInStart(hwi) != MMSYSERR_NOERROR) {
69553a5a1b3Sopenharmony_ci            pa_log("failed to start waveIn");
69653a5a1b3Sopenharmony_ci            goto fail;
69753a5a1b3Sopenharmony_ci        }
69853a5a1b3Sopenharmony_ci    }
69953a5a1b3Sopenharmony_ci
70053a5a1b3Sopenharmony_ci    if (playback) {
70153a5a1b3Sopenharmony_ci        result = waveOutOpen(&hwo, output_device, &wf, 0, 0, WAVE_FORMAT_DIRECT | WAVE_FORMAT_QUERY);
70253a5a1b3Sopenharmony_ci        if (result != MMSYSERR_NOERROR) {
70353a5a1b3Sopenharmony_ci            pa_log_warn("Sample spec not supported by WaveOut, falling back to default sample rate.");
70453a5a1b3Sopenharmony_ci            ss.rate = wf.Format.nSamplesPerSec = m->core->default_sample_spec.rate;
70553a5a1b3Sopenharmony_ci        }
70653a5a1b3Sopenharmony_ci        result = waveOutOpen(&hwo, output_device, &wf, (DWORD_PTR) chunk_done_cb, (DWORD_PTR) u, CALLBACK_FUNCTION);
70753a5a1b3Sopenharmony_ci        if (result != MMSYSERR_NOERROR) {
70853a5a1b3Sopenharmony_ci            char errortext[MAXERRORLENGTH];
70953a5a1b3Sopenharmony_ci            pa_log("Failed to open WaveOut.");
71053a5a1b3Sopenharmony_ci            if (waveOutGetErrorText(result, errortext, sizeof(errortext)) == MMSYSERR_NOERROR)
71153a5a1b3Sopenharmony_ci                pa_log("Error: %s", errortext);
71253a5a1b3Sopenharmony_ci            goto fail;
71353a5a1b3Sopenharmony_ci        }
71453a5a1b3Sopenharmony_ci    }
71553a5a1b3Sopenharmony_ci
71653a5a1b3Sopenharmony_ci    InitializeCriticalSection(&u->crit);
71753a5a1b3Sopenharmony_ci
71853a5a1b3Sopenharmony_ci    if (hwi != INVALID_HANDLE_VALUE) {
71953a5a1b3Sopenharmony_ci        pa_source_new_data data;
72053a5a1b3Sopenharmony_ci        pa_source_new_data_init(&data);
72153a5a1b3Sopenharmony_ci        data.driver = __FILE__;
72253a5a1b3Sopenharmony_ci        data.module = m;
72353a5a1b3Sopenharmony_ci        pa_source_new_data_set_sample_spec(&data, &ss);
72453a5a1b3Sopenharmony_ci        pa_source_new_data_set_channel_map(&data, &map);
72553a5a1b3Sopenharmony_ci        pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
72653a5a1b3Sopenharmony_ci        pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "WaveIn on %s", input_device_name);
72753a5a1b3Sopenharmony_ci        u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
72853a5a1b3Sopenharmony_ci        pa_source_new_data_done(&data);
72953a5a1b3Sopenharmony_ci
73053a5a1b3Sopenharmony_ci        pa_assert(u->source);
73153a5a1b3Sopenharmony_ci        u->source->userdata = u;
73253a5a1b3Sopenharmony_ci        u->source->parent.process_msg = process_msg;
73353a5a1b3Sopenharmony_ci    } else
73453a5a1b3Sopenharmony_ci        u->source = NULL;
73553a5a1b3Sopenharmony_ci
73653a5a1b3Sopenharmony_ci    if (hwo != INVALID_HANDLE_VALUE) {
73753a5a1b3Sopenharmony_ci        pa_sink_new_data data;
73853a5a1b3Sopenharmony_ci        pa_sink_new_data_init(&data);
73953a5a1b3Sopenharmony_ci        data.driver = __FILE__;
74053a5a1b3Sopenharmony_ci        data.module = m;
74153a5a1b3Sopenharmony_ci        pa_sink_new_data_set_sample_spec(&data, &ss);
74253a5a1b3Sopenharmony_ci        pa_sink_new_data_set_channel_map(&data, &map);
74353a5a1b3Sopenharmony_ci        pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
74453a5a1b3Sopenharmony_ci        pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "WaveOut on %s", output_device_name);
74553a5a1b3Sopenharmony_ci        u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
74653a5a1b3Sopenharmony_ci        pa_sink_new_data_done(&data);
74753a5a1b3Sopenharmony_ci
74853a5a1b3Sopenharmony_ci        pa_assert(u->sink);
74953a5a1b3Sopenharmony_ci        pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb);
75053a5a1b3Sopenharmony_ci        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
75153a5a1b3Sopenharmony_ci        u->sink->userdata = u;
75253a5a1b3Sopenharmony_ci        u->sink->parent.process_msg = process_msg;
75353a5a1b3Sopenharmony_ci    } else
75453a5a1b3Sopenharmony_ci        u->sink = NULL;
75553a5a1b3Sopenharmony_ci
75653a5a1b3Sopenharmony_ci    pa_assert(u->source || u->sink);
75753a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
75853a5a1b3Sopenharmony_ci
75953a5a1b3Sopenharmony_ci    u->core = m->core;
76053a5a1b3Sopenharmony_ci    u->hwi = hwi;
76153a5a1b3Sopenharmony_ci    u->hwo = hwo;
76253a5a1b3Sopenharmony_ci
76353a5a1b3Sopenharmony_ci    u->fragments = nfrags;
76453a5a1b3Sopenharmony_ci    u->free_ifrags = u->fragments;
76553a5a1b3Sopenharmony_ci    u->free_ofrags = u->fragments;
76653a5a1b3Sopenharmony_ci    u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss));
76753a5a1b3Sopenharmony_ci
76853a5a1b3Sopenharmony_ci    u->written_bytes = 0;
76953a5a1b3Sopenharmony_ci    u->sink_underflow = 1;
77053a5a1b3Sopenharmony_ci
77153a5a1b3Sopenharmony_ci    u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 10, &ss);
77253a5a1b3Sopenharmony_ci    pa_log_debug("Poll timeout = %.1f ms", (double) u->poll_timeout / PA_USEC_PER_MSEC);
77353a5a1b3Sopenharmony_ci
77453a5a1b3Sopenharmony_ci    u->cur_ihdr = 0;
77553a5a1b3Sopenharmony_ci    u->cur_ohdr = 0;
77653a5a1b3Sopenharmony_ci    u->ihdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
77753a5a1b3Sopenharmony_ci    pa_assert(u->ihdrs);
77853a5a1b3Sopenharmony_ci    u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
77953a5a1b3Sopenharmony_ci    pa_assert(u->ohdrs);
78053a5a1b3Sopenharmony_ci    for (i = 0; i < u->fragments; i++) {
78153a5a1b3Sopenharmony_ci        u->ihdrs[i].dwBufferLength = u->fragment_size;
78253a5a1b3Sopenharmony_ci        u->ohdrs[i].dwBufferLength = u->fragment_size;
78353a5a1b3Sopenharmony_ci        u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size);
78453a5a1b3Sopenharmony_ci        pa_assert(u->ihdrs);
78553a5a1b3Sopenharmony_ci        u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
78653a5a1b3Sopenharmony_ci        pa_assert(u->ohdrs);
78753a5a1b3Sopenharmony_ci    }
78853a5a1b3Sopenharmony_ci
78953a5a1b3Sopenharmony_ci    u->module = m;
79053a5a1b3Sopenharmony_ci    m->userdata = u;
79153a5a1b3Sopenharmony_ci
79253a5a1b3Sopenharmony_ci    /* Read mixer settings */
79353a5a1b3Sopenharmony_ci    if (u->sink)
79453a5a1b3Sopenharmony_ci        sink_get_volume_cb(u->sink);
79553a5a1b3Sopenharmony_ci
79653a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
79753a5a1b3Sopenharmony_ci
79853a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
79953a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
80053a5a1b3Sopenharmony_ci        goto fail;
80153a5a1b3Sopenharmony_ci    }
80253a5a1b3Sopenharmony_ci
80353a5a1b3Sopenharmony_ci    if (u->sink) {
80453a5a1b3Sopenharmony_ci        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
80553a5a1b3Sopenharmony_ci        pa_sink_set_rtpoll(u->sink, u->rtpoll);
80653a5a1b3Sopenharmony_ci    }
80753a5a1b3Sopenharmony_ci    if (u->source) {
80853a5a1b3Sopenharmony_ci        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
80953a5a1b3Sopenharmony_ci        pa_source_set_rtpoll(u->source, u->rtpoll);
81053a5a1b3Sopenharmony_ci    }
81153a5a1b3Sopenharmony_ci
81253a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("waveout", thread_func, u))) {
81353a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
81453a5a1b3Sopenharmony_ci        goto fail;
81553a5a1b3Sopenharmony_ci    }
81653a5a1b3Sopenharmony_ci
81753a5a1b3Sopenharmony_ci    if (u->sink)
81853a5a1b3Sopenharmony_ci        pa_sink_put(u->sink);
81953a5a1b3Sopenharmony_ci    if (u->source)
82053a5a1b3Sopenharmony_ci        pa_source_put(u->source);
82153a5a1b3Sopenharmony_ci
82253a5a1b3Sopenharmony_ci    return 0;
82353a5a1b3Sopenharmony_ci
82453a5a1b3Sopenharmony_cifail:
82553a5a1b3Sopenharmony_ci    if (ma)
82653a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
82753a5a1b3Sopenharmony_ci
82853a5a1b3Sopenharmony_ci    pa__done(m);
82953a5a1b3Sopenharmony_ci
83053a5a1b3Sopenharmony_ci    return -1;
83153a5a1b3Sopenharmony_ci}
83253a5a1b3Sopenharmony_ci
83353a5a1b3Sopenharmony_civoid pa__done(pa_module *m) {
83453a5a1b3Sopenharmony_ci    struct userdata *u;
83553a5a1b3Sopenharmony_ci    unsigned int i;
83653a5a1b3Sopenharmony_ci
83753a5a1b3Sopenharmony_ci    pa_assert(m);
83853a5a1b3Sopenharmony_ci    pa_assert(m->core);
83953a5a1b3Sopenharmony_ci
84053a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
84153a5a1b3Sopenharmony_ci        return;
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci    if (u->sink)
84453a5a1b3Sopenharmony_ci        pa_sink_unlink(u->sink);
84553a5a1b3Sopenharmony_ci    if (u->source)
84653a5a1b3Sopenharmony_ci        pa_source_unlink(u->source);
84753a5a1b3Sopenharmony_ci
84853a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
84953a5a1b3Sopenharmony_ci    if (u->thread)
85053a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
85153a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
85253a5a1b3Sopenharmony_ci
85353a5a1b3Sopenharmony_ci    if (u->sink)
85453a5a1b3Sopenharmony_ci        pa_sink_unref(u->sink);
85553a5a1b3Sopenharmony_ci    if (u->source)
85653a5a1b3Sopenharmony_ci        pa_source_unref(u->source);
85753a5a1b3Sopenharmony_ci
85853a5a1b3Sopenharmony_ci    if (u->rtpoll)
85953a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_ci    if (u->hwi != INVALID_HANDLE_VALUE) {
86253a5a1b3Sopenharmony_ci        waveInReset(u->hwi);
86353a5a1b3Sopenharmony_ci        waveInClose(u->hwi);
86453a5a1b3Sopenharmony_ci    }
86553a5a1b3Sopenharmony_ci
86653a5a1b3Sopenharmony_ci    if (u->hwo != INVALID_HANDLE_VALUE) {
86753a5a1b3Sopenharmony_ci        waveOutReset(u->hwo);
86853a5a1b3Sopenharmony_ci        waveOutClose(u->hwo);
86953a5a1b3Sopenharmony_ci    }
87053a5a1b3Sopenharmony_ci
87153a5a1b3Sopenharmony_ci    for (i = 0; i < u->fragments; i++) {
87253a5a1b3Sopenharmony_ci        pa_xfree(u->ihdrs[i].lpData);
87353a5a1b3Sopenharmony_ci        pa_xfree(u->ohdrs[i].lpData);
87453a5a1b3Sopenharmony_ci    }
87553a5a1b3Sopenharmony_ci
87653a5a1b3Sopenharmony_ci    pa_xfree(u->ihdrs);
87753a5a1b3Sopenharmony_ci    pa_xfree(u->ohdrs);
87853a5a1b3Sopenharmony_ci
87953a5a1b3Sopenharmony_ci    DeleteCriticalSection(&u->crit);
88053a5a1b3Sopenharmony_ci
88153a5a1b3Sopenharmony_ci    pa_xfree(u);
88253a5a1b3Sopenharmony_ci}
883