Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
audio_focus.hpp
Go to the documentation of this file.
1#pragma once
2
3// Cross-platform audio-focus registry.
4//
5// Android's AudioManager.OnAudioFocusChangeListener fires lost / duck /
6// gained / lost-transient events when another app grabs the speaker
7// (phone call, navigation prompt, another DAW). The plugin / app needs
8// to pause output on full loss, attenuate on duck, and resume on gain.
9//
10// AndroidManifest-level wiring lives in android/app/src/main/kotlin/
11// com/pulp/audio/PulpAudioFocus.kt, which forwards events over JNI
12// (core/platform/src/android/jni_bridge.cpp) into this registry.
13//
14// Listeners subscribe with an RAII token. The publish path is lock-free
15// on the hot side (atomic snapshot); subscribe/unsubscribe take a mutex
16// but neither is ever called from the audio thread.
17//
18// Shape mirrors pulp::platform::Environment so clients see a consistent
19// observer pattern across system-signal surfaces. iOS support uses
20// AVAudioSession interruption notifications when that backend is present;
21// on platforms without an OS-level audio-focus concept (desktop
22// macOS/Linux/Windows) the registry stays idle and reports `gained`.
23
24#include <atomic>
25#include <cstdint>
26#include <functional>
27#include <mutex>
28#include <vector>
29
30namespace pulp::audio {
31
41enum class AudioFocusState : std::uint8_t {
42 gained = 0,
43 duck = 1,
44 lost = 2,
46};
47
48class AudioFocusRegistry {
49public:
50 using Callback = std::function<void(AudioFocusState)>;
51
53 class Token {
54 public:
55 Token() = default;
56 explicit Token(int id) : id_(id) {}
57 Token(Token&& other) noexcept : id_(other.id_) { other.id_ = 0; }
58 Token& operator=(Token&& other) noexcept {
59 if (this != &other) {
60 reset();
61 id_ = other.id_;
62 other.id_ = 0;
63 }
64 return *this;
65 }
66 Token(const Token&) = delete;
67 Token& operator=(const Token&) = delete;
68 ~Token() { reset(); }
69 void reset() noexcept;
70 int id() const noexcept { return id_; }
71 private:
72 int id_ = 0;
73 };
74
75 static AudioFocusRegistry& instance();
76
83
88
91 AudioFocusState current() const noexcept {
92 return static_cast<AudioFocusState>(
93 current_.load(std::memory_order_acquire));
94 }
95
98
99private:
100 friend class Token;
101 void unsubscribe(int id) noexcept;
102
103 AudioFocusRegistry() = default;
104
105 mutable std::mutex mtx_;
106 int next_id_ = 1;
107 std::vector<std::pair<int, Callback>> cbs_;
108 std::atomic<std::uint8_t> current_{
109 static_cast<std::uint8_t>(AudioFocusState::gained)};
110};
111
112} // namespace pulp::audio
RAII subscription token. Move-only; destruction unsubscribes.
Definition audio_focus.hpp:53
~Token()
Definition audio_focus.hpp:68
Token & operator=(const Token &)=delete
int id() const noexcept
Definition audio_focus.hpp:70
Token & operator=(Token &&other) noexcept
Definition audio_focus.hpp:58
Token(Token &&other) noexcept
Definition audio_focus.hpp:57
Token(int id)
Definition audio_focus.hpp:56
static AudioFocusRegistry & instance()
void reset_for_test()
Reset for tests — clears all subscribers + state.
Token subscribe(Callback cb)
Subscribe a callback.
void publish(AudioFocusState state)
Publish a new state.
std::function< void(AudioFocusState)> Callback
Definition audio_focus.hpp:50
AudioFocusState current() const noexcept
Snapshot the last published state.
Definition audio_focus.hpp:91
Definition analyzer_provider.hpp:12
AudioFocusState
Audio-focus state reported to listeners.
Definition audio_focus.hpp:41
@ lost
Definition audio_focus.hpp:44
@ duck
Definition audio_focus.hpp:43
@ gained
Definition audio_focus.hpp:42
@ lost_transient
Definition audio_focus.hpp:45
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48