""" 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