| 208 | } |
| 209 | |
| 210 | audio::Sample Teensy_I2S_Audio::read() { |
| 211 | if (mHasError || !mInitialized || !mRecorder) { |
| 212 | return audio::Sample(); // Invalid sample |
| 213 | } |
| 214 | |
| 215 | u32 firstBlockTimestamp = 0; |
| 216 | |
| 217 | if (mConfig.mAudioChannel == audio::AudioChannel::Both) { |
| 218 | // Stereo downmix mode: accumulate L/R blocks and downmix to mono |
| 219 | // Each iteration: get L block + R block, average them -> 128 mono samples |
| 220 | // Repeat 4 times -> 512 mono samples total |
| 221 | while (mBlocksAccumulated < BLOCKS_TO_ACCUMULATE) { |
| 222 | fl::vector<fl::i16> leftSamples, rightSamples; |
| 223 | u8 leftChannel, rightChannel; |
| 224 | u32 leftTimestamp, rightTimestamp; |
| 225 | |
| 226 | bool hasLeft = mRecorder->dequeueBlock(leftSamples, leftChannel, leftTimestamp); |
| 227 | bool hasRight = mRecorder->dequeueBlock(rightSamples, rightChannel, rightTimestamp); |
| 228 | |
| 229 | if (!hasLeft || !hasRight || leftChannel != 0 || rightChannel != 1) { |
| 230 | // Need both channels, but don't have them yet |
| 231 | return audio::Sample(); |
| 232 | } |
| 233 | |
| 234 | if (mBlocksAccumulated == 0) { |
| 235 | firstBlockTimestamp = leftTimestamp; |
| 236 | } |
| 237 | |
| 238 | // Downmix L+R to mono: (L + R) / 2 |
| 239 | fl::size minSize = (leftSamples.size() < rightSamples.size()) ? |
| 240 | leftSamples.size() : rightSamples.size(); |
| 241 | for (fl::size i = 0; i < minSize; i++) { |
| 242 | fl::i16 monoSample = (static_cast<fl::i32>(leftSamples[i]) + |
| 243 | static_cast<fl::i32>(rightSamples[i])) / 2; |
| 244 | mAccumulatedSamples.push_back(monoSample); |
| 245 | } |
| 246 | |
| 247 | mBlocksAccumulated++; |
| 248 | } |
| 249 | } else { |
| 250 | // Mono mode: accumulate single channel blocks |
| 251 | // Target: 512 samples from one channel (Left or Right) |
| 252 | u8 expectedChannel = (mConfig.mAudioChannel == audio::AudioChannel::Left) ? 0 : 1; |
| 253 | |
| 254 | while (mBlocksAccumulated < BLOCKS_TO_ACCUMULATE) { |
| 255 | fl::vector<fl::i16> samples; |
| 256 | u8 channel; |
| 257 | u32 timestamp; |
| 258 | |
| 259 | if (!mRecorder->dequeueBlock(samples, channel, timestamp)) { |
| 260 | // No data available yet |
| 261 | return audio::Sample(); |
| 262 | } |
| 263 | |
| 264 | if (channel != expectedChannel) { |
| 265 | // Wrong channel, skip this block |
| 266 | continue; |
| 267 | } |
no test coverage detected