碎碎念:这个项目是自己的毕业设计,机缘巧合入了音频开发的坑,为了让自己尽快熟悉音频相关,故选择该课题。实际最终效果并没有让我满意,也有点拿不出手,在线音乐模块是花费最长时间开发的,初衷也是为了贴近网易云,所以很多看起来都很赝品,还是模仿不到位的粗糙赝品;趣味音乐模块是原理很简单,但凡能接入个乐器分离就能做到自动化获取源文件,可惜当时的我并没有找到合适的接口,选择了最笨的离线操作,是个很不完美的成果;K歌模块是一个晚上的开发成果,在毕业论文交稿的前三天晚上突然一拍脑袋,K歌不就是基于之前的模式就能实现的吗,于是乎当天晚上兴致勃勃地写完了这块代码,录制播放其实也没什么问题,但少了打分,其实也不算K歌了,但没有音调检测和midi文件,咱也不会做……总之,虽然代码有很多不足,但还是希望看到这篇文章的你能够有一点点收获。
简介
本项目设计的音乐播放器旨在通过iOS操作系统与Objective-C语言实现一个集播放歌曲、趣味播放、录制歌曲于一体的多样性音乐播放器,让使用者可以更好地理解音乐,从而发挥音乐的积极作用。
APP会根据网络数据向用户提供四个榜单以及其歌曲列表,用户可选择其中歌曲播放,歌曲播放同时实时滚动歌词,用户可随时更改歌曲播放的位置,也可随时选择播放列表中其他歌曲进行播放。当榜单中没有所需歌曲时,APP提供搜索功能,用户可根据歌曲名字、歌手姓名等信息查找歌曲,并在弹出列表框中选择歌曲进行播放。
除此之外,若用户好奇音乐去除各个音轨会是怎样的表现形式时,APP提供独特有趣的播放形式,用户可在播放过程中去除人声、钢琴、鼓、贝斯及伴奏其他声音,也可随时恢复为原音频。此外,当用户想角色转换成为歌手时,APP可提供录制歌曲的功能,用户搜索所需歌曲伴奏并开始录制,录制可随时结束,录制结束后用户可播放所录制的音频。
源码:https://github.com/xxmmaini/musicPlayer/tree/master
总结:项目整体分为在线音乐模块、趣味音乐模块、K歌模块三个模块,每个模块实现一个或多个功能,且音频处理均使用Audio Unit实现。
效果预览
在线音乐
![]() |
![]() |
![]() |
![]() |
![]() |
趣味音乐
![]() |
![]() |
![]() |
K歌
![]() |
![]() |
![]() |
![]() |
功能解析
在线音乐获取并显示
在线音乐的获取可以通过网络接口链接进行访问,从而获取Json数据,从Json数据中解析即可获取每一首歌曲的信息,再交由交互界面的控制类进行显示即可。
如图所示,musicViewController在初始化前通知musicInfo获取Json数据并解析,将解析结果存储,该步骤为网络数据获取与解析,考虑其耗时较长,这里采用子线程获取。之后初始化slideTabBarView实例,slideTabBarView实例向musicInfo请求各大榜单歌曲信息,musicInfo通过通过getJson解析获取的结果存储需要的歌曲信息并将结果返回,slideTabBarView获取结果之后重新加载榜单,显示榜单歌曲信息。
在线音乐播放(⚠️核心技术点)
在线音乐播放是通过onlineSongPlayer音频播放类辅助实现,如图所示,用户双击歌曲之后由songInfo获取自身所存音乐的url并将音频文件下载存储至本地,再解析获取音频裸数据,再将裸数据传给onlineSongPlayer进行播放,这一步骤由子线程完成,主线程同时更新模块主界面与播放界面。扬声器播放由onlineSongPlayer中I/O类别的Audio Unit实现,播放过程中暂停/开始/重置功能都通过控制Audio Unit实现。
播放界面实时更新进度
实时更新进度实际是定时获取当前音频播放的位置,再将该位置/音频文件长度即可得知音频播放进度并更新界面即可。音频播放的位置由音频播放类提供,音频文件长度由songInfo提供,songInfo解码的同时获取该音频文件长度并存储。需要注意的是,由于音频原先采样率可能与AudioUnit所需采样率44100不同,解码过程中会对音频文件重采样,音频文件长度也会随之变长/变短,songInfo最终记录音频文件长度为原先音频文件长度*(44100.0 / 原先音频采样率),因此songInfo记录音频长度可能与实际重采样之后的音频长度存在一定误差。
歌词实时滚动
歌词实时滚动功能与播放界面实时更新进度实现原理基本一致,不同的是onlineSongPlayer选择时间精度更高的CADisplayLink作为定时触发器,播放界面滚动歌词前需要判断返回音频时间是否在当前歌词范围内或在下一句歌词内,若都不是则需要重新定位歌词位置,是则判断是否需要滚动至下一句歌词。同时告知存储歌词的cell当前歌词播放进度,从而进行重绘。K歌模块的歌词实时滚动功能与在线音乐模块的原理一致,不多阐述。
搜索音乐
音乐搜索功能是由界面类searchViewController实现与显示,如图3-5所示。当搜索框有文本键入并确认时通知界面进行搜索并跳转搜索结果界面,这里搜索歌曲的方法是通过网络接口链接进行搜索,同样可获取Json数据并解析获取音乐数据,该步骤为网络数据获取与解析,因此由子线程完成,主线程显示结果界面。
趣味音乐播放(⚠️核心技术点)
趣味音乐的播放功能主要由音频播放类mixSongPlayer实现,如图3-6所示。用户双击音乐获取音频数据,初始化AUGraph,以上操作完成之后开启AUGraph即可完成趣味音乐的播放。暂停/重新开始与暂停/开启AUGraph同理。AUGraph由mix类别的Audio Unit与I/O类别的Audio Unit组合而成,其中AUGraph将mix_unit的输出与output_unit的输入链接,当AUGraph被开启时,mix_unit与output_unit也同时被开启,当mix_unit有数据输出时,output_unit就会获取数据并传给扬声器进行播放。
趣味音乐音轨去除与恢复
趣味音乐音轨去除与恢复功能同样由音频播放类mixSongPlayer实现,如图3-7所示。mixSongPlayer中对每个音轨都以一个BOOL类型的变量作为标识,当对应音轨的BOOL类型变量为NO时,mix类型的Audio Unit中对应的回调函数将获取均为0的音频数据;BOOL类型变量为YES,回调函数获取音轨的音频数据并播放。用户单击按钮实际是通过按钮当前颜色去控制上述每个音轨的BOOL类型变量。
伴奏播放与录音
K歌模块的录音功能与伴奏播放功能由音频播放类singSongPlayer辅助实现,如图3-8所示。这一功能通过控制singSongPlayer类中的I/O类别Audio Unit实例record_unit实现,当用户单击开始录制按钮时,record_unit被初始化并开启,它的麦克风录音与扬声器播放功能均被开启,并且绑定两个回调函数,一个回调函数获取并存储录音数据,一个回调函数获取伴奏音频数据,从而实现伴奏播放与录音同步的功能。
混合音频播放(⚠️核心技术点)
录制之后的作品播放功能与趣味音乐播放模块实现原理基本一致,与之不同的是singSongPlayer类中的AUGraph只需要两个输入与两个回调函数,一个回调函数获取伴奏音频文件的音频数据,一个回调函数获取录制音频的音频数据。录制音频的音频数据在用户单击终止录制时进行存储。用户单击播放作品按钮即开启singSongPlayer中的AUGraph,实现混合音频的播放。AUGraph的初始化在用户单击终止录制按钮时进行。
作品调整
作品录制完毕可进行播放,播放过程中可以打开作品调整界面,拖拽对应滑块调整人声位置、人声音量大小以及伴奏音量大小。如图3-10所示,拖动滑块本质也是修改singSongPlayer实例中的变量,从而修改播放过程中的偏移位置或mix_unit的音量设置。