summaryrefslogtreecommitdiff
path: root/sys/waveform
diff options
context:
space:
mode:
authorSébastien Moutte <sebastien@moutte.net>2007-02-20 21:34:00 +0000
committerSébastien Moutte <sebastien@moutte.net>2007-02-20 21:34:00 +0000
commit296687a398ba315f40fb1ead0de97ae4727162b0 (patch)
tree6cc66cdba1652af7290b20650ee43b8479ff2937 /sys/waveform
parent2d1b42029af8c75e3539c11314f818b61c6e6a8e (diff)
sys/directsound/gstdirectsoundsink.*: Remove include of unused headers.
Original commit message from CVS: * sys/directsound/gstdirectsoundsink.c: * sys/directsound/gstdirectsoundsink.h: Remove include of unused headers. * sys/waveform/gstwaveformplugin.c: * sys/waveform/gstwaveformsink.c: * sys/waveform/gstwaveformsink.h: * win32/vs6/libgstwaveform.dsp: Add a new waveform plugin which includes an audio sink element using the WaveForm win32 API. * win32/MANIFEST: Add the new project file form waveform plugin.
Diffstat (limited to 'sys/waveform')
-rw-r--r--sys/waveform/gstwaveformplugin.c42
-rw-r--r--sys/waveform/gstwaveformsink.c601
-rw-r--r--sys/waveform/gstwaveformsink.h96
3 files changed, 739 insertions, 0 deletions
diff --git a/sys/waveform/gstwaveformplugin.c b/sys/waveform/gstwaveformplugin.c
new file mode 100644
index 000000000..0d7943e91
--- /dev/null
+++ b/sys/waveform/gstwaveformplugin.c
@@ -0,0 +1,42 @@
+/* GStreamer
+* Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
+*
+* gstwaveformplugin.c:
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library General Public
+* License as published by the Free Software Foundation; either
+* version 2 of the License, or (at your option) any later version.
+*
+* This library 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
+* Library General Public License for more details.
+*
+* You should have received a copy of the GNU Library General Public
+* License along with this library; if not, write to the
+* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+* Boston, MA 02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstwaveformsink.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "waveformsink", GST_RANK_PRIMARY,
+ GST_TYPE_WAVEFORM_SINK))
+ return FALSE;
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "waveform",
+ "WaveForm win32 API plugin",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/sys/waveform/gstwaveformsink.c b/sys/waveform/gstwaveformsink.c
new file mode 100644
index 000000000..e709d649a
--- /dev/null
+++ b/sys/waveform/gstwaveformsink.c
@@ -0,0 +1,601 @@
+/* GStreamer
+* Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
+*
+* gstwaveformsink.c:
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library General Public
+* License as published by the Free Software Foundation; either
+* version 2 of the License, or (at your option) any later version.
+*
+* This library 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
+* Library General Public License for more details.
+*
+* You should have received a copy of the GNU Library General Public
+* License along with this library; if not, write to the
+* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+* Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * SECTION:element-waveformsink
+ * @short_description: output sound using WaveForm API
+ *
+ * <refsect2>
+ * <para>
+ * This element lets you output sound using the WaveForm API.
+ * </para>
+ * <para>
+ * Note that you should almost always use generic audio conversion elements
+ * like audioconvert and audioresample in front of an audiosink to make sure
+ * your pipeline works under all circumstances (those conversion elements will
+ * act in passthrough-mode if no conversion is necessary).
+ * </para>
+ * <title>Example pipelines</title>
+ * <para>
+ * <programlisting>
+ * gst-launch-0.10 -v audiotestsrc ! audioconvert ! volume volume=0.1 ! waveformsink
+ * </programlisting>
+ * will output a sine wave (continuous beep sound) to your sound card (with
+ * a very low volume as precaution).
+ * </para>
+ * <para>
+ * <programlisting>
+ * gst-launch-0.10 -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! waveformsink
+ * </programlisting>
+ * will play an Ogg/Vorbis audio file and output it.
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstwaveformsink.h"
+
+GST_DEBUG_CATEGORY_STATIC (waveformsink_debug);
+
+/* elementfactory information */
+static const GstElementDetails gst_waveform_sink_details =
+GST_ELEMENT_DETAILS ("WaveForm Audio Sink",
+ "Sink/Audio",
+ "Output to a sound card via WaveForm API",
+ "Sebastien Moutte <sebastien@moutte.net>");
+
+static void gst_waveform_sink_base_init (gpointer g_class);
+static void gst_waveform_sink_class_init (GstWaveFormSinkClass * klass);
+static void gst_waveform_sink_init (GstWaveFormSink * wfsink,
+ GstWaveFormSinkClass * g_class);
+static void gst_waveform_sink_finalise (GObject * object);
+static void gst_waveform_sink_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_waveform_sink_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+static GstCaps *gst_waveform_sink_getcaps (GstBaseSink * bsink);
+
+/************************************************************************/
+/* GstAudioSink functions */
+/************************************************************************/
+static gboolean gst_waveform_sink_prepare (GstAudioSink * asink,
+ GstRingBufferSpec * spec);
+static gboolean gst_waveform_sink_unprepare (GstAudioSink * asink);
+static gboolean gst_waveform_sink_open (GstAudioSink * asink);
+static gboolean gst_waveform_sink_close (GstAudioSink * asink);
+static guint gst_waveform_sink_write (GstAudioSink * asink, gpointer data,
+ guint length);
+static guint gst_waveform_sink_delay (GstAudioSink * asink);
+static void gst_waveform_sink_reset (GstAudioSink * asink);
+
+/************************************************************************/
+/* Utils */
+/************************************************************************/
+GstCaps *gst_waveform_sink_create_caps (gint rate, gint channels,
+ gint bits_per_sample);
+WAVEHDR *bufferpool_get_buffer (GstWaveFormSink * wfsink, gpointer data,
+ guint length);
+void CALLBACK waveOutProc (HWAVEOUT hwo, UINT uMsg, unsigned long dwInstance,
+ DWORD dwParam1, DWORD dwParam2);
+
+static GstStaticPadTemplate waveformsink_sink_factory =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw-int, "
+ "signed = (boolean) { TRUE, FALSE }, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
+ "audio/x-raw-int, "
+ "signed = (boolean) { TRUE, FALSE }, "
+ "width = (int) 8, "
+ "depth = (int) 8, "
+ "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]"));
+
+GST_BOILERPLATE (GstWaveFormSink, gst_waveform_sink, GstAudioSink,
+ GST_TYPE_AUDIO_SINK);
+
+static void
+gst_waveform_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (element_class, &gst_waveform_sink_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&waveformsink_sink_factory));
+}
+
+static void
+gst_waveform_sink_class_init (GstWaveFormSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
+ GstBaseAudioSinkClass *gstbaseaudiosink_class;
+ GstAudioSinkClass *gstaudiosink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+ gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
+ gstaudiosink_class = (GstAudioSinkClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_waveform_sink_finalise);
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_waveform_sink_get_property);
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_waveform_sink_set_property);
+
+ gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_waveform_sink_getcaps);
+
+ gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_waveform_sink_prepare);
+ gstaudiosink_class->unprepare =
+ GST_DEBUG_FUNCPTR (gst_waveform_sink_unprepare);
+ gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_waveform_sink_open);
+ gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_waveform_sink_close);
+ gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_waveform_sink_write);
+ gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_waveform_sink_delay);
+ gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_waveform_sink_reset);
+
+ GST_DEBUG_CATEGORY_INIT (waveformsink_debug, "waveformsink", 0,
+ "Waveform sink");
+}
+
+static void
+gst_waveform_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_waveform_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_waveform_sink_init (GstWaveFormSink * wfsink,
+ GstWaveFormSinkClass * g_class)
+{
+ /* initialize members */
+ wfsink->hwaveout = NULL;
+ wfsink->cached_caps = NULL;
+ wfsink->wave_buffers = NULL;
+ wfsink->write_buffer = 0;
+ wfsink->buffer_count = BUFFER_COUNT;
+ wfsink->buffer_size = BUFFER_SIZE;
+ wfsink->free_buffers_count = wfsink->buffer_count;
+ wfsink->bytes_in_queue = 0;
+
+ InitializeCriticalSection (&wfsink->critic_wave);
+}
+
+static void
+gst_waveform_sink_finalise (GObject * object)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (object);
+
+ if (wfsink->cached_caps) {
+ gst_caps_unref (wfsink->cached_caps);
+ wfsink->cached_caps = NULL;
+ }
+
+ DeleteCriticalSection (&wfsink->critic_wave);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstCaps *
+gst_waveform_sink_getcaps (GstBaseSink * bsink)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (bsink);
+ MMRESULT mmresult;
+ WAVEOUTCAPS wocaps;
+ GstCaps *caps, *caps_temp;
+
+ /* return the cached caps if already defined */
+ if (wfsink->cached_caps) {
+ return gst_caps_ref (wfsink->cached_caps);
+ }
+
+ /* get the default device caps */
+ mmresult = waveOutGetDevCaps (WAVE_MAPPER, &wocaps, sizeof (wocaps));
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
+ GST_ELEMENT_ERROR (wfsink, RESOURCE, SETTINGS,
+ ("gst_waveform_sink_getcaps: waveOutGetDevCaps failed error=>%s",
+ wfsink->error_string), (NULL));
+ return NULL;
+ }
+
+ caps = gst_caps_new_empty ();
+
+ /* create a caps for all wave formats supported by the device
+ starting by the best quality format */
+ if (wocaps.dwFormats & WAVE_FORMAT_96S16) {
+ caps_temp = gst_waveform_sink_create_caps (96000, 2, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_96S08) {
+ caps_temp = gst_waveform_sink_create_caps (96000, 2, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_96M16) {
+ caps_temp = gst_waveform_sink_create_caps (96000, 1, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_96M08) {
+ caps_temp = gst_waveform_sink_create_caps (96000, 1, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_4S16) {
+ caps_temp = gst_waveform_sink_create_caps (44100, 2, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_4S08) {
+ caps_temp = gst_waveform_sink_create_caps (44100, 2, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_4M16) {
+ caps_temp = gst_waveform_sink_create_caps (44100, 1, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_4M08) {
+ caps_temp = gst_waveform_sink_create_caps (44100, 1, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_2S16) {
+ caps_temp = gst_waveform_sink_create_caps (22050, 2, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_2S08) {
+ caps_temp = gst_waveform_sink_create_caps (22050, 2, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_2M16) {
+ caps_temp = gst_waveform_sink_create_caps (22050, 1, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_2M08) {
+ caps_temp = gst_waveform_sink_create_caps (22050, 1, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_1S16) {
+ caps_temp = gst_waveform_sink_create_caps (11025, 2, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_1S08) {
+ caps_temp = gst_waveform_sink_create_caps (11025, 2, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_1M16) {
+ caps_temp = gst_waveform_sink_create_caps (11025, 1, 16);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+ if (wocaps.dwFormats & WAVE_FORMAT_1M08) {
+ caps_temp = gst_waveform_sink_create_caps (11025, 1, 8);
+ if (caps_temp) {
+ gst_caps_append (caps, caps_temp);
+ }
+ }
+
+ if (gst_caps_is_empty (caps)) {
+ gst_caps_unref (caps);
+ caps = NULL;
+ } else {
+ wfsink->cached_caps = gst_caps_ref (caps);
+ }
+
+ GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink, "Returning caps %s",
+ gst_caps_to_string (caps));
+
+ return caps;
+}
+
+static gboolean
+gst_waveform_sink_open (GstAudioSink * asink)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+
+ /* nothing to do here as the device needs to be opened with the format we will use */
+
+ return TRUE;
+}
+
+static gboolean
+gst_waveform_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+ WAVEFORMATEX wfx;
+ MMRESULT mmresult;
+ guint index;
+
+ /* setup waveformex struture with the input ringbuffer specs */
+ memset (&wfx, 0, sizeof (wfx));
+ wfx.cbSize = 0;
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nChannels = spec->channels;
+ wfx.nSamplesPerSec = spec->rate;
+ wfx.wBitsPerSample = (spec->bytes_per_sample * 8) / wfx.nChannels;
+ wfx.nBlockAlign = spec->bytes_per_sample;
+ wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
+
+ /* save bytes per sample to use it in delay */
+ wfsink->bytes_per_sample = spec->bytes_per_sample;
+
+ /* open the default audio device with the given caps */
+ mmresult = waveOutOpen (&wfsink->hwaveout, WAVE_MAPPER,
+ &wfx, (DWORD) waveOutProc, (DWORD) wfsink, CALLBACK_FUNCTION);
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
+ GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
+ ("gst_waveform_sink_prepare: waveOutOpen failed error=>%s",
+ wfsink->error_string), (NULL));
+ return FALSE;
+ }
+
+ /* evaluate the buffer size and the number of buffers needed */
+ wfsink->free_buffers_count = wfsink->buffer_count;
+
+ /* allocate wave buffers */
+ wfsink->wave_buffers = (WAVEHDR *) g_new0 (WAVEHDR, wfsink->buffer_count);
+ if (!wfsink->wave_buffers) {
+ GST_ELEMENT_ERROR (wfsink, RESOURCE, OPEN_WRITE,
+ ("gst_waveform_sink_prepare: Failed to allocate wave buffer headers (buffer count=%d)",
+ wfsink->buffer_count), (NULL));
+ return FALSE;
+ }
+ memset (wfsink->wave_buffers, 0, sizeof (WAVEHDR) * wfsink->buffer_count);
+
+ /* setup headers */
+ for (index = 0; index < wfsink->buffer_count; index++) {
+ wfsink->wave_buffers[index].dwBufferLength = wfsink->buffer_size;
+ wfsink->wave_buffers[index].lpData = g_new0 (gchar, wfsink->buffer_size);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_waveform_sink_unprepare (GstAudioSink * asink)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+
+ /* free wave buffers */
+ if (wfsink->wave_buffers) {
+ guint index;
+
+ for (index = 0; index < wfsink->buffer_count; index++) {
+ if (wfsink->wave_buffers[index].dwFlags & WHDR_PREPARED) {
+ MMRESULT mmresult =
+ waveOutUnprepareHeader (wfsink->hwaveout,
+ &wfsink->wave_buffers[index], sizeof (WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string,
+ ERROR_LENGTH - 1);
+ GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
+ "gst_waveform_sink_unprepare: Error unpreparing buffer => %s",
+ wfsink->error_string);
+ }
+ }
+ g_free (wfsink->wave_buffers[index].lpData);
+ }
+ g_free (wfsink->wave_buffers);
+ wfsink->wave_buffers = NULL;
+ }
+
+ /* close waveform-audio output device */
+ if (wfsink->hwaveout) {
+ waveOutClose (wfsink->hwaveout);
+ wfsink->hwaveout = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_waveform_sink_close (GstAudioSink * asink)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+
+ return TRUE;
+}
+
+static guint
+gst_waveform_sink_write (GstAudioSink * asink, gpointer data, guint length)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+ WAVEHDR *waveheader;
+ MMRESULT mmresult;
+ guint bytes_to_write = length;
+ guint remaining_length = length;
+
+ wfsink->bytes_in_queue += length;
+
+ while (remaining_length > 0) {
+ if (wfsink->free_buffers_count == 0) {
+ /* no free buffer available, wait for one */
+ Sleep (10);
+ continue;
+ }
+
+ /* get the current write buffer header */
+ waveheader = &wfsink->wave_buffers[wfsink->write_buffer];
+
+ /* unprepare the header if needed */
+ if (waveheader->dwFlags & WHDR_PREPARED) {
+ mmresult =
+ waveOutUnprepareHeader (wfsink->hwaveout, waveheader,
+ sizeof (WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
+ GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
+ "Error unpreparing buffer => %s", wfsink->error_string);
+ }
+ }
+
+ if (wfsink->buffer_size - waveheader->dwUser >= remaining_length)
+ bytes_to_write = remaining_length;
+ else
+ bytes_to_write = wfsink->buffer_size - waveheader->dwUser;
+
+ memcpy (waveheader->lpData + waveheader->dwUser, data, bytes_to_write);
+ waveheader->dwUser += bytes_to_write;
+ remaining_length -= bytes_to_write;
+ data = (byte *) data + bytes_to_write;
+
+ if (waveheader->dwUser == wfsink->buffer_size) {
+ /* we have filled a buffer, let's prepare it and next write it to the device */
+ mmresult =
+ waveOutPrepareHeader (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
+ GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
+ "gst_waveform_sink_write: Error preparing header => %s",
+ wfsink->error_string);
+ }
+ mmresult = waveOutWrite (wfsink->hwaveout, waveheader, sizeof (WAVEHDR));
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
+ GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
+ "gst_waveform_sink_write: Error writting buffer to the device => %s",
+ wfsink->error_string);
+ }
+
+ EnterCriticalSection (&wfsink->critic_wave);
+ wfsink->free_buffers_count--;
+ LeaveCriticalSection (&wfsink->critic_wave);
+
+ wfsink->write_buffer++;
+ wfsink->write_buffer %= wfsink->buffer_count;
+ waveheader->dwUser = 0;
+ wfsink->bytes_in_queue = 0;
+ GST_CAT_LOG_OBJECT (waveformsink_debug, wfsink,
+ "gst_waveform_sink_write: Writting a buffer to the device (free buffers remaining=%d, write buffer=%d)",
+ wfsink->free_buffers_count, wfsink->write_buffer);
+ }
+ }
+
+ return length;
+}
+
+static guint
+gst_waveform_sink_delay (GstAudioSink * asink)
+{
+ /* return the number of samples in queue (device+internal queue) */
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+ guint bytes_in_device =
+ (wfsink->buffer_count - wfsink->free_buffers_count) * wfsink->buffer_size;
+ guint delay =
+ (bytes_in_device + wfsink->bytes_in_queue) / wfsink->bytes_per_sample;
+ return delay;
+}
+
+static void
+gst_waveform_sink_reset (GstAudioSink * asink)
+{
+ GstWaveFormSink *wfsink = GST_WAVEFORM_SINK (asink);
+ MMRESULT mmresult = waveOutReset (wfsink->hwaveout);
+
+ if (mmresult != MMSYSERR_NOERROR) {
+ waveOutGetErrorText (mmresult, wfsink->error_string, ERROR_LENGTH - 1);
+ GST_CAT_WARNING_OBJECT (waveformsink_debug, wfsink,
+ "gst_waveform_sink_reset: Error reseting waveform-audio device => %s",
+ wfsink->error_string);
+ }
+}
+
+GstCaps *
+gst_waveform_sink_create_caps (gint rate, gint channels, gint bits_per_sample)
+{
+ GstCaps *caps = NULL;
+
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "width", G_TYPE_INT, bits_per_sample,
+ "depth", G_TYPE_INT, bits_per_sample,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER,
+ "signed", G_TYPE_BOOLEAN, TRUE,
+ "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, rate, NULL);
+ return caps;
+}
+
+void CALLBACK
+waveOutProc (HWAVEOUT hwo,
+ UINT uMsg, unsigned long dwInstance, DWORD dwParam1, DWORD dwParam2)
+{
+ GstWaveFormSink *wfsink = (GstWaveFormSink *) dwInstance;
+
+ if (uMsg == WOM_DONE) {
+ EnterCriticalSection (&wfsink->critic_wave);
+ wfsink->free_buffers_count++;
+ LeaveCriticalSection (&wfsink->critic_wave);
+ }
+}
diff --git a/sys/waveform/gstwaveformsink.h b/sys/waveform/gstwaveformsink.h
new file mode 100644
index 000000000..79227794e
--- /dev/null
+++ b/sys/waveform/gstwaveformsink.h
@@ -0,0 +1,96 @@
+/* GStreamer
+ * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
+ *
+ * gstwaveformsink.h:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_WAVEFORMSINK_H__
+#define __GST_WAVEFORMSINK_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiosink.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#define WAVE_FORMAT_96M08 0x00001000 /* 96 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_96S08 0x00002000 /* 96 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_96M16 0x00004000 /* 96 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_96S16 0x00008000 /* 96 kHz, Stereo, 16-bit */
+
+#define ERROR_LENGTH MAXERRORLENGTH+50
+#define BUFFER_COUNT 20
+#define BUFFER_SIZE 8192
+
+G_BEGIN_DECLS
+#define GST_TYPE_WAVEFORM_SINK (gst_waveform_sink_get_type())
+#define GST_WAVEFORM_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAVEFORM_SINK,GstWaveFormSink))
+#define GST_WAVEFORM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAVEFORM_SINK,GstWaveFormSinkClass))
+#define GST_IS_WAVEFORM_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAVEFORM_SINK))
+#define GST_IS_WAVEFORM_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAVEFORM_SINK))
+typedef struct _GstWaveFormSink GstWaveFormSink;
+typedef struct _GstWaveFormSinkClass GstWaveFormSinkClass;
+
+struct _GstWaveFormSink
+{
+ /* parent object */
+ GstAudioSink sink;
+
+ /* supported caps */
+ GstCaps *cached_caps;
+
+ /* handle to the waveform-audio output device */
+ HWAVEOUT hwaveout;
+
+ /* table of buffer headers */
+ WAVEHDR *wave_buffers;
+
+ /* critical section protecting access to the number of free buffers */
+ CRITICAL_SECTION critic_wave;
+
+ /* number of free buffers available */
+ guint free_buffers_count;
+
+ /* current free buffer where you have to write incoming data */
+ guint write_buffer;
+
+ /* size of buffers streamed to the device */
+ guint buffer_size;
+
+ /* number of buffers streamed to the device */
+ guint buffer_count;
+
+ /* total of bytes in queue before they are written to the device */
+ guint bytes_in_queue;
+
+ /* bytes per sample from setcaps used to evaluate the number samples returned by delay */
+ guint bytes_per_sample;
+
+ /* wave form error string */
+ gchar error_string[ERROR_LENGTH];
+};
+
+struct _GstWaveFormSinkClass
+{
+ GstAudioSinkClass parent_class;
+};
+
+GType gst_waveform_sink_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_WAVEFORMSINK_H__ */