2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2017-02-27 20:57:46 +00:00
# include <atomic>
# include <mutex>
2013-12-30 09:17:11 +00:00
# include "Common/CommonTypes.h"
2020-08-10 07:12:51 +00:00
# include "Common/Serialize/Serializer.h"
# include "Common/Serialize/SerializeFuncs.h"
2020-10-05 18:58:33 +00:00
# include "Common/Data/Collections/FixedSizeQueue.h"
2013-12-30 09:17:11 +00:00
2014-12-07 21:00:30 +00:00
# ifdef _M_SSE
# include <emmintrin.h>
# endif
2015-01-11 21:13:03 +00:00
# include "Core/Config.h"
2013-12-30 09:17:11 +00:00
# include "Core/CoreTiming.h"
# include "Core/Host.h"
2015-04-06 01:03:50 +00:00
# include "Core/MemMapHelpers.h"
2015-01-11 21:13:03 +00:00
# include "Core/Reporting.h"
2016-09-03 14:06:40 +00:00
# include "Core/System.h"
2016-09-03 22:26:01 +00:00
# ifndef MOBILE_DEVICE
# include "Core/WaveFile.h"
2017-11-13 23:25:49 +00:00
# include "Core/ELF/ParamSFO.h"
# include "Core/HLE/sceKernelTime.h"
# include "StringUtils.h"
2016-09-03 22:26:01 +00:00
# endif
2013-12-30 09:17:11 +00:00
# include "Core/HLE/__sceAudio.h"
# include "Core/HLE/sceAudio.h"
# include "Core/HLE/sceKernel.h"
# include "Core/HLE/sceKernelThread.h"
2015-01-11 14:13:43 +00:00
# include "Core/HW/StereoResampler.h"
2015-01-11 21:50:52 +00:00
# include "Core/Util/AudioFormat.h"
2012-11-01 15:19:01 +00:00
2015-01-11 14:13:43 +00:00
StereoResampler resampler ;
2014-12-07 21:00:30 +00:00
2013-05-19 17:58:06 +00:00
// Should be used to lock anything related to the outAudioQueue.
2013-12-30 09:17:11 +00:00
// atomic locks are used on the lock. TODO: make this lock-free
2017-02-27 20:57:46 +00:00
std : : atomic_flag atomicLock_ ;
2012-11-01 15:19:01 +00:00
2020-08-10 05:03:49 +00:00
// We copy samples as they are written into this simple ring buffer.
// Might try something more efficient later.
FixedSizeQueue < s16 , 32768 * 8 > chanSampleQueues [ PSP_AUDIO_CHANNEL_MAX + 1 ] ;
2012-11-01 15:19:01 +00:00
int eventAudioUpdate = - 1 ;
2016-09-03 14:06:40 +00:00
int eventHostAudioUpdate = - 1 ;
2012-12-17 18:38:04 +00:00
int mixFrequency = 44100 ;
2019-07-01 06:09:46 +00:00
int srcFrequency = 0 ;
2012-12-18 10:40:14 +00:00
2013-07-06 21:23:17 +00:00
const int hwSampleRate = 44100 ;
2013-07-06 22:06:30 +00:00
2013-08-24 00:16:10 +00:00
int hwBlockSize = 64 ;
int hostAttemptBlockSize = 512 ;
2014-07-02 06:08:38 +00:00
static int audioIntervalCycles ;
static int audioHostIntervalCycles ;
2013-07-06 22:06:30 +00:00
2013-08-24 00:16:10 +00:00
static s32 * mixBuffer ;
2016-09-03 22:26:01 +00:00
static s16 * clampedMixBuffer ;
# ifndef MOBILE_DEVICE
2016-09-03 14:06:40 +00:00
WaveFileWriter g_wave_writer ;
2016-09-03 22:26:01 +00:00
static bool m_logAudio ;
# endif
2016-09-03 14:06:40 +00:00
2013-05-19 19:31:47 +00:00
// High and low watermarks, basically. For perfect emulation, the correct values are 0 and 1, respectively.
2013-08-24 09:51:00 +00:00
// TODO: Tweak. Hm, there aren't actually even used currently...
static int chanQueueMaxSizeFactor ;
static int chanQueueMinSizeFactor ;
2012-11-17 13:20:04 +00:00
2014-12-08 09:40:08 +00:00
static void hleAudioUpdate ( u64 userdata , int cyclesLate ) {
2014-07-01 07:25:20 +00:00
// Schedule the next cycle first. __AudioUpdate() may consume cycles.
2014-07-02 06:08:38 +00:00
CoreTiming : : ScheduleEvent ( audioIntervalCycles - cyclesLate , eventAudioUpdate , 0 ) ;
2014-07-01 07:25:20 +00:00
__AudioUpdate ( ) ;
2012-11-17 13:20:04 +00:00
}
2014-12-08 09:40:08 +00:00
static void hleHostAudioUpdate ( u64 userdata , int cyclesLate ) {
2014-07-02 06:08:38 +00:00
CoreTiming : : ScheduleEvent ( audioHostIntervalCycles - cyclesLate , eventHostAudioUpdate , 0 ) ;
2014-07-01 07:25:20 +00:00
2013-06-19 10:00:24 +00:00
// Not all hosts need this call to poke their audio system once in a while, but those that don't
// can just ignore it.
2012-11-17 13:20:04 +00:00
host - > UpdateSound ( ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static void __AudioCPUMHzChange ( ) {
2014-07-02 06:08:38 +00:00
audioIntervalCycles = ( int ) ( usToCycles ( 1000000ULL ) * hwBlockSize / hwSampleRate ) ;
audioHostIntervalCycles = ( int ) ( usToCycles ( 1000000ULL ) * hostAttemptBlockSize / hwSampleRate ) ;
}
2013-08-24 00:16:10 +00:00
void __AudioInit ( ) {
2020-05-16 17:44:33 +00:00
resampler . ResetStatCounters ( ) ;
2012-12-17 18:38:04 +00:00
mixFrequency = 44100 ;
2019-07-01 06:09:46 +00:00
srcFrequency = 0 ;
2012-11-17 13:20:04 +00:00
2020-04-08 06:34:01 +00:00
chanQueueMaxSizeFactor = 2 ;
chanQueueMinSizeFactor = 1 ;
hwBlockSize = 64 ;
hostAttemptBlockSize = 512 ;
2013-08-24 00:16:10 +00:00
2014-07-02 06:08:38 +00:00
__AudioCPUMHzChange ( ) ;
2013-08-24 00:16:10 +00:00
2012-11-01 15:19:01 +00:00
eventAudioUpdate = CoreTiming : : RegisterEvent ( " AudioUpdate " , & hleAudioUpdate ) ;
2012-11-17 13:20:04 +00:00
eventHostAudioUpdate = CoreTiming : : RegisterEvent ( " AudioUpdateHost " , & hleHostAudioUpdate ) ;
2012-11-01 15:19:01 +00:00
2014-07-02 06:08:38 +00:00
CoreTiming : : ScheduleEvent ( audioIntervalCycles , eventAudioUpdate , 0 ) ;
CoreTiming : : ScheduleEvent ( audioHostIntervalCycles , eventHostAudioUpdate , 0 ) ;
2020-08-10 05:03:49 +00:00
for ( u32 i = 0 ; i < PSP_AUDIO_CHANNEL_MAX + 1 ; i + + ) {
chans [ i ] . index = i ;
2012-11-01 15:19:01 +00:00
chans [ i ] . clear ( ) ;
2020-08-10 05:03:49 +00:00
}
2013-08-24 00:16:10 +00:00
mixBuffer = new s32 [ hwBlockSize * 2 ] ;
2016-09-03 22:26:01 +00:00
clampedMixBuffer = new s16 [ hwBlockSize * 2 ] ;
2013-08-24 00:16:10 +00:00
memset ( mixBuffer , 0 , hwBlockSize * 2 * sizeof ( s32 ) ) ;
2013-10-15 06:06:37 +00:00
2015-01-11 14:13:43 +00:00
resampler . Clear ( ) ;
2014-07-02 06:08:38 +00:00
CoreTiming : : RegisterMHzChangeCallback ( & __AudioCPUMHzChange ) ;
2012-11-01 15:19:01 +00:00
}
2014-07-02 15:11:26 +00:00
2013-08-24 09:51:00 +00:00
void __AudioDoState ( PointerWrap & p ) {
2019-07-01 06:09:46 +00:00
auto s = p . Section ( " sceAudio " , 1 , 2 ) ;
2013-09-15 03:23:03 +00:00
if ( ! s )
return ;
2020-08-10 04:20:42 +00:00
Do ( p , eventAudioUpdate ) ;
2012-12-28 08:05:54 +00:00
CoreTiming : : RestoreRegisterEvent ( eventAudioUpdate , " AudioUpdate " , & hleAudioUpdate ) ;
2020-08-10 04:20:42 +00:00
Do ( p , eventHostAudioUpdate ) ;
2013-01-31 06:48:35 +00:00
CoreTiming : : RestoreRegisterEvent ( eventHostAudioUpdate , " AudioUpdateHost " , & hleHostAudioUpdate ) ;
2012-12-28 08:05:54 +00:00
2020-08-10 04:20:42 +00:00
Do ( p , mixFrequency ) ;
2019-07-01 06:09:46 +00:00
if ( s > = 2 ) {
2020-08-10 04:20:42 +00:00
Do ( p , srcFrequency ) ;
2019-07-01 06:09:46 +00:00
} else {
// Assume that it was actually the SRC channel frequency.
srcFrequency = mixFrequency ;
mixFrequency = 44100 ;
}
2013-05-19 17:58:06 +00:00
2018-06-23 04:25:07 +00:00
// TODO: This never happens because maxVer=1.
2015-01-11 14:13:43 +00:00
if ( s > = 2 ) {
resampler . DoState ( p ) ;
} else {
// Only to preserve the previous file format. Might cause a slight audio glitch on upgrades?
FixedSizeQueue < s16 , 512 * 16 > outAudioQueue ;
2013-06-04 21:54:37 +00:00
outAudioQueue . DoState ( p ) ;
2013-10-15 06:06:37 +00:00
2015-01-11 14:13:43 +00:00
resampler . Clear ( ) ;
2013-06-04 21:54:37 +00:00
}
2012-12-28 08:05:54 +00:00
int chanCount = ARRAY_SIZE ( chans ) ;
2020-08-10 04:20:42 +00:00
Do ( p , chanCount ) ;
2012-12-28 08:05:54 +00:00
if ( chanCount ! = ARRAY_SIZE ( chans ) )
{
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEAUDIO , " Savestate failure: different number of audio channels. " ) ;
2019-07-01 06:09:46 +00:00
p . SetError ( p . ERROR_FAILURE ) ;
2012-12-28 08:05:54 +00:00
return ;
}
2020-08-10 05:03:49 +00:00
for ( int i = 0 ; i < chanCount ; + + i ) {
chans [ i ] . index = i ;
2012-12-28 08:05:54 +00:00
chans [ i ] . DoState ( p ) ;
2020-08-10 05:03:49 +00:00
}
2014-07-02 15:11:26 +00:00
__AudioCPUMHzChange ( ) ;
2012-12-28 08:05:54 +00:00
}
2013-08-24 00:16:10 +00:00
void __AudioShutdown ( ) {
delete [ ] mixBuffer ;
2016-09-03 22:26:01 +00:00
delete [ ] clampedMixBuffer ;
2013-10-15 06:06:37 +00:00
2013-08-24 00:16:10 +00:00
mixBuffer = 0 ;
2020-08-10 05:03:49 +00:00
for ( u32 i = 0 ; i < PSP_AUDIO_CHANNEL_MAX + 1 ; i + + ) {
chans [ i ] . index = i ;
2012-11-17 13:20:04 +00:00
chans [ i ] . clear ( ) ;
2020-08-10 05:03:49 +00:00
}
2016-09-03 14:06:40 +00:00
2016-09-03 22:26:01 +00:00
# ifndef MOBILE_DEVICE
2017-02-24 04:36:44 +00:00
if ( g_Config . bDumpAudio ) {
2016-09-03 14:06:40 +00:00
__StopLogAudio ( ) ;
}
2016-09-03 22:26:01 +00:00
# endif
2012-11-01 15:19:01 +00:00
}
2013-08-24 00:16:10 +00:00
u32 __AudioEnqueue ( AudioChannel & chan , int chanNum , bool blocking ) {
2013-05-19 19:31:47 +00:00
u32 ret = chan . sampleCount ;
if ( chan . sampleAddress = = 0 ) {
// For some reason, multichannel audio lies and returns the sample count here.
if ( chanNum = = PSP_AUDIO_CHANNEL_SRC | | chanNum = = PSP_AUDIO_CHANNEL_OUTPUT2 ) {
ret = 0 ;
}
}
// If there's anything on the queue at all, it should be busy, but we try to be a bit lax.
2020-08-10 05:03:49 +00:00
//if (chanSampleQueues[chanNum].size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) {
if ( chanSampleQueues [ chanNum ] . size ( ) > 0 ) {
2012-11-01 15:19:01 +00:00
if ( blocking ) {
2013-05-19 19:31:47 +00:00
// TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync?
2020-08-10 05:03:49 +00:00
int blockSamples = ( int ) chanSampleQueues [ chanNum ] . size ( ) / 2 / chanQueueMinSizeFactor ;
2013-05-19 19:31:47 +00:00
2013-08-28 15:13:44 +00:00
if ( __KernelIsDispatchEnabled ( ) ) {
AudioChannelWaitInfo waitInfo = { __KernelGetCurThread ( ) , blockSamples } ;
chan . waitingThreads . push_back ( waitInfo ) ;
// Also remember the value to return in the waitValue.
__KernelWaitCurThread ( WAITTYPE_AUDIOCHANNEL , ( SceUID ) chanNum + 1 , ret , 0 , false , " blocking audio " ) ;
} else {
// TODO: Maybe we shouldn't take this audio after all?
ret = SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
}
2013-05-19 19:31:47 +00:00
2012-12-17 19:15:23 +00:00
// Fall through to the sample queueing, don't want to lose the samples even though
2013-05-19 19:31:47 +00:00
// we're getting full. The PSP would enqueue after blocking.
} else {
// Non-blocking doesn't even enqueue, but it's not commonly used.
2012-11-01 15:19:01 +00:00
return SCE_ERROR_AUDIO_CHANNEL_BUSY ;
}
}
2013-05-19 19:31:47 +00:00
if ( chan . sampleAddress = = 0 ) {
return ret ;
}
2013-10-28 14:16:37 +00:00
int leftVol = chan . leftVolume ;
int rightVol = chan . rightVolume ;
2013-11-07 10:49:40 +00:00
if ( leftVol = = ( 1 < < 15 ) & & rightVol = = ( 1 < < 15 ) & & chan . format = = PSP_AUDIO_FORMAT_STEREO & & IS_LITTLE_ENDIAN ) {
// TODO: Add mono->stereo conversion to this path.
2013-10-29 15:24:55 +00:00
// Good news: the volume doesn't affect the values at all.
// We can just do a direct memory copy.
const u32 totalSamples = chan . sampleCount * ( chan . format = = PSP_AUDIO_FORMAT_STEREO ? 2 : 1 ) ;
s16 * buf1 = 0 , * buf2 = 0 ;
size_t sz1 , sz2 ;
2020-08-10 05:03:49 +00:00
chanSampleQueues [ chanNum ] . pushPointers ( totalSamples , & buf1 , & sz1 , & buf2 , & sz2 ) ;
2013-10-29 15:24:55 +00:00
if ( Memory : : IsValidAddress ( chan . sampleAddress + ( totalSamples - 1 ) * sizeof ( s16_le ) ) ) {
Memory : : Memcpy ( buf1 , chan . sampleAddress , ( u32 ) sz1 * sizeof ( s16 ) ) ;
if ( buf2 )
Memory : : Memcpy ( buf2 , chan . sampleAddress + ( u32 ) sz1 * sizeof ( s16 ) , ( u32 ) sz2 * sizeof ( s16 ) ) ;
}
} else {
// Remember that maximum volume allowed is 0xFFFFF so left shift is no issue.
// This way we can optimally shift by 16.
leftVol < < = 1 ;
rightVol < < = 1 ;
if ( chan . format = = PSP_AUDIO_FORMAT_STEREO ) {
const u32 totalSamples = chan . sampleCount * 2 ;
2013-01-27 21:40:57 +00:00
2013-10-29 15:24:55 +00:00
s16_le * sampleData = ( s16_le * ) Memory : : GetPointer ( chan . sampleAddress ) ;
2013-01-27 21:40:57 +00:00
// Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr.
2013-10-29 15:24:55 +00:00
if ( Memory : : IsValidAddress ( chan . sampleAddress + ( totalSamples - 1 ) * sizeof ( s16_le ) ) ) {
2013-06-19 10:00:24 +00:00
s16 * buf1 = 0 , * buf2 = 0 ;
size_t sz1 , sz2 ;
2020-08-10 05:03:49 +00:00
chanSampleQueues [ chanNum ] . pushPointers ( totalSamples , & buf1 , & sz1 , & buf2 , & sz2 ) ;
2014-12-07 20:14:09 +00:00
AdjustVolumeBlock ( buf1 , sampleData , sz1 , leftVol , rightVol ) ;
2013-06-19 10:00:24 +00:00
if ( buf2 ) {
2014-12-07 20:14:09 +00:00
AdjustVolumeBlock ( buf2 , sampleData + sz1 , sz2 , leftVol , rightVol ) ;
2013-06-19 10:00:24 +00:00
}
2013-01-27 21:40:57 +00:00
}
2013-10-29 15:24:55 +00:00
} else if ( chan . format = = PSP_AUDIO_FORMAT_MONO ) {
2014-12-07 20:14:09 +00:00
// Rare, so unoptimized. Expands to stereo.
2013-10-29 15:24:55 +00:00
for ( u32 i = 0 ; i < chan . sampleCount ; i + + ) {
s16 sample = ( s16 ) Memory : : Read_U16 ( chan . sampleAddress + 2 * i ) ;
2020-08-10 05:03:49 +00:00
chanSampleQueues [ chanNum ] . push ( ApplySampleVolume ( sample , leftVol ) ) ;
chanSampleQueues [ chanNum ] . push ( ApplySampleVolume ( sample , rightVol ) ) ;
2013-06-05 08:16:53 +00:00
}
2012-11-01 15:19:01 +00:00
}
}
2013-02-01 16:07:58 +00:00
return ret ;
2012-11-01 15:19:01 +00:00
}
2013-08-24 09:51:00 +00:00
inline void __AudioWakeThreads ( AudioChannel & chan , int result , int step ) {
2013-05-19 19:31:47 +00:00
u32 error ;
2014-07-01 15:02:34 +00:00
bool wokeThreads = false ;
2013-08-24 09:51:00 +00:00
for ( size_t w = 0 ; w < chan . waitingThreads . size ( ) ; + + w ) {
2013-05-19 19:31:47 +00:00
AudioChannelWaitInfo & waitInfo = chan . waitingThreads [ w ] ;
2013-06-05 19:13:52 +00:00
waitInfo . numSamples - = step ;
2013-05-19 19:31:47 +00:00
// If it's done (there will still be samples on queue) and actually still waiting, wake it up.
2013-06-09 06:42:59 +00:00
u32 waitID = __KernelGetWaitID ( waitInfo . threadID , WAITTYPE_AUDIOCHANNEL , error ) ;
2013-08-24 09:51:00 +00:00
if ( waitInfo . numSamples < = 0 & & waitID ! = 0 ) {
2013-09-07 19:19:21 +00:00
// DEBUG_LOG(SCEAUDIO, "Woke thread %i for some buffer filling", waitingThread);
2013-06-05 19:20:07 +00:00
u32 ret = result = = 0 ? __KernelGetWaitValue ( waitInfo . threadID , error ) : SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED ;
2013-05-19 19:31:47 +00:00
__KernelResumeThreadFromWait ( waitInfo . threadID , ret ) ;
2014-07-01 15:02:34 +00:00
wokeThreads = true ;
2013-05-19 19:31:47 +00:00
chan . waitingThreads . erase ( chan . waitingThreads . begin ( ) + w - - ) ;
}
2013-06-09 06:42:59 +00:00
// This means the thread stopped waiting, so stop trying to wake it.
else if ( waitID = = 0 )
chan . waitingThreads . erase ( chan . waitingThreads . begin ( ) + w - - ) ;
2013-05-19 19:31:47 +00:00
}
2014-07-01 15:02:34 +00:00
if ( wokeThreads ) {
__KernelReSchedule ( " audio drain " ) ;
}
2013-05-19 19:31:47 +00:00
}
2013-08-24 09:51:00 +00:00
void __AudioWakeThreads ( AudioChannel & chan , int result ) {
2013-06-05 19:20:07 +00:00
__AudioWakeThreads ( chan , result , 0x7FFFFFFF ) ;
2013-05-19 19:31:47 +00:00
}
2013-08-24 09:51:00 +00:00
void __AudioSetOutputFrequency ( int freq ) {
2015-01-11 21:13:03 +00:00
if ( freq ! = 44100 ) {
WARN_LOG_REPORT ( SCEAUDIO , " Switching audio frequency to %i " , freq ) ;
} else {
DEBUG_LOG ( SCEAUDIO , " Switching audio frequency to %i " , freq ) ;
}
2013-06-19 10:00:24 +00:00
mixFrequency = freq ;
}
2019-07-01 06:09:46 +00:00
void __AudioSetSRCFrequency ( int freq ) {
srcFrequency = freq ;
}
2012-11-17 13:20:04 +00:00
// Mix samples from the various audio channels into a single sample queue.
// This single sample queue is where __AudioMix should read from. If the sample queue is full, we should
// just sleep the main emulator thread a little.
2017-11-14 04:12:27 +00:00
void __AudioUpdate ( bool resetRecording ) {
2012-11-23 09:33:19 +00:00
// Audio throttle doesn't really work on the PSP since the mixing intervals are so closely tied
// to the CPU. Much better to throttle the frame rate on frame display and just throw away audio
// if the buffer somehow gets full.
2013-06-19 10:00:24 +00:00
bool firstChannel = true ;
2019-07-01 06:44:18 +00:00
std : : vector < int16_t > srcBuffer ;
2012-11-01 15:19:01 +00:00
2013-06-19 10:00:24 +00:00
for ( u32 i = 0 ; i < PSP_AUDIO_CHANNEL_MAX + 1 ; i + + ) {
2012-11-17 13:20:04 +00:00
if ( ! chans [ i ] . reserved )
continue ;
2013-06-19 10:00:24 +00:00
2013-06-05 19:20:07 +00:00
__AudioWakeThreads ( chans [ i ] , 0 , hwBlockSize ) ;
2013-05-19 19:31:47 +00:00
2020-08-10 05:03:49 +00:00
if ( ! chanSampleQueues [ i ] . size ( ) ) {
2012-11-17 13:20:04 +00:00
continue ;
}
2019-07-01 06:44:18 +00:00
bool needsResample = i = = PSP_AUDIO_CHANNEL_SRC & & srcFrequency ! = 0 & & srcFrequency ! = mixFrequency ;
size_t sz = needsResample ? ( hwBlockSize * 2 * srcFrequency ) / mixFrequency : hwBlockSize * 2 ;
2020-08-10 05:03:49 +00:00
if ( sz > chanSampleQueues [ i ] . size ( ) ) {
ERROR_LOG ( SCEAUDIO , " Channel %i buffer underrun at %i of %i " , i , ( int ) chanSampleQueues [ i ] . size ( ) / 2 , ( int ) sz / 2 ) ;
2013-06-19 10:00:24 +00:00
}
const s16 * buf1 = 0 , * buf2 = 0 ;
size_t sz1 , sz2 ;
2020-08-10 05:03:49 +00:00
chanSampleQueues [ i ] . popPointers ( sz , & buf1 , & sz1 , & buf2 , & sz2 ) ;
2019-07-01 06:44:18 +00:00
if ( needsResample ) {
auto read = [ & ] ( size_t i ) {
if ( i < sz1 )
return buf1 [ i ] ;
if ( i < sz1 + sz2 )
return buf2 [ i - sz1 ] ;
if ( buf2 )
return buf2 [ sz2 - 1 ] ;
return buf1 [ sz1 - 1 ] ;
} ;
srcBuffer . resize ( hwBlockSize * 2 ) ;
// TODO: This is terrible, since it's doing it by small chunk and discarding frac.
const uint32_t ratio = ( uint32_t ) ( 65536.0 * srcFrequency / ( double ) mixFrequency ) ;
uint32_t frac = 0 ;
size_t readIndex = 0 ;
for ( size_t outIndex = 0 ; readIndex < sz & & outIndex < srcBuffer . size ( ) ; outIndex + = 2 ) {
size_t readIndex2 = readIndex + 2 ;
int16_t l1 = read ( readIndex ) ;
int16_t r1 = read ( readIndex + 1 ) ;
int16_t l2 = read ( readIndex2 ) ;
int16_t r2 = read ( readIndex2 + 1 ) ;
int sampleL = ( ( l1 < < 16 ) + ( l2 - l1 ) * ( uint16_t ) frac ) > > 16 ;
int sampleR = ( ( r1 < < 16 ) + ( r2 - r1 ) * ( uint16_t ) frac ) > > 16 ;
srcBuffer [ outIndex ] = sampleL ;
srcBuffer [ outIndex + 1 ] = sampleR ;
frac + = ratio ;
readIndex + = 2 * ( uint16_t ) ( frac > > 16 ) ;
frac & = 0xffff ;
}
buf1 = srcBuffer . data ( ) ;
sz1 = srcBuffer . size ( ) ;
buf2 = nullptr ;
sz2 = 0 ;
}
2013-06-19 10:00:24 +00:00
if ( firstChannel ) {
2013-06-25 13:54:42 +00:00
for ( size_t s = 0 ; s < sz1 ; s + + )
2013-06-19 10:00:24 +00:00
mixBuffer [ s ] = buf1 [ s ] ;
if ( buf2 ) {
2013-06-25 13:54:42 +00:00
for ( size_t s = 0 ; s < sz2 ; s + + )
2013-06-19 10:00:24 +00:00
mixBuffer [ s + sz1 ] = buf2 [ s ] ;
}
firstChannel = false ;
} else {
2014-12-07 19:23:47 +00:00
// Surprisingly hard to SIMD efficiently on SSE2 due to lack of 16-to-32-bit sign extension. NEON should be straight-forward though, and SSE4.1 can do it nicely.
2015-01-26 23:12:40 +00:00
// Actually, the cmple/pack trick should work fine...
2013-06-25 13:54:42 +00:00
for ( size_t s = 0 ; s < sz1 ; s + + )
2013-06-19 10:00:24 +00:00
mixBuffer [ s ] + = buf1 [ s ] ;
if ( buf2 ) {
2013-06-25 13:54:42 +00:00
for ( size_t s = 0 ; s < sz2 ; s + + )
2013-06-19 10:00:24 +00:00
mixBuffer [ s + sz1 ] + = buf2 [ s ] ;
2012-11-17 13:20:04 +00:00
}
}
}
2013-06-19 10:00:24 +00:00
if ( firstChannel ) {
2014-12-07 19:23:47 +00:00
// Nothing was written above, let's memset.
2013-08-27 04:06:26 +00:00
memset ( mixBuffer , 0 , hwBlockSize * 2 * sizeof ( s32 ) ) ;
2013-06-19 10:00:24 +00:00
}
2012-12-18 10:40:14 +00:00
if ( g_Config . bEnableSound ) {
2015-01-11 14:13:43 +00:00
resampler . PushSamples ( mixBuffer , hwBlockSize ) ;
2016-09-03 22:26:01 +00:00
# ifndef MOBILE_DEVICE
2017-11-14 04:12:27 +00:00
if ( g_Config . bSaveLoadResetsAVdumping & & resetRecording ) {
__StopLogAudio ( ) ;
std : : string discID = g_paramSFO . GetDiscID ( ) ;
2017-11-14 08:46:31 +00:00
std : : string audio_file_name = StringFromFormat ( " %s%s_%s.wav " , GetSysDirectory ( DIRECTORY_AUDIO ) . c_str ( ) , discID . c_str ( ) , KernelTimeNowFormatted ( ) . c_str ( ) ) . c_str ( ) ;
2017-11-14 04:12:27 +00:00
INFO_LOG ( COMMON , " Restarted audio recording to: %s " , audio_file_name . c_str ( ) ) ;
if ( ! File : : Exists ( GetSysDirectory ( DIRECTORY_AUDIO ) ) )
File : : CreateDir ( GetSysDirectory ( DIRECTORY_AUDIO ) ) ;
File : : CreateEmptyFile ( audio_file_name ) ;
__StartLogAudio ( audio_file_name ) ;
}
2017-02-24 04:36:44 +00:00
if ( ! m_logAudio ) {
if ( g_Config . bDumpAudio ) {
2017-11-13 23:25:49 +00:00
// Use gameID_EmulatedTimestamp for filename
std : : string discID = g_paramSFO . GetDiscID ( ) ;
2017-11-14 08:46:31 +00:00
std : : string audio_file_name = StringFromFormat ( " %s%s_%s.wav " , GetSysDirectory ( DIRECTORY_AUDIO ) . c_str ( ) , discID . c_str ( ) , KernelTimeNowFormatted ( ) . c_str ( ) ) . c_str ( ) ;
2017-11-13 23:25:49 +00:00
INFO_LOG ( COMMON , " Recording audio to: %s " , audio_file_name . c_str ( ) ) ;
2017-02-24 04:36:44 +00:00
// Create the path just in case it doesn't exist
2017-11-13 23:25:49 +00:00
if ( ! File : : Exists ( GetSysDirectory ( DIRECTORY_AUDIO ) ) )
File : : CreateDir ( GetSysDirectory ( DIRECTORY_AUDIO ) ) ;
2017-02-24 04:36:44 +00:00
File : : CreateEmptyFile ( audio_file_name ) ;
__StartLogAudio ( audio_file_name ) ;
}
} else {
if ( g_Config . bDumpAudio ) {
for ( int i = 0 ; i < hwBlockSize * 2 ; i + + ) {
clampedMixBuffer [ i ] = clamp_s16 ( mixBuffer [ i ] ) ;
}
g_wave_writer . AddStereoSamples ( clampedMixBuffer , hwBlockSize ) ;
} else {
__StopLogAudio ( ) ;
2016-09-03 14:06:40 +00:00
}
}
2016-09-03 22:26:01 +00:00
# endif
2012-11-17 13:20:04 +00:00
}
}
// numFrames is number of stereo frames.
2013-05-19 17:58:06 +00:00
// This is called from *outside* the emulator thread.
2015-01-11 14:13:43 +00:00
int __AudioMix ( short * outstereo , int numFrames , int sampleRate ) {
2017-02-28 00:47:13 +00:00
return resampler . Mix ( outstereo , numFrames , false , sampleRate ) ;
2013-10-15 06:06:37 +00:00
}
2015-01-29 11:55:49 +00:00
2020-05-16 17:44:33 +00:00
void __AudioGetDebugStats ( char * buf , size_t bufSize ) {
resampler . GetAudioDebugStats ( buf , bufSize ) ;
2015-01-29 13:58:03 +00:00
}
void __PushExternalAudio ( const s32 * audio , int numSamples ) {
if ( audio ) {
resampler . PushSamples ( audio , numSamples ) ;
} else {
resampler . Clear ( ) ;
}
2016-02-10 11:39:26 +00:00
}
2016-09-03 22:26:01 +00:00
# ifndef MOBILE_DEVICE
2017-02-24 04:36:44 +00:00
void __StartLogAudio ( const std : : string & filename ) {
if ( ! m_logAudio ) {
2016-09-03 14:06:40 +00:00
m_logAudio = true ;
g_wave_writer . Start ( filename , 44100 ) ;
g_wave_writer . SetSkipSilence ( false ) ;
NOTICE_LOG ( SCEAUDIO , " Starting Audio logging " ) ;
2017-02-24 04:36:44 +00:00
} else {
2016-09-03 14:06:40 +00:00
WARN_LOG ( SCEAUDIO , " Audio logging has already been started " ) ;
}
}
2017-02-24 04:36:44 +00:00
void __StopLogAudio ( ) {
if ( m_logAudio ) {
2016-09-03 14:06:40 +00:00
m_logAudio = false ;
g_wave_writer . Stop ( ) ;
NOTICE_LOG ( SCEAUDIO , " Stopping Audio logging " ) ;
2017-02-24 04:36:44 +00:00
} else {
2016-09-03 14:06:40 +00:00
WARN_LOG ( SCEAUDIO , " Audio logging has already been stopped " ) ;
}
}
2016-09-03 22:26:01 +00:00
# endif
2017-11-14 04:12:27 +00:00
void WAVDump : : Reset ( ) {
__AudioUpdate ( true ) ;
}