我想将音频单元创建的自定义视图嵌入到使用自动布局的窗口中。在尝试了数十种组合之后,我仍然没有找到一种方法来做到这一点,它适用于大量的音频单元。
目标是使用音频单元视图作为子视图创建容器NSView
对象,并以容器大小与 AU 视图的大小匹配的方式设置约束,包括后者调整自身大小时。容器应该translatesAutoresizingMaskIntoConstraints
设置为NO
,因此它可以与窗口的其余部分很好地配合使用。
我的第一个尝试是创建两个约束,简单地强制两个(容器和 AU(视图的大小相等。这将失败,因为 AU 视图随后折叠到非常小的大小。
另一个尝试是为容器视图创建固定大小约束,这些约束初始化以匹配 AU 视图,侦听 AU 视图的NSViewFrameDidChangeNotification
,并根据需要调整固定大小。一旦 AU 视图想要调整自身大小,就会崩溃。原因是当我增加容器的大小以匹配 AU 视图时,自动调整大小掩码约束指示 AU 视图再次增加其大小,从而导致无限循环。
到目前为止,最有效的方法是关闭 AU 视图的translatesAutoresizingMaskIntoConstraints
,并通过侦听帧更改通知来设置容器的大小。这似乎适用于所有 AUv2 音频单元,但对于 AUv3 音频单元(特别是 Apple 的演示 AUv3(,我总是得到 (1,1( 的视图大小,这显然是无用的。我将非常感谢有关如何使这项工作的任何见解......
根据您提供的示例项目,这应该可以工作:
@interface MyViewController ()
{
NSLayoutConstraint *widthConstraint, *heightConstraint;
}
@end
@implementation MyViewController
-(void)setFormatForBus:(AUAudioUnitBus*)bus
{
AudioStreamBasicDescription fmt = {44100, kAudioFormatLinearPCM, kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked, 4, 1, 4, 2, 32, 0}; // Stereo audio on each bus
AVAudioFormat* format = [[AVAudioFormat alloc] initWithStreamDescription:&fmt];
NSError* error = nil;
if (![bus setFormat:format error:&error]) {
NSLog(@"Couldn't set the format for bus... %@", error);
return;
}
bus.enabled = YES;
}
-(void)doLoad:(AVAudioUnit*)node controller:(NSViewController*)viewController
{
NSView* auView = nil;
if (viewController == nil) {
auView = [[AUGenericView alloc] initWithAudioUnit:node.audioUnit displayFlags:(AUViewPropertiesDisplayFlag | AUViewParametersDisplayFlag)];
auView.frame = self.view.frame;
[self.view addSubview:auView];
}
else {
self.auViewController = viewController;
auView = self.auViewController.view;
auView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:auView];
}
NSSize size = _auViewController.view.frame.size;
widthConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:size.width];
heightConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:size.height];
[self.view addConstraints:@[widthConstraint, heightConstraint]];
NSLayoutConstraint* top = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:auView
attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
NSLayoutConstraint* bottom = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:auView
attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
NSLayoutConstraint* left = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:auView
attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
NSLayoutConstraint* right = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:auView
attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
[self.view addConstraints:@[top, bottom, left, right]];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSArray* list = [[AVAudioUnitComponentManager sharedAudioUnitComponentManager] componentsPassingTest: ^BOOL(AVAudioUnitComponent *comp, BOOL *stop) {
return [comp.typeName containsString:@"Effect"];
}];
AVAudioUnitComponent* component = nil;
for (AVAudioUnitComponent* comp in list) {
if ([comp.name containsString:@"MultibandCompressor"]) // That allows itself to be squashed. Also, it doesn't resize itself when "details" is toggled
// if ([comp.name containsString:@"FilterDemo"]) // That one is squashed both vertically and horizontally... (Apple's sample AUv3 Audio Unit, needs to be installed first.)
// if ([comp.name containsString:@"GraphicEQ"]) // That one is squashed vertically
// if ([comp.name containsString:@"AUDelay"])
component = comp;
}
[AVAudioUnit instantiateWithComponentDescription:component.audioComponentDescription options:0
completionHandler:^(__kindof AVAudioUnit *audioUnit, NSError *error) {
if (error) {
NSLog(@"Error instantiating AU: %@", error);
return;
}
self.audioUnit = audioUnit;
AUAudioUnitBusArray* inputBusses = audioUnit.AUAudioUnit.inputBusses;
AUAudioUnitBusArray* outputBusses = audioUnit.AUAudioUnit.outputBusses;
if (!inputBusses.count || !outputBusses.count) {
NSLog(@"No busses...");
return;
}
[self setFormatForBus:[inputBusses objectAtIndexedSubscript:0]];
[self setFormatForBus:[outputBusses objectAtIndexedSubscript:0]];
NSError* AUerror = nil;
if (![audioUnit.AUAudioUnit allocateRenderResourcesAndReturnError:&AUerror]){
NSLog(@"Error allocating resources: %@", AUerror);
return;
}
[audioUnit.AUAudioUnit requestViewControllerWithCompletionHandler:^(NSViewController* viewController) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doLoad:audioUnit controller:viewController];
});
}];
}];
}
- (void)viewWillLayout
{
[super viewWillLayout];
if(_auViewController != nil) {
[self.view removeConstraints:@[widthConstraint, heightConstraint]];
NSSize size = _auViewController.view.frame.size;
widthConstraint.constant = size.width;
heightConstraint.constant = size.height;
[self.view addConstraints:@[widthConstraint, heightConstraint]];
}
}
@end