Skip to content

Commit 173967a

Browse files
committed
Add a listener holder helper class.
1 parent dc6e37c commit 173967a

File tree

15 files changed

+296
-218
lines changed

15 files changed

+296
-218
lines changed

Common/Cpp/ListenerSet.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* Listener Set
2+
*
3+
* From: https://github.com/PokemonAutomation/Arduino-Source
4+
*
5+
*/
6+
7+
#ifndef PokemonAutomation_ListenerSet_H
8+
#define PokemonAutomation_ListenerSet_H
9+
10+
#include <map>
11+
#include <mutex>
12+
13+
namespace PokemonAutomation{
14+
15+
16+
17+
template <typename ListenerType>
18+
class ListenerSet{
19+
public:
20+
size_t count_unique() const{
21+
std::lock_guard<std::mutex> lg(m_lock);
22+
return m_listeners.size();
23+
}
24+
25+
void add(ListenerType& listener){
26+
std::lock_guard<std::mutex> lg(m_lock);
27+
m_listeners[&listener]++;
28+
}
29+
void remove(ListenerType& listener){
30+
std::lock_guard<std::mutex> lg(m_lock);
31+
auto iter = m_listeners.find(&listener);
32+
if (iter == m_listeners.end()){
33+
return;
34+
}
35+
if (--iter->second == 0){
36+
m_listeners.erase(iter);
37+
}
38+
}
39+
40+
template <typename Lambda>
41+
void run_lambda_unique(Lambda&& lambda){
42+
std::lock_guard<std::mutex> lg(m_lock);
43+
for (auto& item : m_listeners){
44+
lambda(*item.first);
45+
}
46+
}
47+
template <typename Lambda>
48+
void run_lambda_with_duplicates(Lambda&& lambda){
49+
std::lock_guard<std::mutex> lg(m_lock);
50+
for (auto& item : m_listeners){
51+
ListenerType& listener = *item.first;
52+
size_t count = item.second;
53+
do{
54+
lambda(listener);
55+
}while (--count);
56+
}
57+
}
58+
59+
template <typename Function, class... Args>
60+
void run_method_unique(Function function, Args&&... args){
61+
std::lock_guard<std::mutex> lg(m_lock);
62+
for (auto& item : m_listeners){
63+
(item.first->*function)(std::forward<Args>(args)...);
64+
}
65+
}
66+
template <typename Function, class... Args>
67+
void run_method_with_duplicates(Function function, Args&&... args){
68+
std::lock_guard<std::mutex> lg(m_lock);
69+
for (auto& item : m_listeners){
70+
ListenerType& listener = *item.first;
71+
size_t count = item.second;
72+
do{
73+
(listener->*function)(std::forward<Args>(args)...);
74+
}while (--count);
75+
}
76+
}
77+
78+
private:
79+
mutable std::mutex m_lock;
80+
std::map<ListenerType*, size_t> m_listeners;
81+
};
82+
83+
84+
85+
86+
}
87+
#endif

SerialPrograms/Source/CommonFramework/AudioPipeline/AudioSession.cpp

Lines changed: 47 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ namespace PokemonAutomation{
2323

2424

2525
void AudioSession::add_state_listener(StateListener& listener){
26-
std::lock_guard<std::mutex> lg(m_lock);
27-
m_listeners.insert(&listener);
26+
m_listeners.add(listener);
2827
}
2928
void AudioSession::remove_state_listener(StateListener& listener){
30-
std::lock_guard<std::mutex> lg(m_lock);
31-
m_listeners.erase(&listener);
29+
m_listeners.remove(listener);
3230
}
3331
void AudioSession::add_stream_listener(AudioFloatStreamListener& listener){
3432
m_devices->add_listener(listener);
@@ -74,33 +72,33 @@ void AudioSession::get(AudioOption& option){
7472
option.m_display_type = m_option.m_display_type;
7573
}
7674
void AudioSession::set(const AudioOption& option){
77-
std::lock_guard<std::mutex> lg(m_lock);
78-
signal_pre_input_change();
75+
{
76+
std::lock_guard<std::mutex> lg(m_lock);
77+
signal_pre_input_change();
7978

80-
m_option.m_input_file = option.m_input_file;
81-
m_option.m_input_device = option.m_input_device;
82-
m_option.m_input_format = option.m_input_format;
83-
m_option.m_output_device = option.m_output_device;
84-
m_option.m_volume = option.m_volume;
85-
m_option.m_display_type = option.m_display_type;
79+
m_option.m_input_file = option.m_input_file;
80+
m_option.m_input_device = option.m_input_device;
81+
m_option.m_input_format = option.m_input_format;
82+
m_option.m_output_device = option.m_output_device;
83+
m_option.m_volume = option.m_volume;
84+
m_option.m_display_type = option.m_display_type;
8685

87-
if (!m_option.m_input_file.empty()){
88-
sanitize_format();
89-
m_devices->set_audio_source(m_option.m_input_file);
90-
}else if (sanitize_format()){
91-
m_devices->set_audio_source(m_option.m_input_device, m_option.m_input_format);
92-
}else{
93-
m_devices->clear_audio_source();
94-
}
86+
if (!m_option.m_input_file.empty()){
87+
sanitize_format();
88+
m_devices->set_audio_source(m_option.m_input_file);
89+
}else if (sanitize_format()){
90+
m_devices->set_audio_source(m_option.m_input_device, m_option.m_input_format);
91+
}else{
92+
m_devices->clear_audio_source();
93+
}
9594

96-
m_devices->set_audio_sink(m_option.m_output_device, m_option.m_volume);
95+
m_devices->set_audio_sink(m_option.m_output_device, m_option.m_volume);
96+
}
9797

9898
signal_post_input_change();
9999
signal_post_output_change();
100-
for (StateListener* listener : m_listeners){
101-
listener->post_volume_change(m_option.volume());
102-
listener->post_display_change(m_option.m_display_type);
103-
}
100+
m_listeners.run_method_unique(&StateListener::post_volume_change, m_option.volume());
101+
m_listeners.run_method_unique(&StateListener::post_display_change, m_option.m_display_type);
104102
}
105103
std::pair<std::string, AudioDeviceInfo> AudioSession::input_device() const{
106104
std::lock_guard<std::mutex> lg(m_lock);
@@ -193,25 +191,25 @@ void AudioSession::set_audio_output(AudioDeviceInfo info){
193191
signal_post_output_change();
194192
}
195193
void AudioSession::set_volume(double volume){
196-
std::lock_guard<std::mutex> lg(m_lock);
197-
if (m_option.m_volume == volume){
198-
return;
199-
}
200-
m_devices->set_sink_volume(volume);
201-
m_option.m_volume = volume;
202-
for (StateListener* listener : m_listeners){
203-
listener->post_volume_change(m_option.volume());
194+
{
195+
std::lock_guard<std::mutex> lg(m_lock);
196+
if (m_option.m_volume == volume){
197+
return;
198+
}
199+
m_devices->set_sink_volume(volume);
200+
m_option.m_volume = volume;
204201
}
202+
m_listeners.run_method_unique(&StateListener::post_volume_change, m_option.volume());
205203
}
206204
void AudioSession::set_display(AudioOption::AudioDisplayType display){
207-
std::lock_guard<std::mutex> lg(m_lock);
208-
if (m_option.m_display_type == display){
209-
return;
210-
}
211-
m_option.m_display_type = display;
212-
for (StateListener* listener : m_listeners){
213-
listener->post_display_change(m_option.m_display_type);
205+
{
206+
std::lock_guard<std::mutex> lg(m_lock);
207+
if (m_option.m_display_type == display){
208+
return;
209+
}
210+
m_option.m_display_type = display;
214211
}
212+
m_listeners.run_method_unique(&StateListener::post_display_change, m_option.m_display_type);
215213
}
216214

217215
bool AudioSession::sanitize_format(){
@@ -243,19 +241,18 @@ bool AudioSession::sanitize_format(){
243241
return true;
244242
}
245243
void AudioSession::signal_pre_input_change(){
246-
for (StateListener* listener : m_listeners){
247-
listener->pre_input_change();
248-
}
244+
m_listeners.run_method_unique(&StateListener::pre_input_change);
249245
}
250246
void AudioSession::signal_post_input_change(){
251-
for (StateListener* listener : m_listeners){
252-
listener->post_input_change(m_option.input_file(), m_option.input_device(), m_option.input_format());
253-
}
247+
m_listeners.run_method_unique(
248+
&StateListener::post_input_change,
249+
m_option.input_file(),
250+
m_option.input_device(),
251+
m_option.input_format()
252+
);
254253
}
255254
void AudioSession::signal_post_output_change(){
256-
for (StateListener* listener : m_listeners){
257-
listener->post_output_change(m_option.output_device());
258-
}
255+
m_listeners.run_method_unique(&StateListener::post_output_change, m_option.output_device());
259256
}
260257

261258

@@ -290,7 +287,7 @@ void AudioSession::add_overlay(uint64_t starting_seqnum, size_t end_seqnum, Colo
290287
}
291288

292289

293-
void AudioSession::on_fft(size_t sample_rate, std::shared_ptr<AlignedVector<float>> fft_output){
290+
void AudioSession::on_fft(size_t sample_rate, std::shared_ptr<const AlignedVector<float>> fft_output){
294291
m_spectrum_holder.push_spectrum(sample_rate, std::move(fft_output));
295292
global_watchdog().delay(*this);
296293
}

SerialPrograms/Source/CommonFramework/AudioPipeline/AudioSession.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#ifndef PokemonAutomation_AudioPipeline_AudioSession_H
1717
#define PokemonAutomation_AudioPipeline_AudioSession_H
1818

19+
#include "Common/Cpp/ListenerSet.h"
1920
#include "Common/Cpp/Concurrency/Watchdog.h"
2021
#include "AudioFeed.h"
2122
#include "AudioPassthroughPair.h"
@@ -82,7 +83,7 @@ class AudioSession final : public AudioFeed, private FFTListener, private Watchd
8283

8384

8485
private:
85-
virtual void on_fft(size_t sample_rate, std::shared_ptr<AlignedVector<float>> fft_output) override;
86+
virtual void on_fft(size_t sample_rate, std::shared_ptr<const AlignedVector<float>> fft_output) override;
8687
virtual void on_watchdog_timeout() override;
8788

8889
bool sanitize_format();
@@ -99,7 +100,9 @@ class AudioSession final : public AudioFeed, private FFTListener, private Watchd
99100
std::unique_ptr<AudioPassthroughPair> m_devices;
100101

101102
mutable std::mutex m_lock;
102-
std::set<StateListener*> m_listeners;
103+
104+
ListenerSet<StateListener> m_listeners;
105+
103106
};
104107

105108

SerialPrograms/Source/CommonFramework/AudioPipeline/AudioStream.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ void AudioFloatToStream::add_listener(StreamListener& listener){
141141
if (listener.object_size != m_frame_size){
142142
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Mismatching frame size.");
143143
}
144-
m_listeners.insert(&listener);
144+
m_listeners.add(listener);
145145
}
146146
void AudioFloatToStream::remove_listener(StreamListener& listener){
147147
auto scope_check = m_sanitizer.check_scope();
148-
m_listeners.erase(&listener);
148+
m_listeners.remove(listener);
149149
}
150150

151151
AudioFloatToStream::AudioFloatToStream(AudioSampleFormat output_format, size_t samples_per_frame)
@@ -171,9 +171,10 @@ void AudioFloatToStream::on_samples(const float* data, size_t frames){
171171
return;
172172
}
173173
if (m_format == AudioSampleFormat::FLOAT32){
174-
for (StreamListener* listener : m_listeners){
175-
listener->on_objects(data, frames);
176-
}
174+
m_listeners.run_method_unique(
175+
&StreamListener::on_objects,
176+
data, frames
177+
);
177178
return;
178179
}
179180

@@ -195,9 +196,10 @@ void AudioFloatToStream::on_samples(const float* data, size_t frames){
195196
default:
196197
return;
197198
}
198-
for (StreamListener* listener : m_listeners){
199-
listener->on_objects(m_buffer.data(), block);
200-
}
199+
m_listeners.run_method_unique(
200+
&StreamListener::on_objects,
201+
m_buffer.data(), block
202+
);
201203
data = (const float*)((const char*)data + block * m_frame_size);
202204
frames -= block;
203205
}

SerialPrograms/Source/CommonFramework/AudioPipeline/AudioStream.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#ifndef PokemonAutomation_AudioPipeline_AudioSourceReader_H
88
#define PokemonAutomation_AudioPipeline_AudioSourceReader_H
99

10+
#include "Common/Cpp/ListenerSet.h"
1011
#include "Common/Cpp/LifetimeSanitizer.h"
11-
#include "Common/Cpp/Time.h"
1212
#include "Common/Cpp/StreamConverters.h"
1313
#include "Common/Cpp/Containers/AlignedVector.h"
1414
#include "AudioInfo.h"
@@ -81,7 +81,7 @@ class AudioFloatToStream : public AudioFloatStreamListener{
8181
size_t m_frame_size;
8282
size_t m_buffer_size;
8383
AlignedVector<char> m_buffer;
84-
std::set<StreamListener*> m_listeners;
84+
ListenerSet<StreamListener> m_listeners;
8585

8686
LifetimeSanitizer m_sanitizer;
8787
};

0 commit comments

Comments
 (0)