diff --git a/include/Nazara/Audio/Algorithm.hpp b/include/Nazara/Audio/Algorithm.hpp new file mode 100644 index 000000000..9ee136909 --- /dev/null +++ b/include/Nazara/Audio/Algorithm.hpp @@ -0,0 +1,16 @@ +// Copyright (C) 2013 Jérôme Leclercq +// This file is part of the "Nazara Engine - Audio module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ALGORITHM_AUDIO_HPP +#define NAZARA_ALGORITHM_AUDIO_HPP + +#include + +template void NzMixToMono(T* input, T* output, unsigned int channelCount, unsigned int frameCount); + +#include + +#endif // NAZARA_ALGORITHM_AUDIO_HPP diff --git a/include/Nazara/Audio/Algorithm.inl b/include/Nazara/Audio/Algorithm.inl new file mode 100644 index 000000000..4cf202906 --- /dev/null +++ b/include/Nazara/Audio/Algorithm.inl @@ -0,0 +1,25 @@ +// Copyright (C) 2013 Jérôme Leclercq +// This file is part of the "Nazara Engine - Audio module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +template +void NzMixToMono(T* input, T* output, unsigned int channelCount, unsigned int frameCount) +{ + // Pour éviter l'overflow, on utilise comme accumulateur un type assez grand, (u)int 64 bits pour les entiers, double pour les flottants + typedef typename std::conditional::value, nzUInt64, nzInt64>::type BiggestInt; + typedef typename std::conditional::value, BiggestInt, double>::type Biggest; + + for (unsigned int i = 0; i < frameCount; ++i) + { + Biggest acc = Biggest(0); + for (unsigned int j = 0; j < channelCount; ++j) + acc += input[i*channelCount + j]; + + output[i] = acc/channelCount; + } +} + +#include diff --git a/include/Nazara/Audio/Music.hpp b/include/Nazara/Audio/Music.hpp index 9af61c3d4..809443962 100644 --- a/include/Nazara/Audio/Music.hpp +++ b/include/Nazara/Audio/Music.hpp @@ -14,6 +14,8 @@ struct NzMusicParams { + bool forceMono = false; + bool IsValid() const; }; diff --git a/include/Nazara/Audio/SoundBuffer.hpp b/include/Nazara/Audio/SoundBuffer.hpp index 7b8b30575..376047ef0 100644 --- a/include/Nazara/Audio/SoundBuffer.hpp +++ b/include/Nazara/Audio/SoundBuffer.hpp @@ -17,6 +17,8 @@ struct NzSoundBufferParams { + bool forceMono = false; + bool IsValid() const; }; diff --git a/src/Nazara/Audio/Loaders/sndfile/Loader.cpp b/src/Nazara/Audio/Loaders/sndfile/Loader.cpp index 73e839bf1..36c2461b4 100644 --- a/src/Nazara/Audio/Loaders/sndfile/Loader.cpp +++ b/src/Nazara/Audio/Loaders/sndfile/Loader.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include #include #include @@ -89,7 +90,10 @@ namespace nzAudioFormat GetFormat() const { - return m_format; + if (m_mixToMono) + return nzAudioFormat_Mono; + else + return m_format; } unsigned int GetSampleCount() const @@ -102,7 +106,7 @@ namespace return m_sampleRate; } - bool Open(const NzString& filePath) + bool Open(const NzString& filePath, bool forceMono) { m_file = new NzFile(filePath); if (!m_file->Open(NzFile::ReadOnly)) @@ -111,12 +115,14 @@ namespace return false; } - return Open(*m_file); + return Open(*m_file, forceMono); } - bool Open(NzInputStream& stream) + bool Open(NzInputStream& stream, bool forceMono) { SF_INFO infos; + infos.format = 0; + m_handle = sf_open_virtual(&callbacks, SFM_READ, &infos, &stream); if (!m_handle) { @@ -127,10 +133,10 @@ namespace m_format = NzAudio::GetAudioFormat(infos.channels); if (m_format == nzAudioFormat_Unknown) { - NazaraError("Channel count not handled"); sf_close(m_handle); m_handle = nullptr; + NazaraError("Channel count not handled"); return false; } @@ -145,12 +151,29 @@ namespace if (infos.format & SF_FORMAT_VORBIS) sf_command(m_handle, SFC_SET_SCALE_FLOAT_INT_READ, nullptr, SF_TRUE); + if (forceMono && m_format != nzAudioFormat_Mono) + { + m_mixToMono = true; + m_sampleCount = infos.frames; + } + else + m_mixToMono = false; + return true; } unsigned int Read(void* buffer, unsigned int sampleCount) { - return sf_read_short(m_handle, reinterpret_cast(buffer), sampleCount); + if (m_mixToMono) + { + std::unique_ptr samples(new nzInt16[m_format*sampleCount]); + unsigned int readSampleCount = sf_read_short(m_handle, samples.get(), m_format*sampleCount); + NzMixToMono(samples.get(), reinterpret_cast(buffer), m_format, sampleCount); + + return readSampleCount / m_format; + } + else + return sf_read_short(m_handle, reinterpret_cast(buffer), sampleCount); } void Seek(nzUInt32 offset) @@ -162,6 +185,7 @@ namespace nzAudioFormat m_format; NzFile* m_file; SNDFILE* m_handle; + bool m_mixToMono; unsigned int m_duration; unsigned int m_sampleCount; unsigned int m_sampleRate; @@ -199,7 +223,7 @@ namespace NazaraUnused(parameters); std::unique_ptr musicStream(new sndfileStream); - if (!musicStream->Open(filePath)) + if (!musicStream->Open(filePath, parameters.forceMono)) { NazaraError("Failed to open music stream"); return false; @@ -221,7 +245,7 @@ namespace NazaraUnused(parameters); std::unique_ptr musicStream(new sndfileStream); - if (!musicStream->Open(stream)) + if (!musicStream->Open(stream, parameters.forceMono)) { NazaraError("Failed to open music stream"); return false; @@ -295,6 +319,16 @@ namespace return false; } + if (parameters.forceMono && format != nzAudioFormat_Mono) + { + std::unique_ptr monoSamples(new nzInt16[info.frames]); + NzMixToMono(samples.get(), monoSamples.get(), info.channels, info.frames); + + format = nzAudioFormat_Mono; + samples = std::move(monoSamples); + sampleCount = info.frames; + } + if (!soundBuffer->Create(format, static_cast(sampleCount), info.samplerate, samples.get())) { sf_close(file);