Fix "no remove aqua speed" bug when player leaves the water
[ryzomcore.git] / nel / src / sound / audio_decoder_mp3.cpp
blobf19da18c8d2930ac665fe50675192e7f2edc6c94
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2018-2019 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdsound.h"
23 #if !defined(NL_OS_WINDOWS) || (NL_COMP_VC_VERSION > 90) /* VS2008 does not have stdint.h */
25 #include <nel/sound/audio_decoder_mp3.h>
27 #define DR_MP3_IMPLEMENTATION
28 #include <nel/sound/decoder/dr_mp3.h>
30 using namespace std;
31 using namespace NLMISC;
32 using namespace NLSOUND;
34 namespace NLSOUND {
36 // callback for drmp3
37 static size_t drmp3_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
39 NLSOUND::CAudioDecoderMP3 *decoder = static_cast<NLSOUND::CAudioDecoderMP3 *>(pUserData);
40 NLMISC::IStream *stream = decoder->getStream();
41 nlassert(stream->isReading());
43 uint32 available = decoder->getStreamSize() - stream->getPos();
44 if (available == 0)
45 return 0;
47 if (bytesToRead > available)
48 bytesToRead = available;
50 stream->serialBuffer((uint8 *)pBufferOut, bytesToRead);
51 return bytesToRead;
54 // callback for drmp3
55 static drmp3_bool32 drmp3_seek(void* pUserData, int offset, drmp3_seek_origin origin)
57 NLSOUND::CAudioDecoderMP3 *decoder = static_cast<NLSOUND::CAudioDecoderMP3 *>(pUserData);
58 NLMISC::IStream *stream = decoder->getStream();
59 nlassert(stream->isReading());
61 NLMISC::IStream::TSeekOrigin seekOrigin;
62 if (origin == drmp3_seek_origin_start)
63 seekOrigin = NLMISC::IStream::begin;
64 else if (origin == drmp3_seek_origin_current)
65 seekOrigin = NLMISC::IStream::current;
66 else
67 return false;
69 stream->seek((sint32) offset, seekOrigin);
70 return true;
73 // these should always be 44100Hz/16bit/2ch
74 #define MP3_SAMPLE_RATE 44100
75 #define MP3_BITS_PER_SAMPLE 16
76 #define MP3_CHANNELS 2
78 CAudioDecoderMP3::CAudioDecoderMP3(NLMISC::IStream *stream, bool loop)
79 : IAudioDecoder(),
80 _Stream(stream), _Loop(loop), _IsMusicEnded(false), _StreamSize(0), _IsSupported(false), _PCMFrameCount(0)
82 _StreamOffset = stream->getPos();
83 stream->seek(0, NLMISC::IStream::end);
84 _StreamSize = stream->getPos();
85 stream->seek(_StreamOffset, NLMISC::IStream::begin);
87 drmp3_config config;
88 config.outputChannels = MP3_CHANNELS;
89 config.outputSampleRate = MP3_SAMPLE_RATE;
91 _IsSupported = drmp3_init(&_Decoder, &drmp3_read, &drmp3_seek, this, &config);
92 if (!_IsSupported)
94 nlwarning("MP3: Decoder failed to read stream");
98 CAudioDecoderMP3::~CAudioDecoderMP3()
100 if (_IsSupported)
102 drmp3_uninit(&_Decoder);
106 bool CAudioDecoderMP3::isFormatSupported() const
108 return _IsSupported;
111 /// Get information on a music file.
112 bool CAudioDecoderMP3::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title, float &length)
114 CAudioDecoderMP3 mp3(stream, false);
115 if (!mp3.isFormatSupported())
117 title.clear();
118 artist.clear();
119 length = 0.f;
121 return false;
123 length = mp3.getLength();
125 // ID3v1
126 stream->seek(-128, NLMISC::IStream::end);
128 uint8 buf[128];
129 stream->serialBuffer(buf, 128);
131 if(buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G')
133 uint i;
134 for(i = 0; i < 30; ++i) if (buf[3+i] == '\0') break;
135 artist.assign((char *)&buf[3], i);
137 for(i = 0; i < 30; ++i) if (buf[33+i] == '\0') break;
138 title.assign((char *)&buf[33], i);
142 return true;
145 uint32 CAudioDecoderMP3::getRequiredBytes()
147 return 0; // no minimum requirement of bytes to buffer out
150 uint32 CAudioDecoderMP3::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum)
152 if (_IsMusicEnded) return 0;
153 nlassert(minimum <= maximum); // can't have this..
155 // TODO: CStreamFileSource::play() will stall when there is no frames on warmup
156 // supported can be set false if there is an issue creating converter
157 if (!_IsSupported)
159 _IsMusicEnded = true;
160 return 1;
163 sint16 *pFrameBufferOut = (sint16 *)buffer;
164 uint32 bytesPerFrame = MP3_BITS_PER_SAMPLE / 8 * _Decoder.channels;
166 uint32 totalFramesRead = 0;
167 uint32 framesToRead = minimum / bytesPerFrame;
168 while(framesToRead > 0)
170 float tempBuffer[4096];
171 uint64 tempFrames = drmp3_countof(tempBuffer) / _Decoder.channels;
173 if (tempFrames > framesToRead)
174 tempFrames = framesToRead;
176 tempFrames = drmp3_read_pcm_frames_f32(&_Decoder, tempFrames, tempBuffer);
177 if (tempFrames == 0)
178 break;
180 drmp3dec_f32_to_s16(tempBuffer, pFrameBufferOut, tempFrames * _Decoder.channels);
181 pFrameBufferOut += tempFrames * _Decoder.channels;
183 framesToRead -= tempFrames;
184 totalFramesRead += tempFrames;
187 _IsMusicEnded = (framesToRead > 0);
188 return totalFramesRead * bytesPerFrame;
191 uint8 CAudioDecoderMP3::getChannels()
193 return _Decoder.channels;
196 uint CAudioDecoderMP3::getSamplesPerSec()
198 return _Decoder.sampleRate;
201 uint8 CAudioDecoderMP3::getBitsPerSample()
203 return MP3_BITS_PER_SAMPLE;
206 bool CAudioDecoderMP3::isMusicEnded()
208 return _IsMusicEnded;
211 float CAudioDecoderMP3::getLength()
213 // cached because drmp3_get_pcm_frame_count is reading full file
214 if (_PCMFrameCount == 0)
216 _PCMFrameCount = drmp3_get_pcm_frame_count(&_Decoder);
219 return _PCMFrameCount / (float) _Decoder.sampleRate;
222 void CAudioDecoderMP3::setLooping(bool loop)
224 _Loop = loop;
227 } /* namespace NLSOUND */
229 #endif /* (NL_COMP_VC_VERSION > 90) */
231 /* end of file */