- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试将2个CAF文件本地转换为单个文件。这两个CAF文件是单声道流,理想情况下,我希望它们是立体声文件,这样我就可以从一个声道获取麦克风,从另一个声道获取扬声器。
我最初从使用AVAssetTrack和AVMutableCompositionTracks开始,但是我无法解决混合问题。我合并的文件是一个单声道流,将两个文件交织在一起。所以我选择了AVAudioEngine路线。
根据我的理解,我可以传入两个文件作为输入节点,将它们附加到混音器,并具有一个能够获取立体声混音的输出节点。输出文件具有立体声布局,但是似乎没有音频数据写入其中,因为我可以在Audacity中打开它并查看立体声布局。在installTapOnBus调用周围放置一个dipatch sephamore信号也无济于事。任何见识将不胜感激,因为CoreAudio一直是理解的挑战。
// obtain path of microphone and speaker files
NSString *micPath = [[NSBundle mainBundle] pathForResource:@"microphone" ofType:@"caf"];
NSString *spkPath = [[NSBundle mainBundle] pathForResource:@"speaker" ofType:@"caf"];
NSURL *micURL = [NSURL fileURLWithPath:micPath];
NSURL *spkURL = [NSURL fileURLWithPath:spkPath];
// create engine
AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AVAudioFormat *stereoFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:16000 channels:2];
AVAudioMixerNode *mainMixer = engine.mainMixerNode;
// create audio files
AVAudioFile *audioFile1 = [[AVAudioFile alloc] initForReading:micURL error:nil];
AVAudioFile *audioFile2 = [[AVAudioFile alloc] initForReading:spkURL error:nil];
// create player input nodes
AVAudioPlayerNode *apNode1 = [[AVAudioPlayerNode alloc] init];
AVAudioPlayerNode *apNode2 = [[AVAudioPlayerNode alloc] init];
// attach nodes to the engine
[engine attachNode:apNode1];
[engine attachNode:apNode2];
// connect player nodes to engine's main mixer
stereoFormat = [mainMixer outputFormatForBus:0];
[engine connect:apNode1 to:mainMixer fromBus:0 toBus:0 format:audioFile1.processingFormat];
[engine connect:apNode2 to:mainMixer fromBus:0 toBus:1 format:audioFile2.processingFormat];
[engine connect:mainMixer to:engine.outputNode format:stereoFormat];
// start the engine
NSError *error = nil;
if(![engine startAndReturnError:&error]){
NSLog(@"Engine failed to start.");
}
// create output file
NSString *mergedAudioFile = [[micPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"merged.caf"];
[[NSFileManager defaultManager] removeItemAtPath:mergedAudioFile error:&error];
NSURL *mergedURL = [NSURL fileURLWithPath:mergedAudioFile];
AVAudioFile *outputFile = [[AVAudioFile alloc] initForWriting:mergedURL settings:[engine.inputNode inputFormatForBus:0].settings error:&error];
// write from buffer to output file
[mainMixer installTapOnBus:0 bufferSize:4096 format:[mainMixer outputFormatForBus:0] block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when){
NSError *error;
BOOL success;
NSLog(@"Writing");
if((outputFile.length < audioFile1.length) || (outputFile.length < audioFile2.length)){
success = [outputFile writeFromBuffer:buffer error:&error];
NSCAssert(success, @"error writing buffer data to file, %@", [error localizedDescription]);
if(error){
NSLog(@"Error: %@", error);
}
}
else{
[mainMixer removeTapOnBus:0];
NSLog(@"Done writing");
}
}];
最佳答案
使用ExtAudioFile进行此操作涉及三个文件和三个缓冲区。两个单声道用于阅读,一个立体声用于写作。在一个循环中,每个单声道文件都会将一小段音频读取到其单声道输出缓冲区,然后复制到立体声缓冲区的正确“一半”。然后,在立体声缓冲区充满数据的情况下,将该缓冲区写入输出文件,重复进行直到两个单声道文件读完为止(如果一个单声道文件长于另一个单声道文件,则写入零)。
对我而言,最有问题的领域是正确设置文件格式,core-audio要求非常特定的格式。幸运的是,存在AVAudioFormat可以简化某些常见格式的创建。
每个音频文件读取器/写入器都有两种格式,一种表示数据存储的格式(file_format),另一种指示读取器/写入器的输出格式(client_format)。如果格式不同,读取器/写入器中会内置格式转换器。
这是一个例子:
-(void)soTest{
//This is what format the readers will output
AVAudioFormat *monoClienFormat = [[AVAudioFormat alloc]initWithCommonFormat:AVAudioPCMFormatInt16 sampleRate:44100.0 channels:1 interleaved:0];
//This is the format the writer will take as input
AVAudioFormat *stereoClientFormat = [[AVAudioFormat alloc]initWithCommonFormat:AVAudioPCMFormatInt16 sampleRate:44100 channels:2 interleaved:0];
//This is the format that will be written to storage. It must be interleaved.
AVAudioFormat *stereoFileFormat = [[AVAudioFormat alloc]initWithCommonFormat:AVAudioPCMFormatInt16 sampleRate:44100 channels:2 interleaved:1];
NSURL *leftURL = [NSBundle.mainBundle URLForResource:@"left" withExtension:@"wav"];
NSURL *rightURL = [NSBundle.mainBundle URLForResource:@"right" withExtension:@"wav"];
NSString *stereoPath = [documentsDir() stringByAppendingPathComponent:@"stereo.wav"];
NSURL *stereoURL = [NSURL URLWithString:stereoPath];
ExtAudioFileRef leftReader;
ExtAudioFileRef rightReader;
ExtAudioFileRef stereoWriter;
OSStatus status = 0;
//Create readers and writer
status = ExtAudioFileOpenURL((__bridge CFURLRef)leftURL, &leftReader);
if(status)printf("error %i",status);//All the ExtAudioFile functins return a non-zero status if there's an error, I'm only checking one to demonstrate, but you should be checking all the ExtAudioFile function returns.
ExtAudioFileOpenURL((__bridge CFURLRef)rightURL, &rightReader);
//Here the file format is set to stereo interleaved.
ExtAudioFileCreateWithURL((__bridge CFURLRef)stereoURL, kAudioFileCAFType, stereoFileFormat.streamDescription, nil, kAudioFileFlags_EraseFile, &stereoWriter);
//Set client format for readers and writer
ExtAudioFileSetProperty(leftReader, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), monoClienFormat.streamDescription);
ExtAudioFileSetProperty(rightReader, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), monoClienFormat.streamDescription);
ExtAudioFileSetProperty(stereoWriter, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), stereoClientFormat.streamDescription);
int framesPerRead = 4096;
int bufferSize = framesPerRead * sizeof(SInt16);
//Allocate memory for the buffers
AudioBufferList *leftBuffer = createBufferList(bufferSize,1);
AudioBufferList *rightBuffer = createBufferList(bufferSize,1);
AudioBufferList *stereoBuffer = createBufferList(bufferSize,2);
//ExtAudioFileRead takes an ioNumberFrames argument. On input the number of frames you want, on otput it's the number of frames you got. 0 means your done.
UInt32 leftFramesIO = framesPerRead;
UInt32 rightFramesIO = framesPerRead;
while (leftFramesIO || rightFramesIO) {
if (leftFramesIO){
//If frames to read is less than a full buffer, zero out the remainder of the buffer
int framesRemaining = framesPerRead - leftFramesIO;
if (framesRemaining){
memset(((SInt16 *)leftBuffer->mBuffers[0].mData) + framesRemaining, 0, sizeof(SInt16) * framesRemaining);
}
//Read into left buffer
leftBuffer->mBuffers[0].mDataByteSize = leftFramesIO * sizeof(SInt16);
ExtAudioFileRead(leftReader, &leftFramesIO, leftBuffer);
}
else{
//set to zero if no more frames to read
memset(leftBuffer->mBuffers[0].mData, 0, sizeof(SInt16) * framesPerRead);
}
if (rightFramesIO){
int framesRemaining = framesPerRead - rightFramesIO;
if (framesRemaining){
memset(((SInt16 *)rightBuffer->mBuffers[0].mData) + framesRemaining, 0, sizeof(SInt16) * framesRemaining);
}
rightBuffer->mBuffers[0].mDataByteSize = rightFramesIO * sizeof(SInt16);
ExtAudioFileRead(rightReader, &rightFramesIO, rightBuffer);
}
else{
memset(rightBuffer->mBuffers[0].mData, 0, sizeof(SInt16) * framesPerRead);
}
UInt32 stereoFrames = MAX(leftFramesIO, rightFramesIO);
//copy left to stereoLeft and right to stereoRight
memcpy(stereoBuffer->mBuffers[0].mData, leftBuffer->mBuffers[0].mData, sizeof(SInt16) * stereoFrames);
memcpy(stereoBuffer->mBuffers[1].mData, rightBuffer->mBuffers[0].mData, sizeof(SInt16) * stereoFrames);
//write to file
stereoBuffer->mBuffers[0].mDataByteSize = stereoFrames * sizeof(SInt16);
stereoBuffer->mBuffers[1].mDataByteSize = stereoFrames * sizeof(SInt16);
ExtAudioFileWrite(stereoWriter, stereoFrames, stereoBuffer);
}
ExtAudioFileDispose(leftReader);
ExtAudioFileDispose(rightReader);
ExtAudioFileDispose(stereoWriter);
freeBufferList(leftBuffer);
freeBufferList(rightBuffer);
freeBufferList(stereoBuffer);
}
AudioBufferList *createBufferList(int bufferSize, int numberBuffers){
assert(bufferSize > 0 && numberBuffers > 0);
int bufferlistByteSize = sizeof(AudioBufferList);
bufferlistByteSize += sizeof(AudioBuffer) * (numberBuffers - 1);
AudioBufferList *bufferList = malloc(bufferlistByteSize);
bufferList->mNumberBuffers = numberBuffers;
for (int i = 0; i < numberBuffers; i++) {
bufferList->mBuffers[i].mNumberChannels = 1;
bufferList->mBuffers[i].mData = malloc(bufferSize);
}
return bufferList;
};
void freeBufferList(AudioBufferList *bufferList){
for (int i = 0; i < bufferList->mNumberBuffers; i++) {
free(bufferList->mBuffers[i].mData);
}
free(bufferList);
}
NSString *documentsDir(){
static NSString *path = NULL;
if(!path){
path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, 1).firstObject;
}
return path;
}
关于ios - 如何在iOS中将2个单声道文件转换为单个立体声文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42236176/
我正在尝试将一个字符串逐个字符地复制到另一个字符串中。目的不是复制整个字符串,而是复制其中的一部分(我稍后会为此做一些条件......) 但我不知道如何使用迭代器。 你能帮帮我吗? std::stri
我想将 void 指针转换为结构引用。 结构的最小示例: #include "Interface.h" class Foo { public: Foo() : mAddress((uint
这有点烦人:我有一个 div,它从窗口的左上角开始过渡,即使它位于文档的其他任何位置。我试过 usign -webkit-transform-origin 但没有成功,也许我用错了。有人可以帮助我吗?
假设,如果将 CSS3 转换/转换/动画分配给 DOM 元素,我是否可以检测到该过程的状态? 我想这样做的原因是因为我正在寻找类似过渡链的东西,例如,在前一个过渡之后运行一个过渡。 最佳答案 我在 h
最近我遇到了“不稳定”屏幕,这很可能是由 CSS 转换引起的。事实上,它只发生在 Chrome 浏览器 上(可能还有 Safari,因为一些人也报告了它)。知道如何让它看起来光滑吗?此外,您可能会注意
我正在开发一个简单的 slider ,它使用 CSS 过渡来为幻灯片设置动画。我用一些基本样式和一些 javascript 创建了一支笔 here .注意:由于 Codepen 使用 Prefixfr
我正在使用以下代码返回 IList: public IList FindCodesByCountry(string country) { var query =
如何设计像这样的操作: 计算 转化 翻译 例如:从“EUR”转换为“CNY”金额“100”。 这是 /convert?from=EUR&to=CNY&amount=100 RESTful 吗? 最佳答
我使用 jquery 组合了一个图像滚动器,如下所示 function rotateImages(whichHolder, start) { var images = $('#' +which
如何使用 CSS (-moz-transform) 更改一个如下所示的 div: 最佳答案 你可以看看Mozilla Developer Center .甚至还有例子。 但是,在我看来,您的具体示例不
我需要帮助我正在尝试在选中和未选中的汉堡菜单上实现动画。我能够为菜单设置动画,但我不知道如何在转换为 0 时为左菜单动画设置动画 &__menu { transform: translateX(
我正在为字典格式之间的转换而苦苦挣扎:我正在尝试将下面的项目数组转换为下面的结果数组。本质上是通过在项目第一个元素中查找重复项,然后仅在第一个参数不同时才将文件添加到结果集中。 var items:[
如果我有两个定义相同的结构,那么在它们之间进行转换的最佳方式是什么? struct A { int i; float f; }; struct B { int i; float f; }; void
我编写了一个 javascript 代码,可以将视口(viewport)从一个链接滑动到另一个链接。基本上一切正常,你怎么能在那里看到http://jsfiddle.net/DruwJ/8/ 我现在的
我需要将文件上传到 meteor ,对其进行一些图像处理(必要时进行图像转换,从图像生成缩略图),然后将其存储在外部图像存储服务器(s3)中。这应该尽可能快。 您对 nodejs 图像处理库有什么建议
刚开始接触KDB+,有一些问题很难从Q for Mortals中得到。 说,这里 http://code.kx.com/wiki/JB:QforMortals2/casting_and_enumera
我在这里的一个项目中使用 JSF 1.2 和 IceFaces 1.8。 我有一个页面,它基本上是一大堆浮点数字段的大编辑网格。这是通过 inputText 实现的页面上的字段指向具有原始值的值对象
ScnMatrix4 是一个 4x4 矩阵。我的问题是什么矩阵行对应于位置(ScnVector3),旋转(ScnVector4),比例(ScnVector3)。第 4 行是空的吗? 编辑: 我玩弄了
恐怕我是 Scala 新手: 我正在尝试根据一些简单的逻辑将 Map 转换为新 Map: val postVals = Map("test" -> "testing1", "test2" -> "te
输入: This is sample 1 This is sample 2 输出: ~COLOR~[Green]This is sample 1~COLOR~[Red]This is sam
我是一名优秀的程序员,十分优秀!