Sarim Ashfaq
Sarim Ashfaq

Reputation: 286

Change preferred volume of AVAsset

Is it possible to increase/decrease volume of AVAsset track or AVMutableComposition of an audio file? I have two audio files (background instrumental and recorded song), I want to decrease one file's volume and merge it with the other.

Upvotes: 1

Views: 1129

Answers (1)

WoodyDev
WoodyDev

Reputation: 1476

1. Change the Track's volume

To do this to the physical file, you will need to load the raw PCM data into Swift. Below is an example of getting the floating point data thanks to this SO post:

import AVFoundation
// ...

let url = NSBundle.mainBundle().URLForResource("your audio file", withExtension: "wav")
let file = try! AVAudioFile(forReading: url!)
let format = AVAudioFormat(commonFormat: .PCMFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: 1, interleaved: false)

let buf = AVAudioPCMBuffer(PCMFormat: format, frameCapacity: 1024)
try! file.readIntoBuffer(buf)

// this makes a copy, you might not want that
let floatArray = Array(UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength)))

print("floatArray \(floatArray)\n")

Once you have the data in your floatArray, simply multiply every value in the array by a number between 0 and 1 to adjust the gain. If you are more familiar with decibels then put your decibel value into the following line, and multiply every array value by the linGain:

var linGain = pow(10.0f, decibelGain/20.0f).

Then it's a question of writing the audio file back again before you load it (credit):

let SAMPLE_RATE =  Float64(16000.0)

let outputFormatSettings = [
    AVFormatIDKey:kAudioFormatLinearPCM,
    AVLinearPCMBitDepthKey:32,
    AVLinearPCMIsFloatKey: true,
    //  AVLinearPCMIsBigEndianKey: false,
    AVSampleRateKey: SAMPLE_RATE,
    AVNumberOfChannelsKey: 1
    ] as [String : Any]

let audioFile = try? AVAudioFile(forWriting: url, settings: outputFormatSettings, commonFormat: AVAudioCommonFormat.pcmFormatFloat32, interleaved: true)

let bufferFormat = AVAudioFormat(settings: outputFormatSettings)

let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat, frameCapacity: AVAudioFrameCount(buff.count))

// i had my samples in doubles, so convert then write

for i in 0..<buff.count {
    outputBuffer.floatChannelData!.pointee[i] = Float( buff[i] )
}
outputBuffer.frameLength = AVAudioFrameCount( buff.count )

do{
    try audioFile?.write(from: outputBuffer)

} catch let error as NSError {
    print("error:", error.localizedDescription)
}

2. Mix the Tracks Together

Once you have your new .wav files of your audio, you can load both into your AVAssets like before, but this time with the desired gain you applied before.

Then it looks like you will want to be using the AVAssetReaderAudioMixOutput which has a method specifically for mixing together two audio tracks.

AVAssetReaderAudioMixOutput.init(audioTracks: [AVAssetTrack], audioSettings: [String : Any]?)

Note: I would not continuously use steps 1 & 2 for example if you wanted to mix the song with a slider and hear the result, I would recommend using AVPlayer's and adjust their volume and then when the user is ready, call this file IO and mixing.

Upvotes: 1

Related Questions