Changeset 769 for trunk/examples/multimedia/audiooutput/audiooutput.cpp
- Timestamp:
- Aug 2, 2010, 9:27:30 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk
- Property svn:mergeinfo changed
/branches/vendor/nokia/qt/4.6.3 (added) merged: 768 /branches/vendor/nokia/qt/current merged: 767 /branches/vendor/nokia/qt/4.6.2 removed
- Property svn:mergeinfo changed
-
trunk/examples/multimedia/audiooutput/audiooutput.cpp
r651 r769 45 45 #include <QAudioOutput> 46 46 #include <QAudioDeviceInfo> 47 #include <QtCore/qmath.h> 48 #include <QtCore/qendian.h> 47 49 #include "audiooutput.h" 48 50 49 #ifndef M_PI 50 #define M_PI 3.14159265358979323846 51 #endif 52 53 #define SECONDS 1 54 #define FREQ 600 55 #define SYSTEM_FREQ 44100 56 57 Generator::Generator(QObject *parent) 58 :QIODevice( parent ) 59 { 60 finished = false; 61 buffer = new char[SECONDS*SYSTEM_FREQ*4+1000]; 62 t=buffer; 63 len=fillData(t,FREQ,SECONDS); /* mono FREQHz sine */ 64 pos = 0; 65 total = len; 51 const QString AudioTest::PushModeLabel(tr("Enable push mode")); 52 const QString AudioTest::PullModeLabel(tr("Enable pull mode")); 53 const QString AudioTest::SuspendLabel(tr("Suspend playback")); 54 const QString AudioTest::ResumeLabel(tr("Resume playback")); 55 56 const int DurationSeconds = 1; 57 const int ToneFrequencyHz = 600; 58 const int DataFrequencyHz = 44100; 59 const int BufferSize = 32768; 60 61 62 Generator::Generator(const QAudioFormat &format, 63 qint64 durationUs, 64 int frequency, 65 QObject *parent) 66 : QIODevice(parent) 67 , m_pos(0) 68 { 69 generateData(format, durationUs, frequency); 66 70 } 67 71 68 72 Generator::~Generator() 69 73 { 70 delete [] buffer; 74 71 75 } 72 76 … … 78 82 void Generator::stop() 79 83 { 84 m_pos = 0; 80 85 close(); 81 86 } 82 87 83 int Generator::putShort(char *t, unsigned int value) 84 { 85 *(unsigned char *)(t++)=value&255; 86 *(unsigned char *)(t)=(value/256)&255; 87 return 2; 88 } 89 90 int Generator::fillData(char *start, int frequency, int seconds) 91 { 92 int i, len=0; 93 int value; 94 for(i=0; i<seconds*SYSTEM_FREQ; i++) { 95 value=(int)(32767.0*sin(2.0*M_PI*((double)(i))*(double)(frequency)/SYSTEM_FREQ)); 96 putShort(start, value); 97 start += 4; 98 len+=2; 99 } 100 return len; 101 } 102 103 qint64 Generator::readData(char *data, qint64 maxlen) 104 { 105 int len = maxlen; 106 if (len > 16384) 107 len = 16384; 108 109 if (len < (SECONDS*SYSTEM_FREQ*2)-pos) { 110 // Normal 111 memcpy(data,t+pos,len); 112 pos+=len; 113 return len; 114 } else { 115 // Whats left and reset to start 116 qint64 left = (SECONDS*SYSTEM_FREQ*2)-pos; 117 memcpy(data,t+pos,left); 118 pos=0; 119 return left; 120 } 88 void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int frequency) 89 { 90 const int channelBytes = format.sampleSize() / 8; 91 const int sampleBytes = format.channels() * channelBytes; 92 93 qint64 length = (format.frequency() * format.channels() * (format.sampleSize() / 8)) 94 * durationUs / 100000; 95 96 Q_ASSERT(length % sampleBytes == 0); 97 Q_UNUSED(sampleBytes) // suppress warning in release builds 98 99 m_buffer.resize(length); 100 unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data()); 101 int sampleIndex = 0; 102 103 while (length) { 104 const qreal x = qSin(2 * M_PI * frequency * qreal(sampleIndex % format.frequency()) / format.frequency()); 105 for (int i=0; i<format.channels(); ++i) { 106 if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) { 107 const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255); 108 *reinterpret_cast<quint8*>(ptr) = value; 109 } else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) { 110 const qint8 value = static_cast<qint8>(x * 127); 111 *reinterpret_cast<quint8*>(ptr) = value; 112 } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::UnSignedInt) { 113 quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535); 114 if (format.byteOrder() == QAudioFormat::LittleEndian) 115 qToLittleEndian<quint16>(value, ptr); 116 else 117 qToBigEndian<quint16>(value, ptr); 118 } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) { 119 qint16 value = static_cast<qint16>(x * 32767); 120 if (format.byteOrder() == QAudioFormat::LittleEndian) 121 qToLittleEndian<qint16>(value, ptr); 122 else 123 qToBigEndian<qint16>(value, ptr); 124 } 125 126 ptr += channelBytes; 127 length -= channelBytes; 128 } 129 ++sampleIndex; 130 } 131 } 132 133 qint64 Generator::readData(char *data, qint64 len) 134 { 135 qint64 total = 0; 136 while (len - total) { 137 const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total); 138 memcpy(data, m_buffer.constData() + m_pos, chunk); 139 m_pos = (m_pos + chunk) % m_buffer.size(); 140 total += chunk; 141 } 142 return total; 121 143 } 122 144 … … 129 151 } 130 152 153 qint64 Generator::bytesAvailable() const 154 { 155 return m_buffer.size() + QIODevice::bytesAvailable(); 156 } 157 131 158 AudioTest::AudioTest() 132 { 133 QWidget *window = new QWidget; 134 QVBoxLayout* layout = new QVBoxLayout; 135 136 deviceBox = new QComboBox(this); 159 : m_pullTimer(new QTimer(this)) 160 , m_modeButton(0) 161 , m_suspendResumeButton(0) 162 , m_deviceBox(0) 163 , m_device(QAudioDeviceInfo::defaultOutputDevice()) 164 , m_generator(0) 165 , m_audioOutput(0) 166 , m_output(0) 167 , m_buffer(BufferSize, 0) 168 { 169 initializeWindow(); 170 initializeAudio(); 171 } 172 173 void AudioTest::initializeWindow() 174 { 175 QScopedPointer<QWidget> window(new QWidget); 176 QScopedPointer<QVBoxLayout> layout(new QVBoxLayout); 177 178 m_deviceBox = new QComboBox(this); 137 179 foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) 138 deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); 139 connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); 140 layout->addWidget(deviceBox); 141 142 button = new QPushButton(this); 143 button->setText(tr("Click for Push Mode")); 144 connect(button,SIGNAL(clicked()),SLOT(toggle())); 145 layout->addWidget(button); 146 147 button2 = new QPushButton(this); 148 button2->setText(tr("Click To Suspend")); 149 connect(button2,SIGNAL(clicked()),SLOT(togglePlay())); 150 layout->addWidget(button2); 151 152 window->setLayout(layout); 153 setCentralWidget(window); 154 window->show(); 155 156 buffer = new char[BUFFER_SIZE]; 157 158 gen = new Generator(this); 159 160 pullMode = true; 161 162 timer = new QTimer(this); 163 connect(timer,SIGNAL(timeout()),SLOT(writeMore())); 164 165 gen->start(); 166 167 settings.setFrequency(SYSTEM_FREQ); 168 settings.setChannels(1); 169 settings.setSampleSize(16); 170 settings.setCodec("audio/pcm"); 171 settings.setByteOrder(QAudioFormat::LittleEndian); 172 settings.setSampleType(QAudioFormat::SignedInt); 180 m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); 181 connect(m_deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); 182 layout->addWidget(m_deviceBox); 183 184 m_modeButton = new QPushButton(this); 185 m_modeButton->setText(PushModeLabel); 186 connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode())); 187 layout->addWidget(m_modeButton); 188 189 m_suspendResumeButton = new QPushButton(this); 190 m_suspendResumeButton->setText(SuspendLabel); 191 connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); 192 layout->addWidget(m_suspendResumeButton); 193 194 window->setLayout(layout.data()); 195 layout.take(); // ownership transferred 196 197 setCentralWidget(window.data()); 198 QWidget *const windowPtr = window.take(); // ownership transferred 199 windowPtr->show(); 200 } 201 202 void AudioTest::initializeAudio() 203 { 204 connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired())); 205 206 m_pullMode = true; 207 208 m_format.setFrequency(DataFrequencyHz); 209 m_format.setChannels(1); 210 m_format.setSampleSize(16); 211 m_format.setCodec("audio/pcm"); 212 m_format.setByteOrder(QAudioFormat::LittleEndian); 213 m_format.setSampleType(QAudioFormat::SignedInt); 173 214 174 215 QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); 175 if (!info.isFormatSupported(settings)) { 176 qWarning()<<"default format not supported try to use nearest"; 177 settings = info.nearestFormat(settings); 178 } 179 180 if(settings.sampleSize() != 16) { 181 qWarning()<<"audio device doesn't support 16 bit samples, example cannot run"; 182 button->setDisabled(true); 183 button2->setDisabled(true); 184 audioOutput = 0; 185 return; 186 } 187 188 audioOutput = new QAudioOutput(settings,this); 189 connect(audioOutput,SIGNAL(notify()),SLOT(status())); 190 connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); 191 192 audioOutput->start(gen); 216 if (!info.isFormatSupported(m_format)) { 217 qWarning() << "Default format not supported - trying to use nearest"; 218 m_format = info.nearestFormat(m_format); 219 } 220 221 m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, this); 222 223 createAudioOutput(); 224 } 225 226 void AudioTest::createAudioOutput() 227 { 228 delete m_audioOutput; 229 m_audioOutput = 0; 230 m_audioOutput = new QAudioOutput(m_device, m_format, this); 231 connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); 232 connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); 233 m_generator->start(); 234 m_audioOutput->start(m_generator); 193 235 } 194 236 195 237 AudioTest::~AudioTest() 196 238 { 197 delete [] buffer; 198 } 199 200 void AudioTest::deviceChanged(int idx) 201 { 202 timer->stop(); 203 gen->stop(); 204 audioOutput->stop(); 205 audioOutput->disconnect(this); 206 delete audioOutput; 207 208 device = deviceBox->itemData(idx).value<QAudioDeviceInfo>(); 209 audioOutput = new QAudioOutput(device,settings,this); 210 connect(audioOutput,SIGNAL(notify()),SLOT(status())); 211 connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); 212 gen->start(); 213 audioOutput->start(gen); 214 } 215 216 void AudioTest::status() 217 { 218 qWarning() << "byteFree = " << audioOutput->bytesFree() << " bytes, elapsedUSecs = " << audioOutput->elapsedUSecs() << ", processedUSecs = " << audioOutput->processedUSecs(); 219 } 220 221 void AudioTest::writeMore() 222 { 223 if (!audioOutput) 224 return; 225 226 if (audioOutput->state() == QAudio::StoppedState) 227 return; 228 229 int l; 230 int out; 231 232 int chunks = audioOutput->bytesFree()/audioOutput->periodSize(); 233 while(chunks) { 234 l = gen->read(buffer,audioOutput->periodSize()); 235 if (l > 0) 236 out = output->write(buffer,l); 237 if (l != audioOutput->periodSize()) 238 break; 239 chunks--; 240 } 241 } 242 243 void AudioTest::toggle() 244 { 245 // Change between pull and push modes 246 247 timer->stop(); 248 audioOutput->stop(); 249 250 if (pullMode) { 251 button->setText("Click for Pull Mode"); 252 output = audioOutput->start(); 253 pullMode = false; 254 timer->start(20); 239 240 } 241 242 void AudioTest::deviceChanged(int index) 243 { 244 m_pullTimer->stop(); 245 m_generator->stop(); 246 m_audioOutput->stop(); 247 m_audioOutput->disconnect(this); 248 m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>(); 249 createAudioOutput(); 250 } 251 252 void AudioTest::notified() 253 { 254 qWarning() << "bytesFree = " << m_audioOutput->bytesFree() 255 << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs() 256 << ", " << "processedUSecs = " << m_audioOutput->processedUSecs(); 257 } 258 259 void AudioTest::pullTimerExpired() 260 { 261 if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) { 262 int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize(); 263 while (chunks) { 264 const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize()); 265 if (len) 266 m_output->write(m_buffer.data(), len); 267 if (len != m_audioOutput->periodSize()) 268 break; 269 --chunks; 270 } 271 } 272 } 273 274 void AudioTest::toggleMode() 275 { 276 m_pullTimer->stop(); 277 m_audioOutput->stop(); 278 279 if (m_pullMode) { 280 m_modeButton->setText(PullModeLabel); 281 m_output = m_audioOutput->start(); 282 m_pullMode = false; 283 m_pullTimer->start(20); 255 284 } else { 256 button->setText("Click for Push Mode"); 257 pullMode = true; 258 audioOutput->start(gen); 259 } 260 } 261 262 void AudioTest::togglePlay() 263 { 264 // toggle suspend/resume 265 if (audioOutput->state() == QAudio::SuspendedState) { 285 m_modeButton->setText(PushModeLabel); 286 m_pullMode = true; 287 m_audioOutput->start(m_generator); 288 } 289 290 m_suspendResumeButton->setText(SuspendLabel); 291 } 292 293 void AudioTest::toggleSuspendResume() 294 { 295 if (m_audioOutput->state() == QAudio::SuspendedState) { 266 296 qWarning() << "status: Suspended, resume()"; 267 audioOutput->resume();268 button2->setText("Click To Suspend");269 } else if ( audioOutput->state() == QAudio::ActiveState) {297 m_audioOutput->resume(); 298 m_suspendResumeButton->setText(SuspendLabel); 299 } else if (m_audioOutput->state() == QAudio::ActiveState) { 270 300 qWarning() << "status: Active, suspend()"; 271 audioOutput->suspend();272 button2->setText("Click To Resume");273 } else if ( audioOutput->state() == QAudio::StoppedState) {301 m_audioOutput->suspend(); 302 m_suspendResumeButton->setText(ResumeLabel); 303 } else if (m_audioOutput->state() == QAudio::StoppedState) { 274 304 qWarning() << "status: Stopped, resume()"; 275 audioOutput->resume();276 button2->setText("Click To Suspend");277 } else if ( audioOutput->state() == QAudio::IdleState) {305 m_audioOutput->resume(); 306 m_suspendResumeButton->setText(SuspendLabel); 307 } else if (m_audioOutput->state() == QAudio::IdleState) { 278 308 qWarning() << "status: IdleState"; 279 309 } 280 310 } 281 311 282 void AudioTest::state (QAudio::State state)283 { 284 qWarning() << " state=" << state;285 } 312 void AudioTest::stateChanged(QAudio::State state) 313 { 314 qWarning() << "state = " << state; 315 }
Note:
See TracChangeset
for help on using the changeset viewer.