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); } }
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; } } }
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); } }
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); }
/*-------------------------------------------------------------------------* * 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; }
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; }
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); } }
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; }
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()); }
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)); } }
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); } } }
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; }
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(); }
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; }
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(); }
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(); }
// 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; }
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(); }
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; }
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(); } }
void WLibraryTableView::setTrackTableRowHeight(int rowHeight) { QFontMetrics metrics(font()); int fontHeightPx = metrics.height(); verticalHeader()->setDefaultSectionSize(math_max( rowHeight, fontHeightPx)); }
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(); }
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; }