SDL - Как воспроизводить аудио асинхронно в C ++, не останавливая выполнение кода? - PullRequest
0 голосов
/ 08 мая 2018

Я разрабатываю клон Asteroid на чистом C ++, и для этой цели мне нужно добавить звуки к различным событиям, таким как, когда пуля запускается и когда происходит взрыв. Однако проблема в том, что у меня нет опыта работы с аудио библиотеками.

Я использую Simple DirectMedia Layer (SDL) и написал функцию playsound () для воспроизведения звука в случае возникновения определенного события. Проблема, однако, заключается в том, что если происходит событие, вызывается playsound () и выполнение кода останавливается до тех пор, пока звук полностью не будет воспроизведен или пока я не вернусь из функции (я задерживаю возврат с помощью функции delay func).

Что я хотел бы сделать, так это чтобы звук воспроизводился в фоновом режиме, не создавая задержки для остальной части игры. Я работаю в Ubuntu 16.04 и не могу использовать Windows PlaySound () для вызова флага ASYNC.

Вот функция:

void playsound(string path) {
    // Initialize SDL.
        if (SDL_Init(SDL_INIT_AUDIO) < 0)
                return;

        // local variables
        Uint32 wav_length; // length of our sample
        Uint8 *wav_buffer; // buffer containing our audio file
        SDL_AudioSpec wav_spec;
        if(SDL_LoadWAV(path.c_str(), &wav_spec, &wav_buffer, &wav_length) == NULL){
          return;
        }
        SDL_AudioDeviceID deviceId = SDL_OpenAudioDevice(NULL, 0, &wav_spec, NULL, 0);
        SDL_QueueAudio(deviceId, wav_buffer, wav_length);
        SDL_PauseAudioDevice(deviceId, 0);
        SDL_Delay(50);
        SDL_CloseAudioDevice(deviceId);
        SDL_FreeWAV(wav_buffer);
        SDL_Quit();
}

Ответы [ 3 ]

0 голосов
/ 08 мая 2018

Вы хотите загрузить все необходимые активы при инициализации игры. Затем, когда вы захотите поиграть в них, они будут загружены в память игры, и лагов не будет. А также, возможно, воспроизводите звуки в отдельном потоке, чтобы он не блокировал ваш основной поток.

0 голосов
/ 09 мая 2018

Ваша задержка препятствует выполнению вашего кода, задержка 50 мс - это почти 2 кадра при 33 мс на кадр или 3 кадра при 16 мс на кадр, с пропуском кадра здесь, и может не быть проблем, но вы могли видеть, как вызывать несколько звуков подряд замедлят вашу программу.

Вот так я воспроизводю звуки в своем движке, используя SDL2_mixer (короткие звуки, для музыки у вас есть другой метод, называемый Mix_PlayMusic), он может быть вам полезен. У меня нет задержки (и я не использую сон или задержки в моем коде). Как только вы вызываете play (), звук должен воспроизводиться полностью, если только ваш код не останавливает что-либо еще.

#pragma once
#include <string>
#include <memory>
#include <SDL2/SDL_mixer.h>

class sample {
public:
    sample(const std::string &path, int volume);
    void play();
    void play(int times);
    void set_volume(int volume);

private:
    std::unique_ptr<Mix_Chunk, void (*)(Mix_Chunk *)> chunk;
};

и файл cpp

#include <sample.h>

sample::sample(const std::string &path, int volume)
    : chunk(Mix_LoadWAV(path.c_str()), Mix_FreeChunk) {
    if (!chunk.get()) {
        // LOG("Couldn't load audio sample: ", path);
    }

    Mix_VolumeChunk(chunk.get(), volume);
}

// -1 here means we let SDL_mixer pick the first channel that is free
// If no channel is free it'll return an err code.
void sample::play() {
    Mix_PlayChannel(-1, chunk.get(), 0);
}

void sample::play(int times) {
    Mix_PlayChannel(-1, chunk.get(), times - 1);
}

void sample::set_volume(int volume) {
    Mix_VolumeChunk(chunk.get(), volume);
}

Обратите внимание, что мне не нужно создавать потоки для моей модели, каждый раз, когда что-то вызывает воспроизведение звука, программа продолжает выполняться. (Я думаю, SDL_Mixer играет в основном потоке SDL).

Чтобы это работало, когда вы запускаете SDL, вам также нужно будет инициализировать микшер как

if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) < 0) {
    // Error message if can't initialize
}

// Amount of channels (Max amount of sounds playing at the same time)
Mix_AllocateChannels(32);

А примером того, как воспроизвести звук, будет

// at some point loaded a sample s with sample(path to wave mp3 or whatever)
s.play();

Несколько замечаний, вам не нужно использовать, но можете, код такой, какой он есть, это скорее простой пример использования SDL2_mixer.

Это означает, что функциональность отсутствует, вам может потребоваться более жесткая обработка звука, например, чтобы остановить звук в середине воспроизведения (по какой-то причине), вы можете сделать это, если воспроизводите свои звуки в разных каналах с помощью функции Mix_HaltChannel, и функция play () может принимать канал, на котором вы хотите его воспроизвести.

Все эти функции возвращают значения ошибок, например, если незарезервированный канал недоступен. Mix_PlayChannel возвращает код ошибки.

Еще одна вещь, которую вы должны иметь в виду, если вы играете один и тот же звук несколько раз, он начнет расплываться / вы не заметите, если тот же самый звук воспроизводится снова. Таким образом, вы можете добавить целое число к семплу, чтобы подсчитать, сколько раз семпл может быть воспроизведен.

Если вы ДЕЙСТВИТЕЛЬНО хотите записать свой микшер / аудио из основного потока SDL (и все еще используете только SDL), вы можете просто создать новый контекст SDL в потоке и каким-то образом посылать сигналы для воспроизведения аудио.

0 голосов
/ 08 мая 2018

В C ++ есть несколько инструментов для асинхронных операций. Вы можете попробовать самый простой std::async:

auto handle = std::async(std::launch::async,
                         playsound, std::string{"/path/to/cute/sound"});

// Some other stuff. Your game logic doesn't blocked here.

handle.get(); // This can actually block.

Вы должны указать флаг std::launch::async, что означает, что будет использоваться новый поток. Затем необходимо вызвать имя вызываемого и его параметры. Не забудьте включить заголовок <future>.

...