fbi*_*ich 5 audio macos cocoa objective-c
(更新:找到答案,见下文.)
我想在Objective-C Cocoa应用程序中播放1 kHz的正弦波音调; 我(尝试)将Swift示例转换为Objective-C,但某处必定存在错误,因为结果音调大约为440 Hz而不是1 kHz,并且仅在左侧通道上.
代码:
@property (nonatomic, strong) AVAudioEngine *audioEngine;
@property (nonatomic, strong) AVAudioPlayerNode *player;
@property (nonatomic, strong) AVAudioMixerNode *mixer;
@property (nonatomic, strong) AVAudioPCMBuffer *buffer;
// -----
self.audioEngine = [[AVAudioEngine alloc] init];
self.player = [[AVAudioPlayerNode alloc] init];
self.mixer = self.audioEngine.mainMixerNode;
self.buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:[self.player outputFormatForBus:0] frameCapacity:100];
self.buffer.frameLength = 100;
float amplitude = 0.4;
float frequency = 1000;
float sampleRate = [[self.mixer outputFormatForBus:0] sampleRate];
NSInteger channelCount = [[self.mixer outputFormatForBus:0] channelCount];
float *const *floatChannelData = self.buffer.floatChannelData;
float *p2 = *floatChannelData;
NSLog(@"Sine generator: sample rate = %.1f, %ld channels, frame length = %u.", sampleRate, (long)channelCount, self.buffer.frameLength);
for (int i = 0; i < self.buffer.frameLength ; i += channelCount) {
// a = Amplitude
// n = current sample
// r = Sample rate (samples / sec.)
//
// f(n) = a * sin( theta(n) )
// where theta(n) = 2 * M_PI * n / r
float theta = 441.0f * i * 2.0 * M_PI / sampleRate;
float value = sinf(theta);
p2[i] = value * amplitude;
}
[self.audioEngine attachNode:self.player];
[self.audioEngine connect:self.player to:self.mixer format:[self.player outputFormatForBus:0]];
[self.audioEngine startAndReturnError:nil];
[self.player play];
[self.player scheduleBuffer:self.buffer atTime:nil options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
Run Code Online (Sandbox Code Playgroud)
我怀疑行中有数学错误float theta=...,或者我在使用floatChannelData缓冲区时出错了.最初的Swift系列读取:
buffer.floatChannelData.memory[i] = val * 0.5
Run Code Online (Sandbox Code Playgroud)
不知道该怎么做float *const *的floatChannelData确切类型.我的理解是这是一个指向2 x float * const数组的指针.(2因为频道数量,左/右.)
Swift代码的来源如下:http://www.tmroyal.com/playing-sounds-in-swift-audioengine.html
如果有人能向我解释缓冲结构,那将是非常好的.
问题是双重的.首先,值441.0确实控制了频率.但仅仅改变这一点并没有解决问题; 由此产生的音调比正弦更像锯齿状,并找出原因.
因子441和44.1 kHz的采样率,这些值的比率是1:100 - 恰好是缓冲器中的样本数.将441更改为不是整数倍的值会导致"不完整"的正弦波:最后一个样本帧(#100)中的值不为零,这会在循环重新开始时导致急剧下降 - 这听起来像锯齿波.
我不得不将帧缓冲区长度更改为频率 - 采样率比率的精确(或倍数),以便最后一个样本值为(接近)零.
更新的代码:
self.audioEngine = [[AVAudioEngine alloc] init];
self.player = [[AVAudioPlayerNode alloc] init];
self.mixer = self.audioEngine.mainMixerNode;
float sampleRate = [[self.mixer outputFormatForBus:0] sampleRate];
AVAudioFrameCount frameBufferLength = floor(sampleRate / self.frequency) * 1;
self.buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:[self.player outputFormatForBus:0] frameCapacity:frameBufferLength];
self.buffer.frameLength = frameBufferLength;
NSInteger channelCount = [[self.mixer outputFormatForBus:0] channelCount];
float *const *floatChannelData = self.buffer.floatChannelData;
NSLog(@"Sine generator: sample rate = %.1f, %ld channels, frame length = %u.", sampleRate, (long)channelCount, self.buffer.frameLength);
for (int i = 0; i < self.buffer.frameLength ; i ++) {
float theta = self.frequency * i * 2.0 * M_PI / sampleRate;
float value = sinf(theta);
for (int channelNumber = 0; channelNumber < channelCount ; channelNumber++) {
float * const channelBuffer = floatChannelData[channelNumber];
channelBuffer[i] = value * self.amplitude;
}
}
Run Code Online (Sandbox Code Playgroud)
这样也可以正确处理任意数量的通道.
频率部分很简单:441.0f计算中的文字theta控制着它,所以只需将其更改为您想要的任何内容即可。
对于单声道问题,您似乎只写入一个数据通道:p2[i] = value * amplitude;如果您对 的组成是正确的floatChannelData,那么您需要这样:
float * const * floatChannelData = self.buffer.floatChannelData;
float * const left = floatChannelData[0];
float * const right = floatChannelData[1];
//...
// N.B. Changed the increment
for (int i = 0; i < self.buffer.frameLength ; i++ ) {
// ...
left[i] = value * amplitude;
right[i] = value * amplitude;
}
Run Code Online (Sandbox Code Playgroud)
但是,考虑到 for 循环中的增量步骤,缓冲区可能会交错(左通道和右通道在同一缓冲区中交替)。在这种情况下,您保留循环增量,但在每个步骤上写入 和p2[i](p2[i+1]对于立体声来说很容易;如果您有更多通道,您可以对这些通道进行内部循环并写入p2[j]从j0 到 $NUM_CHANNELS 的值)。
| 归档时间: |
|
| 查看次数: |
957 次 |
| 最近记录: |