我正在构建一个iPhone应用程序,该应用程序通过播放“caf”格式的单个录制的吉他音符来生成随机的吉他音乐。这些音符的持续时间从3到11秒不等,具体取决于延音的量。

我最初使用AVAudioPlayer进行播放,并在模拟器上以120 bpm的速度播放16音符,其唱出的音色优美,但在我的手机上,
仅演奏1/4音符就将速度提高到60 bpm一点,它像狗一样奔跑,不会及时走动。我的兴高采烈是短暂的。

为了减少延迟,我尝试使用Apple MixerHost项目作为音频引擎的模板通过音频单元实现播放,但是在将其连接并连接好所有组件后,仍然出现严重的访问错误。

经过数小时的努力,我放弃了这种想法,转而选择了Novocaine音频引擎。

我现在遇到一堵砖墙,试图将其连接到我的模型。

在最基本的级别上,我的模型是一个Neck对象,其中包含NSDictionary of Note对象。

每个Note对象都知道它在吉他脖子上的弦线和品格,并包含其自己的AVAudioPlayer。

根据用户首选项中所选择的琴颈尺寸,我构建了一个彩色吉他琴颈,其中包含122个音符(6弦乘22品格)或144个音符(6弦乘24品格)。

我将这些音符用作我的单一事实点,因此音乐引擎生成的所有标量音符都是指向此半音符音符桶的指针。

@interface Note : NSObject <NSCopying>
{
    NSString *name;
    AVAudioPlayer *soundFilePlayer;
    int stringNumber;
    int fretNumber;
}

我总是从选定音阶的根音符或和弦开始播放,然后生成要播放的音符,因此我总是在生成的音符后面播放一个音符。这样,下一个要播放的音符总是排队准备就绪。

通过以下代码可以实现对这些Notes的播放控制:
- (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];
    }

每个音符的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”文件传递给float?我不确定如何实现Novocaine-在我脑海中仍然很模糊。

我希望有人可以帮助我的问题如下...
  • Novocaine对象是否类似于AVAudioPlayer对象,只是用途更多,并且已调整到最大以最小延迟?即自包含的音频播放(/记录/生成)单元?
  • 我可以直接在模型中使用Novocaine吗?即每个彩色音符1个Novocaine对象,或者我应该有1个包含所有色音符的novocain对象?还是只将URL存储在笔记中,然后将其传递给Novocaine播放器?
  • 当我的音频是“caf”文件并且“audioToPlay”处于 float 状态时,如何将我的音频放入“audioToPlay”中?
  • 如果我在Note.m中包含并声明了Novocaine属性,我是否必须将类重命名为Note.mm才能使用Novocaine对象?
  • 如何同时播放多个Novocaine对象,以重现和弦和音程?
  • 是否可以循环Novocaine对象的播放?
  • 我可以设置音符的播放长度吗?即播放10秒音符仅1秒?
  • 我可以修改上面的代码以使用Novocaine吗?
  • 我用于runMusicGenerator的方法是否正确,以便维持符合专业标准的速度?
  • 最佳答案

    Novocaine消除了您手动设置RemoteIO AudioUnit的麻烦,从而使您的生活更加轻松。这包括必须痛苦地填充一堆CoreAudio结构,并提供一堆回调,例如此音频过程回调。
    static OSStatus PerformThru(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
    相反,Novocaine在其实现中处理该问题,然后调用您通过执行此操作设置的块。
    [audioManager setOutputBlock: ^(float *audioToPlay, UInt32 numSamples, UInt32 numChannels){} ];
    无论您写什么audioToPlay都会被播放。

  • Novocaine为您设置RemoteIO AudioUnit。这是一个低级CoreAudio API,不同于高级AVFoundation,并且延迟很短。你是对的,因为诺沃卡因是独立的。您可以实时记录,生成和处理音频。
  • Novocaine是一个单例,不能有多个Novocaine实例。一种方法是将吉他声音/声音存储在单独的类或数组中,然后编写一些方法,使用Novocaine进行演奏。
  • 您有很多选择。您可以使用Novocaine的AudioFileReader来为您播放.caf文件。为此,您可以分配一个AudioFileReader,然后按照示例代码传递要播放的.caf文件的URL。然后,按照示例代码,将[fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels]粘贴到您的块中。每次您的块被调用时,AudioFileReader都会从磁盘中抓取并缓冲一部分音频,并将其放入audioToPlay中,随后该音频将被播放。这有一些缺点。对于简短的声音(例如我假设的吉他声音),反复调用retrieveFreshAudio会提高性能。对于整个声音,将整个文件同步,顺序地读取到内存中通常是一个更好的主意(对于短声音)。 Novocaine尚未提供执行此操作的方法(尚未)。您将必须使用ExtAudioFileServices来执行此操作。 Apple示例项目MixerHost详细说明了如何执行此操作。
  • 如果您使用的是AudioFileReader,则为yes。仅当从Obj-C++ header 进行#import编码或C++ header 进行#include编码时,才重命名为.mm。
  • 如前所述,仅允许1个Novocaine实例。您可以通过混合多个音频源来实现复音。这只是将缓冲区加在一起。如果您以不同的音调制作了同一吉他声音的多个版本,只需将它们全部读入内存中,然后混合即可。如果您只想拥有一种吉他声音,则必须实时更改要弹奏的所有音符的播放速率,然后进行混音。
  • Novocaine与您实际玩的游戏无关,并且不在乎您要玩多长时间。为了循环播放声音,您必须维护已经过多少个采样的计数,检查您是否处于声音结尾,然后将该计数重新设置为0。
  • 是的。假设采样率为44.1k,则1秒钟的音频= 44100个采样。然后,当计数达到44100时,您将重置计数。
  • 是的。看起来像这样。假设您有4种单声道且超过1秒长的吉他声音,并且已将它们读入内存float *guitarC*guitarE*guitarG*guitarB;(爵士CMaj7和弦w00t),并想将其混合1秒钟并循环回去在单声道中:


  • 不完全是。不保证使用performSelector或在运行循环或线程上调度的任何机制都是精确的。例如,当CPU负载波动时,您可能会遇到时序不规则的情况。如果您想采样准确的时间,请使用音频块。
  • 关于iphone - 在音频应用程序中使用Novocaine,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13291119/

    10-12 06:19