config.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # -*- coding: utf-8 -*-
  2. """
  3. Stream-Konfiguration.
  4. Einstellungen für Audio-Streaming.
  5. """
  6. from dataclasses import dataclass, field
  7. from typing import Any
  8. @dataclass
  9. class StreamConfig:
  10. """
  11. Konfiguration für Audio-Streaming.
  12. Definiert Audio-Format, Puffergrößen und Streaming-Verhalten.
  13. """
  14. # Audio-Format
  15. sample_rate: int = 44100 # Hz (44.1kHz - Standard für die meisten Geräte)
  16. channels: int = 2 # Stereo
  17. bit_depth: int = 16 # 16-bit
  18. # Pufferung
  19. buffer_size_ms: int = 100 # Puffergröße in Millisekunden
  20. prebuffer_ms: int = 500 # Vor-Pufferung bevor Start
  21. max_buffer_ms: int = 5000 # Maximale Puffergröße
  22. # Streaming
  23. chunk_size: int = 4096 # Bytes pro Chunk
  24. send_interval_ms: int = 20 # Intervall zwischen Paketen
  25. # Synchronisation
  26. sync_enabled: bool = True # Zeit-Synchronisation
  27. sync_interval_ms: int = 1000 # Sync-Intervall
  28. max_drift_ms: int = 50 # Maximale erlaubte Drift
  29. # Fehlerbehandlung
  30. retry_count: int = 3 # Wiederholungsversuche
  31. retry_delay_ms: int = 100 # Verzögerung zwischen Versuchen
  32. disconnect_timeout_ms: int = 5000 # Timeout für Disconnect
  33. # Kompression (optional)
  34. compression_enabled: bool = False
  35. compression_level: int = 6 # 0-9 (0=keine, 9=max)
  36. # Metadaten
  37. send_metadata: bool = True # Track-Info mitsenden
  38. metadata_interval_ms: int = 5000 # Metadaten-Intervall
  39. # Zusätzliche Einstellungen
  40. extra: dict[str, Any] = field(default_factory=dict)
  41. @property
  42. def bytes_per_sample(self) -> int:
  43. """Bytes pro Sample."""
  44. return self.bit_depth // 8
  45. @property
  46. def bytes_per_frame(self) -> int:
  47. """Bytes pro Frame (alle Kanäle)."""
  48. return self.bytes_per_sample * self.channels
  49. @property
  50. def bytes_per_second(self) -> int:
  51. """Bytes pro Sekunde."""
  52. return self.sample_rate * self.bytes_per_frame
  53. @property
  54. def buffer_size_bytes(self) -> int:
  55. """Puffergröße in Bytes."""
  56. return int(self.bytes_per_second * self.buffer_size_ms / 1000)
  57. @property
  58. def prebuffer_bytes(self) -> int:
  59. """Vor-Puffer in Bytes."""
  60. return int(self.bytes_per_second * self.prebuffer_ms / 1000)
  61. def validate(self) -> list[str]:
  62. """
  63. Validiert die Konfiguration.
  64. Returns:
  65. Liste von Fehlermeldungen (leer = valide)
  66. """
  67. errors = []
  68. if self.sample_rate not in (8000, 11025, 16000, 22050, 44100, 48000, 96000):
  69. errors.append(f"Ungültige Sample-Rate: {self.sample_rate}")
  70. if self.channels not in (1, 2):
  71. errors.append(f"Ungültige Kanalzahl: {self.channels}")
  72. if self.bit_depth not in (8, 16, 24, 32):
  73. errors.append(f"Ungültige Bit-Tiefe: {self.bit_depth}")
  74. if self.buffer_size_ms < 10:
  75. errors.append("Buffer zu klein (min 10ms)")
  76. if self.prebuffer_ms > self.max_buffer_ms:
  77. errors.append("Prebuffer größer als max_buffer")
  78. return errors
  79. @classmethod
  80. def for_voice(cls) -> "StreamConfig":
  81. """Konfiguration für Sprach-Audio (16kHz mono)."""
  82. return cls(
  83. sample_rate=16000,
  84. channels=1,
  85. bit_depth=16,
  86. buffer_size_ms=50,
  87. prebuffer_ms=200,
  88. )
  89. @classmethod
  90. def for_music(cls) -> "StreamConfig":
  91. """Konfiguration für Musik (48kHz stereo für beste Kompatibilität)."""
  92. return cls(
  93. sample_rate=48000,
  94. channels=2,
  95. bit_depth=16,
  96. buffer_size_ms=100,
  97. prebuffer_ms=500,
  98. )
  99. @classmethod
  100. def for_low_latency(cls) -> "StreamConfig":
  101. """Konfiguration für geringe Latenz."""
  102. return cls(
  103. sample_rate=48000,
  104. channels=2,
  105. bit_depth=16,
  106. buffer_size_ms=20,
  107. prebuffer_ms=100,
  108. max_buffer_ms=1000,
  109. send_interval_ms=10,
  110. )
  111. def to_dict(self) -> dict[str, Any]:
  112. """Konvertiert zu Dictionary."""
  113. return {
  114. "sample_rate": self.sample_rate,
  115. "channels": self.channels,
  116. "bit_depth": self.bit_depth,
  117. "buffer_size_ms": self.buffer_size_ms,
  118. "prebuffer_ms": self.prebuffer_ms,
  119. "max_buffer_ms": self.max_buffer_ms,
  120. "chunk_size": self.chunk_size,
  121. "send_interval_ms": self.send_interval_ms,
  122. "sync_enabled": self.sync_enabled,
  123. "compression_enabled": self.compression_enabled,
  124. }