當(dāng)時(shí)寫這個(gè)demo,是現(xiàn)公司出的面試題,題目的大致意思是通過隨機(jī)數(shù)映射成實(shí)物贵涵,例如0~255橘茉,可以映射成顏色,然后問還有沒有其他好的點(diǎn)子。
我當(dāng)時(shí)想了很多的點(diǎn)子,我開始想模仿 GarageBand ,自己組合一些樂器來變成不同的音樂症昏,但是我發(fā)現(xiàn)實(shí)現(xiàn)起來有很多的難點(diǎn),可能來不及完成父丰,就放棄了肝谭。還想了一些點(diǎn)子,但是沒有算法支持蛾扇,也放棄了攘烛,最后我還是決定通過已有的函數(shù)來寫個(gè) demo,最下面有 demo 的地址镀首,可以感受一下坟漱,波形與音樂結(jié)合起來比較優(yōu)美。
根據(jù)音樂中產(chǎn)生的分貝數(shù)值映射成波浪線的振幅
原理說明:
1.根據(jù)正弦函數(shù):f(x) = Asin(2πωx+φ)更哄;
2.設(shè)置默認(rèn)周期 T = 1芋齿;
3.曲線往右移平移,φ值需要減谐婶妗觅捆;
4.如果φ值每次都減小固定的數(shù)值,視覺上看起來曲線是勻速往右移動麻敌;
#import <UIKit/UIKit.h>
@interface SoundWaveView : UIView
@property (nonatomic, copy) void (^levelCallback)(SoundWaveView * waveView);
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) CGFloat level;
@end
#import "SoundWaveView.h"
@interface SoundWaveView ()
//相位變化,用來移動曲線
@property (nonatomic, assign) CGFloat offX;
//存放線的數(shù)組
@property (nonatomic, strong) NSMutableArray * linesArr;
//高度
@property (nonatomic, assign) CGFloat maxHeight;
//寬度
@property (nonatomic, assign) CGFloat maxWidth;
//最大振幅
@property (nonatomic, assign) CGFloat maxAmplitude;
//振幅系數(shù)
@property (nonatomic, assign) CGFloat amplitudeLevel;
@end
@implementation SoundWaveView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
- (void)setup
{
self.linesArr = [[NSMutableArray alloc]init];
self.maxHeight = CGRectGetHeight(self.bounds);
self.maxWidth = CGRectGetWidth(self.bounds);
self.maxAmplitude = self.maxHeight - 2;
self.amplitudeLevel = 1;
}
- (void)setLevelCallback:(void (^)(SoundWaveView *waveView))levelCallback
{
_levelCallback = levelCallback;
[self.displayLink invalidate];
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(invokeLevelCallback)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
for(int i=0; i < 5; i++){
CAShapeLayer *line = [CAShapeLayer layer];
line.fillColor = [[UIColor clearColor] CGColor];
line.lineCap = kCALineCapSquare;
line.lineJoin = kCALineJoinRound;
line.lineWidth = i == 0 ? 2 : 1;
CGFloat progress = 1.0f - (CGFloat)i / 5;
UIColor *color = [[UIColor whiteColor]colorWithAlphaComponent:(i == 0 ? 1.0 : progress *progress)];
line.strokeColor = color.CGColor;
[self.layer addSublayer:line];
[self.linesArr addObject:line];
}
}
- (void)invokeLevelCallback
{
self.levelCallback(self);
}
- (void)setLevel:(CGFloat)level
{
_level = level;
self.offX -= 0.25f;
self.amplitudeLevel = fmax(level, 0.01f);
[self refreshLines];
}
- (void)refreshLines
{
UIGraphicsBeginImageContext(self.frame.size);
for(int i=0; i < 5; i++) {
UIBezierPath *wavelinePath = [UIBezierPath bezierPath];
CGFloat progress = 1.0f - (CGFloat)i / 5;
CGFloat nowAmplitudeLevel = (1.5f * progress - 0.5f) * self.amplitudeLevel;
for(CGFloat x = 0; x < self.maxWidth; x ++) {
CGFloat midScale = 1 - pow(x / (self.maxWidth / 2) - 1, 2);
CGFloat y = midScale * self.maxAmplitude * nowAmplitudeLevel * sinf(2 * M_PI *(x / self.maxWidth) * 1 + self.offX) + (self.maxHeight * 0.5);
if (x==0) {
[wavelinePath moveToPoint:CGPointMake(x, y)];
}
else {
[wavelinePath addLineToPoint:CGPointMake(x, y)];
}
}
CAShapeLayer *waveline = [self.linesArr objectAtIndex:i];
waveline.path = [wavelinePath CGPath];
}
UIGraphicsEndImageContext();
}
- (void)dealloc
{
}
@end
其中的 pow 函數(shù)(1 - pow(x / (self.maxWidth / 2) - 1, 2))
是為了讓越靠近屏幕中間的波形振幅越明顯栅炒。