void SndBuffer::_WriteSamples(StereoOut32 *bData, int nSamples) { m_predictData = 0; // Problem: // If the SPU2 gets out of sync with the SndOut device, the writepos of the // circular buffer will overtake the readpos, leading to a prolonged period // of hopscotching read/write accesses (ie, lots of staticy crap sound for // several seconds). // // Compromise: // When an overrun occurs, we adapt by discarding a portion of the buffer. // The older portion of the buffer is discarded rather than incoming data, // so that the overall audio synchronization is better. int free = m_size - _GetApproximateDataInBuffer(); // -1, but the <= handles that if( free <= nSamples ) { // Disabled since the lock-free queue can't handle changing the read end from the write thread #if 0 // Buffer overrun! // Dump samples from the read portion of the buffer instead of dropping // the newly written stuff. s32 comp; if( SynchMode == 0 ) // TimeStrech on { comp = timeStretchOverrun(); } else { // Toss half the buffer plus whatever's being written anew: comp = GetAlignedBufferSize( (m_size + nSamples ) / 16 ); if( comp > (m_size-SndOutPacketSize) ) comp = m_size-SndOutPacketSize; } _DropSamples_Internal(comp); if( MsgOverruns() ) ConLog(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize ); lastPct = 0.0; // normalize the timestretcher #else if( MsgOverruns() ) ConLog(" * SPU2 > Overrun! 1 packet tossed)\n"); lastPct = 0.0; // normalize the timestretcher return; #endif } _WriteSamples_Safe(bData, nSamples); }
void SndBuffer::Init() { //if( mods[OutputModule] == NULL ) //{ // _InitFail(); // return; //} // initialize sound buffer // Buffer actually attempts to run ~50%, so allocate near double what // the requested latency is: m_rpos = 0; m_wpos = 0; m_data = 0; try { const float latencyMS = SndOutLatencyMS * (timeStretchDisabled ? 1.5f : 2.0f ); m_size = GetAlignedBufferSize( (int)(latencyMS * SampleRate / 1000.0f ) ); m_buffer = new StereoOut32[m_size]; m_underrun_freeze = false; sndTempBuffer = new StereoOut32[SndOutPacketSize]; sndTempBuffer16 = new StereoOut16[SndOutPacketSize]; } catch( std::bad_alloc& ) { // out of memory exception (most likely) printf( "Out of memory error occurred while initializing SPU2." ); _InitFail(); return; } // clear buffers! // Fixes loopy sounds on emu resets. memset( sndTempBuffer, 0, sizeof(StereoOut32) * SndOutPacketSize ); memset( sndTempBuffer16, 0, sizeof(StereoOut16) * SndOutPacketSize ); sndTempProgress = 0; soundtouchInit(); // initializes the timestretching // some crap //spdif_set51(mods[OutputModule]->Is51Out()); // initialize module //if( mods[OutputModule]->Init() == -1 ) _InitFail(); }
// Returns TRUE if there is data to be output, or false if no data // is available to be copied. bool SndBuffer::CheckUnderrunStatus( int& nSamples, int& quietSampleCount ) { quietSampleCount = 0; int data = _GetApproximateDataInBuffer(); if( m_underrun_freeze ) { int toFill = m_size / ( (SynchMode == 2) ? 32 : 400); // TimeStretch and Async off? toFill = GetAlignedBufferSize( toFill ); // toFill is now aligned to a SndOutPacket if( data < toFill ) { quietSampleCount = nSamples; return false; } m_underrun_freeze = false; if( MsgOverruns() ) ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize ); lastPct = 0.0; // normalize timestretcher } else if( data < nSamples ) { nSamples = data; quietSampleCount = SndOutPacketSize - data; m_underrun_freeze = true; if( SynchMode == 0 ) // TimeStrech on timeStretchUnderrun(); return nSamples != 0; } return true; }
// Returns TRUE if there is data to be output, or false if no data // is available to be copied. bool SndBuffer::CheckUnderrunStatus( int& nSamples, int& quietSampleCount ) { quietSampleCount = 0; if( m_underrun_freeze ) { int toFill = (int)(m_size * ( timeStretchDisabled ? 0.50f : 0.1f ) ); toFill = GetAlignedBufferSize( toFill ); // toFill is now aligned to a SndOutPacket if( m_data < toFill ) { quietSampleCount = nSamples; return false; } m_underrun_freeze = false; //TODO //if( MsgOverruns() ) printf(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize ); lastPct = 0.0; // normalize timestretcher } else if( m_data < nSamples ) { nSamples = m_data; quietSampleCount = SndOutPacketSize - m_data; m_underrun_freeze = true; if( !timeStretchDisabled ) timeStretchUnderrun(); return nSamples != 0; } return true; }
void SndBuffer::_WriteSamples(StereoOut32 *bData, int nSamples) { int free = m_size-m_data; m_predictData = 0; assert( m_data <= m_size ); // Problem: // If the SPU2 gets out of sync with the SndOut device, the writepos of the // circular buffer will overtake the readpos, leading to a prolonged period // of hopscotching read/write accesses (ie, lots of staticy crap sound for // several seconds). // // Compromise: // When an overrun occurs, we adapt by discarding a portion of the buffer. // The older portion of the buffer is discarded rather than incoming data, // so that the overall audio synchronization is better. if( free < nSamples ) { // Buffer overrun! // Dump samples from the read portion of the buffer instead of dropping // the newly written stuff. s32 comp; if( !timeStretchDisabled ) { comp = timeStretchOverrun(); } else { // Toss half the buffer plus whatever's being written anew: comp = GetAlignedBufferSize( (m_size + nSamples ) / 2 ); if( comp > (m_size-SndOutPacketSize) ) comp = m_size-SndOutPacketSize; } m_data -= comp; m_rpos = (m_rpos+comp) % m_size; //TODO //if( MsgOverruns() ) printf(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize ); lastPct = 0.0; // normalize the timestretcher } // copy in two phases, since there's a chance the packet // wraps around the buffer (it'd be nice to deal in packets only, but // the timestretcher and DSP options require flexibility). const int endPos = m_wpos + nSamples; const int secondCopyLen = endPos - m_size; StereoOut32* wposbuffer = &m_buffer[m_wpos]; m_data += nSamples; if( secondCopyLen > 0 ) { nSamples -= secondCopyLen; memcpy( m_buffer, &bData[nSamples], secondCopyLen * sizeof( *bData ) ); m_wpos = secondCopyLen; } else m_wpos += nSamples; memcpy( wposbuffer, bData, nSamples * sizeof( *bData ) ); }