audio test
This commit is contained in:
parent
4a233d72ed
commit
b809c2df84
@ -24,6 +24,7 @@ Speaker* ALSound::newInstance(int priority) const {
|
||||
if (source == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
AL_CHECK(alSourcei(source, AL_BUFFER, buffer));
|
||||
return new ALSpeaker(al, source, priority);
|
||||
}
|
||||
|
||||
@ -146,7 +147,7 @@ ALAudio::~ALAudio() {
|
||||
}
|
||||
|
||||
AL_CHECK(alcMakeContextCurrent(context));
|
||||
AL_CHECK(alcDestroyContext(context));
|
||||
alcDestroyContext(context);
|
||||
if (!alcCloseDevice(device)) {
|
||||
std::cerr << "AL: device not closed!" << std::endl;
|
||||
}
|
||||
@ -155,8 +156,10 @@ ALAudio::~ALAudio() {
|
||||
}
|
||||
|
||||
Sound* ALAudio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
|
||||
// TODO: implement
|
||||
return nullptr;
|
||||
auto format = AL::to_al_format(pcm->channels, pcm->bitsPerSample);
|
||||
uint buffer = getFreeBuffer();
|
||||
AL_CHECK(alBufferData(buffer, format, pcm->data.data(), pcm->data.size(), pcm->sampleRate));
|
||||
return new ALSound(this, buffer, pcm, keepPCM);
|
||||
}
|
||||
|
||||
ALAudio* ALAudio::create() {
|
||||
@ -187,7 +190,6 @@ uint ALAudio::getFreeSource(){
|
||||
alGenSources(1, &id);
|
||||
if (!AL_GET_ERORR())
|
||||
return 0;
|
||||
|
||||
allsources.push_back(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -111,6 +111,10 @@ namespace audio {
|
||||
) override;
|
||||
|
||||
void update(double delta) override;
|
||||
|
||||
bool isDummy() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
static ALAudio* create();
|
||||
};
|
||||
|
||||
@ -39,6 +39,10 @@ namespace audio {
|
||||
|
||||
void update(double delta) override {}
|
||||
|
||||
bool isDummy() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
static NoAudio* create();
|
||||
};
|
||||
}
|
||||
|
||||
@ -14,27 +14,6 @@
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
bool is_big_endian(void){
|
||||
uint32_t ui32_v = 0x01020304;
|
||||
char bytes[sizeof(uint32_t)];
|
||||
std::memcpy(bytes, &ui32_v, sizeof(uint32_t));
|
||||
|
||||
return bytes[0] == 1;
|
||||
}
|
||||
|
||||
std::int32_t convert_to_int(char* buffer, std::size_t len){
|
||||
std::int32_t a = 0;
|
||||
if (!is_big_endian()) {
|
||||
std::memcpy(&a, buffer, len);
|
||||
}
|
||||
else {
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
reinterpret_cast<char*>(&a)[3 - i] = buffer[i];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line){
|
||||
ALenum error = alGetError();
|
||||
if(error != AL_NO_ERROR){
|
||||
@ -63,147 +42,3 @@ bool AL::check_errors(const std::string& filename, const std::uint_fast32_t line
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_wav_file_header(std::ifstream& file,
|
||||
std::uint8_t& channels,
|
||||
std::int32_t& sampleRate,
|
||||
std::uint8_t& bitsPerSample,
|
||||
ALsizei& size){
|
||||
char buffer[4];
|
||||
if(!file.is_open())
|
||||
return false;
|
||||
|
||||
// the RIFF
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read RIFF" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if(std::strncmp(buffer, "RIFF", 4) != 0){
|
||||
std::cerr << "ERROR: file is not a valid WAVE file (header doesn't begin with RIFF)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// the size of the file
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read size of file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// the WAVE
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read WAVE" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if(std::strncmp(buffer, "WAVE", 4) != 0){
|
||||
std::cerr << "ERROR: file is not a valid WAVE file (header doesn't contain WAVE)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// "fmt/0"
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read fmt/0" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is always 16, the size of the fmt data chunk
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read the 16" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// PCM should be 1?
|
||||
if(!file.read(buffer, 2)){
|
||||
std::cerr << "ERROR: could not read PCM" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// the number of channels
|
||||
if(!file.read(buffer, 2)){
|
||||
std::cerr << "ERROR: could not read number of channels" << std::endl;
|
||||
return false;
|
||||
}
|
||||
channels = convert_to_int(buffer, 2);
|
||||
|
||||
// sample rate
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read sample rate" << std::endl;
|
||||
return false;
|
||||
}
|
||||
sampleRate = convert_to_int(buffer, 4);
|
||||
|
||||
// (sampleRate * bitsPerSample * channels) / 8
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read (sampleRate * bitsPerSample * channels) / 8" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ?? dafaq
|
||||
if(!file.read(buffer, 2)){
|
||||
std::cerr << "ERROR: could not read dafaq" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// bitsPerSample
|
||||
if(!file.read(buffer, 2)){
|
||||
std::cerr << "ERROR: could not read bits per sample" << std::endl;
|
||||
return false;
|
||||
}
|
||||
bitsPerSample = convert_to_int(buffer, 2);
|
||||
|
||||
// data chunk header "data"
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read data chunk header" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if(std::strncmp(buffer, "data", 4) != 0){
|
||||
std::cerr << "ERROR: file is not a valid WAVE file (doesn't have 'data' tag)" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// size of data
|
||||
if(!file.read(buffer, 4)){
|
||||
std::cerr << "ERROR: could not read data size" << std::endl;
|
||||
return false;
|
||||
}
|
||||
size = convert_to_int(buffer, 4);
|
||||
|
||||
/* cannot be at the end of file */
|
||||
if(file.eof()){
|
||||
std::cerr << "ERROR: reached EOF on the file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if(file.fail()){
|
||||
std::cerr << "ERROR: fail state set on the file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// after that user must free returned memory by himself!
|
||||
char* load_wav(
|
||||
const std::string& filename,
|
||||
std::uint8_t& channels,
|
||||
std::int32_t& sampleRate,
|
||||
std::uint8_t& bitsPerSample,
|
||||
ALsizei& size
|
||||
){
|
||||
std::ifstream in(filename, std::ios::binary);
|
||||
if(!in.is_open()){
|
||||
std::cerr << "ERROR: Could not open \"" << filename << "\"" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
if(!load_wav_file_header(in, channels, sampleRate, bitsPerSample, size)){
|
||||
std::cerr << "ERROR: Could not load wav header of \"" << filename << "\"" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> data (new char[size]);
|
||||
try {
|
||||
in.read(data.get(), size);
|
||||
return data.release();
|
||||
}
|
||||
catch (const std::exception&) {
|
||||
std::cerr << "ERROR: Could not load wav data of \"" << filename << "\"" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,23 +16,6 @@
|
||||
#define AL_CHECK(STATEMENT) STATEMENT; AL::check_errors(__FILE__, __LINE__)
|
||||
#define AL_GET_ERORR() AL::check_errors(__FILE__, __LINE__)
|
||||
|
||||
bool load_wav_file_header(
|
||||
std::ifstream& file,
|
||||
std::uint8_t& channels,
|
||||
std::int32_t& sampleRate,
|
||||
std::uint8_t& bitsPerSample,
|
||||
ALsizei& size
|
||||
);
|
||||
|
||||
char* load_wav(
|
||||
const std::string& filename,
|
||||
std::uint8_t& channels,
|
||||
std::int32_t& sampleRate,
|
||||
std::uint8_t& bitsPerSample,
|
||||
ALsizei& size
|
||||
);
|
||||
|
||||
|
||||
namespace AL {
|
||||
bool check_errors(const std::string& filename, const std::uint_fast32_t line);
|
||||
|
||||
@ -69,10 +52,10 @@ namespace AL {
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline ALenum to_al_format(short channels, short samples){
|
||||
static inline ALenum to_al_format(short channels, short bitsPerSample){
|
||||
bool stereo = (channels > 1);
|
||||
|
||||
switch (samples) {
|
||||
switch (bitsPerSample) {
|
||||
case 16:
|
||||
if (stereo)
|
||||
return AL_FORMAT_STEREO16;
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
#include "audio.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "ALAudio.h"
|
||||
#include "NoAudio.h"
|
||||
|
||||
#include "../coders/wav.h"
|
||||
|
||||
namespace audio {
|
||||
static speakerid_t nextId = 1;
|
||||
static Backend* backend;
|
||||
@ -23,6 +26,19 @@ void audio::initialize(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
PCM* audio::loadPCM(const fs::path& file, bool headerOnly) {
|
||||
std::string ext = file.extension().u8string();
|
||||
if (ext == ".wav" || ext == ".WAV") {
|
||||
return wav::load_pcm(file, headerOnly);
|
||||
} // TODO: OGG support
|
||||
throw std::runtime_error("unsupported audio format");
|
||||
}
|
||||
|
||||
Sound* audio::loadSound(const fs::path& file, bool keepPCM) {
|
||||
std::shared_ptr<PCM> pcm(loadPCM(file, !keepPCM && backend->isDummy()));
|
||||
return backend->createSound(pcm, keepPCM);
|
||||
}
|
||||
|
||||
Sound* audio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
|
||||
return backend->createSound(pcm, keepPCM);
|
||||
}
|
||||
@ -40,7 +56,7 @@ void remove_lower_priority_speaker(int priority) {
|
||||
for (auto it = speakers.begin(); it != speakers.end();) {
|
||||
if (it->second->getPriority() < priority && it->second->isPaused()) {
|
||||
it->second->stop();
|
||||
speakers.erase(it);
|
||||
it = speakers.erase(it);
|
||||
return;
|
||||
}
|
||||
it++;
|
||||
@ -48,7 +64,7 @@ void remove_lower_priority_speaker(int priority) {
|
||||
for (auto it = speakers.begin(); it != speakers.end();) {
|
||||
if (it->second->getPriority() < priority) {
|
||||
it->second->stop();
|
||||
speakers.erase(it);
|
||||
it = speakers.erase(it);
|
||||
return;
|
||||
}
|
||||
it++;
|
||||
@ -72,7 +88,7 @@ speakerid_t audio::play(
|
||||
return 0;
|
||||
}
|
||||
speakerid_t id = nextId++;
|
||||
speakers[id].reset(speaker);
|
||||
speakers.emplace(id, speaker);
|
||||
speaker->setPosition(position);
|
||||
speaker->setVolume(volume);
|
||||
speaker->setPitch(pitch);
|
||||
@ -94,7 +110,7 @@ void audio::update(double delta) {
|
||||
|
||||
for (auto it = speakers.begin(); it != speakers.end();) {
|
||||
if (it->second->isStopped()) {
|
||||
speakers.erase(it);
|
||||
it = speakers.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
|
||||
@ -3,9 +3,12 @@
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
#include <glm/glm.hpp>
|
||||
#include "../typedefs.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace audio {
|
||||
using speakerid_t = int64_t;
|
||||
/// @brief duration unit is second
|
||||
@ -32,12 +35,23 @@ namespace audio {
|
||||
uint8_t bitsPerSample;
|
||||
uint sampleRate;
|
||||
|
||||
PCM(
|
||||
std::vector<char> data,
|
||||
uint8_t channels,
|
||||
uint8_t bitsPerSample,
|
||||
uint sampleRate
|
||||
) : data(std::move(data)),
|
||||
channels(channels),
|
||||
bitsPerSample(bitsPerSample),
|
||||
sampleRate(sampleRate) {}
|
||||
|
||||
constexpr inline size_t countSamples() const {
|
||||
return data.size() / channels / (bitsPerSample / 8);
|
||||
}
|
||||
|
||||
constexpr inline duration_t getDuration() const {
|
||||
return countSamples() / static_cast<duration_t>(sampleRate);
|
||||
return static_cast<duration_t>(countSamples()) /
|
||||
static_cast<duration_t>(sampleRate);
|
||||
}
|
||||
};
|
||||
|
||||
@ -157,16 +171,33 @@ namespace audio {
|
||||
) = 0;
|
||||
|
||||
virtual void update(double delta) = 0;
|
||||
|
||||
/// @brief Check if backend is an abstraction that does not internally
|
||||
/// work with actual audio data or play anything
|
||||
virtual bool isDummy() const = 0;
|
||||
};
|
||||
|
||||
/// @brief Initialize audio system or use no audio mode
|
||||
/// @param enabled try to initialize actual audio
|
||||
extern void initialize(bool enabled);
|
||||
|
||||
/// @brief Load audio file info and PCM data
|
||||
/// @param file audio file
|
||||
/// @param headerOnly read header only
|
||||
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
||||
/// @return PCM audio data
|
||||
extern PCM* loadPCM(const fs::path& file, bool headerOnly);
|
||||
|
||||
/// @brief Load sound from file
|
||||
/// @param file audio file path
|
||||
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
|
||||
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
||||
/// @return new Sound instance
|
||||
extern Sound* loadSound(const fs::path& file, bool keepPCM);
|
||||
|
||||
/// @brief Create new sound from PCM data
|
||||
/// @param pcm PCM data
|
||||
/// @param keepPCM store PCM data in sound to make it accessible with
|
||||
/// Sound::getPCM
|
||||
/// @param keepPCM store PCM data in sound to make it accessible with Sound::getPCM
|
||||
/// @return new Sound instance
|
||||
extern Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM);
|
||||
|
||||
@ -200,6 +231,9 @@ namespace audio {
|
||||
int priority
|
||||
);
|
||||
|
||||
/// @brief Get speaker by id
|
||||
/// @param id speaker id
|
||||
/// @return speaker or nullptr
|
||||
extern Speaker* get(speakerid_t id);
|
||||
|
||||
/// @brief Update audio streams and sound instanced
|
||||
|
||||
118
src/coders/wav.cpp
Normal file
118
src/coders/wav.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "wav.h"
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../audio/audio.h"
|
||||
|
||||
bool is_big_endian() {
|
||||
uint32_t ui32_v = 0x01020304;
|
||||
char bytes[sizeof(uint32_t)];
|
||||
std::memcpy(bytes, &ui32_v, sizeof(uint32_t));
|
||||
return bytes[0] == 1;
|
||||
}
|
||||
|
||||
std::int32_t convert_to_int(char* buffer, std::size_t len){
|
||||
std::int32_t a = 0;
|
||||
if (!is_big_endian()) {
|
||||
std::memcpy(&a, buffer, len);
|
||||
}
|
||||
else {
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
reinterpret_cast<char*>(&a)[3 - i] = buffer[i];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) {
|
||||
std::ifstream in(file, std::ios::binary);
|
||||
if(!in.is_open()){
|
||||
throw std::runtime_error("could not to open file '"+file.u8string()+"'");
|
||||
}
|
||||
|
||||
char buffer[4];
|
||||
// the RIFF
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not to read RIFF");
|
||||
}
|
||||
if(std::strncmp(buffer, "RIFF", 4) != 0){
|
||||
throw std::runtime_error("file is not a valid WAVE file (header doesn't begin with RIFF)");
|
||||
}
|
||||
// the size of the file
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not read size of file");
|
||||
}
|
||||
// the WAVE
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not to read WAVE");
|
||||
}
|
||||
if(std::strncmp(buffer, "WAVE", 4) != 0){
|
||||
throw std::runtime_error("file is not a valid WAVE file (header doesn't contain WAVE)");
|
||||
}
|
||||
// "fmt/0"
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not read fmt/0");
|
||||
}
|
||||
// this is always 16, the size of the fmt data chunk
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not read the 16");
|
||||
}
|
||||
// PCM should be 1?
|
||||
if(!in.read(buffer, 2)){
|
||||
throw std::runtime_error("could not read PCM");
|
||||
}
|
||||
// the number of channels
|
||||
if(!in.read(buffer, 2)){
|
||||
throw std::runtime_error("could not read number of channels");
|
||||
}
|
||||
int channels = convert_to_int(buffer, 2);
|
||||
// sample rate
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not read sample rate");
|
||||
}
|
||||
int sampleRate = convert_to_int(buffer, 4);
|
||||
if (!in.read(buffer, 6)) {
|
||||
throw std::runtime_error("could not to read WAV header");
|
||||
}
|
||||
|
||||
// bitsPerSample
|
||||
if(!in.read(buffer, 2)){
|
||||
throw std::runtime_error("could not read bits per sample");
|
||||
}
|
||||
int bitsPerSample = convert_to_int(buffer, 2);
|
||||
channels /= bitsPerSample/8;
|
||||
|
||||
// data chunk header "data"
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not read data chunk header");
|
||||
}
|
||||
if(std::strncmp(buffer, "data", 4) != 0){
|
||||
throw std::runtime_error("file is not a valid WAVE file (doesn't have 'data' tag)");
|
||||
}
|
||||
|
||||
// size of data
|
||||
if(!in.read(buffer, 4)){
|
||||
throw std::runtime_error("could not read data size");
|
||||
}
|
||||
size_t size = convert_to_int(buffer, 4);
|
||||
|
||||
/* cannot be at the end of file */
|
||||
if(in.eof()){
|
||||
throw std::runtime_error("reached EOF on the file");
|
||||
}
|
||||
if(in.fail()){
|
||||
throw std::runtime_error("fail state set on the file");
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
if (!headerOnly) {
|
||||
data.resize(size);
|
||||
if (!in.read(data.data(), size)) {
|
||||
throw std::runtime_error("could not load wav data of '"+file.u8string()+"'");
|
||||
}
|
||||
}
|
||||
return new audio::PCM(std::move(data), channels, bitsPerSample, sampleRate);
|
||||
}
|
||||
14
src/coders/wav.h
Normal file
14
src/coders/wav.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef CODERS_WAV_H_
|
||||
#define CODERS_WAV_H_
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace audio {
|
||||
struct PCM;
|
||||
}
|
||||
|
||||
namespace wav {
|
||||
extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly);
|
||||
}
|
||||
|
||||
#endif // CODERS_WAV_H_
|
||||
@ -8,6 +8,7 @@
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../audio/audio.h"
|
||||
#include "../window/Camera.h"
|
||||
#include "../window/Events.h"
|
||||
#include "../window/input.h"
|
||||
@ -19,6 +20,7 @@
|
||||
#include "../world/Level.h"
|
||||
#include "../world/World.h"
|
||||
#include "../objects/Player.h"
|
||||
#include "../physics/Hitbox.h"
|
||||
#include "../logic/ChunksController.h"
|
||||
#include "../logic/LevelController.h"
|
||||
#include "../logic/scripting/scripting.h"
|
||||
@ -148,6 +150,14 @@ void LevelScreen::update(float delta) {
|
||||
updateHotkeys();
|
||||
}
|
||||
|
||||
auto camera = level->player->camera;
|
||||
audio::setListener(
|
||||
camera->position,
|
||||
level->player->hitbox->velocity,
|
||||
camera->position+camera->dir,
|
||||
camera->up
|
||||
);
|
||||
|
||||
// TODO: subscribe for setting change
|
||||
EngineSettings& settings = engine->getSettings();
|
||||
level->player->camera->setFov(glm::radians(settings.camera.fov));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user