[英]Reverse an audio file Swift/Objective-C
有没有办法可以反转和导出 .m4a 音频文件? 我在这里找到了反转音轨的解决方案,但它似乎只适用于 .caf 文件格式。 如果唯一的方法是使用 .caf,有没有办法先将 .m4a 文件转换为 .caf?
更新:在另一篇文章中,我发现 AVAssetReader 可用于从音频文件中读取音频样本,但我不知道如何以相反的顺序写回样本。 下面的代码片段是直接来自帖子的答案。 任何帮助,将不胜感激。 谢谢
+ (void) reverseAudioTrack: (AVAsset *)audioAsset outputURL: (NSURL *)outputURL {
NSError *error;
AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:audioAsset error:&error];
if (error) {NSLog(@"%@", error.localizedDescription);}
AVAssetTrack* track = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
NSMutableDictionary* audioReadSettings = [NSMutableDictionary dictionary];
[audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM]
forKey:AVFormatIDKey];
AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track outputSettings:audioReadSettings];
[reader addOutput:readerOutput];
[reader startReading];
CMSampleBufferRef sample; //= [readerOutput copyNextSampleBuffer];
NSMutableArray *samples = [[NSMutableArray alloc] init];
// Get all samples
while((sample = [readerOutput copyNextSampleBuffer])) {
[samples addObject:(__bridge id)sample];
CFRelease(sample);
}
// Process samples in reverse
AudioChannelLayout acl;
bzero(&acl, sizeof(acl));
acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:outputURL
fileType:AVFileTypeAppleM4A
error:&error];
if (error) {NSLog(@"%@", error.localizedDescription);}
NSDictionary *writerOutputSettings = [ NSDictionary dictionaryWithObjectsAndKeys:
[ NSNumber numberWithInt: kAudioFormatAppleLossless ], AVFormatIDKey,
[ NSNumber numberWithInt: 16 ], AVEncoderBitDepthHintKey,
[ NSNumber numberWithFloat: 44100.0 ], AVSampleRateKey,
[ NSNumber numberWithInt: 1 ], AVNumberOfChannelsKey,
[ NSData dataWithBytes: &acl length: sizeof( acl ) ], AVChannelLayoutKey, nil ];
AVAssetWriterInput *audioWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:writerOutputSettings];
[writer addInput:audioWriterInput];
[writer startWriting];
[writer startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp((__bridge CMSampleBufferRef)samples[0]) ];
// (1) Would it work if I loop in reverse here?
for (NSInteger i = 0; i < samples.count; i++) {
CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer((__bridge CMSampleBufferRef)samples[i]);
CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples((__bridge CMSampleBufferRef)samples[i]);
AudioBufferList audioBufferList;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer((__bridge CMSampleBufferRef)samples[i],
NULL,
&audioBufferList,
sizeof(audioBufferList),
NULL,
NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
&buffer
);
for (int bufferCount = 0; bufferCount < audioBufferList.mNumberBuffers; bufferCount++) {
SInt16* samples = (SInt16 *)audioBufferList.mBuffers[bufferCount].mData;
for (int i=0; i < numSamplesInBuffer; i++) {
// amplitude for the sample is samples[i], assuming you have linear pcm to start with
// (2) What should I be doing to write the samples into an audio file?
}
}
CFRelease(buffer);
}
是的,有一种方法可以处理并导出 iOS 支持的任何音频文件。
但是,这些格式中的大多数(例如mp3
)都是有损和压缩的。 您必须首先解压缩数据,应用转换,然后重新压缩。 您将应用于音频信息的大多数转换可能应该在原始 PCM 级别完成。
结合这两个语句,您可以通过几次来完成此操作:
kAudioFormatLinearPCM
兼容的音频文件,如AIFF
就像如果你对压缩的jpeg
图像应用转换一样,这个过程中也会有退化。 最终的音频充其量会再经历一次压缩循环。
因此,这种方法的真正数学答案实际上是否定的。
仅供参考,这里是 swift 3 中的一些入门代码。它需要进一步改进以跳过文件头。
var outAudioFile:AudioFileID?
var pcm = AudioStreamBasicDescription(mSampleRate: 44100.0,
mFormatID: kAudioFormatLinearPCM,
mFormatFlags: kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger,
mBytesPerPacket: 2,
mFramesPerPacket: 1,
mBytesPerFrame: 2,
mChannelsPerFrame: 1,
mBitsPerChannel: 16,
mReserved: 0)
var theErr = AudioFileCreateWithURL(destUrl as CFURL!,
kAudioFileAIFFType,
&pcm,
.eraseFile,
&outAudioFile)
if noErr == theErr, let outAudioFile = outAudioFile {
var inAudioFile:AudioFileID?
theErr = AudioFileOpenURL(sourceUrl as! CFURL, .readPermission, 0, &inAudioFile)
if noErr == theErr, let inAudioFile = inAudioFile {
var fileDataSize:UInt64 = 0
var thePropertySize:UInt32 = UInt32(MemoryLayout<UInt64>.stride)
theErr = AudioFileGetProperty(inAudioFile,
kAudioFilePropertyAudioDataByteCount,
&thePropertySize,
&fileDataSize)
if( noErr == theErr) {
let dataSize:Int64 = Int64(fileDataSize)
let theData = UnsafeMutableRawPointer.allocate(bytes: Int(dataSize),
alignedTo: MemoryLayout<UInt8>.alignment)
var readPoint:Int64 = Int64(dataSize)
var writePoint:Int64 = 0
while( readPoint > 0 )
{
var bytesToRead = UInt32(2)
AudioFileReadBytes( inAudioFile, false, readPoint, &bytesToRead, theData)
AudioFileWriteBytes( outAudioFile, false, writePoint, &bytesToRead, theData)
writePoint += 2
readPoint -= 2
}
theData.deallocate(bytes: Int(dataSize), alignedTo: MemoryLayout<UInt8>.alignment)
AudioFileClose(inAudioFile);
AudioFileClose(outAudioFile);
}
}
}
我从这个 repo 在 GitHub 上找到了一个解决方案: https : //github.com/tomisacat/AudioReverse
下面的功能对我来说非常适合反转 m4a 文件
func reverse(fromUrl: URL) -> URL? {
do {
let inFile: AVAudioFile = try AVAudioFile(forReading: fromUrl)
let format: AVAudioFormat = inFile.processingFormat
let frameCount: AVAudioFrameCount = UInt32(inFile.length)
let outSettings = [AVNumberOfChannelsKey: format.channelCount,
AVSampleRateKey: format.sampleRate,
AVLinearPCMBitDepthKey: 16,
AVFormatIDKey: kAudioFormatMPEG4AAC] as [String : Any]
let outputPath = NSTemporaryDirectory() + "/" + "reverse.m4a"
let outputUrl = URL(fileURLWithPath: outputPath)
let outFile: AVAudioFile = try AVAudioFile(forWriting: outputUrl, settings: outSettings)
let forwardBuffer: AVAudioPCMBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount)
let reverseBuffer: AVAudioPCMBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount)
try inFile.read(into: forwardBuffer)
let frameLength = forwardBuffer.frameLength
reverseBuffer.frameLength = frameLength
let audioStride = forwardBuffer.stride
for channelIdx in 0..<forwardBuffer.format.channelCount {
let forwardChannelData = forwardBuffer.floatChannelData?.advanced(by: Int(channelIdx)).pointee
let reverseChannelData = reverseBuffer.floatChannelData?.advanced(by: Int(channelIdx)).pointee
var reverseIdx: Int = 0
for idx in stride(from: frameLength, to: 0, by: -1) {
memcpy(reverseChannelData?.advanced(by: reverseIdx * audioStride), forwardChannelData?.advanced(by: Int(idx) * audioStride), MemoryLayout<Float>.size)
reverseIdx += 1
}
}
try outFile.write(from: reverseBuffer)
return outputUrl
} catch let error {
print(error.localizedDescription)
return nil
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.