Ignore:
Timestamp:
Aug 2, 2010, 9:27:30 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/examples/multimedia/audiooutput/audiooutput.cpp

    r651 r769  
    4545#include <QAudioOutput>
    4646#include <QAudioDeviceInfo>
     47#include <QtCore/qmath.h>
     48#include <QtCore/qendian.h>
    4749#include "audiooutput.h"
    4850
    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;
     51const QString AudioTest::PushModeLabel(tr("Enable push mode"));
     52const QString AudioTest::PullModeLabel(tr("Enable pull mode"));
     53const QString AudioTest::SuspendLabel(tr("Suspend playback"));
     54const QString AudioTest::ResumeLabel(tr("Resume playback"));
     55
     56const int DurationSeconds = 1;
     57const int ToneFrequencyHz = 600;
     58const int DataFrequencyHz = 44100;
     59const int BufferSize      = 32768;
     60
     61
     62Generator::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);
    6670}
    6771
    6872Generator::~Generator()
    6973{
    70     delete [] buffer;
     74
    7175}
    7276
     
    7882void Generator::stop()
    7983{
     84    m_pos = 0;
    8085    close();
    8186}
    8287
    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     }
     88void 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
     133qint64 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;
    121143}
    122144
     
    129151}
    130152
     153qint64 Generator::bytesAvailable() const
     154{
     155    return m_buffer.size() + QIODevice::bytesAvailable();
     156}
     157
    131158AudioTest::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
     173void AudioTest::initializeWindow()
     174{
     175    QScopedPointer<QWidget> window(new QWidget);
     176    QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);
     177
     178    m_deviceBox = new QComboBox(this);
    137179    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
     202void 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);
    173214
    174215    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
     226void 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);
    193235}
    194236
    195237AudioTest::~AudioTest()
    196238{
    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
     242void 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
     252void AudioTest::notified()
     253{
     254    qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
     255               << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
     256               << ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
     257}
     258
     259void 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
     274void 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);
    255284    } 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
     293void AudioTest::toggleSuspendResume()
     294{
     295    if (m_audioOutput->state() == QAudio::SuspendedState) {
    266296        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) {
    270300        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) {
    274304        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) {
    278308        qWarning() << "status: IdleState";
    279309    }
    280310}
    281311
    282 void AudioTest::state(QAudio::State state)
    283 {
    284     qWarning() << " state=" << state;
    285 }
     312void AudioTest::stateChanged(QAudio::State state)
     313{
     314    qWarning() << "state = " << state;
     315}
Note: See TracChangeset for help on using the changeset viewer.