SFML2.6 音频模块--播放声音和音乐

声音和音乐

SFML 提供两个用于播放音频的类:sf::Sound 和 sf::Music。它们都提供了大致相同的功能,主要区别在于它们的工作方式。

sf::Sound 是一个轻量级对象,它可以播放从 sf::SoundBuffer 中加载的音频数据。它应该用于可以适应内存并且在播放时不会出现延迟的小音效。例如枪声、脚步声等。

sf::Music 不会将所有音频数据加载到内存中,而是从源文件实时流式传输它们。它通常用于播放持续几分钟的压缩音乐,否则会需要多秒钟才能加载并占用数百 MB 的内存。

加载并播放声音

如上所述,声音数据并不直接存储在 sf::Sound 中,而是存储在一个名为 sf::SoundBuffer 的单独类中。该类封装了音频数据,它基本上是一个由 16 位有符号整数(称为 “音频样本”)组成的数组。样本是在给定时间点上声音信号的振幅,因此样本数组表示了完整的声音。

事实上,sf::Sound / sf::SoundBuffer 类与来自图形模块的 sf::Sprite / sf::Texture 类使用相同的方式。因此,如果您了解精灵和纹理如何配合使用,那么可以将相同的概念应用于声音和声音缓冲区。

您可以使用 loadFromFile 函数从磁盘上的文件加载声音缓冲区:

#include <SFML/Audio.hpp>

int main()
{
    sf::SoundBuffer buffer;
    if (!buffer.loadFromFile("sound.wav"))
        return -1;

    ...

    return 0;
}

与其他内容一样,您还可以从内存(loadFromMemory)或自定义输入流(loadFromStream)中加载音频文件。

SFML支持音频文件格式WAV,OGG / Vorbis和FLAC。由于许可问题,不支持MP3。

如果来自另一个源的情况下,您还可以直接从样本数组加载声音缓冲区:

std::vector<sf::Int16> samples = ...;
buffer.loadFromSamples(&samples[0], samples.size(), 2, 44100);

由于loadFromSamples加载的是样本的原始数组,而不是音频文件,因此它需要其他参数才能完全描述声音。第一个参数(第三个参数)是通道数;1个通道定义为单声道声音,2个通道定义为立体声声音,等等。第二个附加属性(第四个参数)是采样率;它定义了每秒必须播放多少样本才能重建原始声音。

现在,音频数据已加载,我们可以使用sf::Sound实例播放它。

sf::SoundBuffer buffer;
// load something into the sound buffer...

sf::Sound sound;
sound.setBuffer(buffer);
sound.play();

有趣的是,如果需要,您可以将相同的音频缓冲区分配给多个声音。甚至可以同时播放它们而不会出现任何问题。

声音(和音乐)在单独的线程中播放。这意味着在调用play()后,您可以自由地执行任何操作(当然不能销毁声音或其数据),直到声音播放完成或明确停止为止。

播放音乐

与sf :: Sound不同,sf :: Music不会预加载音频数据,而是直接从源流式传输数据。因此,音乐的初始化更加直接:

sf::Music music;
if (!music.openFromFile("music.ogg"))
    return -1; // error
music.play();

需要注意的是,与所有其他SFML资源不同,加载函数的名称命名为openFromFile而不是loadFromFile。这是因为音乐并没有真正地加载,这个方法仅仅是打开了音乐文件。数据只有在播放音乐时才会被加载。此外,需要记住,只要音乐在播放,音频文件就必须保持可用。其他的加载函数sf::Music也遵循同样的约定:openFromMemory,openFromStream。

接下来做什么?

现在,你已经可以加载和播放声音或音乐了,让我们看看你可以做什么。

要控制播放,可以使用以下函数:

  • play 开始或恢复播放
  • pause 暂停播放
  • stop 停止播放并倒回
  • setPlayingOffset 更改当前播放位置

例子:

// start playback
sound.play();

// advance to 2 seconds
sound.setPlayingOffset(sf::seconds(2.f));

// pause playback
sound.pause();

// resume playback
sound.play();

// stop playback and rewind
sound.stop();

getStatus函数返回声音或音乐的当前状态,可以使用它来知道它是停止、播放还是暂停。

声音和音乐的播放也受到几个属性的控制,这些属性可以在任何时候进行更改。

音高是改变声音的感知频率的因素:大于1会以更高的音高播放声音,小于1会以更低的音高播放声音,而1则保持不变。更改音高会产生一个副作用:它会影响播放速度。

sound.setPitch(1.2f);

音量就是…音量。其值从0(静音)到100(最大音量)范围内。默认值是100,这意味着您不能将声音调得比其初始音量更大。

sound.setVolume(50.f);

循环属性控制声音/音乐是否自动循环播放。如果设置了循环,它将在完成后从头开始重新播放,直到您明确调用停止为止重复播放。如果未设置循环,则在完成后会自动停止播放。

sound.setLoop(true);

还有其他属性可供使用,但它们与空间化有关,将在相应的教程中进行解释。

常见错误

已销毁的声音缓冲区

最常见的错误是当声音仍在使用缓冲区时,使缓冲区超出范围(因此被销毁)。

sf::Sound loadSound(std::string filename)
{
    sf::SoundBuffer buffer; // this buffer is local to the function, it will be destroyed...
    buffer.loadFromFile(filename);
    return sf::Sound(buffer);
} // ... here

sf::Sound sound = loadSound("s.wav");
sound.play(); // ERROR: the sound's buffer no longer exists, the behavior is undefined

请记住,声音只保持对您提供给它的声音缓冲区的指针,它不包含自己的副本。您必须正确管理声音缓冲区的生命周期,以使它们在被声音使用时保持活动状态。

太多的声音

另一个错误的源头是尝试创建大量的声音。SFML内部有一个限制;它可以因操作系统而异,但您绝不能超过256个。这个限制是同时存在的sf::Sound和sf::Music实例的数量。保持低于限制的好方法是在不再需要时销毁(或回收)未使用的声音。当然,这只适用于您必须管理大量声音和音乐的情况。

在音乐正在播放时销毁音源

请记住,只要音乐正在播放,它就需要保持音源有效。当您的应用程序播放来自内存中的文件或自定义输入流的音乐时,情况会变得更加复杂:

// we start with a music file in memory (imagine that we extracted it from a zip archive)
std::vector<char> fileData = ...;

// we play it
sf::Music music;
music.openFromMemory(&fileData[0], fileData.size());
music.play();

// "ok, it seems that we don't need the source file any longer"
fileData.clear();

// ERROR: the music was still streaming the contents of fileData! The behavior is now undefined

sf::Music是不能复制的

最后一个需要注意的提示是:sf::Music类是不可复制的,因此您不能复制音乐对象。因此,您不能使用复制构造函数或赋值运算符来创建一个新的音乐对象。如果您需要将音乐对象传递给函数,应该使用引用或指针。

sf::Music music;
sf::Music anotherMusic = music; // ERROR

void doSomething(sf::Music music)
{
    ...
}
sf::Music music;
doSomething(music); // ERROR (the function should take its argument by reference, not by value)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面我会给你讲解如何使用SFML来实现n-puzzle游戏的可视化。首先,我们需要了解一下SFML是什么。 SFML是一个跨平台的图形库,它提供了简单易用的API,可以帮助我们快速创建2D游戏和图形应用程序。它支持多种编程语言,包括C++、Python、Java等。 接下来,我们可以开始实现n-puzzle游戏的可视化了。首先,我们需要创建一个窗口来显示游戏界面。以下是创建窗口的代码: ```cpp #include <SFML/Graphics.hpp> int main() { sf::RenderWindow window(sf::VideoMode(800, 600), "N-Puzzle Game"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(sf::Color::White); // 绘制游戏界面 window.display(); } return 0; } ``` 这段代码创建了一个800x600大小的窗口,并且在每一帧中都会清空窗口并绘制游戏界面。接下来,我们需要实现游戏界面的绘制。 n-puzzle游戏的界面由一个N*N的网格组成,每个格子中都会有一个数字。我们可以使用SFML中的矩形来表示每个格子,并且使用文本来显示数字。以下是绘制游戏界面的代码: ```cpp const int N = 3; const int tileSize = 100; sf::RectangleShape tile(sf::Vector2f(tileSize, tileSize)); tile.setFillColor(sf::Color::White); tile.setOutlineColor(sf::Color::Black); tile.setOutlineThickness(2); sf::Font font; font.loadFromFile("arial.ttf"); sf::Text text("", font, tileSize / 2); text.setFillColor(sf::Color::Black); text.setStyle(sf::Text::Bold); text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int number = i * N + j + 1; if (number == N * N) continue; tile.setPosition(j * tileSize, i * tileSize); text.setString(std::to_string(number)); text.setPosition(j * tileSize + tileSize / 2, i * tileSize + tileSize / 2); window.draw(tile); window.draw(text); } } ``` 这段代码首先定义了每个格子的大小为100x100,并且使用白色填充和黑色边框。然后,它加载了一个字体文件,并且创建了一个文本对象来显示数字。最后,它使用两个嵌套循环来绘制整个游戏界面。 这样,我们就完成了n-puzzle游戏的可视化。完整代码如下: ```cpp #include <SFML/Graphics.hpp> const int N = 3; const int tileSize = 100; int main() { sf::RenderWindow window(sf::VideoMode(N * tileSize, N * tileSize), "N-Puzzle Game"); sf::RectangleShape tile(sf::Vector2f(tileSize, tileSize)); tile.setFillColor(sf::Color::White); tile.setOutlineColor(sf::Color::Black); tile.setOutlineThickness(2); sf::Font font; font.loadFromFile("arial.ttf"); sf::Text text("", font, tileSize / 2); text.setFillColor(sf::Color::Black); text.setStyle(sf::Text::Bold); text.setOrigin(text.getGlobalBounds().width / 2, text.getGlobalBounds().height / 2); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(sf::Color::White); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { int number = i * N + j + 1; if (number == N * N) continue; tile.setPosition(j * tileSize, i * tileSize); text.setString(std::to_string(number)); text.setPosition(j * tileSize + tileSize / 2, i * tileSize + tileSize / 2); window.draw(tile); window.draw(text); } } window.display(); } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值