pyglet bump

This commit is contained in:
shenjack 2023-01-27 20:28:43 +08:00
parent 4fa8619f69
commit 9dcf407ff7
5 changed files with 372 additions and 9 deletions

View File

@ -19,6 +19,9 @@ kCFStringEncodingUTF8 = 0x08000100
CFAllocatorRef = c_void_p
CFStringEncoding = c_uint32
CFURLRef = c_void_p
CFStringRef = c_void_p
CFURLPathStyle = c_int
cf.CFStringCreateWithCString.restype = c_void_p
cf.CFStringCreateWithCString.argtypes = [CFAllocatorRef, c_char_p, CFStringEncoding]
@ -97,6 +100,10 @@ cf.CFNumberGetTypeID.argtypes = []
cf.CFGetTypeID.restype = CFTypeID
cf.CFGetTypeID.argtypes = [c_void_p]
cf.CFURLCreateWithFileSystemPath.restype = CFURLRef
cf.CFURLCreateWithFileSystemPath.argtypes = [CFAllocatorRef, CFStringRef, CFURLPathStyle, c_bool]
# CFNumber.h
kCFNumberSInt8Type = 1
kCFNumberSInt16Type = 2

View File

@ -0,0 +1,172 @@
import ctypes
from ctypes import c_void_p, c_int, c_bool, Structure, c_uint32, util, cdll, c_uint, c_double, POINTER, c_int64
from pyglet.libs.darwin import CFURLRef
lib = util.find_library('CoreAudio')
if lib is None:
lib = '/System/Library/Frameworks/CoreAudio.framework/CoreAudio'
ca = cdll.LoadLibrary(lib)
class AudioStreamPacketDescription(Structure):
_fields_ = [
('mStartOffset', c_int64),
('mVariableFramesInPacket', c_uint32),
('mDataByteSize', c_uint32)
]
class AudioStreamBasicDescription(Structure):
_fields_ = [
('mSampleRate', c_double),
('mFormatID', c_uint32),
('mFormatFlags', c_uint32),
('mBytesPerPacket', c_uint32),
('mFramesPerPacket', c_uint32),
('mBytesPerFrame', c_uint32),
('mChannelsPerFrame', c_uint32),
('mBitsPerChannel', c_uint32),
('mReserved', c_uint32)
]
def __repr__(self):
return f"AudioStreamBasicDescription(sample_rate={self.mSampleRate}, channels={self.mChannelsPerFrame}, " \
f"fmt={self.mFormatID}, bytes_per_packet={self.mBytesPerPacket}, bits={self.mBitsPerChannel}, " \
f"frames_per_packet={self.mFramesPerPacket}, bytes_per_frame={self.mBytesPerFrame})"
class AudioBuffer(Structure):
_fields_ = [
("mNumberChannels", c_uint),
("mDataByteSize", c_uint),
("mData", c_void_p),
]
class AudioBufferList(Structure):
_fields_ = [
("mNumberBuffers", c_uint),
("mBuffers", AudioBuffer * 1),
]
kCFURLPOSIXPathStyle = 0
ExtAudioFilePropertyID = c_uint32
OSStatus = c_int
ExtAudioFileRef = c_void_p
ca.ExtAudioFileOpenURL.restype = c_int
ca.ExtAudioFileOpenURL.argtypes = [CFURLRef, ExtAudioFileRef]
ca.ExtAudioFileGetProperty.restype = c_int
ca.ExtAudioFileGetProperty.argtypes = [ExtAudioFileRef, ExtAudioFilePropertyID, POINTER(c_uint32), c_void_p]
ca.ExtAudioFileSetProperty.restype = c_int
ca.ExtAudioFileSetProperty.argtypes = [ExtAudioFileRef, ExtAudioFilePropertyID, c_uint32, c_void_p]
ca.ExtAudioFileOpenURL.restype = OSStatus
ca.ExtAudioFileOpenURL.argtypes = [CFURLRef, ExtAudioFileRef]
AudioFileTypeID = c_uint32
AudioFileID = c_void_p
AudioFile_ReadProc = ctypes.CFUNCTYPE(c_int, c_void_p, ctypes.c_int64, c_uint32, c_void_p, POINTER(c_uint32))
AudioFile_GetSizeProc = ctypes.CFUNCTYPE(ctypes.c_int64, c_void_p)
ca.AudioFileOpenWithCallbacks.restype = OSStatus
ca.AudioFileOpenWithCallbacks.argtypes = [c_void_p, AudioFile_ReadProc, c_void_p, AudioFile_GetSizeProc, c_void_p,
AudioFileTypeID, POINTER(AudioFileID)]
ca.ExtAudioFileWrapAudioFileID.restype = OSStatus
ca.ExtAudioFileWrapAudioFileID.argtypes = [AudioFileID, c_bool, POINTER(ExtAudioFileRef)]
ca.ExtAudioFileRead.restype = OSStatus
ca.ExtAudioFileRead.argtypes = [ExtAudioFileRef, POINTER(c_uint32), POINTER(AudioBufferList)]
ca.ExtAudioFileSeek.restype = OSStatus
ca.ExtAudioFileSeek.argtypes = [ExtAudioFileRef, c_int64]
ca.ExtAudioFileDispose.restype = OSStatus
ca.ExtAudioFileDispose.argtypes = [ExtAudioFileRef]
ca.AudioFileClose.restype = OSStatus
ca.AudioFileClose.argtypes = [AudioFileID]
kCFAllocatorDefault = None
def c_literal(literal):
"""Example 'xyz' -> 7895418.
Used for some CoreAudio constants."""
num = 0
for idx, char in enumerate(literal):
num |= ord(char) << (len(literal) - idx - 1) * 8
return num
kAudioFilePropertyMagicCookieData = c_literal('mgic')
kExtAudioFileProperty_FileDataFormat = c_literal('ffmt')
kExtAudioFileProperty_ClientDataFormat = c_literal('cfmt')
kExtAudioFileProperty_FileLengthFrames = c_literal('#frm')
kAudioFormatLinearPCM = c_literal('lpcm')
kAudioFormatFlagIsFloat = 1 << 0
kAudioFormatFlagIsBigEndian = 1 << 1
kAudioFormatFlagIsSignedInteger = 1 << 2
kAudioFormatFlagIsPacked = 1 << 3
kAudioFormatFlagsNativeEndian = 0
kAudioFormatFlagsCanonical = kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked
kAudioQueueProperty_MagicCookie = c_literal('aqmc')
# ERRORS:
kAudio_UnimplementedError = -4
kAudio_FileNotFoundError = -43
kAudio_ParamError = -50
kAudio_MemFullError = -108
kAudioFileUnspecifiedError = c_literal('wht?') # 0x7768743F, 2003334207
kAudioFileUnsupportedFileTypeError = c_literal('typ?') # 0x7479703F, 1954115647
kAudioFileUnsupportedDataFormatError = c_literal('fmt?') # 0x666D743F, 1718449215
kAudioFileUnsupportedPropertyError = c_literal('pty?') # 0x7074793F, 1886681407
kAudioFileBadPropertySizeError = c_literal('!siz') # 0x2173697A, 561211770
kAudioFilePermissionsError = c_literal('prm?') # 0x70726D3F, 1886547263
kAudioFileNotOptimizedError = c_literal('optm') # 0x6F70746D, 1869640813
# file format specific error codes
kAudioFileInvalidChunkError = c_literal('chk?') # 0x63686B3F, 1667787583
kAudioFileDoesNotAllow64BitDataSizeError = c_literal('off?') # 0x6F66663F, 1868981823
kAudioFileInvalidPacketOffsetError = c_literal('pck?') # 0x70636B3F, 1885563711
kAudioFileInvalidFileError = c_literal('dta?') # 0x6474613F, 1685348671
kAudioFileOperationNotSupportedError = c_literal('op?') # 0x6F703F3F
# general file error codes
kAudioFileNotOpenError = -38
kAudioFileEndOfFileError = -39
kAudioFilePositionError = -40
kAudioFileFileNotFoundError = -43
err_str_db = {
kAudioFileUnspecifiedError: "An unspecified error has occurred.",
kAudioFileUnsupportedFileTypeError: "The file type is not supported.",
kAudioFileUnsupportedDataFormatError: "The data format is not supported by this file type.",
kAudioFileUnsupportedPropertyError: "The property is not supported.",
kAudioFileBadPropertySizeError: "The size of the property data was not correct.",
kAudioFilePermissionsError: "The operation violated the file permissions.",
kAudioFileNotOptimizedError: "The chunks following the audio data chunk are preventing the extension of the audio data chunk. To write more data, you must optimize the file.",
kAudioFileInvalidChunkError: "Either the chunk does not exist in the file or it is not supported by the file.",
kAudioFileDoesNotAllow64BitDataSizeError: "The file offset was too large for the file type. The AIFF and WAVE file format types have 32-bit file size limits.",
kAudioFileInvalidPacketOffsetError: "A packet offset was past the end of the file, or not at the end of the file when a VBR format was written, or a corrupt packet size was read when the packet table was built.",
kAudioFileInvalidFileError: "The file is malformed, or otherwise not a valid instance of an audio file of its type.",
kAudioFileOperationNotSupportedError: "The operation cannot be performed.",
kAudioFileNotOpenError: "The file is closed.",
kAudioFileEndOfFileError: "End of file.",
kAudioFilePositionError: "Invalid file position.",
kAudioFileFileNotFoundError: "File not found.",
}
def err_check(err):
if err != 0:
raise Exception(err, err_str_db.get(err, "Unknown Error"))

View File

@ -0,0 +1,182 @@
from ctypes import memmove, byref, c_uint32, sizeof, cast, c_void_p, create_string_buffer, POINTER, c_char, \
c_long
from pyglet.libs.darwin import cf, CFSTR
from pyglet.libs.darwin.coreaudio import kCFURLPOSIXPathStyle, AudioStreamBasicDescription, ca, ExtAudioFileRef, \
kExtAudioFileProperty_FileDataFormat, kAudioFormatLinearPCM, kAudioFormatFlagIsSignedInteger, \
kAudioFormatFlagIsPacked, kExtAudioFileProperty_ClientDataFormat, AudioFile_ReadProc, \
AudioFile_GetSizeProc, AudioBufferList, kExtAudioFileProperty_FileLengthFrames, AudioFileID, err_check
from pyglet.media import StreamingSource, StaticSource
from pyglet.media.codecs import AudioFormat, MediaDecoder, AudioData
class MemoryFileObject:
def __init__(self, file):
self.file = file
if not getattr(self.file, 'seek', None) or not getattr(self.file, 'tell', None):
raise Exception("File object does not support seeking.")
# Seek to end of file to get the filesize.
self.file.seek(0, 2)
self.file_size = self.file.tell()
self.file.seek(0) # Put cursor back at the beginning.
self.data = []
def read_data_cb(ref, offset, requested_length, buffer, actual_count):
self.file.seek(offset)
data = self.file.read(requested_length)
data_size = len(data)
memmove(buffer, data, data_size)
actual_count.contents.value = data_size # Actual read.
return 0
def getsize_cb(ref):
return self.file_size
self.getsize_func = AudioFile_GetSizeProc(getsize_cb)
self.read_func = AudioFile_ReadProc(read_data_cb)
class CoreAudioSource(StreamingSource):
def __init__(self, filename, file=None):
self._bl = None
self._file = file
self._deleted = False
self._file_obj = None
self._audfile = None
self._audref = None
audref = ExtAudioFileRef()
if file is None:
fn_str = CFSTR(filename)
url_ref = cf.CFURLCreateWithFileSystemPath(None, fn_str, kCFURLPOSIXPathStyle, False)
err_check(ca.ExtAudioFileOpenURL(url_ref, byref(audref)))
else:
self.file_obj = MemoryFileObject(file)
self._audfile = AudioFileID()
err_check(ca.AudioFileOpenWithCallbacks(
None, self.file_obj.read_func, None, self.file_obj.getsize_func, None,
0,
byref(self._audfile))
)
err_check(ca.ExtAudioFileWrapAudioFileID(self._audfile, False, byref(audref)))
self._audref = audref
format_info = AudioStreamBasicDescription()
size = c_uint32(sizeof(format_info))
err_check(ca.ExtAudioFileGetProperty(self._audref,
kExtAudioFileProperty_FileDataFormat,
byref(size),
byref(format_info)))
self.convert_desc = self.convert_format(format_info)
err_check(ca.ExtAudioFileSetProperty(
self._audref,
kExtAudioFileProperty_ClientDataFormat,
sizeof(self.convert_desc),
byref(self.convert_desc)
))
length = c_long()
size = c_uint32(sizeof(format_info))
# File length.
err_check(ca.ExtAudioFileGetProperty(
self._audref,
kExtAudioFileProperty_FileLengthFrames,
byref(size),
byref(length)
))
self.audio_format = AudioFormat(channels=self.convert_desc.mChannelsPerFrame,
sample_size=self.convert_desc.mBitsPerChannel,
sample_rate=int(self.convert_desc.mSampleRate))
self._num_frames = length.value
self._bytes_per_frame = self.convert_desc.mBytesPerFrame
self._duration = self._num_frames / self.convert_desc.mSampleRate
self._duration_per_frame = self._duration / self._num_frames
@staticmethod
def convert_format(original_desc, bitdepth=16):
adesc = AudioStreamBasicDescription()
adesc.mSampleRate = original_desc.mSampleRate
adesc.mFormatID = kAudioFormatLinearPCM
adesc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
adesc.mChannelsPerFrame = original_desc.mChannelsPerFrame
adesc.mBitsPerChannel = bitdepth
adesc.mBytesPerPacket = original_desc.mChannelsPerFrame * adesc.mBitsPerChannel // 8
adesc.mFramesPerPacket = 1
adesc.mBytesPerFrame = adesc.mBytesPerPacket
return adesc
def __del__(self):
if self._file:
self._file.close()
self._file = None
if self._audfile:
err_check(ca.AudioFileClose(self._audfile))
self._audfile = None
if self._audref:
err_check(ca.ExtAudioFileDispose(self._audref))
self._audref = None
def get_audio_data(self, num_bytes, compensation_time=0.0):
num_frames = c_uint32(num_bytes // self.convert_desc.mBytesPerFrame)
if not self._bl:
buffer = create_string_buffer(num_bytes)
self._bl = AudioBufferList()
self._bl.mNumberBuffers = 1
self._bl.mBuffers[0].mNumberChannels = self.convert_desc.mChannelsPerFrame
self._bl.mBuffers[0].mDataByteSize = num_bytes
self._bl.mBuffers[0].mData = cast(buffer, c_void_p)
while True:
ca.ExtAudioFileRead(self._audref, byref(num_frames), byref(self._bl))
size = self._bl.mBuffers[0].mDataByteSize
if not size:
break
data = cast(self._bl.mBuffers[0].mData, POINTER(c_char))
slice = data[:size]
return AudioData(slice, size, 0.0, 0.0, [])
def seek(self, timestamp):
timestamp = max(0.0, min(timestamp, self._duration))
position = int(timestamp / self._duration_per_frame)
ca.ExtAudioFileSeek(self._audref, position)
#########################################
# Decoder class:
#########################################
class CoreAudioDecoder(MediaDecoder):
def get_file_extensions(self):
return '.aac', '.ac3', '.aif', '.aiff', '.aifc', '.caf', '.mp3', '.mp4', '.m4a', '.snd', '.au', '.sd2', '.wav'
def decode(self, filename, file, streaming=True):
if streaming:
return CoreAudioSource(filename, file)
else:
return StaticSource(CoreAudioSource(filename, file))
def get_decoders():
return [CoreAudioDecoder()]
def get_encoders():
return []

View File

@ -13,7 +13,7 @@ _debug = debug_print('debug_media')
avutil = pyglet.lib.load_library(
'avutil',
win32=('avutil-57', 'avutil-56'),
darwin=('avutil.57', 'avutil-56')
darwin=('avutil.57', 'avutil.56')
)
avutil.avutil_version.restype = c_int

View File

@ -34,12 +34,14 @@ def get_audio_driver():
_audio_driver = pulse.create_audio_driver()
break
elif driver_name == 'xaudio2':
if pyglet.compat_platform in ('win32', 'cygwin'):
from pyglet.libs.win32.constants import WINDOWS_8_OR_GREATER
if WINDOWS_8_OR_GREATER:
from . import xaudio2
_audio_driver = xaudio2.create_audio_driver()
break
elif driver_name == 'directsound':
if pyglet.compat_platform in ('win32', 'cygwin'):
from . import directsound
_audio_driver = directsound.create_audio_driver()
break