适用于 macOS 的 Xcode 应用程序。这就是我设置从 USB 麦克风输入获取音频的方式。一年前工作,现在不行。为啥

Posted

技术标签:

【中文标题】适用于 macOS 的 Xcode 应用程序。这就是我设置从 USB 麦克风输入获取音频的方式。一年前工作,现在不行。为啥【英文标题】:Xcode app for macOS. This is how I setup to get audio from usb mic input. Worked a year ago, now doesn't. Why适用于 macOS 的 Xcode 应用程序。这就是我设置从 USB 麦克风输入获取音频的方式。一年前工作,现在不行。为什么 【发布时间】:2020-05-17 21:16:03 【问题描述】:

这是我的音频初始化代码。当队列缓冲区准备好时,我的应用程序会做出响应,但缓冲区中的所有数据都为零。检查系统首选项中的声音表明声音输入对话框中的 USB 音频编解码器处于活动状态。应用启动后立即调用 AudioInit()。


    #pragma mark user data struct
    typedef struct MyRecorder
    
        AudioFileID recordFile;
        SInt64 recordPacket;
        Float32 *pSampledData;
        MorseDecode *pMorseDecoder;

     MyRecorder;

    #pragma mark utility functions
    void CheckError(OSStatus error, const char *operation)
    
        if(error == noErr) return;

        char errorString[20];
        // see if it appears to be a 4 char code
        *(UInt32*)(errorString + 1) = CFSwapInt32HostToBig(error);
        if (isprint(errorString[1]) && isprint(errorString[2]) &&
            isprint(errorString[3]) && isprint(errorString[4]))
        
            errorString[0] = errorString[5] = '\'';
            errorString[6] = '\0';
        

        else
        
            sprintf(errorString, "%d", (int)error);

        
        fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
    

    OSStatus MyGetDefaultInputDeviceSampleRate(Float64 *outSampleRate)
    
        OSStatus error;
        AudioDeviceID deviceID = 0;
        AudioObjectPropertyAddress propertyAddress;
        UInt32 propertySize;
        propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
        propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
        propertyAddress.mElement = 0;
        propertySize = sizeof(AudioDeviceID);
        error = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                                    &propertyAddress,
                                                    0,
                                                    NULL,
                                                    &propertySize,
                                                    &deviceID);

        if(error)
            return error;

        propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
        propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
        propertyAddress.mElement = 0;
        propertySize = sizeof(Float64);
        error = AudioObjectGetPropertyData(deviceID,
                                                    &propertyAddress,
                                                    0,
                                                    NULL,
                                                    &propertySize,
                                                    outSampleRate);

        return error;
    

    static int MyComputeRecordBufferSize(const AudiostreamBasicDescription *format,
                                         AudioQueueRef queue,
                                         float seconds)
    
        int packets, frames, bytes;
        frames = (int)ceil(seconds * format->mSampleRate);
        if(format->mBytesPerFrame > 0)
        
            bytes = frames * format->mBytesPerFrame;
        
        else
        
            UInt32 maxPacketSize;
            if(format->mBytesPerPacket > 0)
            
                // constant packet size
                maxPacketSize = format->mBytesPerPacket;
            
            else
            
                // get the largest single packet size possible
                UInt32 propertySize = sizeof(maxPacketSize);
                CheckError(AudioQueueGetProperty(queue,
                                                 kAudioConverterPropertyMaximumOutputPacketSize,
                                                 &maxPacketSize,
                                                 &propertySize),
                           "Couldn't get queues max output packet size");
            
            if(format->mFramesPerPacket > 0)
                packets = frames / format->mFramesPerPacket;
            else
                // worst case scenario: 1 frame in a packet
                packets = frames;
            // sanity check
            if(packets == 0)
                packets = 1;
            bytes = packets * maxPacketSize;

        
        return bytes;
    


    extern void bridgeToMainThread(MorseDecode *pDecode);

    static int callBacks = 0;
    // ---------------------------------------------
    static void MyAQInputCallback(void *inUserData,
                                  AudioQueueRef inQueue,
                                  AudioQueueBufferRef inBuffer,
                                  const AudioTimeStamp *inStartTime,
                                  UInt32 inNumPackets,
                                  const AudioStreamPacketDescription *inPacketDesc)
    
        MyRecorder *recorder = (MyRecorder*)inUserData;
        Float32 *pAudioData = (Float32*)(inBuffer->mAudioData);
        recorder->pMorseDecoder->pBuffer = pAudioData;
        recorder->pMorseDecoder->bufferSize = inNumPackets;
        bridgeToMainThread(recorder->pMorseDecoder);
        CheckError(AudioQueueEnqueueBuffer(inQueue,
                                           inBuffer,
                                           0,
                                           NULL),
                   "AudioQueueEnqueueBuffer failed");
        printf("packets = %ld, bytes = %ld\n",(long)inNumPackets,(long)inBuffer->mAudioDataByteSize);
        callBacks++;
        //printf("\ncallBacks = %d\n",callBacks);
        //if(callBacks == 0)
            //audioStop();
    


    static AudioQueueRef queue = 0;
    static MyRecorder recorder = 0;
    static AudioStreamBasicDescription recordFormat;


    void audioInit()
    
        // set up format
        memset(&recordFormat,0,sizeof(recordFormat));
        recordFormat.mFormatID = kAudioFormatLinearPCM;
        recordFormat.mChannelsPerFrame = 2;
        recordFormat.mBitsPerChannel = 32;
        recordFormat.mBytesPerPacket = recordFormat.mBytesPerFrame = recordFormat.mChannelsPerFrame * sizeof(Float32);
        recordFormat.mFramesPerPacket = 1;
        //recordFormat.mFormatFlags = kAudioFormatFlagsCanonical;
        recordFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
        MyGetDefaultInputDeviceSampleRate(&recordFormat.mSampleRate);
        UInt32 propSize = sizeof(recordFormat);
        CheckError(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
                                          0,
                                          NULL,
                                          &propSize,
                                          &recordFormat),
                    "AudioFormatProperty failed");


        recorder.pMorseDecoder = MorseDecode::pInstance();
        recorder.pMorseDecoder->m_sampleRate = recordFormat.mSampleRate;
        //    recorder.pMorseDecoder->setCircularBuffer();

        //set up queue

        CheckError(AudioQueueNewInput(&recordFormat,
                                      MyAQInputCallback,
                                      &recorder,
                                      NULL,
                                      kCFRunLoopCommonModes,
                                      0,
                                      &queue),
                   "AudioQueueNewInput failed");
        UInt32 size = sizeof(recordFormat);
        CheckError(AudioQueueGetProperty(queue,
                                         kAudioConverterCurrentOutputStreamDescription,
                                         &recordFormat,
                                         &size), "Couldn't get queue's format");

        // set up buffers and enqueue

        const int kNumberRecordBuffers = 3;

        int bufferByteSize = MyComputeRecordBufferSize(&recordFormat, queue, AUDIO_BUFFER_DURATION);
        for(int bufferIndex = 0; bufferIndex < kNumberRecordBuffers; bufferIndex++)
        
            AudioQueueBufferRef buffer;
            CheckError(AudioQueueAllocateBuffer(queue,
                                                bufferByteSize,
                                                &buffer),
                       "AudioQueueAllocateBuffer failed");
            CheckError(AudioQueueEnqueueBuffer(queue,
                                               buffer,
                                               0,
                                               NULL),
                       "AudioQueueEnqueueBuffer failed");
        
    

    void audioRun()
    
        CheckError(AudioQueueStart(queue, NULL), "AudioQueueStart failed");
    

    void audioStop()
    
        CheckError(AudioQueuePause(queue), "AudioQueuePause failed");
    


【问题讨论】:

如何将我的应用列表添加到麦克风访问列表中?我在 Xcode 中工作并使用调试器运行。我需要做些什么来安装它以便 macOS 识别它吗? 你不能。此行为旨在让用户控制哪些应用程序可以访问麦克风。 【参考方案1】:

这听起来像是新的 macOS 的“麦克风隐私”设置,如果将您的应用设置为“禁止访问”,则会导致这种行为。所以:

    打开“系统偏好设置”窗格。

    点击“安全和隐私”。

    选择“隐私”选项卡。

    点击左侧窗格中的“麦克风”。

    在右侧窗格中找到您的应用并勾选旁边的复选框。

然后重新启动您的应用并进行测试。

乏味,不是吗?


编辑:如 cmets 中所述,您不能直接请求麦克风访问权限,但您可以通过调用 [AVCaptureDevice authorizationStatusForMediaType: AVMediaTypeAudio] 来检测它是否已授予您的应用程序。

【讨论】:

我没有专门使用麦克风,该设备名为“USB”类型的“USB Audio CODEC”。我在输入选项卡下的系统偏好设置的声音框中选择了它。它在输入电平指示器中显示活动。 “麦克风”是 Apple 所说的任何形式的音频输入,系统偏好设置面板中的信号是偶然的。被阻止的是您的应用的信号,上述步骤将修复它。 那么我需要知道如何进行。我的应用程序是否可以通过某种方式请求访问权限,然后将其传递给可以允许访问的用户?当我转到隐私选项卡时,我看不到我的应用程序。如何将它添加到我可以授予访问权限的需要访问权限的应用列表中? 当您的应用程序第一次尝试访问“麦克风”时,MacOS 会弹出一个框询问用户他/她是否希望允许它。如果他们说是,那么 MacOS 会记住该选择并允许此后访问您的应用程序。您的应用也将出现在隐私选项卡中,并勾选复选框。如果他们说不,那么您的应用将在此后收到无声的音频流,并且它会出现在隐私选项卡中,且复选框未选中。 您无法直接影响任何此类行为,但请参阅我的编辑。

以上是关于适用于 macOS 的 Xcode 应用程序。这就是我设置从 USB 麦克风输入获取音频的方式。一年前工作,现在不行。为啥的主要内容,如果未能解决你的问题,请参考以下文章

macOS 安装 wget

极简的 macOS 系统分析器和清理器

在 MacOS 上构建 SwiftUI “Hello World”

SwiftUI Preview 不适用于 MacOS 构建,而它适用于 iOS 构建

如何使用适用于 macOS 的 Visual Studio 代码为外部 Mac 应用商店签署和公证电子应用程序构建?

Ujson 适用于 MacOS,但不适用于 Ubuntu