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