首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在音频应用程序中使用诺沃卡因

在音频应用程序中使用诺沃卡因
EN

Stack Overflow用户
提问于 2012-11-08 14:38:32
回答 1查看 3.4K关注 0票数 5

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

我最初用AVAudioPlayer来播放,在模拟器里,每分钟12点,播放第16个音符唱得很棒,但在我的手机上,当我把节奏提高了60 bpm,只播放了1/4的音符,它就像狗一样跑了起来,没有时间。我的兴高采烈是非常短暂的。

为了减少延迟,我尝试使用Apple MixerHost项目作为音频引擎的模板,通过音频单元实现回放,但在我将其连接起来并将其连接起来之后,一直出现了一个糟糕的访问错误。

经过了几个小时的努力,我放弃了这条思路,转而选择了诺沃卡因的音频引擎。

我现在碰到了一块砖墙,试图把它和我的模型连接起来。

在最基本的层次上,我的模型是一个包含NSDictionary of Note的Neck对象。

每个Note都知道吉他脖子上的字符串和烦扰,并包含自己的AVAudioPlayer。

我构建了一个彩色吉他颈,包含122个音符(6个字符串,22个frets)或144个音符(6个字符串乘以24个frets),这取决于用户首选项中选择的颈部大小。

我使用这些笔记作为我的唯一真理点,所以所有由音乐引擎生成的标量笔记都是指向这个彩色音符桶的指针。

代码语言:javascript
运行
复制
@interface Note : NSObject <NSCopying>
{
    NSString *name;
    AVAudioPlayer *soundFilePlayer;
    int stringNumber;
    int fretNumber; 
}

我总是从所选音阶的根音符或和弦开始播放,然后生成要播放的音符,所以我总是在生成音符之后播放一个音符。这样,下一个要玩的笔记总是排好队准备出发。

这些Notes的回放控制是通过以下代码实现的:

代码语言:javascript
运行
复制
- (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按以下方式加载:

代码语言:javascript
运行
复制
- (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;
}

这就是我来的地方--一个种植者。

根据诺沃卡·吉顿的网页..。

播放音频

代码语言:javascript
运行
复制
Novocaine *audioManager = [Novocaine audioManager];
[audioManager setOutputBlock:^(float *audioToPlay, UInt32 numSamples, UInt32 numChannels) {
    // All you have to do is put your audio into "audioToPlay".
}];

但是在下载的项目中,使用以下代码加载音频.

代码语言:javascript
运行
复制
// 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);
}];

这里是我真正开始感到困惑的地方,因为第一个方法使用float,第二个方法使用URL。

如何将"caf“文件传递给浮点数?我不知道如何实施诺沃卡因--它在我的脑海中仍然是模糊的。

我希望有人能帮我解决以下问题.

  1. 诺沃卡因对象是否类似于AVAudioPlayer对象,只是更多功能,并被调整到最大延迟?即独立的音频播放(/录音/生成)单元?
  2. 我能用诺沃卡因作为我的模型吗?也就是说,一个诺沃卡因物体每色音符,还是我应该有一个诺沃卡对象,其中包含所有的彩色笔记?或者,我只是将URL存储在便条中,然后将其传递给诺沃卡因播放机?
  3. 当我的音频是"caf“文件,而"audioToPlay”接受浮动时,我如何将音频放进"audioToPlay“中?
  4. 如果我在Note.m中包含并声明了Novocaine属性,那么为了使用Novocaine对象,必须将类重命名为Note.mm吗?
  5. 如何同时演奏多个诺沃卡因物体,以再现和弦和音程?
  6. 我能循环诺沃卡对象的回放吗?
  7. 我可以设置音符的回放长度吗?即只演奏10秒音符1秒?
  8. 我可以修改上面的代码来使用诺沃卡因吗?
  9. 我为runMusicGenerator使用的方法是否是正确的方法,以便保持一个符合专业标准的节奏?
EN

回答 1

Stack Overflow用户

发布于 2012-12-26 23:10:17

诺沃卡因消除了手动设置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,都会被播放。

  1. 诺沃卡因为你设置了RemoteIO AudioUnit。这是一个低级别的CoreAudio API,与高级别的AVFoundation不同,并且像预期的那样非常低延迟.你说得对诺沃卡因是自成一体的。您可以实时记录、生成和处理音频。
  2. 诺沃卡因是单例,你不能有多个诺沃卡因实例。一种方法是将你的吉他声音或声音存储在一个单独的类或数组中,然后用诺沃卡因演奏它们,然后编写一堆方法。
  3. 你有很多选择。您可以使用诺沃卡因的AudioFileReader为您播放您的.caf文件。为此,您可以分配一个AudioFileReader,然后传递要播放的.caf文件的URL,如示例代码所示。然后将[fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels]插入块中,如示例代码所示。每次调用您的块时,AudioFileReader都会从磁盘抓取和缓冲一段音频,并将其放入audioToPlay中,然后播放。这样做有一些缺点。对于简短的声音(如你的吉他声音,我假设),反复调用retrieveFreshAudio是一个表演热门。通常情况下,对整个文件执行同步的连续读取进入内存是一个更好的主意(对于短声音而言)。诺沃卡因目前还没有提供一种方法来做到这一点。要做到这一点,您必须使用ExtAudioFileServices。Apple示例项目MixerHost详细介绍了如何做到这一点。
  4. 如果您正在使用AudioFileReader,是的。只有当您从Obj++标头或.mm #include ing C++标头中对#import进行重命名时才会重命名为C++。
  5. 如前所述,只允许一个Novocaine实例。您可以通过混合多个音频源来实现复调。这只是简单地将缓冲区添加到一起。如果你制作了不同音高的同一吉他声音的多个版本,只要把它们都读到记忆中,然后混合起来就可以了。如果你只想有一个吉他声音,那么你必须,在实时,改变回放率的多少音符,你正在演奏,然后混音。
  6. 诺沃卡因不知道你到底在玩什么,也不在乎你要玩多长时间的样本。为了循环一个声音,您必须保持一个计数,知道已经运行了多少个样本,检查您是否在声音的末尾,然后将这个计数设置为0。
  7. 是。假设采样率为44.1k,1秒音频= 44100个样本。然后,当计数达到44100时,就会重新设置计数。
  8. 是。看上去像这样。假设你有4个单音、长1秒以上的吉他音,并且你已经把它们读入内存float *guitarC*guitarE*guitarG*guitarB; (jazzy CMaj7 chord w00t),并想把它们混合起来1秒,然后用单曲回放:

[audioManager setOutputBlock:^(浮点数*数据,UInt32 numFrames,UInt32 numChannels){静态int计数= 0;for(int i=0;ivDSP\_vadd来增加性能。datacount = (guitarCcount + guitarEcount + guitarGcount + guitarBcount) * 0.25;if(++count >= 44100) count = 0;//播放混合1秒}];

  1. 不完全同意。使用performSelector或在运行循环或线程上调度的任何机制都不能保证精确。例如,当CPU负载波动时,您可能会遇到计时不正常的情况。使用音频块,如果你想要样本准确的时间。
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13291119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档