win-stream-audio
Version:
đ§ Stream Windows system audio to Android devices over WiFi with professional audio controls, EQ, pitch shifting, and effects
271 lines (232 loc) âĸ 9.98 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Audio Source</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background: #f0f0f0;
}
.container {
max-width: 500px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 10px;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 15px 20px;
margin: 10px 0;
border-radius: 5px;
width: 100%;
font-size: 16px;
cursor: pointer;
}
button:disabled {
background: #ccc;
}
.status {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
background: #f8f9fa;
border: 1px solid #ddd;
}
.debug {
font-size: 12px;
background: #e9ecef;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
word-wrap: break-word;
max-height: 200px;
overflow-y: auto;
}
select {
width: 100%;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<div class="container">
<h1>đĩ Simple Audio Source</h1>
<div class="status" id="status">Ready</div>
<div class="debug" id="debug">Debug: Page loaded</div>
<select id="audioSource">
<option value="">Loading audio sources...</option>
</select>
<button id="startBtn">đ¤ Start Raw Audio Streaming</button>
<button id="stopBtn" disabled>âšī¸ Stop Streaming</button>
</div>
<script>
// Get elements
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const statusDiv = document.getElementById('status');
const debugDiv = document.getElementById('debug');
const audioSourceSelect = document.getElementById('audioSource');
let websocket = null;
let mediaStream = null;
let audioContext = null;
let processor = null;
let source = null;
// Debug logging
function addDebugLog(message) {
const timestamp = new Date().toLocaleTimeString();
const logEntry = `[${timestamp}] ${message}`;
debugDiv.textContent += '\n' + logEntry;
debugDiv.scrollTop = debugDiv.scrollHeight;
}
// Load audio sources
async function loadAudioSources() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInputs = devices.filter(device => device.kind === 'audioinput');
audioSourceSelect.innerHTML = '';
audioInputs.forEach(device => {
const option = document.createElement('option');
option.value = device.deviceId;
option.textContent = device.label || `Audio Input ${device.deviceId.substring(0, 8)}`;
audioSourceSelect.appendChild(option);
});
addDebugLog(`Found ${audioInputs.length} audio sources`);
} catch (error) {
addDebugLog('Error loading audio sources: ' + error.message);
}
}
// Start streaming
startBtn.onclick = async function() {
try {
addDebugLog('đ Starting raw audio streaming...');
const selectedDeviceId = audioSourceSelect.value;
if (!selectedDeviceId) {
addDebugLog('â Please select an audio source first');
return;
}
// Get audio stream
mediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId: { exact: selectedDeviceId },
sampleRate: 44100,
channelCount: 2,
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false
}
});
addDebugLog('â
Got audio stream');
// Connect to WebSocket
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = protocol + '//' + window.location.host;
websocket = new WebSocket(wsUrl);
websocket.onopen = function() {
addDebugLog('â
WebSocket connected');
// Send role
websocket.send(JSON.stringify({
type: 'role',
role: 'source'
}));
// Set up Web Audio API for raw audio capture with optimized settings
audioContext = new (window.AudioContext || window.webkitAudioContext)();
source = audioContext.createMediaStreamSource(mediaStream);
processor = audioContext.createScriptProcessor(4096, 2, 2); // Back to 4096 for stability
processor.onaudioprocess = function(event) {
if (websocket && websocket.readyState === WebSocket.OPEN) {
// Get raw audio data
const leftChannel = event.inputBuffer.getChannelData(0);
const rightChannel = event.inputBuffer.getChannelData(1);
const length = leftChannel.length;
// Create interleaved stereo data
const audioData = new Float32Array(length * 2);
for (let i = 0; i < length; i++) {
audioData[i * 2] = leftChannel[i];
audioData[i * 2 + 1] = rightChannel[i];
}
// Send metadata occasionally for format detection
if (Math.random() < 0.05) { // Only 5% of the time to reduce overhead
const metadata = {
type: 'raw-audio',
sampleRate: audioContext.sampleRate,
channels: 2,
length: length,
timestamp: Date.now()
};
try {
websocket.send(JSON.stringify(metadata));
} catch (error) {
// Ignore metadata send errors
}
}
// Always send audio data
try {
websocket.send(audioData.buffer);
} catch (error) {
addDebugLog('â Send error: ' + error.message);
}
}
};
source.connect(processor);
processor.connect(audioContext.destination);
addDebugLog('đĩ Raw audio streaming started!');
statusDiv.textContent = 'Streaming Raw Audio';
startBtn.disabled = true;
stopBtn.disabled = false;
};
websocket.onclose = function() {
addDebugLog('â WebSocket closed');
stopStreaming();
};
websocket.onerror = function() {
addDebugLog('â WebSocket error');
stopStreaming();
};
} catch (error) {
addDebugLog('â Start error: ' + error.message);
}
};
// Stop streaming
stopBtn.onclick = function() {
stopStreaming();
};
function stopStreaming() {
addDebugLog('âšī¸ Stopping streaming...');
if (processor) {
processor.disconnect();
processor = null;
}
if (source) {
source.disconnect();
source = null;
}
if (audioContext) {
audioContext.close();
audioContext = null;
}
if (mediaStream) {
mediaStream.getTracks().forEach(track => track.stop());
mediaStream = null;
}
if (websocket) {
websocket.close();
websocket = null;
}
statusDiv.textContent = 'Stopped';
startBtn.disabled = false;
stopBtn.disabled = true;
addDebugLog('â
Streaming stopped');
}
// Load audio sources on page load
loadAudioSources();
</script>
</body>
</html>