Dibujar formas de onda de audio grabadas en iOS
Forma de onda de tira
Forma de onda de línea
Configurar AvAudioSession
Antes de dibujar una forma de onda, necesita configurar una AVAudioSession y crear una matriz para almacenar datos de volumen.
Propiedades relacionadas
recorderSetting se utiliza para configurar la calidad de grabación y otros datos relacionados.
el temporizador y la frecuencia de actualización se utilizan para actualizar las formas de onda con regularidad.
soundMeter y soundMeterCount se utilizan para guardar grupos de recuento de volumen.
recordTime se utiliza para registrar el tiempo de grabación y se puede utilizar para determinar si el tiempo de grabación alcanza el nivel requerido y otros requisitos adicionales.
/// Grabador
grabador var privado: AVAudioRecorder! /// Configuración de la grabadora
private let recorderSetting = [AVSampleRateKey: NSNumber(value.Float(44100.0)), // frecuencia de muestreo de sonido
AVFormatIDKey: NSNumber(value: Int32(kAudioFormatMPEG4AAC )), // Formato de codificación
AVNumberOfChannelsKey: NSNumber(valor: 1), // Capturar pista de audio
AVencoderAudioQualityKey: NSNumber(valor: Int32(AVAudioQuality.medium.rawValue)] // Calidad de sonido
/// Temporizador de grabación
temporizador var privado: Temporizador /// Intervalo de actualización de forma de onda
privado let updateFequency = 0,05
/// Matriz de datos de sonido
private var soundMeters: [Float]! /// capacidad de la matriz de datos de sonido
private let soundMeterCount = 10
/// recordTime
private var recordTime = 0.00
Configuración relacionada con AvAudioSession
configAVAudioSession se usa para configurar AVAudioSession, donde AVAudioSessionCategoryRecord significa usar solo esta sesión para grabar operación, y la operación de reproducción se puede configurar en AVAudioSessionCategoryPlayAndRecord o AVAudioSessionCategoryPlayBlack. La diferencia entre los dos es que uno puede grabar y reproducir, y el otro puede reproducir en segundo plano (es decir, la voz aún se puede reproducir después de silenciar)
Se utiliza configRecord. Configure todo AVAudioRecoder, incluida la obtención de permisos, la configuración de la fuente del proxy y si se debe registrar el medidor de volumen.
El directorio URL se utiliza para guardar el archivo de configuración.
función privada configAVAudioSession() { let session = AVAudioSession.sharedInstance() do { try session.setCategory( AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker) } catch { print("error de configuración de sesión") }
}
función privada configRecord() { AVAudioSession.sharedInstance().requestRecordPermission { (permitido) en
si !permitido { retorno
}
} let session = AVAudioSession.sharedInstance() do { try session.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker) } catch { print("falló la configuración de la sesión") } do { self.recorder = try AVAudioRecorder (url: self.directoryURL()!, configuración: self.recorderSetting) self.recorder.delegate = self
self.recorder.prepareToRecord() self.recorder.isMeteringEnabled = true
} catch { print(error.localizedDescription)
} do { try AVAudioSession.sharedInstance().setActive(true) } catch { print(" sesión activa fallida") }
}
diversión privada divertida.
función privada directorioURL() -gt; URL?{ // hacer algo...
devolver URLArchivoSonido
}
Grabar datos de audio
Después de que comience la grabación, usaremos el temporizador recién configurado para obtener continuamente la potencia promedio y guardarla en una matriz.
El temporizador llama a UpdateMeters(updateMeters) para guardar continuamente los datos de volumen grabados en la grabadora en la matriz del sonómetro.
addSoundMeter realiza el trabajo de agregar datos.
función privada updateMeters() {
recorder.updateMeters()
recordTime = updateFequency
addSoundMeter(elemento: recorder .averagePower( forChannel: 0))
}
función privada addSoundMeter(elemento: Float) { if soundMeters.count lt; soundMeterCount {
soundMeters.append(elemento)
} else { for (index, _) in soundMeters.enumeated() { if index lt; soundMeterCount - 1 {
soundMeters[índice] = soundMeters[índice 1] p>
}
}
}//Insertar nuevos datos
soundMeters[soundMeterCount - 1] = elemento NotificationCenter.default.post(name. NSNotification.Name.init("updateMeters"), objeto: soundMeters)
}
}
}
Comience a dibujar la forma de onda
Ahora que tenemos todos los datos que necesitamos, podemos empezar a dibujar la forma de onda. En este punto, vayamos al archivo MCVolumeView.swift. En el paso anterior, enviamos una notificación llamada updateMeters para notificar a MCVolumeView que actualice la forma de onda.
anular init(frame: CGRect) { super.init(frame: frame)
backgroundColor = UIColor.clear
contentMode = .redraw?
contentMode = .redraw? porque necesita volver a dibujar el medidor de volumen varias veces
NotificationCenter.default.addObserver(self, selector: #name: NSNotification.Name. init("updateMeters"), objeto: nulo)
}
@obotifyCenter.default.addObserver(self, selector: updateView(aviso:)), nombre: NSNotification.Name.
@ objc función privada updateView(aviso: Notificación) {
soundMeters = aviso.objeto como! [Float]
setNeedsDisplay()
}
Una vez que se llama a setNeedsDisplay, se llama al método drawRect y podemos dibujar la forma de onda aquí.
noVoice y maxVolume se utilizan para garantizar que el sonido se muestre dentro de un rango determinado.
La forma de onda se dibuja usando CGContext y, por supuesto, también se puede dibujar usando UIBezierPath.
anular func draw(_ rect: CGRect) { if soundMeters ! = nil amp; soundMeters.count gt; 0 { let contexto = UIGraphicsGetCurrentContext()
contexto?.setLineCap(.round)
contexto?.setLineJoin(.round)
context?.setStrokeColor(UIColor.white.cgColor)
context?cgColor)
let noVoice = -46.0 // Este valor representa un valor inferior a -46.0 Cualquiera se considera que el contenido no tiene sonido
let maxVolume = 55.0 // Este valor representa un volumen máximo de 55.0
// Dibuja el volumen...
context ?.strokePath()
}
}
Dibujo de forma de onda de columna
Calcula la altura de cada columna según maxVolume y noVoice y muévalo al contexto para dibujarlo
Otro problema a tener en cuenta es que los puntos de coordenadas en CGContext están invertidos, por lo que los ejes de coordenadas deben invertirse para el cálculo.
case .bar: ?
context?.setLineWidth(3) ? for (índice, elemento) in soundMeters.enumeated() { let barHeight = maxVolume - (Double(item) - noVoice) // Calcula la altura de la mesa de sonido que debe mostrar la mesa de sonido actual
context?.move( to: CGPoint(x: index * 6 3, y: 40))
contexto?.addLine(to: CGPoint(x: index * 6 3, y. Int(barHeight)))
contexto?Int(barHeight)))
}
Dibujar un gráfico de líneas
Los gráficos de líneas y de barras utilizan el mismo método para calcular la "altura", pero en un gráfico de barras, la línea se dibuja primero y luego se mueve ; en un gráfico de líneas, primero mueva la polilínea y luego dibuje la polilínea.
case .line:
contexto?.setLineWidth(1.5) for (índice, elemento) in soundMeters.enumeated() { let position = maxVolume - (Double(item) - noVoice ) // Calcula la altura del segmento de línea correspondiente
context?.addLine(to: CGPoint(x: double(index * 6 3), y: position))
context ?.move (to: CGPoint(x: Double(index * 6 3), y: position))
}
}
Mejorar aún más nuestra forma de onda
p>En muchos casos, la grabación necesita mostrar no solo la forma de onda, sino también el tiempo y el progreso de la grabación actual, por lo que podemos agregar una barra de progreso de grabación en la forma de onda. Podemos hacer esto usando el archivo MCProgressView.swift.
Dibuja usando UIBezierPath y CAShapeLayer.
maskPath se usa como máscara para toda la ruta de progreso. Debido a que nuestro HUD de grabación no es cuadrado, necesitamos usar la ruta de progreso de la máscara para recortar.
ProgressPath es la ruta de progreso y el progreso se dibuja de izquierda a derecha.
la animación es la animación que dibuja la ruta de progreso.
función privada configAnimate() { let maskPath = UIBezierPath(roundedRect: CGRect.init(x: 0, y: 0, ancho: frame.width, alto: frame.height), cornerRadius: HUDCornerRadius) let MaskLayer = CAShapeLayer()
MaskLayer.BackgroundColor = UIColor.clear.cgColor
MaskLayer.frame = límites
// ProgressPath
/*
El centro del camino es el centro del HUD, el ancho es la altura del HUD, dibujado de izquierda a derecha
*/
let ProgressPath = CGMutablePath()
ProgressPath.move(a: CGPoint(x: 0, y: frame.height/2))
ProgressPath.addLine(a: CGPoint( x. frame.width, y : frame.height / 2))
ProgressLayer = CAShapeLayer()
ProgressLayer.frame = límites
ProgressLayer.fillColor = UIColor.clear.cgColor // Color de fondo de la capa
ProgressLayer.strokeColor = UIColor(rojo: 0,29, verde: 0,29, azul: 0,29, alfa: 0,90).cgColor?//Color de dibujo de la capa
ProgressLayer.frame = CAShapeLayer()
ProgressLayer.frame = límites
ProgressLayer.fillColor = UIColor.clear.cgColor p>
ProgressLayer? .lineCap = kCALineCapButt p>
ProgressLayer.lineCapButt = kCALineCapButt
ProgressLayer.fillColor = UIColor.clear.cgColor?timingFunction = CAMediaTimingFunction(nombre: kCAMediaTimingFunctionLinear) // Incluso hacia adelante
animación.fillMode = kCAFillModeForwards
animación.fromValue = 0.0
animación.toValue = 1.0
animación.autoreverses = false
animación.repeatCount = 1