├── 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 |
--------------------------------------------------------------------------------