be6b2c894c046fe1e7e8b3294afd7bbd6b41a7fd
[platform/upstream/gstreamer.git] / sys / directsound / gstdirectsoundsrc.c
1 /*
2  * GStreamer
3  * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
4  * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5  * Copyright 2005 S�bastien Moutte <sebastien@moutte.net>
6  * Copyright 2006 Joni Valtanen <joni.valtanen@movial.fi>
7  * 
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  * Alternatively, the contents of this file may be used under the
27  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28  * which case the following provisions apply instead of the ones
29  * mentioned above:
30  *
31  * This library is free software; you can redistribute it and/or
32  * modify it under the terms of the GNU Library General Public
33  * License as published by the Free Software Foundation; either
34  * version 2 of the License, or (at your option) any later version.
35  *
36  * This library is distributed in the hope that it will be useful,
37  * but WITHOUT ANY WARRANTY; without even the implied warranty of
38  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
39  * Library General Public License for more details.
40  *
41  * You should have received a copy of the GNU Library General Public
42  * License along with this library; if not, write to the
43  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
44  * Boston, MA 02110-1301, USA.
45  */
46
47 /*
48   TODO: add mixer device init for selection by device-guid
49 */
50
51 /**
52  * SECTION:element-directsoundsrc
53  * @title: directsoundsrc
54  *
55  * Reads audio data using the DirectSound API.
56  *
57  * ## Example pipelines
58  * |[
59  * gst-launch-1.0 -v directsoundsrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=dsound.ogg
60  * ]| Record from DirectSound and encode to Ogg/Vorbis.
61  *
62  */
63
64 #ifdef HAVE_CONFIG_H
65 #  include "config.h"
66 #endif
67
68 #include <gst/gst.h>
69 #include <gst/audio/audio.h>
70 #include <gst/audio/gstaudiobasesrc.h>
71
72 #include "gstdirectsoundsrc.h"
73
74 #include <windows.h>
75 #include <dsound.h>
76 #include <mmsystem.h>
77 #include <stdio.h>
78
79 GST_DEBUG_CATEGORY_STATIC (directsoundsrc_debug);
80 #define GST_CAT_DEFAULT directsoundsrc_debug
81
82 /* defaults here */
83 #define DEFAULT_DEVICE 0
84 #define DEFAULT_MUTE FALSE
85
86 /* properties */
87 enum
88 {
89   PROP_0,
90   PROP_DEVICE_NAME,
91   PROP_DEVICE,
92   PROP_VOLUME,
93   PROP_MUTE
94 };
95
96 static void gst_directsound_src_finalize (GObject * object);
97
98 static void gst_directsound_src_set_property (GObject * object,
99     guint prop_id, const GValue * value, GParamSpec * pspec);
100
101 static void gst_directsound_src_get_property (GObject * object,
102     guint prop_id, GValue * value, GParamSpec * pspec);
103
104 static gboolean gst_directsound_src_open (GstAudioSrc * asrc);
105 static gboolean gst_directsound_src_close (GstAudioSrc * asrc);
106 static gboolean gst_directsound_src_prepare (GstAudioSrc * asrc,
107     GstAudioRingBufferSpec * spec);
108 static gboolean gst_directsound_src_unprepare (GstAudioSrc * asrc);
109 static void gst_directsound_src_reset (GstAudioSrc * asrc);
110 static GstCaps *gst_directsound_src_getcaps (GstBaseSrc * bsrc,
111     GstCaps * filter);
112
113 static guint gst_directsound_src_read (GstAudioSrc * asrc,
114     gpointer data, guint length, GstClockTime * timestamp);
115
116 static void gst_directsound_src_dispose (GObject * object);
117
118 static guint gst_directsound_src_delay (GstAudioSrc * asrc);
119
120 static gboolean gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
121     MIXERCAPS * mixer_caps);
122 static void gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc);
123
124 static gdouble gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc);
125 static void gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc,
126     gdouble volume);
127
128 static gboolean gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc);
129 static void gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc,
130     gboolean mute);
131
132 static const gchar *gst_directsound_src_get_device (GstDirectSoundSrc *
133     dsoundsrc);
134 static void gst_directsound_src_set_device (GstDirectSoundSrc * dsoundsrc,
135     const gchar * device_id);
136
137 static GstStaticPadTemplate directsound_src_src_factory =
138 GST_STATIC_PAD_TEMPLATE ("src",
139     GST_PAD_SRC,
140     GST_PAD_ALWAYS,
141     GST_STATIC_CAPS (GST_DIRECTSOUND_SRC_CAPS));
142
143 #define gst_directsound_src_parent_class parent_class
144 G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSrc, gst_directsound_src,
145     GST_TYPE_AUDIO_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL)
146     );
147
148 static void
149 gst_directsound_src_dispose (GObject * object)
150 {
151   G_OBJECT_CLASS (parent_class)->dispose (object);
152 }
153
154 static void
155 gst_directsound_src_finalize (GObject * object)
156 {
157   GstDirectSoundSrc *dsoundsrc = GST_DIRECTSOUND_SRC (object);
158
159   g_mutex_clear (&dsoundsrc->dsound_lock);
160   gst_object_unref (dsoundsrc->system_clock);
161   if (dsoundsrc->read_wait_clock_id != NULL)
162     gst_clock_id_unref (dsoundsrc->read_wait_clock_id);
163
164   g_free (dsoundsrc->device_name);
165
166   g_free (dsoundsrc->device_id);
167
168   g_free (dsoundsrc->device_guid);
169
170   G_OBJECT_CLASS (parent_class)->finalize (object);
171 }
172
173 static void
174 gst_directsound_src_class_init (GstDirectSoundSrcClass * klass)
175 {
176   GObjectClass *gobject_class;
177   GstElementClass *gstelement_class;
178   GstBaseSrcClass *gstbasesrc_class;
179   GstAudioSrcClass *gstaudiosrc_class;
180
181   gobject_class = (GObjectClass *) klass;
182   gstelement_class = (GstElementClass *) klass;
183   gstbasesrc_class = (GstBaseSrcClass *) klass;
184   gstaudiosrc_class = (GstAudioSrcClass *) klass;
185
186   GST_DEBUG_CATEGORY_INIT (directsoundsrc_debug, "directsoundsrc", 0,
187       "DirectSound Src");
188
189   GST_DEBUG ("initializing directsoundsrc class");
190
191   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directsound_src_finalize);
192   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_directsound_src_dispose);
193   gobject_class->get_property =
194       GST_DEBUG_FUNCPTR (gst_directsound_src_get_property);
195   gobject_class->set_property =
196       GST_DEBUG_FUNCPTR (gst_directsound_src_set_property);
197
198   gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_directsound_src_getcaps);
199
200   gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_directsound_src_open);
201   gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_directsound_src_close);
202   gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_directsound_src_read);
203   gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_directsound_src_prepare);
204   gstaudiosrc_class->unprepare =
205       GST_DEBUG_FUNCPTR (gst_directsound_src_unprepare);
206   gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_src_delay);
207   gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_src_reset);
208
209   gst_element_class_set_static_metadata (gstelement_class,
210       "DirectSound audio source", "Source/Audio",
211       "Capture from a soundcard via DirectSound",
212       "Joni Valtanen <joni.valtanen@movial.fi>");
213
214   gst_element_class_add_static_pad_template (gstelement_class,
215       &directsound_src_src_factory);
216
217   g_object_class_install_property
218       (gobject_class, PROP_DEVICE_NAME,
219       g_param_spec_string ("device-name", "Device name",
220           "Human-readable name of the sound device", NULL, G_PARAM_READWRITE));
221
222   g_object_class_install_property (gobject_class,
223       PROP_DEVICE,
224       g_param_spec_string ("device", "Device",
225           "DirectSound playback device as a GUID string (volume and mute will not work!)",
226           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227
228   g_object_class_install_property
229       (gobject_class, PROP_VOLUME,
230       g_param_spec_double ("volume", "Volume",
231           "Volume of this stream", 0.0, 1.0, 1.0,
232           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
233
234   g_object_class_install_property
235       (gobject_class, PROP_MUTE,
236       g_param_spec_boolean ("mute", "Mute",
237           "Mute state of this stream", DEFAULT_MUTE,
238           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
239 }
240
241 static GstCaps *
242 gst_directsound_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
243 {
244   GstCaps *caps = NULL;
245   GST_DEBUG_OBJECT (bsrc, "get caps");
246
247   caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
248   return caps;
249 }
250
251 static void
252 gst_directsound_src_set_property (GObject * object, guint prop_id,
253     const GValue * value, GParamSpec * pspec)
254 {
255   GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
256   GST_DEBUG ("set property");
257
258   switch (prop_id) {
259     case PROP_DEVICE_NAME:
260       if (src->device_name) {
261         g_free (src->device_name);
262         src->device_name = NULL;
263       }
264       if (g_value_get_string (value)) {
265         src->device_name = g_strdup (g_value_get_string (value));
266       }
267       break;
268     case PROP_VOLUME:
269       gst_directsound_src_set_volume (src, g_value_get_double (value));
270       break;
271     case PROP_MUTE:
272       gst_directsound_src_set_mute (src, g_value_get_boolean (value));
273       break;
274     case PROP_DEVICE:
275       gst_directsound_src_set_device (src, g_value_get_string (value));
276       break;
277     default:
278       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
279       break;
280   }
281 }
282
283 static void
284 gst_directsound_src_get_property (GObject * object, guint prop_id,
285     GValue * value, GParamSpec * pspec)
286 {
287   GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
288
289   GST_DEBUG ("get property");
290
291   switch (prop_id) {
292     case PROP_DEVICE_NAME:
293       g_value_set_string (value, src->device_name);
294       break;
295     case PROP_DEVICE:
296       g_value_set_string (value, gst_directsound_src_get_device (src));
297       break;
298     case PROP_VOLUME:
299       g_value_set_double (value, gst_directsound_src_get_volume (src));
300       break;
301     case PROP_MUTE:
302       g_value_set_boolean (value, gst_directsound_src_get_mute (src));
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306       break;
307   }
308 }
309
310
311 /* initialize the new element
312  * instantiate pads and add them to element
313  * set functions
314  * initialize structure
315  */
316 static void
317 gst_directsound_src_init (GstDirectSoundSrc * src)
318 {
319   GST_DEBUG_OBJECT (src, "initializing directsoundsrc");
320   g_mutex_init (&src->dsound_lock);
321   src->system_clock = gst_system_clock_obtain ();
322   src->read_wait_clock_id = NULL;
323   src->reset_while_sleeping = FALSE;
324   src->device_guid = NULL;
325   src->device_id = NULL;
326   src->device_name = NULL;
327   src->mixer = NULL;
328   src->control_id_mute = -1;
329   src->control_id_volume = -1;
330   src->volume = 100;
331   src->mute = FALSE;
332 }
333
334
335 /* Enumeration callback called by DirectSoundCaptureEnumerate.
336  * Gets the GUID of request audio device
337  */
338 static BOOL CALLBACK
339 gst_directsound_enum_callback (GUID * pGUID, TCHAR * strDesc,
340     TCHAR * strDrvName, VOID * pContext)
341 {
342   GstDirectSoundSrc *dsoundsrc = GST_DIRECTSOUND_SRC (pContext);
343   gchar *driver, *description;
344
345   description = g_locale_to_utf8 (strDesc, -1, NULL, NULL, NULL);
346   if (!description) {
347     GST_ERROR_OBJECT (dsoundsrc,
348         "Failed to convert description from locale encoding to UTF8");
349     return TRUE;
350   }
351
352   driver = g_locale_to_utf8 (strDrvName, -1, NULL, NULL, NULL);
353
354   if (pGUID && dsoundsrc && dsoundsrc->device_name &&
355       !g_strcmp0 (dsoundsrc->device_name, description)) {
356     g_free (dsoundsrc->device_guid);
357     dsoundsrc->device_guid = (GUID *) g_malloc0 (sizeof (GUID));
358     memcpy (dsoundsrc->device_guid, pGUID, sizeof (GUID));
359     GST_INFO_OBJECT (dsoundsrc, "found the requested audio device :%s",
360         dsoundsrc->device_name);
361     g_free (description);
362     g_free (driver);
363     return FALSE;
364   }
365
366   GST_INFO_OBJECT (dsoundsrc, "sound device names: %s, %s, requested device:%s",
367       description, driver, dsoundsrc->device_name);
368
369   g_free (description);
370   g_free (driver);
371
372   return TRUE;
373 }
374
375 static LPGUID
376 string_to_guid (const gchar * str)
377 {
378   HRESULT ret;
379   gunichar2 *wstr;
380   LPGUID out;
381
382   wstr = g_utf8_to_utf16 (str, -1, NULL, NULL, NULL);
383   if (!wstr)
384     return NULL;
385
386   out = g_new (GUID, 1);
387   ret = CLSIDFromString ((LPOLESTR) wstr, out);
388   g_free (wstr);
389   if (ret != NOERROR) {
390     g_free (out);
391     return NULL;
392   }
393
394   return out;
395 }
396
397 static gboolean
398 gst_directsound_src_open (GstAudioSrc * asrc)
399 {
400   GstDirectSoundSrc *dsoundsrc;
401   HRESULT hRes;                 /* Result for windows functions */
402
403   GST_DEBUG_OBJECT (asrc, "opening directsoundsrc");
404
405   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
406
407   if (dsoundsrc->device_id) {
408     GST_DEBUG_OBJECT (asrc, "device id set to: %s ", dsoundsrc->device_id);
409     dsoundsrc->device_guid = string_to_guid (dsoundsrc->device_id);
410     if (dsoundsrc->device_guid == NULL) {
411       GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
412           ("gst_directsound_src_open: device set, but guid not found: %s",
413               dsoundsrc->device_id), (NULL));
414       g_free (dsoundsrc->device_guid);
415       return FALSE;
416     }
417   } else {
418
419     hRes = DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)
420         gst_directsound_enum_callback, (VOID *) dsoundsrc);
421
422     if (FAILED (hRes)) {
423       goto capture_enumerate;
424     }
425   }
426   /* Create capture object */
427   hRes =
428       DirectSoundCaptureCreate (dsoundsrc->device_guid, &dsoundsrc->pDSC, NULL);
429
430
431   if (FAILED (hRes)) {
432     goto capture_object;
433   }
434   // mixer is only supported when device-id is not set
435   if (!dsoundsrc->device_id) {
436     gst_directsound_src_mixer_init (dsoundsrc);
437   }
438
439   return TRUE;
440
441 capture_enumerate:
442   {
443     GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
444         ("Unable to enumerate audio capture devices"), (NULL));
445     return FALSE;
446   }
447 capture_object:
448   {
449     GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
450         ("Unable to create capture object"), (NULL));
451     return FALSE;
452   }
453 }
454
455 static gboolean
456 gst_directsound_src_close (GstAudioSrc * asrc)
457 {
458   GstDirectSoundSrc *dsoundsrc;
459
460   GST_DEBUG_OBJECT (asrc, "closing directsoundsrc");
461
462   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
463
464   /* Release capture handler  */
465   IDirectSoundCapture_Release (dsoundsrc->pDSC);
466
467   if (dsoundsrc->mixer)
468     mixerClose (dsoundsrc->mixer);
469
470   return TRUE;
471 }
472
473 static gboolean
474 gst_directsound_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
475 {
476   GstDirectSoundSrc *dsoundsrc;
477   WAVEFORMATEX wfx;             /* Wave format structure */
478   HRESULT hRes;                 /* Result for windows functions */
479   DSCBUFFERDESC descSecondary;  /* Capturebuffer description */
480
481   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
482
483   GST_DEBUG_OBJECT (asrc, "preparing directsoundsrc");
484
485   /* Define buffer */
486   memset (&wfx, 0, sizeof (WAVEFORMATEX));
487   wfx.wFormatTag = WAVE_FORMAT_PCM;
488   wfx.nChannels = GST_AUDIO_INFO_CHANNELS (&spec->info);
489   wfx.nSamplesPerSec = GST_AUDIO_INFO_RATE (&spec->info);
490   wfx.wBitsPerSample = GST_AUDIO_INFO_BPF (&spec->info) * 8 / wfx.nChannels;
491   wfx.nBlockAlign = GST_AUDIO_INFO_BPF (&spec->info);
492   wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
493   /* Ignored for WAVE_FORMAT_PCM. */
494   wfx.cbSize = 0;
495
496   if (wfx.wBitsPerSample != 16 && wfx.wBitsPerSample != 8)
497     goto dodgy_width;
498
499   GST_INFO_OBJECT (asrc, "latency time: %" G_GUINT64_FORMAT " - buffer time: %"
500       G_GUINT64_FORMAT, spec->latency_time, spec->buffer_time);
501
502   /* Buffer-time should always be >= 2*latency */
503   if (spec->buffer_time < spec->latency_time * 2) {
504     spec->buffer_time = spec->latency_time * 2;
505     GST_WARNING ("buffer-time was less than 2*latency-time, clamping");
506   }
507
508   /* Set the buffer size from our configured buffer time (in microsecs) */
509   dsoundsrc->buffer_size =
510       gst_util_uint64_scale_int (spec->buffer_time, wfx.nAvgBytesPerSec,
511       GST_SECOND / GST_USECOND);
512
513   GST_INFO_OBJECT (asrc, "Buffer size: %d", dsoundsrc->buffer_size);
514
515   spec->segsize =
516       gst_util_uint64_scale (spec->latency_time, wfx.nAvgBytesPerSec,
517       GST_SECOND / GST_USECOND);
518
519   /* Sanitized segsize */
520   if (spec->segsize < GST_AUDIO_INFO_BPF (&spec->info))
521     spec->segsize = GST_AUDIO_INFO_BPF (&spec->info);
522   else if (spec->segsize % GST_AUDIO_INFO_BPF (&spec->info) != 0)
523     spec->segsize =
524         ((spec->segsize + GST_AUDIO_INFO_BPF (&spec->info) -
525             1) / GST_AUDIO_INFO_BPF (&spec->info)) *
526         GST_AUDIO_INFO_BPF (&spec->info);
527   spec->segtotal = dsoundsrc->buffer_size / spec->segsize;
528   /* The device usually takes time = 1-2 segments to start producing buffers */
529   spec->seglatency = spec->segtotal + 2;
530
531   /* Fetch and set the actual latency time that will be used */
532   dsoundsrc->latency_time =
533       gst_util_uint64_scale (spec->segsize, GST_SECOND / GST_USECOND,
534       GST_AUDIO_INFO_BPF (&spec->info) * GST_AUDIO_INFO_RATE (&spec->info));
535
536   GST_INFO_OBJECT (asrc, "actual latency time: %" G_GUINT64_FORMAT,
537       spec->latency_time);
538
539   /* Init secondary buffer desciption */
540   memset (&descSecondary, 0, sizeof (DSCBUFFERDESC));
541   descSecondary.dwSize = sizeof (DSCBUFFERDESC);
542   descSecondary.dwFlags = 0;
543   descSecondary.dwReserved = 0;
544
545   /* This is not primary buffer so have to set size  */
546   descSecondary.dwBufferBytes = dsoundsrc->buffer_size;
547   descSecondary.lpwfxFormat = &wfx;
548
549   /* Create buffer */
550   hRes = IDirectSoundCapture_CreateCaptureBuffer (dsoundsrc->pDSC,
551       &descSecondary, &dsoundsrc->pDSBSecondary, NULL);
552   if (hRes != DS_OK)
553     goto capture_buffer;
554
555   dsoundsrc->bytes_per_sample = GST_AUDIO_INFO_BPF (&spec->info);
556
557   GST_INFO_OBJECT (asrc,
558       "bytes/sec: %lu, buffer size: %d, segsize: %d, segtotal: %d",
559       wfx.nAvgBytesPerSec, dsoundsrc->buffer_size, spec->segsize,
560       spec->segtotal);
561
562   /* Not read anything yet */
563   dsoundsrc->current_circular_offset = 0;
564
565   GST_INFO_OBJECT (asrc, "channels: %d, rate: %d, bytes_per_sample: %d"
566       " WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d,"
567       " WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld",
568       GST_AUDIO_INFO_CHANNELS (&spec->info), GST_AUDIO_INFO_RATE (&spec->info),
569       GST_AUDIO_INFO_BPF (&spec->info), wfx.nSamplesPerSec, wfx.wBitsPerSample,
570       wfx.nBlockAlign, wfx.nAvgBytesPerSec);
571
572   return TRUE;
573
574 capture_buffer:
575   {
576     GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
577         ("Unable to create capturebuffer"), (NULL));
578     return FALSE;
579   }
580 dodgy_width:
581   {
582     GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
583         ("Unexpected width %d", wfx.wBitsPerSample), (NULL));
584     return FALSE;
585   }
586 }
587
588 static gboolean
589 gst_directsound_src_unprepare (GstAudioSrc * asrc)
590 {
591   GstDirectSoundSrc *dsoundsrc;
592
593   GST_DEBUG_OBJECT (asrc, "unpreparing directsoundsrc");
594
595   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
596
597   GST_DSOUND_LOCK (dsoundsrc);
598
599   /* Stop capturing */
600   IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary);
601
602   /* Release buffer  */
603   IDirectSoundCaptureBuffer_Release (dsoundsrc->pDSBSecondary);
604   GST_DSOUND_UNLOCK (dsoundsrc);
605   return TRUE;
606 }
607
608 /* 
609 return number of readed bytes */
610 static guint
611 gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length,
612     GstClockTime * timestamp)
613 {
614   GstDirectSoundSrc *dsoundsrc;
615   guint64 sleep_time_ms, sleep_until;
616   GstClockID clock_id;
617
618   HRESULT hRes;                 /* Result for windows functions */
619   DWORD dwCurrentCaptureCursor = 0;
620   DWORD dwBufferSize = 0;
621
622   LPVOID pLockedBuffer1 = NULL;
623   LPVOID pLockedBuffer2 = NULL;
624   DWORD dwSizeBuffer1 = 0;
625   DWORD dwSizeBuffer2 = 0;
626
627   DWORD dwStatus = 0;
628
629   GST_DEBUG_OBJECT (asrc, "reading directsoundsrc");
630
631   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
632
633   GST_DSOUND_LOCK (dsoundsrc);
634
635   /* Get current buffer status */
636   hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary,
637       &dwStatus);
638
639   if (FAILED (hRes)) {
640     GST_DSOUND_UNLOCK (dsoundsrc);
641     return -1;
642   }
643
644   /* Starting capturing if not already */
645   if (!(dwStatus & DSCBSTATUS_CAPTURING)) {
646     hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary,
647         DSCBSTART_LOOPING);
648     GST_INFO_OBJECT (asrc, "capture started");
649   }
650
651   /* Loop till the source has produced bytes equal to or greater than @length.
652    *
653    * DirectSound has a notification-based API that uses Windows CreateEvent()
654    * + WaitForSingleObject(), but it is completely useless for live streams.
655    *
656    *  1. You must schedule all events before starting capture
657    *  2. The events are all fired exactly once
658    *  3. You cannot schedule new events while a capture is running
659    *  4. You cannot stop/schedule/start either
660    *
661    * This means you cannot use the API while doing live looped capture and we
662    * must resort to this.
663    *
664    * However, this is almost as efficient as event-based capture since it's ok
665    * to consistently overwait by a fixed amount; the extra bytes will just end
666    * up being used in the next call, and the extra latency will be constant. */
667   while (TRUE) {
668     hRes =
669         IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary,
670         &dwCurrentCaptureCursor, NULL);
671
672     if (FAILED (hRes)) {
673       GST_DSOUND_UNLOCK (dsoundsrc);
674       return -1;
675     }
676
677     /* calculate the size of the buffer that's been captured while accounting
678      * for wrap-arounds */
679     if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) {
680       dwBufferSize = dsoundsrc->buffer_size -
681           (dsoundsrc->current_circular_offset - dwCurrentCaptureCursor);
682     } else {
683       dwBufferSize =
684           dwCurrentCaptureCursor - dsoundsrc->current_circular_offset;
685     }
686
687     if (dwBufferSize >= length) {
688       /* Yay, we got all the data we need */
689       break;
690     } else {
691       GST_DEBUG_OBJECT (asrc, "not enough data, got %lu (want at least %u)",
692           dwBufferSize, length);
693       /* If we didn't get enough data, sleep for a proportionate time */
694       sleep_time_ms = gst_util_uint64_scale (dsoundsrc->latency_time,
695           length - dwBufferSize, length * 1000);
696       /* Make sure we don't run in a tight loop unnecessarily */
697       sleep_time_ms = MAX (sleep_time_ms, 10);
698       /* Sleep using gst_clock_id_wait() so that we can be interrupted */
699       sleep_until = gst_clock_get_time (dsoundsrc->system_clock) +
700           sleep_time_ms * GST_MSECOND;
701       /* Setup the clock id wait */
702       if (G_UNLIKELY (dsoundsrc->read_wait_clock_id == NULL ||
703               gst_clock_single_shot_id_reinit (dsoundsrc->system_clock,
704                   dsoundsrc->read_wait_clock_id, sleep_until) == FALSE)) {
705         if (dsoundsrc->read_wait_clock_id != NULL)
706           gst_clock_id_unref (dsoundsrc->read_wait_clock_id);
707         dsoundsrc->read_wait_clock_id =
708             gst_clock_new_single_shot_id (dsoundsrc->system_clock, sleep_until);
709       }
710
711       clock_id = dsoundsrc->read_wait_clock_id;
712       dsoundsrc->reset_while_sleeping = FALSE;
713
714       GST_DEBUG_OBJECT (asrc, "waiting %" G_GUINT64_FORMAT "ms for more data",
715           sleep_time_ms);
716       GST_DSOUND_UNLOCK (dsoundsrc);
717
718       gst_clock_id_wait (clock_id, NULL);
719
720       GST_DSOUND_LOCK (dsoundsrc);
721
722       if (dsoundsrc->reset_while_sleeping == TRUE) {
723         GST_DEBUG_OBJECT (asrc, "reset while sleeping, cancelled read");
724         GST_DSOUND_UNLOCK (dsoundsrc);
725         return -1;
726       }
727     }
728   }
729
730   GST_DEBUG_OBJECT (asrc, "Got enough data: %lu bytes (wanted at least %u)",
731       dwBufferSize, length);
732
733   /* Lock the buffer and read only the first @length bytes. Keep the rest in
734    * the capture buffer for the next read. */
735   hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary,
736       dsoundsrc->current_circular_offset,
737       length,
738       &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);
739
740   /* NOTE: We now assume that dwSizeBuffer1 + dwSizeBuffer2 == length since the
741    * API is supposed to guarantee that */
742
743   /* Copy buffer data to another buffer */
744   if (hRes == DS_OK) {
745     memcpy (data, pLockedBuffer1, dwSizeBuffer1);
746   }
747
748   /* ...and if something is in another buffer */
749   if (pLockedBuffer2 != NULL) {
750     memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2);
751   }
752
753   dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2;
754   dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size;
755
756   IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
757       pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
758
759   GST_DSOUND_UNLOCK (dsoundsrc);
760
761   /* We always read exactly @length data */
762   return length;
763 }
764
765 static guint
766 gst_directsound_src_delay (GstAudioSrc * asrc)
767 {
768   GstDirectSoundSrc *dsoundsrc;
769   HRESULT hRes;
770   DWORD dwCurrentCaptureCursor;
771   DWORD dwBytesInQueue = 0;
772   gint nNbSamplesInQueue = 0;
773
774   GST_INFO_OBJECT (asrc, "Delay");
775
776   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
777
778   /* evaluate the number of samples in queue in the circular buffer */
779   hRes =
780       IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary,
781       &dwCurrentCaptureCursor, NULL);
782   /* FIXME: Check is this calculated right */
783   if (hRes == S_OK) {
784     if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) {
785       dwBytesInQueue =
786           dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset -
787           dwCurrentCaptureCursor);
788     } else {
789       dwBytesInQueue =
790           dwCurrentCaptureCursor - dsoundsrc->current_circular_offset;
791     }
792
793     nNbSamplesInQueue = dwBytesInQueue / dsoundsrc->bytes_per_sample;
794   }
795
796   GST_INFO_OBJECT (asrc, "Delay is %d samples", nNbSamplesInQueue);
797
798   return nNbSamplesInQueue;
799 }
800
801 static void
802 gst_directsound_src_reset (GstAudioSrc * asrc)
803 {
804   GstDirectSoundSrc *dsoundsrc;
805   LPVOID pLockedBuffer = NULL;
806   DWORD dwSizeBuffer = 0;
807
808   GST_DEBUG_OBJECT (asrc, "reset directsoundsrc");
809
810   dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
811
812   GST_DSOUND_LOCK (dsoundsrc);
813
814   dsoundsrc->reset_while_sleeping = TRUE;
815   /* Interrupt read sleep if required */
816   if (dsoundsrc->read_wait_clock_id != NULL)
817     gst_clock_id_unschedule (dsoundsrc->read_wait_clock_id);
818
819   if (dsoundsrc->pDSBSecondary) {
820     /*stop capturing */
821     HRESULT hRes = IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary);
822
823     /*reset position */
824     /*    hRes = IDirectSoundCaptureBuffer_SetCurrentPosition (dsoundsrc->pDSBSecondary, 0); */
825
826     /*reset the buffer */
827     hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary,
828         dsoundsrc->current_circular_offset, dsoundsrc->buffer_size,
829         &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);
830
831     if (SUCCEEDED (hRes)) {
832       memset (pLockedBuffer, 0, dwSizeBuffer);
833
834       hRes =
835           IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
836           pLockedBuffer, dwSizeBuffer, NULL, 0);
837     }
838     dsoundsrc->current_circular_offset = 0;
839
840   }
841
842   GST_DSOUND_UNLOCK (dsoundsrc);
843 }
844
845 /* If the PROP_DEVICE_NAME is set, find the mixer related to device;
846  * otherwise we get the default input mixer. */
847 static gboolean
848 gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
849     MIXERCAPS * mixer_caps)
850 {
851   MMRESULT mmres;
852   guint i, num_mixers;
853
854   num_mixers = mixerGetNumDevs ();
855   for (i = 0; i < num_mixers; i++) {
856     mmres = mixerOpen (&dsoundsrc->mixer, i, 0L, 0L,
857         MIXER_OBJECTF_MIXER | MIXER_OBJECTF_WAVEIN);
858
859     if (mmres != MMSYSERR_NOERROR)
860       continue;
861
862     mmres = mixerGetDevCaps ((UINT_PTR) dsoundsrc->mixer,
863         mixer_caps, sizeof (MIXERCAPS));
864
865     if (mmres != MMSYSERR_NOERROR) {
866       mixerClose (dsoundsrc->mixer);
867       continue;
868     }
869
870     /* Get default mixer */
871     if (dsoundsrc->device_name == NULL) {
872       GST_DEBUG ("Got default input mixer: %s", mixer_caps->szPname);
873       return TRUE;
874     }
875
876     if (g_strstr_len (dsoundsrc->device_name, -1, mixer_caps->szPname) != NULL) {
877       GST_DEBUG ("Got requested input mixer: %s", mixer_caps->szPname);
878       return TRUE;
879     }
880
881     /* Wrong mixer */
882     mixerClose (dsoundsrc->mixer);
883   }
884
885   GST_DEBUG ("Can't find input mixer");
886   return FALSE;
887 }
888
889 static void
890 gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc)
891 {
892   gint i, k;
893   gboolean found_mic;
894   MMRESULT mmres;
895   MIXERCAPS mixer_caps;
896   MIXERLINE mixer_line;
897   MIXERLINECONTROLS ml_ctrl;
898   PMIXERCONTROL pamixer_ctrls;
899
900   if (!gst_directsound_src_mixer_find (dsoundsrc, &mixer_caps))
901     goto mixer_init_fail;
902
903   /* Find the MIXERLINE related to MICROPHONE */
904   found_mic = FALSE;
905   for (i = 0; i < mixer_caps.cDestinations && !found_mic; i++) {
906     gint j, num_connections;
907
908     mixer_line.cbStruct = sizeof (mixer_line);
909     mixer_line.dwDestination = i;
910     mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer,
911         &mixer_line, MIXER_GETLINEINFOF_DESTINATION);
912
913     if (mmres != MMSYSERR_NOERROR)
914       goto mixer_init_fail;
915
916     num_connections = mixer_line.cConnections;
917     for (j = 0; j < num_connections && !found_mic; j++) {
918       mixer_line.cbStruct = sizeof (mixer_line);
919       mixer_line.dwDestination = i;
920       mixer_line.dwSource = j;
921       mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer,
922           &mixer_line, MIXER_GETLINEINFOF_SOURCE);
923
924       if (mmres != MMSYSERR_NOERROR)
925         goto mixer_init_fail;
926
927       if (mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
928           || mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE)
929         found_mic = TRUE;
930     }
931   }
932
933   if (found_mic == FALSE) {
934     GST_DEBUG ("Can't find mixer line related to input");
935     goto mixer_init_fail;
936   }
937
938   /* Get control associated with microphone audio line */
939   pamixer_ctrls = g_malloc (sizeof (MIXERCONTROL) * mixer_line.cControls);
940   ml_ctrl.cbStruct = sizeof (ml_ctrl);
941   ml_ctrl.dwLineID = mixer_line.dwLineID;
942   ml_ctrl.cControls = mixer_line.cControls;
943   ml_ctrl.cbmxctrl = sizeof (MIXERCONTROL);
944   ml_ctrl.pamxctrl = pamixer_ctrls;
945   mmres = mixerGetLineControls ((HMIXEROBJ) dsoundsrc->mixer,
946       &ml_ctrl, MIXER_GETLINECONTROLSF_ALL);
947
948   /* Find control associated with volume and mute */
949   for (k = 0; k < mixer_line.cControls; k++) {
950     if (strstr (pamixer_ctrls[k].szName, "Volume") != NULL) {
951       dsoundsrc->control_id_volume = pamixer_ctrls[k].dwControlID;
952       dsoundsrc->dw_vol_max = pamixer_ctrls[k].Bounds.dwMaximum;
953       dsoundsrc->dw_vol_min = pamixer_ctrls[k].Bounds.dwMinimum;
954     } else if (strstr (pamixer_ctrls[k].szName, "Mute") != NULL) {
955       dsoundsrc->control_id_mute = pamixer_ctrls[k].dwControlID;
956     } else {
957       GST_DEBUG ("Control not handled: %s", pamixer_ctrls[k].szName);
958     }
959   }
960   g_free (pamixer_ctrls);
961
962   if (dsoundsrc->control_id_volume < 0 && dsoundsrc->control_id_mute < 0)
963     goto mixer_init_fail;
964
965   /* Save cChannels information to properly changes in volume */
966   dsoundsrc->mixerline_cchannels = mixer_line.cChannels;
967   return;
968
969 mixer_init_fail:
970   GST_WARNING ("Failed to get Volume and Mute controls");
971   if (dsoundsrc->mixer != NULL) {
972     mixerClose (dsoundsrc->mixer);
973     dsoundsrc->mixer = NULL;
974   }
975 }
976
977 static gdouble
978 gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc)
979 {
980   return (gdouble) dsoundsrc->volume / 100;
981 }
982
983 static gboolean
984 gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc)
985 {
986   return dsoundsrc->mute;
987 }
988
989 static void
990 gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc, gdouble volume)
991 {
992   MMRESULT mmres;
993   MIXERCONTROLDETAILS details;
994   MIXERCONTROLDETAILS_UNSIGNED details_unsigned;
995   glong dwvolume;
996
997   if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_volume < 0) {
998     GST_WARNING ("mixer not initialized");
999     return;
1000   }
1001
1002   dwvolume = volume * dsoundsrc->dw_vol_max;
1003   dwvolume = CLAMP (dwvolume, dsoundsrc->dw_vol_min, dsoundsrc->dw_vol_max);
1004
1005   GST_DEBUG ("max volume %ld | min volume %ld",
1006       dsoundsrc->dw_vol_max, dsoundsrc->dw_vol_min);
1007   GST_DEBUG ("set volume to %f (%ld)", volume, dwvolume);
1008
1009   details.cbStruct = sizeof (details);
1010   details.dwControlID = dsoundsrc->control_id_volume;
1011   details.cChannels = dsoundsrc->mixerline_cchannels;
1012   details.cMultipleItems = 0;
1013
1014   details_unsigned.dwValue = dwvolume;
1015   details.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
1016   details.paDetails = &details_unsigned;
1017
1018   mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer,
1019       &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
1020
1021   if (mmres != MMSYSERR_NOERROR)
1022     GST_WARNING ("Failed to set volume");
1023   else
1024     dsoundsrc->volume = volume * 100;
1025 }
1026
1027 static void
1028 gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc, gboolean mute)
1029 {
1030   MMRESULT mmres;
1031   MIXERCONTROLDETAILS details;
1032   MIXERCONTROLDETAILS_BOOLEAN details_boolean;
1033
1034   if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_mute < 0) {
1035     GST_WARNING ("mixer not initialized");
1036     return;
1037   }
1038
1039   details.cbStruct = sizeof (details);
1040   details.dwControlID = dsoundsrc->control_id_mute;
1041   details.cChannels = dsoundsrc->mixerline_cchannels;
1042   details.cMultipleItems = 0;
1043
1044   details_boolean.fValue = mute;
1045   details.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN);
1046   details.paDetails = &details_boolean;
1047
1048   mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer,
1049       &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
1050
1051   if (mmres != MMSYSERR_NOERROR)
1052     GST_WARNING ("Failed to set mute");
1053   else
1054     dsoundsrc->mute = mute;
1055 }
1056
1057 static const gchar *
1058 gst_directsound_src_get_device (GstDirectSoundSrc * dsoundsrc)
1059 {
1060   return dsoundsrc->device_id;
1061 }
1062
1063 static void
1064 gst_directsound_src_set_device (GstDirectSoundSrc * dsoundsrc,
1065     const gchar * device_id)
1066 {
1067   g_free (dsoundsrc->device_id);
1068   dsoundsrc->device_id = g_strdup (device_id);
1069 }