]> code.delx.au - pulseaudio/blobdiff - src/modules/module-waveout.c
bluetooth: Fire DEVICE_CONNECTION_CHANGED in set_device_info_valid()
[pulseaudio] / src / modules / module-waveout.c
index 44b15dae438429a5b343df177d8932a8351ad495..b274271f11a53348fc5a74ad06b745e1a2f5e939 100644 (file)
 #include <windows.h>
 #include <mmsystem.h>
 
-#include <pulse/mainloop-api.h>
-
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
-#include <pulse/rtclock.h>
 
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
@@ -56,11 +53,11 @@ PA_MODULE_USAGE(
     "record=<enable source?> "
     "playback=<enable sink?> "
     "format=<sample format> "
-    "channels=<number of channels> "
     "rate=<sample rate> "
+    "channels=<number of channels> "
+    "channel_map=<channel map> "
     "fragments=<number of fragments> "
-    "fragment_size=<fragment size> "
-    "channel_map=<channel map>");
+    "fragment_size=<fragment size>");
 
 #define DEFAULT_SINK_NAME "wave_output"
 #define DEFAULT_SOURCE_NAME "wave_input"
@@ -256,24 +253,30 @@ static void thread_func(void *userdata) {
 
     for (;;) {
         int ret;
+        bool need_timer = false;
 
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state) ||
-            PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
-
-            if (u->sink->thread_info.rewind_requested)
+        if (u->sink) {
+            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
                 pa_sink_process_rewind(u->sink, 0);
 
-            if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+            if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
                 do_write(u);
-            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
-                do_read(u);
+                need_timer = true;
+            }
+        }
+
+        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
+            do_read(u);
+            need_timer = true;
+        }
 
+        if (need_timer)
             pa_rtpoll_set_timer_relative(u->rtpoll, u->poll_timeout);
-        else
+        else
             pa_rtpoll_set_timer_disabled(u->rtpoll);
 
         /* Hmm, nothing to do. Let's sleep */
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
             goto fail;
 
         if (ret == 0)
@@ -400,14 +403,23 @@ static int process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa
 
 static void sink_get_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
+    WAVEOUTCAPS caps;
     DWORD vol;
     pa_volume_t left, right;
 
+    if (waveOutGetDevCaps(u->hwo, &caps, sizeof(caps)) != MMSYSERR_NOERROR)
+        return;
+    if (!(caps.dwSupport & WAVECAPS_VOLUME))
+        return;
+
     if (waveOutGetVolume(u->hwo, &vol) != MMSYSERR_NOERROR)
         return;
 
     left = PA_CLAMP_VOLUME((vol & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
-    right = PA_CLAMP_VOLUME(((vol >> 16) & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
+    if (caps.dwSupport & WAVECAPS_LRVOLUME)
+        right = PA_CLAMP_VOLUME(((vol >> 16) & 0xFFFF) * PA_VOLUME_NORM / WAVEOUT_MAX_VOLUME);
+    else
+        right = left;
 
     /* Windows supports > 2 channels, except for volume control */
     if (s->real_volume.channels > 2)
@@ -420,11 +432,21 @@ static void sink_get_volume_cb(pa_sink *s) {
 
 static void sink_set_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
+    WAVEOUTCAPS caps;
     DWORD vol;
 
-    vol = s->real_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM;
-    if (s->real_volume.channels > 1)
-        vol |= (s->real_volume.values[1] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
+    if (waveOutGetDevCaps(u->hwo, &caps, sizeof(caps)) != MMSYSERR_NOERROR)
+        return;
+    if (!(caps.dwSupport & WAVECAPS_VOLUME))
+        return;
+
+    if (s->real_volume.channels == 2 && caps.dwSupport & WAVECAPS_LRVOLUME) {
+        vol = (s->real_volume.values[0] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM)
+            | (s->real_volume.values[1] * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
+    } else {
+        vol = (pa_cvolume_avg(&(s->real_volume)) * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM)
+            | (pa_cvolume_avg(&(s->real_volume)) * WAVEOUT_MAX_VOLUME / PA_VOLUME_NORM) << 16;
+    }
 
     if (waveOutSetVolume(u->hwo, vol) != MMSYSERR_NOERROR)
         return;
@@ -477,7 +499,7 @@ int pa__init(pa_module *m) {
     WAVEOUTCAPS pwoc;
     MMRESULT result;
     int nfrags, frag_size;
-    pa_bool_t record = TRUE, playback = TRUE;
+    bool record = true, playback = true;
     unsigned int device;
     pa_sample_spec ss;
     pa_channel_map map;
@@ -586,7 +608,6 @@ int pa__init(pa_module *m) {
     InitializeCriticalSection(&u->crit);
 
     if (hwi != INVALID_HANDLE_VALUE) {
-        char *description = pa_sprintf_malloc("WaveIn on %s", device_name);
         pa_source_new_data data;
         pa_source_new_data_init(&data);
         data.driver = __FILE__;
@@ -594,19 +615,17 @@ int pa__init(pa_module *m) {
         pa_source_new_data_set_sample_spec(&data, &ss);
         pa_source_new_data_set_channel_map(&data, &map);
         pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+        pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "WaveIn on %s", device_name);
         u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
         pa_source_new_data_done(&data);
 
         pa_assert(u->source);
         u->source->userdata = u;
-        pa_source_set_description(u->source, description);
         u->source->parent.process_msg = process_msg;
-        pa_xfree(description);
     } else
         u->source = NULL;
 
     if (hwo != INVALID_HANDLE_VALUE) {
-        char *description = pa_sprintf_malloc("WaveOut on %s", device_name);
         pa_sink_new_data data;
         pa_sink_new_data_init(&data);
         data.driver = __FILE__;
@@ -614,16 +633,15 @@ int pa__init(pa_module *m) {
         pa_sink_new_data_set_sample_spec(&data, &ss);
         pa_sink_new_data_set_channel_map(&data, &map);
         pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+        pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "WaveOut on %s", device_name);
         u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
         pa_sink_new_data_done(&data);
 
         pa_assert(u->sink);
-        u->sink->get_volume = sink_get_volume_cb;
-        u->sink->set_volume = sink_set_volume_cb;
+        pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb);
+        pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
         u->sink->userdata = u;
-        pa_sink_set_description(u->sink, description);
         u->sink->parent.process_msg = process_msg;
-        pa_xfree(description);
     } else
         u->sink = NULL;
 
@@ -643,6 +661,7 @@ int pa__init(pa_module *m) {
     u->sink_underflow = 1;
 
     u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 10, &ss);
+    pa_log_debug("Poll timeout = %.1f ms", (double) u->poll_timeout / PA_USEC_PER_MSEC);
 
     u->cur_ihdr = 0;
     u->cur_ohdr = 0;
@@ -650,7 +669,7 @@ int pa__init(pa_module *m) {
     pa_assert(u->ihdrs);
     u->ohdrs = pa_xmalloc0(sizeof(WAVEHDR) * u->fragments);
     pa_assert(u->ohdrs);
-    for (i = 0;i < u->fragments;i++) {
+    for (i = 0; i < u->fragments; i++) {
         u->ihdrs[i].dwBufferLength = u->fragment_size;
         u->ohdrs[i].dwBufferLength = u->fragment_size;
         u->ihdrs[i].lpData = pa_xmalloc(u->fragment_size);
@@ -668,22 +687,26 @@ int pa__init(pa_module *m) {
 
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
-    if (!(u->thread = pa_thread_new("waveout-source", thread_func, u))) {
-        pa_log("Failed to create thread.");
-        goto fail;
-    }
 
     if (u->sink) {
         pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
         pa_sink_set_rtpoll(u->sink, u->rtpoll);
-        pa_sink_put(u->sink);
     }
     if (u->source) {
         pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
         pa_source_set_rtpoll(u->source, u->rtpoll);
-        pa_source_put(u->source);
     }
 
+    if (!(u->thread = pa_thread_new("waveout", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    if (u->sink)
+        pa_sink_put(u->sink);
+    if (u->source)
+        pa_source_put(u->source);
+
     return 0;
 
 fail:
@@ -712,7 +735,7 @@ void pa__done(pa_module *m) {
 
     pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
     if (u->thread)
-      pa_thread_free(u->thread);
+        pa_thread_free(u->thread);
     pa_thread_mq_done(&u->thread_mq);
 
     if (u->sink)
@@ -733,7 +756,7 @@ void pa__done(pa_module *m) {
         waveOutClose(u->hwo);
     }
 
-    for (i = 0;i < u->fragments;i++) {
+    for (i = 0; i < u->fragments; i++) {
         pa_xfree(u->ihdrs[i].lpData);
         pa_xfree(u->ohdrs[i].lpData);
     }