void AudioItem::createWaveform(bool left, bool right) { if ((left == true || right == true) && m_audio->getAudioDecoder() != NULL) { AudioDecoder *ad = m_audio->getAudioDecoder(); AudioParameters ap = ad->audioParameters(); // 1- find out how many samples have to be represented on a single pixel on a 1:1 time scale int sampleSize = ap.sampleSize(); int channels = ap.channels(); int oneSecondSamples = ap.sampleRate() * channels; int onePixelSamples = oneSecondSamples / 50; //qint32 maxValue = qPow(0xFF, sampleSize); qint32 maxValue = 0; if (left == true && right == true) maxValue = 0x7F << (8 * (sampleSize - 1)); else maxValue = 0x3F << (8 * (sampleSize - 1)); quint32 defaultDataLen = onePixelSamples * sampleSize; // 2- decode the whole file and fill a QPixmap with a sample block RMS value for each pixel qint64 dataRead = 1; unsigned char audioData[defaultDataLen * 4]; quint32 audioDataOffset = 0; m_preview = new QPixmap((50 * m_audio->totalDuration()) / 1000, 76); m_preview->fill(Qt::transparent); QPainter p(m_preview); int xpos = 0; qDebug() << "Audio duration: " << m_audio->totalDuration() << ", pixmap width: " << ((50 * m_audio->totalDuration()) / 1000) << ", maxValue: " << maxValue; qDebug() << "Samples per second: " << oneSecondSamples << ", for one pixel: " << onePixelSamples; while (dataRead) { quint32 tmpExceedData = 0; if (audioDataOffset < defaultDataLen) { dataRead = ad->read((char *)audioData + audioDataOffset, defaultDataLen * 2); if (dataRead > 0) { if(dataRead + audioDataOffset >= defaultDataLen) { tmpExceedData = dataRead + audioDataOffset - defaultDataLen; dataRead = defaultDataLen; } else { audioDataOffset = dataRead; continue; } } } else { dataRead = defaultDataLen; tmpExceedData = audioDataOffset - defaultDataLen; } if (dataRead > 0) { quint32 i = 0; // calculate the RMS value (peak) for this data block double rmsLeft = 0; double rmsRight = 0; bool done = false; while (!done) { if (left == true) { qint32 sampleVal = getSample(audioData, &i, sampleSize); rmsLeft += (sampleVal * sampleVal); } if (channels == 2) { if (right == true) { qint32 sampleVal = getSample(audioData, &i, sampleSize); rmsRight += (sampleVal * sampleVal); } else getSample(audioData, &i, sampleSize); // got to read it anyway and discard data } if (i >= dataRead / sampleSize) done = true; } quint32 divisor = (dataRead / sampleSize) / channels; if (left == true) rmsLeft = sqrt(rmsLeft / divisor); if (right == true) rmsRight = sqrt(rmsRight / divisor); // 3- Draw the actual waveform unsigned short lineHeightLeft = 0, lineHeightRight = 0; if (left == true) lineHeightLeft = (76 * rmsLeft) / maxValue; if (right == true) lineHeightRight = (76 * rmsRight) / maxValue; if (left == true && right == true) { if (lineHeightLeft > 1) p.drawLine(xpos, 19 - (lineHeightLeft / 2), xpos, 19 + (lineHeightLeft / 2)); else p.drawLine(xpos, 19, xpos + 1, 19); if (lineHeightRight > 1) p.drawLine(xpos, 51 - (lineHeightRight / 2), xpos, 51 + (lineHeightRight / 2)); else p.drawLine(xpos, 51, xpos + 1, 51); } else { unsigned short lineHeight = 0; if (left == true) lineHeight = lineHeightLeft; else lineHeight = lineHeightRight; if (lineHeight > 1) p.drawLine(xpos, 38 - (lineHeight / 2), xpos, 38 + (lineHeight / 2)); else p.drawLine(xpos, 38, xpos + 1, 38); //qDebug() << "Data read: " << dataRead << ", rms: " << rms << ", line height: " << lineHeight << ", xpos = " << xpos; } xpos++; if (tmpExceedData > 0) { //qDebug() << "Exceed data found: " << tmpExceedData; memmove(audioData, audioData + defaultDataLen, tmpExceedData); audioDataOffset = tmpExceedData; } else audioDataOffset = 0; } } //qDebug() << "Iterations done: " << xpos; ad->seek(0); } else // no preview selected. Delete pixmap { delete m_preview; m_preview = NULL; } update(); }
void AudioPlayer::rotateBufferThread(int offsetFrame) { char* tmpBuffer = nullptr; AudioDecoder* decoder = AudioDecoderManager::createDecoder(_audioCache->_fileFullPath.c_str()); do { BREAK_IF(decoder == nullptr || !decoder->open(_audioCache->_fileFullPath.c_str())); uint32_t framesRead = 0; const uint32_t framesToRead = _audioCache->_queBufferFrames; const uint32_t bufferSize = framesToRead * decoder->getBytesPerFrame(); tmpBuffer = (char*)malloc(bufferSize); memset(tmpBuffer, 0, bufferSize); if (offsetFrame != 0) { decoder->seek(offsetFrame); } ALint sourceState; ALint bufferProcessed = 0; bool needToExitThread = false; while (!_isDestroyed) { alGetSourcei(_alSource, AL_SOURCE_STATE, &sourceState); if (sourceState == AL_PLAYING) { alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &bufferProcessed); while (bufferProcessed > 0) { bufferProcessed--; if (_timeDirty) { _timeDirty = false; offsetFrame = _currTime * decoder->getSampleRate(); decoder->seek(offsetFrame); } else { _currTime += QUEUEBUFFER_TIME_STEP; if (_currTime > _audioCache->_duration) { if (_loop) { _currTime = 0.0f; } else { _currTime = _audioCache->_duration; } } } framesRead = decoder->readFixedFrames(framesToRead, tmpBuffer); if (framesRead == 0) { if (_loop) { decoder->seek(0); framesRead = decoder->readFixedFrames(framesToRead, tmpBuffer); } else { needToExitThread = true; break; } } ALuint bid; alSourceUnqueueBuffers(_alSource, 1, &bid); alBufferData(bid, _audioCache->_format, tmpBuffer, framesRead * decoder->getBytesPerFrame(), decoder->getSampleRate()); alSourceQueueBuffers(_alSource, 1, &bid); } } std::unique_lock<std::mutex> lk(_sleepMutex); if (_isDestroyed || needToExitThread) { break; } _sleepCondition.wait_for(lk,std::chrono::milliseconds(75)); } } while(false); ALOGV("Exit rotate buffer thread ..."); if (decoder != nullptr) { decoder->close(); } AudioDecoderManager::destroyDecoder(decoder); free(tmpBuffer); _isRotateThreadExited = true; ALOGV("%s exited.\n", __FUNCTION__); }