How to Get and Save The Mixed of Multiple Audios in to Single Audio in Swift

Mixing two Audio Files using AVComposition on IOS

I'm posting the code that I eventually got to work, in case anybody else is trying to do the same thing and would like some code samples (My problem above I suspect was that the audio files weren't being loaded correctly)

 [self showActivityIndicator]; // This code takes a while so show the user an activity Indicator
AVMutableComposition *composition = [AVMutableComposition composition];
NSArray* tracks = [NSArray arrayWithObjects:@"backingTrack", @"RobotR33", nil];
NSString* audioFileType = @"wav";

for (NSString* trackName in tracks) {
AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:trackName ofType:audioFileType]]options:nil];

AVMutableCompositionTrack* audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];

NSError* error;
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0] atTime:kCMTimeZero error:&error];
if (error)
{
NSLog(@"%@", [error localizedDescription]);
}
}
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A];

NSString* mixedAudio = @"mixedAudio.m4a";

NSString *exportPath = [NSTemporaryDirectory() stringByAppendingString:mixedAudio];
NSURL *exportURL = [NSURL fileURLWithPath:exportPath];

if ([[NSFileManager defaultManager]fileExistsAtPath:exportPath]) {
[[NSFileManager defaultManager]removeItemAtPath:exportPath error:nil];
}
_assetExport.outputFileType = AVFileTypeAppleM4A;
_assetExport.outputURL = exportURL;
_assetExport.shouldOptimizeForNetworkUse = YES;

[_assetExport exportAsynchronouslyWithCompletionHandler:^{
[self hideActivityIndicator];
NSLog(@"Completed Sucessfully");
}];

Concatenate two audio files in Swift and play them

I got your code working by changing two things:

  • the preset name: from AVAssetExportPresetPassthrough to AVAssetExportPresetAppleM4A

  • the output file type: from AVFileTypeWAVE to AVFileTypeAppleM4A

Modify your assetExport declaration like this:

var assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A)
assetExport.outputFileType = AVFileTypeAppleM4A

then it will properly merge the files.

It looks like AVAssetExportSession only exports M4A format and ignores other presets. There may be a way to make it export other formats (by subclassing it?), though I haven't explored this possibility yet.

Merging audio tracks in a single track using AVMutableComposition

OK, finally I should have found the solution. Really using the AVMutableAudioMix resulting movie file has only one audio track instead of two.

EDIT
Answering to Justin comment, here is the trick:

        let audioMix = AVMutableAudioMix()
let vip = AVMutableAudioMixInputParameters(track: self.videoAudioTrack!)
vip.trackID = self.videoAudioTrack!.trackID
vip.setVolume(self.videoAudioMixerVolume, at: .zero)
let aip = AVMutableAudioMixInputParameters(track: self.audioTrack!)
aip.trackID = self.audioTrack!.trackID
aip.setVolume(self.audioMixerVolume, at: .zero)
audioMix.inputParameters = [vip, aip]
easset.audioMix = audioMix

Where videoAudioTrack is the audio track for the video clip, wherease audioTrack is another simple audio track. easset is the AVAssetExporterSession object.

SWIFT - Is it possible to save audio from AVAudioEngine, or from AudioPlayerNode? If yes, how?

Yes, it's quite easy. You simply put a tap on a node and save the buffer into a file.

Unfortunately this means you have to play through the node. I was hoping that AVAudioEngine would let me process one sound file into another directly, but apparently that's impossible - you have to play and process in real time.

iOS - Mixing two audio files

This code merges the audio tracks down into one - I think that's thanks to the AVMutableComposition (and not the AVAssetExportSession):

AVAssetTrack *videoTrack = ...;
AVAssetTrack *audioTrack1 = ...;
AVAssetTrack *audioTrack2 = ...;

AVMutableComposition *composition = [AVMutableComposition composition];

for (AVAssetTrack* inputTrack in @[videoTrack, audioTrack1, audioTrack2]) {
AVMutableCompositionTrack* outputTrack;
outputTrack = [composition addMutableTrackWithMediaType:inputTrack.mediaType preferredTrackID:kCMPersistentTrackID_Invalid];

NSError* error;
if (![outputTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, inputTrack.timeRange.duration) ofTrack:inputTrack atTime:kCMTimeZero error:&error]) {
NSLog(@"insertTimeRange error: %@", error);
}
}

AVAssetExportSession* exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];

NSURL *outputURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0] URLByAppendingPathComponent:@"output.mp4"];
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];

exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.outputURL = outputURL;

[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (AVAssetExportSessionStatusCompleted == exportSession.status) {
NSLog(@"success %@", outputURL);
} else {
NSLog(@"failed %li", (long)exportSession.status);
}
}];

Setting multiple Volumes to each Video tracks using AudioMixInputParameters AVFoundation is not working in Swift iOS

Here is the working solution for my question:

func addVolumeToIndividualVideoClip(_ assetURL: URL, video: VideoFileModel, completion : ((_ session: AVAssetExportSession?, _ outputURL : URL?) -> ())?){

//Create Asset from Url
let filteredVideoAsset: AVAsset = AVAsset(url: assetURL)

video.fileID = String(video.videoID)

//Get the path of App Document Directory
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

let url = URL(fileURLWithPath: documentDirectory).appendingPathComponent("\(video.fileID)_\("FilterVideo").mov")

let filePath = url.path
let fileManager = FileManager.default

do {
if fileManager.fileExists(atPath: filePath) {
print("FILE AVAILABLE")
try fileManager.removeItem(atPath:filePath)
} else {
print("FILE NOT AVAILABLE")
}
} catch _ {
}

let composition: AVMutableComposition = AVMutableComposition()
let compositionVideo: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
let compositionAudioVideo: AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())

//Add video to the final record
do {
try compositionVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, filteredVideoAsset.duration), of: filteredVideoAsset.tracks(withMediaType: AVMediaTypeVideo)[0], at: kCMTimeZero)
} catch _ {
}

//Extract audio from the video and the music
let audioMix: AVMutableAudioMix = AVMutableAudioMix()
var audioMixParam: [AVMutableAudioMixInputParameters] = []

let assetVideoTrack: AVAssetTrack = filteredVideoAsset.tracks(withMediaType: AVMediaTypeAudio)[0]

let videoParam: AVMutableAudioMixInputParameters = AVMutableAudioMixInputParameters(track: assetVideoTrack)
videoParam.trackID = compositionAudioVideo.trackID

//Set final volume of the audio record and the music
videoParam.setVolume(video.videoVolume, at: kCMTimeZero)

//Add setting
audioMixParam.append(videoParam)

//Add audio on final record
//First: the audio of the record and Second: the music
do {
try compositionAudioVideo.insertTimeRange(CMTimeRangeMake(kCMTimeZero, filteredVideoAsset.duration), of: assetVideoTrack, at: kCMTimeZero)
} catch _ {
assertionFailure()
}

//Fading volume out for background music
let durationInSeconds = CMTimeGetSeconds(filteredVideoAsset.duration)

let firstSecond = CMTimeRangeMake(CMTimeMakeWithSeconds(0, 1), CMTimeMakeWithSeconds(1, 1))
let lastSecond = CMTimeRangeMake(CMTimeMakeWithSeconds(durationInSeconds-1, 1), CMTimeMakeWithSeconds(1, 1))

videoParam.setVolumeRamp(fromStartVolume: 0, toEndVolume: video.videoVolume, timeRange: firstSecond)
videoParam.setVolumeRamp(fromStartVolume: video.videoVolume, toEndVolume: 0, timeRange: lastSecond)

//Add parameter
audioMix.inputParameters = audioMixParam

//Remove the previous temp video if exist
let filemgr = FileManager.default
do {
if filemgr.fileExists(atPath: "\(video.fileID)_\("FilterVideo").mov") {
try filemgr.removeItem(atPath: "\(video.fileID)_\("FilterVideo").mov")
} else {
}
} catch _ {
}

//Exporte the final record’
let exporter: AVAssetExportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality)!
exporter.outputURL = url
exporter.outputFileType = AVFileTypeMPEG4
exporter.audioMix = audioMix

exporter.exportAsynchronously(completionHandler: { () -> Void in
completion!(exporter, url)

// self.saveVideoToLibrary(from: filePath)
})
}


Related Topics



Leave a reply



Submit