- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等) - java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等) - javax/ 4个模块 (net/crypto/security/sql) - org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http) - dalvik/ 1个模块 (system) - ~3053个类/接口,方法签名完全对齐Android SDK - 桌面跨平台运行 (Windows/macOS/Linux) - 修复所有导入错误和Python关键字冲突
1538 lines
51 KiB
Python
1538 lines
51 KiB
Python
"""
|
|
Android media 音频扩展组件 — AudioFormat, AudioTrack, AudioRecord, AudioDeviceInfo,
|
|
AudioRouting, AudioManager, AudioMix, AudioProfile, AudioProductStrategy 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
import time
|
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
from ..java.lang import RuntimeException
|
|
|
|
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
|
|
|
__all__ = [
|
|
"AudioFormat", "AudioAttributes", "AudioTrack", "AudioRecord",
|
|
"AudioDeviceInfo", "AudioDeviceCallback", "AudioRouting",
|
|
"AudioTimestamp", "AudioPresentation", "AudioPlaybackConfiguration",
|
|
"AudioRecordingConfiguration", "AudioManagerAudio", "AudioMixPort",
|
|
"AudioPort", "AudioPatch", "AudioGain", "AudioGainConfig",
|
|
"AudioMix", "AudioProfile", "AudioProductStrategy", "AudioVolumeGroup",
|
|
]
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioFormat
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioFormat(Stub):
|
|
"""AudioFormat — 音频格式"""
|
|
|
|
ENCODING_INVALID = 0
|
|
ENCODING_DEFAULT = 1
|
|
ENCODING_PCM_16BIT = 2
|
|
ENCODING_PCM_8BIT = 3
|
|
ENCODING_PCM_FLOAT = 4
|
|
ENCODING_AC3 = 5
|
|
ENCODING_E_AC3 = 6
|
|
ENCODING_DTS = 7
|
|
ENCODING_DTS_HD = 8
|
|
ENCODING_MP3 = 9
|
|
ENCODING_AAC_LC = 10
|
|
ENCODING_AAC_HE_V1 = 11
|
|
ENCODING_AAC_HE_V2 = 12
|
|
ENCODING_PCM_24BIT_PACKED = 13
|
|
ENCODING_PCM_32BIT = 14
|
|
ENCODING_PCM_16BIT_BUNDLED = 15
|
|
ENCODING_PCM_24BIT = 16
|
|
ENCODING_DOLBY_TRUEHD = 17
|
|
ENCODING_DOLBY_MAT = 18
|
|
ENCODING_IEC61937 = 19
|
|
ENCODING_OPUS = 20
|
|
ENCODING_AMRNB = 21
|
|
ENCODING_AMRWB = 22
|
|
ENCODING_FLAC = 23
|
|
ENCODING_MP2 = 24
|
|
ENCODING_AC4 = 25
|
|
ENCODING_AC3_PLUS = 26
|
|
ENCODING_E_AC3_JOC = 27
|
|
ENCODING_DOLBY_MAT_21 = 28
|
|
ENCODING_DSD = 29
|
|
ENCODING_PCM_8_24_BIT = 30
|
|
ENCODING_WMA = 31
|
|
ENCODING_WMA_PRO = 32
|
|
ENCODING_ALAC = 33
|
|
ENCODING_APE = 34
|
|
ENCODING_VORBIS = 35
|
|
|
|
CHANNEL_INVALID = 0
|
|
CHANNEL_OUT_FRONT_LEFT = 0x4
|
|
CHANNEL_OUT_FRONT_RIGHT = 0x8
|
|
CHANNEL_OUT_FRONT_CENTER = 0x10
|
|
CHANNEL_OUT_LOW_FREQUENCY = 0x20
|
|
CHANNEL_OUT_BACK_LEFT = 0x40
|
|
CHANNEL_OUT_BACK_RIGHT = 0x80
|
|
CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100
|
|
CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200
|
|
CHANNEL_OUT_BACK_CENTER = 0x400
|
|
CHANNEL_OUT_SIDE_LEFT = 0x800
|
|
CHANNEL_OUT_SIDE_RIGHT = 0x1000
|
|
CHANNEL_OUT_TOP_CENTER = 0x2000
|
|
CHANNEL_OUT_TOP_FRONT_LEFT = 0x4000
|
|
CHANNEL_OUT_TOP_FRONT_RIGHT = 0x8000
|
|
CHANNEL_OUT_TOP_FRONT_CENTER = 0x10000
|
|
CHANNEL_OUT_TOP_BACK_LEFT = 0x20000
|
|
CHANNEL_OUT_TOP_BACK_RIGHT = 0x40000
|
|
CHANNEL_OUT_TOP_BACK_CENTER = 0x80000
|
|
CHANNEL_OUT_MONO = 0x4
|
|
CHANNEL_OUT_STEREO = 0xC
|
|
CHANNEL_OUT_QUAD = 0xCC
|
|
CHANNEL_OUT_SURROUND = 0x41C
|
|
CHANNEL_OUT_5POINT1 = 0xFC
|
|
CHANNEL_OUT_7POINT1 = 0x4FC
|
|
CHANNEL_OUT_5POINT1_SIDE = 0x8FC
|
|
CHANNEL_OUT_7POINT1_SURROUND = 0x18FC
|
|
|
|
CHANNEL_IN_LEFT = 0x4
|
|
CHANNEL_IN_RIGHT = 0x8
|
|
CHANNEL_IN_FRONT = 0x10
|
|
CHANNEL_IN_BACK = 0x40
|
|
CHANNEL_IN_LEFT_PROCESSED = 0x20
|
|
CHANNEL_IN_RIGHT_PROCESSED = 0x80
|
|
CHANNEL_IN_FRONT_PROCESSED = 0x100
|
|
CHANNEL_IN_BACK_PROCESSED = 0x200
|
|
CHANNEL_IN_PRESSURE = 0x400
|
|
CHANNEL_IN_X_AXIS = 0x800
|
|
CHANNEL_IN_Y_AXIS = 0x1000
|
|
CHANNEL_IN_Z_AXIS = 0x2000
|
|
CHANNEL_IN_VOICE_UPLINK = 0x4000
|
|
CHANNEL_IN_VOICE_DNLINK = 0x8000
|
|
CHANNEL_IN_MONO = 0x10
|
|
CHANNEL_IN_STEREO = 0xC
|
|
CHANNEL_IN_DEFAULT = 0x1
|
|
|
|
SAMPLE_RATE_UNSPECIFIED = 0
|
|
|
|
class Builder(Stub):
|
|
"""AudioFormat.Builder — 音频格式构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._encoding = AudioFormat.ENCODING_DEFAULT
|
|
self._sample_rate = AudioFormat.SAMPLE_RATE_UNSPECIFIED
|
|
self._channel_mask = AudioFormat.CHANNEL_INVALID
|
|
self._channel_index_mask = 0
|
|
|
|
def setEncoding(self, encoding: int) -> "AudioFormat.Builder":
|
|
self._encoding = encoding
|
|
return self
|
|
|
|
def setSampleRate(self, sampleRate: int) -> "AudioFormat.Builder":
|
|
self._sample_rate = sampleRate
|
|
return self
|
|
|
|
def setChannelMask(self, channelMask: int) -> "AudioFormat.Builder":
|
|
self._channel_mask = channelMask
|
|
return self
|
|
|
|
def setChannelIndexMask(self, channelIndexMask: int) -> "AudioFormat.Builder":
|
|
self._channel_index_mask = channelIndexMask
|
|
return self
|
|
|
|
def build(self) -> "AudioFormat":
|
|
fmt = AudioFormat()
|
|
fmt._encoding = self._encoding
|
|
fmt._sample_rate = self._sample_rate
|
|
fmt._channel_mask = self._channel_mask
|
|
fmt._channel_index_mask = self._channel_index_mask
|
|
return fmt
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._encoding = AudioFormat.ENCODING_DEFAULT
|
|
self._sample_rate = AudioFormat.SAMPLE_RATE_UNSPECIFIED
|
|
self._channel_mask = AudioFormat.CHANNEL_INVALID
|
|
self._channel_index_mask = 0
|
|
|
|
def getEncoding(self) -> int:
|
|
return self._encoding
|
|
|
|
def getSampleRate(self) -> int:
|
|
return self._sample_rate
|
|
|
|
def getChannelMask(self) -> int:
|
|
return self._channel_mask
|
|
|
|
def getChannelIndexMask(self) -> int:
|
|
return self._channel_index_mask
|
|
|
|
def getChannelCount(self) -> int:
|
|
mask = self._channel_mask
|
|
if mask == AudioFormat.CHANNEL_INVALID:
|
|
return 0
|
|
return bin(mask).count("1")
|
|
|
|
def toString(self) -> str:
|
|
return (f"AudioFormat(encoding={self._encoding}, "
|
|
f"sampleRate={self._sample_rate}, channels={self.getChannelCount()})")
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioFormat):
|
|
return False
|
|
return (self._encoding == other._encoding and
|
|
self._sample_rate == other._sample_rate and
|
|
self._channel_mask == other._channel_mask)
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self._encoding, self._sample_rate, self._channel_mask)) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioAttributes (扩展)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioAttributes(Stub):
|
|
"""AudioAttributes — 音频属性 (完整版)"""
|
|
|
|
CONTENT_TYPE_UNKNOWN = 0
|
|
CONTENT_TYPE_SPEECH = 1
|
|
CONTENT_TYPE_MUSIC = 2
|
|
CONTENT_TYPE_MOVIE = 3
|
|
CONTENT_TYPE_SONIFICATION = 4
|
|
|
|
USAGE_UNKNOWN = 0
|
|
USAGE_MEDIA = 1
|
|
USAGE_VOICE_COMMUNICATION = 2
|
|
USAGE_VOICE_COMMUNICATION_SIGNALLING = 3
|
|
USAGE_ALARM = 4
|
|
USAGE_NOTIFICATION = 5
|
|
USAGE_NOTIFICATION_RINGTONE = 6
|
|
USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7
|
|
USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8
|
|
USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9
|
|
USAGE_NOTIFICATION_EVENT = 10
|
|
USAGE_ASSISTANCE_ACCESSIBILITY = 11
|
|
USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12
|
|
USAGE_ASSISTANCE_SONIFICATION = 13
|
|
USAGE_GAME = 14
|
|
USAGE_VIRTUAL_SOURCE = 15
|
|
USAGE_ASSISTANT = 16
|
|
USAGE_CALL_ASSISTANT = 17
|
|
USAGE_EMERGENCY = 18
|
|
USAGE_SAFETY = 19
|
|
USAGE_VEHICLE_STATUS = 20
|
|
USAGE_ANNOUNCEMENT = 21
|
|
|
|
FLAG_AUDIBILITY_ENFORCED = 1
|
|
FLAG_SECURE = 2
|
|
FLAG_SCO = 4
|
|
FLAG_BEACON = 8
|
|
FLAG_BYPASS_INTERRUPTION_POLICY = 16
|
|
FLAG_BYPASS_MUTE = 32
|
|
FLAG_LOW_LATENCY = 64
|
|
FLAG_DEEP_BUFFER = 128
|
|
FLAG_NO_EXPANDABLE = 256
|
|
FLAG_HW_AV_SYNC = 512
|
|
FLAG_BYPASS_INTERRUPTION_POLICY_FLAG = 1024
|
|
|
|
ALLOW_CAPTURE_BY_ALL = 0
|
|
ALLOW_CAPTURE_BY_SYSTEM = 1
|
|
ALLOW_CAPTURE_BY_NONE = 2
|
|
|
|
class Builder(Stub):
|
|
"""AudioAttributes.Builder — 音频属性构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._content_type = AudioAttributes.CONTENT_TYPE_UNKNOWN
|
|
self._usage = AudioAttributes.USAGE_UNKNOWN
|
|
self._flags = 0
|
|
self._legacy_stream_type = -1
|
|
self._allowed_capture_policy = AudioAttributes.ALLOW_CAPTURE_BY_ALL
|
|
self._spatial_behavior = 0
|
|
|
|
def setContentType(self, contentType: int) -> "AudioAttributes.Builder":
|
|
self._content_type = contentType
|
|
return self
|
|
|
|
def setUsage(self, usage: int) -> "AudioAttributes.Builder":
|
|
self._usage = usage
|
|
return self
|
|
|
|
def setFlags(self, flags: int) -> "AudioAttributes.Builder":
|
|
self._flags = flags
|
|
return self
|
|
|
|
def setLegacyStreamType(self, streamType: int) -> "AudioAttributes.Builder":
|
|
self._legacy_stream_type = streamType
|
|
return self
|
|
|
|
def setAllowedCapturePolicy(self, capturePolicy: int) -> "AudioAttributes.Builder":
|
|
self._allowed_capture_policy = capturePolicy
|
|
return self
|
|
|
|
def setSpatialBehavior(self, spatialBehavior: int) -> "AudioAttributes.Builder":
|
|
self._spatial_behavior = spatialBehavior
|
|
return self
|
|
|
|
def build(self) -> "AudioAttributes":
|
|
attrs = AudioAttributes()
|
|
attrs._content_type = self._content_type
|
|
attrs._usage = self._usage
|
|
attrs._flags = self._flags
|
|
attrs._legacy_stream_type = self._legacy_stream_type
|
|
attrs._allowed_capture_policy = self._allowed_capture_policy
|
|
attrs._spatial_behavior = self._spatial_behavior
|
|
return attrs
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._content_type = AudioAttributes.CONTENT_TYPE_UNKNOWN
|
|
self._usage = AudioAttributes.USAGE_UNKNOWN
|
|
self._flags = 0
|
|
self._legacy_stream_type = -1
|
|
self._allowed_capture_policy = AudioAttributes.ALLOW_CAPTURE_BY_ALL
|
|
self._spatial_behavior = 0
|
|
|
|
def getContentType(self) -> int:
|
|
return self._content_type
|
|
|
|
def getUsage(self) -> int:
|
|
return self._usage
|
|
|
|
def getFlags(self) -> int:
|
|
return self._flags
|
|
|
|
def getAllowedCapturePolicy(self) -> int:
|
|
return self._allowed_capture_policy
|
|
|
|
def getSpatialBehavior(self) -> int:
|
|
return self._spatial_behavior
|
|
|
|
def getVolumeControlStream(self) -> int:
|
|
if self._usage in (AudioAttributes.USAGE_VOICE_COMMUNICATION,
|
|
AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING):
|
|
return 0
|
|
if self._usage == AudioAttributes.USAGE_ALARM:
|
|
return 4
|
|
if self._usage in (AudioAttributes.USAGE_NOTIFICATION,
|
|
AudioAttributes.USAGE_NOTIFICATION_RINGTONE,
|
|
AudioAttributes.USAGE_NOTIFICATION_EVENT):
|
|
return 5
|
|
if self._usage in (AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY,
|
|
AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
|
|
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION):
|
|
return 3
|
|
return 3
|
|
|
|
toString = lambda self: f"AudioAttributes(usage={self._usage}, contentType={self._content_type})"
|
|
equals = lambda self, other: self is other if not isinstance(other, AudioAttributes) else (self._usage == other._usage and self._content_type == other._content_type)
|
|
hashCode = lambda self: hash((self._usage, self._content_type)) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioTimestamp
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioTimestamp(Stub):
|
|
"""AudioTimestamp — 音频时间戳"""
|
|
|
|
TIMEBASE_MONOTONIC = 0
|
|
TIMEBASE_BOOTTIME = 1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.framePosition: int = 0
|
|
self.nanoTime: int = 0
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioTimestamp(frame={self.framePosition}, nanoTime={self.nanoTime})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioTimestamp):
|
|
return False
|
|
return self.framePosition == other.framePosition and self.nanoTime == other.nanoTime
|
|
|
|
def hashCode(self) -> int:
|
|
return hash((self.framePosition, self.nanoTime)) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioPresentation
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioPresentation(Stub):
|
|
"""AudioPresentation — 音频呈现"""
|
|
|
|
CONTENT_TYPE_UNKNOWN = 0
|
|
CONTENT_TYPE_DIALOG = 1
|
|
CONTENT_TYPE_MUSIC = 2
|
|
CONTENT_TYPE_VOICEOVER = 3
|
|
CONTENT_TYPE_NARRATION = 4
|
|
CONTENT_TYPE_VISUALLY_IMPAIRED = 5
|
|
CONTENT_TYPE_HEARING_IMPAIRED = 6
|
|
CONTENT_TYPE_COMMENTARY = 7
|
|
CONTENT_TYPE_SPOKEN_SUBTITLE = 8
|
|
CONTENT_TYPE_EMERGENCY = 9
|
|
CONTENT_TYPE_MAIN = 10
|
|
CONTENT_TYPE_SUPPLEMENTARY = 11
|
|
|
|
MASTERED_FOR_STEREO = 0
|
|
MASTERED_FOR_SURROUND = 1
|
|
MASTERED_FOR_3D = 2
|
|
MASTERED_FOR_HEADPHONE = 3
|
|
|
|
def __init__(self, presentationId: int = 0, programId: int = 0,
|
|
locale: str = "", contentType: int = CONTENT_TYPE_UNKNOWN,
|
|
masteredFor: int = MASTERED_FOR_STEREO,
|
|
labels: Dict[str, str] = None):
|
|
super().__init__()
|
|
self._presentation_id = presentationId
|
|
self._program_id = programId
|
|
self._locale = locale
|
|
self._content_type = contentType
|
|
self._mastered_for = masteredFor
|
|
self._labels = labels or {}
|
|
|
|
def getPresentationId(self) -> int:
|
|
return self._presentation_id
|
|
|
|
def getProgramId(self) -> int:
|
|
return self._program_id
|
|
|
|
def getLocale(self) -> str:
|
|
return self._locale
|
|
|
|
def getContentType(self) -> int:
|
|
return self._content_type
|
|
|
|
def getMasteredFor(self) -> int:
|
|
return self._mastered_for
|
|
|
|
def getLabels(self) -> Dict[str, str]:
|
|
return dict(self._labels)
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioPresentation(id={self._presentation_id}, locale={self._locale})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioPresentation):
|
|
return False
|
|
return self._presentation_id == other._presentation_id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._presentation_id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioTrack
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioTrack(Stub):
|
|
"""AudioTrack — 音频回放轨道"""
|
|
|
|
MODE_STATIC = 0
|
|
MODE_STREAM = 1
|
|
|
|
STATE_UNINITIALIZED = 0
|
|
STATE_INITIALIZED = 1
|
|
STATE_NO_STATIC_DATA = 2
|
|
|
|
WRITE_BLOCKING = 0
|
|
SUCCESS = 0
|
|
ERROR = -1
|
|
ERROR_BAD_VALUE = -2
|
|
ERROR_INVALID_OPERATION = -3
|
|
ERROR_DEAD_OBJECT = -6
|
|
ERROR_WOULD_BLOCK = -7
|
|
|
|
PERFORMANCE_MODE_NONE = 0
|
|
PERFORMANCE_MODE_LOW_LATENCY = 1
|
|
PERFORMANCE_MODE_POWER_SAVING = 2
|
|
|
|
class Builder(Stub):
|
|
"""AudioTrack.Builder — 回放轨道构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._audio_format = None
|
|
self._audio_attributes = None
|
|
self._buffer_size = 0
|
|
self._session_id = 0
|
|
self._transfer_mode = AudioTrack.MODE_STREAM
|
|
self._performance_mode = AudioTrack.PERFORMANCE_MODE_NONE
|
|
self._context = None
|
|
|
|
def setAudioFormat(self, format_: AudioFormat) -> "AudioTrack.Builder":
|
|
self._audio_format = format_
|
|
return self
|
|
|
|
def setAudioAttributes(self, attributes: AudioAttributes) -> "AudioTrack.Builder":
|
|
self._audio_attributes = attributes
|
|
return self
|
|
|
|
def setBufferSizeInBytes(self, bufferSize: int) -> "AudioTrack.Builder":
|
|
self._buffer_size = bufferSize
|
|
return self
|
|
|
|
def setSessionId(self, sessionId: int) -> "AudioTrack.Builder":
|
|
self._session_id = sessionId
|
|
return self
|
|
|
|
def setTransferMode(self, mode: int) -> "AudioTrack.Builder":
|
|
self._transfer_mode = mode
|
|
return self
|
|
|
|
def setPerformanceMode(self, mode: int) -> "AudioTrack.Builder":
|
|
self._performance_mode = mode
|
|
return self
|
|
|
|
def build(self) -> "AudioTrack":
|
|
return AudioTrack(
|
|
format_=self._audio_format,
|
|
attributes=self._audio_attributes,
|
|
bufferSize=self._buffer_size,
|
|
sessionId=self._session_id,
|
|
mode=self._transfer_mode,
|
|
performanceMode=self._performance_mode,
|
|
)
|
|
|
|
def __init__(self, format_: Any = None, attributes: Any = None,
|
|
bufferSize: int = 0, sessionId: int = 0,
|
|
mode: int = MODE_STREAM, performanceMode: int = PERFORMANCE_MODE_NONE,
|
|
sampleRate: int = 44100, channelConfig: int = AudioFormat.CHANNEL_OUT_STEREO,
|
|
audioFormat: int = AudioFormat.ENCODING_PCM_16BIT):
|
|
super().__init__()
|
|
self._state = AudioTrack.STATE_INITIALIZED
|
|
self._sample_rate = sampleRate
|
|
self._channel_config = channelConfig
|
|
self._audio_format = audioFormat
|
|
self._buffer_size = bufferSize if bufferSize > 0 else 4096
|
|
self._mode = mode
|
|
self._session_id = sessionId
|
|
self._performance_mode = performanceMode
|
|
self._playback_rate = 1.0
|
|
self._volume = 1.0
|
|
self._position = 0
|
|
self._is_playing = False
|
|
self._audio_attributes = attributes
|
|
self._format = format_
|
|
self._timestamp = AudioTimestamp()
|
|
|
|
def play(self):
|
|
self._is_playing = True
|
|
|
|
def stop(self):
|
|
self._is_playing = False
|
|
|
|
def pause(self):
|
|
self._is_playing = False
|
|
|
|
def flush(self):
|
|
self._position = 0
|
|
|
|
def release(self):
|
|
self._state = AudioTrack.STATE_UNINITIALIZED
|
|
self._is_playing = False
|
|
|
|
def write(self, audioData: Any, offsetInBytes: int = 0, sizeInBytes: int = 0) -> int:
|
|
if self._state != AudioTrack.STATE_INITIALIZED:
|
|
return AudioTrack.ERROR_INVALID_OPERATION
|
|
if isinstance(audioData, (bytes, bytearray)):
|
|
written = sizeInBytes if sizeInBytes > 0 else len(audioData)
|
|
self._position += written
|
|
return written
|
|
if hasattr(audioData, 'nbytes'):
|
|
written = sizeInBytes or audioData.nbytes
|
|
self._position += written
|
|
return written
|
|
return AudioTrack.ERROR_BAD_VALUE
|
|
|
|
def getMinBufferSize(sampleRateInHz: int, channelConfig: int, audioFormat: int) -> int:
|
|
return 4096
|
|
|
|
def getNativeOutputSampleRate(streamType: int) -> int:
|
|
return 44100
|
|
|
|
def getSampleRate(self) -> int:
|
|
return self._sample_rate
|
|
|
|
def getPlaybackRate(self) -> float:
|
|
return self._playback_rate
|
|
|
|
def setPlaybackRate(self, rate: float):
|
|
self._playback_rate = rate
|
|
|
|
def getVolume(self) -> float:
|
|
return self._volume
|
|
|
|
def setVolume(self, volume: float):
|
|
self._volume = max(0.0, min(1.0, volume))
|
|
|
|
def getAudioFormat(self) -> int:
|
|
return self._audio_format
|
|
|
|
def getChannelConfiguration(self) -> int:
|
|
return self._channel_config
|
|
|
|
def getChannelCount(self) -> int:
|
|
return bin(self._channel_config).count("1")
|
|
|
|
def getState(self) -> int:
|
|
return self._state
|
|
|
|
def getPlayState(self) -> int:
|
|
return 3 if self._is_playing else 2
|
|
|
|
def getBufferSizeInFrames(self) -> int:
|
|
return self._buffer_size // 4
|
|
|
|
def getTimestamp(self, outTimestamp: AudioTimestamp) -> int:
|
|
base_time = time.time_ns()
|
|
outTimestamp.nanoTime = base_time
|
|
outTimestamp.framePosition = self._position
|
|
return AudioTrack.SUCCESS
|
|
|
|
def getSessionId(self) -> int:
|
|
return self._session_id
|
|
|
|
def getPerformanceMode(self) -> int:
|
|
return self._performance_mode
|
|
|
|
def setPreferredDevice(self, deviceInfo: Any) -> bool:
|
|
return True
|
|
|
|
def getPreferredDevice(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def toString(self) -> str:
|
|
return (f"AudioTrack(sampleRate={self._sample_rate}, "
|
|
f"playing={self._is_playing}, state={self._state})")
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioRecord
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioRecord(Stub):
|
|
"""AudioRecord — 音频录制"""
|
|
|
|
STATE_UNINITIALIZED = 0
|
|
STATE_INITIALIZED = 1
|
|
|
|
SUCCESS = 0
|
|
ERROR = -1
|
|
ERROR_BAD_VALUE = -2
|
|
ERROR_INVALID_OPERATION = -3
|
|
ERROR_DEAD_OBJECT = -6
|
|
|
|
READ_BLOCKING = 0
|
|
READ_NON_BLOCKING = 1
|
|
|
|
class Builder(Stub):
|
|
"""AudioRecord.Builder — 录制构建器"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._audio_format = None
|
|
self._audio_attributes = None
|
|
self._buffer_size = 0
|
|
self._session_id = 0
|
|
self._context = None
|
|
|
|
def setAudioFormat(self, format_: AudioFormat) -> "AudioRecord.Builder":
|
|
self._audio_format = format_
|
|
return self
|
|
|
|
def setAudioAttributes(self, attributes: AudioAttributes) -> "AudioRecord.Builder":
|
|
self._audio_attributes = attributes
|
|
return self
|
|
|
|
def setBufferSizeInBytes(self, bufferSize: int) -> "AudioRecord.Builder":
|
|
self._buffer_size = bufferSize
|
|
return self
|
|
|
|
def setSessionId(self, sessionId: int) -> "AudioRecord.Builder":
|
|
self._session_id = sessionId
|
|
return self
|
|
|
|
def build(self) -> "AudioRecord":
|
|
return AudioRecord(
|
|
format_=self._audio_format,
|
|
attributes=self._audio_attributes,
|
|
bufferSize=self._buffer_size,
|
|
sessionId=self._session_id,
|
|
)
|
|
|
|
def __init__(self, format_: Any = None, attributes: Any = None,
|
|
bufferSize: int = 0, sessionId: int = 0,
|
|
sampleRate: int = 44100, channelConfig: int = AudioFormat.CHANNEL_IN_MONO,
|
|
audioFormat: int = AudioFormat.ENCODING_PCM_16BIT):
|
|
super().__init__()
|
|
self._state = AudioRecord.STATE_INITIALIZED
|
|
self._sample_rate = sampleRate
|
|
self._channel_config = channelConfig
|
|
self._audio_format = audioFormat
|
|
self._buffer_size = bufferSize if bufferSize > 0 else 4096
|
|
self._session_id = sessionId
|
|
self._is_recording = False
|
|
self._position = 0
|
|
self._format = format_
|
|
self._audio_attributes = attributes
|
|
|
|
def startRecording(self):
|
|
self._is_recording = True
|
|
|
|
def stop(self):
|
|
self._is_recording = False
|
|
|
|
def release(self):
|
|
self._state = AudioRecord.STATE_UNINITIALIZED
|
|
self._is_recording = False
|
|
|
|
def read(self, audioData: Any, offsetInBytes: int = 0, sizeInBytes: int = 0) -> int:
|
|
if not self._is_recording:
|
|
return AudioRecord.ERROR_INVALID_OPERATION
|
|
return sizeInBytes if sizeInBytes > 0 else 0
|
|
|
|
def getMinBufferSize(sampleRateInHz: int, channelConfig: int, audioFormat: int) -> int:
|
|
return 4096
|
|
|
|
def getSampleRate(self) -> int:
|
|
return self._sample_rate
|
|
|
|
def getAudioFormat(self) -> int:
|
|
return self._audio_format
|
|
|
|
def getChannelConfiguration(self) -> int:
|
|
return self._channel_config
|
|
|
|
def getChannelCount(self) -> int:
|
|
return bin(self._channel_config).count("1")
|
|
|
|
def getState(self) -> int:
|
|
return self._state
|
|
|
|
def getRecordingState(self) -> int:
|
|
return 3 if self._is_recording else 2
|
|
|
|
def getBufferSizeInFrames(self) -> int:
|
|
return self._buffer_size // 4
|
|
|
|
def getSessionId(self) -> int:
|
|
return self._session_id
|
|
|
|
def setPreferredDevice(self, deviceInfo: Any) -> bool:
|
|
return True
|
|
|
|
def getPreferredDevice(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioRecord(sampleRate={self._sample_rate}, recording={self._is_recording})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioDeviceInfo
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioDeviceInfo(Stub):
|
|
"""AudioDeviceInfo — 音频设备信息"""
|
|
|
|
TYPE_UNKNOWN = 0
|
|
TYPE_BUILTIN_EARPIECE = 1
|
|
TYPE_BUILTIN_SPEAKER = 2
|
|
TYPE_WIRED_HEADSET = 3
|
|
TYPE_WIRED_HEADPHONES = 4
|
|
TYPE_LINE_ANALOG = 5
|
|
TYPE_LINE_DIGITAL = 6
|
|
TYPE_BLUETOOTH_SCO = 7
|
|
TYPE_BLUETOOTH_A2DP = 8
|
|
TYPE_HDMI = 9
|
|
TYPE_HDMI_ARC = 10
|
|
TYPE_USB_DEVICE = 11
|
|
TYPE_USB_ACCESSORY = 12
|
|
TYPE_DOCK = 13
|
|
TYPE_FM = 14
|
|
TYPE_BUILTIN_MIC = 15
|
|
TYPE_FM_TUNER = 16
|
|
TYPE_TV_TUNER = 17
|
|
TYPE_TELEPHONY = 18
|
|
TYPE_AUX_LINE = 19
|
|
TYPE_IP = 20
|
|
TYPE_BUS = 21
|
|
TYPE_USB_HEADSET = 22
|
|
TYPE_HEARING_AID = 23
|
|
TYPE_BUILTIN_SPEAKER_SAFE = 24
|
|
TYPE_REMOTE_SUBMIX = 25
|
|
TYPE_BLE_HEADSET = 26
|
|
TYPE_BLE_SPEAKER = 27
|
|
TYPE_BLE_BROADCAST = 28
|
|
|
|
def __init__(self, id: int = 0, type_: int = TYPE_UNKNOWN,
|
|
productName: str = "", address: str = "",
|
|
isSource: bool = False, isSink: bool = False,
|
|
sampleRates: List[int] = None,
|
|
channelMasks: List[int] = None,
|
|
encodings: List[int] = None):
|
|
super().__init__()
|
|
self._id = id
|
|
self._type = type_
|
|
self._product_name = productName
|
|
self._address = address
|
|
self._is_source = isSource
|
|
self._is_sink = isSink
|
|
self._sample_rates = sampleRates or [44100, 48000]
|
|
self._channel_masks = channelMasks or [AudioFormat.CHANNEL_OUT_STEREO]
|
|
self._encodings = encodings or [AudioFormat.ENCODING_PCM_16BIT]
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getType(self) -> int:
|
|
return self._type
|
|
|
|
def getProductName(self) -> Any:
|
|
return self._product_name
|
|
|
|
def getAddress(self) -> str:
|
|
return self._address
|
|
|
|
def isSource(self) -> bool:
|
|
return self._is_source
|
|
|
|
def isSink(self) -> bool:
|
|
return self._is_sink
|
|
|
|
def getSampleRates(self) -> List[int]:
|
|
return list(self._sample_rates)
|
|
|
|
def getChannelMasks(self) -> List[int]:
|
|
return list(self._channel_masks)
|
|
|
|
def getEncodings(self) -> List[int]:
|
|
return list(self._encodings)
|
|
|
|
def getChannelCounts(self) -> List[int]:
|
|
return [bin(m).count("1") for m in self._channel_masks]
|
|
|
|
def toString(self) -> str:
|
|
type_names = {
|
|
1: "BUILTIN_EARPIECE", 2: "BUILTIN_SPEAKER", 3: "WIRED_HEADSET",
|
|
4: "WIRED_HEADPHONES", 8: "BTSCO", 9: "HDMI", 11: "USB_DEVICE",
|
|
}
|
|
return (f"AudioDeviceInfo(id={self._id}, "
|
|
f"type={type_names.get(self._type, str(self._type))})")
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioDeviceInfo):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioDeviceCallback
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioDeviceCallback(Stub):
|
|
"""AudioDeviceCallback — 音频设备回调"""
|
|
|
|
def onAudioDevicesAdded(self, addedDevices: List[AudioDeviceInfo]):
|
|
pass
|
|
|
|
def onAudioDevicesRemoved(self, removedDevices: List[AudioDeviceInfo]):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioDeviceCallback@{hex(id(self))}"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioRouting
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioRouting(Stub):
|
|
"""AudioRouting — 音频路由接口"""
|
|
|
|
def setPreferredDevice(self, deviceInfo: AudioDeviceInfo) -> bool:
|
|
raise NotImplementedError
|
|
|
|
def getPreferredDevice(self) -> Optional[AudioDeviceInfo]:
|
|
return None
|
|
|
|
def getDevices(self, flags: int) -> List[AudioDeviceInfo]:
|
|
return []
|
|
|
|
def addOnRoutingPrefChangedListener(self, listener: Any, handler: Any = None):
|
|
pass
|
|
|
|
def removeOnRoutingPrefChangedListener(self, listener: Any):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioRouting@{hex(id(self))}"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioPlaybackConfiguration
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioPlaybackConfiguration(Stub):
|
|
"""AudioPlaybackConfiguration — 音频回放配置"""
|
|
|
|
PLAYER_STATE_UNKNOWN = -1
|
|
PLAYER_STATE_IDLE = 0
|
|
PLAYER_STATE_STARTED = 1
|
|
PLAYER_STATE_PAUSED = 2
|
|
PLAYER_STATE_STOPPED = 3
|
|
|
|
def __init__(self, id_: int = 0, usage: int = AudioAttributes.USAGE_MEDIA,
|
|
contentType: int = AudioAttributes.CONTENT_TYPE_MUSIC,
|
|
playerState: int = PLAYER_STATE_UNKNOWN):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._usage = usage
|
|
self._content_type = contentType
|
|
self._player_state = playerState
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getUsage(self) -> int:
|
|
return self._usage
|
|
|
|
def getContentType(self) -> int:
|
|
return self._content_type
|
|
|
|
def getPlayerState(self) -> int:
|
|
return self._player_state
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioPlaybackConfiguration(id={self._id}, state={self._player_state})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioPlaybackConfiguration):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioRecordingConfiguration
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioRecordingConfiguration(Stub):
|
|
"""AudioRecordingConfiguration — 音频录制配置"""
|
|
|
|
def __init__(self, id_: int = 0, clientUid: int = 0,
|
|
sessionId: int = 0, source: int = 0,
|
|
audioFormat: Optional[AudioFormat] = None):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._client_uid = clientUid
|
|
self._session_id = sessionId
|
|
self._source = source
|
|
self._audio_format = audioFormat or AudioFormat()
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getClientUid(self) -> int:
|
|
return self._client_uid
|
|
|
|
def getSessionId(self) -> int:
|
|
return self._session_id
|
|
|
|
def getSource(self) -> int:
|
|
return self._source
|
|
|
|
def getAudioFormat(self) -> AudioFormat:
|
|
return self._audio_format
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioRecordingConfiguration(id={self._id}, source={self._source})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioRecordingConfiguration):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioManagerAudio (扩展音频管理器)
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioManagerAudio(Stub):
|
|
"""AudioManager — 音频管理器 (扩展版)"""
|
|
|
|
STREAM_VOICE_CALL = 0
|
|
STREAM_SYSTEM = 1
|
|
STREAM_RING = 2
|
|
STREAM_MUSIC = 3
|
|
STREAM_ALARM = 4
|
|
STREAM_NOTIFICATION = 5
|
|
STREAM_BLUETOOTH_SCO = 6
|
|
STREAM_SYSTEM_ENFORCED = 7
|
|
STREAM_DTMF = 8
|
|
STREAM_TTS = 9
|
|
STREAM_ACCESSIBILITY = 10
|
|
|
|
FLAG_SHOW_UI = 1
|
|
FLAG_ALLOW_RINGER_MODES = 2
|
|
FLAG_PLAY_SOUND = 4
|
|
FLAG_REMOVE_SOUND_AND_VIBRATE = 8
|
|
FLAG_VIBRATE = 16
|
|
FLAG_BYPASS_INTERRUPTION_POLICY = 32
|
|
FLAG_BYPASS_MUTE = 64
|
|
FLAG_HDMI_SYSTEM_AUDIO_VOLUME = 128
|
|
FLAG_FIXED_VOLUME = 256
|
|
|
|
MODE_INVALID = -2
|
|
MODE_CURRENT = -1
|
|
MODE_NORMAL = 0
|
|
MODE_RINGTONE = 1
|
|
MODE_IN_CALL = 2
|
|
MODE_IN_COMMUNICATION = 3
|
|
|
|
DEVICE_OUT_EARPIECE = 0x1
|
|
DEVICE_OUT_SPEAKER = 0x2
|
|
DEVICE_OUT_WIRED_HEADSET = 0x4
|
|
DEVICE_OUT_WIRED_HEADPHONE = 0x8
|
|
DEVICE_OUT_BLUETOOTH_SCO = 0x10
|
|
DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20
|
|
DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40
|
|
DEVICE_OUT_BLUETOOTH_A2DP = 0x80
|
|
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100
|
|
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200
|
|
DEVICE_OUT_AUX_DIGITAL = 0x400
|
|
DEVICE_OUT_HDMI = 0x800
|
|
DEVICE_OUT_ANLG_DOCK_HEADSET = 0x1000
|
|
DEVICE_OUT_DGTL_DOCK_HEADSET = 0x2000
|
|
DEVICE_OUT_USB_ACCESSORY = 0x4000
|
|
DEVICE_OUT_USB_DEVICE = 0x8000
|
|
DEVICE_OUT_USB_HEADSET = 0x10000
|
|
DEVICE_OUT_LINE = 0x20000
|
|
DEVICE_OUT_HDMI_ARC = 0x40000
|
|
DEVICE_OUT_HDMI_EARC = 0x80000
|
|
DEVICE_OUT_BLE_HEADSET = 0x100000
|
|
DEVICE_OUT_BLE_SPEAKER = 0x200000
|
|
DEVICE_OUT_REMOTE_SUBMIX = 0x800000
|
|
|
|
DEVICE_IN_BUILTIN_MIC = 0x80000004
|
|
DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000010
|
|
DEVICE_IN_WIRED_HEADSET = 0x80000020
|
|
DEVICE_IN_HDMI = 0x80000040
|
|
DEVICE_IN_LINE = 0x80000080
|
|
DEVICE_IN_USB_ACCESSORY = 0x80000100
|
|
DEVICE_IN_USB_DEVICE = 0x80000200
|
|
DEVICE_IN_BLUETOOTH_A2DP = 0x80000400
|
|
DEVICE_IN_USB_HEADSET = 0x80000800
|
|
DEVICE_IN_BLE_HEADSET = 0x80001000
|
|
DEVICE_IN_REMOTE_SUBMIX = 0x80002000
|
|
|
|
FORMAT_DEFAULT = 0
|
|
FORMAT_PCM_16_BIT = 1
|
|
FORMAT_PCM_8_BIT = 2
|
|
FORMAT_PCM_FLOAT = 4
|
|
FORMAT_AC3 = 5
|
|
FORMAT_E_AC3 = 6
|
|
FORMAT_DTS = 7
|
|
FORMAT_DTS_HD = 8
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._volumes: Dict[int, int] = {
|
|
AudioManagerAudio.STREAM_MUSIC: 10,
|
|
AudioManagerAudio.STREAM_RING: 7,
|
|
AudioManagerAudio.STREAM_ALARM: 10,
|
|
AudioManagerAudio.STREAM_NOTIFICATION: 7,
|
|
AudioManagerAudio.STREAM_SYSTEM: 7,
|
|
AudioManagerAudio.STREAM_VOICE_CALL: 5,
|
|
}
|
|
self._muted: Dict[int, bool] = {}
|
|
self._mode = AudioManagerAudio.MODE_NORMAL
|
|
self._microphone_muted = False
|
|
self._speakerphone_on = False
|
|
self._master_volume = 1.0
|
|
self._master_mute = False
|
|
self._device_callbacks: List[AudioDeviceCallback] = []
|
|
self._devices: List[AudioDeviceInfo] = []
|
|
self._routed_devices: Dict[int, int] = {}
|
|
|
|
def getStreamVolume(self, streamType: int) -> int:
|
|
return self._volumes.get(streamType, 0)
|
|
|
|
def setStreamVolume(self, streamType: int, index: int, flags: int = 0):
|
|
self._volumes[streamType] = max(0, min(index, self.getStreamMaxVolume(streamType)))
|
|
|
|
def getStreamMaxVolume(self, streamType: int) -> int:
|
|
return 15
|
|
|
|
def getStreamMinVolume(self, streamType: int) -> int:
|
|
return 0
|
|
|
|
def isStreamMute(self, streamType: int) -> bool:
|
|
return self._muted.get(streamType, False)
|
|
|
|
def setStreamMute(self, streamType: int, state: bool):
|
|
self._muted[streamType] = state
|
|
|
|
def adjustVolume(self, direction: int, flags: int = 0):
|
|
pass
|
|
|
|
def adjustStreamVolume(self, streamType: int, direction: int, flags: int = 0):
|
|
current = self.getStreamVolume(streamType)
|
|
if direction == 1:
|
|
self.setStreamVolume(streamType, current + 1, flags)
|
|
elif direction == -1:
|
|
self.setStreamVolume(streamType, current - 1, flags)
|
|
|
|
def getMode(self) -> int:
|
|
return self._mode
|
|
|
|
def setMode(self, mode: int):
|
|
self._mode = mode
|
|
|
|
def isMicrophoneMute(self) -> bool:
|
|
return self._microphone_muted
|
|
|
|
def setMicrophoneMute(self, on: bool):
|
|
self._microphone_muted = on
|
|
|
|
def isSpeakerphoneOn(self) -> bool:
|
|
return self._speakerphone_on
|
|
|
|
def setSpeakerphoneOn(self, on: bool):
|
|
self._speakerphone_on = on
|
|
|
|
def getMasterVolume(self) -> float:
|
|
return self._master_volume
|
|
|
|
def setMasterVolume(self, volume: float):
|
|
self._master_volume = max(0.0, min(1.0, volume))
|
|
|
|
def isMasterMute(self) -> bool:
|
|
return self._master_mute
|
|
|
|
def setMasterMute(self, mute: bool):
|
|
self._master_mute = mute
|
|
|
|
def getDevices(self, flags: int) -> List[AudioDeviceInfo]:
|
|
return list(self._devices)
|
|
|
|
def setPreferredDeviceForStrategy(self, strategy: int, device: AudioDeviceInfo) -> bool:
|
|
self._routed_devices[strategy] = device.getId() if device else -1
|
|
return True
|
|
|
|
def registerAudioDeviceCallback(self, callback: AudioDeviceCallback, handler: Any = None):
|
|
self._device_callbacks.append(callback)
|
|
|
|
def unregisterAudioDeviceCallback(self, callback: AudioDeviceCallback):
|
|
self._device_callbacks = [cb for cb in self._device_callbacks if cb is not callback]
|
|
|
|
def getAudioPlaybackConfigurations(self) -> List[AudioPlaybackConfiguration]:
|
|
return []
|
|
|
|
def getAudioRecordingConfigurations(self) -> List[AudioRecordingConfiguration]:
|
|
return []
|
|
|
|
def generateAudioSessionId() -> int:
|
|
return int(time.time() * 1000) & 0x7FFFFFFF
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioManager(mode={self._mode}, masterVol={self._master_volume})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return id(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioMixPort / AudioPort / AudioPatch / AudioGain / AudioGainConfig
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioPort(Stub):
|
|
"""AudioPort — 音频端口"""
|
|
|
|
ROLE_NONE = 0
|
|
ROLE_SOURCE = 1
|
|
ROLE_SINK = 2
|
|
|
|
TYPE_NONE = 0
|
|
TYPE_DEVICE = 1
|
|
TYPE_SUBMIX = 2
|
|
TYPE_SESSION = 3
|
|
|
|
def __init__(self, id_: int = 0, name: str = "",
|
|
role: int = ROLE_NONE, type_: int = TYPE_NONE,
|
|
gains: List["AudioGain"] = None):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._name = name
|
|
self._role = role
|
|
self._type = type_
|
|
self._gains = gains or []
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getRole(self) -> int:
|
|
return self._role
|
|
|
|
def type(self) -> int:
|
|
return self._type
|
|
|
|
def gains(self) -> List["AudioGain"]:
|
|
return list(self._gains)
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioPort(id={self._id}, name={self._name})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioPort):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
class AudioMixPort(AudioPort):
|
|
"""AudioMixPort — 音频混合端口"""
|
|
|
|
def __init__(self, id_: int = 0, name: str = "",
|
|
role: int = AudioPort.ROLE_NONE,
|
|
gains: List["AudioGain"] = None):
|
|
super().__init__(id_=id_, name=name, role=role,
|
|
type_=AudioPort.TYPE_SUBMIX, gains=gains)
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioMixPort(id={self._id}, name={self._name})"
|
|
|
|
|
|
class AudioGain(Stub):
|
|
"""AudioGain — 音频增益"""
|
|
|
|
MODE_JOINT = 1
|
|
MODE_CHANNELS = 2
|
|
MODE_RAMP = 4
|
|
|
|
def __init__(self, id_: int = 0, mode: int = MODE_JOINT,
|
|
channelMask: int = 0, minValue: int = 0,
|
|
maxValue: int = 100, defaultValue: int = 50,
|
|
stepValue: int = 1, minRampMs: int = 0, maxRampMs: int = 0):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._mode = mode
|
|
self._channel_mask = channelMask
|
|
self._min_value = minValue
|
|
self._max_value = maxValue
|
|
self._default_value = defaultValue
|
|
self._step_value = stepValue
|
|
self._min_ramp_ms = minRampMs
|
|
self._max_ramp_ms = maxRampMs
|
|
|
|
def mode(self) -> int:
|
|
return self._mode
|
|
|
|
def channelMask(self) -> int:
|
|
return self._channel_mask
|
|
|
|
def minValue(self) -> int:
|
|
return self._min_value
|
|
|
|
def maxValue(self) -> int:
|
|
return self._max_value
|
|
|
|
def defaultValue(self) -> int:
|
|
return self._default_value
|
|
|
|
def stepValue(self) -> int:
|
|
return self._step_value
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioGain(id={self._id}, range=[{self._min_value}, {self._max_value}])"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioGain):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
class AudioGainConfig(Stub):
|
|
"""AudioGainConfig — 音频增益配置"""
|
|
|
|
def __init__(self, gain: Optional[AudioGain] = None,
|
|
mode: int = AudioGain.MODE_JOINT,
|
|
channelMask: int = 0,
|
|
values: List[int] = None,
|
|
rampDurationMs: int = 0):
|
|
super().__init__()
|
|
self._gain = gain
|
|
self._mode = mode
|
|
self._channel_mask = channelMask
|
|
self._values = values or []
|
|
self._ramp_duration_ms = rampDurationMs
|
|
|
|
def getGain(self) -> Optional[AudioGain]:
|
|
return self._gain
|
|
|
|
def getMode(self) -> int:
|
|
return self._mode
|
|
|
|
def getChannelMask(self) -> int:
|
|
return self._channel_mask
|
|
|
|
def getValues(self) -> List[int]:
|
|
return list(self._values)
|
|
|
|
def getRampDurationMs(self) -> int:
|
|
return self._ramp_duration_ms
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioGainConfig(mode={self._mode}, values={self._values})"
|
|
|
|
|
|
class AudioPatch(Stub):
|
|
"""AudioPatch — 音频补丁"""
|
|
|
|
def __init__(self, id_: int = 0, sources: List[AudioPort] = None,
|
|
sinks: List[AudioPort] = None):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._sources = sources or []
|
|
self._sinks = sinks or []
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getSources(self) -> List[AudioPort]:
|
|
return list(self._sources)
|
|
|
|
def getSinks(self) -> List[AudioPort]:
|
|
return list(self._sinks)
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioPatch(id={self._id}, sources={len(self._sources)}, sinks={len(self._sinks)})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioPatch):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioMix
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioMix(Stub):
|
|
"""AudioMix — 音频混合"""
|
|
|
|
ROUTE_FLAG_RENDER = 1
|
|
ROUTE_FLAG_LOOP_BACK = 2
|
|
ROUTE_FLAG_CAPTURE = 4
|
|
|
|
CALLBACK_FLAG_NOTIFY_ACTIVITY = 1
|
|
|
|
def __init__(self, routeFlags: int = ROUTE_FLAG_RENDER,
|
|
callbackFlags: int = 0,
|
|
format_: Optional[AudioFormat] = None,
|
|
deviceType: int = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER):
|
|
super().__init__()
|
|
self._route_flags = routeFlags
|
|
self._callback_flags = callbackFlags
|
|
self._format = format_ or AudioFormat()
|
|
self._device_type = deviceType
|
|
|
|
def getRouteFlags(self) -> int:
|
|
return self._route_flags
|
|
|
|
def getCallbackFlags(self) -> int:
|
|
return self._callback_flags
|
|
|
|
def getFormat(self) -> AudioFormat:
|
|
return self._format
|
|
|
|
def getDeviceType(self) -> int:
|
|
return self._device_type
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioMix(routeFlags={self._route_flags})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioMix):
|
|
return False
|
|
return self._route_flags == other._route_flags
|
|
|
|
def hashCode(self) -> int:
|
|
return self._route_flags & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioProfile
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioProfile(Stub):
|
|
"""AudioProfile — 音频配置文件"""
|
|
|
|
def __init__(self, format_: int = AudioFormat.ENCODING_DEFAULT,
|
|
sampleRates: List[int] = None,
|
|
channelMasks: List[int] = None,
|
|
channelIndexMasks: List[int] = None):
|
|
super().__init__()
|
|
self._format = format_
|
|
self._sample_rates = sampleRates or [44100, 48000]
|
|
self._channel_masks = channelMasks or [AudioFormat.CHANNEL_OUT_STEREO]
|
|
self._channel_index_masks = channelIndexMasks or []
|
|
|
|
def getFormat(self) -> int:
|
|
return self._format
|
|
|
|
def getSampleRates(self) -> List[int]:
|
|
return list(self._sample_rates)
|
|
|
|
def getChannelMasks(self) -> List[int]:
|
|
return list(self._channel_masks)
|
|
|
|
def getChannelIndexMasks(self) -> List[int]:
|
|
return list(self._channel_index_masks)
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioProfile(format={self._format})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioProfile):
|
|
return False
|
|
return self._format == other._format
|
|
|
|
def hashCode(self) -> int:
|
|
return self._format & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioProductStrategy
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioProductStrategy(Stub):
|
|
"""AudioProductStrategy — 音频产品策略"""
|
|
|
|
def __init__(self, id_: int = 0, name: str = "",
|
|
toleratedFormats: List[int] = None,
|
|
volumeGroup: Optional["AudioVolumeGroup"] = None):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._name = name
|
|
self._tolerated_formats = toleratedFormats or []
|
|
self._volume_group = volume_group
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getToleratedFormats(self) -> List[int]:
|
|
return list(self._tolerated_formats)
|
|
|
|
def getVolumeGroup(self) -> Optional["AudioVolumeGroup"]:
|
|
return self._volume_group
|
|
|
|
def setVolumeGroup(self, group: "AudioVolumeGroup"):
|
|
self._volume_group = group
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioProductStrategy(id={self._id}, name={self._name})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioProductStrategy):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# AudioVolumeGroup
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class AudioVolumeGroup(Stub):
|
|
"""AudioVolumeGroup — 音频音量组"""
|
|
|
|
def __init__(self, id_: int = 0, name: str = "",
|
|
volumeIndexMin: int = 0, volumeIndexMax: int = 15):
|
|
super().__init__()
|
|
self._id = id_
|
|
self._name = name
|
|
self._volume_index_min = volumeIndexMin
|
|
self._volume_index_max = volumeIndexMax
|
|
|
|
def getId(self) -> int:
|
|
return self._id
|
|
|
|
def getName(self) -> str:
|
|
return self._name
|
|
|
|
def getMinVolumeIndex(self) -> int:
|
|
return self._volume_index_min
|
|
|
|
def getMaxVolumeIndex(self) -> int:
|
|
return self._volume_index_max
|
|
|
|
def toString(self) -> str:
|
|
return f"AudioVolumeGroup(id={self._id}, name={self._name})"
|
|
|
|
def equals(self, other: Any) -> bool:
|
|
if not isinstance(other, AudioVolumeGroup):
|
|
return False
|
|
return self._id == other._id
|
|
|
|
def hashCode(self) -> int:
|
|
return self._id & 0x7FFFFFFF |