This commit is contained in:
2025-07-10 15:47:38 +01:00
commit b19d93a237
137 changed files with 1426 additions and 0 deletions
Binary file not shown.
@@ -0,0 +1,45 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}|Dante.pyproj|C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\Dante.py||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}|Dante.pyproj|solutionrelative:Dante.py||{8B382828-6202-11D1-8870-0000F87579D2}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 209,
"SelectedChildIndex": 2,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{e506b91c-c606-466a-90a9-123d1d1e12b3}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Dante.py",
"DocumentMoniker": "C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\Dante.py",
"RelativeDocumentMoniker": "Dante.py",
"ToolTip": "C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\Dante.py",
"RelativeToolTip": "Dante.py",
"ViewState": "AgIAAAIAAAAAAAAAAAAowBAAAABSAAAAAAAAAA==",
"Icon": "00000000-0000-0000-0000-000000000000.000000|iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHfSURBVDhPpZCxaxNhGMZ/9zVJaUuDUlq1FBWLZBFTCXo4BFyU1qGKi4OkIHhkzF/g0KF00M2lR9YUdDDQQVAcJILoQUd1UNRmkhoRm8RL777v7nOoCZd4AcEH3uF5nvd73vd74X\u002BgtTai3LIsHeWDEEJct217a1DvwbIs7bpubFUqFW3b9naxWLwWfSOipAvHcfqqi0KhkMtms3cty1rsaomeG4FpmoMSANVqlXQ6nQMWgacMC4hOBchduEj\u002ByjKjScGh8SS1Wq3nxQZEN3j1eY/Vrfe8rTeQvsf89ARLS1cz5XIZ/uUGXxptQiVRUhJIybudXaZn52a7vbEBpmkyNb/A69YM9W8tDEJOTI2hpI\u002BSkvOZH2f13sNAB\u002B07sV9wHIdH9Unquz9RvoeUPtL3UFISKkky\u002BAT7HwTi6FpsgGma3N9\u002Bg5I\u002BKaFZyU8wOpJChwHnjiuE9wKCXyC/zsQGOI7DeELjtiUPbgky6Wd/HA3KBelC4AIpNfQGl88cQ0mfI5PugRhKkM2DUk0INYwtPI/dAGAlf4rbl06T6DyBVgeCABJzYPhwcm0V8ICNvwJKpVIf37h3IxSyKVAuBMDIYQAMw1jvaxwGrYNl3Xq5o79vat3Y3Nedj4\u002B11je7/m\u002BXOuyzFtDhUgAAAABJRU5ErkJggg==",
"WhenOpened": "2025-02-14T16:33:07.888Z",
"EditorCaption": ""
}
]
}
]
}
]
}
+41
View File
@@ -0,0 +1,41 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}|Dante.pyproj|C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\Dante.py||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}|Dante.pyproj|solutionrelative:Dante.py||{8B382828-6202-11D1-8870-0000F87579D2}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 209,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{e506b91c-c606-466a-90a9-123d1d1e12b3}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Dante.py",
"DocumentMoniker": "C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\Dante.py",
"RelativeDocumentMoniker": "Dante.py",
"ToolTip": "C:\\Users\\William\\Documents\\HawkEye\\Dante Tones\\Dante\\Dante.py",
"RelativeToolTip": "Dante.py",
"ViewState": "AgIAAAIAAAAAAAAAAAAowBAAAABSAAAAAAAAAA==",
"Icon": "00000000-0000-0000-0000-000000000000.000000|iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd\u002BUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAUqSURBVEhLvZbRb1RVHsc/5947l3ZaZ6YtbUkVOwxMmlpWhGhCspIASVfTZPvgCxsxGn0xGUkMpvKyf8AmbLJRnzS8sESiWfABE6xEgmsq6mIt6KYpBYemFtthWqaUTtuZe\u002B85v30YatrLOBgf9pt8c3N/55zfJ7/fvefcC/9PiYgKxwCi0ejDjuNEw/EHSAO5u3fvLocH1qoqsK\u002Bv7/zevXv3O44THqqqfD5PsVic9Tzv7\u002BfOnXtvcnJyMTxnVb8KPHbs2P5EIhEeqqqzZ8\u002BysLBwJ5/P356YmDg\u002BNDT01vj4\u002BFJ4HoAVDvxebd26NdHf39\u002BcTCYP7t69e6Cnp6chPAfADgcA0un0i/39/Vtc1\u002BXq1avMzc3d50KhQHNzM0oprl\u002B/TjQapaenpz6VSm2Ynp7e6Lpuu9b60uzsrL829wMrTCaTVd3Z2YlS9z\u002BRTZs2PXTw4MGuVCrVl0gkMuHxmkClFK7r/qpXZVkWp06d4siRIwwMDHD06NGGXC63C0itSwjUfA1FhNHR0XAYANu26e7uRilFb28v\u002B/btWzeezWY5fPjwuhi/pcLt27dXdXd3N7ZtY1B4YlEWB48IOC7RaHRdB9aqJhBgZmamqktlj7HcMh9fzvHBNz9x4ssJTgxlef/LLJ9euYknNi0tLeF0tYEiguM4VR0oh9PDU0zNLzF8Y47PfrjJ4Mgkp7/6kffO/Zeh7AKP79gRTlkbqJSira2tqsvGYqawTGpjA/E6B6M1YgKM1iwUVxifWeSFl16xDxw4sG7r1QSKCCMjI1Xta0NufpETX1xjbOo2Wmt0YDBao41GB5q29k1WR0fHOuD9G2nN0RaPxzHGwL1qA6PI3i4xM7\u002BMrw0rZR9jNMYIN3J3uDg6xd3lEkZrHn80xj9e7iIWKYDTfhu3828bWzvefiCwWCziOA6\u002Bcvnw25/5fDRH2Qsw2icIKi3U2scEGq0DdKAxQcCOznreeUloMN\u002BBWBA7oIk\u002B2ffAlt66dYtCocDoVIEfJucpez7G\u002BGgdVKrTq9DK1egAIwG2Mth4YDToZSh\u002BYQOv1gQqpUilUmzevJlFX1FYXKkkDHQl\u002BWqFq/daY4wmXu\u002BypzuKa26CLlVcugqin67Z0lgsRjabJR6P8/W04fi/r6O1jw4ClAi7kg\u002BxJw0b7DIics\u002BGeN0Sf2idJGZNgCmDeKA9SP5TalZI5TAmkUhgK1AYTGAw2pBur\u002BPN3jx96a/o3fotf9o2zDPp73h22yX\u002B\u002BPAwMSsLegXMvQp1GVRE1QQqpWhsbKSuro6maIRG10JLpW19O1tojVzGYQFLFrHMAlYwhxXMgj8fgpUg0gEqcq0mUES4cuUKY2NjtNYLyZYGlA7QOqApalAYEKm8GMGa5LpUAepSJW40NO41KPV\u002BTaBlWezcuZOuri66O9t5bvcW9jzWwa5UK/GGDSAGjA9mBRBQDaBiYMXAaYUNXVC/HeLPlmh6ZuTChQvv1vw8rZXWml1bWnhqWxsAjuRgtlwBag\u002BsOrCbwIpU4M5GaHnhGnbDxcHBwdzk5L9OZzKZuZoVhmWMwfd9fN\u002BvnIprW\u002BjloTgMlgtOK7L0PSLBxTNnzrx26NChv2YymcvU\u002BgCfPHmSWr\u002BJz//lz7TZreDfAOOB9kHKUJ6DCBjnkfmzn1wYfeONgdLExMQv66ruw9/yIzw7m38iYm6\u002By53BJoqXwJsGDLhpJL5fTOO\u002B/xw7/tHLmUxmPLz2d6m5uVmJSFpE3haRH0XEE5ElEflaRF4/f/58LLwG4H9\u002BJsjcTp8b4QAAAABJRU5ErkJggg==",
"WhenOpened": "2025-02-14T16:33:07.888Z",
"EditorCaption": ""
}
]
}
]
}
]
}
+121
View File
@@ -0,0 +1,121 @@
import sounddevice as sd
import numpy as np
import threading
import tkinter as tk
from tkinter import ttk
import re
playing_threads = {}
def list_dante_devices():
"""List all unique Dante Virtual Soundcard output devices sorted by channel order."""
devices = sd.query_devices()
seen = {}
for d in devices:
if "DVS Transmit" in d['name'] and d['max_output_channels'] == 2:
match = re.search(r'(\d+)-', d['name']) # Extract first number before '-'
if match:
channel_number = int(match.group(1))
if channel_number not in seen:
seen[channel_number] = d
return [seen[key] for key in sorted(seen.keys())]
def generate_tone(frequency=440, sample_rate=48000):
"""Generate a continuous sine wave tone."""
t = np.linspace(0, 1, sample_rate, False)
wave = 0.5 * np.sin(2 * np.pi * frequency * t)
return wave.astype(np.float32)
def play_sound_on_device(device_index, channel, frequency=440, sample_rate=48000):
"""Continuously play a sine wave on a specific stereo Dante Virtual Soundcard channel using OutputStream."""
if channel not in [1, 2]:
print("Invalid channel. Must be 1 (Left) or 2 (Right).")
return
tone = generate_tone(frequency, sample_rate)
output = np.zeros((len(tone), 2), dtype=np.float32) # Stereo output
output[:, channel - 1] = tone # Assign tone to the selected channel
def callback(outdata, frames, time, status):
if status:
print(status)
outdata[:frames] = output[:frames]
stream = sd.OutputStream(device=device_index, samplerate=sample_rate, channels=2, callback=callback)
playing_threads[(device_index, channel)] = stream
stream.start()
def stop_sound(device_index, channel):
"""Stop playing sound on a given device and channel."""
stream = playing_threads.pop((device_index, channel), None)
if stream:
stream.stop()
stream.close()
def stop_all_sounds():
"""Stop all currently playing sounds."""
for key in list(playing_threads.keys()):
stop_sound(*key)
def play_all_sounds(frequency):
"""Play sound on all available Dante channels."""
devices = list_dante_devices()
for device in devices:
for channel in [1, 2]:
play_sound_on_device(device['index'], channel, frequency)
def start_gui():
devices = list_dante_devices()
root = tk.Tk()
root.title("Dante Virtual Soundcard Player")
frame = ttk.Frame(root)
frame.pack(padx=10, pady=10)
ttk.Label(frame, text="Frequency (Hz):").grid(row=0, column=1)
frequency_entry = ttk.Entry(frame)
frequency_entry.grid(row=0, column=2)
frequency_entry.insert(0, "440")
def toggle_all_play():
freq = float(frequency_entry.get())
play_all_sounds(freq)
def toggle_all_stop():
stop_all_sounds()
all_on_btn = ttk.Button(frame, text="All ON", command=toggle_all_play)
all_on_btn.grid(row=1, column=3, padx=5)
all_off_btn = ttk.Button(frame, text="All OFF", command=toggle_all_stop)
all_off_btn.grid(row=1, column=4, padx=5)
buttons = []
for idx, device in enumerate(devices):
device_name = device['name']
device_index = device['index']
ttk.Label(frame, text=device_name).grid(row=idx + 2, column=0, sticky="w")
for channel in [1, 2]:
btn = ttk.Button(frame, text=f"Ch {channel} ON", width=10)
btn.grid(row=idx + 2, column=channel, padx=5)
def toggle_play(dev_idx=device_index, ch=channel, button=btn):
if (dev_idx, ch) in playing_threads:
stop_sound(dev_idx, ch)
button.config(text=f"Ch {ch} ON")
else:
freq = float(frequency_entry.get())
play_sound_on_device(dev_idx, ch, freq)
button.config(text=f"Ch {ch} OFF")
btn.config(command=toggle_play)
buttons.append(btn)
root.mainloop()
if __name__ == "__main__":
start_gui()
+35
View File
@@ -0,0 +1,35 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>5e5ba753-acea-4dc7-912d-3cfcf80d955c</ProjectGuid>
<ProjectHome>.</ProjectHome>
<StartupFile>Dante.py</StartupFile>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<Name>Dante</Name>
<RootNamespace>Dante</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<ItemGroup>
<Compile Include="Dante.py" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
<!-- Uncomment the CoreCompile target to enable the Build command in
Visual Studio and specify your pre- and post-build commands in
the BeforeBuild and AfterBuild targets below. -->
<!--<Target Name="CoreCompile" />-->
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
</Project>
+23
View File
@@ -0,0 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35806.99 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Dante", "Dante.pyproj", "{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E5BA753-ACEA-4DC7-912D-3CFCF80D955C}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1D410C2F-2713-4E1C-AADF-650AB25B7904}
EndGlobalSection
EndGlobal