]> code.delx.au - pulseaudio/blobdiff - src/modules/module-waveout.c
Subject: rtpoll: better support for DEBUG_TIMING logs
[pulseaudio] / src / modules / module-waveout.c
index ce9ea84d800bb64699b27b1a952cc499951c7595..d1b9f2ffb83a2ea2f4ac1505e23a89b49a33d281 100644 (file)
@@ -1,20 +1,21 @@
-/* $Id$ */
-
 /***
-  This file is part of polypaudio.
-  polypaudio is free software; you can redistribute it and/or modify
+  This file is part of PulseAudio.
+
+  Copyright 2006 Lennart Poettering
+  Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
-  polypaudio is distributed in the hope that it will be useful, but
+
+  PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public License
-  along with polypaudio; if not, write to the Free Software
+  along with PulseAudio; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
   USA.
 ***/
 
 #include <windows.h>
 #include <mmsystem.h>
-#include <assert.h>
 
-#include <polyp/mainloop-api.h>
+#include <pulse/mainloop-api.h>
 
-#include <polyp/xmalloc.h>
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
 
-#include <polypcore/sink.h>
-#include <polypcore/source.h>
-#include <polypcore/module.h>
-#include <polypcore/modargs.h>
-#include <polypcore/sample-util.h>
-#include <polypcore/core-util.h>
-#include <polypcore/log.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
 
 #include "module-waveout-symdef.h"
 
@@ -46,7 +47,8 @@ PA_MODULE_DESCRIPTION("Windows waveOut Sink/Source")
 PA_MODULE_VERSION(PACKAGE_VERSION)
 PA_MODULE_USAGE(
     "sink_name=<name for the sink> "
-    "source_name=<name for the source>"
+    "source_name=<name for the source> "
+    "device=<device number> "
     "record=<enable source?> "
     "playback=<enable sink?> "
     "format=<sample format> "
@@ -74,11 +76,10 @@ struct userdata {
     uint32_t free_ofrags, free_ifrags;
 
     DWORD written_bytes;
+    int sink_underflow;
 
     int cur_ohdr, cur_ihdr;
-    unsigned int oremain;
     WAVEHDR *ohdrs, *ihdrs;
-    pa_memchunk silence;
 
     HWAVEOUT hwo;
     HWAVEIN hwi;
@@ -90,6 +91,7 @@ struct userdata {
 static const char* const valid_modargs[] = {
     "sink_name",
     "source_name",
+    "device",
     "record",
     "playback",
     "fragments",
@@ -103,15 +105,14 @@ static const char* const valid_modargs[] = {
 
 static void update_usage(struct userdata *u) {
    pa_module_set_used(u->module,
-                      (u->sink ? pa_idxset_size(u->sink->inputs) : 0) +
-                      (u->sink ? pa_idxset_size(u->sink->monitor_source->outputs) : 0) +
-                      (u->source ? pa_idxset_size(u->source->outputs) : 0));
+                      (u->sink ? pa_sink_used_by(u->sink) : 0) +
+                      (u->source ? pa_source_used_by(u->source) : 0));
 }
 
 static void do_write(struct userdata *u)
 {
-    uint32_t free_frags, remain;
-    pa_memchunk memchunk, *cur_chunk;
+    uint32_t free_frags;
+    pa_memchunk memchunk;
     WAVEHDR *hdr;
     MMRESULT res;
 
@@ -119,59 +120,50 @@ static void do_write(struct userdata *u)
         return;
 
     EnterCriticalSection(&u->crit);
-
     free_frags = u->free_ofrags;
-    u->free_ofrags = 0;
-
     LeaveCriticalSection(&u->crit);
 
-    if (free_frags == u->fragments)
-        pa_log_debug(__FILE__": WaveOut underflow!");
+    if (!u->sink_underflow && (free_frags == u->fragments))
+        pa_log_debug("WaveOut underflow!");
 
     while (free_frags) {
         hdr = &u->ohdrs[u->cur_ohdr];
         if (hdr->dwFlags & WHDR_PREPARED)
             waveOutUnprepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
 
-        remain = u->oremain;
-        while (remain) {
-            cur_chunk = &memchunk;
+        hdr->dwBufferLength = 0;
+        while (hdr->dwBufferLength < u->fragment_size) {
+            size_t len;
 
-            if (pa_sink_render(u->sink, remain, cur_chunk) < 0) {
-                /*
-                 * Don't fill with silence unless we're getting close to
-                 * underflowing.
-                 */
-                if (free_frags > u->fragments/2)
-                    cur_chunk = &u->silence;
-                else {
-                    EnterCriticalSection(&u->crit);
+            len = u->fragment_size - hdr->dwBufferLength;
 
-                    u->free_ofrags += free_frags;
+            if (pa_sink_render(u->sink, len, &memchunk) < 0)
+                break;
 
-                    LeaveCriticalSection(&u->crit);
+            assert(memchunk.memblock);
+            assert(memchunk.memblock->data);
+            assert(memchunk.length);
 
-                    u->oremain = remain;
-                    return;
-                }
-            }
+            if (memchunk.length < len)
+                len = memchunk.length;
 
-            assert(cur_chunk->memblock);
-            assert(cur_chunk->memblock->data);
-            assert(cur_chunk->length);
+            memcpy(hdr->lpData + hdr->dwBufferLength,
+                (char*)memchunk.memblock->data + memchunk.index, len);
 
-            memcpy(hdr->lpData + u->fragment_size - remain,
-                (char*)cur_chunk->memblock->data + cur_chunk->index,
-                (cur_chunk->length < remain)?cur_chunk->length:remain);
+            hdr->dwBufferLength += len;
 
-            remain -= (cur_chunk->length < remain)?cur_chunk->length:remain;
+            pa_memblock_unref(memchunk.memblock);
+            memchunk.memblock = NULL;
+        }
 
-            if (cur_chunk != &u->silence) {
-                pa_memblock_unref(cur_chunk->memblock);
-                cur_chunk->memblock = NULL;
-            }
+        /* Insufficient data in sink buffer? */
+        if (hdr->dwBufferLength == 0) {
+            u->sink_underflow = 1;
+            break;
         }
 
+        u->sink_underflow = 0;
+
         res = waveOutPrepareHeader(u->hwo, hdr, sizeof(WAVEHDR));
         if (res != MMSYSERR_NOERROR) {
             pa_log_error(__FILE__ ": ERROR: Unable to prepare waveOut block: %d",
@@ -182,13 +174,16 @@ static void do_write(struct userdata *u)
             pa_log_error(__FILE__ ": ERROR: Unable to write waveOut block: %d",
                 res);
         }
-        
-        u->written_bytes += u->fragment_size;
+
+        u->written_bytes += hdr->dwBufferLength;
+
+        EnterCriticalSection(&u->crit);
+        u->free_ofrags--;
+        LeaveCriticalSection(&u->crit);
 
         free_frags--;
         u->cur_ohdr++;
         u->cur_ohdr %= u->fragments;
-        u->oremain = u->fragment_size;
     }
 }
 
@@ -210,7 +205,7 @@ static void do_read(struct userdata *u)
     LeaveCriticalSection(&u->crit);
 
     if (free_frags == u->fragments)
-        pa_log_debug(__FILE__": WaveIn overflow!");
+        pa_log_debug("WaveIn overflow!");
 
     while (free_frags) {
         hdr = &u->ihdrs[u->cur_ihdr];
@@ -218,7 +213,7 @@ static void do_read(struct userdata *u)
             waveInUnprepareHeader(u->hwi, hdr, sizeof(WAVEHDR));
 
         if (hdr->dwBytesRecorded) {
-            memchunk.memblock = pa_memblock_new(hdr->dwBytesRecorded, u->core->memblock_stat);
+            memchunk.memblock = pa_memblock_new(u->core->mempool, hdr->dwBytesRecorded);
             assert(memchunk.memblock);
 
             memcpy((char*)memchunk.memblock->data, hdr->lpData, hdr->dwBytesRecorded);
@@ -240,7 +235,7 @@ static void do_read(struct userdata *u)
             pa_log_error(__FILE__ ": ERROR: Unable to add waveIn block: %d",
                 res);
         }
-        
+
         free_frags--;
         u->cur_ihdr++;
         u->cur_ihdr %= u->fragments;
@@ -261,7 +256,7 @@ static void poll_cb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *t
     pa_gettimeofday(&ntv);
     pa_timeval_add(&ntv, u->poll_timeout);
 
-    a->time_restart(e, &ntv);
+    a->rtclock_time_restart(e, &ntv);
 }
 
 static void defer_cb(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
@@ -396,7 +391,7 @@ static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
     wf->wFormatTag = WAVE_FORMAT_PCM;
 
     if (ss->channels > 2) {
-        pa_log_error(__FILE__": ERROR: More than two channels not supported.");
+        pa_log_error("ERROR: More than two channels not supported.");
         return -1;
     }
 
@@ -409,7 +404,7 @@ static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
     case 44100:
         break;
     default:
-        pa_log_error(__FILE__": ERROR: Unsupported sample rate.");
+        pa_log_error("ERROR: Unsupported sample rate.");
         return -1;
     }
 
@@ -420,7 +415,7 @@ static int ss_to_waveformat(pa_sample_spec *ss, LPWAVEFORMATEX wf) {
     else if (ss->format == PA_SAMPLE_S16NE)
         wf->wBitsPerSample = 16;
     else {
-        pa_log_error(__FILE__": ERROR: Unsupported sample format.");
+        pa_log_error("ERROR: Unsupported sample format.");
         return -1;
     }
 
@@ -439,6 +434,7 @@ int pa__init(pa_core *c, pa_module*m) {
     WAVEFORMATEX wf;
     int nfrags, frag_size;
     int record = 1, playback = 1;
+    unsigned int device;
     pa_sample_spec ss;
     pa_channel_map map;
     pa_modargs *ma = NULL;
@@ -448,30 +444,36 @@ int pa__init(pa_core *c, pa_module*m) {
     assert(c && m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log(__FILE__": failed to parse module arguments.");
+        pa_log("failed to parse module arguments.");
         goto fail;
     }
 
     if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
-        pa_log(__FILE__": record= and playback= expect boolean argument.");
+        pa_log("record= and playback= expect boolean argument.");
         goto fail;
     }
 
     if (!playback && !record) {
-        pa_log(__FILE__": neither playback nor record enabled for device.");
+        pa_log("neither playback nor record enabled for device.");
+        goto fail;
+    }
+
+    device = WAVE_MAPPER;
+    if (pa_modargs_get_value_u32(ma, "device", &device) < 0) {
+        pa_log("failed to parse device argument");
         goto fail;
     }
 
     nfrags = 5;
     frag_size = 8192;
     if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
-        pa_log(__FILE__": failed to parse fragments arguments");
+        pa_log("failed to parse fragments arguments");
         goto fail;
     }
 
     ss = c->default_sample_spec;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_WAVEEX) < 0) {
-        pa_log(__FILE__": failed to parse sample specification");
+        pa_log("failed to parse sample specification");
         goto fail;
     }
 
@@ -481,17 +483,23 @@ int pa__init(pa_core *c, pa_module*m) {
     u = pa_xmalloc(sizeof(struct userdata));
 
     if (record) {
-        if (waveInOpen(&hwi, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
+        if (waveInOpen(&hwi, device, &wf, (DWORD_PTR)chunk_ready_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+            pa_log("failed to open waveIn");
             goto fail;
-        if (waveInStart(hwi) != MMSYSERR_NOERROR)
+        }
+        if (waveInStart(hwi) != MMSYSERR_NOERROR) {
+            pa_log("failed to start waveIn");
             goto fail;
-        pa_log_debug(__FILE__": Opened waveIn subsystem.");
+        }
+        pa_log_debug("Opened waveIn subsystem.");
     }
 
     if (playback) {
-        if (waveOutOpen(&hwo, WAVE_MAPPER, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
+        if (waveOutOpen(&hwo, device, &wf, (DWORD_PTR)chunk_done_cb, (DWORD_PTR)u, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+            pa_log("failed to open waveOut");
             goto fail;
-        pa_log_debug(__FILE__": Opened waveOut subsystem.");
+        }
+        pa_log_debug("Opened waveOut subsystem.");
     }
 
     InitializeCriticalSection(&u->crit);
@@ -503,7 +511,8 @@ int pa__init(pa_core *c, pa_module*m) {
         u->source->notify = notify_source_cb;
         u->source->get_latency = source_get_latency_cb;
         pa_source_set_owner(u->source, m);
-        u->source->description = pa_sprintf_malloc("Windows waveIn PCM");
+        pa_source_set_description(u->source, "Windows waveIn PCM");
+        u->source->is_hardware = 1;
     } else
         u->source = NULL;
 
@@ -516,7 +525,8 @@ int pa__init(pa_core *c, pa_module*m) {
         u->sink->set_hw_volume = sink_set_hw_volume_cb;
         u->sink->userdata = u;
         pa_sink_set_owner(u->sink, m);
-        u->sink->description = pa_sprintf_malloc("Windows waveOut PCM");
+        pa_sink_set_description(u->sink, "Windows waveOut PCM");
+        u->sink->is_hardware = 1;
     } else
         u->sink = NULL;
 
@@ -532,15 +542,14 @@ int pa__init(pa_core *c, pa_module*m) {
     u->fragment_size = frag_size - (frag_size % pa_frame_size(&ss));
 
     u->written_bytes = 0;
-
-    u->oremain = u->fragment_size;
+    u->sink_underflow = 1;
 
     u->poll_timeout = pa_bytes_to_usec(u->fragments * u->fragment_size / 10, &ss);
 
     pa_gettimeofday(&tv);
     pa_timeval_add(&tv, u->poll_timeout);
 
-    u->event = c->mainloop->time_new(c->mainloop, &tv, poll_cb, u);
+    u->event = c->mainloop->rtclock_time_new(c->mainloop, &tv, poll_cb, u);
     assert(u->event);
 
     u->defer = c->mainloop->defer_new(c->mainloop, defer_cb, u);
@@ -561,12 +570,6 @@ int pa__init(pa_core *c, pa_module*m) {
         u->ohdrs[i].lpData = pa_xmalloc(u->fragment_size);
         assert(u->ohdrs);
     }
-    
-    u->silence.length = u->fragment_size;
-    u->silence.memblock = pa_memblock_new(u->silence.length, u->core->memblock_stat);
-    assert(u->silence.memblock);
-    pa_silence_memblock(u->silence.memblock, &ss);
-    u->silence.index = 0;
 
     u->module = m;
     m->userdata = u;
@@ -591,7 +594,7 @@ fail:
 
     if (ma)
         pa_modargs_free(ma);
-    
+
     return -1;
 }
 
@@ -603,7 +606,7 @@ void pa__done(pa_core *c, pa_module*m) {
 
     if (!(u = m->userdata))
         return;
-    
+
     if (u->event)
         c->mainloop->time_free(u->event);
 
@@ -614,12 +617,12 @@ void pa__done(pa_core *c, pa_module*m) {
         pa_sink_disconnect(u->sink);
         pa_sink_unref(u->sink);
     }
-    
+
     if (u->source) {
         pa_source_disconnect(u->source);
         pa_source_unref(u->source);
     }
-    
+
     if (u->hwi != INVALID_HANDLE_VALUE) {
         waveInReset(u->hwi);
         waveInClose(u->hwi);
@@ -639,6 +642,6 @@ void pa__done(pa_core *c, pa_module*m) {
     pa_xfree(u->ohdrs);
 
     DeleteCriticalSection(&u->crit);
-    
+
     pa_xfree(u);
 }