导航
导航
文章目录
  1. Audio Unit 学习笔记
    1. 封装 Audio Unit 录音播放器的一般步骤
  2. 我的玩具划水鸭
    1. 已实现
    2. 已知问题
    3. 亟待实现
  3. 明天的学习计划
    1. 学习目标

学习手记 —— Audio Unit 实现耳返功能

因为年初的一场网络暴力,我颓废了很久。微博基本不玩了,社交网络也转成 Twitter 和 Instgram,不再希望结交新的朋友。然而学习上也懈怠了,内心十分懊悔,咸鱼心态淡泊名利也无可厚非,但是无所事事颓废下去就人生就真的太虚无了。所以,从今起开始每天坚持总结下所学所想。当然内容可能都很基础浅显,只为记录自己所做的事,不图多深奥。

从今天开始,我将每天总结当天的学习成果,激励自己每天都要有所进步。

Audio Unit 学习笔记

今天再次快速翻阅了《音视频开发进阶指南》,对 Audio Unit 框架大致有了了解,Audio Unit 是 Apple 提供的底层的音频框架。使用 Audio Unit 可以满足音频同时输入输出的的需求,实现耳返的功能。 Audio Unit 可以封装录音机和音频播放器。此外, Audio Unit 可以实现均衡效果器、压缩效果器和混响效果器。这也是音频软件主要的效果器。

封装 Audio Unit 录音播放器的一般步骤

  • 初始化 AudioSession
    在 iOS 的音视频开发中,使用 API 都要先调用 [AudioSession sharedInstance] 方法,来获取音频的会话。
NSError *error;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
[audioSession setPreferredSampleRate:44100 error:&error];
[audioSession setPreferredInputNumberOfChannels:1 error:&error];
[audioSession setPreferredIOBufferDuration:0.05 error:&error];
  • 初始化 Audio Unit

分配缓冲区并创建 Audio Component,AudioComponentInstanceNew 创建 Audio Unit 实例

- (void)initBuffer {
UInt32 flag = 0;
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
INPUT_BUS,
&flag,
sizeof(flag));
buffList = (AudioBufferList*)malloc(sizeof(AudioBufferList));
buffList->mNumberBuffers = 1;
buffList->mBuffers[0].mNumberChannels = 1;
buffList->mBuffers[0].mDataByteSize = 2048 * sizeof(short);
buffList->mBuffers[0].mData = (short *)malloc(sizeof(short) * 2048);
}
- (void)initAudioComponent {
AudioComponentDescription audioDesc;
audioDesc.componentType = kAudioUnitType_Output;
audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
audioDesc.componentFlags = 0;
audioDesc.componentFlagsMask = 0;
AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc);
AudioComponentInstanceNew(inputComponent, &audioUnit);
}
  • 创建 AudioStreamBasicDescription 并设置给 Audio Unit 实例
- (void)initFormat {
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
INPUT_BUS,
&audioFormat,
sizeof(audioFormat));
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
OUTPUT_BUS,
&audioFormat,
sizeof(audioFormat));
}
  • Audio Unit 的通用参数设置


如上图所示, RemoteIO Unit 分为 Element0 和 Element1,Element0 控制输出端(Output),Element1 控制输入端(Input)。若想要使用扬声器的声音播放功能必须将 Element0 的 OutputScope 和 Speaker 进行连接。若想使用麦克风的录音功能,必须将 Element1 的 InputScope 和 Microphone 进行连接。

- (void)initAudioProperty {
UInt32 flag = 1;
AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
INPUT_BUS,
&flag,
sizeof(flag));
AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
OUTPUT_BUS,
&flag,
sizeof(flag));
}
  • 构造 AUGraph 实现耳返功能
- (void)initRecordeCallback {
AURenderCallbackStruct recordCallback;
recordCallback.inputProc = RecordCallback;
recordCallback.inputProcRefCon = (__bridge void *)self;
AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
INPUT_BUS,
&recordCallback,
sizeof(recordCallback));
}
- (void)initPlayCallback {
AURenderCallbackStruct playCallback;
playCallback.inputProc = PlayCallback;
playCallback.inputProcRefCon = (__bridge void *)self;
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
OUTPUT_BUS,
&playCallback,
sizeof(playCallback));
}
static OSStatus RecordCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
AudioUnitRender(audioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, buffList);
short *data = (short *)buffList->mBuffers[0].mData;
NSLog(@"%d", data[0]);
return noErr;
}
static OSStatus PlayCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
AudioUnitRender(audioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
return noErr;
}

我的玩具划水鸭

已实现

最近在修改之前即时通讯的 bug,写了个玩具划水鸭。

已知问题

Web App 访问的地址暂时不放了,主要是 bug 太多,并且还有很多想完善的功能。

  • 输入栏始终在最底部。
  • 可以有聊天记录。
  • ws重连。
  • 可以调用图灵 API 这样始终会有人在聊天哈哈哈哈哈哈
  • 登录登出消息广播。
  • 表情代码解析。
  • 昵称校验。
  • 在线人数统计。
  • 把我 iPhone 的 APNs token 加上,这样就能及时收到推送消息了。
  • 收到消息列表滚到最底下。
  • 翻墙搜索功能:利用海外 VPS 优势。
  • iOS client reconnect close 问题。
  • iOS client 心跳包。

亟待实现

后端需要做一个简单的用户系统,简单的用户注册和登录。

明天的学习计划

明天仍以音视频开发学习为主,主要用书《音视频开发进阶指南》《OpenGL ES 应用开发指南 iOS 卷》。主要学习 GLSL、 OpenGL API 使用和视频合成的相关技术。

学习目标

  • 争取能画个简单的三角形。
  • 争取能在 Caputure Session 的 Output Buffer 回调方法中利用 OpenGL API 渲染图片。
赞赏
感谢支持~ 😘