commit-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Commit-gnuradio] [gnuradio] 03/09: Completely refactored windows audio


From: git
Subject: [Commit-gnuradio] [gnuradio] 03/09: Completely refactored windows audio sink to use multiple buffers, eliminates skipping sounds when nperiods and period_time are set within reason. Default values should work fine for most. Set verbose=true to see additional info. output device can be set to either the ordinal number of the device to use, or the string name of the device. Setting a string in verbose mode will also display a list of choices in the console on run
Date: Tue, 26 Apr 2016 00:41:04 +0000 (UTC)

This is an automated email from the git hooks/post-receive script.

jcorgan pushed a commit to branch master
in repository gnuradio.

commit 03669f5e7b4943e34909b1fed8d186ce2eebbf15
Author: gnieboer <address@hidden>
Date:   Mon Mar 28 02:30:21 2016 +0300

    Completely refactored windows audio sink to use multiple buffers, 
eliminates skipping sounds when nperiods and period_time are set within reason. 
 Default values should work fine for most.  Set verbose=true to see additional 
info.  output device can be set to either the ordinal number of the device to 
use, or the string name of the device.  Setting a string in verbose mode will 
also display a list of choices in the console on run
---
 gr-audio/lib/windows/windows_sink.cc | 424 ++++++++++++++++++++---------------
 gr-audio/lib/windows/windows_sink.h  |  13 +-
 2 files changed, 246 insertions(+), 191 deletions(-)

diff --git a/gr-audio/lib/windows/windows_sink.cc 
b/gr-audio/lib/windows/windows_sink.cc
index 6598c97..4ec798b 100644
--- a/gr-audio/lib/windows/windows_sink.cc
+++ b/gr-audio/lib/windows/windows_sink.cc
@@ -27,6 +27,8 @@
 #include "audio_registry.h"
 #include <windows_sink.h>
 #include <gnuradio/io_signature.h>
+#include <gnuradio/prefs.h>
+#include <gnuradio/logger.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -36,27 +38,29 @@
 #include <stdexcept>
 #include <string>
 #include <sstream>
+#include "boost/lexical_cast.hpp"
 
 namespace gr {
   namespace audio {
 
     sink::sptr
     windows_sink_fcn(int sampling_rate,
-                     const std::string &device_name,
-                     bool)
+        const std::string &device_name,
+        bool)
     {
       return sink::sptr
         (new windows_sink(sampling_rate, device_name));
     }
 
-    static const double CHUNK_TIME = 0.1; //0.001; // 100 ms
-
-    // FIXME these should query some kind of user preference
+    static const double CHUNK_TIME = 
prefs::singleton()->get_double("audio_windows", "period_time", 0.1); // 100 ms 
(below 3ms distortion will likely occur regardless of number of buffers, will 
likely be a higher limit on slower machines)
+    static const int nPeriods = prefs::singleton()->get_long("audio_windows", 
"nperiods", 4); // 4 should be more than enough with a normal chunk time (2 
will likely work as well)... at 3ms chunks 10 was enough on a fast machine
+    static const bool verbose = prefs::singleton()->get_bool("audio_windows", 
"verbose", false);
+    static const std::string default_device = 
prefs::singleton()->get_string("audio_windows", "standard_output_device", 
"default");
 
     static std::string
     default_device_name()
     {
-      return "WAVE_MAPPER";
+      return (default_device == "default" ? "WAVE_MAPPER" : default_device);
     }
 
     windows_sink::windows_sink(int sampling_freq, const std::string 
device_name)
@@ -65,28 +69,63 @@ namespace gr {
                       io_signature::make(0, 0, 0)),
         d_sampling_freq(sampling_freq),
         d_device_name(device_name.empty() ? default_device_name() : 
device_name),
-        d_fd(-1), d_buffer(0), d_chunk_size(0)
+        d_fd(-1), d_buffers(0), d_chunk_size(0)
     {
+      /* Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo */
+      wave_format.wFormatTag = WAVE_FORMAT_PCM;
+      wave_format.nChannels = 2;                                               
// changing this will require adjustments to the work routine.
+      wave_format.wBitsPerSample = 16;                         // changing 
this will necessitate changing buffer type from short.
+      wave_format.nSamplesPerSec = d_sampling_freq;    // 44100 is default but 
up to flowgraph settings;
+      wave_format.nBlockAlign =
+        wave_format.nChannels * (wave_format.wBitsPerSample / 8);
+      wave_format.nAvgBytesPerSec =
+        wave_format.nSamplesPerSec * wave_format.nBlockAlign;
+      wave_format.cbSize = 0;
+
+      d_chunk_size = (int)(d_sampling_freq * CHUNK_TIME);  // Samples per chunk
+      set_output_multiple(d_chunk_size);
+      d_buffer_size = d_chunk_size * wave_format.nChannels * 
(wave_format.wBitsPerSample / 8); // room for 16-bit audio on two channels.
+
       d_wave_write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-      if(open_waveout_device() < 0) {
-        //fprintf(stderr, "audio_windows_sink:open_waveout_device() failed\n");
+      if (open_waveout_device() < 0) {
         perror("audio_windows_sink:open_waveout_device() failed\n");
         throw
-          std::runtime_error ("audio_windows_sink:open_waveout_device() 
failed");
+          std::runtime_error("audio_windows_sink:open_waveout_device() 
failed");
       }
-
-      d_chunk_size = (int)(d_sampling_freq * CHUNK_TIME);
-      set_output_multiple(d_chunk_size);
-
-      d_buffer = new short[d_chunk_size * 2];
+      else if (verbose) {
+        GR_LOG_INFO(logger, "Opened windows waveout device");
+      }
+      d_buffers = new LPWAVEHDR[nPeriods];
+      for (int i = 0; i < nPeriods; i++)
+      {
+        d_buffers[i] = new WAVEHDR;
+        d_buffers[i]->dwLoops = 0L;
+        d_buffers[i]->dwFlags = WHDR_DONE;
+        d_buffers[i]->dwBufferLength = d_buffer_size;
+        d_buffers[i]->lpData = new CHAR[d_buffer_size];
+      }
+      if (verbose) GR_LOG_INFO(logger, boost::format("Initialized %1% %2%ms 
audio buffers, total memory used: %3$0.2fkB") % (nPeriods) % (CHUNK_TIME * 
1000) % ((d_buffer_size * nPeriods) / 1024.0));
     }
 
     windows_sink::~windows_sink()
     {
+      // stop playback and set all buffers to DONE.
+      waveOutReset(d_h_waveout);
+      // Now we can deallocate the buffers
+      for (int i = 0; i < nPeriods; i++)
+      {
+        if (d_buffers[i]->dwFlags & (WHDR_DONE | WHDR_PREPARED)) {
+          waveOutUnprepareHeader(d_h_waveout, d_buffers[i], 
sizeof(d_buffers[i]));
+        }
+        else {
+
+        }
+        delete d_buffers[i]->lpData;
+      }
       /* Free the callback Event */
       CloseHandle(d_wave_write_event);
       waveOutClose(d_h_waveout);
-      delete [] d_buffer;
+      delete [] d_buffers;
     }
 
     int
@@ -95,66 +134,71 @@ namespace gr {
                        gr_vector_void_star & output_items)
     {
       const float *f0, *f1;
-      bool playtestsound = false;
-      if(playtestsound) {
-        // dummy
-        f0 = (const float*)input_items[0];
 
-        for(int i = 0; i < noutput_items; i += d_chunk_size) {
-         for(int j = 0; j < d_chunk_size; j++) {
-            d_buffer[2*j + 0] = (short)(sin(2.0 * 3.1415926535897932384626 *
-                                            (float)j * 1000.0 / 
(float)d_sampling_freq) *
-                                        8192 + 0);     //+32767
-            d_buffer[2*j + 1] = d_buffer[2*j + 0];
+      // Pick the first available wave header (buffer)
+      // If none available, then wait until the processing event if fired and 
check again
+      // Not all events free up a buffer, so it could take more than one loop 
to get one
+      // however, to avoid a lock, only wait 1 second for a freed up buffer 
then abort.
+      LPWAVEHDR chosen_header = NULL;
+      int c = 0;
+      while (!chosen_header)
+      {
+        ResetEvent(d_wave_write_event);
+        for (int i = 0; i < nPeriods; i++)
+        {
+          if (d_buffers[i]->dwFlags & WHDR_DONE) {
+            // uncomment the below to see which buffers are being consumed
+            // printf("%d ", i);
+            chosen_header = d_buffers[i];
+            break;
           }
-         f0 += d_chunk_size;
-         if(write_waveout
-             ((HPSTR)d_buffer, 2*d_chunk_size * sizeof(short)) < 0) {
-            fprintf(stderr, "audio_windows_sink: write failed\n");
-            perror("audio_windows_sink: write failed");
+        }
+        if (!chosen_header) {
+          WaitForSingleObject(d_wave_write_event, 100);
+          printf("aO");
+        }
+        if (c++ > 10) {
+          for (int i = 0; i < nPeriods; i++) {
+            printf("%d: %d\n", i, d_buffers[i]->dwFlags);
           }
-       }
-        // break;
+          perror("audio_windows_sink: no audio buffers available");
+          return -1;
+        }
       }
-      else {
-        switch(input_items.size ()) {
-        case 1:         // mono input
-          f0 = (const float*)input_items[0];
-
-         for(int i = 0; i < noutput_items; i += d_chunk_size) {
-            for(int j = 0; j < d_chunk_size; j++) {
-              d_buffer[2*j + 0] = (short)(f0[j] * 32767);
-              d_buffer[2*j + 1] = (short)(f0[j] * 32767);
-            }
-            f0 += d_chunk_size;
-            if(write_waveout
-               ((HPSTR)d_buffer, 2*d_chunk_size * sizeof(short)) < 0) {
-              //fprintf(stderr, "audio_windows_sink: write failed\n");
-              perror("audio_windows_sink: write failed");
-            }
-          }
-         break;
 
-       case 2:           // stereo input
-         f0 = (const float*)input_items[0];
-         f1 = (const float*)input_items[1];
+      short *d_buffer = (short *)chosen_header->lpData;
 
-         for(int i = 0; i < noutput_items; i += d_chunk_size)  {
-            for(int j = 0; j < d_chunk_size; j++) {
-              d_buffer[2*j + 0] = (short)(f0[j] * 32767);
-              d_buffer[2*j + 1] = (short)(f1[j] * 32767);
-            }
-            f0 += d_chunk_size;
-            f1 += d_chunk_size;
-            if(write_waveout
-               ((HPSTR)d_buffer, 2*d_chunk_size * sizeof(short)) < 0) {
-              //fprintf(stderr, "audio_windows_sink: write failed\n");
-              perror("audio_windows_sink: write failed");
-            }
+      switch (input_items.size()) {
+      case 1:         // mono input
+        f0 = (const float*)input_items[0];
+
+        for (int i = 0; i < noutput_items; i += d_chunk_size) {
+          for (int j = 0; j < d_chunk_size; j++) {
+            d_buffer[2 * j + 0] = (short)(f0[j] * 32767);
+            d_buffer[2 * j + 1] = (short)(f0[j] * 32767);
           }
-         break;
-       }
+          f0 += d_chunk_size;
+        }
+        break;
+      case 2:           // stereo input
+        f0 = (const float*)input_items[0];
+        f1 = (const float*)input_items[1];
+
+        for (int i = 0; i < noutput_items; i += d_chunk_size) {
+          for (int j = 0; j < d_chunk_size; j++) {
+            d_buffer[2 * j + 0] = (short)(f0[j] * 32767);
+            d_buffer[2 * j + 1] = (short)(f1[j] * 32767);
+          }
+          f0 += d_chunk_size;
+          f1 += d_chunk_size;
+        }
+        break;
+      }
+      if (write_waveout
+        (chosen_header) < 0) {
+        perror("audio_windows_sink: write failed");
       }
+
       return noutput_items;
     }
 
@@ -162,154 +206,162 @@ namespace gr {
     windows_sink::string_to_int(const std::string & s)
     {
       int i;
-      std::istringstream (s) >> i;
+      std::istringstream(s) >> i;
       return i;
-    } //ToInt()
+    }
 
-    int
-    windows_sink::open_waveout_device(void)
+    MMRESULT windows_sink::is_format_supported(LPWAVEFORMATEX pwfx, UINT 
uDeviceID)
     {
-      UINT /*UINT_PTR */ u_device_id;
-
-      /** Identifier of the waveform-audio output device to open. It
-          can be either a device identifier or a handle of an open
-          waveform-audio input device. You can use the following flag
-          instead of a device identifier.
-       *
-       * Value Meaning
-       * WAVE_MAPPER The function selects a waveform-audio output
-       * device capable of playing the given format.
-       */
-      if(d_device_name.empty () || default_device_name () == d_device_name)
-        u_device_id = WAVE_MAPPER;
-      else
-        u_device_id = (UINT) string_to_int (d_device_name);
-      // Open a waveform device for output using event callback.
-
-      unsigned long result;
-      //HWAVEOUT  outHandle;
-      WAVEFORMATEX wave_format;
+      return (waveOutOpen(
+        NULL,                 // ptr can be NULL for query 
+        uDeviceID,            // the device identifier 
+        pwfx,                 // defines requested format 
+        NULL,                 // no callback 
+        NULL,                 // no instance data 
+        WAVE_FORMAT_QUERY));  // query only, do not open device
+    }
 
-      /* Initialize the WAVEFORMATEX for 16-bit, 44KHz, stereo */
-      wave_format.wFormatTag = WAVE_FORMAT_PCM;
-      wave_format.nChannels = 2;
-      wave_format.nSamplesPerSec = d_sampling_freq;    //44100;
-      wave_format.wBitsPerSample = 16;
-      wave_format.nBlockAlign =
-        wave_format.nChannels * (wave_format.wBitsPerSample / 8);
-      wave_format.nAvgBytesPerSec =
-        wave_format.nSamplesPerSec * wave_format.nBlockAlign;
-      wave_format.cbSize = 0;
+    bool windows_sink::is_number(const std::string& s)
+    {
+      std::string::const_iterator it = s.begin();
+      while (it != s.end() && std::isdigit(*it)) ++it;
+      return !s.empty() && it == s.end();
+    }
 
-      /* Open the (preferred) Digital Audio Out device. */
-      result = waveOutOpen(&d_h_waveout, WAVE_MAPPER,
-                           &wave_format,
-                           (DWORD_PTR)d_wave_write_event,
-                           0, CALLBACK_EVENT | WAVE_ALLOWSYNC);
-      //|WAVE_FORMAT_DIRECT | CALLBACK_EVENT| WAVE_ALLOWSYNC
+    UINT windows_sink::find_device(std::string szDeviceName)
+    {
+      UINT result = -1;
+      UINT num_devices = waveOutGetNumDevs();
+      if (num_devices > 0) {
+        // what the device name passed as a number?
+        if (is_number(szDeviceName))
+        {
+          // a number, so must be referencing a device ID (which incremement 
from zero)
+          UINT num = std::stoul(szDeviceName);
+          if (num < num_devices) {
+            result = num;
+          }
+          else {
+            GR_LOG_INFO(logger, boost::format("Warning: waveOut deviceID %d 
was not found, defaulting to WAVE_MAPPER") % num);
+            result = WAVE_MAPPER;
+          }
 
-      if(result) {
-        //fprintf(stderr, "audio_windows_sink: Failed to open waveform output 
device.\n");
-        perror("audio_windows_sink: Failed to open waveform output device.");
-        //LocalUnlock(hFormat);
-        //LocalFree(hFormat);
-        //mmioClose(hmmio, 0);
-        return -1;
+        }
+        else {
+          // device name passed as string
+          for (UINT i = 0; i < num_devices; i++)
+          {
+            WAVEOUTCAPS woc;
+            if (waveOutGetDevCaps(i, &woc, sizeof(woc)) != MMSYSERR_NOERROR)
+            {
+              perror("Error: Could not retrieve wave out device capabilities 
for device");
+              return -1;
+            }
+            if (woc.szPname == szDeviceName)
+            {
+              result = i;
+            }
+            if (verbose) GR_LOG_INFO(logger, boost::format("WaveOut Device %d: 
%s") % i % woc.szPname);
+          }
+          if (result == -1) {
+            GR_LOG_INFO(logger, boost::format("Warning: waveOut device '%s' 
was not found, defaulting to WAVE_MAPPER") % szDeviceName);
+            result = WAVE_MAPPER;
+          }
+        }
+      }
+      else {
+        perror("Error: No WaveOut devices present or accessible");
       }
+      return result;
+    }
 
-      //
-      // Do not Swallow the "open" event.
-      //
-      //WaitForSingleObject(d_wave_write_event, INFINITE);
+    int
+      windows_sink::open_waveout_device(void)
+    {
+      UINT u_device_id;
+      unsigned long result;
 
-      // Allocate and lock memory for the header.
+      /** Identifier of the waveform-audio output device to open. It
+        can be either a device identifier or a handle of an open
+        waveform-audio input device. You can use the following flag
+        instead of a device identifier.
+        WAVE_MAPPER The function selects a waveform-audio output
+        device capable of playing the given format.
+      */
+      if (d_device_name.empty() || default_device_name() == d_device_name)
+        u_device_id = WAVE_MAPPER;
+      else
+        // The below could be uncommented to allow selection of different 
device handles
+        // however it is unclear what other devices are out there and how a 
user
+        // would know the device ID so at the moment we will ignore that 
setting
+        // and stick with WAVE_MAPPER
+        u_device_id = find_device(d_device_name);
+      if (verbose) GR_LOG_INFO(logger, boost::format("waveOut Device ID: %1%") 
% (u_device_id));
 
-      d_h_wave_hdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
-                                 (DWORD)sizeof(WAVEHDR));
-      if(d_h_wave_hdr == NULL) {
-        //GlobalUnlock(hData);
-        //GlobalFree(hData);
-        //fprintf(stderr, "audio_windows_sink: Not enough memory for 
header.\n");
-        perror("audio_windows_sink: Not enough memory for header.");
+      // Check if the sampling rate/bits/channels are good to go with the 
device.
+      MMRESULT supported = is_format_supported(&wave_format, u_device_id);
+      if (supported != MMSYSERR_NOERROR) {
+        char err_msg[50];
+        waveOutGetErrorText(supported, err_msg, 50);
+        GR_LOG_INFO(logger, boost::format("format error: %s") % err_msg);
+        perror("audio_windows_sink: Requested audio format is not supported by 
device driver");
         return -1;
       }
 
-      d_lp_wave_hdr = (LPWAVEHDR)GlobalLock(d_h_wave_hdr);
-      if(d_lp_wave_hdr == NULL) {
-        //GlobalUnlock(hData);
-        //GlobalFree(hData);
-        //fprintf(stderr, "audio_windows_sink: Failed to lock memory for 
header.\n");
-        perror("audio_windows_sink: Failed to lock memory for header.");
+      // Open a waveform device for output using event callback.
+      result = waveOutOpen(&d_h_waveout, u_device_id,
+        &wave_format,
+        (DWORD_PTR)d_wave_write_event,
+        0, CALLBACK_EVENT | WAVE_ALLOWSYNC);
+
+      if (result) {
+        perror("audio_windows_sink: Failed to open waveform output device.");
         return -1;
       }
-      //d_lp_wave_hdr->dwFlags = WHDR_DONE;
       return 0;
     }
 
     int
-    windows_sink::write_waveout(HPSTR lp_data, DWORD dw_data_size)
+    windows_sink::write_waveout(LPWAVEHDR lp_wave_hdr)
     {
       UINT w_result;
-      int teller = 100;
-      // After allocation, set up and prepare header.
-      /*while ((d_lp_wave_hdr->dwFlags & WHDR_DONE)==0 && teller>0) {
-        teller--;
-        Sleep(1);
-        } */
-      // Wait until previous wave write completes (first event is the open 
event).
-      WaitForSingleObject(d_wave_write_event, 100);  // INFINITE
-      d_lp_wave_hdr->lpData = lp_data;
-      d_lp_wave_hdr->dwBufferLength = dw_data_size;
-      d_lp_wave_hdr->dwFlags = 0L;
+
       /* Clear the WHDR_DONE bit (which the driver set last time that
-         this WAVEHDR was sent via waveOutWrite and was played). Some
-         drivers need this to be cleared */
-      //d_lp_wave_hdr->dwFlags &= ~WHDR_DONE;
+      this WAVEHDR was sent via waveOutWrite and was played). Some
+      drivers need this to be cleared */
+      lp_wave_hdr->dwFlags = 0L;
 
-      d_lp_wave_hdr->dwLoops = 0L;
       w_result =
-        waveOutPrepareHeader(d_h_waveout, d_lp_wave_hdr, sizeof(WAVEHDR));
-      if(w_result != 0) {
-        //GlobalUnlock(hData);
-        //GlobalFree(hData);
-        //fprintf(stderr, "audio_windows_sink: Failed to waveOutPrepareHeader. 
error %i\n",w_result);
+        waveOutPrepareHeader(d_h_waveout, lp_wave_hdr, sizeof(WAVEHDR));
+      if (w_result != 0) {
         perror("audio_windows_sink: Failed to waveOutPrepareHeader");
+        return -1;
       }
-      // Now the data block can be sent to the output device. The
-      // waveOutWrite function returns immediately and waveform
-      // data is sent to the output device in the background.
-      //while(!readyforplayback) Sleep(1);
-      //readyforplayback=false;
-
-      w_result = waveOutWrite(d_h_waveout, d_lp_wave_hdr, sizeof(WAVEHDR));
-      if(w_result != 0) {
-        //GlobalUnlock(hData);
-        //GlobalFree(hData);
-        //fprintf(stderr, "audio_windows_sink: Failed to write block to 
device.error %i\n",w_result);
+
+      w_result = waveOutWrite(d_h_waveout, lp_wave_hdr, sizeof(WAVEHDR));
+      if (w_result != 0) {
         perror("audio_windows_sink: Failed to write block to device");
-        switch(w_result) {
-       case MMSYSERR_INVALHANDLE:
-         fprintf(stderr, "Specified device handle is invalid.\n");
-         break;
-       case MMSYSERR_NODRIVER:
-         fprintf(stderr, " No device driver is present.\n");
-         break;
-       case MMSYSERR_NOMEM:
-         fprintf(stderr, " Unable to allocate or lock memory.\n");
-         break;
-       case WAVERR_UNPREPARED:
-         fprintf(stderr,
-                  " The data block pointed to by the pwh parameter hasn't been 
prepared.\n");
-         break;
-       default:
-         fprintf(stderr, "Unknown error %i\n", w_result);
-       }
-        waveOutUnprepareHeader(d_h_waveout, d_lp_wave_hdr, sizeof(WAVEHDR));
+        switch (w_result) {
+       case MMSYSERR_INVALHANDLE:
+         fprintf(stderr, "Specified device handle is invalid.\n");
+         break;
+       case MMSYSERR_NODRIVER:
+         fprintf(stderr, " No device driver is present.\n");
+         break;
+       case MMSYSERR_NOMEM:
+         fprintf(stderr, " Unable to allocate or lock memory.\n");
+         break;
+       case WAVERR_UNPREPARED:
+         fprintf(stderr,
+           " The data block pointed to by the pwh parameter hasn't been 
prepared.\n");
+         break;
+       default:
+         fprintf(stderr, "Unknown error %i\n", w_result);
+       }
+        waveOutUnprepareHeader(d_h_waveout, lp_wave_hdr, sizeof(WAVEHDR));
         return -1;
       }
-      //WaitForSingleObject(d_wave_write_event, INFINITE);
       return 0;
     }
-
   } /* namespace audio */
 } /* namespace gr */
diff --git a/gr-audio/lib/windows/windows_sink.h 
b/gr-audio/lib/windows/windows_sink.h
index 3d21cc4..2bfdbd3 100644
--- a/gr-audio/lib/windows/windows_sink.h
+++ b/gr-audio/lib/windows/windows_sink.h
@@ -47,17 +47,20 @@ namespace gr {
       int         d_sampling_freq;
       std::string d_device_name;
       int         d_fd;
-      short      *d_buffer;
-      int         d_chunk_size;
+      LPWAVEHDR  *d_buffers;
+      DWORD       d_chunk_size;
+         DWORD       d_buffer_size;
       HWAVEOUT    d_h_waveout;
-      HGLOBAL     d_h_wave_hdr;
-      LPWAVEHDR   d_lp_wave_hdr;
       HANDLE      d_wave_write_event;
+         WAVEFORMATEX wave_format;
 
     protected:
       int string_to_int(const std::string & s);
       int open_waveout_device(void);
-      int write_waveout(HPSTR lp_data, DWORD dw_data_size);
+      int write_waveout(LPWAVEHDR lp_wave_hdr);
+         MMRESULT is_format_supported(LPWAVEFORMATEX pwfx, UINT uDeviceID);
+         bool is_number(const std::string& s);
+         UINT find_device(std::string szDeviceName);
 
     public:
       windows_sink(int sampling_freq,



reply via email to

[Prev in Thread] Current Thread [Next in Thread]