适用于带有AVFoundation的AVAudioUnitSampler的正确音量信封

如何正确创buildAVAudioUnitSampler的卷信封?

基本上我想添加一个攻击阶段,其中的体积在几毫秒内消失,以避免点击。 另外,我想添加一个释放阶段,以便在播放音符后音量消失。

我到目前为止做了什么:

我设置了一个代表我的采样率的全局计时器。 为此,我使用了一个间隔长度为1个样本的NSTimer(对于样本率44100,一个样本持续时间是1/44100)。 这样一个人不应该听到音量“跳跃”导致“点击”。 也许我需要过采样两倍的采样率。

只要增益低于某个阈值,定时器就会调用一个增加masterGain的函数。 通过将期望和当前增益的差值除以采样率,然后将该值除以所需的衰减时间,来计算增加的​​量。 (在下面的例子中,我使用一个固定的值来简化阅读)

达到阈值后,取出计时器并停止笔记。

– >我认为这种方法非常复杂,尤其是在我使用发布阶段的时候。

请检查我的示例代码。 我用UIButton touchDown来触发弹奏的音符。 请告诉我,如果你知道一个更简单的方法随着时间的推移自动化的价值/build立一个淡入/淡出的体积。

谢谢

import UIKit import AVFoundation class ViewController: UIViewController { var engine: AVAudioEngine! var sampler: AVAudioUnitSampler! var error: NSError? var timer: NSTimer = NSTimer() override func viewDidLoad() { super.viewDidLoad() engine = AVAudioEngine() sampler = AVAudioUnitSampler() engine.attachNode(sampler) engine.connect(sampler, to: engine.mainMixerNode, format: nil) if !engine!.startAndReturnError(&error) { if let error = error { println("Engine could not start!") println(error.localizedDescription) } } } @IBAction func touchDown(sender: UIButton, forEvent event: UIEvent) { sampler.startNote(66, withVelocity: 66, onChannel: 0) sampler.masterGain = -90 timer = NSTimer.scheduledTimerWithTimeInterval (1/44100, target: self, selector: "updateVolume", userInfo: nil, repeats: true) } func updateVolume() { if sampler.masterGain <= -10 { sampler.masterGain = sampler.masterGain + 0.1 println("volume updated") } else { timer.invalidate() } } } 

哇! 这是一个相当复杂的卷信封的方式!

你有没有想过让它变得更简单? 而不是试图控制增益控制,我只是修改出来的实际audio。 首先我将我的样本存储为一个向量。 然后,我会创build另一个向量来代表我的音量包络。 然后,我将创build一个新的播放样本,这将是样本和幅度向量的元素乘法元素。 这是matlab中的一个例子。

 %Load your sample [x, Fs] = audioread('filename.wav'); %get the length of your sample sampSize = length(x); %Create volume envelope. (Im just using a Gaussian window here. You can %replace it with any type of envelope you want so long as the values are %between 0 and 1.) ampEnv = gausswin(length(x)); %Multiply element by element of your two vectors. newSample = x.*ampEnv; %Plot your new sound. plot(newSample); 

另外,如果你想实时做,它会变得更加棘手。 如果在这里延迟不是一个大问题,我build议有一个轻微的前瞻性的窗口,将存储你的声音样本,并开始在那里应用幅度体积法。

最简单的方法是build立一个性能参数,检查AUSampler – 实时控制AUSampler的设置 。 然后设定你的攻击。 您也可以在创build预设时对这些值进行硬编码,但参数更加灵活。

编辑

目前优胜美地的AULab已经被打破,同时,我从自己的预设中检查了攻击所需的控制连接。 预设的格式是相当具体的,所以我不会感到惊讶,如果有一些冲突,但这适用于基本的设置。

这种方法只是一个临时的解决办法。 它所做的是检索预设(字典数组字典的数组字典的NSDictionary),然后将控件(NSDictionary)添加到“控件”数组。 然后你设置你的采样器预设。 这是在您从AVAudioUnitSampler的audioUnit属性访问的底层AUSampler上加载预设或样本后完成的。

 //this is the connection we will be adding NSMutableDictionary *attackConnection = [NSMutableDictionary dictionaryWithDictionary: @{@"ID" :@0, @"control" :@0, @"destination":@570425344, @"enabled" :[NSNumber numberWithBool:1], @"inverse" :[NSNumber numberWithBool:0], @"scale" :@10, @"source" :@73, @"transform" :@1, }]; AVAudioUnitSampler *sampler;//already initialized and loaded with samples or this won't work CFPropertyListRef presetPlist; UInt32 presetSize = sizeof(CFPropertyListRef); AudioUnitGetProperty(sampler.audioUnit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &presetPlist, &presetSize); NSMutableDictionary *mutablePreset = [NSMutableDictionary dictionaryWithDictionary:(__bridge NSDictionary *)presetPlist]; CFRelease(presetPlist); NSMutableDictionary *instrument = [NSMutableDictionary dictionaryWithDictionary: mutablePreset[@"Instrument"]]; NSArray *existingLayers = instrument[@"Layers"]; if (existingLayers.count) { NSMutableArray *layers = [[NSMutableArray alloc]init]; for (NSDictionary *layer in existingLayers){ NSMutableDictionary *mutableLayer = [NSMutableDictionary dictionaryWithDictionary:layer]; NSArray *existingConections = mutableLayer[@"Connections"]; if (existingConections) { attackConnection[@"ID"] = [NSNumber numberWithInteger:existingConections.count]; NSMutableArray *connections = [NSMutableArray arrayWithArray:existingConections]; [connections addObject:attackConnection]; [mutableLayer setObject:connections forKey:@"Connections"]; } else{ attackConnection[@"ID"] = [NSNumber numberWithInteger:0]; [mutableLayer setObject:@[attackConnection] forKey:@"Connections"]; } [layers addObject:mutableLayer]; } [instrument setObject:layers forKeyedSubscript:@"Layers"]; } else{ instrument[@"Layers"] = @[@{@"Connections":@[attackConnection]}]; } [mutablePreset setObject:instrument forKey:@"Instrument"]; CFPropertyListRef editedPreset = (__bridge CFPropertyListRef)mutablePreset; AudioUnitSetProperty(sampler.audioUnit,kAudioUnitProperty_ClassInfo,kAudioUnitScope_Global,0,&editedPreset,sizeof(presetPlist)); 

然后在这个连接之后,你设置这样的攻击。

 uint8_t value = 100; //0 -> 127 [sampler sendController:73 withValue:value onChannel:0];