iOS調(diào)節(jié)系統(tǒng)鈴聲音量的方案

需求背景:

最近有一個需求,需要新增一個系統(tǒng)鈴聲,并且可以支持調(diào)整鈴聲的大小。但是 iOS 系統(tǒng)其實是沒有直接提供 API 來直接調(diào)整系統(tǒng)鈴聲的大小。我發(fā)現(xiàn)可以調(diào)整音頻文件的聲音大小,所以我們可以先調(diào)整 wav 文件的聲音,然后再通過重復(fù)注冊系統(tǒng)鈴聲,再通過注冊的聲音 id 進(jìn)行播放就好了。

實現(xiàn)代碼

調(diào)整 wav 文件音量的代碼:

import Foundation
import AVFoundation

/// WAV 音頻音量調(diào)節(jié)工具類
class DDWavVolumeAdjuster {
    
    /// 音量調(diào)節(jié)錯誤類型
    enum VolumeAdjustError: Error {
        case invalidURL
        case invalidFileFormat
        case readError
        case writeError
        case processingError
    }
    
    /// 調(diào)整 WAV 文件音量并生成新文件
    /// - Parameters:
    ///   - inputPath: 輸入 WAV 文件路徑
    ///   - outputPath: 輸出 WAV 文件路徑
    ///   - volumeMultiplier: 音量倍數(shù) (0.0 ~ N.0,1.0 為原始音量)
    /// - Throws: VolumeAdjustError
    static func adjustVolume(inputURL: URL,
                             outputURL: URL,
                             volumeMultiplier: Float) throws {
        
        // 創(chuàng)建音頻文件
        guard let audioFile = try? AVAudioFile(forReading: inputURL) else {
            throw VolumeAdjustError.invalidFileFormat
        }
        
        // 獲取音頻格式
        let format = audioFile.processingFormat
        let frameCount = UInt32(audioFile.length)
        
        // 創(chuàng)建 PCM 緩沖區(qū)
        guard let buffer = AVAudioPCMBuffer(pcmFormat: format,
                                            frameCapacity: frameCount) else {
            throw VolumeAdjustError.processingError
        }
        
        // 讀取音頻數(shù)據(jù)
        do {
            try audioFile.read(into: buffer)
        } catch {
            throw VolumeAdjustError.readError
        }
        
        // 調(diào)整音量
        if let floatChannelData = buffer.floatChannelData {
            let channelCount = Int(format.channelCount)
            let frameLength = Int(buffer.frameLength)
            
            for channel in 0..<channelCount {
                let channelData = floatChannelData[channel]
                for frame in 0..<frameLength {
                    channelData[frame] *= volumeMultiplier
                    
                    // 確保音頻數(shù)據(jù)在有效范圍內(nèi) (-1.0 到 1.0)
                    if channelData[frame] > 1.0 {
                        channelData[frame] = 1.0
                    } else if channelData[frame] < -1.0 {
                        channelData[frame] = -1.0
                    }
                }
            }
        }
        
        // 創(chuàng)建輸出文件
        do {
            // 如果文件已存在,先刪除
            try? FileManager.default.removeItem(at: outputURL)
            
            let outputFile = try AVAudioFile(
                forWriting: outputURL,
                settings: audioFile.fileFormat.settings
            )
            
            // 寫入調(diào)整后的音頻數(shù)據(jù)
            try outputFile.write(from: buffer)
        } catch {
            throw VolumeAdjustError.writeError
        }
    }
}

注冊系統(tǒng)鈴聲:

// 播放器

    static var defaultSystemSoundID: SystemSoundID = 0

    class func setupSystemAudio() {
        
        if defaultSystemSoundID != 0 {
            AudioServicesRemoveSystemSoundCompletion(defaultSystemSoundID)
            defaultSystemSoundID = 0
        }
        
        let outputHitUrl = FileManager.appGroupSharedSupportDirectoryURL
            .appendingPathComponent(DingDingConstants.hitWAVFile, isDirectory: false)
        
        var soundID: SystemSoundID = 0
        AudioServicesCreateSystemSoundID(outputHitUrl as CFURL, &soundID)
        defaultSystemSoundID = soundID
        
        let completion: AudioServicesSystemSoundCompletionProc = { (soundID, _) in
            debugPrint("Sound \(soundID) finished playing!")
        }
        
        AudioServicesAddSystemSoundCompletion(soundID,
                                              CFRunLoopGetMain(),
                                              CFRunLoopMode.defaultMode.rawValue,
                                              completion,
                                              nil)
    }

播放系統(tǒng)聲音:

        DispatchQueue.global().async {
            if 0 != StandardAudioFeedbackEngine.defaultSystemSoundID {
                AudioServicesPlaySystemSound(StandardAudioFeedbackEngine.defaultSystemSoundID)
            } else {
                AudioServicesPlaySystemSound(id)
            }
        }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容