I have a problem with paintEvent.
I have created the class
{
Q_OBJECT
public:
WaveDisplay
( QWidget* parent
= 0 );
virtual ~WaveDisplay();
void closeSoundFile
( QString soundName_
);
void setWave( SoundData* );
void playSound(int vol_ );
void stopSound();
...code...
public slots:
...code...
private slots:
void setElapsedTime( uint soundPos );
void fitToWindow();
signals:
void soundPositionChanged( uint );
void zoomFactorChanged( float );
protected:
/* paint event */
...more code...
private:
void updateWave();
void drawStandardPCM16(QPainter&, int);
void drawStandardPCM8(QPainter&);
SoundData* m_wave;
...more code...
};
class WaveDisplay : public QAbstractScrollArea
{
Q_OBJECT
public:
WaveDisplay( QWidget* parent = 0 );
virtual ~WaveDisplay();
void closeSoundFile( QString soundName_ );
void setWave( SoundData* );
void playSound(int vol_ );
void stopSound();
...code...
public slots:
...code...
private slots:
void setElapsedTime( uint soundPos );
void fitToWindow();
signals:
void soundPositionChanged( uint );
void zoomFactorChanged( float );
protected:
/* paint event */
virtual void paintEvent( QPaintEvent* event );
...more code...
private:
void updateWave();
void drawStandardPCM16(QPainter&, int);
void drawStandardPCM8(QPainter&);
QPixmap m_waveCachePixmap;
SoundData* m_wave;
...more code...
};
To copy to clipboard, switch view to plain text mode
and I mainly would implement the paintEvent.
So I changed a bit the paintEvent of my last class
/*****************************************/
/* Paint event */
/*****************************************/
{
// Update the ev->rect area with the wave from the rendered QPixmap here
if( m_waveCachePixmap.isNull() )
{
updateWave();
}
p.
setRenderHint( QPainter::Antialiasing,
true );
// wave
p.drawPixmap( 0, 0, m_waveCachePixmap );
// timeline
pen.setColor(Qt::darkRed);
pen.setStyle(Qt::SolidLine);
p.setPen( pen );
p.drawLine( m_CurrentTimePosition, 0, m_CurrentTimePosition, height() );
// elapsed time
setElapsedTime( m_CurrentSoundPosition );
font.setPointSize( 24 );
p.setFont( font );
p.
drawText( QPoint(20,
50), m_elapsedTimeString
);
}
/*****************************************/
/* Paint event */
/*****************************************/
void WaveDisplay::paintEvent( QPaintEvent* pe )
{
QAbstractScrollArea::paintEvent( pe );
// Update the ev->rect area with the wave from the rendered QPixmap here
if( m_waveCachePixmap.isNull() )
{
updateWave();
}
QPainter p( viewport() );
p.setRenderHint( QPainter::Antialiasing, true );
QPen pen = QPen( Qt::darkRed, 1 );
// wave
p.drawPixmap( 0, 0, m_waveCachePixmap );
// timeline
pen.setColor(Qt::darkRed);
pen.setStyle(Qt::SolidLine);
p.setPen( pen );
p.drawLine( m_CurrentTimePosition, 0, m_CurrentTimePosition, height() );
// elapsed time
setElapsedTime( m_CurrentSoundPosition );
QFont font = p.font();
font.setPointSize( 24 );
p.setFont( font );
p.drawText( QPoint(20, 50), m_elapsedTimeString );
}
To copy to clipboard, switch view to plain text mode
and the updateWave
void WaveDisplay::updateWave()
{
int h = this->height();
int w = this->width();
// I want another width and height here.
// Also, you might to clear the cache at resize events
//p.setRenderHint( QPainter::Antialiasing, true );
p.fillRect( temp.rect(), bgColor );
// timeline
pen.setWidthF( 1.0 );
pen.setStyle(Qt::SolidLine);
p.setPen(pen);
p.drawLine( 0.0, 0.0, (qreal)w, 0.0 );
// Draw the waveform or load no data image
if( m_wave )
{
pen.setColor( wfColor ); // darkCyan solid line, 1 pixels wide
pen.setStyle(Qt::SolidLine);
p.setPen( pen );
void* soundStream = m_wave->getSoundStream();
unsigned int numSamples = m_wave->getSamples();
int channels = m_wave->getChannels();
FMOD_SOUND_TYPE soundType = m_wave->getSoundType();
FMOD_SOUND_FORMAT soundFormat = m_wave->getSoundFormat();
unsigned int dur = m_wave->getLengthMSeconds();
float* minValue = new float[channels];
float* maxValue = new float[channels];
//int sampleSize = channels * ( soundFormat == PCM8 ) ? 1 : 2;
int sampleSize = channels; //<-- questo concetto non funziona, io lo chiamerei step
int maxHeight = (h / 2) / channels;
int increment = (h / 2);
switch( soundFormat )
{
case PCM8: //8 bits mono
{
//..to implement
break;
}
case PCM16: //16 bits stereo (LRLRLRLR...LRLR)
{
drawStandardPCM16( p, 1 );
qreal samplePerPixel = m_wave->getSamples() / width();
// qua disegno le tacchette temporali
if(!m_timePosition.isEmpty())
{
pen.setColor( Qt::black );
p.setPen(pen);
QList<uint>::const_iterator it;
qreal widgetPos = 0;
for( it = m_timePosition.constBegin(); it != m_timePosition.constEnd(); ++it )
{
/* trasformo il valore temporale in valore di coordinata x del widget */
widgetPos = *it / samplePerPixel;
p.drawLine( widgetPos, 0, widgetPos, 10 );// from 0 to 10 for the seconds
}
}
//markers
if( !m_ListaMarcatori.isEmpty() )
{
pen.setColor(Qt::yellow);
pen.setStyle( Qt::DashLine );
p.setPen(pen);
QList<uint>::const_iterator it;
qreal widgetPos = 0;
for(it = m_ListaMarcatori.constBegin(); it != m_ListaMarcatori.constEnd(); ++it)
{
/* from time value to widget value */
widgetPos = *it / samplePerPixel;
p.drawLine( widgetPos, 0, widgetPos, height() );
}
}
break;
}
default:
{
qDebug("Undefined sound type");
break;
}
}
}
else
{
p.
drawImage( rect
(),
QImage(":/images/logo/nodata.jpg") );
}
m_waveCachePixmap = temp;
}
void WaveDisplay::updateWave()
{
int h = this->height();
int w = this->width();
// I want another width and height here.
// Also, you might to clear the cache at resize events
QPixmap temp = QPixmap( w, h );
QPainter p( &temp );
//p.setRenderHint( QPainter::Antialiasing, true );
p.fillRect( temp.rect(), bgColor );
// timeline
QPen pen(Qt::black);
pen.setWidthF( 1.0 );
pen.setStyle(Qt::SolidLine);
p.setPen(pen);
p.drawLine( 0.0, 0.0, (qreal)w, 0.0 );
// Draw the waveform or load no data image
if( m_wave )
{
pen.setColor( wfColor ); // darkCyan solid line, 1 pixels wide
pen.setStyle(Qt::SolidLine);
p.setPen( pen );
void* soundStream = m_wave->getSoundStream();
unsigned int numSamples = m_wave->getSamples();
int channels = m_wave->getChannels();
FMOD_SOUND_TYPE soundType = m_wave->getSoundType();
FMOD_SOUND_FORMAT soundFormat = m_wave->getSoundFormat();
unsigned int dur = m_wave->getLengthMSeconds();
float* minValue = new float[channels];
float* maxValue = new float[channels];
//int sampleSize = channels * ( soundFormat == PCM8 ) ? 1 : 2;
int sampleSize = channels; //<-- questo concetto non funziona, io lo chiamerei step
int maxHeight = (h / 2) / channels;
int increment = (h / 2);
switch( soundFormat )
{
case PCM8: //8 bits mono
{
//..to implement
break;
}
case PCM16: //16 bits stereo (LRLRLRLR...LRLR)
{
drawStandardPCM16( p, 1 );
qreal samplePerPixel = m_wave->getSamples() / width();
// qua disegno le tacchette temporali
if(!m_timePosition.isEmpty())
{
pen.setColor( Qt::black );
p.setPen(pen);
QList<uint>::const_iterator it;
qreal widgetPos = 0;
for( it = m_timePosition.constBegin(); it != m_timePosition.constEnd(); ++it )
{
/* trasformo il valore temporale in valore di coordinata x del widget */
widgetPos = *it / samplePerPixel;
p.drawLine( widgetPos, 0, widgetPos, 10 );// from 0 to 10 for the seconds
}
}
//markers
if( !m_ListaMarcatori.isEmpty() )
{
pen.setColor(Qt::yellow);
pen.setStyle( Qt::DashLine );
p.setPen(pen);
QList<uint>::const_iterator it;
qreal widgetPos = 0;
for(it = m_ListaMarcatori.constBegin(); it != m_ListaMarcatori.constEnd(); ++it)
{
/* from time value to widget value */
widgetPos = *it / samplePerPixel;
p.drawLine( widgetPos, 0, widgetPos, height() );
}
}
break;
}
default:
{
qDebug("Undefined sound type");
break;
}
}
}
else
{
p.drawImage( rect(), QImage(":/images/logo/nodata.jpg") );
}
m_waveCachePixmap = temp;
}
To copy to clipboard, switch view to plain text mode
and finally the drawStandardPCM16
/************************************************************************/
/* drawStandardPCM16: spostarlo nel sounddata */
/************************************************************************/
void WaveDisplay::drawStandardPCM16( QPainter& painter, int numSamples )
{
int h = height();
int w = width();
// Calculate magical constants that should be left untouched unless dealing with
// other variants of PCM-Streams.
int channels = m_wave->getChannels();
int bits = m_wave->getBits();
int pcm_length = m_wave->getPcmLength();
int freq = m_wave->getFrequency();
int sample_size = channels * bits / 8;
int samples = pcm_length / sample_size; //pcm_length bytes totali del sound
int maxHeight = (h / 2) / channels;
int increment = (h / 2);
float modifier = (float)maxHeight / 0x0000FFFF * 2;
int* minValues = new int[channels];
int* maxValues = new int[channels];
//Adjust these values to obtain a different drawing scheme. Offset defines the initial sample
//where to begin drawing (it takes into account channel, sample size, bit size etc). maxSamplesOnscreen
//defines the amount of samples displayed on the screen.
int startOffset = 0;
int maxSamplesOnscreen = samples;
// Calculate the starting position in the stream where start fetching our data. And calculate
// the width between samples. By default the width truncated to the nearest integer for drawing.
// Note: doubles are needed for this calculation due floating point in-precision. Floating point
// operations have the same speed either so it's quite valid.
char* stream = (char*)m_wave->getSoundStream() + (startOffset * sample_size);
double samplesInc = 1.0f / ( maxSamplesOnscreen / (double)w);
double samplesCur = 0.0f;
//Correct the maximum samples displayed on the screen, if this step is not done we'ld be
//drawing outside the boundaries of the data stream.
maxSamplesOnscreen = (samples - startOffset) > maxSamplesOnscreen ? maxSamplesOnscreen : (samples - startOffset);
"Creating waveform...",
"Abort process",
0,
maxSamplesOnscreen,
this);
if( !m_soundLoaded )
progress->setWindowModality( Qt::WindowModal );
//Iterate over each sample that lies withing the lower and upper boundaries that we precomputed. For
//every sample we draw the maximum value, and minimum value. Store the value in a temp. array and process that
//array once we detect our x is changed.
for( int i = startOffset, maxI = i + maxSamplesOnscreen, oldX = 0, x = 0; i < maxI; i++, stream += sample_size, samplesCur += samplesInc, x = samplesCur)
{
if( i % (60 * freq) == 0 ) //ogni minuto
{
// aggiungo un marcatore del tempo alla lista
//m_timePosition.append(i);// qua dovrei anche aggiungere i minuti, magari con una map
m_timePosition.append(i);// qua dovrei anche aggiungere i minuti, magari con una map
}
//Analyze the raw stream of data to determine the min and max amplitude of the sound.
for( int k = 0, j = 0; k < channels; k++, j += 2 )
{
signed short* value1 = reinterpret_cast<signed short*>(stream + j);
int y = *value1 * modifier;
maxValues[k] = y > maxValues[k] ? y : maxValues[k];
minValues[k] = y < minValues[k] ? y : minValues[k];
}
//Early out of x is the same
if( oldX == x)
continue;
oldX = x;
if( !m_soundLoaded )
progress->setValue(i);
//Draw the peaks from midpoint to upper peak, and from midpoint to lower peaks
for( int k = 0, startY = maxHeight; k < channels; k++ )
{
painter.
drawLine( midPoint,
QPoint(x, startY
+ maxValues
[k
]) );
painter.
drawLine( midPoint,
QPoint(x, startY
+ minValues
[k
]) );
maxValues[k] = minValues[k] = 0;
startY += increment;
}
}
if( !m_soundLoaded )
{
progress->setValue( maxSamplesOnscreen );
progress->deleteLater();
}
//Draw a mid-based line for each channel around, so there is always 'signal displayed' used against
//double point in-precision.
for( int k = 0, startY = maxHeight; k < channels; k++ )
{
startY += increment;
}
//Clean up the outline tables for min and max values.
delete minValues;
delete maxValues;
m_soundLoaded = true;
}
/************************************************************************/
/* drawStandardPCM16: spostarlo nel sounddata */
/************************************************************************/
void WaveDisplay::drawStandardPCM16( QPainter& painter, int numSamples )
{
int h = height();
int w = width();
// Calculate magical constants that should be left untouched unless dealing with
// other variants of PCM-Streams.
int channels = m_wave->getChannels();
int bits = m_wave->getBits();
int pcm_length = m_wave->getPcmLength();
int freq = m_wave->getFrequency();
int sample_size = channels * bits / 8;
int samples = pcm_length / sample_size; //pcm_length bytes totali del sound
int maxHeight = (h / 2) / channels;
int increment = (h / 2);
float modifier = (float)maxHeight / 0x0000FFFF * 2;
int* minValues = new int[channels];
int* maxValues = new int[channels];
//Adjust these values to obtain a different drawing scheme. Offset defines the initial sample
//where to begin drawing (it takes into account channel, sample size, bit size etc). maxSamplesOnscreen
//defines the amount of samples displayed on the screen.
int startOffset = 0;
int maxSamplesOnscreen = samples;
// Calculate the starting position in the stream where start fetching our data. And calculate
// the width between samples. By default the width truncated to the nearest integer for drawing.
// Note: doubles are needed for this calculation due floating point in-precision. Floating point
// operations have the same speed either so it's quite valid.
char* stream = (char*)m_wave->getSoundStream() + (startOffset * sample_size);
double samplesInc = 1.0f / ( maxSamplesOnscreen / (double)w);
double samplesCur = 0.0f;
//Correct the maximum samples displayed on the screen, if this step is not done we'ld be
//drawing outside the boundaries of the data stream.
maxSamplesOnscreen = (samples - startOffset) > maxSamplesOnscreen ? maxSamplesOnscreen : (samples - startOffset);
QProgressDialog *progress = new QProgressDialog(
"Creating waveform...",
"Abort process",
0,
maxSamplesOnscreen,
this);
if( !m_soundLoaded )
progress->setWindowModality( Qt::WindowModal );
//Iterate over each sample that lies withing the lower and upper boundaries that we precomputed. For
//every sample we draw the maximum value, and minimum value. Store the value in a temp. array and process that
//array once we detect our x is changed.
for( int i = startOffset, maxI = i + maxSamplesOnscreen, oldX = 0, x = 0; i < maxI; i++, stream += sample_size, samplesCur += samplesInc, x = samplesCur)
{
if( i % (60 * freq) == 0 ) //ogni minuto
{
// aggiungo un marcatore del tempo alla lista
//m_timePosition.append(i);// qua dovrei anche aggiungere i minuti, magari con una map
m_timePosition.append(i);// qua dovrei anche aggiungere i minuti, magari con una map
}
//Analyze the raw stream of data to determine the min and max amplitude of the sound.
for( int k = 0, j = 0; k < channels; k++, j += 2 )
{
signed short* value1 = reinterpret_cast<signed short*>(stream + j);
int y = *value1 * modifier;
maxValues[k] = y > maxValues[k] ? y : maxValues[k];
minValues[k] = y < minValues[k] ? y : minValues[k];
}
//Early out of x is the same
if( oldX == x)
continue;
oldX = x;
if( !m_soundLoaded )
progress->setValue(i);
//Draw the peaks from midpoint to upper peak, and from midpoint to lower peaks
for( int k = 0, startY = maxHeight; k < channels; k++ )
{
QPoint midPoint = QPoint(x, startY);
painter.drawLine( midPoint, QPoint(x, startY + maxValues[k]) );
painter.drawLine( midPoint, QPoint(x, startY + minValues[k]) );
maxValues[k] = minValues[k] = 0;
startY += increment;
}
}
if( !m_soundLoaded )
{
progress->setValue( maxSamplesOnscreen );
progress->deleteLater();
}
//Draw a mid-based line for each channel around, so there is always 'signal displayed' used against
//double point in-precision.
for( int k = 0, startY = maxHeight; k < channels; k++ )
{
painter.drawLine( QPoint(0, startY), QPoint(w, startY) );
startY += increment;
}
//Clean up the outline tables for min and max values.
delete minValues;
delete maxValues;
m_soundLoaded = true;
}
To copy to clipboard, switch view to plain text mode
In brief if no sound is loaded I show an image, if a sound is loaded I draw the waveform of the sound.
If the sound is not loaded It correctly display the "NO SOUND" image. When I load a sound It doesn't draw nothing.
I paint in a cache in the updateWave and I draw the cache content in the paintEvent but nothing is displayed.
Where my code is wrong? Is the first time that I derive from QAbstractScrollArea and for me is not easy.
I need help to improve my knoledge please.
Regards
Bookmarks