[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,
- [Commit-gnuradio] [gnuradio] branch master updated (ece754f -> 6a33fff), git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 02/09: added a gr-preferences getter, git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 01/09: Merge pull request #1 from gnuradio/master, git, 2016/04/25
- [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,
git <=
- [Commit-gnuradio] [gnuradio] 06/09: Merge remote-tracking branch 'gnieboer/windows_audio', git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 05/09: qtgui: added C++-only QTGUI application example., git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 07/09: Merge remote-tracking branch 'mmueller/udp_source_add_payload_buffer_gr_pref', git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 09/09: Merge remote-tracking branch 'tom/qtgui_c++_example', git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 04/09: qtgui: more appropriate doxygen link., git, 2016/04/25
- [Commit-gnuradio] [gnuradio] 08/09: Merge branch 'maint', git, 2016/04/25