Я пытаюсь изучить Core Audio и для начала я попытался создать базовый c аудиоустройство, которое просто генерирует синусоидальную волну с регулируемой частотой. Проблема, с которой я столкнулся сейчас, заключается в том, что у меня возникают сильные потрескивания и треск, когда я настраиваю частоту, и я не уверен, где искать причину этого.
Я даже не могу сказать, если это только потому, что я Мне нужно сделать некоторое сглаживание при переходе между частотами, хотя я пока не видел этого ни в одном примере.
В любом случае, вот реализация моего аудиоустройства:
#import "VCOUnit.h"
@interface VCOUnit () {
AUParameterTree *_parameterTree;
AUAudioUnitBusArray *_outputBusses;
AVAudioPCMBuffer *_outputBuffer;
}
@end
@implementation VCOUnit
- (instancetype)initWithComponentDescription:(AudioComponentDescription)componentDescription options:(AudioComponentInstantiationOptions)options error:(NSError *__autoreleasing _Nullable *)outError {
self = [super initWithComponentDescription:componentDescription options:options error:outError];
return self;
}
- (AUParameterTree *)parameterTree {
if (!_parameterTree) {
AUParameter *frequency = [AUParameterTree createParameterWithIdentifier:@"frequency"
name:@"Frequency"
address:0
min:10
max:500
unit:kAudioUnitParameterUnit_Hertz
unitName:nil
flags:0
valueStrings:nil
dependentParameters:nil];
frequency.value = 220.0;
_parameterTree = [AUParameterTree createTreeWithChildren:@[frequency]];
}
return _parameterTree;
}
- (AUAudioUnitBusArray *)outputBusses {
if (!_outputBusses) {
AVAudioFormat *format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:1];
AUAudioUnitBus *bus = [[AUAudioUnitBus alloc] initWithFormat:format error:nil];
_outputBusses = [[AUAudioUnitBusArray alloc] initWithAudioUnit:self busType:AUAudioUnitBusTypeOutput busses:@[bus]];
}
return _outputBusses;
}
- (BOOL)allocateRenderResourcesAndReturnError:(NSError *__autoreleasing _Nullable *)outError {
if (![super allocateRenderResourcesAndReturnError:outError]) {
return NO;
}
AVAudioFormat *format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:1];
_outputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:1024];
return YES;
}
- (AUInternalRenderBlock)internalRenderBlock {
AUParameter *frequencyParam = [self.parameterTree parameterWithAddress:0];
float deltaTime = 1.0 / 44100.0;
__block float time = 0;
return ^AUAudioUnitStatus(AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp *timestamp, AVAudioFrameCount frameCount, NSInteger outputBusNumber, AudioBufferList *outputData, const AURenderEvent *realtimeEventListHead, AURenderPullInputBlock pullInputBlock) {
AudioBufferList *abl = outputData;
for (int frame = 0; frame < frameCount; frame++) {
float value = sin(2.0 * M_PI * frequencyParam.value * time);
for (int i = 0; i < abl->mNumberBuffers; i++) {
float *output = abl->mBuffers[i].mData;
output[frame] = value;
}
time += deltaTime;
}
return noErr;
};
}
@end
У меня есть класс Swift, который обертывает его и который я использую для привязки частоты к ползунку SwiftUI:
class VCO: ObservableObject {
@Published var frequency = 220.0 {
didSet {
guard
let parameterTree = unit.auAudioUnit.parameterTree,
let freqParam = parameterTree.parameter(withAddress: 0) else {
return
}
freqParam.setValue(AUValue(frequency), originator: nil)
}
}
var unit: AVAudioUnit!
private let componentDescription = AudioComponentDescription(componentType: kAudioUnitType_Generator,
componentSubType: OSType(1),
componentManufacturer: 0x666f6f20,
componentFlags: 0,
componentFlagsMask: 0
)
init() {
AUAudioUnit.registerSubclass(VCOUnit.self, as: componentDescription, name: "VCO", version: 1)
}
func setup(sampleRate: Double) {
let opts = AudioComponentInstantiationOptions.init(rawValue: 0)
AVAudioUnit.instantiate(with: componentDescription, options: opts) { [weak self] (unit, error) in
guard let unit = unit else {
print(error!)
return
}
self?.unit = unit
}
}
}
Конечно, на данном этапе это не рабочий код, но я подозреваю причину появления трещин и всплывающих окон. это нечто вполне конкретное c. Я даже начинал думать, связано ли это с тем, что я использую привязки SwiftUI для обновления или что-то в этом роде, но это звучит немного надуманным. Скорее всего, проблема в блоке рендеринга, но я пока не вижу его. :)
В любом случае, любой вклад приветствуется.