我正在构建一个 iPhone 应用程序,它通过以“caf”格式播放单个录制的吉他音符来生成随机吉他音乐。这些音符的持续时间从 3 秒到 11 秒不等,具体取决于延音量。
我最初使用 AVAudioPlayer 进行播放,在模拟器中以 120 bpm 播放 16 分音符,它唱得很好,但在我的手机上,只要我将速度提高到 60 bpm 以上,只播放 1/4 个音符,它就跑起来像一条狗,不会及时赶上。我的兴高采烈很短暂。
为了减少延迟,我尝试使用 Apple MixerHost 项目作为音频引擎的模板通过 Audio Units 实现播放,但在我将其固定并连接所有内容后,一直出现错误的访问错误。
经过几个小时的努力,我放弃了这种想法,转而使用 Novocaine 音频引擎。
我现在遇到了一堵砖墙,试图将它与我的模型连接起来。
在最基本的层面上,我的模型是一个包含 Note 对象的 NSDictionary 的 Neck 对象。
每个 Note 对象都知道它所在的吉他琴颈的琴弦和音品,并包含自己的 AVAudioPlayer。
我构建了一个半音吉他琴颈,其中包含 122 个音符(6 弦 x 22 品)或 144 个音符(6 弦 x 24 品),具体取决于用户偏好中选择的琴颈尺寸。
我使用这些音符作为我的单一事实点,因此音乐引擎生成的所有标量音符都是指向这个半音音符桶的指针。
@interface Note : NSObject <NSCopying>
{
NSString *name;
AVAudioPlayer *soundFilePlayer;
int stringNumber;
int fretNumber;
}
我总是从所选音阶的根音符或和弦开始播放,然后生成要播放的音符,所以我总是在生成的音符后面播放一个音符。这样,下一个要播放的音符总是排队等待播放。
这些音符的播放控制是通过以下代码实现的:
- (void)runMusicGenerator:(NSNumber *)counter
{
if (self.isRunning) {
Note *NoteToPlay;
// pulseRate is the time interval between beats
// staticNoteLength = 1/4 notes, 1/8th notes, 16th notes, etc.
float delay = self.pulseRate / [self grabStaticNoteLength];
// user setting to play single, double or triplet notes.
if (self.beatCounter == CONST_BEAT_COUNTER_INIT_VAL) {
NoteToPlay = [self.GuitarNeck generateNoteToPlayNext];
} else {
NoteToPlay = [self.GuitarNeck cloneNote:self.GuitarNeck.NoteToPlayNow];
}
self.GuitarNeck.NoteToPlayNow = NoteToPlay;
[self callOutNoteToPlay];
[self performSelector:@selector(runDrill:) withObject:NoteToPlay afterDelay:delay];
}
- (Note *)generateNoteToPlayNext
{
if ((self.musicPaused) || (self.musicStopped)) {
// grab the root note on the string to resume
self.NoteToPlayNow = [self grabRootNoteForString];
//reset the flags
self.musicPaused = NO;
self.musicStopped = NO;
} else {
// Set NoteRingingOut to NoteToPlayNow
self.NoteRingingOut = self.NoteToPlayNow;
// Set NoteToPlaNowy to NoteToPlayNext
self.NoteToPlayNow = self.NoteToPlayNext;
if (!self.NoteToPlayNow) {
self.NoteToPlayNow = [self grabRootNoteForString];
// now prep the note's audio player for playback
[self.NoteToPlayNow.soundFilePlayer prepareToPlay];
}
}
// Load NoteToPlayNext
self.NoteToPlayNext = [self generateRandomNote];
}
- (void)callOutNoteToPlay
{
self.GuitarNeck.NoteToPlayNow.soundFilePlayer.delegate = (id)self;
[self.GuitarNeck.NoteToPlayNow.soundFilePlayer setVolume:1.0];
[self.GuitarNeck.NoteToPlayNow.soundFilePlayer setCurrentTime:0];
[self.GuitarNeck.NoteToPlayNow.soundFilePlayer play];
}
每个 Note 的 AVAudioPlayer 加载如下:
- (AVAudioPlayer *)buildStringNotePlayer:(NSString *)nameOfNote
{
NSString *soundFileName = @"S";
soundFileName = [soundFileName stringByAppendingString:[NSString stringWithFormat:@"%d", stringNumber]];
soundFileName = [soundFileName stringByAppendingString:@"F"];
if (fretNumber < 10) {
soundFileName = [soundFileName stringByAppendingString:@"0"];
}
soundFileName = [soundFileName stringByAppendingString:[NSString stringWithFormat:@"%d", fretNumber]];
NSString *soundPath = [[NSBundle mainBundle] pathForResource:soundFileName ofType:@"caf"];
NSURL *fileURL = [NSURL fileURLWithPath:soundPath];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
return notePlayer;
}
这是我来收割机的地方。
根据 Novocaine Github 页面...
播放音频
Novocaine *audioManager = [Novocaine audioManager];
[audioManager setOutputBlock:^(float *audioToPlay, UInt32 numSamples, UInt32 numChannels) {
// All you have to do is put your audio into "audioToPlay".
}];
但是在下载的项目中,您使用以下代码来加载音频...
// AUDIO FILE READING OHHH YEAHHHH
// ========================================
NSURL *inputFileURL = [[NSBundle mainBundle] URLForResource:@"TLC" withExtension:@"mp3"];
fileReader = [[AudioFileReader alloc]
initWithAudioFileURL:inputFileURL
samplingRate:audioManager.samplingRate
numChannels:audioManager.numOutputChannels];
[fileReader play];
fileReader.currentTime = 30.0;
[audioManager setOutputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels)
{
[fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels];
NSLog(@"Time: %f", fileReader.currentTime);
}];
这是我真正开始感到困惑的地方,因为第一种方法使用浮点数,而第二种方法使用 URL。
你如何将“caf”文件传递给浮点数?我不知道如何实施 Novocaine - 它在我的脑海中仍然很模糊。
我希望有人可以帮助我的问题如下...
Novocaine 对象是否类似于 AVAudioPlayer 对象,只是更通用并调整到最大以实现最小延迟?即独立的音频播放(/录音/生成)单元?
我可以在我的模型中原样使用 Novocaine 吗?即每个半音音符 1 个 Novocaine 对象,还是我应该有 1 个包含所有半音音符的 novocain 对象?还是我只是将 URL 存储在便笺中并将其传递给 Novocaine 播放器?
当我的音频是“caf”文件并且“audioToPlay”浮动时,如何将音频放入“audioToPlay”?
如果我在 Note.m 中包含并声明了 Novocaine 属性,我是否必须将类重命名为 Note.mm 才能使用 Novocaine 对象?
如何同时演奏多个 Novocaine 对象以重现和弦和音程?
我可以循环播放 Novocaine 对象吗?
我可以设置音符的播放长度吗?即只播放 1 秒的 10 秒音符?
我可以修改上面的代码来使用 Novocaine 吗?
我用于 runMusicGenerator 的方法是否正确,以保持符合专业标准的节奏?