diff --git a/examples/DopplerEffect/main.cpp b/examples/DopplerEffect/main.cpp index d57c0e082..7ecd473b8 100644 --- a/examples/DopplerEffect/main.cpp +++ b/examples/DopplerEffect/main.cpp @@ -61,7 +61,7 @@ int main() // La boucle du programme (Pour déplacer le son) Nz::Clock clock; - while (sound.GetStatus() == Nz::SoundStatus_Playing) + while (sound.GetStatus() == Nz::SoundStatus::Playing) { // Comme le son se joue dans un thread séparé, on peut mettre en pause le principal régulièrement int sleepTime = int(1000/60 - clock.GetMilliseconds()); // 60 FPS diff --git a/include/Nazara/Audio/Algorithm.hpp b/include/Nazara/Audio/Algorithm.hpp index 830e6ead6..27d1e5394 100644 --- a/include/Nazara/Audio/Algorithm.hpp +++ b/include/Nazara/Audio/Algorithm.hpp @@ -8,9 +8,11 @@ #define NAZARA_ALGORITHM_AUDIO_HPP #include +#include namespace Nz { + inline UInt32 GetChannelCount(AudioFormat format); template void MixToMono(T* input, T* output, UInt32 channelCount, UInt64 frameCount); } diff --git a/include/Nazara/Audio/Algorithm.inl b/include/Nazara/Audio/Algorithm.inl index f7db75e77..1d1932e47 100644 --- a/include/Nazara/Audio/Algorithm.inl +++ b/include/Nazara/Audio/Algorithm.inl @@ -2,11 +2,52 @@ // This file is part of the "Nazara Engine - Audio module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include namespace Nz { + /*! + * \ingroup audio + * \brief Get the number of channels occupied by an audio format + * \returns The number of channels occupied by an audio format (mono returns 1, stero returns 2, etc.) + * + * \param format A valid audio format + * + * \remark The format must be valid (using AudioFormat::Unknown will trigger an error) + */ + UInt32 GetChannelCount(AudioFormat format) + { + NazaraAssert(format != AudioFormat::Unknown, "invalid audio format"); + + switch (format) + { + case AudioFormat::Unknown: //< Just to make the compiler stop complaining + break; + + case AudioFormat::U16_Mono: + return 1; + + case AudioFormat::U16_Stereo: + return 2; + + case AudioFormat::U16_Quad: + return 4; + + case AudioFormat::U16_5_1: + return 6; + + case AudioFormat::U16_6_1: + return 7; + + case AudioFormat::U16_7_1: + return 8; + } + + return 0; + } + /*! * \ingroup audio * \brief Mixes channels in mono diff --git a/include/Nazara/Audio/Audio.hpp b/include/Nazara/Audio/Audio.hpp index a25418cf4..affab10a9 100644 --- a/include/Nazara/Audio/Audio.hpp +++ b/include/Nazara/Audio/Audio.hpp @@ -32,7 +32,6 @@ namespace Nz Audio(Audio&&) = delete; ~Audio(); - AudioFormat GetAudioFormat(unsigned int channelCount) const; float GetDopplerFactor() const; float GetGlobalVolume() const; Vector3f GetListenerDirection() const; diff --git a/include/Nazara/Audio/Enums.hpp b/include/Nazara/Audio/Enums.hpp index e3e12b41b..71c338de1 100644 --- a/include/Nazara/Audio/Enums.hpp +++ b/include/Nazara/Audio/Enums.hpp @@ -9,27 +9,32 @@ namespace Nz { - enum AudioFormat + enum class AudioFormat { - AudioFormat_Unknown = -1, + Unknown = -1, - // The integer value is the number of channels used by the format - AudioFormat_Mono = 1, - AudioFormat_Stereo = 2, - AudioFormat_Quad = 4, - AudioFormat_5_1 = 6, - AudioFormat_6_1 = 7, - AudioFormat_7_1 = 8, + U16_Mono, + U16_Stereo, + U16_Quad, + U16_5_1, + U16_6_1, + U16_7_1, - AudioFormat_Max = AudioFormat_7_1 + Max = U16_7_1 }; - enum SoundStatus + constexpr std::size_t AudioFormatCount = static_cast(AudioFormat::Max) + 1; + + enum class SoundStatus { - SoundStatus_Playing, - SoundStatus_Paused, - SoundStatus_Stopped + Playing, + Paused, + Stopped, + + Max = Stopped }; + + constexpr std::size_t SoundStatusCount = static_cast(SoundStatus::Max) + 1; } #endif // NAZARA_ENUMS_AUDIO_HPP diff --git a/include/Nazara/Audio/OpenAL.hpp b/include/Nazara/Audio/OpenAL.hpp index d60a1261c..5f1bdf879 100644 --- a/include/Nazara/Audio/OpenAL.hpp +++ b/include/Nazara/Audio/OpenAL.hpp @@ -80,7 +80,7 @@ namespace Nz static void Uninitialize(); - static ALenum AudioFormat[AudioFormat_Max + 1]; + static ALenum AudioFormat[AudioFormatCount]; private: static void CloseDevice(); diff --git a/src/Nazara/Audio/Audio.cpp b/src/Nazara/Audio/Audio.cpp index 863930497..77bb50cc0 100644 --- a/src/Nazara/Audio/Audio.cpp +++ b/src/Nazara/Audio/Audio.cpp @@ -43,33 +43,6 @@ namespace Nz OpenAL::Uninitialize(); } - /*! - * \brief Gets the format of the audio - * \return AudioFormat Enumeration type for the format - * - * \param channelCount Number of channels - * - * \remark Produces a NazaraError if the number of channels is erroneous (3 or 5) and AudioFormat_Unknown is returned - */ - - AudioFormat Audio::GetAudioFormat(unsigned int channelCount) const - { - switch (channelCount) - { - case 1: - case 2: - case 4: - case 6: - case 7: - case 8: - return static_cast(channelCount); - - default: - NazaraError("Invalid channel count: " + NumberToString(channelCount)); - return AudioFormat_Unknown; - } - } - /*! * \brief Gets the factor of the doppler effect * \return Global factor of the doppler effect @@ -200,10 +173,10 @@ namespace Nz */ bool Audio::IsFormatSupported(AudioFormat format) const { - if (format == AudioFormat_Unknown) + if (format == AudioFormat::Unknown) return false; - return OpenAL::AudioFormat[format] != 0; + return OpenAL::AudioFormat[UnderlyingCast(format)] != 0; } /*! diff --git a/src/Nazara/Audio/Formats/sndfileLoader.cpp b/src/Nazara/Audio/Formats/sndfileLoader.cpp index 9e373b3ee..2490724fb 100644 --- a/src/Nazara/Audio/Formats/sndfileLoader.cpp +++ b/src/Nazara/Audio/Formats/sndfileLoader.cpp @@ -15,17 +15,45 @@ #include #include #include +#include #include +#include #include #include #include -#include #include namespace Nz { namespace Detail { + std::optional GuessFormat(UInt32 channelCount) + { + switch (channelCount) + { + case 1: + return AudioFormat::U16_Mono; + + case 2: + return AudioFormat::U16_Stereo; + + case 4: + return AudioFormat::U16_Quad; + + case 5: + return AudioFormat::U16_5_1; + + case 6: + return AudioFormat::U16_6_1; + + case 7: + return AudioFormat::U16_7_1; + + default: + return std::nullopt; + } + } + sf_count_t GetSize(void* user_data) { Stream* stream = static_cast(user_data); @@ -93,7 +121,7 @@ namespace Nz { // Nous avons besoin du nombre de canaux d'origine pour convertir en mono, nous trichons donc un peu... if (m_mixToMono) - return AudioFormat_Mono; + return AudioFormat::U16_Mono; else return m_format; } @@ -136,10 +164,10 @@ namespace Nz bool Open(Stream& stream, bool forceMono) { - SF_INFO infos; - infos.format = 0; // Unknown format + SF_INFO info; + info.format = 0; // Unknown format - m_handle = sf_open_virtual(&callbacks, SFM_READ, &infos, &stream); + m_handle = sf_open_virtual(&callbacks, SFM_READ, &info, &stream); if (!m_handle) { NazaraError("Failed to open sound: " + std::string(sf_strerror(m_handle))); @@ -153,30 +181,30 @@ namespace Nz m_handle = nullptr; }); - m_format = Audio::Instance()->GetAudioFormat(infos.channels); - if (m_format == AudioFormat_Unknown) + std::optional formatOpt = GuessFormat(info.channels); + if (!formatOpt) { - NazaraError("Channel count not handled"); + NazaraError("unexpected channel count: " + std::to_string(info.channels)); return false; } - m_sampleCount = infos.channels*infos.frames; - m_sampleRate = infos.samplerate; + m_format = *formatOpt; - // Durée de la musique (s) = samples / channels*rate - m_duration = static_cast(1000ULL*m_sampleCount / (m_format*m_sampleRate)); + m_duration = static_cast(1000ULL * info.frames / info.samplerate); + m_sampleCount = info.channels * info.frames; + m_sampleRate = info.samplerate; // https://github.com/LaurentGomila/SFML/issues/271 // http://www.mega-nerd.com/libsndfile/command.html#SFC_SET_SCALE_FLOAT_INT_READ ///FIXME: Seulement le Vorbis ? - if (infos.format & SF_FORMAT_VORBIS) + if (info.format & SF_FORMAT_VORBIS) sf_command(m_handle, SFC_SET_SCALE_FLOAT_INT_READ, nullptr, SF_TRUE); // On mixera en mono lors de la lecture - if (forceMono && m_format != AudioFormat_Mono) + if (forceMono && m_format != AudioFormat::U16_Mono) { m_mixToMono = true; - m_sampleCount = static_cast(infos.frames); + m_sampleCount = static_cast(info.frames); } else m_mixToMono = false; @@ -191,12 +219,14 @@ namespace Nz // Si la musique a été demandée en mono, nous devons la convertir à la volée lors de la lecture if (m_mixToMono) { - // On garde un buffer sur le côté pour éviter la réallocation - m_mixBuffer.resize(m_format * sampleCount); - sf_count_t readSampleCount = sf_read_short(m_handle, m_mixBuffer.data(), m_format * sampleCount); - MixToMono(m_mixBuffer.data(), static_cast(buffer), m_format, sampleCount); + UInt32 channelCount = GetChannelCount(m_format); - return readSampleCount / m_format; + // On garde un buffer sur le côté pour éviter la réallocation + m_mixBuffer.resize(channelCount * sampleCount); + sf_count_t readSampleCount = sf_read_short(m_handle, m_mixBuffer.data(), channelCount * sampleCount); + MixToMono(m_mixBuffer.data(), static_cast(buffer), channelCount, sampleCount); + + return readSampleCount / channelCount; } else return sf_read_short(m_handle, static_cast(buffer), sampleCount); @@ -314,7 +344,7 @@ namespace Nz if (!file) { NazaraError("Failed to load sound file: " + std::string(sf_strerror(file))); - return nullptr; + return {}; } CallOnExit onExit([file] @@ -322,13 +352,15 @@ namespace Nz sf_close(file); }); - AudioFormat format = Audio::Instance()->GetAudioFormat(info.channels); - if (format == AudioFormat_Unknown) + std::optional formatOpt = GuessFormat(info.channels); + if (!formatOpt) { - NazaraError("Channel count not handled"); - return nullptr; + NazaraError("unexpected channel count: " + std::to_string(info.channels)); + return {}; } + AudioFormat format = *formatOpt; + // https://github.com/LaurentGomila/SFML/issues/271 // http://www.mega-nerd.com/libsndfile/command.html#SFC_SET_SCALE_FLOAT_INT_READ ///FIXME: Only Vorbis? @@ -341,15 +373,15 @@ namespace Nz if (sf_read_short(file, samples.get(), sampleCount) != sampleCount) { NazaraError("Failed to read samples"); - return nullptr; + return {}; } // Convert to mono if required - if (parameters.forceMono && format != AudioFormat_Mono) + if (parameters.forceMono && format != AudioFormat::U16_Mono) { MixToMono(samples.get(), samples.get(), static_cast(info.channels), static_cast(info.frames)); - format = AudioFormat_Mono; + format = AudioFormat::U16_Mono; sampleCount = static_cast(info.frames); } diff --git a/src/Nazara/Audio/Music.cpp b/src/Nazara/Audio/Music.cpp index 5264ce3d4..a6cad6961 100644 --- a/src/Nazara/Audio/Music.cpp +++ b/src/Nazara/Audio/Music.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -67,8 +68,8 @@ namespace Nz m_impl = std::make_unique(); m_impl->sampleRate = soundStream->GetSampleRate(); - m_impl->audioFormat = OpenAL::AudioFormat[format]; - m_impl->chunkSamples.resize(format * m_impl->sampleRate); // One second of samples + m_impl->audioFormat = OpenAL::AudioFormat[UnderlyingCast(format)]; + m_impl->chunkSamples.resize(GetChannelCount(format) * m_impl->sampleRate); // One second of samples m_impl->stream = std::move(soundStream); SetPlayingOffset(0); @@ -147,7 +148,7 @@ namespace Nz ALint samples = 0; alGetSourcei(m_source, AL_SAMPLE_OFFSET, &samples); - return static_cast((1000ULL * (samples + (m_impl->processedSamples / m_impl->stream->GetFormat()))) / m_impl->sampleRate); + return static_cast((1000ULL * (samples + (m_impl->processedSamples / GetChannelCount(m_impl->stream->GetFormat())))) / m_impl->sampleRate); } /*! @@ -189,8 +190,8 @@ namespace Nz SoundStatus status = GetInternalStatus(); // To compensate any delays (or the timelaps between Play() and the thread startup) - if (m_impl->streaming && status == SoundStatus_Stopped) - status = SoundStatus_Playing; + if (m_impl->streaming && status == SoundStatus::Stopped) + status = SoundStatus::Playing; return status; } @@ -289,11 +290,11 @@ namespace Nz { switch (GetStatus()) { - case SoundStatus_Playing: + case SoundStatus::Playing: SetPlayingOffset(0); break; - case SoundStatus_Paused: + case SoundStatus::Paused: alSourcePlay(m_source); break; @@ -328,7 +329,7 @@ namespace Nz Stop(); m_impl->playingOffset = offset; - m_impl->processedSamples = UInt64(offset) * m_impl->sampleRate * m_impl->stream->GetFormat() / 1000ULL; + m_impl->processedSamples = UInt64(offset) * m_impl->sampleRate * GetChannelCount(m_impl->stream->GetFormat()) / 1000ULL; if (isPlaying) Play(); @@ -405,7 +406,7 @@ namespace Nz { // The reading has stopped, we have reached the end of the stream SoundStatus status = GetInternalStatus(); - if (status == SoundStatus_Stopped) + if (status == SoundStatus::Stopped) { m_impl->streaming = false; break; diff --git a/src/Nazara/Audio/OpenAL.cpp b/src/Nazara/Audio/OpenAL.cpp index 3e516115c..5e579634f 100644 --- a/src/Nazara/Audio/OpenAL.cpp +++ b/src/Nazara/Audio/OpenAL.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -347,8 +348,7 @@ namespace Nz s_library.Unload(); } - ///WARNING: The integer value is the number of canals owned by the format - ALenum OpenAL::AudioFormat[AudioFormat_Max+1] = {0}; // Added values with loading of OpenAL + ALenum OpenAL::AudioFormat[AudioFormatCount] = {0}; // Added values with loading of OpenAL /*! * \brief Closes the device @@ -440,19 +440,19 @@ namespace Nz } // We complete the formats table - AudioFormat[AudioFormat_Mono] = AL_FORMAT_MONO16; - AudioFormat[AudioFormat_Stereo] = AL_FORMAT_STEREO16; + AudioFormat[UnderlyingCast(AudioFormat::U16_Mono)] = AL_FORMAT_MONO16; + AudioFormat[UnderlyingCast(AudioFormat::U16_Stereo)] = AL_FORMAT_STEREO16; // "The presence of an enum value does not guarantee the applicability of an extension to the current context." if (alIsExtensionPresent("AL_EXT_MCFORMATS")) { - AudioFormat[AudioFormat_Quad] = alGetEnumValue("AL_FORMAT_QUAD16"); - AudioFormat[AudioFormat_5_1] = alGetEnumValue("AL_FORMAT_51CHN16"); - AudioFormat[AudioFormat_6_1] = alGetEnumValue("AL_FORMAT_61CHN16"); - AudioFormat[AudioFormat_7_1] = alGetEnumValue("AL_FORMAT_71CHN16"); + AudioFormat[UnderlyingCast(AudioFormat::U16_Quad)] = alGetEnumValue("AL_FORMAT_QUAD16"); + AudioFormat[UnderlyingCast(AudioFormat::U16_5_1)] = alGetEnumValue("AL_FORMAT_51CHN16"); + AudioFormat[UnderlyingCast(AudioFormat::U16_6_1)] = alGetEnumValue("AL_FORMAT_61CHN16"); + AudioFormat[UnderlyingCast(AudioFormat::U16_7_1)] = alGetEnumValue("AL_FORMAT_71CHN16"); } else if (alIsExtensionPresent("AL_LOKI_quadriphonic")) - AudioFormat[AudioFormat_Quad] = alGetEnumValue("AL_FORMAT_QUAD16_LOKI"); + AudioFormat[UnderlyingCast(AudioFormat::U16_Quad)] = alGetEnumValue("AL_FORMAT_QUAD16_LOKI"); return true; } diff --git a/src/Nazara/Audio/Sound.cpp b/src/Nazara/Audio/Sound.cpp index 3692b2954..23ee721ae 100644 --- a/src/Nazara/Audio/Sound.cpp +++ b/src/Nazara/Audio/Sound.cpp @@ -125,7 +125,7 @@ namespace Nz */ bool Sound::IsPlaying() const { - return GetStatus() == SoundStatus_Playing; + return GetStatus() == SoundStatus::Playing; } /*! diff --git a/src/Nazara/Audio/SoundBuffer.cpp b/src/Nazara/Audio/SoundBuffer.cpp index 06ef15dd9..fe078846c 100644 --- a/src/Nazara/Audio/SoundBuffer.cpp +++ b/src/Nazara/Audio/SoundBuffer.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -130,7 +131,7 @@ namespace Nz CallOnExit clearBufferOnExit([buffer] () { alDeleteBuffers(1, &buffer); }); - alBufferData(buffer, OpenAL::AudioFormat[format], samples, static_cast(sampleCount*sizeof(Int16)), static_cast(sampleRate)); + alBufferData(buffer, OpenAL::AudioFormat[UnderlyingCast(format)], samples, static_cast(sampleCount*sizeof(Int16)), static_cast(sampleRate)); if (alGetError() != AL_NO_ERROR) { @@ -140,7 +141,7 @@ namespace Nz m_impl = std::make_unique(); m_impl->buffer = buffer; - m_impl->duration = static_cast((1000ULL*sampleCount / (format * sampleRate))); + m_impl->duration = static_cast((1000ULL*sampleCount / (GetChannelCount(format) * sampleRate))); m_impl->format = format; m_impl->sampleCount = sampleCount; m_impl->sampleRate = sampleRate; diff --git a/src/Nazara/Audio/SoundEmitter.cpp b/src/Nazara/Audio/SoundEmitter.cpp index c81f26846..deeb27855 100644 --- a/src/Nazara/Audio/SoundEmitter.cpp +++ b/src/Nazara/Audio/SoundEmitter.cpp @@ -326,18 +326,18 @@ namespace Nz { case AL_INITIAL: case AL_STOPPED: - return SoundStatus_Stopped; + return SoundStatus::Stopped; case AL_PAUSED: - return SoundStatus_Paused; + return SoundStatus::Paused; case AL_PLAYING: - return SoundStatus_Playing; + return SoundStatus::Playing; default: NazaraInternalError("Source state unrecognized"); } - return SoundStatus_Stopped; + return SoundStatus::Stopped; } } diff --git a/tests/Engine/Audio/Music.cpp b/tests/Engine/Audio/Music.cpp index cd0d2857a..9df9f2989 100644 --- a/tests/Engine/Audio/Music.cpp +++ b/tests/Engine/Audio/Music.cpp @@ -20,12 +20,12 @@ SCENARIO("Music", "[AUDIO][MUSIC]") { CHECK(music.GetDuration() <= 64000); // 1 min 03 = 63s = 63000ms CHECK(music.GetDuration() >= 63000); - CHECK(music.GetFormat() == Nz::AudioFormat_Stereo); + CHECK(music.GetFormat() == Nz::AudioFormat::U16_Stereo); CHECK(music.GetPlayingOffset() == 0); CHECK(music.GetSampleCount() <= 5644800); // 64s * 44100 Hz * 2 (stereo) CHECK(music.GetSampleCount() >= 5556600); // 63s * 44100 Hz * 2 (stereo) CHECK(music.GetSampleRate() == 44100 /* Hz */); - CHECK(music.GetStatus() == Nz::SoundStatus_Stopped); + CHECK(music.GetStatus() == Nz::SoundStatus::Stopped); CHECK(music.IsLooping() == false); } @@ -39,7 +39,7 @@ SCENARIO("Music", "[AUDIO][MUSIC]") std::this_thread::sleep_for(std::chrono::milliseconds(200)); REQUIRE(music.GetPlayingOffset() <= 1300); music.Pause(); - REQUIRE(music.GetStatus() == Nz::SoundStatus_Paused); + REQUIRE(music.GetStatus() == Nz::SoundStatus::Paused); music.SetPlayingOffset(3500); REQUIRE(music.GetPlayingOffset() >= 3500); diff --git a/tests/Engine/Audio/Sound.cpp b/tests/Engine/Audio/Sound.cpp index 8f239d694..3ff62a732 100644 --- a/tests/Engine/Audio/Sound.cpp +++ b/tests/Engine/Audio/Sound.cpp @@ -20,7 +20,7 @@ SCENARIO("Sound", "[AUDIO][SOUND]") { REQUIRE(sound.GetDuration() <= 8500); // 8s = 8000ms REQUIRE(sound.GetDuration() >= 8000); - REQUIRE(sound.GetStatus() == Nz::SoundStatus_Stopped); + REQUIRE(sound.GetStatus() == Nz::SoundStatus::Stopped); REQUIRE(sound.IsLooping() == false); } @@ -34,7 +34,7 @@ SCENARIO("Sound", "[AUDIO][SOUND]") std::this_thread::sleep_for(std::chrono::milliseconds(200)); REQUIRE(sound.GetPlayingOffset() <= 1300); sound.Pause(); - REQUIRE(sound.GetStatus() == Nz::SoundStatus_Paused); + REQUIRE(sound.GetStatus() == Nz::SoundStatus::Paused); sound.SetPlayingOffset(3500); REQUIRE(sound.GetPlayingOffset() >= 3500);