/** * End of job processing */ void Terminate(Option_t*) { fList = dynamic_cast<TList*>(GetOutputData(1)); if (!fList) { AliError(Form("No output list defined (%p)", GetOutputData(1))); if (GetOutputData(1)) GetOutputData(1)->Print(); return; } TList* output = new TList; output->SetName("triggerResults"); output->SetOwner(); fVertexMC = static_cast<TH1D*>(fList->FindObject("vertexMC")); fVertexESD = static_cast<TH1D*>(fList->FindObject("vertexESD")); fM = static_cast<TH1D*>(fList->FindObject("m")); if (fVertexMC) { TH1D* vtxMC = static_cast<TH1D*>(fVertexMC->Clone("vertexMC")); vtxMC->SetDirectory(0); if (vtxMC->GetEntries() > 0) vtxMC->Scale(1. / vtxMC->GetEntries()); else vtxMC->Scale(0); output->Add(vtxMC); } if (fVertexESD) { TH1D* vtxESD = static_cast<TH1D*>(fVertexESD->Clone("vertexESD")); vtxESD->SetDirectory(0); if (vtxESD->GetEntries() > 0) vtxESD->Scale(1. / vtxESD->GetEntries()); else vtxESD->Scale(0); output->Add(vtxESD); } if (fM) { TH1D* m = static_cast<TH1D*>(fM->Clone("m")); m->SetDirectory(0); m->SetYTitle("P(N_{ch}|_{|#eta|<1} < X)"); if (m->GetBinContent(1) > 0) m->Scale(1. / m->GetBinContent(1)); else m->Scale(0); output->Add(m); } TString vtxReq; if (fVertexRequirement & kMC) vtxReq.Append("MC "); if (fVertexRequirement & kESD) vtxReq.Append("ESD "); output->Add(new TNamed("vtxReq", vtxReq.Data())); output->Add(new TNamed("trkReq", fTrackletRequirement == kMC ? "MC" : "ESD")); fInel.Finish(fList, output); fInelGt0.Finish(fList, output); fNSD.Finish(fList, output); fNClusterGt0.Finish(fList, output); PostData(2, output); }
// Load a static copy buffer (g_temppaintbuffer) with the requested number of samples, // with the first sample(s) in the buffer always set up as the last sample(s) of the previous load. // Return a pointer to the head of the copy buffer. // This ensures that interpolating pitch shifters always have the previous sample to reference. // pChannel: sound's channel data // sample_load_request: number of samples to load from source data // pSamplesLoaded: returns the actual number of samples loaded (should always = sample_load_request) // copyBuf: req'd by GetOutputData, used by some Mixers // Returns: NULL ptr to data if no samples available, otherwise always fills remainder of copy buffer with // 0 to pad remainder. // NOTE: DO NOT MODIFY THIS ROUTINE (KELLYB) char *CAudioMixerWave::LoadMixBuffer( channel_t *pChannel, int sample_load_request, int *pSamplesLoaded, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) { int samples_loaded; char *pSample = NULL; char *pData = NULL; int cCopySamps = 0; // save index of last sample loaded (updated in GetOutputData) int sample_loaded_index = m_sample_loaded_index; // get data from source (copyBuf is expected to be available for use) samples_loaded = GetOutputData( (void **)&pData, sample_load_request, copyBuf ); if ( !samples_loaded && sample_load_request ) { // none available, bail out // 360 might not be able to get samples due to latency of loop seek // could also be the valid EOF for non-loops (caller keeps polling for data, until no more) AssertOnce( IsX360() || !m_pData->Source().IsLooped() ); *pSamplesLoaded = 0; return NULL; } int samplesize = GetMixSampleSize(); char *pCopy = (char *)g_temppaintbuffer; if ( IsX360() || IsDebug() ) { // for safety, 360 always validates sample request, due to new xma audio code and possible logic flaws // PC can expect number of requested samples to be within tolerances due to exisiting aged code // otherwise buffer overruns cause hard to track random crashes if ( ( ( sample_load_request + 1 ) * samplesize ) > ( TEMP_COPY_BUFFER_SIZE * sizeof( portable_samplepair_t ) ) ) { // make sure requested samples will fit in temp buffer. // if this assert fails, then pitch is too high (ie: > 2.0) or the sample counters have diverged. // NOTE: to prevent this, pitch should always be capped in MixDataToDevice (but isn't nor are the sample counters). DevWarning( "LoadMixBuffer: sample load request %d exceeds buffer sizes\n", sample_load_request ); Assert( 0 ); *pSamplesLoaded = 0; return NULL; } } // copy all samples from pData to copy buffer, set 0th sample to saved previous sample - this ensures // interpolation pitch shift routines always have a previous sample to reference. // copy previous sample(s) to head of copy buffer pCopy // In some cases, we'll need the previous 2 samples. This occurs when // Rate < 1.0 - in example below, sample 4.86 - 6.48 requires samples 4-7 (previous samples saved are 4 & 5) /* Example: rate = 0.81, sampleCount = 3 (ie: # of samples to return ) _____load 3______ ____load 3_______ __load 2__ 0 1 2 3 4 5 6 7 sample_index (whole samples) ^ ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | | | 0 0.81 1.68 2.43 3.24 4.05 4.86 5.67 6.48 m_fsample_index (rate*sample) _______________ ________________ ________________ ^ ^ ^ ^ | | | | m_sample_loaded_index | | m_sample_loaded_index | | m_fsample_index---- ----m_fsample_index [return 3 samp] [return 3 samp] [return 3 samp] */ pSample = &(pChannel->sample_prev[0]); // determine how many saved samples we need to copy to head of copy buffer (0,1 or 2) // so that pitch interpolation will correctly reference samples. // NOTE: pitch interpolators always reference the sample before and after the indexed sample. // cCopySamps = sample_max_loaded - floor(m_fsample_index); if ( sample_loaded_index < 0 || (floor(m_fsample_index) > sample_loaded_index)) { // no samples previously loaded, or // next sample index is entirely within the next block of samples to be loaded, // so we won't need any samples from the previous block. (can occur when rate > 2.0) cCopySamps = 0; } else if ( m_fsample_index < sample_loaded_index ) { // next sample index is entirely within the previous block of samples loaded, // so we'll need the last 2 samples loaded. (can occur when rate < 1.0) Assert ( ceil(m_fsample_index + 0.00000001) == sample_loaded_index ); cCopySamps = 2; } else { // next sample index is between the next block and the previously loaded block, // so we'll need the last sample loaded. (can occur when 1.0 < rate < 2.0) Assert( floor(m_fsample_index) == sample_loaded_index ); cCopySamps = 1; } Assert( cCopySamps >= 0 && cCopySamps <= 2 ); // point to the sample(s) we are to copy if ( cCopySamps ) { pSample = cCopySamps == 1 ? pSample + samplesize : pSample; Q_memcpy( pCopy, pSample, samplesize * cCopySamps ); pCopy += samplesize * cCopySamps; } // don't overflow copy buffer Assert ( (PAINTBUFFER_MEM_SIZE * sizeof( portable_samplepair_t )) > ( ( samples_loaded + 1 ) * samplesize ) ); // copy loaded samples from pData into pCopy // and update pointer to free space in copy buffer if ( ( samples_loaded * samplesize ) != 0 && !pData ) { char const *pWavName = ""; CSfxTable *source = pChannel->sfx; if ( source ) { pWavName = source->getname(); } Warning( "CAudioMixerWave::LoadMixBuffer: '%s' samples_loaded * samplesize = %i but pData == NULL\n", pWavName, ( samples_loaded * samplesize ) ); *pSamplesLoaded = 0; return NULL; } Q_memcpy( pCopy, pData, samples_loaded * samplesize ); pCopy += samples_loaded * samplesize; // if we loaded fewer samples than we wanted to, and we're not // delaying, load more samples or, if we run out of samples from non-looping source, // pad copy buffer. if ( samples_loaded < sample_load_request ) { // retry loading source data until 0 bytes returned, or we've loaded enough data. // if we hit 0 bytes, fill remaining space in copy buffer with 0 and exit int samples_load_extra; int samples_loaded_retry = -1; for ( int k = 0; (k < 10000 && samples_loaded_retry && samples_loaded < sample_load_request); k++ ) { // how many more samples do we need to satisfy load request samples_load_extra = sample_load_request - samples_loaded; samples_loaded_retry = GetOutputData( (void**)&pData, samples_load_extra, copyBuf ); // copy loaded samples from pData into pCopy if ( samples_loaded_retry ) { if ( ( samples_loaded_retry * samplesize ) != 0 && !pData ) { Warning( "CAudioMixerWave::LoadMixBuffer: samples_loaded_retry * samplesize = %i but pData == NULL\n", ( samples_loaded_retry * samplesize ) ); *pSamplesLoaded = 0; return NULL; } Q_memcpy( pCopy, pData, samples_loaded_retry * samplesize ); pCopy += samples_loaded_retry * samplesize; samples_loaded += samples_loaded_retry; } } } // if we still couldn't load the requested samples, fill rest of copy buffer with 0 if ( samples_loaded < sample_load_request ) { // should always be able to get as many samples as we request from looping sound sources AssertOnce ( IsX360() || !m_pData->Source().IsLooped() ); // these samples are filled with 0, not loaded. // non-looping source hit end of data, fill rest of g_temppaintbuffer with 0 int samples_zero_fill = sample_load_request - samples_loaded; Q_memset( pCopy, 0, samples_zero_fill * samplesize ); pCopy += samples_zero_fill * samplesize; samples_loaded += samples_zero_fill; } if ( samples_loaded >= 2 ) { // always save last 2 samples from copy buffer to channel // (we'll need 0,1 or 2 samples as start of next buffer for interpolation) Assert( sizeof( pChannel->sample_prev ) >= samplesize*2 ); pSample = pCopy - samplesize*2; Q_memcpy( &(pChannel->sample_prev[0]), pSample, samplesize*2 ); } // this routine must always return as many samples loaded (or zeros) as requested. Assert( samples_loaded == sample_load_request ); *pSamplesLoaded = samples_loaded; return (char *)g_temppaintbuffer; }
//----------------------------------------------------------------------------- // Purpose: The device calls this to request data. The mixer must provide the // full amount of samples or have silence in its output stream. // Input : *pDevice - requesting device // sampleCount - number of samples at the output rate // outputRate - sampling rate of the request // Output : Returns true to keep mixing, false to delete this mixer //----------------------------------------------------------------------------- bool CAudioMixerWave::MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int startSample, int sampleCount, int outputRate, bool forward /*= true*/ ) { int offset = 0; int inputSampleRate = (int)(pChannel->pitch * m_pData->Source().SampleRate()); float rate = (float)inputSampleRate / outputRate; fixedint fracstep = FIX_FLOAT( rate ); sampleCount = min( sampleCount, PAINTBUFFER_SIZE ); int startpos = startSample; if ( !forward ) { int requestedstart = startSample - (int)( sampleCount * rate ); if ( requestedstart < 0 ) return false; startpos = max( 0, requestedstart ); SetSamplePosition( startpos ); } while ( sampleCount > 0 ) { int availableSamples; int inputSampleCount; char *pData = NULL; int outputSampleCount = sampleCount; if ( outputRate != inputSampleRate ) { inputSampleCount = (int)(sampleCount * rate); int availableSamples = GetOutputData( (void **)&pData, startpos, inputSampleCount, forward ); if ( !availableSamples ) break; if ( availableSamples < inputSampleCount ) { outputSampleCount = (int)(availableSamples / rate); inputSampleCount = availableSamples; } Mix( pDevice, pChannel, pData, offset, m_fracOffset, fracstep, outputSampleCount, 0, forward ); // compute new fraction part of sample index float offset = (m_fracOffset / FIX_SCALE) + (rate * outputSampleCount); offset = offset - (float)((int)offset); m_fracOffset = FIX_FLOAT(offset); } else { availableSamples = GetOutputData( (void **)&pData, startpos, sampleCount, forward ); if ( !availableSamples ) break; outputSampleCount = availableSamples; inputSampleCount = availableSamples; Mix( pDevice, pChannel, pData, offset, m_fracOffset, FIX(1), outputSampleCount, 0, forward ); } offset += outputSampleCount; sampleCount -= outputSampleCount; if ( forward ) { m_sample += inputSampleCount; m_absoluteSample += inputSampleCount; } if ( m_loop != 0 && m_sample >= m_loop ) { SetSamplePosition( m_startpos ); } } if ( sampleCount > 0 ) return false; return true; }