Files
room_glitch/labs/glitch_odin/gm/lib/teddy_audio_web.dart
T

63 lines
2.5 KiB
Dart

// ignore_for_file: deprecated_member_use
import 'dart:html' as html;
import 'dart:convert';
import 'dart:typed_data';
import 'dart:math' as math;
class TeddyAudio {
final html.AudioElement _audio = html.AudioElement();
bool enabled = false;
String status = 'Audio disabled on this device';
String? _url;
int _generation = 0;
TeddyAudio() {
_audio.onEnded.listen((_) { status = 'Cue finished'; });
_audio.onError.listen((_) { status = 'Browser audio error ${_audio.error?.code}. Use Play current hint here to retry.'; });
}
Future<void> enable() async {
// Unlock this exact media element in the user's tap handler (required on phones).
final data = ByteData(16044);
void text(int offset, String value) { for (var i=0;i<value.length;i++) { data.setUint8(offset+i,value.codeUnitAt(i)); } }
text(0,'RIFF'); data.setUint32(4,16036,Endian.little); text(8,'WAVEfmt ');
data.setUint32(16,16,Endian.little); data.setUint16(20,1,Endian.little);
data.setUint16(22,1,Endian.little); data.setUint32(24,8000,Endian.little);
data.setUint32(28,16000,Endian.little); data.setUint16(32,2,Endian.little);
data.setUint16(34,16,Endian.little); text(36,'data'); data.setUint32(40,16000,Endian.little);
for (var i=0;i<8000;i++) { data.setInt16(44+i*2,(4000*math.sin(2*math.pi*440*i/8000)).round(),Endian.little); }
_audio.src = 'data:audio/wav;base64,${base64Encode(data.buffer.asUint8List())}';
try {
await _audio.play();
enabled = true;
_url = null;
status = 'Speaker test playing (one-second tone)';
} catch (_) {
enabled = false;
status = 'Browser blocked audio. Tap Enable audio again.';
}
}
void playNow(String url) { enabled = true; sync(url, force:true); }
void sync(String? url, {bool force=false}) {
if (!enabled || (!force && url == _url)) return;
_generation++;
final generation = _generation;
_audio.pause();
_url = url;
if (url == null) { status = 'Waiting for Teddy'; return; }
_audio.src = url;
status = 'Loading Teddy audio';
_audio.play().then((_) {
if (generation == _generation) status = 'Playing on this device';
}).catchError((Object error) {
if (generation == _generation) status = 'Playback blocked or unavailable. Tap Play current hint here.';
});
}
void disable() {
_generation++;
enabled = false;
_url = null;
_audio.pause();
status = 'Audio disabled on this device';
}
void dispose() { disable(); _audio.src = ''; }
}