Skip to content

Commit cfd07ee

Browse files
committed
feat: add MockAudioDevice and factory for audio simulation
1 parent 6c20d46 commit cfd07ee

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System.Collections.Generic;
2+
using System.Timers;
3+
4+
using PepperDash.Core;
5+
using PepperDash.Core.Logging;
6+
using PepperDash.Essentials.Core;
7+
using PepperDash.Essentials.Core.Config;
8+
using Serilog.Events;
9+
10+
namespace PepperDash.Essentials.Devices.Common;
11+
12+
/// <summary>
13+
/// Mock audio device for testing and simulation purposes. Behaves like a real DSP fader channel,
14+
/// with ramping volume up/down while pressed and mute on/off/toggle, all backed by fake in-memory
15+
/// state rather than any real hardware communication.
16+
/// </summary>
17+
public class MockAudioDevice : EssentialsDevice, IBasicVolumeWithFeedback
18+
{
19+
private const int VolumeHeldRepeatIntervalMs = 100;
20+
private const ushort VolumeStep = 655;
21+
22+
private ushort _volumeLevel = 32768;
23+
private bool _isMuted;
24+
private Timer _volumeUpTimer;
25+
private Timer _volumeDownTimer;
26+
27+
/// <inheritdoc />
28+
public IntFeedback VolumeLevelFeedback { get; private set; }
29+
30+
/// <inheritdoc />
31+
public BoolFeedback MuteFeedback { get; private set; }
32+
33+
/// <summary>
34+
/// Constructor for MockAudioDevice
35+
/// </summary>
36+
/// <param name="key">Device key</param>
37+
/// <param name="name">Device name</param>
38+
public MockAudioDevice(string key, string name)
39+
: base(key, name)
40+
{
41+
VolumeLevelFeedback = new IntFeedback("volume", () => _volumeLevel);
42+
MuteFeedback = new BoolFeedback("muteOn", () => _isMuted);
43+
44+
// Seed the feedback's cached value immediately so the UI reflects the starting level
45+
// rather than 0 until the first volume change fires an update.
46+
VolumeLevelFeedback.FireUpdate();
47+
MuteFeedback.FireUpdate();
48+
}
49+
50+
/// <inheritdoc />
51+
public void SetVolume(ushort level)
52+
{
53+
_volumeLevel = level;
54+
VolumeLevelFeedback.InvokeFireUpdate();
55+
this.LogDebug("SetVolume: {Level}", _volumeLevel);
56+
}
57+
58+
/// <inheritdoc />
59+
public void MuteOn()
60+
{
61+
_isMuted = true;
62+
MuteFeedback.InvokeFireUpdate();
63+
}
64+
65+
/// <inheritdoc />
66+
public void MuteOff()
67+
{
68+
_isMuted = false;
69+
MuteFeedback.InvokeFireUpdate();
70+
}
71+
72+
/// <inheritdoc />
73+
public void MuteToggle()
74+
{
75+
if (_isMuted)
76+
MuteOff();
77+
else
78+
MuteOn();
79+
}
80+
81+
/// <inheritdoc />
82+
public void VolumeUp(bool pressRelease)
83+
{
84+
if (pressRelease)
85+
{
86+
RampVolume(VolumeStep);
87+
88+
if (_volumeUpTimer == null)
89+
{
90+
_volumeUpTimer = new Timer(VolumeHeldRepeatIntervalMs) { AutoReset = true };
91+
_volumeUpTimer.Elapsed += (s, e) => RampVolume(VolumeStep);
92+
_volumeUpTimer.Start();
93+
}
94+
}
95+
else
96+
{
97+
_volumeUpTimer?.Stop();
98+
_volumeUpTimer = null;
99+
}
100+
}
101+
102+
/// <inheritdoc />
103+
public void VolumeDown(bool pressRelease)
104+
{
105+
if (pressRelease)
106+
{
107+
RampVolume(-VolumeStep);
108+
109+
if (_volumeDownTimer == null)
110+
{
111+
_volumeDownTimer = new Timer(VolumeHeldRepeatIntervalMs) { AutoReset = true };
112+
_volumeDownTimer.Elapsed += (s, e) => RampVolume(-VolumeStep);
113+
_volumeDownTimer.Start();
114+
}
115+
}
116+
else
117+
{
118+
_volumeDownTimer?.Stop();
119+
_volumeDownTimer = null;
120+
}
121+
}
122+
123+
// Clamps to ushort range so repeated ramping at the extremes doesn't wrap around.
124+
private void RampVolume(int delta)
125+
{
126+
var newLevel = _volumeLevel + delta;
127+
if (newLevel < ushort.MinValue)
128+
newLevel = ushort.MinValue;
129+
else if (newLevel > ushort.MaxValue)
130+
newLevel = ushort.MaxValue;
131+
132+
SetVolume((ushort)newLevel);
133+
}
134+
}
135+
136+
/// <summary>
137+
/// Factory for building <see cref="MockAudioDevice"/> devices.
138+
/// </summary>
139+
public class MockAudioDeviceFactory : EssentialsDeviceFactory<MockAudioDevice>
140+
{
141+
/// <summary>
142+
/// Initializes a new instance of the <see cref="MockAudioDeviceFactory"/> class.
143+
/// </summary>
144+
public MockAudioDeviceFactory()
145+
{
146+
TypeNames = new List<string> { "mockaudiodevice", "mockaudio" };
147+
}
148+
149+
/// <inheritdoc />
150+
public override EssentialsDevice BuildDevice(DeviceConfig dc)
151+
{
152+
Debug.LogMessage(LogEventLevel.Debug, "Factory attempting to create new Mock Audio Device");
153+
return new MockAudioDevice(dc.Key, dc.Name);
154+
}
155+
}

0 commit comments

Comments
 (0)