audio streaming interfaces update
This commit is contained in:
@@ -46,6 +46,14 @@ Sound* audio::createSound(std::shared_ptr<PCM> pcm, bool keepPCM) {
|
|||||||
return backend->createSound(pcm, keepPCM);
|
return backend->createSound(pcm, keepPCM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PCMStream* audio::openPCMStream(const fs::path& file) {
|
||||||
|
std::string ext = file.extension().u8string();
|
||||||
|
if (ext == ".ogg" || ext == ".OGG") {
|
||||||
|
return ogg::create_stream(file);
|
||||||
|
}
|
||||||
|
throw std::runtime_error("unsupported audio stream format");
|
||||||
|
}
|
||||||
|
|
||||||
void audio::setListener(
|
void audio::setListener(
|
||||||
glm::vec3 position,
|
glm::vec3 position,
|
||||||
glm::vec3 velocity,
|
glm::vec3 velocity,
|
||||||
@@ -121,6 +129,7 @@ void audio::update(double delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void audio::close() {
|
void audio::close() {
|
||||||
|
speakers.clear();
|
||||||
delete backend;
|
delete backend;
|
||||||
backend = nullptr;
|
backend = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-2
@@ -12,7 +12,7 @@ namespace fs = std::filesystem;
|
|||||||
namespace audio {
|
namespace audio {
|
||||||
using speakerid_t = int64_t;
|
using speakerid_t = int64_t;
|
||||||
/// @brief duration unit is second
|
/// @brief duration unit is second
|
||||||
using duration_t = float;
|
using duration_t = double;
|
||||||
|
|
||||||
constexpr inline int PRIORITY_LOW = 0;
|
constexpr inline int PRIORITY_LOW = 0;
|
||||||
constexpr inline int PRIORITY_NORMAL = 5;
|
constexpr inline int PRIORITY_NORMAL = 5;
|
||||||
@@ -58,18 +58,46 @@ namespace audio {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief PCM data streaming interface
|
/// @brief audio::PCMStream is a data source for audio::Stream
|
||||||
class PCMStream {
|
class PCMStream {
|
||||||
public:
|
public:
|
||||||
virtual ~PCMStream() {};
|
virtual ~PCMStream() {};
|
||||||
|
|
||||||
|
/// @brief Read samples data to buffer
|
||||||
|
/// @param buffer destination buffer
|
||||||
|
/// @param bufferSize destination buffer size
|
||||||
|
/// @param loop loop stream (seek to start when end reached)
|
||||||
|
/// @return size of data received
|
||||||
|
/// (always equals bufferSize if seekable and looped)
|
||||||
virtual size_t read(char* buffer, size_t bufferSize, bool loop)=0;
|
virtual size_t read(char* buffer, size_t bufferSize, bool loop)=0;
|
||||||
|
|
||||||
|
/// @brief Close stream
|
||||||
virtual void close()=0;
|
virtual void close()=0;
|
||||||
|
|
||||||
|
/// @brief Get total samples number if seekable or 0
|
||||||
virtual size_t getTotalSamples() const=0;
|
virtual size_t getTotalSamples() const=0;
|
||||||
|
|
||||||
|
/// @brief Get total audio track duration if seekable or 0.0
|
||||||
virtual duration_t getTotalDuration() const=0;
|
virtual duration_t getTotalDuration() const=0;
|
||||||
|
|
||||||
|
/// @brief Get number of audio channels
|
||||||
|
/// @return 1 if mono, 2 if stereo
|
||||||
virtual uint getChannels() const=0;
|
virtual uint getChannels() const=0;
|
||||||
|
|
||||||
|
/// @brief Get audio sampling frequency
|
||||||
|
/// @return number of mono samples per second
|
||||||
virtual uint getSampleRate() const=0;
|
virtual uint getSampleRate() const=0;
|
||||||
|
|
||||||
|
/// @brief Get number of bits per mono sample
|
||||||
|
/// @return 8 or 16
|
||||||
virtual uint getBitsPerSample() const=0;
|
virtual uint getBitsPerSample() const=0;
|
||||||
|
|
||||||
|
/// @brief Check if the stream does support seek feature
|
||||||
|
virtual bool isSeekable() const=0;
|
||||||
|
|
||||||
|
/// @brief Move playhead to the selected sample number
|
||||||
|
/// @param position selected sample number
|
||||||
|
virtual void seek(size_t position) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Audio streaming interface
|
/// @brief Audio streaming interface
|
||||||
@@ -241,6 +269,12 @@ namespace audio {
|
|||||||
/// @return new Sound instance
|
/// @return new Sound instance
|
||||||
extern Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM);
|
extern Sound* createSound(std::shared_ptr<PCM> pcm, bool keepPCM);
|
||||||
|
|
||||||
|
/// @brief Open new PCM stream from file
|
||||||
|
/// @param file audio file path
|
||||||
|
/// @throws std::runtime_error if I/O error ocurred or format is unknown
|
||||||
|
/// @return new PCMStream instance
|
||||||
|
extern PCMStream* openPCMStream(const fs::path& file);
|
||||||
|
|
||||||
/// @brief Configure 3D listener
|
/// @brief Configure 3D listener
|
||||||
/// @param position listener position
|
/// @param position listener position
|
||||||
/// @param velocity listener velocity (used for Doppler effect)
|
/// @param velocity listener velocity (used for Doppler effect)
|
||||||
|
|||||||
+101
-3
@@ -7,6 +7,8 @@
|
|||||||
#include "../audio/audio.h"
|
#include "../audio/audio.h"
|
||||||
#include "../typedefs.h"
|
#include "../typedefs.h"
|
||||||
|
|
||||||
|
using namespace audio;
|
||||||
|
|
||||||
static inline const char* vorbis_error_message(int code) {
|
static inline const char* vorbis_error_message(int code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 0: return "no error";
|
case 0: return "no error";
|
||||||
@@ -31,7 +33,7 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
|
|||||||
vorbis_info* info = ov_info(&vf, -1);
|
vorbis_info* info = ov_info(&vf, -1);
|
||||||
uint channels = info->channels;
|
uint channels = info->channels;
|
||||||
uint sampleRate = info->rate;
|
uint sampleRate = info->rate;
|
||||||
size_t totalSamples = ov_pcm_total(&vf, -1);
|
size_t totalSamples = ov_seekable(&vf) ? ov_pcm_total(&vf, -1) : 0;
|
||||||
|
|
||||||
if (!headerOnly) {
|
if (!headerOnly) {
|
||||||
const int bufferSize = 4096;
|
const int bufferSize = 4096;
|
||||||
@@ -49,8 +51,104 @@ audio::PCM* ogg::load_pcm(const std::filesystem::path& file, bool headerOnly) {
|
|||||||
data.insert(data.end(), std::begin(buffer), std::begin(buffer)+ret);
|
data.insert(data.end(), std::begin(buffer), std::begin(buffer)+ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
totalSamples = data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
ov_clear(&vf);
|
ov_clear(&vf);
|
||||||
return new audio::PCM(std::move(data), totalSamples, channels, 16, sampleRate);
|
return new PCM(std::move(data), totalSamples, channels, 16, sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
class OggStream : public PCMStream {
|
||||||
|
OggVorbis_File vf;
|
||||||
|
bool closed = false;
|
||||||
|
uint channels;
|
||||||
|
uint sampleRate;
|
||||||
|
size_t totalSamples = 0;
|
||||||
|
bool seekable;
|
||||||
|
public:
|
||||||
|
OggStream(OggVorbis_File vf) : vf(std::move(vf)) {
|
||||||
|
vorbis_info* info = ov_info(&vf, -1);
|
||||||
|
channels = info->channels;
|
||||||
|
sampleRate = info->rate;
|
||||||
|
seekable = ov_seekable(&vf);
|
||||||
|
if (seekable) {
|
||||||
|
totalSamples = ov_pcm_total(&vf, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~OggStream() {
|
||||||
|
if (!closed) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read(char* buffer, size_t bufferSize, bool loop) {
|
||||||
|
int bitstream;
|
||||||
|
long bytes = 0;
|
||||||
|
size_t size = 0;
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
bytes = ov_read(&vf, buffer, bufferSize, 0, 2, true, &bitstream);
|
||||||
|
if (bytes < 0) {
|
||||||
|
std::cerr << vorbis_error_message(bytes) << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size += bytes;
|
||||||
|
bufferSize -= bytes;
|
||||||
|
buffer += bytes;
|
||||||
|
} while (bytes > 0);
|
||||||
|
|
||||||
|
if (loop) {
|
||||||
|
seek(0);
|
||||||
|
}
|
||||||
|
if (bufferSize == 0) {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
} while (loop);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
ov_clear(&vf);
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getTotalSamples() const {
|
||||||
|
return totalSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
duration_t getTotalDuration() const {
|
||||||
|
return static_cast<duration_t>(totalSamples) /
|
||||||
|
static_cast<duration_t>(sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint getChannels() const {
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint getSampleRate() const {
|
||||||
|
return sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint getBitsPerSample() const {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSeekable() const {
|
||||||
|
return seekable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seek(size_t position) {
|
||||||
|
if (seekable) {
|
||||||
|
ov_raw_seek(&vf, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PCMStream* ogg::create_stream(const std::filesystem::path& file) {
|
||||||
|
OggVorbis_File vf;
|
||||||
|
int code;
|
||||||
|
if ((code = ov_fopen(file.u8string().c_str(), &vf))) {
|
||||||
|
throw std::runtime_error(vorbis_error_message(code));
|
||||||
|
}
|
||||||
|
return new OggStream(std::move(vf));
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -5,11 +5,12 @@
|
|||||||
|
|
||||||
namespace audio {
|
namespace audio {
|
||||||
struct PCM;
|
struct PCM;
|
||||||
class Stream;
|
class PCMStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ogg {
|
namespace ogg {
|
||||||
extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly);
|
extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly);
|
||||||
|
extern audio::PCMStream* create_stream(const std::filesystem::path& file);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CODERS_OGG_H_
|
#endif // CODERS_OGG_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user