Sync upstream

This commit is contained in:
Mark Qvist
2025-11-27 15:09:56 +01:00
parent 68c2cf489c
commit 1d57d83517
4 changed files with 200 additions and 12 deletions

View File

@@ -0,0 +1,185 @@
# Adapted from Bastian Bechtold's soundcard library, originally released
# under the BSD 3-Clause License
#
# https://github.com/bastibe/SoundCard
#
# Copyright (c) 2016 Bastian Bechtold
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the
# distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Modifications and improvements Copyright 2025 Mark Qvist, and released
# under the same BSD 3-Clause License.
kAudioObjectSystemObject = 1
kAudioHardwarePropertyDevices = int.from_bytes(b'dev#', byteorder='big')
kAudioHardwarePropertyDefaultInputDevice = int.from_bytes(b'dIn ', byteorder='big')
kAudioHardwarePropertyDefaultOutputDevice = int.from_bytes(b'dOut', byteorder='big')
kAudioObjectPropertyScopeGlobal = int.from_bytes(b'glob', byteorder='big')
kAudioObjectPropertyScopeInput = int.from_bytes(b'inpt', byteorder='big')
kAudioObjectPropertyScopeOutput = int.from_bytes(b'outp', byteorder='big')
kAudioObjectPropertyScopePlayThrough = int.from_bytes(b'ptru', byteorder='big')
kAudioObjectPropertyName = int.from_bytes(b'lnam', byteorder='big')
kAudioObjectPropertyModelName = int.from_bytes(b'lmod', byteorder='big')
kAudioObjectPropertyManufacturer = int.from_bytes(b'lmak', byteorder='big')
kAudioDevicePropertyNominalSampleRate = int.from_bytes(b'nsrt', byteorder='big')
kAudioDevicePropertyBufferFrameSize = int.from_bytes(b'fsiz', byteorder='big')
kAudioDevicePropertyBufferFrameSizeRange = int.from_bytes(b'fsz#', byteorder='big')
kAudioDevicePropertyUsesVariableBufferFrameSizes = int.from_bytes(b'vfsz', byteorder='big')
kAudioDevicePropertyStreamConfiguration = int.from_bytes(b'slay', byteorder='big')
kCFStringEncodingUTF8 = 0x08000100
kAudioObjectPropertyElementMaster = 0
kAudioUnitType_Output = int.from_bytes(b'auou', byteorder='big')
kAudioUnitManufacturer_Apple = int.from_bytes(b'appl', byteorder='big')
kAudioUnitSubType_GenericOutput = int.from_bytes(b'genr', byteorder='big')
kAudioUnitSubType_HALOutput = int.from_bytes(b'ahal', byteorder='big')
kAudioUnitSubType_DefaultOutput = int.from_bytes(b'def ', byteorder='big')
# The audio unit can do input from the device as well as output to the
# device. Bus 0 is used for the output side, bus 1 is used to get audio
# input from the device.
outputbus = 0
inputbus = 1
def error_number_to_string(num):
if num == kAudioUnitErr_InvalidProperty:
return "The property is not supported"
elif num == kAudioUnitErr_InvalidParameter:
return "The parameter is not supported"
elif num == kAudioUnitErr_InvalidElement:
return "The specified element is not valid"
elif num == kAudioUnitErr_NoConnection:
return "There is no connection (generally an audio unit is asked to render but it has" \
" not input from which to gather data)"
elif num == kAudioUnitErr_FailedInitialization:
return "The audio unit is unable to be initialized"
elif num == kAudioUnitErr_TooManyFramesToProcess:
return "When an audio unit is initialized it has a value which specifies the max" \
" number of frames it will be asked to render at any given time. If an audio" \
" unit is asked to render more than this, this error is returned."
elif num == kAudioUnitErr_InvalidFile:
return "If an audio unit uses external files as a data source, this error is returned" \
" if a file is invalid (Apple's DLS synth returns this error)"
elif num == kAudioUnitErr_UnknownFileType:
return "If an audio unit uses external files as a data source, this error is returned" \
" if a file is invalid (Apple's DLS synth returns this error)"
elif num == kAudioUnitErr_FileNotSpecified:
return "If an audio unit uses external files as a data source, this error is returned" \
" if a file hasn't been set on it (Apple's DLS synth returns this error)"
elif num == kAudioUnitErr_FormatNotSupported:
return "Returned if an input or output format is not supported"
elif num == kAudioUnitErr_Uninitialized:
return "Returned if an operation requires an audio unit to be initialized and it is not."
elif num == kAudioUnitErr_InvalidScope:
return "The specified scope is invalid"
elif num == kAudioUnitErr_PropertyNotWritable:
return "The property cannot be written"
elif num == kAudioUnitErr_CannotDoInCurrentContext:
return "Returned when an audio unit is in a state where it can't perform the requested" \
" action now - but it could later. Its usually used to guard a render operation" \
" when a reconfiguration of its internal state is being performed."
elif num == kAudioUnitErr_InvalidPropertyValue:
return "The property is valid, but the value of the property being provided is not"
elif num == kAudioUnitErr_PropertyNotInUse:
return "Returned when a property is valid, but it hasn't been set to a valid value at this time."
elif num == kAudioUnitErr_Initialized:
return "Indicates the operation cannot be performed because the audio unit is initialized."
elif num == kAudioUnitErr_InvalidOfflineRender:
return "Used to indicate that the offline render operation is invalid. For instance," \
" when the audio unit needs to be pre-flighted, but it hasn't been."
elif num == kAudioUnitErr_Unauthorized:
return "Returned by either Open or Initialize, this error is used to indicate that the" \
" audio unit is not authorised, that it cannot be used. A host can then present" \
" a UI to notify the user the audio unit is not able to be used in its current state."
elif num == kAudioComponentErr_InstanceInvalidated:
return "the component instance's implementation is not available, most likely because the process" \
" that published it is no longer running"
else:
return "error number {}".format(num)
kAudioUnitErr_InvalidProperty = -10879
kAudioUnitErr_InvalidParameter = -10878
kAudioUnitErr_InvalidElement = -10877
kAudioUnitErr_NoConnection = -10876
kAudioUnitErr_FailedInitialization = -10875
kAudioUnitErr_TooManyFramesToProcess = -10874
kAudioUnitErr_InvalidFile = -10871
kAudioUnitErr_UnknownFileType = -10870
kAudioUnitErr_FileNotSpecified = -10869
kAudioUnitErr_FormatNotSupported = -10868
kAudioUnitErr_Uninitialized = -10867
kAudioUnitErr_InvalidScope = -10866
kAudioUnitErr_PropertyNotWritable = -10865
kAudioUnitErr_CannotDoInCurrentContext = -10863
kAudioUnitErr_InvalidPropertyValue = -10851
kAudioUnitErr_PropertyNotInUse = -10850
kAudioUnitErr_Initialized = -10849
kAudioUnitErr_InvalidOfflineRender = -10848
kAudioUnitErr_Unauthorized = -10847
kAudioComponentErr_InstanceInvalidated = -66749
kAudioUnitErr_RenderTimeout = -66745
kAudioOutputUnitProperty_CurrentDevice = 2000
kAudioOutputUnitProperty_EnableIO = 2003 # scope output, element 0 == output,
kAudioOutputUnitProperty_HasIO = 2006 # scope input, element 1 == input
kAudioOutputUnitProperty_IsRunning = 2001
kAudioOutputUnitProperty_ChannelMap = 2002
kAudioFormatLinearPCM = int.from_bytes(b'lpcm', byteorder='big')
kAudioFormatFlagIsFloat = 0x1
kAudioUnitProperty_StreamFormat = 8
kAudioUnitProperty_CPULoad = 6
kAudioUnitProperty_Latency = 12
kAudioUnitProperty_SupportedNumChannels = 13
kAudioUnitProperty_MaximumFramesPerSlice = 14
kAudioUnitProperty_SetRenderCallback = 23
kAudioOutputUnitProperty_SetInputCallback = 2005
kAudioUnitProperty_StreamFormat = 8
kAudioUnitProperty_SampleRate = 2
kAudioUnitProperty_ContextName = 25
kAudioUnitProperty_ElementName = 30
kAudioUnitProperty_NickName = 54
kAudioUnitRenderAction_PreRender = 1 << 2
kAudioUnitRenderAction_PostRender = 1 << 3
kAudioUnitRenderAction_OutputIsSilence = 1 << 4
kAudioOfflineUnitRenderAction_Preflight = 1 << 5
kAudioOfflineUnitRenderAction_Render = 1 << 6
kAudioOfflineUnitRenderAction_Complete = 1 << 7
kAudioUnitRenderAction_PostRenderError = 1 << 8
kAudioUnitRenderAction_DoNotCheckRenderArgs = 1 << 9
kAudioUnitScope_Global = 0 # The context for audio unit characteristics that apply to the audio unit as a whole
kAudioUnitScope_Input = 1 # The context for audio data coming into an audio unit
kAudioUnitScope_Output = 2 # The context for audio data leaving an audio unit

View File

@@ -55,7 +55,7 @@ with open(os.path.join(_package_dir, 'coreaudio.h'), 'rt') as f:
_ca = _ffi.dlopen('CoreAudio')
_au = _ffi.dlopen('AudioUnit')
from soundcard import coreaudioconstants as _cac
import LXST.Platforms.darwin.coreaudioconstants as _cac
def all_speakers():

View File

@@ -581,11 +581,12 @@ class Telephone(SignallingReceiver):
self.audio_input.stop()
self.transmit_mixer.stop()
self.transmit_pipeline.stop()
self.transmit_mixer = Mixer(target_frame_ms=self.target_frame_time_ms, gain=self.transmit_gain)
self.audio_input = LineSource(preferred_device=self.microphone_device, target_frame_ms=self.target_frame_time_ms, codec=Raw(), sink=self.transmit_mixer, filters=self.active_call.filters)
self.transmit_pipeline = Pipeline(source=self.transmit_mixer,
codec=self.transmit_codec,
sink=self.active_call.packetizer)
self.transmit_mixer = Mixer(target_frame_ms=self.target_frame_time_ms, gain=self.transmit_gain)
self.audio_input = LineSource(preferred_device=self.microphone_device, target_frame_ms=self.target_frame_time_ms, codec=Raw(),
sink=self.transmit_mixer, filters=self.active_call.filters, skip=0.075, ease_in=0.0)
self.transmit_pipeline = Pipeline(source=self.transmit_mixer, codec=self.transmit_codec, sink=self.active_call.packetizer)
self.transmit_mixer.mute(self.__transmit_muted)
self.transmit_mixer.start()
@@ -609,11 +610,13 @@ class Telephone(SignallingReceiver):
self.__prepare_dialling_pipelines()
self.active_call.packetizer = Packetizer(self.active_call, failure_callback=self.__packetizer_failure)
self.transmit_mixer = Mixer(target_frame_ms=self.target_frame_time_ms, gain=self.transmit_gain)
self.audio_input = LineSource(preferred_device=self.microphone_device, target_frame_ms=self.target_frame_time_ms, codec=Raw(), sink=self.transmit_mixer, filters=self.active_call.filters)
self.transmit_pipeline = Pipeline(source=self.transmit_mixer,
codec=self.transmit_codec,
sink=self.active_call.packetizer)
self.transmit_mixer = Mixer(target_frame_ms=self.target_frame_time_ms, gain=self.transmit_gain)
self.audio_input = LineSource(preferred_device=self.microphone_device, target_frame_ms=self.target_frame_time_ms, codec=Raw(),
sink=self.transmit_mixer, filters=self.active_call.filters, skip=0.075, ease_in=0.225)
self.transmit_pipeline = Pipeline(source=self.transmit_mixer, codec=self.transmit_codec, sink=self.active_call.packetizer)
self.active_call.audio_source = LinkSource(link=self.active_call, signalling_receiver=self, sink=self.receive_mixer)
self.receive_mixer.set_source_max_frames(self.active_call.audio_source, 2)

View File

@@ -1 +1 @@
__version__ = "0.4.4"
__version__ = "0.4.5"