| 189 | } |
| 190 | |
| 191 | bool VorbisDecoderImpl::decodeNextFrame(audio::Sample* outSample) { |
| 192 | if (!mDecoder.isOpen() || mEndOfStream) { |
| 193 | return false; |
| 194 | } |
| 195 | |
| 196 | VorbisInfo info = mDecoder.getInfo(); |
| 197 | fl::i32 channels = info.channels > 0 ? info.channels : 1; |
| 198 | |
| 199 | // Decode to i16 interleaved |
| 200 | fl::i32 samplesDecoded = mDecoder.getSamplesShortInterleaved( |
| 201 | channels, |
| 202 | mPcmBuffer.data(), |
| 203 | static_cast<fl::i32>(mPcmBuffer.size()) |
| 204 | ); |
| 205 | |
| 206 | if (samplesDecoded == 0) { |
| 207 | mEndOfStream = true; |
| 208 | mPosition = mFileData.size(); // At end of stream |
| 209 | return false; |
| 210 | } |
| 211 | |
| 212 | // Update position estimate based on sample offset ratio |
| 213 | fl::u32 totalSamples = mDecoder.getTotalSamples(); |
| 214 | if (totalSamples > 0) { |
| 215 | fl::u32 currentSample = mDecoder.getSampleOffset(); |
| 216 | mPosition = (mFileData.size() * currentSample) / totalSamples; |
| 217 | } |
| 218 | |
| 219 | // Convert stereo to mono by averaging if needed |
| 220 | if (channels == 2) { |
| 221 | fl::vector<fl::i16> mono; |
| 222 | mono.reserve(samplesDecoded); |
| 223 | for (fl::i32 i = 0; i < samplesDecoded; ++i) { |
| 224 | fl::i32 left = mPcmBuffer[i * 2]; |
| 225 | fl::i32 right = mPcmBuffer[i * 2 + 1]; |
| 226 | mono.push_back(static_cast<fl::i16>((left + right) / 2)); |
| 227 | } |
| 228 | *outSample = audio::Sample(mono); |
| 229 | } else { |
| 230 | *outSample = audio::Sample(fl::span<const fl::i16>(mPcmBuffer.data(), samplesDecoded)); |
| 231 | } |
| 232 | |
| 233 | return true; |
| 234 | } |
| 235 | |
| 236 | void VorbisDecoderImpl::reset() { |
| 237 | if (mDecoder.isOpen()) { |
nothing calls this directly
no test coverage detected