I'd like to graph a sound file. I saw QGraphicsScene an thought it might be perfect, but it seems like sound files have too many elements. In debug mode, the item load code just takes a long, long, long, time and pegs one CPU. In release mode it slams my computer so hard I have to power down to recover. (No mouse response, no or significantly delayed Ctrl-Alt-Del response)

My test file is a single channel WAV, 16-Bit, 44.1kHz, about 6 mins long. There are about 15 million samples (30MB file). I had hoped to draw lines between each sample and let the user zoom in and out and scroll left and right. But it would seem the item load cycle is just too intensive to actually pile in 15 Million line segments. Does that sound right, or is there something obviously wrong with the way I'm loading? Is there some faster way? Do I just need to load and unload items such that there is only a thousand or so at time in the scene and handle scrolling manually? (Doesn't seem like the spirit of the class)

If I'm going to handle scrolling and such manually, should I be looking at QRasterWindow instead?


Qt Code:
  1. MainWindow::MainWindow(QWidget *parent) :
  2. QMainWindow(parent),
  3. ui(new Ui::MainWindow)
  4. {
  5. ui->setupUi(this);
  6.  
  7. connect(&audio_decoder, &QAudioDecoder::bufferReady, this, &MainWindow::on_audio_decoded);
  8. connect(&audio_decoder, static_cast<void(QAudioDecoder::*)(QAudioDecoder::Error)>(&QAudioDecoder::error),
  9. this, &MainWindow::on_audio_decode_error);
  10. connect(&audio_decoder, &QAudioDecoder::stateChanged, this, &MainWindow::on_audio_decoder_stateChanged);
  11. }
  12.  
  13. MainWindow::~MainWindow()
  14. {
  15. delete ui;
  16. }
  17.  
  18. void MainWindow::on_actionLoad_Sound_File_triggered()
  19. {
  20. QString fileName = QFileDialog::getOpenFileName(this, "Open a sound file", QString(), "Sound Files (*.mp3 *.wav *.ogg)");
  21. if (!fileName.isEmpty())
  22. {
  23. if (QFile(fileName).exists())
  24. {
  25. samples.clear();
  26. audio_decoder.setSourceFilename(fileName);
  27. audio_decoder.start();
  28. }
  29. }
  30. }
  31.  
  32.  
  33. void MainWindow::on_audio_decoded()
  34. {
  35. while (audio_decoder.bufferAvailable())
  36. {
  37. QAudioBuffer audio_buffer = audio_decoder.read();
  38. QAudioFormat audio_format = audio_buffer.format();
  39.  
  40.  
  41. if (samples.empty())
  42. {
  43. for(int chan_idx = 0; chan_idx < audio_format.channelCount(); ++ chan_idx)
  44. {
  45. samples.push_back(QVector<qreal>());
  46. }
  47.  
  48. gs = new QGraphicsScene();
  49. }
  50.  
  51. switch(audio_format.sampleType())
  52. {
  53. case QAudioFormat::SignedInt:
  54. switch(audio_format.sampleSize())
  55. {
  56. case 8:
  57. NormalizeIntSamples<qint8>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<qint8>(), (QSysInfo::Endian)(audio_format.byteOrder()), samples);
  58. break;
  59. case 16:
  60. NormalizeIntSamples<qint16>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<qint16>(), (QSysInfo::Endian)(audio_format.byteOrder()), samples);
  61. break;
  62. case 32:
  63. NormalizeIntSamples<qint32>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<qint32>(), (QSysInfo::Endian)(audio_format.byteOrder()), samples);
  64. break;
  65. }
  66. break;
  67. case QAudioFormat::UnSignedInt:
  68. switch(audio_format.sampleSize())
  69. {
  70. case 8:
  71. NormalizeIntSamples<quint8>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<quint8>(), (QSysInfo::Endian)(audio_format.byteOrder()), samples);
  72. break;
  73. case 16:
  74. NormalizeIntSamples<quint16>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<quint16>(), (QSysInfo::Endian)(audio_format.byteOrder()), samples);
  75. break;
  76. case 32:
  77. NormalizeIntSamples<quint32>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<quint32>(), (QSysInfo::Endian)(audio_format.byteOrder()), samples);
  78. break;
  79. }
  80. break;
  81. case QAudioFormat::Float:
  82. switch(audio_format.sampleSize())
  83. {
  84. case 64:
  85. NormalizeFloatSamples<double>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<double>(), samples);
  86. break;
  87. case 32:
  88. NormalizeFloatSamples<float>(audio_buffer.frameCount(), audio_format.channelCount(), audio_buffer.constData<float>(), samples);
  89. break;
  90. }
  91. break;
  92. case QAudioFormat::Unknown:
  93. break;
  94. }
  95. }
  96. }
  97.  
  98. void MainWindow::on_audio_decode_error(QAudioDecoder::Error error)
  99. {
  100. QString decode_error = audio_decoder.errorString();
  101. QMessageBox::warning(this, "Audio Decode Error", decode_error);
  102.  
  103. }
  104.  
  105. void MainWindow::DrawSoundLines()
  106. {
  107. qDebug() << "Drawing.";
  108. QPen pen;
  109. pen.setWidth(0);
  110.  
  111.  
  112. for(int sample_idx = 1; sample_idx < samples[0].size(); ++sample_idx)
  113. {
  114. QLineF line(sample_idx, samples[0][sample_idx], sample_idx -1, samples[0][sample_idx - 1]);
  115. gs->addLine(line, pen);
  116. }
  117.  
  118. std::stringstream msg;
  119. msg << "Drew " << samples[0].size() << " samples";
  120. QMessageBox::information(this, "Audio Decoded", msg.str().c_str());
  121.  
  122. ui->graphicsView->setScene(gs);
  123. }
  124.  
  125. void MainWindow::on_audio_decoder_stateChanged(QAudioDecoder::State state)
  126. {
  127. if (state == QAudioDecoder::StoppedState)
  128. {
  129. //std::async(&MainWindow::DrawSoundLines,this);
  130. DrawSoundLines();
  131. }
  132. }
To copy to clipboard, switch view to plain text mode 


Qt Code:
  1. class MainWindow : public QMainWindow
  2. {
  3. Q_OBJECT
  4.  
  5. public:
  6. explicit MainWindow(QWidget *parent = 0);
  7. ~MainWindow();
  8.  
  9.  
  10.  
  11. private slots:
  12. void on_actionLoad_Sound_File_triggered();
  13.  
  14. void on_audio_decoded();
  15. void on_audio_decode_error(QAudioDecoder::Error error);
  16. void on_audio_decoder_stateChanged(QAudioDecoder::State state);
  17.  
  18. private:
  19. Ui::MainWindow *ui;
  20.  
  21. template<typename T> void NormalizeIntSamples(const int frame_count, const int channel_count, const T* sample_data, QSysInfo::Endian sample_byte_order, QVector<QVector<qreal> >& samples)
  22. {
  23. for(int frame_idx = 0; frame_idx < frame_count; ++frame_idx)
  24. {
  25. for (int channel_idx = 0; channel_idx < channel_count; ++channel_idx)
  26. {
  27. T sample = 0;
  28. switch (sample_byte_order)
  29. {
  30. case QSysInfo::BigEndian:
  31. sample = qFromBigEndian<T>(sample_data[frame_idx*channel_count + channel_idx]);
  32. case QSysInfo::LittleEndian:
  33. sample = qFromLittleEndian<T>(sample_data[frame_idx*channel_count + channel_idx]);
  34. }
  35. samples[channel_idx].push_back((qreal)sample);
  36. }
  37. }
  38. }
  39.  
  40.  
  41. template<typename T> void NormalizeFloatSamples(const int frame_count, const int channel_count, const T* sample_data, QVector<QVector<qreal> >& samples)
  42. {
  43. for(int frame_idx = 0; frame_idx < frame_count; ++frame_idx)
  44. {
  45. for (int channel_idx = 0; channel_idx < channel_count; ++channel_idx)
  46. {
  47. samples[channel_idx].push_back((qreal)(sample_data[frame_idx*channel_count + channel_idx]));
  48. }
  49. }
  50. }
  51.  
  52.  
  53. void DrawSoundLines();
  54.  
  55. QAudioDecoder audio_decoder;
  56. QVector<QVector<qreal> > samples;
  57.  
  58. };
To copy to clipboard, switch view to plain text mode