FMOD.net 流媒体、回调和 exinfo 参数

发布于 2024-10-10 15:01:00 字数 2183 浏览 0 评论 0原文

我发布了一个关于如何播放 nsf 文件(NES FMOD 中的控制台音乐)。虽然没有取得任何结果,但从那以后我取得了一些进步。我决定最简单的方法是将现有播放器编译成 dll,然后从 C# 调用它来填充我的缓冲区。现在的问题是让它听起来正确,并确保我的所有参数都正确。

以下是到目前为止的事实:

  1. nsf dll 正在处理 short,因此数据是 PCM16。
  2. 我正在使用的示例 nsf 的播放速率为 60 Hz。
  3. 现在只是为了玩玩,我使用的频率为 48000。
  4. 根据 2 和 3,dll 计算出必要的缓冲区大小为 48000 / 60hz = 800。这意味着它将渲染 800 shorts每个模拟 NES 帧的缓冲区价值。

到目前为止,我已经有了 C# 代码来以正确的音高和节奏播放 nsf,但它非常颗粒化/模糊,我将其归因于 FMOD 读取回调给出的数据长度为 1600,而我应该期待 800。我尝试过使用所有数字,但它要么崩溃,要么音乐改变音高、节奏,或两者兼而有之。

这是我的一些 C# 代码:

uint channels = 1, frequency = 48000;

FMOD.MODE mode = (FMOD.MODE.DEFAULT | FMOD.MODE.OPENUSER | FMOD.MODE.LOOP_NORMAL);

FMOD.Sound sound = new FMOD.Sound();
FMOD.CREATESOUNDEXINFO ex = new FMOD.CREATESOUNDEXINFO();
ex.cbsize = Marshal.SizeOf(ex);
ex.fileoffset = 0;
ex.format = FMOD.SOUND_FORMAT.PCM16;

// does this even matter? It doesn't change my results as long as it's long enough for one update
ex.length = frequency; 

ex.numchannels = (int)channels;
ex.defaultfrequency = (int)frequency;
ex.pcmreadcallback = pcmreadcallback;
ex.dlsname = null;

// eventually I will calculate this with frequency / nsf hz, but I'm just testing for now
ex.decodebuffersize = 800;

// from the dll
load_nsf_file("file.nsf", 8, (int)frequency); // 8 is the track number to play

var result = system.createSound(
        (string)null,
        (mode | FMOD.MODE.CREATESTREAM),
        ref ex,
        ref sound);

channel = new FMOD.Channel();
result = system.playSound(FMOD.CHANNELINDEX.FREE, sound, false, ref channel);

private FMOD.RESULT PCMREADCALLBACK(IntPtr soundraw, IntPtr data, uint datalen)
{
    // from the dll
    process_buffer(data, (int)800); // if I use datalen, it usually crashes (I can't get datalen to = 800 safely)

    return FMOD.RESULT.OK;
}

那么这里是我的一些问题:

  1. exinfo.decodebuffersize、频率和读取回调的 datalen 参数之间有什么关系?在此代码示例中,它的值为 3200。我不知道它与解码缓冲区大小之间的 4 因数从何而来。
  2. 回调中的datalen是指byte的数量,还是short的数量? process_buffer 函数采用一个短数组及其长度。我预计 fmod 也会谈论短裤,因为我告诉它 PCM16。
  3. 也许我的播放质量由于某些完全不同的原因而很差。如果是这样,我不知道从哪里开始解决这个问题。有什么想法吗?

I posted a question on gamedev about how to play nsf files (NES console music) in FMOD. It didn't get any results, but since then I made some progress. I decided that the easiest method was just to compile an existing player into a dll and then call it from C# to populate my buffer. The problem now is getting it to sound right, and making sure all my paremeters are correct.

Here are the facts so far:

  1. The nsf dll is dealing with shorts, so the data is PCM16.
  2. The sample nsf I'm using has a playback rate of 60 Hz.
  3. Just for playing around now, I'm using a frequency of 48000.
  4. Based on 2 and 3, the dll calculates a necessary buffer size of 48000 / 60hz = 800. This means it will render 800 shorts worth of buffer for every simulated NES frame.

I've so far got my C# code to play the nsf, at the correct pitch and tempo, but it's very grainy / fuzzy, which I'm attributing to the fact that the FMOD read callback is giving a data length of 1600, whereas I should be expecting 800. I've tried playing around with all the numbers and it either crashes, or the music changes pitch, tempo, or both.

Here's some of my C# code:

uint channels = 1, frequency = 48000;

FMOD.MODE mode = (FMOD.MODE.DEFAULT | FMOD.MODE.OPENUSER | FMOD.MODE.LOOP_NORMAL);

FMOD.Sound sound = new FMOD.Sound();
FMOD.CREATESOUNDEXINFO ex = new FMOD.CREATESOUNDEXINFO();
ex.cbsize = Marshal.SizeOf(ex);
ex.fileoffset = 0;
ex.format = FMOD.SOUND_FORMAT.PCM16;

// does this even matter? It doesn't change my results as long as it's long enough for one update
ex.length = frequency; 

ex.numchannels = (int)channels;
ex.defaultfrequency = (int)frequency;
ex.pcmreadcallback = pcmreadcallback;
ex.dlsname = null;

// eventually I will calculate this with frequency / nsf hz, but I'm just testing for now
ex.decodebuffersize = 800;

// from the dll
load_nsf_file("file.nsf", 8, (int)frequency); // 8 is the track number to play

var result = system.createSound(
        (string)null,
        (mode | FMOD.MODE.CREATESTREAM),
        ref ex,
        ref sound);

channel = new FMOD.Channel();
result = system.playSound(FMOD.CHANNELINDEX.FREE, sound, false, ref channel);

private FMOD.RESULT PCMREADCALLBACK(IntPtr soundraw, IntPtr data, uint datalen)
{
    // from the dll
    process_buffer(data, (int)800); // if I use datalen, it usually crashes (I can't get datalen to = 800 safely)

    return FMOD.RESULT.OK;
}

So here are some of my questions:

  1. What is the relationship between exinfo.decodebuffersize, frequency, and the datalen parameter of the read callback? With this code sample, it's coming in as 3200. I don't know where that factor of 4 between it and the decodebuffersize comes from.
  2. Is datalen in the callback referring to number of bytes, or shorts? The process_buffer function takes a short array and its length. I would expect fmod is talking about shorts as well because I told it PCM16.
  3. Maybe my playback quality is bad for some totally different reason. If so I have no idea where to begin solving that. Any ideas there?

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

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

发布评论

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

评论(1

埖埖迣鎅 2024-10-17 15:01:00
  1. FMOD_CREATESOUNDEXINFO“decodebuffersize”成员位于 PCM 样本中,控制“pcmreadcallback”函数中请求的数据量。因此,由于您将其设置为 800 和 PCM16,因此“datalen”将为 1600,因为该值以字节为单位。你提到的“datalen”值是1600(如预期),但后来你说它是3200,也许我混淆了你的意思。最后,“频率”只是告诉 FMOD 播放数据的速度,这应该与您的“process_buffer”函数生成的内容相匹配。

  2. “datalen”以字节为单位。

  3. 简单测试这一点的一个好方法是生成 5 秒的音频,然后将其作为样本加载到 FMOD 中。这避免了读取回调的潜在复杂性。

顺便说一句:“ex.length”告诉 Sound::getLength 您的文件实际有多大,如果您使用 FMOD_CREATESAMPLE,则这就是分配的内存量。

  1. The FMOD_CREATESOUNDEXINFO 'decodebuffersize' member is in PCM samples and controls how much data will be requested in the 'pcmreadcallback' function. So since you set it to 800 and PCM16, 'datalen' will be 1600 as the value is in bytes. You mention at the 'datalen' value is 1600 (as expected) but later you say it's 3200, perhaps I have confused your meaning. Finally the 'frequency' is just telling FMOD how quickly to play your data, this should match what ever your 'process_buffer' function produces.

  2. 'datalen' is in bytes.

  3. A good way to test this simply is to generate say 5 seconds of audio then load it into FMOD as a sample. This avoids potential complications with read callbacks.

BTW: 'ex.length' tells Sound::getLength how big your file actually is, and if you are using FMOD_CREATESAMPLE this is how much memory is allocated.

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