はじめに
iPhoneの標準アプリ「ボイスメモ」を使って音声を録音するときに、録音開始とともに音声波形が表示されます。
この記事では、SwiftUIのPath構造体を使って「ボイスメモ」で表示される音声波形を再現してみようと思います(下図)。
動作環境
- Xcode 14.1
- iOS 16
実装
音声波形は下記の順で実装していきます。
- ある点の線を描画
- 指定した横幅まで、1.の線を任意の間隔で描画
- 2.で描画した音声波形を右から左へ流れるように更新
まず、下図のiPhone中央に位置する音声波形に対して、xy座業系を考えます(iOSデバイスはデバイス下部に向かってy軸が正の値を取るため、y軸が下向きになっていることに注意してください)。
xy座標内の音声波形では、${y=y_0}$を中心として上下長さが対称となる線をx軸に対して一定間隔ごとに描画しています。
① 線の描画
Pathを使ってある点での線を描画するため、上の図の点線の枠①に注目します(拡大したグラフが下図)。
グラフ内の変数${l_n}$は、マイク入力から随時取得できる音の大きさなどを想定しています。
このグラフでは、${(x_n, y_0-l_n)}$を起点として、${(x_n, y_0+l_n)}$までの線を示しており、SwiftUIのPath
を使い、下記のように実装できます。
// 起点
Path { path in
path.move(
to: .init(
x: x, // x_n
y: y0 - l // y_0 - l_n
)
)
// 終点
path.addLine(
to: .init(
x: x, // x_n
y: y0 + l // y_0 + l_n
)
)
}
② 任意の間隔で線を描画
次に、指定した横幅${W}$まで任意の間隔${(=x_{n+1}-x_n)}$で線を配置していきます。(下図)
実装では、(横幅)/(任意の間隔)
個の要素 ${l_n}$を持つ配列levelArray
を用意し、levelArray
の要素数分の線を描画していきます。
Path { path in
let y_0 = view.size.height * 0.5
var x = 0.0
for l in levelArray {
path.move(
to: .init(
x: x,
y: y0 - l
)
)
path.addLine(
to: .init(
x: x,
y: y0 + l
)
)
x += interval
}
}
③ 音声波形の更新
「はじめに」でお見せした音声波形のgif画像では、波形が右から左に流れているように見えると思います。これを実装で表現するためには、音の大きさなどを取得するたびに、levelArray
の末尾に新しい値 ${l_n}$を追加し、一番最初の要素を削除する処理が必要です。このような処理を行うことで音声波形の再描画が右から左に進んでいるように見えます。
以上から、①と②の実装も踏まえると、右から左に流れていく音声波形は下記のように実装できます。
struct SpectrumView: View {
@State var levelArray: [CGFloat] = []
@Binding var value: CGFloat
private let interval:CGFloat = 5.0
var body: some View {
GeometryReader { render in
Path { path in
let y_0 = render.size.height * 0.5
var x = 0.0
for l in levelArray {
path.move(
to: .init(
x: x,
y: y0 - l
)
)
path.addLine(
to: .init(
x: x,
y: y0 + l
)
)
x += interval
}
}
.stroke(lineWidth: 1.0)
.fill(Color.red)
.onChange(of: value, perform: { newValue in
if levelArray.isEmpty { return }
levelArray.append(newValue)
levelArray.remove(at: 0)
})
.onAppear {
if levelArray.isEmpty == false { return }
let count = render.size.width / interval
levelArray = Array(repeating: 0.01, count: Int(count))
}
}
}
}
GitHub
おわりに
最後までご覧いただきありがとうございました。音声を録音するなどのアプリを作る際には参考にしていただければと思います。
他に音声波形の描画方法などありましたら、コメントで教えていただけると幸いです。