Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/BackgroundAudio.cpp
3185 views
1
#include <string>
2
#include <mutex>
3
#include <algorithm>
4
5
#include "ext/minimp3/minimp3_ex.h"
6
7
#include "Common/File/VFS/VFS.h"
8
#include "Common/UI/Root.h"
9
10
#include "Common/Data/Text/I18n.h"
11
#include "Common/CommonTypes.h"
12
#include "Common/Data/Format/RIFF.h"
13
#include "Common/Log.h"
14
#include "Common/System/System.h"
15
#include "Common/System/OSD.h"
16
#include "Common/Serialize/SerializeFuncs.h"
17
#include "Common/TimeUtil.h"
18
#include "Common/Data/Collections/FixedSizeQueue.h"
19
#include "Core/HW/SimpleAudioDec.h"
20
#include "Core/HLE/__sceAudio.h"
21
#include "Core/System.h"
22
#include "Core/Config.h"
23
#include "UI/GameInfoCache.h"
24
#include "UI/BackgroundAudio.h"
25
26
struct WavData {
27
int num_channels = -1;
28
int sample_rate = -1;
29
int numFrames = -1;
30
int samplesPerSec = -1;
31
int avgBytesPerSec = -1;
32
int raw_offset_loop_start = 0;
33
int raw_offset_loop_end = 0;
34
int loop_start_offset = 0;
35
int loop_end_offset = 0;
36
int codec = 0;
37
int raw_bytes_per_frame = 0;
38
uint8_t *raw_data = nullptr;
39
int raw_data_size = 0;
40
u8 at3_extradata[16]{};
41
42
bool Read(RIFFReader &riff);
43
44
~WavData() {
45
free(raw_data);
46
raw_data = nullptr;
47
}
48
49
[[nodiscard]]
50
bool IsSimpleWAV() const {
51
bool isBad = raw_bytes_per_frame > sizeof(int16_t) * num_channels;
52
return !isBad && num_channels > 0 && sample_rate >= 8000 && codec == 0;
53
}
54
};
55
56
bool WavData::Read(RIFFReader &file_) {
57
// If we have no loop start info, we'll just loop the entire audio.
58
raw_offset_loop_start = 0;
59
raw_offset_loop_end = 0;
60
61
if (file_.Descend('RIFF')) {
62
file_.ReadInt(); //get past 'WAVE'
63
if (file_.Descend('fmt ')) { //enter the format chunk
64
int temp = file_.ReadInt();
65
int format = temp & 0xFFFF;
66
switch (format) {
67
case 0xFFFE:
68
codec = PSP_CODEC_AT3PLUS;
69
break;
70
case 0x270:
71
codec = PSP_CODEC_AT3;
72
break;
73
case 1:
74
// Raw wave data, no codec
75
codec = 0;
76
break;
77
default:
78
ERROR_LOG(Log::sceAudio, "Unexpected wave format %04x", format);
79
return false;
80
}
81
82
num_channels = temp >> 16;
83
84
samplesPerSec = file_.ReadInt();
85
/*avgBytesPerSec =*/ file_.ReadInt();
86
87
temp = file_.ReadInt();
88
raw_bytes_per_frame = temp & 0xFFFF;
89
90
if (codec == PSP_CODEC_AT3) {
91
// The first two bytes are actually not a useful part of the extradata.
92
// We already read 16 bytes, so make sure there's enough left.
93
if (file_.GetCurrentChunkSize() >= 32) {
94
file_.ReadData(at3_extradata, 16);
95
} else {
96
memset(at3_extradata, 0, sizeof(at3_extradata));
97
}
98
}
99
file_.Ascend();
100
// INFO_LOG(Log::AUDIO, "got fmt data: %i", samplesPerSec);
101
} else {
102
ERROR_LOG(Log::Audio, "Error - no format chunk in wav");
103
file_.Ascend();
104
return false;
105
}
106
107
if (file_.Descend('smpl')) {
108
std::vector<u8> smplData;
109
smplData.resize(file_.GetCurrentChunkSize());
110
file_.ReadData(&smplData[0], (int)smplData.size());
111
112
int numLoops = *(int *)&smplData[28];
113
struct AtracLoopInfo {
114
int cuePointID;
115
int type;
116
int startSample;
117
int endSample;
118
int fraction;
119
int playCount;
120
};
121
122
if (numLoops > 0 && smplData.size() >= 36 + sizeof(AtracLoopInfo) * numLoops) {
123
AtracLoopInfo *loops = (AtracLoopInfo *)&smplData[36];
124
int samplesPerFrame = codec == PSP_CODEC_AT3PLUS ? 2048 : 1024;
125
126
for (int i = 0; i < numLoops; ++i) {
127
// Only seen forward loops, so let's ignore others.
128
if (loops[i].type != 0)
129
continue;
130
131
// We ignore loop interpolation (fraction) and play count for now.
132
raw_offset_loop_start = (loops[i].startSample / samplesPerFrame) * raw_bytes_per_frame;
133
loop_start_offset = loops[i].startSample % samplesPerFrame;
134
raw_offset_loop_end = (loops[i].endSample / samplesPerFrame) * raw_bytes_per_frame;
135
loop_end_offset = loops[i].endSample % samplesPerFrame;
136
137
if (loops[i].playCount == 0) {
138
// This was an infinite loop, so ignore the rest.
139
// In practice, there's usually only one and it's usually infinite.
140
break;
141
}
142
}
143
}
144
145
file_.Ascend();
146
}
147
148
// enter the data chunk
149
if (file_.Descend('data')) {
150
int numBytes = file_.GetCurrentChunkSize();
151
numFrames = numBytes / raw_bytes_per_frame; // numFrames
152
153
// It seems the atrac3 codec likes to read a little bit outside.
154
const int padding = 32; // 32 is the value FFMPEG uses.
155
raw_data = (uint8_t *)malloc(numBytes + padding);
156
raw_data_size = numBytes;
157
158
if (num_channels == 1 || num_channels == 2) {
159
file_.ReadData(raw_data, numBytes);
160
} else {
161
ERROR_LOG(Log::Audio, "Error - bad blockalign or channels");
162
free(raw_data);
163
raw_data = nullptr;
164
return false;
165
}
166
file_.Ascend();
167
} else {
168
ERROR_LOG(Log::Audio, "Error - no data chunk in wav");
169
file_.Ascend();
170
return false;
171
}
172
file_.Ascend();
173
} else {
174
ERROR_LOG(Log::Audio, "Could not descend into RIFF file.");
175
return false;
176
}
177
sample_rate = samplesPerSec;
178
return true;
179
}
180
181
// Really simple looping in-memory AT3 player that also takes care of reading the file format.
182
// Turns out that AT3 files used for this are modified WAVE files so fairly easy to parse.
183
class AT3PlusReader {
184
public:
185
explicit AT3PlusReader(const std::string &data)
186
: file_((const uint8_t *)&data[0], (int32_t)data.size()) {
187
// Normally 8k but let's be safe.
188
buffer_ = new short[32 * 1024];
189
190
skip_next_samples_ = 0;
191
192
wave_.Read(file_);
193
194
uint8_t *extraData = nullptr;
195
size_t extraDataSize = 0;
196
size_t blockSize = 0;
197
if (wave_.codec == PSP_CODEC_AT3) {
198
extraData = &wave_.at3_extradata[2];
199
extraDataSize = 14;
200
blockSize = wave_.raw_bytes_per_frame;
201
} else if (wave_.codec == PSP_CODEC_AT3PLUS) {
202
blockSize = wave_.raw_bytes_per_frame;
203
}
204
decoder_ = CreateAudioDecoder((PSPAudioType)wave_.codec, wave_.sample_rate, wave_.num_channels, blockSize, extraData, extraDataSize);
205
INFO_LOG(Log::Audio, "read ATRAC, frames: %d, rate %d", wave_.numFrames, wave_.sample_rate);
206
}
207
208
~AT3PlusReader() {
209
delete[] buffer_;
210
buffer_ = nullptr;
211
delete decoder_;
212
decoder_ = nullptr;
213
}
214
215
bool IsOK() const { return wave_.raw_data != nullptr; }
216
217
bool Read(int *buffer, int len) {
218
if (!wave_.raw_data)
219
return false;
220
221
while (bgQueue.size() < (size_t)(len * 2)) {
222
int outSamples = 0;
223
int inbytesConsumed = 0;
224
bool result = decoder_->Decode(wave_.raw_data + raw_offset_, wave_.raw_bytes_per_frame, &inbytesConsumed, 2, (int16_t *)buffer_, &outSamples);
225
if (!result || !outSamples)
226
return false;
227
int outBytes = outSamples * 2 * sizeof(int16_t);
228
229
if (wave_.raw_offset_loop_end != 0 && raw_offset_ == wave_.raw_offset_loop_end) {
230
// Only take the remaining bytes, but convert to stereo s16.
231
outBytes = std::min(outBytes, wave_.loop_end_offset * 4);
232
}
233
234
int start = skip_next_samples_;
235
skip_next_samples_ = 0;
236
237
for (int i = start; i < outBytes / 2; i++) {
238
bgQueue.push(buffer_[i]);
239
}
240
241
if (wave_.raw_offset_loop_end != 0 && raw_offset_ == wave_.raw_offset_loop_end) {
242
// Time to loop. Account for the addition below.
243
raw_offset_ = wave_.raw_offset_loop_start - wave_.raw_bytes_per_frame;
244
// This time we're counting each stereo sample.
245
skip_next_samples_ = wave_.loop_start_offset * 2;
246
}
247
248
// Handle loops when there's no loop info.
249
raw_offset_ += wave_.raw_bytes_per_frame;
250
if (raw_offset_ >= wave_.raw_data_size) {
251
raw_offset_ = 0;
252
}
253
}
254
255
for (int i = 0; i < len * 2; i++) {
256
buffer[i] = bgQueue.pop_front();
257
}
258
return true;
259
}
260
261
private:
262
RIFFReader file_;
263
264
WavData wave_;
265
266
int raw_offset_ = 0;
267
int skip_next_samples_ = 0;
268
FixedSizeQueue<s16, 128 * 1024> bgQueue;
269
short *buffer_ = nullptr;
270
AudioDecoder *decoder_ = nullptr;
271
};
272
273
BackgroundAudio g_BackgroundAudio;
274
275
BackgroundAudio::BackgroundAudio() {
276
buffer_ = new int[BUFSIZE]();
277
sndLoadPending_.store(false);
278
}
279
280
BackgroundAudio::~BackgroundAudio() {
281
delete at3Reader_;
282
delete[] buffer_;
283
}
284
285
void BackgroundAudio::Clear(bool hard) {
286
if (!hard) {
287
fadingOut_ = true;
288
volumeFader_ = 1.0f;
289
return;
290
}
291
if (at3Reader_) {
292
delete at3Reader_;
293
at3Reader_ = nullptr;
294
}
295
playbackOffset_ = 0;
296
sndLoadPending_ = false;
297
}
298
299
void BackgroundAudio::SetGame(const Path &path) {
300
if (path == bgGamePath_) {
301
// Do nothing
302
return;
303
}
304
305
std::lock_guard<std::mutex> lock(mutex_);
306
if (path.empty()) {
307
Clear(false);
308
sndLoadPending_ = false;
309
fadingOut_ = true;
310
} else {
311
Clear(true);
312
gameLastChanged_ = time_now_d();
313
sndLoadPending_ = true;
314
fadingOut_ = false;
315
}
316
volumeFader_ = 1.0f;
317
bgGamePath_ = path;
318
}
319
320
bool BackgroundAudio::Play() {
321
std::lock_guard<std::mutex> lock(mutex_);
322
323
// Immediately stop the sound if it is turned off while playing.
324
if (g_Config.iUIVolume <= 0) {
325
Clear(true);
326
System_AudioClear();
327
return true;
328
}
329
330
double now = time_now_d();
331
int sz = 44100 / 60;
332
if (lastPlaybackTime_ > 0.0 && lastPlaybackTime_ <= now) {
333
sz = (int)((now - lastPlaybackTime_) * 44100);
334
}
335
336
sz = std::min(BUFSIZE / 2, sz);
337
if (at3Reader_) {
338
if (at3Reader_->Read(buffer_, sz)) {
339
if (fadingOut_) {
340
float vol = volumeFader_;
341
// TODO: This isn't optimized. But hardly matters...
342
for (int i = 0; i < sz * 2; i += 2) {
343
const float v = vol;
344
buffer_[i] = (int)((float)buffer_[i] * v);
345
buffer_[i + 1] = (int)((float)buffer_[i + 1] * v);
346
vol += delta_;
347
}
348
volumeFader_ = vol;
349
}
350
}
351
} else {
352
for (int i = 0; i < sz * 2; i += 2) {
353
buffer_[i] = 0;
354
buffer_[i + 1] = 0;
355
}
356
}
357
358
float multiplier = Volume100ToMultiplier(g_Config.iGamePreviewVolume);
359
System_AudioPushSamples(buffer_, sz, multiplier);
360
361
if (at3Reader_ && fadingOut_ && volumeFader_ <= 0.0f) {
362
Clear(true);
363
fadingOut_ = false;
364
gameLastChanged_ = 0;
365
}
366
367
lastPlaybackTime_ = now;
368
369
return true;
370
}
371
372
void BackgroundAudio::Update() {
373
// If there's a game, and some time has passed since the selected game
374
// last changed... (to prevent crazy amount of reads when skipping through a list)
375
if (sndLoadPending_ && (time_now_d() - gameLastChanged_ > 0.5)) {
376
std::lock_guard<std::mutex> lock(mutex_);
377
// Already loaded somehow? Or no game info cache?
378
if (at3Reader_ || !g_gameInfoCache)
379
return;
380
381
// Grab some audio from the current game and play it.
382
std::shared_ptr<GameInfo> gameInfo = g_gameInfoCache->GetInfo(nullptr, bgGamePath_, GameInfoFlags::SND);
383
if (!gameInfo->Ready(GameInfoFlags::SND)) {
384
// Should try again shortly..
385
return;
386
}
387
388
const std::string &data = gameInfo->sndFileData;
389
if (!data.empty()) {
390
at3Reader_ = new AT3PlusReader(data);
391
lastPlaybackTime_ = 0.0;
392
}
393
sndLoadPending_ = false;
394
}
395
}
396
397
inline int16_t ConvertU8ToI16(uint8_t value) {
398
int ivalue = value - 128;
399
return ivalue * 255;
400
}
401
402
Sample *Sample::Load(const std::string &path) {
403
size_t data_size = 0;
404
uint8_t *data = g_VFS.ReadFile(path.c_str(), &data_size);
405
if (!data || data_size > 100000000) {
406
WARN_LOG(Log::Audio, "Failed to load sample '%s'", path.c_str());
407
return nullptr;
408
}
409
410
const char *mp3_magic = "ID3\03";
411
const char *wav_magic = "RIFF";
412
if (!memcmp(data, wav_magic, 4)) {
413
RIFFReader reader(data, (int)data_size);
414
WavData wave;
415
if (!wave.Read(reader)) {
416
delete[] data;
417
return nullptr;
418
}
419
// A wav file.
420
delete[] data;
421
422
if (!wave.IsSimpleWAV()) {
423
ERROR_LOG(Log::Audio, "Wave format not supported for mixer playback. Must be 8-bit or 16-bit raw mono or stereo. '%s'", path.c_str());
424
return nullptr;
425
}
426
427
int16_t *samples = new int16_t[wave.num_channels * wave.numFrames];
428
if (wave.raw_bytes_per_frame == wave.num_channels * 2) {
429
// 16-bit
430
memcpy(samples, wave.raw_data, wave.numFrames * wave.raw_bytes_per_frame);
431
} else if (wave.raw_bytes_per_frame == wave.num_channels) {
432
// 8-bit. Convert.
433
for (int i = 0; i < wave.num_channels * wave.numFrames; i++) {
434
samples[i] = ConvertU8ToI16(wave.raw_data[i]);
435
}
436
}
437
438
// Protect against bad metadata.
439
int actualFrames = std::min(wave.numFrames, wave.raw_data_size / wave.raw_bytes_per_frame);
440
441
return new Sample(samples, wave.num_channels, actualFrames, wave.sample_rate);
442
}
443
444
// Something else.
445
// Let's see if minimp3 can read it.
446
mp3dec_t mp3d;
447
mp3dec_init(&mp3d);
448
mp3dec_file_info_t mp3_info;
449
int retval = mp3dec_load_buf(&mp3d, data, data_size, &mp3_info, nullptr, nullptr);
450
451
if (retval < 0 || mp3_info.samples == 0) {
452
ERROR_LOG(Log::Audio, "Couldn't load MP3 for sound effect from %s", path.c_str());
453
return nullptr;
454
}
455
456
// mp3_info contains the decoded data.
457
int16_t *sample_data = new int16_t[mp3_info.samples];
458
memcpy(sample_data, mp3_info.buffer, mp3_info.samples * sizeof(int16_t));
459
460
Sample *sample = new Sample(sample_data, mp3_info.channels, (int)mp3_info.samples / mp3_info.channels, mp3_info.hz);
461
free(mp3_info.buffer);
462
delete[] data;
463
return sample;
464
}
465
466
static inline int16_t Clamp16(int32_t sample) {
467
if (sample < -32767) return -32767;
468
if (sample > 32767) return 32767;
469
return sample;
470
}
471
472
void SoundEffectMixer::Mix(int16_t *buffer, int sz, int sampleRateHz) {
473
{
474
std::lock_guard<std::mutex> guard(mutex_);
475
if (!queue_.empty()) {
476
for (const auto &entry : queue_) {
477
plays_.push_back(entry);
478
}
479
queue_.clear();
480
}
481
if (plays_.empty()) {
482
return;
483
}
484
}
485
486
for (std::vector<PlayInstance>::iterator iter = plays_.begin(); iter != plays_.end(); ) {
487
auto sample = samples_[(int)iter->sound].get();
488
if (!sample) {
489
// Remove playback instance if sample invalid.
490
iter = plays_.erase(iter);
491
continue;
492
}
493
494
int64_t rateOfSample = sample->rateInHz_;
495
int64_t stride = (rateOfSample << 32) / sampleRateHz;
496
497
for (int i = 0; i < sz * 2; i += 2) {
498
if ((iter->offset >> 32) >= sample->length_ - 2) {
499
iter->done = true;
500
break;
501
}
502
503
int wholeOffset = iter->offset >> 32;
504
int frac = (iter->offset >> 20) & 0xFFF; // Use a 12 bit fraction to get away with 32-bit multiplies
505
506
if (sample->channels_ == 2) {
507
int interpolatedLeft = (sample->data_[wholeOffset * 2] * (0x1000 - frac) + sample->data_[(wholeOffset + 1) * 2] * frac) >> 12;
508
int interpolatedRight = (sample->data_[wholeOffset * 2 + 1] * (0x1000 - frac) + sample->data_[(wholeOffset + 1) * 2 + 1] * frac) >> 12;
509
510
// Clamping add on top per sample. Not great, we should be mixing at higher bitrate instead. Oh well.
511
int left = Clamp16(buffer[i] + (interpolatedLeft * iter->volume >> 8));
512
int right = Clamp16(buffer[i + 1] + (interpolatedRight * iter->volume >> 8));
513
514
buffer[i] = left;
515
buffer[i + 1] = right;
516
} else if (sample->channels_ == 1) {
517
int interpolated = (sample->data_[wholeOffset] * (0x1000 - frac) + sample->data_[wholeOffset + 1] * frac) >> 12;
518
519
// Clamping add on top per sample. Not great, we should be mixing at higher bitrate instead. Oh well.
520
int value = Clamp16(buffer[i] + (interpolated * iter->volume >> 8));
521
522
buffer[i] = value;
523
buffer[i + 1] = value;
524
}
525
526
iter->offset += stride;
527
}
528
529
if (iter->done) {
530
iter = plays_.erase(iter);
531
} else {
532
iter++;
533
}
534
}
535
}
536
537
void SoundEffectMixer::Play(UI::UISound sfx, float multiplier) {
538
std::lock_guard<std::mutex> guard(mutex_);
539
queue_.push_back(PlayInstance{ sfx, 0, (int)(255.0f * multiplier), false });
540
}
541
542
void SoundEffectMixer::UpdateSample(UI::UISound sound, Sample *sample) {
543
if (sample) {
544
std::lock_guard<std::mutex> guard(mutex_);
545
samples_[(size_t)sound] = std::unique_ptr<Sample>(sample);
546
} else {
547
LoadDefaultSample(sound);
548
}
549
}
550
551
void SoundEffectMixer::LoadDefaultSample(UI::UISound sound) {
552
const char *filename = nullptr;
553
554
switch (sound) {
555
case UI::UISound::BACK: filename = "sfx_back.wav"; break;
556
case UI::UISound::SELECT: filename = "sfx_select.wav"; break;
557
case UI::UISound::CONFIRM: filename = "sfx_confirm.wav"; break;
558
case UI::UISound::TOGGLE_ON: filename = "sfx_toggle_on.wav"; break;
559
case UI::UISound::TOGGLE_OFF: filename = "sfx_toggle_off.wav"; break;
560
case UI::UISound::ACHIEVEMENT_UNLOCKED: filename = "sfx_achievement_unlocked.wav"; break;
561
case UI::UISound::LEADERBOARD_SUBMITTED: filename = "sfx_leaderbord_submitted.wav"; break;
562
default:
563
return;
564
}
565
566
Sample *sample = Sample::Load(filename);
567
if (!sample) {
568
ERROR_LOG(Log::Audio, "Failed to load the default sample for UI sound %d", (int)sound);
569
}
570
std::lock_guard<std::mutex> guard(mutex_);
571
samples_[(size_t)sound] = std::unique_ptr<Sample>(sample);
572
}
573
574
class SampleLoadTask : public Task {
575
public:
576
SampleLoadTask(SoundEffectMixer *mixer) : mixer_(mixer) {}
577
TaskType Type() const override { return TaskType::IO_BLOCKING; }
578
TaskPriority Priority() const override {
579
return TaskPriority::NORMAL;
580
}
581
void Run() override {
582
mixer_->LoadSamplesOnThread();
583
}
584
private:
585
SoundEffectMixer *mixer_;
586
};
587
588
void SoundEffectMixer::Init() {
589
samples_.resize((size_t)UI::UISound::COUNT);
590
591
// Setup UI sound callback. For navigation sounds only.
592
UI::SetSoundCallback([](UI::UISound sound) {
593
if (g_Config.bUISound) {
594
float volume = Volume100ToMultiplier(g_Config.iUIVolume);
595
g_BackgroundAudio.SFX().Play(sound, volume);
596
}
597
});
598
599
// Load samples in the background.
600
g_threadManager.EnqueueTask(new SampleLoadTask(this));
601
}
602
603
void SoundEffectMixer::LoadSamplesOnThread() {
604
LoadDefaultSample(UI::UISound::BACK);
605
LoadDefaultSample(UI::UISound::SELECT);
606
LoadDefaultSample(UI::UISound::CONFIRM);
607
LoadDefaultSample(UI::UISound::TOGGLE_ON);
608
LoadDefaultSample(UI::UISound::TOGGLE_OFF);
609
610
if (!g_Config.sAchievementsUnlockAudioFile.empty()) {
611
UpdateSample(UI::UISound::ACHIEVEMENT_UNLOCKED, Sample::Load(g_Config.sAchievementsUnlockAudioFile));
612
} else {
613
LoadDefaultSample(UI::UISound::ACHIEVEMENT_UNLOCKED);
614
}
615
if (!g_Config.sAchievementsLeaderboardSubmitAudioFile.empty()) {
616
UpdateSample(UI::UISound::LEADERBOARD_SUBMITTED, Sample::Load(g_Config.sAchievementsLeaderboardSubmitAudioFile));
617
} else {
618
LoadDefaultSample(UI::UISound::LEADERBOARD_SUBMITTED);
619
}
620
}
621
622