├── README.md └── WaveformView.swift /README.md: -------------------------------------------------------------------------------- 1 | # iOS-Animated-Waveform-in-Swift 2 | A waveform that animates to an AVAudioPlayer. 3 | 4 | 5 | 6 | # Installation 7 | Drag `WaveformView.swift` into your project. 8 | 9 | # Usage 10 | Create a view on a storyboard or in your code manually, and set its type as `WaveformView`. 11 | 12 | To activate the waveform, write this code: 13 | 14 | `myWaveformView.start(&myAudioPlayer)` 15 | 16 | With `myWaveformView` being the view you created as the type of `WaveformView` above, and `myAudioPlayer` being the audio player you want the waveform to animate to. 17 | 18 | # Customization 19 | Attribute | Description | Usage 20 | ----------|-------------|-------- 21 | `color` | Chanes the color of the waveform | `waveform.color = UIColor(r: 0.6, g: 0.3, b: 0.1, a: 0.9)`
`waveform.color = myImage.averageColor()` 22 | `randomColor` | Overrides `color` attribute to be a random color each frame | `waveform.randomColor = true` 23 | `lineWidth` | Changes the width of the lines in the waveform | `waveform.lineWidth = 3.0` 24 | -------------------------------------------------------------------------------- /WaveformView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaveformView.swift 3 | // 4 | // Created by Gabriel Jones on 9/22/15. 5 | // Updated to Swift 3: 3/1/17 6 | // Copyright (c) 2015 Gabriel Jones. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | 12 | extension UIColor { 13 | 14 | /** 15 | Get the RGB of a UIColor 16 | 17 | - returns: `Color` class contains variables `r`, `g`, `b`, `a` according to UIColor it originated from 18 | */ 19 | func rgb() -> [String:CGFloat] { 20 | var fRed : CGFloat = 0 21 | var fGreen : CGFloat = 0 22 | var fBlue : CGFloat = 0 23 | var fAlpha: CGFloat = 0 24 | if self.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha) { 25 | let iRed = CGFloat(fRed * 255.0) 26 | let iGreen = CGFloat(fGreen * 255.0) 27 | let iBlue = CGFloat(fBlue * 255.0) 28 | let iAlpha = CGFloat(fAlpha) 29 | let rgb = ["r": iRed, "g": iGreen, "b": iBlue, "a": iAlpha] 30 | return rgb 31 | } else { 32 | return ["r": 0, "g": 0, "b": 0, "a": 0] 33 | } 34 | } 35 | 36 | } 37 | 38 | extension UIImage { 39 | 40 | /** 41 | Get the average color in a UIImage 42 | 43 | - returns: A UIColor that is the average color in the image 44 | */ 45 | func averageColor() -> UIColor { 46 | let rgba = UnsafeMutablePointer.allocate(capacity: 4) 47 | let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB() 48 | let info = CGImageAlphaInfo.premultipliedLast.rawValue 49 | let context: CGContext = CGContext(data: rgba, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: info)! 50 | 51 | context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: 1, height: 1)) 52 | 53 | if rgba[3] > 0 { 54 | let alpha: CGFloat = CGFloat(rgba[3]) / 255.0 55 | let multiplier: CGFloat = alpha / 255.0 56 | 57 | return UIColor(red: CGFloat(rgba[0]) * multiplier, green: CGFloat(rgba[1]) * multiplier, blue: CGFloat(rgba[2]) * multiplier, alpha: alpha) 58 | } else { 59 | return UIColor(red: CGFloat(rgba[0]) / 255.0, green: CGFloat(rgba[1]) / 255.0, blue: CGFloat(rgba[2]) / 255.0, alpha: CGFloat(rgba[3]) / 255.0) 60 | } 61 | } 62 | 63 | } 64 | 65 | class WaveformView : UIView { 66 | 67 | var color = UIColor(red: 0, green: 0, blue: 0, alpha: 0) 68 | var structure = [Int]() 69 | var power = Float() 70 | var _audioPlayer = AVAudioPlayer() 71 | var randomColor = false 72 | 73 | func start(_ audioPlayer: inout AVAudioPlayer) { 74 | var i = 1 75 | var up = true 76 | var max = 2 77 | var _i = 0 78 | let nextmax = [2,4,2,4,8,12,8,2,4,2] 79 | while true { 80 | if _i == nextmax.count-1 { structure+=[3,2,1,2,2,2,2,2,1];break; } 81 | if max == i && up == true { 82 | _i += 1 83 | structure += [max, max, max, max] 84 | max = nextmax[_i] 85 | up = false 86 | } 87 | if i == 1 { up = true } 88 | structure.append(i) 89 | i += up ? 1 : -1 90 | } 91 | 92 | let dpLink = CADisplayLink(target: self, selector: #selector(WaveformView.update)) 93 | dpLink.frameInterval = 2 94 | audioPlayer.isMeteringEnabled = true 95 | dpLink.add(to: RunLoop.current, forMode: RunLoopMode.commonModes) 96 | _audioPlayer = audioPlayer 97 | } 98 | 99 | override func draw(_ rect: CGRect) { 100 | let context = UIGraphicsGetCurrentContext() 101 | context?.beginPath() 102 | 103 | var i = CGFloat(0.0) 104 | for line in structure { 105 | let _power = power * Float(line) 106 | context?.move(to: CGPoint(x: CGFloat(1+i), y: CGFloat(0))) 107 | let rand = Float(arc4random_uniform(2)) 108 | let _rand = Float(arc4random_uniform(2)) - 2 109 | let decision = Float(arc4random_uniform(1)) 110 | let final: Float = (decision == 0) ? (rand) : (_rand) 111 | context?.addLine(to: CGPoint(x: CGFloat(1+i), y: CGFloat(final + (_power * 0.35)))) 112 | i += self.frame.width / CGFloat(structure.count) 113 | } 114 | 115 | var r = CGFloat(Float(arc4random()) / Float(UINT32_MAX)) 116 | var g = CGFloat(Float(arc4random()) / Float(UINT32_MAX)) 117 | var b = CGFloat(Float(arc4random()) / Float(UINT32_MAX)) 118 | var a = CGFloat(1.0) 119 | 120 | if !randomColor { 121 | r = self.color.rgb()["r"]!/255 122 | g = self.color.rgb()["g"]!/255 123 | b = self.color.rgb()["b"]!/255 124 | a = self.color.rgb()["a"]! 125 | } 126 | 127 | context?.setStrokeColor(red: r, green: g, blue: b, alpha: a) 128 | context?.setLineWidth(1) 129 | context?.strokePath() 130 | } 131 | 132 | func update() { 133 | //Get new meter values 134 | _audioPlayer.updateMeters() 135 | 136 | 137 | var _power = Float() 138 | var __power = [Float]() 139 | 140 | for i in 0 ..< _audioPlayer.numberOfChannels { 141 | __power.append(_audioPlayer.averagePower(forChannel: i)) 142 | } 143 | _power = __power.reduce(0.0) { 144 | return $0 + $1/Float(__power.count) 145 | } 146 | 147 | power = Float(pow(10, (0.05 * _power)) * 10) 148 | self.setNeedsDisplay() 149 | } 150 | 151 | } 152 | --------------------------------------------------------------------------------