Add support for per channel audio control

pull/1/head
firealarmss 11 months ago
parent 3ad8b9b867
commit 2ccd34690f

@ -0,0 +1,4 @@
[*.cs]
# CS4014: Because this call is not awaited, execution of the current method continues before the call is completed
dotnet_diagnostic.CS4014.severity = none

@ -7,6 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WhackerLinkLib", "WhackerLi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WhackerLinkConsoleV2", "WhackerLinkConsoleV2\WhackerLinkConsoleV2.csproj", "{710D1FA8-2E0D-42CB-9174-FCD9EB7A718F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B5A7CF60-CCDE-4B2B-85C1-86AE3A19FB31}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

@ -21,7 +21,6 @@
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using System.Collections.Generic;
using System.Diagnostics;
namespace WhackerLinkConsoleV2
{
@ -29,7 +28,8 @@ namespace WhackerLinkConsoleV2
{
private WaveOutEvent _waveOut;
private MixingSampleProvider _mixer;
private Dictionary<string, BufferedWaveProvider> _talkgroupProviders;
private Dictionary<string, (BufferedWaveProvider buffer, VolumeSampleProvider volumeProvider)> _talkgroupProviders;
/// <summary>
/// Creates an instance of <see cref="AudioManager"/>
@ -37,7 +37,7 @@ namespace WhackerLinkConsoleV2
public AudioManager()
{
_waveOut = new WaveOutEvent();
_talkgroupProviders = new Dictionary<string, BufferedWaveProvider>();
_talkgroupProviders = new Dictionary<string, (BufferedWaveProvider, VolumeSampleProvider)>();
_mixer = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(8000, 1))
{
ReadFully = true
@ -55,17 +55,43 @@ namespace WhackerLinkConsoleV2
public void AddTalkgroupStream(string talkgroupId, byte[] audioData)
{
if (!_talkgroupProviders.ContainsKey(talkgroupId))
AddTalkgroupStream(talkgroupId);
_talkgroupProviders[talkgroupId].buffer.AddSamples(audioData, 0, audioData.Length);
}
/// <summary>
/// Internal helper to create a talkgroup stream
/// </summary>
/// <param name="talkgroupId"></param>
private void AddTalkgroupStream(string talkgroupId)
{
var provider = new BufferedWaveProvider(new WaveFormat(8000, 16, 1))
var bufferProvider = new BufferedWaveProvider(new WaveFormat(8000, 16, 1))
{
DiscardOnBufferOverflow = true
};
_talkgroupProviders[talkgroupId] = provider;
_mixer.AddMixerInput(provider.ToSampleProvider());
var volumeProvider = new VolumeSampleProvider(bufferProvider.ToSampleProvider())
{
Volume = 1.0f
};
_talkgroupProviders[talkgroupId] = (bufferProvider, volumeProvider);
_mixer.AddMixerInput(volumeProvider);
}
_talkgroupProviders[talkgroupId].AddSamples(audioData, 0, audioData.Length);
/// <summary>
/// Adjusts the volume of a specific talkgroup stream
/// </summary>
public void SetTalkgroupVolume(string talkgroupId, float volume)
{
if (_talkgroupProviders.ContainsKey(talkgroupId))
_talkgroupProviders[talkgroupId].volumeProvider.Volume = volume;
else
{
AddTalkgroupStream(talkgroupId);
_talkgroupProviders[talkgroupId].volumeProvider.Volume = volume;
}
}
/// <summary>

@ -1,7 +1,7 @@
<UserControl x:Class="WhackerLinkConsoleV2.Controls.ChannelBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="200" Height="120" Background="{Binding Background, RelativeSource={RelativeSource AncestorType=UserControl}}"
Width="200" Height="150" Background="{Binding Background, RelativeSource={RelativeSource AncestorType=UserControl}}"
BorderBrush="Gray" BorderThickness="2">
<Border CornerRadius="5" Background="{Binding Background, RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid Margin="5">
@ -10,6 +10,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding ChannelName}" FontWeight="Bold" Foreground="White" FontSize="14" Grid.Row="0"/>
@ -20,6 +21,9 @@
HorizontalAlignment="Left" Grid.Row="3" Click="PTTButton_Click"/>
<Button Content="Page Select" Width="80" Margin="80,5,0,0" Background="Green" Foreground="White" Name="PageSelectButton"
HorizontalAlignment="Left" Grid.Row="3" Click="PageSelectButton_Click"/>
<Slider Grid.Row="4" Minimum="0" Maximum="1" Value="{Binding Volume, Mode=TwoWay}"
Width="180" Margin="5" Name="VolumeSlider" ValueChanged="VolumeSlider_ValueChanged"/>
</Grid>
</Border>
</UserControl>

@ -29,11 +29,14 @@ namespace WhackerLinkConsoleV2.Controls
public partial class ChannelBox : UserControl, INotifyPropertyChanged
{
private readonly SelectedChannelsManager _selectedChannelsManager;
private readonly AudioManager _audioManager;
private bool _pttState;
private bool _pageState;
private bool _holdState;
private bool _emergency;
private string _lastSrcId = "0";
private double _volume = 1.0;
public FlashingBackgroundManager _flashingBackgroundManager;
@ -45,6 +48,7 @@ namespace WhackerLinkConsoleV2.Controls
public string ChannelName { get; set; }
public string SystemName { get; set; }
public string DstId { get; set; }
public string LastSrcId
{
@ -121,13 +125,29 @@ namespace WhackerLinkConsoleV2.Controls
}
}
public ChannelBox(SelectedChannelsManager selectedChannelsManager, string channelName, string systemName)
public double Volume
{
get => _volume;
set
{
if (_volume != value)
{
_volume = value;
OnPropertyChanged(nameof(Volume));
_audioManager.SetTalkgroupVolume(DstId, (float)value);
}
}
}
public ChannelBox(SelectedChannelsManager selectedChannelsManager, AudioManager audioManager, string channelName, string systemName, string dstId)
{
InitializeComponent();
DataContext = this;
_selectedChannelsManager = selectedChannelsManager;
_audioManager = audioManager;
_flashingBackgroundManager = new FlashingBackgroundManager(this);
ChannelName = channelName;
DstId = dstId;
SystemName = $"System: {systemName}";
LastSrcId = $"Last SRC: {LastSrcId}";
UpdateBackground();
@ -210,6 +230,11 @@ namespace WhackerLinkConsoleV2.Controls
HoldChannelButtonClicked.Invoke(sender, this);
}
private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Volume = e.NewValue;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

@ -247,7 +247,7 @@ namespace WhackerLinkConsoleV2
{
foreach (var channel in zone.Channels)
{
var channelBox = new ChannelBox(_selectedChannelsManager, channel.Name, channel.System);
var channelBox = new ChannelBox(_selectedChannelsManager, _audioManager, channel.Name, channel.System, channel.Tgid);
if (_settingsManager.ChannelPositions.TryGetValue(channel.Name, out var position))
{
@ -269,10 +269,11 @@ namespace WhackerLinkConsoleV2
ChannelsCanvas.Children.Add(channelBox);
offsetX += 220;
if (offsetX + 200 > ChannelsCanvas.ActualWidth)
{
offsetX = 20;
offsetY += 140;
offsetY += 170;
}
}
}
@ -425,7 +426,7 @@ namespace WhackerLinkConsoleV2
}
}
private void ManualPage_Click(object sender, RoutedEventArgs e)
private async void ManualPage_Click(object sender, RoutedEventArgs e)
{
QuickCallPage pageWindow = new QuickCallPage();
pageWindow.Owner = this;
@ -441,8 +442,11 @@ namespace WhackerLinkConsoleV2
{
ToneGenerator generator = new ToneGenerator();
byte[] toneA = generator.GenerateTone(Double.Parse(pageWindow.ToneA), 1.0);
byte[] toneB = generator.GenerateTone(Double.Parse(pageWindow.ToneB), 3.0);
double toneADuration = 1.0;
double toneBDuration = 3.0;
byte[] toneA = generator.GenerateTone(Double.Parse(pageWindow.ToneA), toneADuration);
byte[] toneB = generator.GenerateTone(Double.Parse(pageWindow.ToneB), toneBDuration);
byte[] combinedAudio = new byte[toneA.Length + toneB.Length];
Buffer.BlockCopy(toneA, 0, combinedAudio, 0, toneA.Length);
@ -457,12 +461,14 @@ namespace WhackerLinkConsoleV2
_audioManager.AddTalkgroupStream(cpgChannel.Tgid, combinedAudio);
});
await Task.Run(async () =>
{
for (int i = 0; i < totalChunks; i++)
{
int offset = i * chunkSize;
int size = Math.Min(chunkSize, combinedAudio.Length - offset);
byte[] chunk = new byte[size];
byte[] chunk = new byte[chunkSize];
Buffer.BlockCopy(combinedAudio, offset, chunk, 0, size);
AudioPacket voicePacket = new AudioPacket
@ -476,11 +482,18 @@ namespace WhackerLinkConsoleV2
Site = system.Site
},
Site = system.Site,
LopServerVocode = true
LopServerVocode = false
};
Console.WriteLine("sending sample");
handler.SendMessage(voicePacket.GetData());
await Task.Delay(20);
}
});
double totalDurationMs = (toneADuration + toneBDuration) * 1000 + 500;
await Task.Delay((int)totalDurationMs);
GRP_VCH_RLS release = new GRP_VCH_RLS
{
@ -491,6 +504,7 @@ namespace WhackerLinkConsoleV2
};
handler.SendMessage(release.GetData());
Dispatcher.Invoke(() =>
{
channel.PageSelectButton.Background = Brushes.Green;
@ -568,7 +582,7 @@ namespace WhackerLinkConsoleV2
Site = system.Site
},
Site = system.Site,
LopServerVocode = true
LopServerVocode = false
};
handler.SendMessage(voicePacket.GetData());

@ -0,0 +1,47 @@
radioWide:
hostVersion: R01.00.00
codeplugVersion: R01.06.00
baseMode: FIVEMRADIO
radioAlias: ''
serialNumber: 123ABC1234
model: CONSOLE
inCarMode: N/A
systems:
- name: System 1
address: localhost
rid: 12345
port: 3000
site:
name: Central Site
controlChannel: 772.74375
voiceChannels: []
location:
x: 757.89
y: 1274.17
z: 360.3
siteID: 1
systemID: 1
range: 1.5
zones:
- name: Zone 1
channels:
- name: Channel 1
system: System 1
tgid: 2001
- name: Channel 2
system: System 1
tgid: 15002
- name: Channel 3
system: System 1
tgid: 15003
- name: Zone 2
channels:
- name: Channel A
system: System 1
tgid: 16001
- name: Channel B
system: System 1
tgid: 16002
- name: Channel C
system: System 1
tgid: 16002
Loading…
Cancel
Save

Powered by TurnKey Linux.