我正在寻找如何根据音乐绘制声波。
我想要像这张照片一样的波浪
这里有一些关于从音乐中显示Waves的讨论
Github示例链接
但是对这种类型的波浪没有任何了解,是否可以像这张图像一样画出波浪?
答案 0 :(得分:4)
免责声明:通过反复试验发现了很多这方面的内容,我可能会在这里发表一些严重的错误假设:
您需要使用AudioUnits框架。初始化播放时,您可以创建AURenderCallbackStruct。您可以在此结构中指定回放回调函数,该函数为您提供一些包含所需信息的参数。
回调函数将具有如下签名:
static OSStatus recordingCallback (void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
此处有一个音频数据阵列,可用于获取每个频率仓的音频缓冲区幅度,或用于计算频率仓的DB值。
我不知道该图表显示的是什么,但它在我看来就像是对每个样本箱的振幅进行了平滑显示。
音频单元并不简单,但它值得玩一段时间才能掌握。
这是我的回调函数的骨架,因此您可以更好地理解我的意思:
答案 1 :(得分:3)
我过去三个月也一直在努力,但我找不到解决办法。目前我根据歌曲类型(静态数据歌曲)使用静态图像。我将图片添加到UIScrollView
并根据音频的当前位置更改了contentOffset
。
答案 2 :(得分:3)
您可以参考此链接Drawing waveform with AVAssetReader,并可以更改生成图片代码。
答案 3 :(得分:0)
从上面的答案中稍微重构一下
import AVFoundation
import CoreGraphics
import Foundation
import UIKit
class WaveGenerator {
private func readBuffer(_ audioUrl: URL) -> UnsafeBufferPointer<Float> {
let file = try! AVAudioFile(forReading: audioUrl)
let audioFormat = file.processingFormat
let audioFrameCount = UInt32(file.length)
guard let buffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)
else { return UnsafeBufferPointer<Float>(_empty: ()) }
do {
try file.read(into: buffer)
} catch {
print(error)
}
// let floatArray = Array(UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength)))
let floatArray = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
return floatArray
}
private func generateWaveImage(
_ samples: UnsafeBufferPointer<Float>,
_ imageSize: CGSize,
_ strokeColor: UIColor,
_ backgroundColor: UIColor
) -> UIImage? {
let drawingRect = CGRect(origin: .zero, size: imageSize)
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
let middleY = imageSize.height / 2
guard let context: CGContext = UIGraphicsGetCurrentContext() else { return nil }
context.setFillColor(backgroundColor.cgColor)
context.setAlpha(1.0)
context.fill(drawingRect)
context.setLineWidth(0.25)
let max: CGFloat = CGFloat(samples.max() ?? 0)
let heightNormalizationFactor = imageSize.height / max / 2
let widthNormalizationFactor = imageSize.width / CGFloat(samples.count)
for index in 0 ..< samples.count {
let pixel = CGFloat(samples[index]) * heightNormalizationFactor
let x = CGFloat(index) * widthNormalizationFactor
context.move(to: CGPoint(x: x, y: middleY - pixel))
context.addLine(to: CGPoint(x: x, y: middleY + pixel))
context.setStrokeColor(strokeColor.cgColor)
context.strokePath()
}
guard let soundWaveImage = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
UIGraphicsEndImageContext()
return soundWaveImage
}
func generateWaveImage(from audioUrl: URL, in imageSize: CGSize) -> UIImage? {
let samples = readBuffer(audioUrl)
let img = generateWaveImage(samples, imageSize, UIColor.blue, UIColor.white)
return img
}
}
使用
let url = Bundle.main.url(forResource: "TEST1.mp3", withExtension: "")!
let img = waveGenerator.generateWaveImage(from: url, in: CGSize(width: 600, height: 200))