mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-19 15:36:02 +00:00
Add encoder params to VoiceClient.play
This commit is contained in:
parent
b276f3f5a2
commit
8b8ce55378
@ -39,10 +39,17 @@ from .errors import DiscordException
|
||||
|
||||
if TYPE_CHECKING:
|
||||
T = TypeVar('T')
|
||||
APPLICATION_CTL = Literal['audio', 'voip', 'lowdelay']
|
||||
BAND_CTL = Literal['narrow', 'medium', 'wide', 'superwide', 'full']
|
||||
SIGNAL_CTL = Literal['auto', 'voice', 'music']
|
||||
|
||||
|
||||
class ApplicationCtl(TypedDict):
|
||||
audio: int
|
||||
voip: int
|
||||
lowdelay: int
|
||||
|
||||
|
||||
class BandCtl(TypedDict):
|
||||
narrow: int
|
||||
medium: int
|
||||
@ -90,9 +97,10 @@ OK = 0
|
||||
BAD_ARG = -1
|
||||
|
||||
# Encoder CTLs
|
||||
APPLICATION_AUDIO = 2049
|
||||
APPLICATION_VOIP = 2048
|
||||
APPLICATION_LOWDELAY = 2051
|
||||
APPLICATION_AUDIO = 'audio'
|
||||
APPLICATION_VOIP = 'voip'
|
||||
APPLICATION_LOWDELAY = 'lowdelay'
|
||||
# These remain as strings for backwards compat
|
||||
|
||||
CTL_SET_BITRATE = 4002
|
||||
CTL_SET_BANDWIDTH = 4008
|
||||
@ -105,6 +113,12 @@ CTL_SET_GAIN = 4034
|
||||
CTL_LAST_PACKET_DURATION = 4039
|
||||
# fmt: on
|
||||
|
||||
application_ctl: ApplicationCtl = {
|
||||
'audio': 2049,
|
||||
'voip': 2048,
|
||||
'lowdelay': 2051,
|
||||
}
|
||||
|
||||
band_ctl: BandCtl = {
|
||||
'narrow': 1101,
|
||||
'medium': 1102,
|
||||
@ -319,16 +333,38 @@ class _OpusStruct:
|
||||
|
||||
|
||||
class Encoder(_OpusStruct):
|
||||
def __init__(self, application: int = APPLICATION_AUDIO):
|
||||
_OpusStruct.get_opus_version()
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
application: APPLICATION_CTL = 'audio',
|
||||
bitrate: int = 128,
|
||||
fec: bool = True,
|
||||
expected_packet_loss: float = 0.15,
|
||||
bandwidth: BAND_CTL = 'full',
|
||||
signal_type: SIGNAL_CTL = 'auto',
|
||||
):
|
||||
if application not in application_ctl:
|
||||
raise ValueError(f'{application} is not a valid application setting. Try one of: {"".join(application_ctl)}')
|
||||
|
||||
self.application: int = application
|
||||
if not 16 <= bitrate <= 512:
|
||||
raise ValueError(f'bitrate must be between 16 and 512, not {bitrate}')
|
||||
|
||||
if not 0 < expected_packet_loss <= 1.0:
|
||||
raise ValueError(
|
||||
f'expected_packet_loss must be a positive number less than or equal to 1, not {expected_packet_loss}'
|
||||
)
|
||||
|
||||
_OpusStruct.get_opus_version() # lazy loads the opus library
|
||||
|
||||
self.application: int = application_ctl[application]
|
||||
self._state: EncoderStruct = self._create_state()
|
||||
self.set_bitrate(128)
|
||||
self.set_fec(True)
|
||||
self.set_expected_packet_loss_percent(0.15)
|
||||
self.set_bandwidth('full')
|
||||
self.set_signal_type('auto')
|
||||
|
||||
self.set_bitrate(bitrate)
|
||||
self.set_fec(fec)
|
||||
if fec:
|
||||
self.set_expected_packet_loss_percent(expected_packet_loss)
|
||||
self.set_bandwidth(bandwidth)
|
||||
self.set_signal_type(signal_type)
|
||||
|
||||
def __del__(self) -> None:
|
||||
if hasattr(self, '_state'):
|
||||
@ -355,7 +391,7 @@ class Encoder(_OpusStruct):
|
||||
|
||||
def set_signal_type(self, req: SIGNAL_CTL) -> None:
|
||||
if req not in signal_ctl:
|
||||
raise KeyError(f'{req!r} is not a valid bandwidth setting. Try one of: {",".join(signal_ctl)}')
|
||||
raise KeyError(f'{req!r} is not a valid signal type setting. Try one of: {",".join(signal_ctl)}')
|
||||
|
||||
k = signal_ctl[req]
|
||||
_lib.opus_encoder_ctl(self._state, CTL_SET_SIGNAL, k)
|
||||
|
@ -58,7 +58,7 @@ if TYPE_CHECKING:
|
||||
from .guild import Guild
|
||||
from .state import ConnectionState
|
||||
from .user import ClientUser
|
||||
from .opus import Encoder
|
||||
from .opus import Encoder, APPLICATION_CTL, BAND_CTL, SIGNAL_CTL
|
||||
from .channel import StageChannel, VoiceChannel
|
||||
from . import abc
|
||||
|
||||
@ -569,7 +569,18 @@ class VoiceClient(VoiceProtocol):
|
||||
|
||||
return header + box.encrypt(bytes(data), bytes(nonce)).ciphertext + nonce[:4]
|
||||
|
||||
def play(self, source: AudioSource, *, after: Optional[Callable[[Optional[Exception]], Any]] = None) -> None:
|
||||
def play(
|
||||
self,
|
||||
source: AudioSource,
|
||||
*,
|
||||
after: Optional[Callable[[Optional[Exception]], Any]] = None,
|
||||
application: APPLICATION_CTL = 'audio',
|
||||
bitrate: int = 128,
|
||||
fec: bool = True,
|
||||
expected_packet_loss: float = 0.15,
|
||||
bandwidth: BAND_CTL = 'full',
|
||||
signal_type: SIGNAL_CTL = 'auto',
|
||||
) -> None:
|
||||
"""Plays an :class:`AudioSource`.
|
||||
|
||||
The finalizer, ``after`` is called after the source has been exhausted
|
||||
@ -579,9 +590,15 @@ class VoiceClient(VoiceProtocol):
|
||||
caught and the audio player is then stopped. If no after callback is
|
||||
passed, any caught exception will be logged using the library logger.
|
||||
|
||||
Extra parameters may be passed to the internal opus encoder if a PCM based
|
||||
source is used. Otherwise, they are ignored.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Instead of writing to ``sys.stderr``, the library's logger is used.
|
||||
|
||||
.. versionchanged:: 2.4
|
||||
Added encoder parameters as keyword arguments.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
source: :class:`AudioSource`
|
||||
@ -590,6 +607,27 @@ class VoiceClient(VoiceProtocol):
|
||||
The finalizer that is called after the stream is exhausted.
|
||||
This function must have a single parameter, ``error``, that
|
||||
denotes an optional exception that was raised during playing.
|
||||
application: :class:`str`
|
||||
Configures the encoder's intended application. Can be one of:
|
||||
``'audio'``, ``'voip'``, ``'lowdelay'``.
|
||||
Defaults to ``'audio'``.
|
||||
bitrate: :class:`int`
|
||||
Configures the bitrate in the encoder. Can be between ``16`` and ``512``.
|
||||
Defaults to ``128``.
|
||||
fec: :class:`bool`
|
||||
Configures the encoder's use of inband forward error correction.
|
||||
Defaults to ``True``.
|
||||
expected_packet_loss: :class:`float`
|
||||
Configures the encoder's expected packet loss percentage. Requires FEC.
|
||||
Defaults to ``0.15``.
|
||||
bandwidth: :class:`str`
|
||||
Configures the encoder's bandpass. Can be one of:
|
||||
``'narrow'``, ``'medium'``, ``'wide'``, ``'superwide'``, ``'full'``.
|
||||
Defaults to ``'full'``.
|
||||
signal_type: :class:`str`
|
||||
Configures the type of signal being encoded. Can be one of:
|
||||
``'auto'``, ``'voice'``, ``'music'``.
|
||||
Defaults to ``'auto'``.
|
||||
|
||||
Raises
|
||||
-------
|
||||
@ -599,6 +637,8 @@ class VoiceClient(VoiceProtocol):
|
||||
Source is not a :class:`AudioSource` or after is not a callable.
|
||||
OpusNotLoaded
|
||||
Source is not opus encoded and opus is not loaded.
|
||||
ValueError
|
||||
An improper value was passed as an encoder parameter.
|
||||
"""
|
||||
|
||||
if not self.is_connected():
|
||||
@ -610,8 +650,15 @@ class VoiceClient(VoiceProtocol):
|
||||
if not isinstance(source, AudioSource):
|
||||
raise TypeError(f'source must be an AudioSource not {source.__class__.__name__}')
|
||||
|
||||
if not self.encoder and not source.is_opus():
|
||||
self.encoder = opus.Encoder()
|
||||
if not source.is_opus():
|
||||
self.encoder = opus.Encoder(
|
||||
application=application,
|
||||
bitrate=bitrate,
|
||||
fec=fec,
|
||||
expected_packet_loss=expected_packet_loss,
|
||||
bandwidth=bandwidth,
|
||||
signal_type=signal_type,
|
||||
)
|
||||
|
||||
self._player = AudioPlayer(source, self, after=after)
|
||||
self._player.start()
|
||||
|
Loading…
x
Reference in New Issue
Block a user