shithub: sox

Download patch

ref: 6e79c8982f988d5caa0984d438e979078389f868
parent: e588f34341a53e9a3cd1f319050a02eaeb99b663
author: cbagwell <cbagwell>
date: Tue Aug 25 23:27:26 EDT 2009

Initial commit of native windows audio output driver.

--- a/ChangeLog
+++ b/ChangeLog
@@ -5,7 +5,7 @@
 sox-11gamma, followed by a list of prior authors and features.
 
 
-sox-14.0.1	2009-xx-xx
+sox-14.3.1	2009-xx-xx
 ----------
 
 Previously deprecated features that have been removed in this release:
@@ -36,6 +36,8 @@
 File formats:
 
 Audio device drivers:
+
+  o Add native windows audio output driver. (Pavel Karneliuk)
 
 Effects:
 
--- a/configure.ac
+++ b/configure.ac
@@ -428,7 +428,9 @@
 AC_OPTIONAL_FORMAT(pulseaudio, PULSEAUDIO, [AC_CHECK_HEADER(pulse/simple.h, [AC_CHECK_LIB(pulse, pa_simple_new, PULSEAUDIO_LIBS="$PULSEAUDIO_LIBS -lpulse -lpulse-simple",using_pulseaudio=no,"-lpulse-simple")], using_pulseaudio=no)])
 
 
+AC_OPTIONAL_FORMAT(waveaudio, WAVEAUDIO, [AC_CHECK_HEADER(mmsystem.h, [WAVEAUDIO_LIBS="$WAVEAUDIO_LIBS -lwinmm"], using_waveaudio=no)])
 
+
 AC_OPTIONAL_FORMAT(sndfile, SNDFILE, [SOX_PATH_SNDFILE(, using_sndfile=no)])
 AC_SUBST(SNDFILE_CFLAGS)
 
@@ -543,6 +545,7 @@
 echo "oss........................$using_oss"
 echo "pulseaudio.................$using_pulseaudio"
 echo "sunaudio...................$using_sunaudio"
+echo "waveaudio..................$using_waveaudio"
 echo
 echo "OPTIONAL FILE FORMATS"
 echo "amrnb......................$using_amrnb"
--- a/src/formats.h
+++ b/src/formats.h
@@ -99,6 +99,9 @@
 #if defined HAVE_PULSEAUDIO && (defined STATIC_PULSEAUDIO || !defined HAVE_LIBLTDL)
   FORMAT(pulseaudio)
 #endif
+#if defined HAVE_WAVEAUDIO && (defined STATIC_WAVEAUDIO || !defined HAVE_LIBLTDL)
+  FORMAT(waveaudio)
+#endif
 #if defined HAVE_SNDIO && (defined STATIC_SNDIO || !defined HAVE_LIBLTDL)
   FORMAT(sndio)
 #endif
--- a/src/optional-fmts.am
+++ b/src/optional-fmts.am
@@ -144,6 +144,18 @@
 endif
 endif
 
+if HAVE_WAVEAUDIO
+if STATIC_WAVEAUDIO
+  libsox_la_SOURCES += waveaudio.c
+  libsox_la_LIBADD += @WAVEAUDIO_LIBS@
+  sox_LDADD += @WAVEAUDIO_LIBS@
+else
+  libsox_fmt_waveaudio_la_SOURCES = waveaudio.c
+  libsox_fmt_waveaudio_la_LIBADD = libsox.la @WAVEAUDIO_LIBS@
+  pkglib_LTLIBRARIES += libsox_fmt_waveaudio.la
+endif
+endif
+
 if HAVE_SNDIO
 if STATIC_SNDIO
   libsox_la_SOURCES += sndio.c
--- a/src/optional-fmts.in
+++ b/src/optional-fmts.in
@@ -24,6 +24,7 @@
 OPT_FORMAT(mp3, MP3, mp3.c mp3-duration.h)
 OPT_FORMAT(oss, OSS, oss.c)
 OPT_FORMAT(pulseaudio, PULSEAUDIO, pulseaudio.c)
+OPT_FORMAT(waveaudio, WAVEAUDIO, waveaudio.c)
 OPT_FORMAT(sndio, SNDIO, sndio.c)
 OPT_FORMAT(sunau, SUN_AUDIO, sunaudio.c)
 OPT_FORMAT(vorbis, OGG_VORBIS, vorbis.c vorbis1.h)
--- /dev/null
+++ b/src/waveaudio.c
@@ -1,0 +1,200 @@
+/* libSoX (c) 2009 SoX contributors
+ * Copyright (c) 2009 Pavel Karneliuk pavel_karneliuk@users.sourceforge.net
+ * Implementation of audio output driver for Windows
+ *
+ * This library 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.1 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 Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "sox_i.h"
+#ifdef HAVE_WAVEAUDIO
+
+#include <assert.h>
+#include <windows.h>
+#include <mmsystem.h>
+
+#define num_buffers 16
+typedef struct {
+    HWAVEOUT    waveout;
+    WAVEFORMATEX  format;
+
+    HGLOBAL handle_data;
+    HGLOBAL handle_wavheader;
+
+    HPSTR   ptr_data[num_buffers];
+    WAVEHDR*   ptr_wavheader;
+
+    HANDLE need_more_data_blocks;
+} priv_t;
+
+void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR instance, DWORD_PTR Param1, DWORD_PTR Param2);
+
+int setup_write(sox_format_t* ft)
+{
+    DWORD buf_len;
+    int i;
+    priv_t *priv = (priv_t *)ft->priv;
+    if(priv == NULL) return SOX_EOF;
+
+    ft->signal.precision = 16;
+
+    priv->format.wFormatTag = WAVE_FORMAT_PCM;
+    priv->format.nChannels = ft->signal.channels;
+    priv->format.nSamplesPerSec = ft->signal.rate;
+    priv->format.wBitsPerSample = sizeof(int16_t)*8;
+    priv->format.nAvgBytesPerSec = priv->format.nSamplesPerSec * priv->format.wBitsPerSample/8;
+    priv->format.nBlockAlign = (priv->format.nChannels*priv->format.wBitsPerSample)/8;
+    priv->format.cbSize = 0;
+
+    buf_len = sox_globals.bufsiz * sizeof(int16_t);
+
+    priv->handle_data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, buf_len * num_buffers);
+    priv->ptr_data[0]      = (HPSTR) GlobalLock(priv->handle_data);
+    priv->handle_wavheader         = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD)sizeof(WAVEHDR) * num_buffers);
+    priv->ptr_wavheader = (WAVEHDR*) GlobalLock(priv->handle_wavheader);
+    memset(priv->ptr_wavheader, 0, sizeof(WAVEHDR) * num_buffers);
+    for(i=0; i<num_buffers; i++)
+    {
+        priv->ptr_data[i] = priv->ptr_data[0] + buf_len*i;
+
+        priv->ptr_wavheader[i].lpData = priv->ptr_data[i];
+        priv->ptr_wavheader[i].dwBufferLength = buf_len;
+        priv->ptr_wavheader[i].dwFlags = 0L;
+        priv->ptr_wavheader[i].dwLoops = 0L;
+    }
+
+    priv->waveout = NULL;
+
+    waveOutOpen((LPHWAVEOUT)&priv->waveout, WAVE_MAPPER, &priv->format,
+                               (DWORD_PTR)waveOutProc, (DWORD_PTR)priv, CALLBACK_FUNCTION);
+
+    priv->need_more_data_blocks = CreateEvent(NULL, TRUE, TRUE, NULL);
+
+    return priv->waveout ? SOX_SUCCESS : SOX_EOF;
+}
+
+int stop_write(sox_format_t* ft)
+{
+    priv_t *priv = (priv_t *)ft->priv;
+    if(priv == NULL) return SOX_EOF;
+
+    while(WAVERR_STILLPLAYING == waveOutClose(priv->waveout))
+    {
+/*        // terminate
+        if( priv->is_cancelled() )
+        {
+            waveOutReset(priv->waveout);
+        }
+        else priv->update_pos();*/
+        Sleep(50);
+    }
+
+    CloseHandle(priv->need_more_data_blocks);
+
+    GlobalUnlock(priv->handle_wavheader);
+    GlobalFree(priv->handle_wavheader);
+
+    GlobalUnlock(priv->handle_data);
+    GlobalFree(priv->handle_data);
+
+    return SOX_SUCCESS;
+}
+
+static size_t write(sox_format_t * ft, const sox_sample_t* buf, size_t len)
+{
+    int i;
+    int clips = 0;
+    size_t j;
+    WAVEHDR* header = NULL;
+    MMRESULT res = 0;
+    priv_t *priv = (priv_t *)ft->priv;
+    if(priv == NULL) return SOX_EOF;
+
+    while(header == NULL)
+    {
+        // find first free header
+        for(i=0; i<num_buffers; i++)
+        {
+            if(priv->ptr_wavheader[i].dwFlags == 0 || priv->ptr_wavheader[i].dwFlags & WHDR_DONE )
+            {
+                header = &priv->ptr_wavheader[i];
+                break;
+            }
+        }
+
+        if(header == NULL) // not found free data blocks
+        {
+            while(WAIT_TIMEOUT == WaitForSingleObject(priv->need_more_data_blocks, 50))
+            {
+/*                if(task->is_cancelled())
+                {
+                    waveOutReset(task->waveout);
+                    *osamp = 0;
+                    return SOX_SUCCESS;
+                }
+                else priv->update_pos();*/
+            }
+            ResetEvent(priv->need_more_data_blocks);
+        }
+    }
+
+    /* put ibuf into data block for playback */
+    if(header)
+    {
+        res = waveOutUnprepareHeader(priv->waveout, header, sizeof(WAVEHDR));
+        assert(MMSYSERR_NOERROR == res);
+
+        for(j=0; j< len; ++j)
+        {
+            SOX_SAMPLE_LOCALS;
+            ((int16_t *)header->lpData)[j] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[j],clips);
+        }
+
+        header->dwBufferLength = len * sizeof(int16_t);
+        header->dwFlags = 0;
+
+        res = waveOutPrepareHeader(priv->waveout, header, sizeof(WAVEHDR));
+        assert(MMSYSERR_NOERROR == res);
+
+        waveOutWrite(priv->waveout, header, sizeof(WAVEHDR));
+        assert(MMSYSERR_NOERROR == res);
+    }
+
+    return len;
+}
+
+void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR instance, DWORD_PTR Param1, DWORD_PTR Param2)
+{
+    priv_t* priv = (priv_t*) instance;
+    /* unlock Sox pipeline if some data block has been processed*/
+    if( uMsg == WOM_DONE )
+    {
+        SetEvent(priv->need_more_data_blocks);
+    }
+}
+
+LSX_FORMAT_HANDLER(waveaudio)
+{
+  static const char * const names[] = {"waveaudio", NULL};
+  static unsigned const write_encodings[] = { SOX_ENCODING_UNKNOWN, 16, 0, 0};
+  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
+    "Windows Multimedia Audio Output", names, 
+    SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
+    NULL, NULL, NULL,
+    setup_write, write, stop_write,
+    NULL, write_encodings, NULL, sizeof(priv_t)
+  };
+  return &handler;
+}
+#endif