-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathAudioSink.cpp
More file actions
142 lines (131 loc) · 4.89 KB
/
AudioSink.cpp
File metadata and controls
142 lines (131 loc) · 4.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright (c) 2014-2016 Josh Blum
// SPDX-License-Identifier: BSL-1.0
#include "AudioBlock.hpp"
#include <algorithm> //min/max
#include <iostream>
/***********************************************************************
* |PothosDoc Audio Sink
*
* The audio sink forwards an input sample stream into an audio output device.
* In interleaved mode, the samples are interleaved from one input port,
* In the port-per-channel mode, each audio channel uses a separate port.
*
* |category /Audio
* |category /Sinks
* |keywords audio sound stereo mono speaker
*
* |param deviceName[Device Name] The name of an audio device on the system,
* the integer index of an audio device on the system,
* or an empty string to use the default input device.
* |widget StringEntry()
* |default ""
* |preview valid
*
* |param sampRate[Sample Rate] The rate of audio samples.
* |option 32e3
* |option 44.1e3
* |option 48e3
* |default 44.1e3
* |units Sps
* |widget ComboBox(editable=true)
*
* |param dtype[Data Type] The data type consumed by the audio sink.
* |option [Float32] "float32"
* |option [Int32] "int32"
* |option [Int16] "int16"
* |option [Int8] "int8"
* |option [UInt8] "uint8"
* |default "float32"
* |preview disable
*
* |param numChans [Num Channels] The number of audio channels.
* This parameter controls the number of samples per stream element.
* |widget SpinBox(minimum=1)
* |default 1
*
* |param chanMode [Channel Mode] The channel mode.
* One port with interleaved channels or one port per channel?
* |option [Interleaved channels] "INTERLEAVED"
* |option [One port per channel] "PORTPERCHAN"
* |default "INTERLEAVED"
* |preview disable
*
* |param reportMode [Report Mode] Options for reporting underflow.
* <ul>
* <li>"LOGGER" - reports the full error message to the logger</li>
* <li>"STDERROR" - prints "aU" (audio underflow) to stderror</li>
* <li>"DISABLED" - disabled mode turns off all reporting</li>
* </ul>
* |default "STDERROR"
* |option [Logging Subsystem] "LOGGER"
* |option [Standard Error] "STDERROR"
* |option [Reporting Disabled] "DISABLED"
* |preview disable
* |tab Underflow
*
* |param backoffTime [Backoff Time] Configurable wait for mitigating underflows.
* The sink block will not consume samples after an underflow for the specified wait time.
* A small wait time of several milliseconds can help to prevent cascading underflows
* when the upstream source is not keeping up with the configured audio rate.
* |units milliseconds
* |preview valid
* |default 0
* |tab Underflow
*
* |factory /audio/sink(dtype, numChans, chanMode)
* |initializer setupDevice(deviceName)
* |initializer setupStream(sampRate)
* |setter setReportMode(reportMode)
* |setter setBackoffTime(backoffTime)
**********************************************************************/
class AudioSink : public AudioBlock
{
public:
AudioSink(const Pothos::DType &dtype, const size_t numChans, const std::string &chanMode):
AudioBlock("AudioSink", true, dtype, numChans, chanMode)
{
//setup ports
if (_interleaved) this->setupInput(0, Pothos::DType::fromDType(dtype, numChans));
else for (size_t i = 0; i < numChans; i++) this->setupInput(i, dtype);
}
static Block *make(const Pothos::DType &dtype, const size_t numChans, const std::string &chanMode)
{
return new AudioSink(dtype, numChans, chanMode);
}
void work(void)
{
if (this->workInfo().minInElements == 0) return;
//calculate the number of frames
int numFrames = Pa_GetStreamWriteAvailable(_stream);
if (numFrames < 0)
{
throw Pothos::Exception("AudioSink::work()", "Pa_GetStreamWriteAvailable: " + std::string(Pa_GetErrorText(numFrames)));
}
if (numFrames == 0) numFrames = MIN_FRAMES_BLOCKING;
numFrames = std::min<int>(numFrames, this->workInfo().minInElements);
//get the buffer
const void *buffer = nullptr;
if (_interleaved) buffer = this->workInfo().inputPointers[0];
else buffer = (const void *)this->workInfo().inputPointers.data();
//peform write to the device
PaError err = Pa_WriteStream(_stream, buffer, numFrames);
//handle the error reporting
bool logError = err != paNoError;
if (err == paOutputUnderflowed)
{
_readyTime += _backoffTime;
if (_reportStderror) std::cerr << "aU" << std::flush;
logError = _reportLogger;
}
if (logError)
{
poco_error(_logger, "Pa_WriteStream: " + std::string(Pa_GetErrorText(err)));
}
//not ready to consume because of backoff
if (_readyTime >= std::chrono::high_resolution_clock::now()) return this->yield();
//consume buffer (all modes)
for (auto port : this->inputs()) port->consume(numFrames);
}
};
static Pothos::BlockRegistry registerAudioSink(
"/audio/sink", &AudioSink::make);