2017-06-09 32 views
-3

我希望用戶播放音頻,更改某些部分的音量,然後使用新的音量級別保存該文件。具有不同音量級別的音頻文件

我改變了與AVAssetExportSession和AVMutableAudioMixInputParameters音頻的音量和工作,問題是,我需要創建這個音頻音頻循環,所以首先我創造了這個循環,然後我需要改變音量

這裏是我的代碼

let type = "m4a" 
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("mixedAudio.m4a") 
var backgroundUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("background.m4a") 

override func viewDidLoad() { 
    super.viewDidLoad() 

    playButotn.isEnabled = false 

    let mainUrl = Bundle.main.url(forResource: "prueba1", withExtension: type, subdirectory: "Audios") 
    let mainDurations = AVAsset(url: mainUrl!).duration 
    let secondAudioUrl = Bundle.main.url(forResource: "walk", withExtension: type, subdirectory: "Audios") 
    let backgroundDuration = AVAsset(url: secondAudioUrl!).duration 

    if mainDurations > backgroundDuration { 
     var times = Int(mainDurations.seconds/backgroundDuration.seconds) 
     let rem = mainDurations.seconds.truncatingRemainder(dividingBy: backgroundDuration.seconds) 
     if rem > 0 { 
      times = times + 1 
     } 

     createLoopAudio(times: times) { 
      self.createFade { 
       self.createMix(mainUrl: mainUrl!, backgroundUrl: self.backgroundUrl) 
      } 

     } 
    }else { 
     backgroundUrl = secondAudioUrl! 
     createMix(mainUrl: mainUrl!, backgroundUrl: backgroundUrl) 
    } 

} 


func createMix(mainUrl: URL, backgroundUrl: URL){ 
    let composition = AVMutableComposition() 
    let mainAsset = AVAsset(url: mainUrl) 
    let backgroundAsset = AVAsset(url: backgroundUrl) 
    let mainDurations = AVAsset(url: mainUrl).duration 

    let mainAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 
    let backgroundAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 

    let timeRange = CMTimeRangeMake(kCMTimeZero, mainDurations) 

    do { 
     try mainAudioTrack.insertTimeRange(timeRange, of: mainAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: kCMTimeZero) 
     try backgroundAudioTrack.insertTimeRange(timeRange, of: backgroundAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: kCMTimeZero) 
    }catch { 
     print(error.localizedDescription) 
    } 


    let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough) 

    assetExport?.outputFileType = AVFileTypeMPEG4 
    assetExport?.outputURL = documentsDirectory 
    assetExport?.shouldOptimizeForNetworkUse = true 


    if FileManager.default.fileExists(atPath: documentsDirectory.path) { 
     try! FileManager.default.removeItem(atPath: documentsDirectory.path) 
    } 

    assetExport?.exportAsynchronously(completionHandler: { 
     switch assetExport!.status 
     { 
     case AVAssetExportSessionStatus.failed: 
      print("failed \(assetExport?.error)") 
     case AVAssetExportSessionStatus.cancelled: 
      print("cancelled \(assetExport?.error)") 
     case AVAssetExportSessionStatus.unknown: 
      print("unknown\(assetExport?.error)") 
     case AVAssetExportSessionStatus.waiting: 
      print("waiting\(assetExport?.error)") 
     case AVAssetExportSessionStatus.exporting: 
      print("exporting\(assetExport?.error)") 
     case AVAssetExportSessionStatus.completed: 
      print("complete") 
      DispatchQueue.main.async { 
       self.playButotn.isEnabled = true 
      } 
     } 
    }) 

} 


func createLoopAudio(times: Int, completion: @escaping() -> Void){ 

    let composition = AVMutableComposition() 
    var nextTimeStartClip = kCMTimeZero 
    for _ in 1...times { 

     let url = Bundle.main.url(forResource: "walk", withExtension: type, subdirectory: "Audios") 
     let audioAsset = AVAsset(url: url!) 

     print("tracks walk \(audioAsset.tracks.count)") 
     let audioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 
     let timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration) 

     do { 
      try audioTrack.insertTimeRange(timeRange, of: audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: nextTimeStartClip) 
     }catch { 
      print(error.localizedDescription) 
     } 



     nextTimeStartClip = CMTimeAdd(nextTimeStartClip, audioAsset.duration) 
    } 

    let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough) 

    assetExport?.outputFileType = AVFileTypeMPEG4 
    assetExport?.outputURL = backgroundUrl 
    assetExport?.shouldOptimizeForNetworkUse = true 

    if FileManager.default.fileExists(atPath: backgroundUrl.path) { 
     try! FileManager.default.removeItem(atPath: backgroundUrl.path) 
    } 

    assetExport?.exportAsynchronously(completionHandler: { 
     switch assetExport!.status 
     { 
     case AVAssetExportSessionStatus.failed: 
      print("failed \(assetExport?.error)") 
     case AVAssetExportSessionStatus.cancelled: 
      print("cancelled \(assetExport?.error)") 
     case AVAssetExportSessionStatus.unknown: 
      print("unknown\(assetExport?.error)") 
     case AVAssetExportSessionStatus.waiting: 
      print("waiting\(assetExport?.error)") 
     case AVAssetExportSessionStatus.exporting: 
      print("exporting\(assetExport?.error)") 
     case AVAssetExportSessionStatus.completed: 
      print("loop complete") 
      completion() 

     } 
    }) 

} 


func createFade(completion: @escaping() -> Void) { 
    let exportAudioMix = AVMutableAudioMix() 
    let audioAsset = AVAsset(url: backgroundUrl) 

    let exportAudioMixInputParameters = AVMutableAudioMixInputParameters(track: audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0]) 


    let start = 2 
    let length = 3 

    exportAudioMixInputParameters.setVolume(0.0, at: CMTimeMakeWithSeconds(Float64(start - 1), 1)) 
    exportAudioMixInputParameters.setVolume(0.1, at: CMTimeMakeWithSeconds(Float64(start), 1)) 
    exportAudioMixInputParameters.setVolume(0.5, at: CMTimeMakeWithSeconds(Float64(start + 1), 1)) 
    exportAudioMixInputParameters.setVolume(1.0, at: CMTimeMakeWithSeconds(Float64(start + 2), 1)) 

    exportAudioMixInputParameters.setVolume(1.0, at: CMTimeMakeWithSeconds(Float64(start + length - 2), 1)) 
    exportAudioMixInputParameters.setVolume(0.5, at: CMTimeMakeWithSeconds(Float64(start + length - 1), 1)) 
    exportAudioMixInputParameters.setVolume(0.1, at: CMTimeMakeWithSeconds(Float64(start + length), 1)) 

    exportAudioMix.inputParameters = [exportAudioMixInputParameters] 


    let composition = AVMutableComposition() 
    print("tracks loop \(audioAsset.tracks.count)") 

    let audioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 
    let timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration) 

    do { 
     try audioTrack.insertTimeRange(timeRange, of: audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: kCMTimeZero) 
    }catch { 
     print(error.localizedDescription) 
    } 



    let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A) 

    assetExport?.outputFileType = AVFileTypeAppleM4A 
    assetExport?.outputURL = backgroundUrl 
    assetExport?.shouldOptimizeForNetworkUse = true 

    if FileManager.default.fileExists(atPath: backgroundUrl.path) { 
     try! FileManager.default.removeItem(atPath: backgroundUrl.path) 
    } 

    assetExport?.exportAsynchronously(completionHandler: { 
     switch assetExport!.status 
     { 
     case AVAssetExportSessionStatus.failed: 
      print("failed \(assetExport?.error)") 
     case AVAssetExportSessionStatus.cancelled: 
      print("cancelled \(assetExport?.error)") 
     case AVAssetExportSessionStatus.unknown: 
      print("unknown\(assetExport?.error)") 
     case AVAssetExportSessionStatus.waiting: 
      print("waiting\(assetExport?.error)") 
     case AVAssetExportSessionStatus.exporting: 
      print("exporting\(assetExport?.error)") 
     case AVAssetExportSessionStatus.completed: 
      print("faded complete") 
      completion() 

     } 
    }) 
+1

你到目前爲止嘗試過什麼? –

+1

這是您給我們的一項廣泛任務 – jhhoff02

+0

如果文件被移動到另一個應用程序,或者只有在應用程序中播放時纔會發生音量變化? –

回答

0

那麼,找到一種方法,工作正常:我有2個音頻; A和B,B的持續時間小於A,所以我創建了一個B的環來適應A,然後我同時播放2個音頻,並用滑塊修改b的音量。最後我保存這個音量配置並混合音頻。這是我的代碼:

import UIKit 
import AVFoundation 

class ViewController: UIViewController { 


    @IBOutlet weak var playButotn: UIButton! 

    var player: AVAudioPlayer? 

    var podcastPlayer: AVAudioPlayer? 

    var times = 0 

    @IBOutlet weak var volumeSlider: UISlider! 

    var volumeRanges = [VolumeRange]() 

    let type = "m4a" 
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("mixedAudio.m4a") 
    var backgroundUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("background.m4a") 
    var mainUrl: URL? 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     mainUrl = Bundle.main.url(forResource: "prueba1", withExtension: type, subdirectory: "Audios") 

     playButotn.isEnabled = false 

     volumeSlider.value = 1 

     let mainDurations = AVAsset(url: mainUrl!).duration 
     print(AVAsset(url: mainUrl!).duration.seconds) 
     let secondAudioUrl = Bundle.main.url(forResource: "rocking", withExtension: type, subdirectory: "Audios") 
     let backgroundDuration = AVAsset(url: secondAudioUrl!).duration 

     if mainDurations > backgroundDuration { 
      times = Int(mainDurations.seconds/backgroundDuration.seconds) 
      let rem = mainDurations.seconds.truncatingRemainder(dividingBy: backgroundDuration.seconds) 
      if rem > 0 { 
       times = times + 1 
      } 

      createLoop(times: times) { 
       DispatchQueue.main.async { 
        self.playButotn.isEnabled = true 
       } 
      } 
     }else { 
      backgroundUrl = secondAudioUrl! 
      createMix() 
     } 

    } 




    func createMix(){ 
     let composition = AVMutableComposition() 
     let mainAsset = AVAsset(url: mainUrl!) 
     let backgroundAsset = AVAsset(url: backgroundUrl) 
     let mainDurations = AVAsset(url: mainUrl!).duration 

     let mainAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 
     let backgroundAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 

     let timeRange = CMTimeRangeMake(kCMTimeZero, mainDurations) 
     do { 
      try mainAudioTrack.insertTimeRange(timeRange, of: mainAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: kCMTimeZero) 
      try backgroundAudioTrack.insertTimeRange(timeRange, of: backgroundAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: kCMTimeZero) 
     }catch { 
      print(error.localizedDescription) 
     } 
     let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality) 
     assetExport?.outputFileType = AVFileTypeMPEG4 
     assetExport?.outputURL = documentsDirectory 
     assetExport?.shouldOptimizeForNetworkUse = true 
     if FileManager.default.fileExists(atPath: documentsDirectory.path) { 
      try! FileManager.default.removeItem(atPath: documentsDirectory.path) 
     } 
     assetExport?.exportAsynchronously(completionHandler: { 
      switch assetExport!.status 
      { 
      case AVAssetExportSessionStatus.failed: 
       print("failed \(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.cancelled: 
       print("cancelled \(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.unknown: 
       print("unknown\(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.waiting: 
       print("waiting\(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.exporting: 
       print("exporting\(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.completed: 
       print("mix complete") 
       DispatchQueue.main.async { 
        self.playButotn.isEnabled = true 
       } 

      } 
     }) 

    } 


    func createLoop(times: Int, completion: @escaping() -> Void){ 
     let urlFondo = Bundle.main.url(forResource: "rocking", withExtension: type, subdirectory: "Audios") 
     let acentoFile = try! JUMAudioFile(forReading: urlFondo!) 
     let acentoPCM = acentoFile.getPCMArrayBufferFromURL() 
     let (_ , acentoCompleteData) = JUMAudioFile.convertToPoints(arrayFloatValues: acentoPCM) 
     var newDraft = [Float]() 
     for _ in 1...times { 
      for array in acentoCompleteData { 
       for fl in array { 
        newDraft.append(fl) 
       } 
      } 
     } 

     let _ = try! JUMAudioFile(createFileFromFloats: [newDraft], url: self.backgroundUrl) 
     print("loop complete") 
     completion() 

    } 

    func createLoopAudioWithFade(completion: @escaping() -> Void){ 
     let composition = AVMutableComposition() 
     let exportAudioMix = AVMutableAudioMix() 
     var exportAudioMixInputParametersArry = [AVMutableAudioMixInputParameters]() 
     let audioAsset = AVAsset(url: self.backgroundUrl) 
     let audioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 
     let timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration) 
     do { 
      try audioTrack.insertTimeRange(timeRange, of: audioAsset.tracks(withMediaType: AVMediaTypeAudio)[0], at: kCMTimeZero) 
     }catch { 
      print(error.localizedDescription) 
     } 
     let exportAudioMixInputParameters = AVMutableAudioMixInputParameters(track: audioTrack) 
     for ranges in volumeRanges { 
      exportAudioMixInputParameters.setVolume(ranges.volume!, at: CMTimeMakeWithSeconds(ranges.start!, 50000)) 
     } 
     exportAudioMixInputParametersArry.append(exportAudioMixInputParameters) 
     exportAudioMix.inputParameters = exportAudioMixInputParametersArry 
     let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality) 

     assetExport?.outputFileType = AVFileTypeMPEG4 
     assetExport?.outputURL = backgroundUrl 
     assetExport?.shouldOptimizeForNetworkUse = true 

     if FileManager.default.fileExists(atPath: backgroundUrl.path) { 
      try! FileManager.default.removeItem(atPath: backgroundUrl.path) 
     } 

     assetExport?.audioMix = exportAudioMix 
     assetExport?.exportAsynchronously(completionHandler: { 
      switch assetExport!.status 
      { 
      case AVAssetExportSessionStatus.failed: 
       print("failed \(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.cancelled: 
       print("cancelled \(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.unknown: 
       print("unknown\(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.waiting: 
       print("waiting\(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.exporting: 
       print("exporting\(assetExport?.error?.localizedDescription ?? "")") 
      case AVAssetExportSessionStatus.completed: 
       print("fade complete") 
       completion() 

      } 
     }) 

    } 




    @IBAction func play(_ sender: Any) { 
     do{ 

      player = try AVAudioPlayer(contentsOf: backgroundUrl) 
      player?.prepareToPlay() 
      player?.volume = 1.0 



      podcastPlayer = try AVAudioPlayer(contentsOf: mainUrl!) 
      podcastPlayer?.prepareToPlay() 
      podcastPlayer?.volume = 1 


      podcastPlayer?.play() 
      player?.play() 
     }catch { 
      print(error.localizedDescription) 
     } 

    } 


    @IBAction func changeVolume(_ sender: UISlider) { 
     if (player?.isPlaying)!{ 
     player?.volume = sender.value 
     let volumeRange = VolumeRange() 
     volumeRange.volume = sender.value 
     volumeRange.start = player?.currentTime 
     volumeRanges.append(volumeRange) 
     } 
    } 


    @IBAction func touchUp(_ sender: UISlider) { 
    } 

    @IBAction func touchUpOutside(_ sender: UISlider) { 
     print("ouside") 
    } 


    @IBAction func generar(_ sender: Any) { 

     playButotn.isEnabled = false 

     self.createLoopAudioWithFade() { 
      self.createMix() 

     } 
    } 

    @IBAction func playMix(_ sender: UIButton) { 
     do { 
     player = try AVAudioPlayer(contentsOf: documentsDirectory) 
     player?.prepareToPlay() 
     player?.volume = 1.0 
      player?.play() 
     }catch { 

     } 
    } 


} 


class VolumeRange { 
    var volume: Float? 
    var start: Double? 
} 
相關問題