Example #1
0
static void updateHeight(TLDNode *node)
{
    if (node != NULL)
    {
        if (node->left != NULL && node->right != NULL)
            node->height = math_max(node->left->height, node->right->height) + 1;
        else if (node->left != NULL)
            node->height = math_max(node->left->height, 0) + 1;
        else if (node->right != NULL)
            node->height = math_max(0, node->right->height) + 1;
        else
            node->height = 1;
        updateHeight(node->parent);
    }
}
Example #2
0
void DlgPrefEQ::validate_levels() {
    m_highEqFreq = math_max(math_min(m_highEqFreq, kFrequencyUpperLimit),
                            kFrequencyLowerLimit);
    m_lowEqFreq = math_max(math_min(m_lowEqFreq, kFrequencyUpperLimit),
                           kFrequencyLowerLimit);
    if (m_lowEqFreq == m_highEqFreq) {
        if (m_lowEqFreq == kFrequencyLowerLimit) {
            ++m_highEqFreq;
        } else if (m_highEqFreq == kFrequencyUpperLimit) {
            --m_lowEqFreq;
        } else {
            ++m_highEqFreq;
        }
    }
}
Example #3
0
void BpmControl::slotAdjustBeatsSlower(double v) {
    BeatsPointer pBeats = m_pBeats;
    if (v > 0 && pBeats && (pBeats->getCapabilities() & Beats::BEATSCAP_SETBPM)) {
        double new_bpm = math_max(10.0, pBeats->getBpm() - .01);
        pBeats->setBpm(new_bpm);
    }
}
Example #4
0
void WaveformWidgetFactory::setFrameRate(int frameRate) {
    m_frameRate = math_min(120, math_max(1, frameRate));
    if (m_config) {
        m_config->set(ConfigKey("[Waveform]","FrameRate"), ConfigValue(m_frameRate));
    }
    m_vsyncThread->setUsSyncIntervalTime(1e6 / m_frameRate);
}
Example #5
0
/*-------------------------------------------------------------------------*
 * PL_FD_TELL_INTERV_INTERV                                                *
 *                                                                         *
 *-------------------------------------------------------------------------*/
Bool
Pl_Fd_Tell_Interv_Interv(WamWord *fdv_adr, int min, int max)
{
  int nb_elem;
  int propag;
  int min1, max1;


  min1 = Min(fdv_adr);
  max1 = Max(fdv_adr);

  min = math_max(min, min1);
  max = math_min(max, max1);

  if (min > max)		/* detects also if the initial   */
    return FALSE;		/* interval (min, max) was empty */

  if (min == max)
    Update_Range_From_Int(fdv_adr, min, propag);
  else
    {
      nb_elem = max - min + 1;
      Update_Interval_From_Interval(fdv_adr, nb_elem, min, max, propag);
    }

  if (propag)
    All_Propagations(fdv_adr, propag);

  return TRUE;
}
Example #6
0
extracted_components *extract_partitions(cog *c, long low, long high) {
  double_struct *ret_struct;
  ret_struct = amerge(c, low, high);
  c = ret_struct->cog;
  iter_cleanup(ret_struct->iter);
  free(ret_struct);
  extracted_components *ret; 
  if(c->type == COG_BTREE) {
    if(c->data.btree.sep <= low) {
      ret = extract_partitions(c->data.btree.rhs, low, high);
      ret->lhs = ret->lhs == NULL ? c->data.btree.lhs : make_btree(c->data.btree.lhs, ret->lhs, c->data.btree.sep);
    } else if(c->data.btree.sep >= high) {
      ret = extract_partitions(c->data.btree.lhs, low, high);
      ret->rhs = ret->rhs == NULL ? c->data.btree.rhs : make_btree(ret->rhs, c->data.btree.rhs, c->data.btree.sep);
    } else {
      ret = extract_partitions(c->data.btree.lhs, low, c->data.btree.sep);
      struct extracted_components *ret2 = extract_partitions(c->data.btree.rhs, c->data.btree.sep, high);
      ret->rhs = ret2->rhs;

      if(ret->iter == NULL) {
        ret->iter = ret2->iter;
        ret->low_key = ret2->low_key;
        ret->high_key = ret2->high_key;
      } else if(ret2->iter != NULL) {
        ret->iter = iter_concat(ret->iter, ret2->iter);
        ret->low_key = math_min(ret->low_key, ret2->low_key);
        ret->high_key = math_max(ret->high_key, ret2->high_key);
      }
    }
    
    return ret;
  } else if(c->type == COG_SORTEDARRAY) {
    int start = c->data.sortedarray.start;
    int len = c->data.sortedarray.len;
    buffer b = c->data.sortedarray.records;
    int low_index = record_binarysearch(b->data, low, start, len);
    int high_index = record_binarysearch(b->data, high, start, len);
    while((low_index < high_index) && (b->data[low_index].key < low)) {
      low_index++;
    }
    while((low_index-1 >= start) && (b->data[low_index-1].key >= low)) {
      low_index--;
    }
    while((high_index < (start+len)) && (b->data[high_index].key <= high)) {
      high_index++;
    }
    cog *lhs = low_index > start ? make_sortedarray(start, low_index - start, b) : NULL;
    cog *rhs = high_index < (start+len) ? make_sortedarray(high_index, start + len - high_index, b) : NULL;
    iterator iter = low_index < high_index ? scan(c, low, high) : NULL;
    long low_key = iter == NULL ? MAX_VALUE :  buffer_key(b, low_index);
    long high_key = iter == NULL ? MIN_VALUE : buffer_key(b, high_index - 1);

    return make_extracted_components(lhs, rhs, low_key, high_key, iter);
  } else {
    return NULL;    
  }
}
double ControlLinPotmeterBehavior::valueToMidiParameter(double dValue) {
    // 7-bit MIDI has 128 values [0, 127]. This means there is no such thing as
    // center. The industry convention is that 64 is center. We fake things a
    // little bit here to make that the case. This function is linear from [0,
    // 127.0/128.0] with slope 128 and then cuts off at 127 from 127.0/128.0 to
    // 1.0.  from 0 to 64 with slope 128 and from 64 to 127 with slope 126.
    double dNorm = valueToParameter(dValue);
    return math_max(127.0, dNorm * 128.0);
}
void ReadAheadSampleBuffer::adjustCapacity(SINT capacity) {
    DEBUG_ASSERT_CLASS_INVARIANT_ReadAheadSampleBuffer;

    SINT newCapacity = math_max(readableLength(), capacity);
    if (newCapacity != this->capacity()) {
        ReadAheadSampleBuffer tmp(*this, newCapacity);
        swap(tmp);
    }

    DEBUG_ASSERT_CLASS_INVARIANT_ReadAheadSampleBuffer;
}
Example #9
0
void WaveformWidgetFactory::setDefaultZoom(int zoom) {
    m_defaultZoom = math_max(WaveformWidgetRenderer::s_waveformMinZoom,
                             math_min(zoom, WaveformWidgetRenderer::s_waveformMaxZoom));
    if (m_config) {
        m_config->set(ConfigKey("[Waveform]","DefaultZoom"), ConfigValue(m_defaultZoom));
    }

    for (int i = 0; i < m_waveformWidgetHolders.size(); i++) {
        m_waveformWidgetHolders[i].m_waveformViewer->setZoom(m_defaultZoom);
    }
}
Example #10
0
void TrackInfoObject::setPlayedAndUpdatePlaycount(bool bPlayed) {
    QMutexLocker lock(&m_qMutex);
    if (bPlayed) {
        ++m_iTimesPlayed;
        setDirty(true);
    }
    else if (m_bPlayed && !bPlayed) {
        m_iTimesPlayed = math_max(0, m_iTimesPlayed - 1);
        setDirty(true);
    }
    m_bPlayed = bPlayed;
}
Example #11
0
void EffectButtonParameterSlot::loadEffect(EffectPointer pEffect) {
    //qDebug() << debugString() << "loadEffect" << (pEffect ? pEffect->getManifest().name() : "(null)");
    if (m_pEffectParameter) {
        clear();
    }

    if (pEffect) {
        m_pEffect = pEffect;
        // Returns null if it doesn't have a parameter for that number
        m_pEffectParameter = pEffect->getButtonParameterForSlot(m_iParameterSlotNumber);

        if (m_pEffectParameter) {
            // Set the number of states
            int numStates = math_max(m_pEffectParameter->manifest()->getSteps().size(), 2);
            m_pControlValue->setStates(numStates);
            //qDebug() << debugString() << "Loading effect parameter" << m_pEffectParameter->name();
            double dValue = m_pEffectParameter->getValue();
            double dMinimum = m_pEffectParameter->getMinimum();
            double dMinimumLimit = dMinimum; // TODO(rryan) expose limit from EffectParameter
            double dMaximum = m_pEffectParameter->getMaximum();
            double dMaximumLimit = dMaximum; // TODO(rryan) expose limit from EffectParameter
            double dDefault = m_pEffectParameter->getDefault();

            if (dValue > dMaximum || dValue < dMinimum ||
                dMinimum < dMinimumLimit || dMaximum > dMaximumLimit ||
                dDefault > dMaximum || dDefault < dMinimum) {
                qWarning() << debugString() << "WARNING: EffectParameter does not satisfy basic sanity checks.";
            }

            // qDebug() << debugString()
            //         << QString("Val: %1 Min: %2 MinLimit: %3 Max: %4 MaxLimit: %5 Default: %6")
            //         .arg(dValue).arg(dMinimum).arg(dMinimumLimit).arg(dMaximum).arg(dMaximumLimit).arg(dDefault);

            m_pControlValue->set(dValue);
            m_pControlValue->setDefaultValue(dDefault);
            EffectManifestParameter::ControlHint type = m_pEffectParameter->getControlHint();
            // TODO(rryan) expose this from EffectParameter
            m_pControlType->forceSet(static_cast<double>(type));
            // Default loaded parameters to loaded and unlinked
            m_pControlLoaded->forceSet(1.0);

            connect(m_pEffectParameter, SIGNAL(valueChanged(double)),
                    this, SLOT(slotParameterValueChanged(double)));
        }
    }
    emit(updated());
}
Example #12
0
void WWaveformViewer::mouseMoveEvent(QMouseEvent* event) {
    // Only send signals for mouse moving if the left button is pressed
    if (m_bScratching && m_waveformWidget) {
        // Adjusts for one-to-one movement.
        double audioSamplePerPixel = m_waveformWidget->getAudioSamplePerPixel();
        double targetPosition = -1.0 * event->pos().x() * audioSamplePerPixel * 2;
        //qDebug() << "Target:" << targetPosition;
        m_pScratchPosition->slotSet(targetPosition);
    } else if (m_bBending) {
        QPoint diff = event->pos() - m_mouseAnchor;
        // start at the middle of 0-127, and emit values based on
        // how far the mouse has traveled horizontally
        double v = 64.0 + diff.x()/10.0f;
        // clamp to [0, 127]
        v = math_min(127.0, math_max(0.0, v));
        emit(valueChangedRightDown(v));
    }
}
Example #13
0
 void updatePlayedAndVerify(PlayCounter* pPlayCounter, bool bPlayed) {
     bool isPlayedBefore = pPlayCounter->isPlayed();
     int timesPlayedBefore = pPlayCounter->getTimesPlayed();
     pPlayCounter->setPlayedAndUpdateTimesPlayed(bPlayed);
     bool isPlayedAfter = pPlayCounter->isPlayed();
     int timesPlayedAfter = pPlayCounter->getTimesPlayed();
     if (bPlayed) {
         EXPECT_TRUE(isPlayedAfter);
         EXPECT_EQ(timesPlayedBefore + 1, timesPlayedAfter);
     } else {
         EXPECT_FALSE(isPlayedAfter);
         if (isPlayedBefore) {
             EXPECT_EQ(math_max(0, timesPlayedBefore - 1), timesPlayedAfter);
         } else {
             EXPECT_EQ(timesPlayedBefore, timesPlayedAfter);
         }
     }
 }
Example #14
0
void CRenderer::MeasureText( const MGuiRendFont* fnt, const char_t* text, uint32* x_out, uint32* y_out )
{
	float x, y, xout;
	float tmp1, tmp2;
	uint32 c;
	register const char_t* s;
	const CFont* font = (const CFont*)fnt;

	if ( font == NULL || text == NULL )
	{
		*x_out = 1;
		*y_out = 1;
		return;
	}

	x = xout = 0;
	y = (float)font->size;

	for ( s = text; *s; s++ )
	{
		c = *(uchar_t*)s;

		if ( c < font->first_char || c > font->last_char )
		{
			continue;
		}
		else
		{
			c -= font->first_char;
			tmp1 = font->texCoords[c][0];
			tmp2 = font->texCoords[c][2];

			x += ( tmp2 - tmp1 ) * font->width - 2 * font->spacing;
		}
	}	

	xout = math_max( xout, x );

	*x_out = (uint32)xout;
	*y_out = (uint32)y;
}
Example #15
0
void WaveformWidgetRenderer::draw(QPainter* painter, QPaintEvent* event) {

#ifdef WAVEFORMWIDGETRENDERER_DEBUG
    m_lastSystemFrameTime = m_timer->restart().toIntegerNanos();
#endif

    //PerformanceTimer timer;
    //timer.start();

    // not ready to display need to wait until track initialization is done
    // draw only first is stack (background)
    int stackSize = m_rendererStack.size();
    if (m_trackSamples <= 0.0 || m_playPos == -1) {
        if (stackSize) {
            m_rendererStack.at(0)->draw(painter, event);
        }
        return;
    } else {
        for (int i = 0; i < stackSize; i++) {
            // qDebug() << i << " a  " << timer.restart().formatNanosWithUnit();
            m_rendererStack.at(i)->draw(painter, event);
            // qDebug() << i << " e " << timer.restart().formatNanosWithUnit();
        }

        painter->setPen(m_colors.getPlayPosColor());
        painter->drawLine(m_width/2,0,m_width/2,m_height);
        painter->setOpacity(0.5);
        painter->setPen(m_colors.getBgColor());
        painter->drawLine(m_width/2 + 1,0,m_width/2 + 1,m_height);
        painter->drawLine(m_width/2 - 1,0,m_width/2 - 1,m_height);
    }

#ifdef WAVEFORMWIDGETRENDERER_DEBUG
    int systemMax = -1;
    int frameMax = -1;
    for (int i = 0; i < 100; ++i) {
        frameMax = math_max(frameMax, m_lastFramesTime[i]);
        systemMax = math_max(systemMax, m_lastSystemFramesTime[i]);
    }

    //hud debug display
    painter->drawText(1,12,
                      QString::number(m_lastFrameTime).rightJustified(2,'0') + "(" +
                      QString::number(frameMax).rightJustified(2,'0') + ")" +
                      QString::number(m_lastSystemFrameTime) + "(" +
                      QString::number(systemMax) + ")" +
                      QString::number(realtimeError));

    painter->drawText(1,m_height-1,
                      QString::number(m_playPos) + " [" +
                      QString::number(m_firstDisplayedPosition) + "-" +
                      QString::number(m_lastDisplayedPosition) + "]" +
                      QString::number(m_rate) + " | " +
                      QString::number(m_gain) + " | " +
                      QString::number(m_rateDir) + " | " +
                      QString::number(m_zoomFactor));

    m_lastFrameTime = m_timer->restart().toIntegerNanos();

    ++currentFrame;
    currentFrame = currentFrame%100;
    m_lastSystemFramesTime[currentFrame] = m_lastSystemFrameTime;
    m_lastFramesTime[currentFrame] = m_lastFrameTime;
#endif

    //qDebug() << "draw() ende" << timer.restart().formatNanosWithUnit();
}
Example #16
0
SoundSource::OpenResult SoundSourceMp3::tryOpen(const AudioSourceConfig& /*audioSrcCfg*/) {
    DEBUG_ASSERT(!hasValidChannelCount());
    DEBUG_ASSERT(!hasValidSamplingRate());

    DEBUG_ASSERT(!m_file.isOpen());
    if (!m_file.open(QIODevice::ReadOnly)) {
        qWarning() << "Failed to open file:" << m_file.fileName();
        return OpenResult::FAILED;
    }

    // Get a pointer to the file using memory mapped IO
    m_fileSize = m_file.size();
    m_pFileData = m_file.map(0, m_fileSize);
    // NOTE(uklotzde): If the file disappears unexpectedly while mapped
    // a SIGBUS error might occur that is not handled and will terminate
    // Mixxx immediately. This behavior is documented in the manpage of
    // mmap(). It has already appeared due to hardware errors and is
    // described in the following bug report:
    // https://bugs.launchpad.net/mixxx/+bug/1452005

    // Transfer it to the mad stream-buffer:
    mad_stream_options(&m_madStream, MAD_OPTION_IGNORECRC);
    mad_stream_buffer(&m_madStream, m_pFileData, m_fileSize);
    DEBUG_ASSERT(m_pFileData == m_madStream.this_frame);

    DEBUG_ASSERT(m_seekFrameList.empty());
    m_avgSeekFrameCount = 0;
    m_curFrameIndex = getMinFrameIndex();
    int headerPerSamplingRate[kSamplingRateCount];
    for (int i = 0; i < kSamplingRateCount; ++i) {
        headerPerSamplingRate[i] = 0;
    }

    // Decode all the headers and calculate audio properties

    unsigned long sumBitrate = 0;

    mad_header madHeader;
    mad_header_init(&madHeader);

    SINT maxChannelCount = getChannelCount();
    do {
        if (!decodeFrameHeader(&madHeader, &m_madStream, true)) {
            if (isStreamValid(m_madStream)) {
                // Skip frame
                continue;
            } else {
                // Abort decoding
                break;
            }
        }

        // Grab data from madHeader
        const unsigned int madSampleRate = madHeader.samplerate;

        // TODO(XXX): Replace DEBUG_ASSERT with static_assert
        // MAD must not change its enum values!
        DEBUG_ASSERT(MAD_UNITS_8000_HZ == 8000);
        const mad_units madUnits = static_cast<mad_units>(madSampleRate);

        const long madFrameLength = mad_timer_count(madHeader.duration, madUnits);
        if (0 >= madFrameLength) {
            qWarning() << "Skipping MP3 frame with invalid length"
                    << madFrameLength
                    << "in:" << m_file.fileName();
            // Skip frame
            continue;
        }

        const SINT madChannelCount = MAD_NCHANNELS(&madHeader);
        if (isValidChannelCount(maxChannelCount) && (madChannelCount != maxChannelCount)) {
            qWarning() << "Differing number of channels"
                    << madChannelCount << "<>" << maxChannelCount
                    << "in some MP3 frame headers:"
                    << m_file.fileName();
        }
        maxChannelCount = math_max(madChannelCount, maxChannelCount);

        const int samplingRateIndex = getIndexBySamplingRate(madSampleRate);
        if (samplingRateIndex >= kSamplingRateCount) {
            qWarning() << "Invalid sample rate:" << m_file.fileName()
                    << madSampleRate;
            // Abort
            mad_header_finish(&madHeader);
            return OpenResult::FAILED;
        }
        // Count valid frames separated by its sampling rate
        headerPerSamplingRate[samplingRateIndex]++;

        addSeekFrame(m_curFrameIndex, m_madStream.this_frame);

        // Accumulate data from the header
        sumBitrate += madHeader.bitrate;

        // Update current stream position
        m_curFrameIndex += madFrameLength;

        DEBUG_ASSERT(m_madStream.this_frame);
        DEBUG_ASSERT(0 <= (m_madStream.this_frame - m_pFileData));
    } while (quint64(m_madStream.this_frame - m_pFileData) < m_fileSize);

    mad_header_finish(&madHeader);

    if (MAD_ERROR_NONE != m_madStream.error) {
        // Unreachable code for recoverable errors
        DEBUG_ASSERT(!MAD_RECOVERABLE(m_madStream.error));
        if (MAD_ERROR_BUFLEN != m_madStream.error) {
            qWarning() << "Unrecoverable MP3 header error:"
                    << mad_stream_errorstr(&m_madStream);
            // Abort
            return OpenResult::FAILED;
        }
    }

    if (m_seekFrameList.empty()) {
        // This is not a working MP3 file.
        qWarning() << "SSMP3: This is not a working MP3 file:"
                << m_file.fileName();
        // Abort
        return OpenResult::FAILED;
    }

    int mostCommonSamplingRateIndex = kSamplingRateCount; // invalid
    int mostCommonSamplingRateCount = 0;
    int differentRates = 0;
    for (int i = 0; i < kSamplingRateCount; ++i) {
        // Find most common sampling rate
        if (mostCommonSamplingRateCount < headerPerSamplingRate[i]) {
            mostCommonSamplingRateCount = headerPerSamplingRate[i];
            mostCommonSamplingRateIndex = i;
            differentRates++;
        }
    }

    if (differentRates > 1) {
        qWarning() << "Differing sampling rate in some headers:"
                   << m_file.fileName();
        for (int i = 0; i < kSamplingRateCount; ++i) {
            if (0 < headerPerSamplingRate[i]) {
                qWarning() << headerPerSamplingRate[i] << "MP3 headers with sampling rate" << getSamplingRateByIndex(i);
            }
        }

        qWarning() << "MP3 files with varying sample rate are not supported!";
        qWarning() << "Since this happens most likely due to a corrupt file";
        qWarning() << "Mixxx tries to plays it with the most common sample rate for this file";
    }

    if (mostCommonSamplingRateIndex < kSamplingRateCount) {
        setSamplingRate(getSamplingRateByIndex(mostCommonSamplingRateIndex));
    } else {
        qWarning() << "No single valid sampling rate in header";
        // Abort
        return OpenResult::FAILED;
    }

    // Initialize the AudioSource
    setChannelCount(maxChannelCount);
    setFrameCount(m_curFrameIndex);

    // Calculate average values
    m_avgSeekFrameCount = getFrameCount() / m_seekFrameList.size();
    const unsigned long avgBitrate = sumBitrate / m_seekFrameList.size();
    setBitrate(avgBitrate / 1000);

    // Terminate m_seekFrameList
    addSeekFrame(m_curFrameIndex, 0);

    // Reset positions
    m_curFrameIndex = getMinFrameIndex();

    // Restart decoding at the beginning of the audio stream
    m_curFrameIndex = restartDecoding(m_seekFrameList.front());
    if (m_curFrameIndex != m_seekFrameList.front().frameIndex) {
        qWarning() << "Failed to start decoding:" << m_file.fileName();
        // Abort
        return OpenResult::FAILED;
    }

    return OpenResult::SUCCEEDED;
}
Example #17
0
void WaveformRendererHSV::draw(QPainter* painter,
                                          QPaintEvent* /*event*/) {
    const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo();
    if (!trackInfo) {
        return;
    }

    ConstWaveformPointer waveform = trackInfo->getWaveform();
    if (waveform.isNull()) {
        return;
    }

    const int dataSize = waveform->getDataSize();
    if (dataSize <= 1) {
        return;
    }

    const WaveformData* data = waveform->data();
    if (data == NULL) {
        return;
    }

    painter->save();
    painter->setRenderHints(QPainter::Antialiasing, false);
    painter->setRenderHints(QPainter::HighQualityAntialiasing, false);
    painter->setRenderHints(QPainter::SmoothPixmapTransform, false);
    painter->setWorldMatrixEnabled(false);
    painter->resetTransform();

    // Rotate if drawing vertical waveforms
    if (m_waveformRenderer->getOrientation() == Qt::Vertical) {
        painter->setTransform(QTransform(0, 1, 1, 0, 0, 0));
    }

    const double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize;
    const double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize;

    const double offset = firstVisualIndex;

    // Represents the # of waveform data points per horizontal pixel.
    const double gain = (lastVisualIndex - firstVisualIndex) /
            (double)m_waveformRenderer->getLength();

    float allGain(1.0);
    getGains(&allGain, NULL, NULL, NULL);

    // Save HSV of waveform color. NOTE(rryan): On ARM, qreal is float so it's
    // important we use qreal here and not double or float or else we will get
    // build failures on ARM.
    qreal h, s, v;

    // Get base color of waveform in the HSV format (s and v isn't use)
    m_pColors->getLowColor().getHsvF(&h, &s, &v);

    QColor color;
    float lo, hi, total;

    const int breadth = m_waveformRenderer->getBreadth();
    const float halfBreadth = (float)breadth / 2.0;

    const float heightFactor = allGain * halfBreadth / 255.0;

    //draw reference line
    painter->setPen(m_pColors->getAxesColor());
    painter->drawLine(0, halfBreadth, m_waveformRenderer->getLength(), halfBreadth);

    for (int x = 0; x < m_waveformRenderer->getLength(); ++x) {
        // Width of the x position in visual indices.
        const double xSampleWidth = gain * x;

        // Effective visual index of x
        const double xVisualSampleIndex = xSampleWidth + offset;

        // Our current pixel (x) corresponds to a number of visual samples
        // (visualSamplerPerPixel) in our waveform object. We take the max of
        // all the data points on either side of xVisualSampleIndex within a
        // window of 'maxSamplingRange' visual samples to measure the maximum
        // data point contained by this pixel.
        double maxSamplingRange = gain / 2.0;

        // Since xVisualSampleIndex is in visual-samples (e.g. R,L,R,L) we want
        // to check +/- maxSamplingRange frames, not samples. To do this, divide
        // xVisualSampleIndex by 2. Since frames indices are integers, we round
        // to the nearest integer by adding 0.5 before casting to int.
        int visualFrameStart = int(xVisualSampleIndex / 2.0 - maxSamplingRange + 0.5);
        int visualFrameStop = int(xVisualSampleIndex / 2.0 + maxSamplingRange + 0.5);
        const int lastVisualFrame = dataSize / 2 - 1;

        // We now know that some subset of [visualFrameStart, visualFrameStop]
        // lies within the valid range of visual frames. Clamp
        // visualFrameStart/Stop to within [0, lastVisualFrame].
        visualFrameStart = math_clamp(visualFrameStart, 0, lastVisualFrame);
        visualFrameStop = math_clamp(visualFrameStop, 0, lastVisualFrame);

        int visualIndexStart = visualFrameStart * 2;
        int visualIndexStop = visualFrameStop * 2;

        int maxLow[2] = {0, 0};
        int maxHigh[2] = {0, 0};
        int maxMid[2] = {0, 0};
        int maxAll[2] = {0, 0};

        for (int i = visualIndexStart;
             i >= 0 && i + 1 < dataSize && i + 1 <= visualIndexStop; i += 2) {
            const WaveformData& waveformData = *(data + i);
            const WaveformData& waveformDataNext = *(data + i + 1);
            maxLow[0] = math_max(maxLow[0], (int)waveformData.filtered.low);
            maxLow[1] = math_max(maxLow[1], (int)waveformDataNext.filtered.low);
            maxMid[0] = math_max(maxMid[0], (int)waveformData.filtered.mid);
            maxMid[1] = math_max(maxMid[1], (int)waveformDataNext.filtered.mid);
            maxHigh[0] = math_max(maxHigh[0], (int)waveformData.filtered.high);
            maxHigh[1] = math_max(maxHigh[1], (int)waveformDataNext.filtered.high);
            maxAll[0] = math_max(maxAll[0], (int)waveformData.filtered.all);
            maxAll[1] = math_max(maxAll[1], (int)waveformDataNext.filtered.all);
        }

        if (maxAll[0] && maxAll[1]) {
            // Calculate sum, to normalize
            // Also multiply on 1.2 to prevent very dark or light color
            total = (maxLow[0] + maxLow[1] + maxMid[0] + maxMid[1] + maxHigh[0] + maxHigh[1]) * 1.2;

            // prevent division by zero
            if (total > 0)
            {
                // Normalize low and high (mid not need, because it not change the color)
                lo = (maxLow[0] + maxLow[1]) / total;
                hi = (maxHigh[0] + maxHigh[1]) / total;
            }
            else
                lo = hi = 0.0;

            // Set color
            color.setHsvF(h, 1.0-hi, 1.0-lo);

            painter->setPen(color);
            switch (m_alignment) {
                case Qt::AlignBottom :
                case Qt::AlignRight :
                    painter->drawLine(
                        x, breadth,
                        x, breadth - (int)(heightFactor * (float)math_max(maxAll[0],maxAll[1])));
                    break;
                case Qt::AlignTop :
                case Qt::AlignLeft :
                    painter->drawLine(
                        x, 0,
                        x, (int)(heightFactor * (float)math_max(maxAll[0],maxAll[1])));
                    break;
                default :
                    painter->drawLine(
                        x, (int)(halfBreadth - heightFactor * (float)maxAll[0]),
                        x, (int)(halfBreadth + heightFactor * (float)maxAll[1]));
            }
        }
    }

    painter->restore();
}
int QtWaveformRendererFilteredSignal::buildPolygon() {
    // We have to check the track is present because it might have been unloaded
    // between the call to draw and the call to buildPolygon
    TrackPointer pTrack = m_waveformRenderer->getTrackInfo();
    if (!pTrack) {
        return 0;
    }

    const Waveform* waveform = pTrack->getWaveform();
    if (waveform == NULL) {
        return 0;
    }

    const int dataSize = waveform->getDataSize();
    if (dataSize <= 1) {
        return 0;
    }

    const WaveformData* data = waveform->data();
    if (data == NULL) {
        return 0;
    }

    const double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize;
    const double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize;

    m_polygon[0].clear();
    m_polygon[1].clear();
    m_polygon[2].clear();

    m_polygon[0].reserve(2 * m_waveformRenderer->getWidth() + 2);
    m_polygon[1].reserve(2 * m_waveformRenderer->getWidth() + 2);
    m_polygon[2].reserve(2 * m_waveformRenderer->getWidth() + 2);

    QPointF point(0.0, 0.0);
    m_polygon[0].append(point);
    m_polygon[1].append(point);
    m_polygon[2].append(point);

    const double offset = firstVisualIndex;

    // Represents the # of waveform data points per horizontal pixel.
    const double gain = (lastVisualIndex - firstVisualIndex) /
            (double)m_waveformRenderer->getWidth();

    float lowGain(1.0), midGain(1.0), highGain(1.0);
    getGains(NULL, &lowGain, &midGain, &highGain);

    //NOTE(vrince) Please help me find a better name for "channelSeparation"
    //this variable stand for merged channel ... 1 = merged & 2 = separated
    int channelSeparation = 2;
    if (m_alignment != Qt::AlignCenter)
        channelSeparation = 1;

    for (int channel = 0; channel < channelSeparation; ++channel) {
        int startPixel = 0;
        int endPixel = m_waveformRenderer->getWidth() - 1;
        int delta = 1;
        double direction = 1.0;

        //Reverse display for merged bottom channel
        if (m_alignment == Qt::AlignBottom)
            direction = -1.0;

        if (channel == 1) {
            startPixel = m_waveformRenderer->getWidth() - 1;
            endPixel = 0;
            delta = -1;
            direction = -1.0;

            // After preparing the first channel, insert the pivot point.
            point = QPointF(m_waveformRenderer->getWidth(), 0.0);
            m_polygon[0].append(point);
            m_polygon[1].append(point);
            m_polygon[2].append(point);
        }

        for (int x = startPixel;
                (startPixel < endPixel) ? (x <= endPixel) : (x >= endPixel);
                x += delta) {

            // TODO(rryan) remove before 1.11 release. I'm seeing crashes
            // sometimes where the pointIndex is very very large. It hasn't come
            // back since adding locking, but I'm leaving this so that we can
            // get some info about it before crashing. (The crash usually
            // corrupts a lot of the stack).
            if (m_polygon[0].size() > 2 * m_waveformRenderer->getWidth() + 2) {
                qDebug() << "OUT OF CONTROL"
                         << 2 * m_waveformRenderer->getWidth() + 2
                         << dataSize
                         << channel << m_polygon[0].size() << x;
            }

            // Width of the x position in visual indices.
            const double xSampleWidth = gain * x;

            // Effective visual index of x
            const double xVisualSampleIndex = xSampleWidth + offset;

            // Our current pixel (x) corresponds to a number of visual samples
            // (visualSamplerPerPixel) in our waveform object. We take the max of
            // all the data points on either side of xVisualSampleIndex within a
            // window of 'maxSamplingRange' visual samples to measure the maximum
            // data point contained by this pixel.
            double maxSamplingRange = gain / 2.0;

            // Since xVisualSampleIndex is in visual-samples (e.g. R,L,R,L) we want
            // to check +/- maxSamplingRange frames, not samples. To do this, divide
            // xVisualSampleIndex by 2. Since frames indices are integers, we round
            // to the nearest integer by adding 0.5 before casting to int.
            int visualFrameStart = int(xVisualSampleIndex / 2.0 - maxSamplingRange + 0.5);
            int visualFrameStop = int(xVisualSampleIndex / 2.0 + maxSamplingRange + 0.5);

            // If the entire sample range is off the screen then don't calculate a
            // point for this pixel.
            const int lastVisualFrame = dataSize / 2 - 1;
            if (visualFrameStop < 0 || visualFrameStart > lastVisualFrame) {
                point = QPointF(x, 0.0);
                m_polygon[0].append(point);
                m_polygon[1].append(point);
                m_polygon[2].append(point);
                continue;
            }

            // We now know that some subset of [visualFrameStart,
            // visualFrameStop] lies within the valid range of visual
            // frames. Clamp visualFrameStart/Stop to within [0,
            // lastVisualFrame].
            visualFrameStart = math_max(math_min(lastVisualFrame, visualFrameStart), 0);
            visualFrameStop = math_max(math_min(lastVisualFrame, visualFrameStop), 0);

            int visualIndexStart = visualFrameStart * 2 + channel;
            int visualIndexStop = visualFrameStop * 2 + channel;

            // if (x == m_waveformRenderer->getWidth() / 2) {
            //     qDebug() << "audioVisualRatio" << waveform->getAudioVisualRatio();
            //     qDebug() << "visualSampleRate" << waveform->getVisualSampleRate();
            //     qDebug() << "audioSamplesPerVisualPixel" << waveform->getAudioSamplesPerVisualSample();
            //     qDebug() << "visualSamplePerPixel" << visualSamplePerPixel;
            //     qDebug() << "xSampleWidth" << xSampleWidth;
            //     qDebug() << "xVisualSampleIndex" << xVisualSampleIndex;
            //     qDebug() << "maxSamplingRange" << maxSamplingRange;;
            //     qDebug() << "Sampling pixel " << x << "over [" << visualIndexStart << visualIndexStop << "]";
            // }

            unsigned char maxLow = 0;
            unsigned char maxBand = 0;
            unsigned char maxHigh = 0;

            for (int i = visualIndexStart; i >= 0 && i < dataSize && i <= visualIndexStop;
                 i += channelSeparation) {
                const WaveformData& waveformData = *(data + i);
                unsigned char low = waveformData.filtered.low;
                unsigned char mid = waveformData.filtered.mid;
                unsigned char high = waveformData.filtered.high;
                maxLow = math_max(maxLow, low);
                maxBand = math_max(maxBand, mid);
                maxHigh = math_max(maxHigh, high);
            }

            m_polygon[0].append(QPointF(x, (float)maxLow * lowGain * direction));
            m_polygon[1].append(QPointF(x, (float)maxBand * midGain * direction));
            m_polygon[2].append(QPointF(x, (float)maxHigh * highGain * direction));
        }
    }

    //If channel are not displayed separately we need to close the loop properly
    if (channelSeparation == 1) {
        point = QPointF(m_waveformRenderer->getWidth(), 0.0);
        m_polygon[0].append(point);
        m_polygon[1].append(point);
        m_polygon[2].append(point);
    }

    return m_polygon[0].size();
}
Example #19
0
SINT SoundSourceM4A::readSampleFrames(
        SINT numberOfFrames, CSAMPLE* sampleBuffer) {
    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));

    const SINT numberOfFramesTotal = math_min(
            numberOfFrames, getMaxFrameIndex() - m_curFrameIndex);
    const SINT numberOfSamplesTotal = frames2samples(numberOfFramesTotal);

    CSAMPLE* pSampleBuffer = sampleBuffer;
    SINT numberOfSamplesRemaining = numberOfSamplesTotal;
    while (0 < numberOfSamplesRemaining) {

        if (!m_sampleBuffer.isEmpty()) {
            // Consume previously decoded sample data
            const SampleBuffer::ReadableChunk readableChunk(
                    m_sampleBuffer.readFromHead(numberOfSamplesRemaining));
            if (pSampleBuffer) {
                SampleUtil::copy(pSampleBuffer, readableChunk.data(), readableChunk.size());
                pSampleBuffer += readableChunk.size();
            }
            m_curFrameIndex += samples2frames(readableChunk.size());
            DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
            DEBUG_ASSERT(numberOfSamplesRemaining >= readableChunk.size());
            numberOfSamplesRemaining -= readableChunk.size();
            if (0 == numberOfSamplesRemaining) {
                break; // exit loop
            }
        }
        // All previously decoded sample data has been consumed now
        DEBUG_ASSERT(m_sampleBuffer.isEmpty());

        if (0 == m_inputBufferLength) {
            // Fill input buffer from file
            if (isValidSampleBlockId(m_curSampleBlockId)) {
                // Read data for next sample block into input buffer
                u_int8_t* pInputBuffer = &m_inputBuffer[0];
                u_int32_t inputBufferLength = m_inputBuffer.size(); // in/out parameter
                if (!MP4ReadSample(m_hFile, m_trackId, m_curSampleBlockId,
                        &pInputBuffer, &inputBufferLength,
                        NULL, NULL, NULL, NULL)) {
                    qWarning()
                            << "Failed to read MP4 input data for sample block"
                            << m_curSampleBlockId << "(" << "min ="
                            << kSampleBlockIdMin << "," << "max ="
                            << m_maxSampleBlockId << ")";
                    break; // abort
                }
                ++m_curSampleBlockId;
                m_inputBufferLength = inputBufferLength;
                m_inputBufferOffset = 0;
            }
        }
        DEBUG_ASSERT(0 <= m_inputBufferLength);
        if (0 == m_inputBufferLength) {
            break; // EOF
        }

        // NOTE(uklotzde): The sample buffer for NeAACDecDecode2 has to
        // be big enough for a whole block of decoded samples, which
        // contains up to kFramesPerSampleBlock frames. Otherwise
        // we need to use a temporary buffer.
        CSAMPLE* pDecodeBuffer; // in/out parameter
        SINT decodeBufferCapacity;
        const SINT decodeBufferCapacityMin = frames2samples(kFramesPerSampleBlock);
        if (pSampleBuffer && (decodeBufferCapacityMin <= numberOfSamplesRemaining)) {
            // Decode samples directly into sampleBuffer
            pDecodeBuffer = pSampleBuffer;
            decodeBufferCapacity = numberOfSamplesRemaining;
        } else {
            // Decode next sample block into temporary buffer
            const SINT writeToTailCount = math_max(
                    numberOfSamplesRemaining, decodeBufferCapacityMin);
            const SampleBuffer::WritableChunk writableChunk(
                    m_sampleBuffer.writeToTail(writeToTailCount));
            pDecodeBuffer = writableChunk.data();
            decodeBufferCapacity = writableChunk.size();
        }
        DEBUG_ASSERT(decodeBufferCapacityMin <= decodeBufferCapacity);

        NeAACDecFrameInfo decFrameInfo;
        void* pDecodeResult = NeAACDecDecode2(
                m_hDecoder, &decFrameInfo,
                &m_inputBuffer[m_inputBufferOffset],
                m_inputBufferLength,
                reinterpret_cast<void**>(&pDecodeBuffer),
                decodeBufferCapacity * sizeof(*pDecodeBuffer));
        // Verify the decoding result
        if (0 != decFrameInfo.error) {
            qWarning() << "AAC decoding error:"
                    << decFrameInfo.error
                    << NeAACDecGetErrorMessage(decFrameInfo.error)
                    << getUrlString();
            break; // abort
        }
        DEBUG_ASSERT(pDecodeResult == pDecodeBuffer); // verify the in/out parameter

        // Verify the decoded sample data for consistency
        if (getChannelCount() != decFrameInfo.channels) {
            qWarning() << "Corrupt or unsupported AAC file:"
                    << "Unexpected number of channels" << decFrameInfo.channels
                    << "<>" << getChannelCount();
            break; // abort
        }
        if (getFrameRate() != SINT(decFrameInfo.samplerate)) {
            qWarning() << "Corrupt or unsupported AAC file:"
                    << "Unexpected sample rate" << decFrameInfo.samplerate
                    << "<>" << getFrameRate();
            break; // abort
        }

        // Consume input data
        m_inputBufferLength -= decFrameInfo.bytesconsumed;
        m_inputBufferOffset += decFrameInfo.bytesconsumed;

        // Consume decoded output data
        const SINT numberOfSamplesDecoded = decFrameInfo.samples;
        DEBUG_ASSERT(numberOfSamplesDecoded <= decodeBufferCapacity);
        SINT numberOfSamplesRead;
        if (pDecodeBuffer == pSampleBuffer) {
            numberOfSamplesRead = math_min(numberOfSamplesDecoded, numberOfSamplesRemaining);
            pSampleBuffer += numberOfSamplesRead;
        } else {
            m_sampleBuffer.readFromTail(decodeBufferCapacity - numberOfSamplesDecoded);
            const SampleBuffer::ReadableChunk readableChunk(
                    m_sampleBuffer.readFromHead(numberOfSamplesRemaining));
            numberOfSamplesRead = readableChunk.size();
            if (pSampleBuffer) {
                SampleUtil::copy(pSampleBuffer, readableChunk.data(), numberOfSamplesRead);
                pSampleBuffer += numberOfSamplesRead;
            }
        }
        // The decoder might decode more samples than actually needed
        // at the end of the file! When the end of the file has been
        // reached decoding can be restarted by seeking to a new
        // position.
        DEBUG_ASSERT(numberOfSamplesDecoded >= numberOfSamplesRead);
        m_curFrameIndex += samples2frames(numberOfSamplesRead);
        DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
        DEBUG_ASSERT(numberOfSamplesRemaining >= numberOfSamplesRead);
        numberOfSamplesRemaining -= numberOfSamplesRead;
    }

    DEBUG_ASSERT(isValidFrameIndex(m_curFrameIndex));
    DEBUG_ASSERT(numberOfSamplesTotal >= numberOfSamplesRemaining);
    return samples2frames(numberOfSamplesTotal - numberOfSamplesRemaining);
}
void GLWaveformRendererFilteredSignal::draw(QPainter* painter, QPaintEvent* /*event*/) {

    TrackPointer pTrack = m_waveformRenderer->getTrackInfo();
    if (!pTrack) {
        return;
    }

    ConstWaveformPointer waveform = pTrack->getWaveform();
    if (waveform.isNull()) {
        return;
    }

    const int dataSize = waveform->getDataSize();
    if (dataSize <= 1) {
        return;
    }

    const WaveformData* data = waveform->data();
    if (data == NULL) {
        return;
    }

    double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize;
    double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize;
    const double lineWidth = (1.0 / m_waveformRenderer->getVisualSamplePerPixel()) + 1.0;

    const int firstIndex = int(firstVisualIndex+0.5);
    firstVisualIndex = firstIndex - firstIndex%2;

    const int lastIndex = int(lastVisualIndex+0.5);
    lastVisualIndex = lastIndex + lastIndex%2;

    // Reset device for native painting
    painter->beginNativePainting();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Per-band gain from the EQ knobs.
    float allGain(1.0), lowGain(1.0), midGain(1.0), highGain(1.0);
    getGains(&allGain, &lowGain, &midGain, &highGain);

#ifndef __OPENGLES__

    if (m_alignment == Qt::AlignCenter) {
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        if (m_orientation == Qt::Vertical) {
            glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
            glScalef(-1.0f, 1.0f, 1.0f);
        }
        glOrtho(firstVisualIndex, lastVisualIndex, -255.0, 255.0, -10.0, 10.0);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        glScalef(1.f,allGain,1.f);

        glLineWidth(1.0);
        glDisable(GL_LINE_SMOOTH);

        //draw reference line
        glBegin(GL_LINES); {
            glColor4f(m_axesColor_r, m_axesColor_g,
                      m_axesColor_b, m_axesColor_a);
            glVertex2f(firstVisualIndex,0);
            glVertex2f(lastVisualIndex,0);
        }
        glEnd();

        glLineWidth(lineWidth);
        glEnable(GL_LINE_SMOOTH);

        glBegin(GL_LINES); {

            int firstIndex = math_max(static_cast<int>(firstVisualIndex), 0);
            int lastIndex = math_min(static_cast<int>(lastVisualIndex), dataSize);

            glColor4f(m_lowColor_r, m_lowColor_g, m_lowColor_b, 0.8);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxLow0 = data[visualIndex].filtered.low;
                GLfloat maxLow1 = data[visualIndex+1].filtered.low;

                glVertex2f(visualIndex,lowGain*maxLow0);
                glVertex2f(visualIndex,-1.f*lowGain*maxLow1);
            }

            glColor4f(m_midColor_r, m_midColor_g, m_midColor_b, 0.85);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxMid0 = data[visualIndex].filtered.mid;
                GLfloat maxMid1 = data[visualIndex+1].filtered.mid;

                glVertex2f(visualIndex, midGain * maxMid0);
                glVertex2f(visualIndex,-1.f * midGain * maxMid1);
            }

            glColor4f(m_highColor_r, m_highColor_g, m_highColor_b, 0.9);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxHigh0 = data[visualIndex].filtered.high;
                GLfloat maxHigh1 = data[visualIndex + 1].filtered.high;

                glVertex2f(visualIndex, highGain * maxHigh0);
                glVertex2f(visualIndex, -1.f * highGain * maxHigh1);
            }
        }
        glEnd();
    } else { //top || bottom
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        if (m_orientation == Qt::Vertical) {
            glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
            glScalef(-1.0f, 1.0f, 1.0f);
        }
        if (m_alignment == Qt::AlignBottom || m_alignment == Qt::AlignRight)
            glOrtho(firstVisualIndex, lastVisualIndex, 0.0, 255.0, -10.0, 10.0);
        else
            glOrtho(firstVisualIndex, lastVisualIndex, 255.0, 0.0, -10.0, 10.0);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        glScalef(1.f,allGain,1.f);

        glLineWidth(lineWidth);
        glEnable(GL_LINE_SMOOTH);

        glBegin(GL_LINES); {

            int firstIndex = math_max(static_cast<int>(firstVisualIndex), 0);
            int lastIndex = math_min(static_cast<int>(lastVisualIndex), dataSize);

            glColor4f(m_lowColor_r, m_lowColor_g, m_lowColor_b, 0.8);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxLow = math_max(
                        data[visualIndex].filtered.low,
                        data[visualIndex+1].filtered.low);

                glVertex2f(visualIndex, 0);
                glVertex2f(visualIndex, lowGain * maxLow);
            }

            glColor4f(m_midColor_r, m_midColor_g, m_midColor_b, 0.85);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxMid = math_max(
                        data[visualIndex].filtered.mid,
                        data[visualIndex+1].filtered.mid);

                glVertex2f(visualIndex, 0.f);
                glVertex2f(visualIndex, midGain * maxMid);
            }

            glColor4f(m_highColor_r, m_highColor_g, m_highColor_b, 0.9);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxHigh = math_max(
                        data[visualIndex].filtered.high,
                        data[visualIndex + 1].filtered.high);

                glVertex2f(visualIndex, 0.f);
                glVertex2f(visualIndex, highGain * maxHigh);
            }
        }
        glEnd();
    }

    //DEBUG
    /*glDisable(GL_ALPHA_TEST);
    glBegin(GL_LINE_LOOP);
    {
        glColor4f(0.5,1.0,0.5,0.25);
        glVertex3f(firstVisualIndex,-1.0f, 0.0f);
        glVertex3f(lastVisualIndex, 1.0f, 0.0f);
        glVertex3f(lastVisualIndex,-1.0f, 0.0f);
        glVertex3f(firstVisualIndex, 1.0f, 0.0f);
    }
    glEnd();*/

    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

#endif

    painter->endNativePainting();
}
Example #21
0
// We return the UserSettings here because we have to make changes to the
// configuration and the location of the file may change between releases.
UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) {

/*  Pre-1.7.0:
*
*   Since we didn't store version numbers in the config file prior to 1.7.0,
*   we check to see if the user is upgrading if his config files are in the old location,
*   since we moved them in 1.7.0. This code takes care of moving them.
*/

    QDir oldLocation = QDir(QDir::homePath());
#ifdef __WINDOWS__
    QFileInfo* pre170Config = new QFileInfo(oldLocation.filePath("mixxx.cfg"));
#else
    QFileInfo* pre170Config = new QFileInfo(oldLocation.filePath(".mixxx.cfg"));
#endif

    if (pre170Config->exists()) {

        // Move the files to their new location
        QDir newLocation = QDir(settingsPath);

        if (!newLocation.exists()) {
            qDebug() << "Creating new settings directory" << newLocation.absolutePath();
            newLocation.mkpath(".");
        }

        QString errorText = "Error moving your %1 file %2 to the new location %3: \n";

#ifdef __WINDOWS__
        QString oldFilePath = oldLocation.filePath("mixxxtrack.xml");
#else
        QString oldFilePath = oldLocation.filePath(".mixxxtrack.xml");
#endif

        QString newFilePath = newLocation.filePath("mixxxtrack.xml");
        QFile* oldFile = new QFile(oldFilePath);
        if (oldFile->exists()) {
            if (oldFile->copy(newFilePath)) {
                oldFile->remove();
            }
            else {
                if (oldFile->error()==14) qDebug() << errorText.arg("library", oldFilePath, newFilePath) << "The destination file already exists.";
                else qDebug() << errorText.arg("library", oldFilePath, newFilePath) << "Error #" << oldFile->error();
            }
        }
        delete oldFile;

#ifdef __WINDOWS__
        oldFilePath = oldLocation.filePath("mixxxbpmschemes.xml");
#else
        oldFilePath = oldLocation.filePath(".mixxxbpmscheme.xml");
#endif
        newFilePath = newLocation.filePath("mixxxbpmscheme.xml");
        oldFile = new QFile(oldFilePath);
        if (oldFile->exists()) {
            if (oldFile->copy(newFilePath))
                oldFile->remove();
            else {
                if (oldFile->error()==14) qDebug() << errorText.arg("settings", oldFilePath, newFilePath) << "The destination file already exists.";
                else qDebug() << errorText.arg("settings", oldFilePath, newFilePath) << "Error #" << oldFile->error();
            }
        }
        delete oldFile;
#ifdef __WINDOWS__
        oldFilePath = oldLocation.filePath("MixxxMIDIBindings.xml");
#else
        oldFilePath = oldLocation.filePath(".MixxxMIDIBindings.xml");
#endif
        newFilePath = newLocation.filePath("MixxxMIDIBindings.xml");
        oldFile = new QFile(oldFilePath);
        if (oldFile->exists()) {
            qWarning() << "The MIDI mapping file format has changed in this version of Mixxx. You will need to reconfigure your MIDI controller. See the Wiki for full details on the new format.";
            if (oldFile->copy(newFilePath))
                oldFile->remove();
            else {
                if (oldFile->error()==14) qDebug() << errorText.arg("MIDI mapping", oldFilePath, newFilePath) << "The destination file already exists.";
                else qDebug() << errorText.arg("MIDI mapping", oldFilePath, newFilePath) << "Error #" << oldFile->error();
            }
        }
        // Tidy up
        delete oldFile;
#ifdef __WINDOWS__
        QFile::remove(oldLocation.filePath("MixxxMIDIDevice.xml")); // Obsolete file, so just delete it
#else
        QFile::remove(oldLocation.filePath(".MixxxMIDIDevice.xml")); // Obsolete file, so just delete it
#endif

#ifdef __WINDOWS__
        oldFilePath = oldLocation.filePath("mixxx.cfg");
#else
        oldFilePath = oldLocation.filePath(".mixxx.cfg");
#endif
        newFilePath = newLocation.filePath(SETTINGS_FILE);
        oldFile = new QFile(oldFilePath);
        if (oldFile->copy(newFilePath))
            oldFile->remove();
        else {
                if (oldFile->error()==14) qDebug() << errorText.arg("configuration", oldFilePath, newFilePath) << "The destination file already exists.";
                else qDebug() << errorText.arg("configuration", oldFilePath, newFilePath) << "Error #" << oldFile->error();
            }
        delete oldFile;

    }
    // Tidy up
    delete pre170Config;
    // End pre-1.7.0 code


/***************************************************************************
*                           Post-1.7.0 upgrade code
*
*   Add entries to the IF ladder below if anything needs to change from the
*   previous to the current version. This allows for incremental upgrades
*   in case a user upgrades from a few versions prior.
****************************************************************************/

    // Read the config file from home directory
    UserSettingsPointer config(new ConfigObject<ConfigValue>(
        QDir(settingsPath).filePath(SETTINGS_FILE)));

    QString configVersion = config->getValueString(ConfigKey("[Config]","Version"));

    if (configVersion.isEmpty()) {

#ifdef __APPLE__
        qDebug() << "Config version is empty, trying to read pre-1.9.0 config";
        // Try to read the config from the pre-1.9.0 final directory on OS X (we moved it in 1.9.0 final)
        QScopedPointer<QFile> oldConfigFile(new QFile(QDir::homePath().append("/").append(".mixxx/mixxx.cfg")));
        if (oldConfigFile->exists() && ! CmdlineArgs::Instance().getSettingsPathSet()) {
            qDebug() << "Found pre-1.9.0 config for OS X";
            // Note: We changed SETTINGS_PATH in 1.9.0 final on OS X so it must be hardcoded to ".mixxx" here for legacy.
            config = UserSettingsPointer(new ConfigObject<ConfigValue>(
                QDir::homePath().append("/.mixxx/mixxx.cfg")));
            // Just to be sure all files like logs and soundconfig go with mixxx.cfg
            // TODO(XXX) Trailing slash not needed anymore as we switches from String::append
            // to QDir::filePath elsewhere in the code. This is candidate for removal.
            CmdlineArgs::Instance().setSettingsPath(QDir::homePath().append("/.mixxx/"));
            configVersion = config->getValueString(ConfigKey("[Config]","Version"));
        }
        else {
#elif __WINDOWS__
        qDebug() << "Config version is empty, trying to read pre-1.12.0 config";
        // Try to read the config from the pre-1.12.0 final directory on Windows (we moved it in 1.12.0 final)
        QScopedPointer<QFile> oldConfigFile(new QFile(QDir::homePath().append("/Local Settings/Application Data/Mixxx/mixxx.cfg")));
        if (oldConfigFile->exists() && ! CmdlineArgs::Instance().getSettingsPathSet()) {
            qDebug() << "Found pre-1.12.0 config for Windows";
            // Note: We changed SETTINGS_PATH in 1.12.0 final on Windows so it must be hardcoded to "Local Settings/Application Data/Mixxx/" here for legacy.
            config = UserSettingsPointer(new ConfigObject<ConfigValue>(
                QDir::homePath().append("/Local Settings/Application Data/Mixxx/mixxx.cfg")));
            // Just to be sure all files like logs and soundconfig go with mixxx.cfg
            // TODO(XXX) Trailing slash not needed anymore as we switches from String::append
            // to QDir::filePath elsewhere in the code. This is candidate for removal.
            CmdlineArgs::Instance().setSettingsPath(QDir::homePath().append("/Local Settings/Application Data/Mixxx/"));
            configVersion = config->getValueString(ConfigKey("[Config]","Version"));
        }
        else {
#endif
            // This must have been the first run... right? :)
            qDebug() << "No version number in configuration file. Setting to" << MIXXX_VERSION;
            config->set(ConfigKey("[Config]","Version"), ConfigValue(MIXXX_VERSION));
            m_bFirstRun = true;
            return config;
#ifdef __APPLE__
        }
#elif __WINDOWS__
        }
#endif
    }

    // If it's already current, stop here
    if (configVersion == MIXXX_VERSION) {
        qDebug() << "Configuration file is at the current version" << MIXXX_VERSION;
        return config;
    }

    // Allows for incremental upgrades in case someone upgrades from a few versions prior
    // (I wish we could do a switch on a QString.)
    /*
    // Examples, since we didn't store the version number prior to v1.7.0
    if (configVersion.startsWith("1.6.0")) {
        qDebug() << "Upgrading from v1.6.0 to 1.6.1...";
        // Upgrade tasks go here
        configVersion = "1.6.1";
        config->set(ConfigKey("[Config]","Version"), ConfigValue("1.6.1"));
    }
    if (configVersion.startsWith("1.6.1")) {
        qDebug() << "Upgrading from v1.6.1 to 1.7.0...";
        // Upgrade tasks go here
        configVersion = "1.7.0";
        config->set(ConfigKey("[Config]","Version"), ConfigValue("1.7.0"));
    }
    */

    // We use the following blocks to detect if this is the first time
    // you've run the latest version of Mixxx. This lets us show
    // the promo tracks stats agreement stuff for all users that are
    // upgrading Mixxx.

    if (configVersion.startsWith("1.7")) {
        qDebug() << "Upgrading from v1.7.x...";
        // Upgrade tasks go here
        // Nothing to change, really
        configVersion = "1.8.0";
        config->set(ConfigKey("[Config]","Version"), ConfigValue("1.8.0"));
    }

    if (configVersion.startsWith("1.8.0~beta1") ||
        configVersion.startsWith("1.8.0~beta2")) {
        qDebug() << "Upgrading from v1.8.0~beta...";
        // Upgrade tasks go here
        configVersion = "1.8.0";
        config->set(ConfigKey("[Config]","Version"), ConfigValue("1.8.0"));
    }
    if (configVersion.startsWith("1.8") || configVersion.startsWith("1.9.0beta1")) {
        qDebug() << "Upgrading from" << configVersion << "...";
        // Upgrade tasks go here
#ifdef __APPLE__
        QString OSXLocation180 = QDir::homePath().append("/").append(".mixxx");
        QString OSXLocation190 = settingsPath;
        QDir newOSXDir(OSXLocation190);
        newOSXDir.mkpath(OSXLocation190);

        QList<QPair<QString, QString> > dirsToMove;
        dirsToMove.push_back(QPair<QString, QString>(OSXLocation180, OSXLocation190));
        dirsToMove.push_back(QPair<QString, QString>(OSXLocation180 + "/midi", OSXLocation190 + "midi"));
        dirsToMove.push_back(QPair<QString, QString>(OSXLocation180 + "/presets", OSXLocation190 + "presets"));

        QListIterator<QPair<QString, QString> > dirIt(dirsToMove);
        QPair<QString, QString> curPair;
        while (dirIt.hasNext())
        {
            curPair = dirIt.next();
            qDebug() << "Moving" << curPair.first << "to" << curPair.second;
            QDir oldSubDir(curPair.first);
            QDir newSubDir(curPair.second);
            newSubDir.mkpath(curPair.second); // Create the new destination directory

            QStringList contents = oldSubDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
            QStringListIterator it(contents);
            QString cur;
            // Iterate over all the files in the source directory and copy them to the dest dir.
            while (it.hasNext())
            {
                cur = it.next();
                QString src = curPair.first + "/" + cur;
                QString dest = curPair.second + "/" + cur;
                qDebug() << "Copying" << src << "to" << dest;
                if (!QFile::copy(src, dest))
                {
                    qDebug() << "Failed to move file during upgrade.";
                }
            }

            // Rename the old directory.
            newOSXDir.rename(OSXLocation180, OSXLocation180 + "-1.8");
        }
        // Reload the configuration file from the new location.
        // (We want to make sure we save to the new location...)
        config = UserSettingsPointer(new ConfigObject<ConfigValue>(
            QDir(settingsPath).filePath(SETTINGS_FILE)));
#endif
        configVersion = "1.9.0";
        config->set(ConfigKey("[Config]","Version"), ConfigValue("1.9.0"));
    }
    if (configVersion.startsWith("1.9") || configVersion.startsWith("1.10")) {
        qDebug() << "Upgrading from v1.9.x/1.10.x...";

        bool successful = true;

        qDebug() << "Copying midi/ to controllers/";
        QString midiPath = legacyUserPresetsPath(config);
        QString controllerPath = userPresetsPath(config);
        QDir oldDir(midiPath);
        QDir newDir(controllerPath);
        newDir.mkpath(controllerPath);  // create the new directory

        QStringList contents = oldDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
        QStringListIterator it(contents);
        QString cur;
        // Iterate over all the files in the source directory and copy them to the dest dir.
        while (it.hasNext()) {
            cur = it.next();
            if (newDir.exists(cur)) {
                qDebug() << cur << "already exists in"
                         << controllerPath << "Skipping.";
                continue;
            }
            QString src = oldDir.absoluteFilePath(cur);
            QString dest = newDir.absoluteFilePath(cur);
            qDebug() << "Copying" << src << "to" << dest;
            if (!QFile::copy(src, dest)) {
                qDebug() << "Failed to copy file during upgrade.";
                successful = false;
            }
        }

        bool reanalyze_choice = askReanalyzeBeats();
        BeatDetectionSettings bpmSettings(config);
        bpmSettings.setReanalyzeWhenSettingsChange(reanalyze_choice);

        if (successful) {
            qDebug() << "Upgrade Successful";
            configVersion = "1.11.0";
            config->set(ConfigKey("[Config]","Version"),
                        ConfigValue(configVersion));
        } else {
            qDebug() << "Upgrade Failed";
        }
    }

    if (configVersion.startsWith("1.11")) {
        qDebug() << "Upgrading from v1.11.x...";
        bool successful = false;
        {
            MixxxDb mixxxDb(config);
            const mixxx::DbConnectionPooler dbConnectionPooler(
                    mixxxDb.connectionPool());
            if (dbConnectionPooler.isPooling()) {
                QSqlDatabase dbConnection = mixxx::DbConnectionPooled(mixxxDb.connectionPool());
                DEBUG_ASSERT(dbConnection.isOpen());
                if (MixxxDb::initDatabaseSchema(dbConnection)) {
                    TrackCollection tc(config);
                    tc.connectDatabase(dbConnection);

                    // upgrade to the multi library folder settings
                    QString currentFolder = config->getValueString(PREF_LEGACY_LIBRARY_DIR);
                    // to migrate the DB just add the current directory to the new
                    // directories table
                    // NOTE(rryan): We don't have to ask for sandbox permission to this
                    // directory because the normal startup integrity check in Library will
                    // notice if we don't have permission and ask for access. Also, the
                    // Sandbox isn't setup yet at this point in startup because it relies on
                    // the config settings path and this function is what loads the config
                    // so it's not ready yet.
                    successful = tc.getDirectoryDAO().addDirectory(currentFolder);

                    tc.disconnectDatabase();
                }
            }
        }

        // ask for library rescan to activate cover art. We can later ask for
        // this variable when the library scanner is constructed.
        m_bRescanLibrary = askReScanLibrary();

        // Versions of mixxx until 1.11 had a hack that multiplied gain by 1/2,
        // which was compensation for another hack that set replaygain to a
        // default of 6.  We've now removed all of the hacks, so subtracting
        // 6 from everyone's replay gain should keep things consistent for
        // all users.
        int oldReplayGain = config->getValue(
                ConfigKey("[ReplayGain]", "InitialReplayGainBoost"), 6);
        int newReplayGain = math_max(-6, oldReplayGain - 6);
        config->set(ConfigKey("[ReplayGain]", "InitialReplayGainBoost"),
                    ConfigValue(newReplayGain));

        // if everything until here worked fine we can mark the configuration as
        // updated
        if (successful) {
            configVersion = MIXXX_VERSION;
            config->set(ConfigKey("[Config]","Version"), ConfigValue(MIXXX_VERSION));
        }
        else {
            qDebug() << "Upgrade failed!\n";
        }
    }

    if (configVersion.startsWith("1.12") ||
        configVersion.startsWith("2.0") ||
        configVersion.startsWith("2.1.0")) {
        // No special upgrade required, just update the value.
        configVersion = MIXXX_VERSION;
        config->set(ConfigKey("[Config]","Version"), ConfigValue(MIXXX_VERSION));
    }

    if (configVersion == MIXXX_VERSION) qDebug() << "Configuration file is now at the current version" << MIXXX_VERSION;
    else {
        qWarning() << "Configuration file is at version" << configVersion
                   << "instead of the current" << MIXXX_VERSION;
    }

    return config;
}
Example #22
0
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;
}
/** Stretch a specified buffer worth of audio using linear interpolation */
CSAMPLE * EngineBufferScaleLinear::do_scale(CSAMPLE* buf,
        unsigned long buf_size, int* samples_read) {
    float rate_add_new = m_dBaseRate;
    float rate_add_old = m_fOldBaseRate; //Smoothly interpolate to new playback rate
    float rate_add = rate_add_new;
    float rate_add_diff = rate_add_new - rate_add_old;

    //Update the old base rate because we only need to
    //interpolate/ramp up the pitch changes once.
    m_fOldBaseRate = m_dBaseRate;

    // Determine position in read_buffer to start from. (This is always 0 with
    // the new EngineBuffer implementation)

    int iRateLerpLength = static_cast<int>(buf_size);

    // Guard against buf_size == 0
    if (iRateLerpLength == 0)
        return buf;

    // We check for scratch condition in the public function, so this shouldn't
    // happen
    if (rate_add_new * rate_add_old < 0) {
        qDebug() << "ERROR: EBSL did not detect scratching correctly.";
    }

    // Special case -- no scaling needed!
    if (rate_add_old == 1.0 && rate_add_new == 1.0) {
        int samples_needed = buf_size;
        CSAMPLE* write_buf = buf;

        // Use up what's left of the internal buffer.
        int iNextSample = static_cast<int>(ceil(m_dNextSampleIndex)) * 2;
        if (iNextSample + 1 < buffer_int_size) {
            for (int i = iNextSample; samples_needed > 2 && i < buffer_int_size; i+=2) {
                *write_buf = buffer_int[i]; write_buf++;
                *write_buf = buffer_int[i+1]; write_buf++;
                samples_needed -= 2;
            }
        }

        // Protection against infinite read loops when (for example) we are
        // reading from a broken file.
        bool last_read_failed = false;

        // We need to repeatedly call the RAMAN because the RAMAN does not bend
        // over backwards to satisfy our request. It assumes you will continue
        // to call getNextSamples until you receive the number of samples you
        // wanted.
        while (samples_needed > 0) {
            int read_size = m_pReadAheadManager->getNextSamples(1.0, write_buf, samples_needed);
            samples_needed -= read_size;
            write_buf += read_size;

            if (read_size == 0) {
                if (last_read_failed) {
                    break;
                }
                last_read_failed = true;
            }
        }

        // Instead of counting how many samples we got from the internal buffer
        // and the RAMAN calls, just measure the difference between what we
        // requested and what we still need.
        int read_samples = buf_size - samples_needed;

        // Even though this code should not trigger for the special case in
        // getScaled for when the rate changes directions, the convention in the
        // rest of this method is that we increment samples_read rather than
        // assign it.
        *samples_read += read_samples;

        // Zero the remaining samples if we didn't fill them.
        SampleUtil::applyGain(write_buf, 0.0f, samples_needed);

        // update our class members so next time we need to scale it's ok. we do
        // blow away the fractional sample position here
        buffer_int_size = 0; // force buffer read
        m_dNextSampleIndex = 0;
        m_fPrevSample[0] = buf[read_samples-2];
        m_fPrevSample[1] = buf[read_samples-1];
        return buf;
    }

    // Simulate the loop to estimate how many samples we need
    double samples = 0;

    for (int j = 0; j < iRateLerpLength; j += 2) {
        rate_add = (rate_add_diff) / (float)iRateLerpLength * (float)j + rate_add_old;
        samples += fabs(rate_add);
    }

    rate_add = rate_add_new;
    double rate_add_abs = fabs(rate_add);

    //we're calculating mono samples, so divide remaining buffer by 2;
    samples += (rate_add_abs * ((float)(buf_size - iRateLerpLength)/2));
    long unscaled_samples_needed = floor(samples);

    //if the current position fraction plus the future position fraction
    //loops over 1.0, we need to round up
    if (m_dNextSampleIndex - floor(m_dNextSampleIndex) + samples - floor(samples) > 1.0) {
        unscaled_samples_needed++;
    }

    // Multiply by 2 because it is predicting mono rates, while we want a stereo
    // number of samples.
    unscaled_samples_needed *= 2;

    //0 is never the right answer
    unscaled_samples_needed = math_max(2,unscaled_samples_needed);

    bool last_read_failed = false;
    CSAMPLE prev_sample[2];
    CSAMPLE cur_sample[2];

    prev_sample[0] = 0;
    prev_sample[1] = 0;
    cur_sample[0] = 0;
    cur_sample[1] = 0;

    int i = 0;
    int screwups = 0;
    while (i < buf_size) {
        //shift indicies
        m_dCurSampleIndex = m_dNextSampleIndex;

        //we're going to be interpolating between two samples, a lower (prev)
        //and upper (cur) sample.  If the lower sample is off the end of the buffer,
        //load it from the saved globals

        if ((int)floor(m_dCurSampleIndex)*2+1 < buffer_int_size && m_dCurSampleIndex >= 0.0) {
            m_fPrevSample[0] = prev_sample[0] = buffer_int[(int)floor(m_dCurSampleIndex)*2];
            m_fPrevSample[1] = prev_sample[1] = buffer_int[(int)floor(m_dCurSampleIndex)*2+1];
        } else {
            prev_sample[0] = m_fPrevSample[0];
            prev_sample[1] = m_fPrevSample[1];
        }

        //Smooth any changes in the playback rate over iRateLerpLength
        //samples. This prevents the change from being discontinuous and helps
        //improve sound quality.
        if (i < iRateLerpLength) {
            rate_add = (float)i * (rate_add_diff) / (float)iRateLerpLength + rate_add_old;
            //rate_add = sigmoid_zero((float)i,(float)iRateLerpLength) * rate_add_diff + rate_add_old;
        } else {
            rate_add = rate_add_new;
        }

        // if we don't have enough samples, load some more
        while ((int)ceil(m_dCurSampleIndex)*2+1 >= buffer_int_size) {
            int old_bufsize = buffer_int_size;
            //qDebug() << "buffer" << buffer_count << rate_add_old << rate_add_new << rate_add << i << m_dCurSampleIndex << buffer_int_size << unscaled_samples_needed;
            //Q_ASSERT(unscaled_samples_needed > 0);
            if (unscaled_samples_needed == 0) {
                //qDebug() << "screwup" << m_dCurSampleIndex << (int)ceil(m_dCurSampleIndex)*2+1 << buffer_int_size;
                unscaled_samples_needed = 2;
                screwups++;
            }

            int samples_to_read = math_min(kiLinearScaleReadAheadLength,
                                           unscaled_samples_needed);

            buffer_int_size = m_pReadAheadManager->getNextSamples(
                rate_add_new == 0 ? rate_add_old : rate_add_new,
                buffer_int, samples_to_read);
            *samples_read += buffer_int_size;

            if (buffer_int_size == 0 && last_read_failed) {
                break;
            }
            last_read_failed = buffer_int_size == 0;

            unscaled_samples_needed -= buffer_int_size;
            //shift the index by the size of the old buffer
            m_dCurSampleIndex -= old_bufsize / 2;
            //fractions below 0 is ok, the ceil will bring it up to 0
            //this happens sometimes, somehow?
            //Q_ASSERT(m_dCurSampleIndex > -1.0);

            //not sure this actually does anything, but it seems to help
            if ((int)floor(m_dCurSampleIndex)*2 >= 0.0) {
                m_fPrevSample[0] = prev_sample[0] = buffer_int[(int)floor(m_dCurSampleIndex)*2];
                m_fPrevSample[1] = prev_sample[1] = buffer_int[(int)floor(m_dCurSampleIndex)*2+1];
            }
        }
        //I guess?
        if (last_read_failed)
            break;

        cur_sample[0] = buffer_int[(int)ceil(m_dCurSampleIndex)*2];
        cur_sample[1] = buffer_int[(int)ceil(m_dCurSampleIndex)*2+1];

        //rate_add was here

        //for the current index, what percentage is it between the previous and the next?
        CSAMPLE frac = m_dCurSampleIndex - floor(m_dCurSampleIndex);

        //Perform linear interpolation
        buf[i] = (float)prev_sample[0] + frac * ((float)cur_sample[0] - (float)prev_sample[0]);
        buf[i+1] = (float)prev_sample[1] + frac * ((float)cur_sample[1] - (float)prev_sample[1]);

        //at extremely low speeds, dampen the gain to hide pops and clicks
        //this does cause odd-looking linear waveforms that go to zero and back

        /*writer << QString("%1,%2,%3,%4\n").arg(buffer_count)
                                         .arg(buffer[i])
                                           .arg(prev_sample[0])
                                           .arg(cur_sample[0]);
        buffer_count++;*/

        //increment the index for the next loop
        m_dNextSampleIndex = m_dCurSampleIndex +
                (i < iRateLerpLength ? fabs(rate_add) : rate_add_abs);
        i+=2;
    }

    // If we broke out of the loop, zero the remaining samples
    // TODO(XXX) memset
    //for (; i < buf_size; i += 2) {
    //    buf[i] = 0.0f;
    //    buf[i+1] = 0.0f;
    //}

    //Q_ASSERT(i>=buf_size);
    SampleUtil::applyGain(&buf[i], 0.0f, buf_size-i);

    // It's possible that we will exit this function without having satisfied
    // this requirement. We may be trying to read past the end of the file.
    //Q_ASSERT(unscaled_samples_needed == 0);

    return buf;
}
void QtWaveformRendererSimpleSignal::draw(QPainter* painter, QPaintEvent* /*event*/){

    TrackPointer pTrack = m_waveformRenderer->getTrackInfo();
    if (!pTrack) {
        return;
    }

    const Waveform* waveform = pTrack->getWaveform();
    if (waveform == NULL) {
        return;
    }

    const int dataSize = waveform->getDataSize();
    if (dataSize <= 1) {
        return;
    }

    const WaveformData* data = waveform->data();
    if (data == NULL) {
        return;
    }

    painter->save();

    painter->setRenderHint(QPainter::Antialiasing);
    painter->resetTransform();

    float allGain(1.0);
    getGains(&allGain, NULL, NULL, NULL);

    double heightGain = allGain * (double)m_waveformRenderer->getHeight()/255.0;
    if (m_alignment == Qt::AlignTop) {
        painter->translate(0.0, 0.0);
        painter->scale(1.0, heightGain);
    } else if (m_alignment == Qt::AlignBottom) {
        painter->translate(0.0, m_waveformRenderer->getHeight());
        painter->scale(1.0, heightGain);
    } else {
        painter->translate(0.0, m_waveformRenderer->getHeight()/2.0);
        painter->scale(1.0, 0.5*heightGain);
    }

    //draw reference line
    if (m_alignment == Qt::AlignCenter) {
        painter->setPen(m_pColors->getAxesColor());
        painter->drawLine(0,0,m_waveformRenderer->getWidth(),0);
    }

    const double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize;
    const double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize;
    m_polygon.clear();
    m_polygon.reserve(2 * m_waveformRenderer->getWidth() + 2);
    m_polygon.append(QPointF(0.0, 0.0));

    const double offset = firstVisualIndex;

    // Represents the # of waveform data points per horizontal pixel.
    const double gain = (lastVisualIndex - firstVisualIndex) /
            (double)m_waveformRenderer->getWidth();

    //NOTE(vrince) Please help me find a better name for "channelSeparation"
    //this variable stand for merged channel ... 1 = merged & 2 = separated
    int channelSeparation = 2;
    if (m_alignment != Qt::AlignCenter)
        channelSeparation = 1;

    for (int channel = 0; channel < channelSeparation; ++channel) {
        int startPixel = 0;
        int endPixel = m_waveformRenderer->getWidth() - 1;
        int delta = 1;
        double direction = 1.0;

        //Reverse display for merged bottom channel
        if (m_alignment == Qt::AlignBottom)
            direction = -1.0;

        if (channel == 1) {
            startPixel = m_waveformRenderer->getWidth() - 1;
            endPixel = 0;
            delta = -1;
            direction = -1.0;

            // After preparing the first channel, insert the pivot point.
            m_polygon.append(QPointF(m_waveformRenderer->getWidth(), 0.0));
        }

        for (int x = startPixel;
                (startPixel < endPixel) ? (x <= endPixel) : (x >= endPixel);
                x += delta) {

            // TODO(rryan) remove before 1.11 release. I'm seeing crashes
            // sometimes where the pointIndex is very very large. It hasn't come
            // back since adding locking, but I'm leaving this so that we can
            // get some info about it before crashing. (The crash usually
            // corrupts a lot of the stack).
            if (m_polygon.size() > 2 * m_waveformRenderer->getWidth() + 2) {
                qDebug() << "OUT OF CONTROL"
                         << 2 * m_waveformRenderer->getWidth() + 2
                         << dataSize
                         << channel << m_polygon.size() << x;
            }

            // Width of the x position in visual indices.
            const double xSampleWidth = gain * x;

            // Effective visual index of x
            const double xVisualSampleIndex = xSampleWidth + offset;

            // Our current pixel (x) corresponds to a number of visual samples
            // (visualSamplerPerPixel) in our waveform object. We take the max of
            // all the data points on either side of xVisualSampleIndex within a
            // window of 'maxSamplingRange' visual samples to measure the maximum
            // data point contained by this pixel.
            double maxSamplingRange = gain / 2.0;

            // Since xVisualSampleIndex is in visual-samples (e.g. R,L,R,L) we want
            // to check +/- maxSamplingRange frames, not samples. To do this, divide
            // xVisualSampleIndex by 2. Since frames indices are integers, we round
            // to the nearest integer by adding 0.5 before casting to int.
            int visualFrameStart = int(xVisualSampleIndex / 2.0 - maxSamplingRange + 0.5);
            int visualFrameStop = int(xVisualSampleIndex / 2.0 + maxSamplingRange + 0.5);

            // If the entire sample range is off the screen then don't calculate a
            // point for this pixel.
            const int lastVisualFrame = dataSize / 2 - 1;
            if (visualFrameStop < 0 || visualFrameStart > lastVisualFrame) {
                m_polygon.append(QPointF(x, 0.0));
                continue;
            }

            // We now know that some subset of [visualFrameStart,
            // visualFrameStop] lies within the valid range of visual
            // frames. Clamp visualFrameStart/Stop to within [0,
            // lastVisualFrame].
            visualFrameStart = math_max(math_min(lastVisualFrame, visualFrameStart), 0);
            visualFrameStop = math_max(math_min(lastVisualFrame, visualFrameStop), 0);

            int visualIndexStart = visualFrameStart * 2 + channel;
            int visualIndexStop = visualFrameStop * 2 + channel;

            // if (x == m_waveformRenderer->getWidth() / 2) {
            //     qDebug() << "audioVisualRatio" << waveform->getAudioVisualRatio();
            //     qDebug() << "visualSampleRate" << waveform->getVisualSampleRate();
            //     qDebug() << "audioSamplesPerVisualPixel" << waveform->getAudioSamplesPerVisualSample();
            //     qDebug() << "visualSamplePerPixel" << visualSamplePerPixel;
            //     qDebug() << "xSampleWidth" << xSampleWidth;
            //     qDebug() << "xVisualSampleIndex" << xVisualSampleIndex;
            //     qDebug() << "maxSamplingRange" << maxSamplingRange;;
            //     qDebug() << "Sampling pixel " << x << "over [" << visualIndexStart << visualIndexStop << "]";
            // }

            unsigned char maxAll = 0;

            for (int i = visualIndexStart; i >= 0 && i < dataSize && i <= visualIndexStop;
                 i += channelSeparation) {
                const WaveformData& waveformData = *(data + i);
                unsigned char all = waveformData.filtered.all;
                maxAll = math_max(maxAll, all);
            }

            m_polygon.append(QPointF(x, (float)maxAll * direction));
        }
    }

    //If channel are not displayed separatly we nne to close the loop properly
    if (channelSeparation == 1) {
        m_polygon.append(QPointF(m_waveformRenderer->getWidth(), 0.0));
    }

    painter->setPen(m_borderPen);
    painter->setBrush(m_brush);

    painter->drawPolygon(&m_polygon[0], m_polygon.size());

    painter->restore();
}
Example #25
0
int CachingReader::read(int sample, int num_samples, CSAMPLE* buffer) {
    // Check for bad inputs
    if (sample % 2 != 0 || num_samples < 0 || !buffer) {
        QString temp = QString("Sample = %1").arg(sample);
        qDebug() << "CachingReader::read() invalid arguments sample:" << sample
                 << "num_samples:" << num_samples << "buffer:" << buffer;
        return 0;
    }

    // If asked to read 0 samples, don't do anything. (this is a perfectly
    // reasonable request that happens sometimes. If no track is loaded, don't
    // do anything.
    if (num_samples == 0 || m_readerStatus != TRACK_LOADED) {
        return 0;
    }

    // Process messages from the reader thread.
    process();

    // TODO: is it possible to move this code out of caching reader
    // and into enginebuffer?  It doesn't quite make sense here, although
    // it makes preroll completely transparent to the rest of the code

    //if we're in preroll...
    int zerosWritten = 0;
    if (sample < 0) {
        if (sample + num_samples <= 0) {
            //everything is zeros, easy
            memset(buffer, 0, sizeof(*buffer) * num_samples);
            return num_samples;
        } else {
            //some of the buffer is zeros, some is from the file
            memset(buffer, 0, sizeof(*buffer) * (0 - sample));
            buffer += (0 - sample);
            num_samples = sample + num_samples;
            zerosWritten = (0 - sample);
            sample = 0;
            //continue processing the rest of the chunks normally
        }
    }

    int start_sample = math_min(m_iTrackNumSamplesCallbackSafe,
                                sample);
    int start_chunk = chunkForSample(start_sample);
    int end_sample = math_min(m_iTrackNumSamplesCallbackSafe,
                              sample + num_samples - 1);
    int end_chunk = chunkForSample(end_sample);

    int samples_remaining = num_samples;
    int current_sample = sample;

    // Sanity checks
    if (start_chunk > end_chunk) {
        qDebug() << "CachingReader::read() bad chunk range to read ["
                 << start_chunk << end_chunk << "]";
        return 0;
    }

    for (int chunk_num = start_chunk; chunk_num <= end_chunk; chunk_num++) {
        Chunk* current = lookupChunk(chunk_num);

        // If the chunk is not in cache, then we must return an error.
        if (current == NULL) {
            // qDebug() << "Couldn't get chunk " << chunk_num
            //          << " in read() of [" << sample << "," << sample + num_samples
            //          << "] chunks " << start_chunk << "-" << end_chunk;

            // Something is wrong. Break out of the loop, that should fill the
            // samples requested with zeroes.
            Counter("CachingReader::read cache miss")++;
            break;
        }

        int chunk_start_sample = CachingReaderWorker::sampleForChunk(chunk_num);
        int chunk_offset = current_sample - chunk_start_sample;
        int chunk_remaining_samples = current->length - chunk_offset;

        // More sanity checks
        if (current_sample < chunk_start_sample || current_sample % 2 != 0) {
            qDebug() << "CachingReader::read() bad chunk parameters"
                     << "chunk_start_sample" << chunk_start_sample
                     << "current_sample" << current_sample;
            break;
        }

        // If we're past the start_chunk then current_sample should be
        // chunk_start_sample.
        if (start_chunk != chunk_num && chunk_start_sample != current_sample) {
            qDebug() << "CachingReader::read() bad chunk parameters"
                     << "chunk_num" << chunk_num
                     << "start_chunk" << start_chunk
                     << "chunk_start_sample" << chunk_start_sample
                     << "current_sample" << current_sample;
            break;
        }

        if (samples_remaining < 0) {
            qDebug() << "CachingReader::read() bad samples remaining"
                     << samples_remaining;
            break;
        }

        // It is completely possible that chunk_remaining_samples is less than
        // zero. If the caller is trying to read from beyond the end of the
        // file, then this can happen. We should tolerate it.
        int samples_to_read = math_max(0, math_min(samples_remaining,
                                       chunk_remaining_samples));

        // If we did not decide to read any samples from this chunk then that
        // means we have exhausted all the samples in the song.
        if (samples_to_read == 0) {
            break;
        }

        // samples_to_read should be non-negative and even
        if (samples_to_read < 0 || samples_to_read % 2 != 0) {
            qDebug() << "CachingReader::read() samples_to_read invalid"
                     << samples_to_read;
            break;
        }

        // TODO(rryan) do a test and see if using memcpy is faster than gcc
        // optimizing the for loop
        CSAMPLE *data = current->data + chunk_offset;
        memcpy(buffer, data, sizeof(*buffer) * samples_to_read);
        // for (int i=0; i < samples_to_read; i++) {
        //     buffer[i] = data[i];
        // }

        buffer += samples_to_read;
        current_sample += samples_to_read;
        samples_remaining -= samples_to_read;
    }

    // If we didn't supply all the samples requested, that probably means we're
    // at the end of the file, or something is wrong. Provide zeroes and pretend
    // all is well. The caller can't be bothered to check how long the file is.
    // TODO(XXX) memset
    for (int i=0; i<samples_remaining; i++) {
        buffer[i] = 0.0f;
    }
    samples_remaining = 0;

    if (samples_remaining != 0) {
        qDebug() << "CachingReader::read() did read all requested samples.";
    }
    return zerosWritten + num_samples - samples_remaining;
}
Example #26
0
void CachingReader::hintAndMaybeWake(const QVector<Hint>& hintList) {
    // If no file is loaded, skip.
    if (m_readerStatus != TRACK_LOADED) {
        return;
    }

    QVectorIterator<Hint> iterator(hintList);

    // To prevent every bit of code having to guess how many samples
    // forward it makes sense to keep in memory, the hinter can provide
    // either 0 for a forward hint or -1 for a backward hint. We should
    // be calculating an appropriate number of samples to go backward as
    // some function of the latency, but for now just leave this as a
    // constant. 2048 is a pretty good number of samples because 25ms
    // latency corresponds to 1102.5 mono samples and we need double
    // that for stereo samples.
    const int default_samples = 2048;

    QSet<int> chunksToFreshen;
    while (iterator.hasNext()) {
        // Copy, don't use reference.
        Hint hint = iterator.next();

        if (hint.length == 0) {
            hint.length = default_samples;
        } else if (hint.length == -1) {
            hint.sample -= default_samples;
            hint.length = default_samples;
            if (hint.sample < 0) {
                hint.length += hint.sample;
                hint.sample = 0;
            }
        }
        if (hint.length < 0) {
            qDebug() << "ERROR: Negative hint length. Ignoring.";
            continue;
        }
        int start_sample = math_max(0, math_min(
                                        m_iTrackNumSamplesCallbackSafe, hint.sample));
        int start_chunk = chunkForSample(start_sample);
        int end_sample = math_max(0, math_min(
                                      m_iTrackNumSamplesCallbackSafe, hint.sample + hint.length - 1));
        int end_chunk = chunkForSample(end_sample);

        for (int current = start_chunk; current <= end_chunk; ++current) {
            chunksToFreshen.insert(current);
        }
    }

    // For every chunk that the hints indicated, check if it is in the cache. If
    // any are not, then wake.
    bool shouldWake = false;
    QSetIterator<int> setIterator(chunksToFreshen);
    while (setIterator.hasNext()) {
        int chunk = setIterator.next();

        // This will cause the chunk to be 'freshened' in the cache. The
        // chunk will be moved to the end of the LRU list.
        if (!m_chunksBeingRead.contains(chunk) && lookupChunk(chunk) == NULL) {
            shouldWake = true;
            Chunk* pChunk = allocateChunkExpireLRU();
            if (pChunk == NULL) {
                qDebug() << "ERROR: Couldn't allocate spare Chunk to make ChunkReadRequest.";
                continue;
            }
            m_chunksBeingRead.insert(chunk, pChunk);
            ChunkReadRequest request;
            pChunk->chunk_number = chunk;
            request.chunk = pChunk;
            // qDebug() << "Requesting read of chunk" << chunk << "into" << pChunk;
            // qDebug() << "Requesting read into " << request.chunk->data;
            if (m_chunkReadRequestFIFO.write(&request, 1) != 1) {
                qDebug() << "ERROR: Could not submit read request for "
                         << chunk;
            }
            //qDebug() << "Checking chunk " << chunk << " shouldWake:" << shouldWake << " chunksToRead" << m_chunksToRead.size();
        }
    }

    // If there are chunks to be read, wake up.
    if (shouldWake) {
        m_pWorker->workReady();
    }
}
Example #27
0
void WLibraryTableView::setTrackTableRowHeight(int rowHeight) {
    QFontMetrics metrics(font());
    int fontHeightPx = metrics.height();
    verticalHeader()->setDefaultSectionSize(math_max(
                                                rowHeight, fontHeightPx));
}
Example #28
0
int pixmapIndexFromPercentage(double dPercentage, int numPixmaps) {
    // See WDisplay::getActivePixmapIndex for more info on this.
    int result = static_cast<int>(dPercentage * numPixmaps - 0.00001);
    result = math_min(numPixmaps - 1, math_max(0, result));
    return result;
}
void GLWaveformRendererSimpleSignal::draw(QPainter* painter, QPaintEvent* /*event*/) {
    TrackPointer pTrack = m_waveformRenderer->getTrackInfo();
    if (!pTrack) {
        return;
    }

    ConstWaveformPointer waveform = pTrack->getWaveform();
    if (waveform.isNull()) {
        return;
    }

    const int dataSize = waveform->getDataSize();
    if (dataSize <= 1) {
        return;
    }

    const WaveformData* data = waveform->data();
    if (data == NULL) {
        return;
    }

    double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize;
    double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize;
    double lineWidth = (1.0 / m_waveformRenderer->getVisualSamplePerPixel()) + 1.0;

    const int firstIndex = int(firstVisualIndex+0.5);
    firstVisualIndex = firstIndex - firstIndex%2;

    const int lastIndex = int(lastVisualIndex+0.5);
    lastVisualIndex = lastIndex + lastIndex%2;

    // Reset device for native painting
    painter->beginNativePainting();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    float allGain(1.0);
    getGains(&allGain, NULL, NULL, NULL);

    if (m_alignment == Qt::AlignCenter) {
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        if (m_orientation == Qt::Vertical) {
            glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
            glScalef(-1.0f, 1.0f, 1.0f);
        }
        glOrtho(firstVisualIndex, lastVisualIndex, -255.0, 255.0, -10.0, 10.0);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        glScalef(1.f, allGain, 1.f);

        glLineWidth(1.0);
        glDisable(GL_LINE_SMOOTH);

        //draw reference line
        glBegin(GL_LINES); {
            glColor4f(m_axesColor_r, m_axesColor_g,
                      m_axesColor_b, m_axesColor_a);
            glVertex2f(firstVisualIndex,0);
            glVertex2f(lastVisualIndex,0);
        }
        glEnd();

        glLineWidth(lineWidth);
        glEnable(GL_LINE_SMOOTH);

        glBegin(GL_LINES); {
            int firstIndex = math_max(static_cast<int>(firstVisualIndex), 0);
            int lastIndex = math_min(static_cast<int>(lastVisualIndex), dataSize);

            glColor4f(m_signalColor_r, m_signalColor_g, m_signalColor_b, 0.9);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxAll0 = data[visualIndex].filtered.all;
                GLfloat maxAll1 = data[visualIndex+1].filtered.all;
                glVertex2f(visualIndex, maxAll0);
                glVertex2f(visualIndex, -1.f * maxAll1);
            }
        }
        glEnd();
    } else { //top || bottom
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        if (m_orientation == Qt::Vertical) {
            glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
            glScalef(-1.0f, 1.0f, 1.0f);
        }
        if (m_alignment == Qt::AlignBottom || m_alignment == Qt::AlignRight)
            glOrtho(firstVisualIndex, lastVisualIndex, 0.0, 255.0, -10.0, 10.0);
        else
            glOrtho(firstVisualIndex, lastVisualIndex, 255.0, 0.0, -10.0, 10.0);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        glScalef(1.f, allGain, 1.f);

        glLineWidth(lineWidth);
        glEnable(GL_LINE_SMOOTH);

        glBegin(GL_LINES); {
            int firstIndex = math_max(static_cast<int>(firstVisualIndex), 0);
            int lastIndex = math_min(static_cast<int>(lastVisualIndex), dataSize);

            glColor4f(m_signalColor_r, m_signalColor_g, m_signalColor_b, 0.8);
            for (int visualIndex = firstIndex;
                    visualIndex < lastIndex;
                    visualIndex += 2) {

                GLfloat maxAll = math_max(
                        data[visualIndex].filtered.all,
                        data[visualIndex+1].filtered.all);
                glVertex2f(float(visualIndex), 0.f);
                glVertex2f(float(visualIndex), maxAll);
            }
        }
        glEnd();
    }
    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

    painter->endNativePainting();
}
Example #30
0
bool WOverviewHSV::drawNextPixmapPart() {
    ScopedTimer t("WOverviewHSV::drawNextPixmapPart");

    //qDebug() << "WOverview::drawNextPixmapPart() - m_waveform" << m_waveform;

    int currentCompletion;

    if (!m_pWaveform) {
        return false;
    }

    const int dataSize = m_pWaveform->getDataSize();
    if (dataSize == 0 ) {
        return false;
    }

    if (!m_pWaveformSourceImage) {
        // Waveform pixmap twice the height of the viewport to be scalable
        // by total_gain
        // We keep full range waveform data to scale it on paint
        m_pWaveformSourceImage = new QImage(dataSize / 2, 2 * 255,
                QImage::Format_ARGB32_Premultiplied);
        m_pWaveformSourceImage->fill(QColor(0,0,0,0).value());
    }

    // Always multiple of 2
    const int waveformCompletion = m_pWaveform->getCompletion();
    // Test if there is some new to draw (at least of pixel width)
    const int completionIncrement = waveformCompletion - m_actualCompletion;

    int visiblePixelIncrement = completionIncrement * width() / dataSize;
    if (completionIncrement < 2 || visiblePixelIncrement == 0) {
        return false;
    }

    if (!m_pWaveform->getMutex()->tryLock()) {
        return false;
    }

    const int nextCompletion = m_actualCompletion + completionIncrement;

    //qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:"
    // << nextCompletion
    // << "m_actualCompletion:" << m_actualCompletion
    // << "waveformCompletion:" << waveformCompletion
    // << "completionIncrement:" << completionIncrement;


    QPainter painter(m_pWaveformSourceImage);
    painter.translate(0.0,(double)m_pWaveformSourceImage->height()/2.0);

    // Get HSV of low color
    double h,s,v;
    m_signalColors.getLowColor().getHsvF(&h,&s,&v);

    QColor color;
    float lo, hi, total;

    unsigned char maxLow[2] = {0, 0};
    unsigned char maxHigh[2] = {0, 0};
    unsigned char maxMid[2] = {0, 0};
    unsigned char maxAll[2] = {0, 0};

    for (currentCompletion = m_actualCompletion;
            currentCompletion < nextCompletion; currentCompletion += 2) {
        maxAll[0] = m_pWaveform->getAll(currentCompletion);
        maxAll[1] = m_pWaveform->getAll(currentCompletion+1);
        if (maxAll[0] || maxAll[1]) {
            maxLow[0] = m_pWaveform->getLow(currentCompletion);
            maxLow[1] = m_pWaveform->getLow(currentCompletion+1);
            maxMid[0] = m_pWaveform->getMid(currentCompletion);
            maxMid[1] = m_pWaveform->getMid(currentCompletion+1);
            maxHigh[0] = m_pWaveform->getHigh(currentCompletion);
            maxHigh[1] = m_pWaveform->getHigh(currentCompletion+1);

            total = (maxLow[0] + maxLow[1] + maxMid[0] + maxMid[1] +
                     maxHigh[0] + maxHigh[1]) * 1.2;

            // Prevent division by zero
            if( total > 0 )
            {
                // Normalize low and high
                // (mid not need, because it not change the color)
                lo = (maxLow[0] + maxLow[1]) / total;
                hi = (maxHigh[0] + maxHigh[1]) / total;
            }
            else
                lo = hi = 0.0;

            // Set color
            color.setHsvF(h, 1.0-hi, 1.0-lo);

            painter.setPen(color);
            painter.drawLine(QPoint(currentCompletion / 2, -maxAll[0]),
                    QPoint(currentCompletion / 2, maxAll[1]));
        }
    }

    // Evaluate waveform ratio peak

    for (currentCompletion = m_actualCompletion;
            currentCompletion < nextCompletion; currentCompletion += 2) {
        m_waveformPeak = math_max(m_waveformPeak,
                (float)m_pWaveform->getAll(currentCompletion));
        m_waveformPeak = math_max(m_waveformPeak,
                (float)m_pWaveform->getAll(currentCompletion+1));
    }

    m_actualCompletion = nextCompletion;
    m_waveformImageScaled = QImage();
    m_diffGain = 0;

    // Test if the complete waveform is done
    if (m_actualCompletion >= dataSize - 2) {
        m_pixmapDone = true;
        //qDebug() << "m_waveformPeakRatio" << m_waveformPeak;
    }

    m_pWaveform->getMutex()->unlock();
    return true;
}