From 8a7a3d173d94cabc5bbfa2518f22849d1aa6f855 Mon Sep 17 00:00:00 2001 From: Justin McPherson Date: Thu, 24 Sep 2009 13:22:57 +1000 Subject: AudioServices; Nicer examples. Reviewed-by: Bill King --- doc/src/examples/audioinput.qdoc | 2 +- .../multimedia/audio/audioinput/audioinput.cpp | 198 ++++++--------------- examples/multimedia/audio/audioinput/audioinput.h | 40 ++--- .../multimedia/audio/audiooutput/audiooutput.cpp | 34 ++-- .../multimedia/audio/audiooutput/audiooutput.h | 2 - 5 files changed, 83 insertions(+), 193 deletions(-) diff --git a/doc/src/examples/audioinput.qdoc b/doc/src/examples/audioinput.qdoc index 0b1b551..ac44d75 100644 --- a/doc/src/examples/audioinput.qdoc +++ b/doc/src/examples/audioinput.qdoc @@ -49,6 +49,6 @@ Qt provides the QAudioInput class to enable audio functionality within a standard application user interface. - This example uses a fast-fourier transform on the input audio from the microphone + This example calculates the maximum linear value of the input audio from the microphone and displays the output. */ diff --git a/examples/multimedia/audio/audioinput/audioinput.cpp b/examples/multimedia/audio/audioinput/audioinput.cpp index c09e7d9..05723ae 100644 --- a/examples/multimedia/audio/audioinput/audioinput.cpp +++ b/examples/multimedia/audio/audioinput/audioinput.cpp @@ -50,46 +50,31 @@ #include #include "audioinput.h" -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif +#define BUFFER_SIZE 4096 -Spectrum::Spectrum(QObject* parent, QAudioInput* device, float* out) +AudioInfo::AudioInfo(QObject* parent, QAudioInput* device) :QIODevice( parent ) { input = device; - output = out; - unsigned int i; - - // Allocate sample buffer and initialize sin and cos lookup tables - fftState = (fft_state *) malloc (sizeof(fft_state)); - - for(i = 0; i < BUFFER_SIZE; i++) { - bitReverse[i] = reverseBits(i); - } - for(i = 0; i < BUFFER_SIZE / 2; i++) { - float j = 2 * M_PI * i / BUFFER_SIZE; - costable[i] = cos(j); - sintable[i] = sin(j); - } + m_maxValue = 0; } -Spectrum::~Spectrum() +AudioInfo::~AudioInfo() { } -void Spectrum::start() +void AudioInfo::start() { open(QIODevice::WriteOnly); } -void Spectrum::stop() +void AudioInfo::stop() { close(); } -qint64 Spectrum::readData(char *data, qint64 maxlen) +qint64 AudioInfo::readData(char *data, qint64 maxlen) { Q_UNUSED(data) Q_UNUSED(maxlen) @@ -97,97 +82,39 @@ qint64 Spectrum::readData(char *data, qint64 maxlen) return 0; } -qint64 Spectrum::writeData(const char *data, qint64 len) +qint64 AudioInfo::writeData(const char *data, qint64 len) { - performFFT((sound_sample*)data); - emit update(); - - return len; -} - -int Spectrum::reverseBits(unsigned int initial) { - // BIT-REVERSE-COPY(a,A) - - unsigned int reversed = 0, loop; - for(loop = 0; loop < BUFFER_SIZE_LOG; loop++) { - reversed <<= 1; - reversed += (initial & 1); - initial >>= 1; - } - return reversed; -} - -void Spectrum::performFFT(const sound_sample *input) { - /* Convert to reverse bit order for FFT */ - prepFFT(input, fftState->real, fftState->imag); + int samples = len/2; // 2 bytes per sample + int maxAmp = 32768; // max for S16 samples + bool clipping = false; - /* Calculate FFT */ - calcFFT(fftState->real, fftState->imag); - - /* Convert FFT to intensities */ - outputFFT(fftState->real, fftState->imag); -} + m_maxValue = 0; -void Spectrum::prepFFT(const sound_sample *input, float * re, float * im) { - unsigned int i; - float *realptr = re; - float *imagptr = im; + qint16* s = (qint16*)data; - /* Get input, in reverse bit order */ - for(i = 0; i < BUFFER_SIZE; i++) { - *realptr++ = input[bitReverse[i]]; - *imagptr++ = 0; - } -} + // sample format is S16LE, only! -void Spectrum::calcFFT(float * re, float * im) { - unsigned int i, j, k; - unsigned int exchanges; - float fact_real, fact_imag; - float tmp_real, tmp_imag; - unsigned int factfact; - - /* Set up some variables to reduce calculation in the loops */ - exchanges = 1; - factfact = BUFFER_SIZE / 2; - - /* divide and conquer method */ - for(i = BUFFER_SIZE_LOG; i != 0; i--) { - for(j = 0; j != exchanges; j++) { - fact_real = costable[j * factfact]; - fact_imag = sintable[j * factfact]; - for(k = j; k < BUFFER_SIZE; k += exchanges << 1) { - int k1 = k + exchanges; - tmp_real = fact_real * re[k1] - fact_imag * im[k1]; - tmp_imag = fact_real * im[k1] + fact_imag * re[k1]; - re[k1] = re[k] - tmp_real; - im[k1] = im[k] - tmp_imag; - re[k] += tmp_real; - im[k] += tmp_imag; - } - } - exchanges <<= 1; - factfact >>= 1; + for(int i=0;i m_maxValue) m_maxValue = abs(sample); } -} + // check for clipping + if(m_maxValue>=(maxAmp-1)) clipping = true; -void Spectrum::outputFFT(const float * re, const float * im) { - const float *realptr = re; - const float *imagptr = im; - float *outputptr = output; + float value = ((float)m_maxValue/(float)maxAmp); + if(clipping) m_maxValue = 100; + else m_maxValue = (int)(value*100); - float *endptr = output + BUFFER_SIZE / 2; - - /* Convert FFT to intensities */ + emit update(); - while(outputptr <= endptr) { - *outputptr = (*realptr * *realptr) + (*imagptr * *imagptr); - outputptr++; realptr++; imagptr++; - } - *output /= 4; - *endptr /= 4; + return len; } +int AudioInfo::LinearMax() +{ + return m_maxValue; +} RenderArea::RenderArea(QWidget *parent) : QWidget(parent) @@ -195,8 +122,7 @@ RenderArea::RenderArea(QWidget *parent) setBackgroundRole(QPalette::Base); setAutoFillBackground(true); - samples = 0; - sampleSize = 0; + level = 0; setMinimumHeight(30); setMinimumWidth(200); } @@ -205,38 +131,32 @@ void RenderArea::paintEvent(QPaintEvent * /* event */) { QPainter painter(this); - if(sampleSize == 0) + painter.setPen(Qt::black); + painter.drawRect(QRect(painter.viewport().left()+10, painter.viewport().top()+10, + painter.viewport().right()-20, painter.viewport().bottom()-20)); + + if(level == 0) return; painter.setPen(Qt::red); - int max = 0; - for(int i=0;i max) - max = m; - } - int x1,y1,x2,y2; + int pos = ((painter.viewport().right()-20)-(painter.viewport().left()+11))*level/100; + int x1,y1,x2,y2; for(int i=0;i<10;i++) { x1 = painter.viewport().left()+11; y1 = painter.viewport().top()+10+i; - x2 = painter.viewport().right()-20-max; + x2 = painter.viewport().left()+20+pos; y2 = painter.viewport().top()+10+i; if(x2 < painter.viewport().left()+10) x2 = painter.viewport().left()+10; painter.drawLine(QPoint(x1,y1),QPoint(x2,y2)); } - - painter.setPen(Qt::black); - painter.drawRect(QRect(painter.viewport().left()+10, painter.viewport().top()+10, - painter.viewport().right()-20, painter.viewport().bottom()-20)); } -void RenderArea::spectrum(float* output, int size) +void RenderArea::setLevel(int value) { - samples = output; - sampleSize = size; + level = value; repaint(); } @@ -271,25 +191,25 @@ InputTest::InputTest() setCentralWidget(window); window->show(); - buffer = new char[BUFFER_SIZE*10]; - output = new float[1024]; + buffer = new char[BUFFER_SIZE]; pullMode = true; + // AudioInfo class only supports mono S16LE samples! format.setFrequency(8000); format.setChannels(1); - format.setSampleSize(8); - format.setSampleType(QAudioFormat::UnSignedInt); + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); format.setByteOrder(QAudioFormat::LittleEndian); format.setCodec("audio/pcm"); audioInput = new QAudioInput(format,this); connect(audioInput,SIGNAL(notify()),SLOT(status())); connect(audioInput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); - spec = new Spectrum(this,audioInput,output); - connect(spec,SIGNAL(update()),SLOT(refreshDisplay())); - spec->start(); - audioInput->start(spec); + audioinfo = new AudioInfo(this,audioInput); + connect(audioinfo,SIGNAL(update()),SLOT(refreshDisplay())); + audioinfo->start(); + audioInput->start(audioinfo); } InputTest::~InputTest() {} @@ -304,11 +224,11 @@ void InputTest::readMore() if(!audioInput) return; qint64 len = audioInput->bytesReady(); - if(len > BUFFER_SIZE*10) - len = BUFFER_SIZE*10; + if(len > 4096) + len = 4096; qint64 l = input->read(buffer,len); if(l > 0) { - spec->write(buffer,l); + audioinfo->write(buffer,l); } } @@ -317,15 +237,15 @@ void InputTest::toggleMode() // Change bewteen pull and push modes audioInput->stop(); - if(pullMode) { - button->setText(tr("Click for Push Mode")); + if (pullMode) { + button->setText(tr("Click for Pull Mode")); input = audioInput->start(0); connect(input,SIGNAL(readyRead()),SLOT(readMore())); pullMode = false; } else { - button->setText(tr("Click for Pull Mode")); + button->setText(tr("Click for Push Mode")); pullMode = true; - audioInput->start(spec); + audioInput->start(audioinfo); } } @@ -356,13 +276,13 @@ void InputTest::state(QAudio::State state) void InputTest::refreshDisplay() { - canvas->spectrum(output,256); + canvas->setLevel(audioinfo->LinearMax()); canvas->repaint(); } void InputTest::deviceChanged(int idx) { - spec->stop(); + audioinfo->stop(); audioInput->stop(); audioInput->disconnect(this); delete audioInput; @@ -371,6 +291,6 @@ void InputTest::deviceChanged(int idx) audioInput = new QAudioInput(device, format, this); connect(audioInput,SIGNAL(notify()),SLOT(status())); connect(audioInput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); - spec->start(); - audioInput->start(spec); + audioinfo->start(); + audioInput->start(audioinfo); } diff --git a/examples/multimedia/audio/audioinput/audioinput.h b/examples/multimedia/audio/audioinput/audioinput.h index 7ba6f1f..14e1bac 100644 --- a/examples/multimedia/audio/audioinput/audioinput.h +++ b/examples/multimedia/audio/audioinput/audioinput.h @@ -48,42 +48,25 @@ #include -#define BUFFER_SIZE_LOG 9 -#define BUFFER_SIZE (1 << BUFFER_SIZE_LOG) - -struct _struct_fft_state { - float real[BUFFER_SIZE]; - float imag[BUFFER_SIZE]; -}; -typedef _struct_fft_state fft_state; -typedef short int sound_sample; - -class Spectrum : public QIODevice +class AudioInfo : public QIODevice { Q_OBJECT public: - Spectrum(QObject* parent, QAudioInput* device, float* out); - ~Spectrum(); + AudioInfo(QObject* parent, QAudioInput* device); + ~AudioInfo(); void start(); void stop(); + int LinearMax(); + qint64 readData(char *data, qint64 maxlen); qint64 writeData(const char *data, qint64 len); QAudioInput* input; - float* output; - fft_state* fftState; - unsigned int bitReverse[BUFFER_SIZE]; - float sintable[BUFFER_SIZE / 2]; - float costable[BUFFER_SIZE / 2]; - - void prepFFT (const sound_sample *input, float *re, float *im); - void calcFFT (float *re, float *im); - void outputFFT (const float *re, const float *im); - int reverseBits (unsigned int initial); - void performFFT (const sound_sample *input); +private: + int m_maxValue; signals: void update(); @@ -97,16 +80,14 @@ class RenderArea : public QWidget public: RenderArea(QWidget *parent = 0); - void spectrum(float* output, int size); + void setLevel(int value); protected: void paintEvent(QPaintEvent *event); private: + int level; QPixmap pixmap; - - float* samples; - int sampleSize; }; class InputTest : public QMainWindow @@ -119,7 +100,7 @@ public: QAudioDeviceInfo device; QAudioFormat format; QAudioInput* audioInput; - Spectrum* spec; + AudioInfo* audioinfo; QIODevice* input; RenderArea* canvas; @@ -130,7 +111,6 @@ public: QComboBox* deviceBox; char* buffer; - float* output; private slots: void refreshDisplay(); diff --git a/examples/multimedia/audio/audiooutput/audiooutput.cpp b/examples/multimedia/audio/audiooutput/audiooutput.cpp index 19f7a3f..9e532cd 100644 --- a/examples/multimedia/audio/audiooutput/audiooutput.cpp +++ b/examples/multimedia/audio/audiooutput/audiooutput.cpp @@ -50,18 +50,19 @@ #define M_PI 3.14159265358979323846 #endif +#define SECONDS 1 +#define FREQ 600 +#define SYSTEM_FREQ 44100 + Generator::Generator(QObject *parent) :QIODevice( parent ) { finished = false; - buffer = new char[SECONDS*44100*4+1000]; + buffer = new char[SECONDS*SYSTEM_FREQ*4+1000]; t=buffer; - len=fillData(t+4,450,SECONDS); /* left channel, 450Hz sine */ - len+=fillData(t+6,452,SECONDS); /* right channel, 452Hz sine */ - putLong(t,len); - putLong(buffer+4,len+8+16+8); + len=fillData(t,FREQ,SECONDS); /* mono FREQHz sine */ pos = 0; - total = len+8+16+8; + total = len; } Generator::~Generator() @@ -86,21 +87,12 @@ int Generator::putShort(char *t, unsigned int value) return 2; } -int Generator::putLong(char *t, unsigned int value) -{ - *(unsigned char *)(t++)=value&255; - *(unsigned char *)(t++)=(value/256)&255; - *(unsigned char *)(t++)=(value/(256*256))&255; - *(unsigned char *)(t)=(value/(256*256*256))&255; - return 4; -} - int Generator::fillData(char *start, int frequency, int seconds) { int i, len=0; int value; - for(i=0; i 16384) len = 16384; - if(len < (SECONDS*44100*4+1000)-pos) { + if(len < (SECONDS*SYSTEM_FREQ*2)-pos) { // Normal memcpy(data,t+pos,len); pos+=len; return len; } else { // Whats left and reset to start - qint64 left = (SECONDS*44100*4+1000)-pos; + qint64 left = (SECONDS*SYSTEM_FREQ*2)-pos; memcpy(data,t+pos,left); pos=0; return left; @@ -172,8 +164,8 @@ AudioTest::AudioTest() gen->start(); - settings.setFrequency(44100); - settings.setChannels(2); + settings.setFrequency(SYSTEM_FREQ); + settings.setChannels(1); settings.setSampleSize(16); settings.setCodec("audio/pcm"); settings.setByteOrder(QAudioFormat::LittleEndian); diff --git a/examples/multimedia/audio/audiooutput/audiooutput.h b/examples/multimedia/audio/audiooutput/audiooutput.h index a5c0289..6c07a3a 100644 --- a/examples/multimedia/audio/audiooutput/audiooutput.h +++ b/examples/multimedia/audio/audiooutput/audiooutput.h @@ -42,7 +42,6 @@ #include #define BUFFER_SIZE 32768 -#define SECONDS 3 #include #include @@ -76,7 +75,6 @@ public: private: int putShort(char *t, unsigned int value); - int putLong(char *t, unsigned int value); int fillData(char *start, int frequency, int seconds); }; -- cgit v0.12