Program Listing for File audio_importers.cpp¶
↰ Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/audio/data/importers/audio_importers.cpp
)
#include <audio/data/importers/audio_importers.hpp>
#if !defined(DOXY_EXCLUDE)
#define MINIMP3_IMPLEMENTATION
#include <minimp3.h>
#include <minimp3_ex.h>
#endif
#include <audio/systems/audiosystem.hpp>
namespace legion::audio
{
common::result_decay_more<audio_segment, fs_error> mp3_audio_loader::load(const fs::basic_resource& resource, audio_import_settings&& settings)
{
using common::Err, common::Ok;
using decay = common::result_decay_more<audio_segment, fs_error>;
mp3dec_map_info_t map_info;
map_info.buffer = resource.data();
map_info.size = resource.size();
mp3dec_t mp3dec;
mp3dec_file_info_t fileInfo;
if (mp3dec_load_mapinfo(&mp3dec, &map_info, &fileInfo, NULL, NULL))
{
return decay(Err(legion_fs_error("Failed to load audio file")));
}
byte* audioData;
// bitsPerSample is always 16 for mp3
int dataSize = fileInfo.samples * sizeof(int16);
int channels = fileInfo.channels;
int samples = fileInfo.samples;
if (settings.channel_processing == audio_import_settings::channel_processing_setting::force_mono)
{
audioData = detail::convertToMono(reinterpret_cast<byte*>(fileInfo.buffer), dataSize, dataSize, channels, 16);
samples /= channels;
}
else
{
audioData = new byte[dataSize];
memmove(audioData, fileInfo.buffer, dataSize);
}
free(fileInfo.buffer);
audio_segment as(
audioData, // fileInfo.samples is int16, therefore byte requires twice as much
0,
samples,
channels,
fileInfo.hz,
fileInfo.layer,
fileInfo.avg_bitrate_kbps
);
std::lock_guard guard(AudioSystem::contextLock);
alcMakeContextCurrent(AudioSystem::alcContext);
//Generate openal buffer
alGenBuffers((ALuint)1, &as.audioBufferId);
ALenum format = AL_FORMAT_MONO16;
if (as.channels == 2) format = AL_FORMAT_STEREO16;
alBufferData(as.audioBufferId, format, as.getData(), dataSize, as.sampleRate);
alcMakeContextCurrent(nullptr);
return decay(Ok(as));
}
common::result_decay_more<audio_segment, fs_error> wav_audio_loader::load(const fs::basic_resource& resource, audio_import_settings&& settings)
{
using common::Err, common::Ok;
using decay = common::result_decay_more<audio_segment, fs_error>;
RIFF_Header header;
WAVE_Data waveData;
memcpy(&header, resource.data(), sizeof(header)); // Copy header data into the header struct
// Check if the loaded file has the correct header
if (header.chunckId[0] != 'R' ||
header.chunckId[1] != 'I' ||
header.chunckId[2] != 'F' ||
header.chunckId[3] != 'F')
{
log::error("Found WAV header: '{}', exptected: 'RIFF'", (char)header.chunckId[0], (char)header.chunckId[1], (char)header.chunckId[2], (char)header.chunckId[3]);
return decay(Err(legion_fs_error("WAV File invalid header, exptected RIFF")));
}
if (header.format[0] != 'W' ||
header.format[1] != 'A' ||
header.format[2] != 'V' ||
header.format[3] != 'E')
{
log::error("Found WAV format: '{}{}{}{}', exptected: 'WAVE'", (char)header.format[0], (char)header.format[1], (char)header.format[2], (char)header.format[3]);
return decay(Err(legion_fs_error("Loaded File is not of type WAV")));
}
if (header.wave_format.subChunckId[0] != 'f' ||
header.wave_format.subChunckId[1] != 'm' ||
header.wave_format.subChunckId[2] != 't' ||
header.wave_format.subChunckId[3] != ' ')
{
log::error("Found WAV format sub chunck ID: '{}{}{}{}', exptected: 'fmt '", (char)header.wave_format.subChunckId[0], (char)header.wave_format.subChunckId[1], (char)header.wave_format.subChunckId[2], (char)header.wave_format.subChunckId[3]);
return decay(Err(legion_fs_error("WAV File sub chunck id was not (fmt )")));
}
memcpy(&waveData, resource.data() + sizeof(header), sizeof(waveData));
if (waveData.subChunckId[0] != 'd' ||
waveData.subChunckId[1] != 'a' ||
waveData.subChunckId[2] != 't' ||
waveData.subChunckId[3] != 'a')
{
log::error("Found WAV data sub chunck ID: '{}{}{}{}', exptected: 'data'", (char)waveData.subChunckId[0], (char)waveData.subChunckId[1], (char)waveData.subChunckId[2], (char)waveData.subChunckId[3]);
return decay(Err(legion_fs_error("WAV File sample data does not start with word (data)")));
}
assert_msg("Audio file channels were 0", header.wave_format.channels != 0);
uint metaSize = sizeof(header) + sizeof(waveData);
int sampleDataSize = resource.size() - metaSize;
byte* audioData;
int channels = header.wave_format.channels;
audio_segment as;
if (settings.channel_processing == audio_import_settings::channel_processing_setting::split_channels)
{
detail::channel_data channelData = detail::extractChannels(resource.data() + metaSize, sampleDataSize, channels, header.wave_format.bitsPerSample);
sampleDataSize /= channels;
audioData = new byte[sampleDataSize];
memmove(audioData, channelData.dataPerChannel[0].data(), sampleDataSize);
//audioData = channelData.dataPerChannel[0].data();
int samplesPerChannel = sampleDataSize / (header.wave_format.bitsPerSample / 8);
as = audio_segment(
audioData,
0,
samplesPerChannel, // Sample count, unknown for wav
1,
(int)header.wave_format.sampleRate,
-1, // Layer, does not exist in wav
-1 // avg_biterate_kbps, unknown for wav
);
audio_segment* previous = &as;
if (channels > 1)
{
for (int i = 1; i < channels; ++i)
{
byte* data = new byte[sampleDataSize];
memmove(data, channelData.dataPerChannel[i].data(), sampleDataSize);
audio_segment* channel_segment = new audio_segment(
data,
0,
samplesPerChannel,
1,
(int)header.wave_format.sampleRate,
-1,
-1
);
detail::createAndBufferAudioData(&(channel_segment->audioBufferId), channel_segment->channels, header.wave_format.bitsPerSample, channel_segment->getData(), sampleDataSize, channel_segment->sampleRate);
log::debug("Setting next audio segment");
previous->setNextAudioSegment(*channel_segment);
previous = channel_segment;
}
}
}
else if (settings.channel_processing == audio_import_settings::channel_processing_setting::force_mono)
{
audioData = detail::convertToMono(resource.data() + metaSize, sampleDataSize, sampleDataSize, channels, header.wave_format.bitsPerSample);
as = audio_segment(
audioData,
0,
sampleDataSize / (header.wave_format.bitsPerSample / 8), // Sample count, unknown for wav
channels,
(int)header.wave_format.sampleRate,
-1, // Layer, does not exist in wav
-1 // avg_biterate_kbps, unknown for wav
);
}
else
{
audioData = new byte[sampleDataSize];
memcpy(audioData, resource.data() + metaSize, sampleDataSize);
as = audio_segment(
audioData,
0,
sampleDataSize / (header.wave_format.bitsPerSample / 8), // Sample count, unknown for wav
channels,
(int)header.wave_format.sampleRate,
-1, // Layer, does not exist in wav
-1 // avg_biterate_kbps, unknown for wav
);
}
detail::createAndBufferAudioData(&as.audioBufferId, as.channels, header.wave_format.bitsPerSample, as.getData(), sampleDataSize, as.sampleRate);
return decay(Ok(as));
}
namespace detail
{
void convertToMono(const byte* inputData, int dataSize, byte* monoData, int channels, int bitsPerSample)
{
assert_msg("0 was passed for channels", channels != 0);
if (channels == 1)
{
memcpy(monoData, inputData, dataSize);
return;
}
uint bytesPerSample = bitsPerSample / 8;
uint j = 0;
uint channelSize = channels * bytesPerSample;
for (uint i = 0; i < dataSize; i += channelSize)
{
switch (bytesPerSample)
{
case 1:
{
uint64 data = 0;
for (uint c = 0; c < channelSize; c += bytesPerSample)
{
data += static_cast<uint64>(*(inputData + i + c));
}
monoData[j] = data / channels;
j += bytesPerSample;
}
break;
case 2:
{
int64 data = 0;
for (uint c = 0; c < channelSize; c += bytesPerSample)
{
data += static_cast<const int64>(*reinterpret_cast<const int16*>(inputData + i + c));
}
*reinterpret_cast<int16*>(monoData + j) = data / channels;
j += bytesPerSample;
}
break;
case 4:
{
float data = 0;
for (uint c = 0; c < channelSize; c += bytesPerSample)
{
data += *reinterpret_cast<const float*>(inputData + i + c);
}
*reinterpret_cast<float*>(monoData + j) = data / channels;
j += bytesPerSample;
}
break;
default:
break;
}
}
}
byte* convertToMono(const byte* inputData, int dataSize, int& monoSize, int& channels, int bitsPerSample)
{
monoSize = dataSize / channels;
byte* monoData = new byte[monoSize];
if (channels == 1)
{
memcpy(monoData, inputData, monoSize);
return monoData;
}
convertToMono(inputData, dataSize, monoData, channels, bitsPerSample);
channels = 1;
return monoData;
}
channel_data extractChannels(const byte* inputData, int dataSize, int channels, int bitsPerSamples)
{
assert_msg("0 was passed for channels", channels != 0);
// channelData is a 2D array of [channels][channelData]
// channelData will hold the audio data per channel
channel_data channelData(channels);
channelData.dataPerChannel.resize(channels);
if (channels == 1)
{
channelData.dataPerChannel[0].resize(dataSize);
std::copy(inputData, inputData + dataSize, std::back_inserter(channelData.dataPerChannel[0]));
//memcpy(channelData.dataPerChannel[0], inputData, dataSize);
return channelData;
}
int bytesPerSample = bitsPerSamples / 8;
for (size_type c = 0; c < channels; ++c)
{
channelData.dataPerChannel[c].resize(dataSize/channels);
}
switch (bytesPerSample)
{
case 1:
{
uint j = 0;
for (size_type i = 0; i < dataSize; i += bytesPerSample * channels)
{
for (size_type c = 0; c < channels; ++c)
{
channelData.dataPerChannel[c][j] = inputData[i + c];
}
++j;
}
}
break;
case 2:
{
uint j = 0;
int16 data = 0;
for (size_type i = 0; i < dataSize; i += bytesPerSample * channels)
{
for (size_type c = 0; c < channels; ++c)
{
data = *reinterpret_cast<const int16*>(inputData + i + (c*bytesPerSample));
*reinterpret_cast<int16*>(channelData.dataPerChannel[c].data()+j) = data;
}
j+=bytesPerSample;
}
}
break;
case 4:
{
uint j = 0;
float data = 0;
for (size_type i = 0; i < dataSize; i += bytesPerSample * channels)
{
for (size_type c = 0; c < channels; ++c)
{
data = *reinterpret_cast<const float*>(inputData + i + (c * bytesPerSample));
*reinterpret_cast<float*>(channelData.dataPerChannel[c].data() + j) = data;
}
j += bytesPerSample;
}
}
break;
default:
break;
}
return channelData;
}
ALenum getAudioFormat(int channels, int bitsPerSample)
{
if (channels == 1)
{
if (bitsPerSample == 8) return AL_FORMAT_MONO8;
else if (bitsPerSample == 16) return AL_FORMAT_MONO16;
else if (bitsPerSample == 32) return AL_FORMAT_MONO_FLOAT32;
}
else if (channels == 2)
{
if (bitsPerSample == 8) return AL_FORMAT_STEREO8;
else if (bitsPerSample == 16) return AL_FORMAT_STEREO16;
else if (bitsPerSample == 32) return AL_FORMAT_STEREO_FLOAT32;
}
else if (channels == 4)
{
if (bitsPerSample == 8) return AL_FORMAT_QUAD8;
else if (bitsPerSample == 16) return AL_FORMAT_QUAD16;
else if (bitsPerSample == 32) return AL_FORMAT_QUAD32;
}
return AL_FORMAT_STEREO16;
}
void createAndBufferAudioData(ALuint* bufferId, int channels, int bitsPerSample, byte* data, int dataSize, int sampleRate)
{
std::lock_guard guard(AudioSystem::contextLock);
alcMakeContextCurrent(AudioSystem::alcContext);
//Generate openal buffer
alGenBuffers((ALuint)1, bufferId);
alBufferData(*bufferId, detail::getAudioFormat(channels, bitsPerSample), data, dataSize, sampleRate);
alcMakeContextCurrent(nullptr);
}
}
}