Thanks Quinn the Eskimo!
I don't like that quite as much, because then all logs from my lib will be at the same level (the one passed in by the user of the lib).
The something-is-very-broken logs would be drowned out in a sea of debug logs. For example, there are debug logs that emit on each audio buffer coming from the mic, containing the sample count in the buffer. These are frequent and only meant to be 'turned on' when I'm trying to debug an issue.
I'll share what I ended up doing. It makes log lines a little cumbersome for me, but thankfully users of my lib don't have to deal with that.
import OSLog
public enum AIProxyLogLevel: Int {
case debug
case info
case warning
case error
case critical
func isAtOrAboveThresholdLevel(_ threshold: AIProxyLogLevel) -> Bool {
return self.rawValue >= threshold.rawValue
}
}
internal var aiproxyCallerDesiredLogLevel = AIProxyLogLevel.warning
internal let aiproxyLogger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? "UnknownApp",
category: "AIProxy"
)
// Why not create a wrapper around OSLog instead of forcing log callsites to include an `if ll(<level>)` check?
// Because I like the Xcode log feature that links to the source location of the log.
// If you create a wrapper, even one that is inlined, the Xcode source feature always links to the wrapper location.
@inline(__always)
internal func ll(_ logLevel: AIProxyLogLevel) -> Bool {
return logLevel.isAtOrAboveThresholdLevel(aiproxyCallerDesiredLogLevel)
}
And then my lib adds log lines like so:
if ll(.warning) { aiproxyLogger.warning("this is a warning log") }
if ll(.debug) { aiproxyLogger.debug("this is a debug log") }
...
And I expose a simple interface to the user of the lib that internally sets aiproxyCallerDesiredLogLevel.
It's a little unorthodox, but it's getting the job done for me, and Xcode's link-to-log-source functionality still works.
Big fan of your work. Every time I stumble on a great forum or technical note thread it always has your name on it.
ps. long live macnetworkprog!
Lou
Post
Replies
Boosts
Views
Activity
I am also seeing this with the AVAudioEngine APIs.
@oliverpahl-mitel did you ever hear back from Apple on your feedback assistant post?
At runtime here are the errors that get dumped to console (I get the KeystrokeSuppressorCore.cpp:44 ERROR, but I don't see the AggInpStreamsChanged wait failed you are getting).
If I setup an input node with inputNode.setVoiceProcessingEnabled(true), here are the errors that get dumped to console:
AddInstanceForFactory: No factory registered for id <CFUUID 0x60000300f780> F8BB1C28-BAE8-11D6-9C31-00039315CD46
throwing -10877
throwing -10877
vpPlatformUtil.mm:312 Cannot retrieve theDeviceBoardID string...
vpPlatformUtil.mm:312 Cannot retrieve theDeviceBoardID string...
AudioHardware-mac-imp.cpp:409 AudioObjectHasProperty: no object with given ID 0
AUVPAggregate.cpp:4929 Failed to get current tap stream physical format, err=2003332927
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
KeystrokeSuppressorCore.cpp:44 ERROR: KeystrokeSuppressor initialization was unsuccessful. Invalid or no plist was provided. AU will be bypassed.
vpSetupUplinkDSPChain.cpp:124 >vp> skip reference mixer setup because reference channel count is 0
vpStrategyManager.mm:486 Error code 2003332927 reported at GetPropertyInfo
HALB_IOThread.cpp:326 HALB_IOThread::_Start: there already is a thread
HALB_IOThread.cpp:326 HALB_IOThread::_Start: there already is a thread
For reference, if I switch inputNode.setVoiceProcessingEnabled(false) and keep all of my surrounding code the same, the following messages are dumped to console:
AddInstanceForFactory: No factory registered for id <CFUUID 0x600003ff2500> F8BB1C28-BAE8-11D6-9C31-00039315CD46
throwing -10877
throwing -10877
One more thing, I took this bit from some tensorflow source [1] code AVAudioFrameCount(outputFormat.sampleRate * 2.0), but I do not think that is a correct computation and have since removed it.
[1] https://sourcegraph.com/github.com/tensorflow/examples/-/blob/lite/examples/speech_commands/ios/SpeechCommands/AudioInputManager/AudioInputManager.swift?L89
I just found something interesting. While AVAudioConverter doesn't play nicely with the five channels, it seems like AVAudioEngine's built in converters do. Because if I specify a tap like this:
let desiredTapFormat = AVAudioFormat(
commonFormat: .pcmFormatInt16,
sampleRate: inputPCMFormat.sampleRate,
channels: 1,
interleaved: false
)
inputNode.installTap(onBus: 0, bufferSize: 256, format: desiredTapFormat) { buffer, when in ... }
I find that the buffer argument already has a single channel, and it's not silence!
(Post replies, not comments.)
Thanks for the tip.
No, what I mean is, if I, an attacker, have my own trivial app, and I collect tokens that my users send to my server (no MITM needed). Then I send those tokens to your app's API.
These would return non-200s from Apple's servers when the token was validated in the server-to-server call. It's actually quite tricky to get DeviceCheck to pass in the first place. A bunch of things need to be true:
An App Identifier in your developer dashboard must match the bundle identifier of your app exactly
The app must be signed using a certificate from the team account that has that App Identifier
A DeviceCheck secret key (used for the backend-to-backend communication) must be created from the same account as 1 and 2.
The fact that it's hard to get right gave me some confidence that it was also hard to forge. Now, I'm not so sure
The traffic image didn't come through in the original post. Here it is.