SINT ReadAheadManager::getNextSamples(double dRate, CSAMPLE* pOutput, SINT requested_samples) { // TODO(XXX): Remove implicit assumption of 2 channels if (!even(requested_samples)) { qDebug() << "ERROR: Non-even requested_samples to ReadAheadManager::getNextSamples"; requested_samples--; } bool in_reverse = dRate < 0; //qDebug() << "start" << start_sample << requested_samples; // A loop will only limit the amount we can read in one shot. const double loop_trigger = m_pLoopingControl->nextTrigger( dRate, m_currentPosition, 0, 0); bool loop_active = loop_trigger != kNoTrigger; SINT preloop_samples = 0; double samplesToLoopTrigger = 0.0; SINT samples_from_reader = requested_samples; if (loop_active) { samplesToLoopTrigger = in_reverse ? m_currentPosition - loop_trigger : loop_trigger - m_currentPosition; if (samplesToLoopTrigger < 0) { // We have already passed the loop trigger samples_from_reader = 0; } else { // We can only read whole frames from the reader. // Use ceil here, to be sure to reach the loop trigger. preloop_samples = SampleUtil::ceilPlayPosToFrameStart(samplesToLoopTrigger, kNumChannels); // clamp requested samples from the caller to the loop trigger point samples_from_reader = math_clamp(requested_samples, static_cast<SINT>(0), preloop_samples); } } // Sanity checks. if (samples_from_reader < 0) { qDebug() << "Need negative samples in ReadAheadManager::getNextSamples. Ignoring read"; return 0; } SINT start_sample = SampleUtil::roundPlayPosToFrameStart( m_currentPosition, kNumChannels); SINT samples_read = m_pReader->read( start_sample, samples_from_reader, in_reverse, pOutput); if (samples_read != samples_from_reader) { qDebug() << "didn't get what we wanted" << samples_read << samples_from_reader; } // Increment or decrement current read-ahead position // Mixing int and double here is desired, because the fractional frame should // be resist if (in_reverse) { addReadLogEntry(m_currentPosition, m_currentPosition - samples_read); m_currentPosition -= samples_read; } else { addReadLogEntry(m_currentPosition, m_currentPosition + samples_read); m_currentPosition += samples_read; } // Activate on this trigger if necessary if (loop_active) { // LoopingControl makes the decision about whether we should loop or // not. const double loop_target = m_pLoopingControl->process( dRate, m_currentPosition, 0, 0); if (loop_target != kNoTrigger) { m_currentPosition = loop_target; if (preloop_samples > 0) { // we are up to one frame ahead of the loop trigger double overshoot = preloop_samples - samplesToLoopTrigger; // start the loop later accordingly to be sure the loop length is as desired // e.g. exactly one bar. m_currentPosition += overshoot; // Example in frames; // loop start 1.1 loop end 3.3 loop length 2.2 // m_currentPosition samplesToLoopTrigger preloop_samples // 2.0 1.3 2 // 1.8 1.5 2 // 1.6 1.7 2 // 1.4 1.9 2 // 1.2 2.1 3 // Average preloop_samples = 2.2 } // start reading before the loop start point, to crossfade these samples // with the samples we need to the loop end int loop_read_position = SampleUtil::roundPlayPosToFrameStart( m_currentPosition + (in_reverse ? preloop_samples : -preloop_samples), kNumChannels); int looping_samples_read = m_pReader->read( loop_read_position, samples_read, in_reverse, m_pCrossFadeBuffer); if (looping_samples_read != samples_read) { qDebug() << "ERROR: Couldn't get all needed samples for crossfade."; } // do crossfade from the current buffer into the new loop beginning if (samples_read != 0) { // avoid division by zero SampleUtil::linearCrossfadeBuffers(pOutput, pOutput, m_pCrossFadeBuffer, samples_read); } } } //qDebug() << "read" << m_currentPosition << samples_read; return samples_read; }
int ReadAheadManager::getNextSamples(double dRate, CSAMPLE* buffer, int requested_samples) { if (!even(requested_samples)) { qDebug() << "ERROR: Non-even requested_samples to ReadAheadManager::getNextSamples"; requested_samples--; } bool in_reverse = dRate < 0; int start_sample = m_iCurrentPosition; //qDebug() << "start" << start_sample << requested_samples; int samples_needed = requested_samples; CSAMPLE* base_buffer = buffer; // A loop will only limit the amount we can read in one shot. const double loop_trigger = m_sEngineControls[0]->nextTrigger( dRate, m_iCurrentPosition, 0, 0); bool loop_active = loop_trigger != kNoTrigger; int preloop_samples = 0; if (loop_active) { int samples_available = in_reverse ? m_iCurrentPosition - loop_trigger : loop_trigger - m_iCurrentPosition; preloop_samples = samples_available; samples_needed = math_max(0, math_min(samples_needed, samples_available)); } if (in_reverse) { start_sample = m_iCurrentPosition - samples_needed; } // Sanity checks. if (samples_needed < 0) { qDebug() << "Need negative samples in ReadAheadManager::getNextSamples. Ignoring read"; return 0; } int samples_read = m_pReader->read(start_sample, samples_needed, base_buffer); if (samples_read != samples_needed) { qDebug() << "didn't get what we wanted" << samples_read << samples_needed; } // Increment or decrement current read-ahead position if (in_reverse) { addReadLogEntry(m_iCurrentPosition, m_iCurrentPosition - samples_read); m_iCurrentPosition -= samples_read; } else { addReadLogEntry(m_iCurrentPosition, m_iCurrentPosition + samples_read); m_iCurrentPosition += samples_read; } // Activate on this trigger if necessary if (loop_active) { // LoopingControl makes the decision about whether we should loop or // not. const double loop_target = m_sEngineControls[0]-> process(dRate, m_iCurrentPosition, 0, 0); if (loop_target != kNoTrigger) { m_iCurrentPosition = loop_target; int loop_read_position = m_iCurrentPosition + (in_reverse ? preloop_samples : -preloop_samples); int looping_samples_read = m_pReader->read( loop_read_position, samples_read, m_pCrossFadeBuffer); if (looping_samples_read != samples_read) { qDebug() << "ERROR: Couldn't get all needed samples for crossfade."; } // do crossfade from the current buffer into the new loop beginning double mix_amount = 0.0; double mix_inc = 2.0 / static_cast<double>(samples_read); for (int i = 0; i < samples_read; i += 2) { base_buffer[i] = base_buffer[i] * (1.0 - mix_amount) + m_pCrossFadeBuffer[i] * mix_amount; base_buffer[i+1] = base_buffer[i+1] * (1.0 - mix_amount) + m_pCrossFadeBuffer[i+1] * mix_amount; mix_amount += mix_inc; } } } // Reverse the samples in-place if (in_reverse) { // TODO(rryan) pull this into MixxxUtil or something CSAMPLE temp1, temp2; for (int j = 0; j < samples_read/2; j += 2) { const int endpos = samples_read-1-j-1; temp1 = base_buffer[j]; temp2 = base_buffer[j+1]; base_buffer[j] = base_buffer[endpos]; base_buffer[j+1] = base_buffer[endpos+1]; base_buffer[endpos] = temp1; base_buffer[endpos+1] = temp2; } } //qDebug() << "read" << m_iCurrentPosition << samples_read; return samples_read; }
int ReadAheadManager::getNextSamples(double dRate, CSAMPLE* buffer, int requested_samples) { if (!even(requested_samples)) { qDebug() << "ERROR: Non-even requested_samples to ReadAheadManager::getNextSamples"; requested_samples--; } bool in_reverse = dRate < 0; int start_sample = m_iCurrentPosition; //qDebug() << "start" << start_sample << requested_samples; int samples_needed = requested_samples; CSAMPLE* base_buffer = buffer; // A loop will only limit the amount we can read in one shot. const double loop_trigger = m_pLoopingControl->nextTrigger( dRate, m_iCurrentPosition, 0, 0); bool loop_active = loop_trigger != kNoTrigger; int preloop_samples = 0; if (loop_active) { int samples_available = in_reverse ? m_iCurrentPosition - loop_trigger : loop_trigger - m_iCurrentPosition; if (samples_available < 0) { samples_needed = 0; } else { preloop_samples = samples_available; samples_needed = math_clamp(samples_needed, 0, samples_available); } } if (in_reverse) { start_sample = m_iCurrentPosition - samples_needed; } // Sanity checks. if (samples_needed < 0) { qDebug() << "Need negative samples in ReadAheadManager::getNextSamples. Ignoring read"; return 0; } int samples_read = m_pReader->read(start_sample, in_reverse, samples_needed, base_buffer); if (samples_read != samples_needed) { qDebug() << "didn't get what we wanted" << samples_read << samples_needed; } // Increment or decrement current read-ahead position if (in_reverse) { addReadLogEntry(m_iCurrentPosition, m_iCurrentPosition - samples_read); m_iCurrentPosition -= samples_read; } else { addReadLogEntry(m_iCurrentPosition, m_iCurrentPosition + samples_read); m_iCurrentPosition += samples_read; } // Activate on this trigger if necessary if (loop_active) { // LoopingControl makes the decision about whether we should loop or // not. const double loop_target = m_pLoopingControl-> process(dRate, m_iCurrentPosition, 0, 0); if (loop_target != kNoTrigger) { m_iCurrentPosition = loop_target; int loop_read_position = m_iCurrentPosition + (in_reverse ? preloop_samples : -preloop_samples); int looping_samples_read = m_pReader->read( loop_read_position, in_reverse, samples_read, m_pCrossFadeBuffer); if (looping_samples_read != samples_read) { qDebug() << "ERROR: Couldn't get all needed samples for crossfade."; } // do crossfade from the current buffer into the new loop beginning if (samples_read != 0) { // avoid division by zero SampleUtil::linearCrossfadeBuffers(base_buffer, base_buffer, m_pCrossFadeBuffer, samples_read); } } } //qDebug() << "read" << m_iCurrentPosition << samples_read; return samples_read; }