Skip to content

Commit 63ea836

Browse files
committed
new class: AudioInputMonitor
1 parent 10f48e0 commit 63ea836

File tree

1 file changed

+194
-26
lines changed

1 file changed

+194
-26
lines changed

src/AudioTools/CoreAudio/AudioStreams.h

Lines changed: 194 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,7 @@ class GeneratedSoundStream : public AudioStream {
449449
AudioInfo audioInfo() override { return generator_ptr->audioInfo(); }
450450

451451
/// This is unbounded so we just return the buffer size
452-
virtual int available() override {
453-
return active ? buffer_size : 0;
454-
}
452+
virtual int available() override { return active ? buffer_size : 0; }
455453

456454
/// privide the data as byte stream
457455
size_t readBytes(uint8_t *data, size_t len) override {
@@ -472,7 +470,8 @@ class GeneratedSoundStream : public AudioStream {
472470
protected:
473471
bool active = true; // support for legacy sketches
474472
SoundGenerator<T> *generator_ptr;
475-
int buffer_size = DEFAULT_BUFFER_SIZE * 100; // there is no reason to limit this
473+
int buffer_size =
474+
DEFAULT_BUFFER_SIZE * 100; // there is no reason to limit this
476475
const char *source_not_defined_error = "Source not defined";
477476
};
478477

@@ -579,15 +578,15 @@ class BufferedStream : public ModifyingStream {
579578
}
580579
}
581580

582-
/// Provides data w/o consuming
581+
/// Provides data w/o consuming
583582
size_t peekBytes(uint8_t *data, size_t len) {
584583
if (buffer.isEmpty()) {
585584
refill();
586-
}
585+
}
587586
return buffer.peekArray(data, len);
588587
}
589588

590-
/// Returns the available bytes
589+
/// Returns the available bytes
591590
int available() override {
592591
if (p_in == nullptr) return 0;
593592
return buffer.available() + p_in->available();
@@ -599,10 +598,9 @@ class BufferedStream : public ModifyingStream {
599598
/// Resize the buffer
600599
void resize(int size) { buffer.resize(size); }
601600

602-
/// Defines the minimum direct unbuffered read size to the original source (default is 1024)
603-
void setMinUnbufferedReadSize(size_t size) {
604-
minReadBufferSize = size;
605-
}
601+
/// Defines the minimum direct unbuffered read size to the original source
602+
/// (default is 1024)
603+
void setMinUnbufferedReadSize(size_t size) { minReadBufferSize = size; }
606604

607605
protected:
608606
SingleBuffer<uint8_t> buffer{0};
@@ -1258,7 +1256,7 @@ class InputMixer : public AudioStream {
12581256
void setRetryCount(int retry) { retry_count = retry; }
12591257

12601258
/// Removes a stream by index position
1261-
bool remove(int idx){
1259+
bool remove(int idx) {
12621260
if (idx < 0 || idx >= size()) {
12631261
return false;
12641262
}
@@ -1313,11 +1311,11 @@ class InputMixer : public AudioStream {
13131311

13141312
/// Recalculate the weights
13151313
void recalculateWeights() {
1316-
int total = 0;
1317-
for (int j = 0; j < weights.size(); j++) {
1318-
total += weights[j];
1319-
}
1320-
total_weights = total;
1314+
int total = 0;
1315+
for (int j = 0; j < weights.size(); j++) {
1316+
total += weights[j];
1317+
}
1318+
total_weights = total;
13211319
}
13221320

13231321
/// mixing using a vector of samples
@@ -1753,14 +1751,8 @@ class FilteredStream : public ModifyingStream {
17531751
class VolumeMeter : public ModifyingStream {
17541752
public:
17551753
VolumeMeter() = default;
1756-
VolumeMeter(AudioStream &as) {
1757-
addNotifyAudioChange(as);
1758-
setStream(as);
1759-
}
1760-
VolumeMeter(AudioOutput &ao) {
1761-
addNotifyAudioChange(ao);
1762-
setOutput(ao);
1763-
}
1754+
VolumeMeter(AudioStream &as) { setStream(as); }
1755+
VolumeMeter(AudioOutput &ao) { setOutput(ao); }
17641756
VolumeMeter(Print &print) { setOutput(print); }
17651757
VolumeMeter(Stream &stream) { setStream(stream); }
17661758

@@ -1875,6 +1867,14 @@ class VolumeMeter : public ModifyingStream {
18751867
}
18761868
}
18771869

1870+
void setOutput(AudioOutput &out) {
1871+
addNotifyAudioChange(out);
1872+
setOutput((Print &)out);
1873+
}
1874+
void setStream(AudioStream &io) {
1875+
addNotifyAudioChange(io);
1876+
setStream((Stream &)io);
1877+
}
18781878
void setOutput(Print &out) override { p_out = &out; }
18791879
void setStream(Stream &io) override {
18801880
p_out = &io;
@@ -1953,10 +1953,178 @@ class VolumeMeter : public ModifyingStream {
19531953
/// @ingroup io
19541954
using VolumePrint = VolumeMeter;
19551955

1956-
/// @brief Legacy alias for VolumeMeter
1956+
/// @brief Legacy alias for VolumeMeter
19571957
/// @ingroup io
19581958
using VolumeOutput = VolumeMeter;
19591959

1960+
/**
1961+
* @brief Monitors audio input and reports if the volume exceeds a specified
1962+
* limit within a given period.
1963+
*
1964+
* This class uses a VolumeMeter internally and tracks if the input volume has
1965+
* exceeded the specified limit during the last period. It can be used in an
1966+
* audio processing chain as a ModifyingStream.
1967+
* @ingroup io
1968+
* @ingroup volume
1969+
* @author Phil Schatzmann
1970+
* @copyright GPLv3
1971+
*/
1972+
class AudioInputMonitor : public ModifyingStream {
1973+
public:
1974+
/**
1975+
* @brief Construct a new AudioInputMonitor with a volume limit in percent
1976+
* @param limitPercent The volume threshold as percent (default: 20)
1977+
*/
1978+
AudioInputMonitor(uint8_t limitPercent = 20) {
1979+
setLimitPercent(limitPercent);
1980+
volume_meter.addNotifyAudioChange(*this);
1981+
}
1982+
/**
1983+
* @brief Construct a new AudioInputMonitor with an input stream
1984+
* @param as Reference to an AudioStream
1985+
* @param limitPercent The volume threshold as percent (default: 20)
1986+
*/
1987+
AudioInputMonitor(AudioStream &as, uint8_t limitPercent = 20)
1988+
: AudioInputMonitor(limitPercent) {
1989+
setStream(as);
1990+
}
1991+
/**
1992+
* @brief Construct a new AudioInputMonitor with an output
1993+
* @param ao Reference to an AudioOutput
1994+
* @param limitPercent The volume threshold as percent (default: 20)
1995+
*/
1996+
AudioInputMonitor(AudioOutput &ao, uint8_t limitPercent = 20)
1997+
: AudioInputMonitor(limitPercent) {
1998+
setOutput(ao);
1999+
}
2000+
/**
2001+
* @brief Construct a new AudioInputMonitor with a Print output
2002+
* @param print Reference to a Print object
2003+
* @param limitPercent The volume threshold as percent (default: 20)
2004+
*/
2005+
AudioInputMonitor(Print &print, uint8_t limitPercent = 20)
2006+
: AudioInputMonitor(limitPercent) {
2007+
setOutput(print);
2008+
}
2009+
/**
2010+
* @brief Construct a new AudioInputMonitor with a Stream input
2011+
* @param stream Reference to a Stream object
2012+
* @param limitPercent The volume threshold as percent (default: 20)
2013+
*/
2014+
AudioInputMonitor(Stream &stream, uint8_t limitPercent = 20)
2015+
: AudioInputMonitor(limitPercent) {
2016+
setStream(stream);
2017+
}
2018+
/**
2019+
* @brief Set the volume threshold as percent
2020+
* @param percent The new volume threshold (0-100)
2021+
*/
2022+
void setLimitPercent(uint8_t percent) { limit_percent = percent; }
2023+
2024+
/**
2025+
* @brief Get the current volume threshold as percent
2026+
* @return The current volume threshold (0-100)
2027+
*/
2028+
uint8_t limitPercent() const { return limit_percent; }
2029+
2030+
/**
2031+
* @brief Begin processing with the given audio information
2032+
* @param info AudioInfo structure
2033+
* @return true if successful
2034+
*/
2035+
bool begin(AudioInfo info) {
2036+
setAudioInfo(info);
2037+
return begin();
2038+
}
2039+
2040+
/**
2041+
* @brief Begin processing with the current audio information
2042+
* @return true if successful
2043+
*/
2044+
bool begin() override {
2045+
volume_meter.begin(audioInfo());
2046+
return true;
2047+
}
2048+
2049+
/**
2050+
* @brief Set the audio information
2051+
* @param info AudioInfo structure
2052+
*/
2053+
void setAudioInfo(AudioInfo info) override {
2054+
ModifyingStream::setAudioInfo(info);
2055+
volume_meter.setAudioInfo(info);
2056+
}
2057+
2058+
/**
2059+
* @brief Write audio data to the monitor (same as VolumeMeter)
2060+
* @param data Pointer to audio data
2061+
* @param len Number of bytes
2062+
* @return Number of bytes written
2063+
*/
2064+
size_t write(const uint8_t *data, size_t len) override {
2065+
size_t result = volume_meter.write(data, len);
2066+
if (result > 0 && volume_meter.volumePercent() > limit_percent) {
2067+
time_over_last_volume_limit = millis();
2068+
}
2069+
return result;
2070+
}
2071+
2072+
/**
2073+
* @brief Read audio data from the monitor (same as VolumeMeter)
2074+
* @param data Pointer to buffer to fill
2075+
* @param len Number of bytes to read
2076+
* @return Number of bytes read
2077+
*/
2078+
size_t readBytes(uint8_t *data, size_t len) override {
2079+
size_t result = volume_meter.readBytes(data, len);
2080+
if (result > 0 && volume_meter.volumePercent() > limit_percent) {
2081+
time_over_last_volume_limit = millis();
2082+
}
2083+
return result;
2084+
}
2085+
2086+
/**
2087+
* @brief Set the output target
2088+
* @param out Reference to a Print object
2089+
*/
2090+
void setOutput(Print &out) override { volume_meter.setOutput(out); }
2091+
/**
2092+
* @brief Set the output target
2093+
* @param out Reference to a Print object
2094+
*/
2095+
void setOutput(AudioOutput &out) { volume_meter.setOutput(out); }
2096+
/**
2097+
* @brief Set the input stream
2098+
* @param io Reference to a Stream object
2099+
*/
2100+
void setStream(Stream &io) override { volume_meter.setStream(io); }
2101+
/**
2102+
* @brief Set the input stream
2103+
* @param io Reference to a Stream object
2104+
*/
2105+
void setStream(AudioStream &io) { volume_meter.setStream(io); }
2106+
2107+
/**
2108+
* @brief Access the underlying VolumeMeter
2109+
* @return Reference to the internal VolumeMeter
2110+
*/
2111+
VolumeMeter &getVolumeMeter() { return volume_meter; }
2112+
2113+
/**
2114+
* @brief Returns true if the volume exceeded the limit during the last period
2115+
* @param time_ms The period in milliseconds to check (default: 1000)
2116+
* @return true if the volume exceeded the limit during the period
2117+
*/
2118+
bool isActive(uint16_t time_ms = 1000) {
2119+
return (millis() - time_over_last_volume_limit) < time_ms;
2120+
}
2121+
2122+
protected:
2123+
VolumeMeter volume_meter; ///< Volume calculation
2124+
uint8_t limit_percent = 20; ///< Threshold percent
2125+
uint64_t time_over_last_volume_limit = 0; ///< Last over-limit time (ms)
2126+
};
2127+
19602128
#ifdef USE_TIMER
19612129
/**
19622130
* @brief TimerCallbackAudioStream Configuration

0 commit comments

Comments
 (0)