骨传导调试
authorliangzhenying <867174124@qq.com>
Mon, 15 Dec 2025 09:14:58 +0000 (17:14 +0800)
committerliangzhenying <867174124@qq.com>
Mon, 15 Dec 2025 09:14:58 +0000 (17:14 +0800)
DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.m [new file with mode: 0644]

diff --git a/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.m b/DeepChordsSdk_iOS/DeepChordsSdk/Example/Example/VC/PcmSound.m
new file mode 100644 (file)
index 0000000..c3e509a
--- /dev/null
@@ -0,0 +1,267 @@
+#import "PcmSound.h"
+#import <AudioToolbox/AudioToolbox.h>
+
+#if TARGET_OS_IOS
+#import <AVFoundation/AVFoundation.h>
+#endif
+
+#define kOutputBus 0
+#define NAMESPACE @"pcm_sound" // Assuming this is the namespace you want
+
+typedef NS_ENUM(NSUInteger, LogLevel) {
+    none = 0,
+    error = 1,
+    standard = 2,
+    verbose = 3,
+};
+
+@interface PcmSound ()
+@property(nonatomic) LogLevel mLogLevel;
+@property(nonatomic) AudioComponentInstance mAudioUnit;
+@property(nonatomic) NSMutableData *mSamples;
+@property(nonatomic) int mNumChannels; 
+@property(nonatomic) int mFeedThreshold; 
+@property(nonatomic) bool mDidInvokeFeedCallback; 
+@end
+
+@implementation PcmSound
+
++ (instancetype)sharedInstance {
+    static PcmSound *sharedInstance = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        sharedInstance = [[self alloc] init];
+        sharedInstance.mLogLevel = verbose;
+        sharedInstance.mSamples = [NSMutableData new];
+        sharedInstance.mFeedThreshold = 0;
+        sharedInstance.mDidInvokeFeedCallback = false;
+    });
+    return sharedInstance;
+}
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+
+    }
+    return self;
+}
+
+-(void)dealloc {
+    
+}
+
+-(void)setLogLevel:(NSNumber*)logLevelNumber {
+    self.mLogLevel = (LogLevel)[logLevelNumber integerValue];
+}
+
+-(void)setup:(int)sampleRate withchannels:(int)numChannels {
+    
+    self.mNumChannels = numChannels;
+
+    // cleanup
+    if (_mAudioUnit != nil) {
+        [self cleanup];
+    }
+
+    // create
+    AudioComponentDescription desc;
+    desc.componentType = kAudioUnitType_Output;
+#if TARGET_OS_IOS
+    desc.componentSubType = kAudioUnitSubType_RemoteIO;
+#else // MacOS
+    desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+#endif
+    desc.componentFlags = 0;
+    desc.componentFlagsMask = 0;
+    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+
+    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
+    OSStatus status = AudioComponentInstanceNew(inputComponent, &_mAudioUnit);
+    if (status != noErr) {
+        //NSString* message = [NSString stringWithFormat:@"AudioComponentInstanceNew failed. OSStatus: %@", @(status)];
+        return;
+    }
+
+    // set stream format
+    AudioStreamBasicDescription audioFormat;
+    audioFormat.mSampleRate = sampleRate;
+    audioFormat.mFormatID = kAudioFormatLinearPCM;
+    audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+    audioFormat.mFramesPerPacket = 1;
+    audioFormat.mChannelsPerFrame = self.mNumChannels;
+    audioFormat.mBitsPerChannel = 16;
+    audioFormat.mBytesPerFrame = self.mNumChannels * (audioFormat.mBitsPerChannel / 8);
+    audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
+
+    status = AudioUnitSetProperty(_mAudioUnit,
+                            kAudioUnitProperty_StreamFormat,
+                            kAudioUnitScope_Input,
+                            kOutputBus,
+                            &audioFormat,
+                            sizeof(audioFormat));
+    if (status != noErr) {
+        //NSString* message = [NSString stringWithFormat:@"AudioUnitSetProperty StreamFormat failed. OSStatus: %@", @(status)];
+        return;
+    }
+
+    // set callback
+    AURenderCallbackStruct callback;
+    callback.inputProc = RenderCallback;
+    callback.inputProcRefCon = (__bridge void *)(self);
+
+    status = AudioUnitSetProperty(_mAudioUnit,
+                        kAudioUnitProperty_SetRenderCallback,
+                        kAudioUnitScope_Global,
+                        kOutputBus,
+                        &callback,
+                        sizeof(callback));
+    if (status != noErr) {
+        //NSString* message = [NSString stringWithFormat:@"AudioUnitSetProperty SetRenderCallback failed. OSStatus: %@", @(status)];
+        return;
+    }
+
+    // initialize
+    status = AudioUnitInitialize(_mAudioUnit);
+    if (status != noErr) {
+        //NSString* message = [NSString stringWithFormat:@"AudioUnitInitialize failed. OSStatus: %@", @(status)];
+        return;
+    }
+}
+
+-(void)feed:(NSData*)buffer {
+
+    @synchronized (self.mSamples) {
+        [self.mSamples appendData:buffer];
+    }
+
+    // reset
+    self.mDidInvokeFeedCallback = false;
+
+    // start playback if we can
+    if (_mAudioUnit != nil) {
+        UInt32 isPlaying = 0;
+        UInt32 size = sizeof(isPlaying);
+        OSStatus status = AudioUnitGetProperty(_mAudioUnit,
+                                            kAudioOutputUnitProperty_IsRunning,
+                                            kAudioUnitScope_Global,
+                                            0,
+                                            &isPlaying,
+                                            &size);
+        if (status != noErr) {
+            //NSString* message = [NSString stringWithFormat:@"AudioUnitGetProperty IsRunning failed. OSStatus: %@", @(status)];
+            return;
+        }
+        if (!isPlaying) {
+            status = AudioOutputUnitStart(_mAudioUnit);
+            if (status != noErr) {
+                //NSString* message = [NSString stringWithFormat:@"AudioOutputUnitStart failed. OSStatus: %@", @(status)];
+                return;
+            }
+        }
+    }
+}
+
+-(void)setFeedThreshold:(NSNumber*)feedThreshold {
+    self.mFeedThreshold = [feedThreshold intValue];
+}
+
+-(BOOL)isPlaying {
+    return self.mSamples.length > 0;
+}
+
+
+- (void)cleanup
+{
+    if (_mAudioUnit != nil) {
+        AudioUnitUninitialize(_mAudioUnit);
+        AudioComponentInstanceDispose(_mAudioUnit);
+        _mAudioUnit = nil;
+    }
+    @synchronized (self.mSamples) {
+        self.mSamples = [NSMutableData new]; 
+    }
+}
+
+- (void)stopAudioUnit
+{
+    if (_mAudioUnit != nil) {
+        UInt32 isRunning = 0;
+        UInt32 size = sizeof(isRunning);
+        OSStatus status = AudioUnitGetProperty(_mAudioUnit,
+                                            kAudioOutputUnitProperty_IsRunning,
+                                            kAudioUnitScope_Global,
+                                            0,
+                                            &isRunning,
+                                            &size);
+        if (status != noErr) {
+            NSLog(@"AudioUnitGetProperty IsRunning failed. OSStatus: %@", @(status));
+            return;
+        }
+        if (isRunning) {
+            status = AudioOutputUnitStop(_mAudioUnit);
+            if (status != noErr) {
+                NSLog(@"AudioOutputUnitStop failed. OSStatus: %@", @(status));
+            } else {
+                NSLog(@"AudioUnit stopped because no more samples");
+            }
+        }
+    }
+}
+
+
+static OSStatus RenderCallback(void *inRefCon,
+                               AudioUnitRenderActionFlags *ioActionFlags,
+                               const AudioTimeStamp *inTimeStamp,
+                               UInt32 inBusNumber,
+                               UInt32 inNumberFrames,
+                               AudioBufferList *ioData)
+{
+    PcmSound *instance = (__bridge PcmSound *)(inRefCon);
+
+    NSUInteger remainingFrames;
+    BOOL shouldRequestMore = false;
+
+    @synchronized (instance.mSamples) {
+
+        // clear
+        memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
+
+        NSUInteger bytesToCopy = MIN(ioData->mBuffers[0].mDataByteSize, [instance.mSamples length]);
+        
+        // provide samples
+        memcpy(ioData->mBuffers[0].mData, [instance.mSamples bytes], bytesToCopy);
+
+        // pop front bytes
+        NSRange range = NSMakeRange(0, bytesToCopy);
+        [instance.mSamples replaceBytesInRange:range withBytes:NULL length:0];
+
+        remainingFrames = [instance.mSamples length] / (instance.mNumChannels * sizeof(short));
+
+        // should request more frames?
+        shouldRequestMore = remainingFrames <= instance.mFeedThreshold && !instance.mDidInvokeFeedCallback;
+    }
+
+    // stop running, if needed
+    if (remainingFrames == 0) {
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [instance stopAudioUnit];
+        });
+    }
+
+    if (shouldRequestMore) { 
+        instance.mDidInvokeFeedCallback = true;
+        //NSDictionary *response = @{@"remaining_frames": @(remainingFrames)};
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if ([instance.delegate respondsToSelector:@selector(remaining:)]) {
+                [instance.delegate remaining:remainingFrames];
+            }
+        });
+    }
+
+    return noErr;
+}
+
+
+@end