实现如下:
需要的数据源:
1、demo.mp3:音频文件
2、samplePoints 数组:每秒音量(0-1)数组,根据音频pcm采样数据文件计算出来的(遍历:每秒取一点,并记录下最大值;最后按取值为0至1,进行缩放。计算完的数存如数组)
(这里不过多描述计算音量过程,以后有时间补...)
接下来是UI实现思路:
用了两个TableView,上面的显示时间和刻度,下面的显示音量Lines。
为了让时间显示在cell中间,所以设置结构如下图:
time tableView 不可滑动,Wave tableView的 scrollViewDidScroll 回调里设置time tableView的偏移量,使其保持一致。
根据上面UI结构,所以第一个cell只有10s的数据,而后开始每个cell画30s,直到最后一个有多少画多少
// 因为时间刻度显示问题,为了把label放在中间,所以第一个section左边空出了10s,每个section画30s的数据
// 刻度的每个间隔是2s,也就是需要可以画2条线(kAudioPlayerLineSpacing:每条线的间距;每个刻度的就*2)
- (void)setPoints:(NSArray *)points {
_points = points;
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:_points];
self.pointArrays = [[NSMutableArray alloc] init];
NSInteger index = 0;
while (tempArray.count > 0) {
if (index == 0) {
if (tempArray.count >= 20) { // section0 数据
[self.pointArrays addObject:[tempArray subarrayWithRange:NSMakeRange(0, 20)]];
[tempArray removeObjectsInRange:NSMakeRange(0, 20)];
}
} else {
if (tempArray.count >= 30) {
[self.pointArrays addObject:[tempArray subarrayWithRange:NSMakeRange(0, 30)]];
[tempArray removeObjectsInRange:NSMakeRange(0, 30)];
} else {
[self.pointArrays addObject:[tempArray copy]];
[tempArray removeAllObjects];
}
}
index++;
}
self.timeView.points = self.pointArrays;
self.waveView.points = self.pointArrays;
}
AVPlayer:通过`addPeriodicTimeObserverForInterval:queue:usingBlock:`实现每秒回调,监听播放进度
_player = [[AVPlayer alloc] initWithURL:self.playURL];
__weak typeof(self) weakSelf = self;
[_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float currentTime = weakSelf.player.currentTime.value / weakSelf.player.currentTime.timescale;
[weakSelf.waveView setContentOffset:CGPointMake(0, currentTime * kAudioPlayerLineSpacing)];
}];
kAudioPlayerLineSpacing:是每条Line之间的距离(可以随意设置)
所以每个刻度的间距就是:kAudioPlayerLineSpacing * 2 (因为个刻度表示2s)
1、首先去掉tableView的滑动惯性,否则滑动出界就很难控制
在 scrollViewWillBeginDecelerating: 开始减速方法里设置 [scrollView setContentOffset: animated:NO]
(设置偏移为手指滑动到的位置,并不需要滑动动画,即可去掉滑动惯性)
2、然后滑动的时候需要暂停player,否则就会跟 TimeObserver 回调里设置的偏移 冲突
在 scrollViewWillBeginDragging: 方法里调用 [_player pause]
在 scrollViewDidEndDragging: 方法里根据 偏移 和 kAudioPlayerLineSpacing 计算出需要播放的时间(秒数),
- (void)didEndDraggingY:(CGFloat)y {
// 拖拽结束后,根据偏移计算时间,设置播放进度
CGFloat second = y / kAudioPlayerLineSpacing;
[self.player seekToTime:CMTimeMakeWithSeconds(second, NSEC_PER_SEC)];
if (self.playBtn.selected) { // 如果是播放状态则开始播放
[self.player play];
}
}
github demo