帮助实时更新 iPhone 上的声音

发布于 2024-10-04 08:44:35 字数 2298 浏览 4 评论 0原文

我的问题有点棘手,而且我经验不足(我可能会弄错一些术语),所以就这样吧。 我正在声明一个名为“Singer”的对象的实例。该实例称为“singer1”。 “singer1”产生音频信号。现在,以下是确定音频信号细节的代码:

OSStatus playbackCallback(void *inRefCon,
                      AudioUnitRenderActionFlags *ioActionFlags,
                      const AudioTimeStamp *inTimeStamp,
                      UInt32 inBusNumber, 
                      UInt32 inNumberFrames,
                      AudioBufferList *ioData) {    

//Singer *me = (Singer *)inRefCon;

static int phase = 0;

for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) {

    int samples = ioData->mBuffers[i].mDataByteSize / sizeof(SInt16);

    SInt16 values[samples];

    float waves;
    float volume=.5;

    for(int j = 0; j < samples; j++) {


        waves = 0;


        waves += sin(kWaveform * 600 * phase)*volume;
        waves += sin(kWaveform * 400 * phase)*volume;
        waves += sin(kWaveform * 200 * phase)*volume;
        waves += sin(kWaveform * 100 * phase)*volume;            

        waves *= 32500 / 4; // <--------- make sure to divide by how many waves you're stacking

        values[j] = (SInt16)waves;
        values[j] += values[j]<<16;

        phase++;

    }

    memcpy(ioData->mBuffers[i].mData, values, samples * sizeof(SInt16));

}

return noErr;

}

其中 99% 是借用的代码,因此我对其工作原理只有基本了解(我不知道 OSStatus 类或方法或不管这是什么。但是,你看到那 4 行有 600、400、200 和 100 吗?它们决定了频率。现在,我想做的是在其中插入我自己的变量来代替常量。 ,我可以随心所欲地更改该变量,该变量称为“fr1”。“fr1”是在头文件中声明的,但如果我尝试编译,则会出现有关“fr1”未声明的错误。如下:在我#import东西的下面,我添加了行

fr1=0.0;//any number will work properly

这种工作,因为代码将编译并且single1.fr1实际上会改变值,如果我告诉它的话,现在的问题是这样的:A)甚至。虽然此编译并且将播放指定的音调(0.0 是无音调),但我收到警告“数据定义没有类型或存储类”和“在‘fr1’声明中类型默认为‘int’”。我敢打赌这是因为由于某种原因它没有在头文件中看到我之前的声明(作为浮点数)。但是,如果我忽略这一行,代码将无法编译,因为“fr1 未声明”。 B)仅仅因为我更改了 fr1 的值并不意味着歌手1将更新存储在“playbackcallback”变量中的值或负责更新输出缓冲区的任何值。也许这可以通过不同的编码来解决? C)即使这确实有效,在暂停/播放音频时仍然存在明显的“间隙”,我需要消除它。这可能意味着对代码进行彻底修改,以便我可以“动态”插入新值而不破坏任何内容。然而,我花费所有精力来发布的原因是因为这个方法正是我想要的(我可以用数学方法计算一个值,它直接进入 DAC,这意味着我将来可以使用它来制作三角形) 、方波等波浪很容易)。我已将 Singer.h 和 .m 上传到 Pastebin,供您欣赏,也许它们会有所帮助。抱歉,我无法发布 2 个 HTML 标签,所以这里是完整的链接。 (http://pastebin.com/ewhKW2Tk) (http://pastebin.com/CNAT4gFv)

所以,TL;DR,我真正想做的就是能够定义 4 个波的当前方程/值,并经常重新定义它们,而不会在声音中出现间隙。 谢谢。 (如果这篇文章令人困惑或偏离轨道,我很抱歉,我很确定它确实如此。)

My question is a little tricky, and I'm not exactly experienced (I might get some terms wrong), so here goes.
I'm declaring an instance of an object called "Singer". The instance is called "singer1". "singer1" produces an audio signal. Now, the following is the code where the specifics of the audio signal are determined:

OSStatus playbackCallback(void *inRefCon,
                      AudioUnitRenderActionFlags *ioActionFlags,
                      const AudioTimeStamp *inTimeStamp,
                      UInt32 inBusNumber, 
                      UInt32 inNumberFrames,
                      AudioBufferList *ioData) {    

//Singer *me = (Singer *)inRefCon;

static int phase = 0;

for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) {

    int samples = ioData->mBuffers[i].mDataByteSize / sizeof(SInt16);

    SInt16 values[samples];

    float waves;
    float volume=.5;

    for(int j = 0; j < samples; j++) {


        waves = 0;


        waves += sin(kWaveform * 600 * phase)*volume;
        waves += sin(kWaveform * 400 * phase)*volume;
        waves += sin(kWaveform * 200 * phase)*volume;
        waves += sin(kWaveform * 100 * phase)*volume;            

        waves *= 32500 / 4; // <--------- make sure to divide by how many waves you're stacking

        values[j] = (SInt16)waves;
        values[j] += values[j]<<16;

        phase++;

    }

    memcpy(ioData->mBuffers[i].mData, values, samples * sizeof(SInt16));

}

return noErr;

}

99% of this is borrowed code, so I only have a basic understanding of how it works (I don't know about the OSStatus class or method or whatever this is. However, you see those 4 lines with 600, 400, 200 and 100 in them? Those determine the frequency. Now, what I want to do (for now) is insert my own variable in there in place of a constant, which I can change on a whim. This variable is called "fr1". "fr1" is declared in the header file, but if I try to compile I get an error about "fr1" being undeclared. Currently, my technique to fix this is the following: right beneath where I #import stuff, I add the line

fr1=0.0;//any number will work properly

This sort of works, as the code will compile and singer1.fr1 will actually change values if I tell it to. The problems are now this:A)even though this compiles and the tone specified will play (0.0 is no tone), I get the warnings "Data definition has no type or storage class" and "Type defaults to 'int' in declaration of 'fr1'". I bet this is because for some reason it's not seeing my previous declaration in the header file (as a float). However, again, if I leave this line out the code won't compile because "fr1 is undeclared". B)Just because I change the value of fr1 doesn't mean that singer1 will update the value stored inside the "playbackcallback" variable or whatever is in charge of updating the output buffers. Perhaps this can be fixed by coding differently? C)even if this did work, there is still a noticeable "gap" when pausing/playing the audio, which I need to eliminate. This might mean a complete overhaul of the code so that I can "dynamically" insert new values without disrupting anything. However, the reason I'm going through all this effort to post is because this method does exactly what I want (I can compute a value mathematically and it goes straight to the DAC, which means I can use it in the future to make triangle, square, etc waves easily). I have uploaded Singer.h and .m to pastebin for your veiwing pleasure, perhaps they will help. Sorry, I can't post 2 HTML tags so here are the full links.
(http://pastebin.com/ewhKW2Tk)
(http://pastebin.com/CNAT4gFv)

So, TL;DR, all I really want to do is be able to define the current equation/value of the 4 waves and re-define them very often without a gap in the sound.
Thanks. (And sorry if the post was confusing or got off track, which I'm pretty sure it did.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

〗斷ホ乔殘χμё〖 2024-10-11 08:44:35

我的理解是,每次需要重新填充缓冲区时都会调用回调函数。因此,更改 fr1..fr4 将改变波形,但仅限于缓冲区更新时。您不需要停止并重新启动声音来进行更改,但如果您更改 fr 值,您会注意到音色突然变化。为了获得音色的平滑过渡,您必须实现一些随时间平滑改变 fr 值的方法。调整缓冲区大小将使您能够控制声音对不断变化的 fr 值的响应程度。

fr 未定义的问题是由于您的回调是直接的 c 函数。您的 fr 变量被声明为 Objective-C 实例变量,作为 Singer 对象的一部分。默认情况下它们是不可访问的。

看一下这个项目,看看他如何在回调中实现对实例变量的访问。基本上,他将对实例的引用传递给回调函数,然后通过该函数访问实例变量。

https://github.com/youpy/dowoscillator

注意:

Sinewave *sineObject = inRefCon;
float freq = sineObject.frequency * 2 * M_PI / samplingRate;

并且:

AURenderCallbackStruct input;
input.inputProc = RenderCallback;
input.inputProcRefCon = self;

此外,您还需要移动您的回调函数位于 @implementation 块之外,因为它实际上不是 Singer 对象的一部分。

您可以在这里看到这一切的实际情况:https://github.com/coryalder/SineWaver

My understanding is that your callback function is called every time the buffer needs to be re-filled. So changing fr1..fr4 will alter the waveform, but only when the buffer updates. You shouldn't need to stop and re-start the sound to get a change, but you will notice an abrupt shift in the timbre if you change your fr values. In order to get a smooth transition in timbre, you'd have to implement something that smoothly changes the fr values over time. Tweaking the buffer size will give you some control over how responsive the sound is to your changing fr values.

Your issue with fr being undefined is due to your callback being a straight c function. Your fr variables are declared as objective-c instance variables as part of your Singer object. They are not accessible by default.

take a look at this project, and see how he implements access to his instance variables from within his callback. Basically he passes a reference to his instance to the callback function, and then accesses instance variables through that.

https://github.com/youpy/dowoscillator

notice:

Sinewave *sineObject = inRefCon;
float freq = sineObject.frequency * 2 * M_PI / samplingRate;

and:

AURenderCallbackStruct input;
input.inputProc = RenderCallback;
input.inputProcRefCon = self;

Also, you'll want to move your callback function outside of your @implementation block, because it's not actually part of your Singer object.

You can see this all in action here: https://github.com/coryalder/SineWaver

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文