voiceanalyzer.cpp Example File
demos/mobile/guitartuner/src/voiceanalyzer.cpp
 
 
 #include "voiceanalyzer.h"
 
 const static float CutOffScaler = 0.05;
 
 const static int PrecisionPerNote = 4;
 
 const static int TargetFrequencyParameter = 4;
 VoiceAnalyzer::VoiceAnalyzer(const QAudioFormat &format, QObject *parent):
     QIODevice(parent),
     m_format(format),
     m_frequency(0),
     m_position(0),
     m_fftHelper(new FastFourierTransformer(this))
 {
     Q_ASSERT(qFuzzyCompare(M_SAMPLE_COUNT_MULTIPLIER,
                            float(2)/(M_TWELTH_ROOT_OF_2 -1.0)));
     m_totalSampleCount = qRound(qreal(PrecisionPerNote)
                                 *TargetFrequencyParameter
                                 *M_SAMPLE_COUNT_MULTIPLIER);
     m_samples.reserve(m_totalSampleCount);
     int i = 2;
     int j = 1;
     for (; i < TargetFrequencyParameter; i *= 2) {
         j++;
     }
     m_maximumVoiceDifference = j*12;
     setCutOffPercentage(CutOffScaler);
 }
 
 void VoiceAnalyzer::start(qreal frequency)
 {
     m_stepSize = (qreal) 1.0 * m_format.sampleRate()
                          / (TargetFrequencyParameter*2*frequency);
     m_frequency = frequency;
     open(QIODevice::WriteOnly);
 }
 
 void VoiceAnalyzer::stop()
 {
     m_samples.clear();
     m_samples.reserve(m_totalSampleCount);
     close();
 }
 
 qint64 VoiceAnalyzer::writeData(const char *data, qint64 maxlen)
 {
     const int channelBytes = m_format.sampleSize() / 8;
     int sampleSize = m_format.channels() * channelBytes;
     int m_stepSizeInBytes = m_stepSize*sampleSize;
     
     Q_ASSERT((m_position % sampleSize)==0);
     const uchar *ptr = reinterpret_cast<const uchar *>(data);
     while (m_position < maxlen) {
         if (m_samples.size() < m_totalSampleCount) {
             m_samples.append(getValueInt16(ptr+m_position));
         }
         else {
             analyzeVoice();
             m_samples.clear();
             m_samples.reserve(m_totalSampleCount);
             
             m_position += ((m_stepSizeInBytes - 1 + maxlen - m_position) /
                            m_stepSizeInBytes) * m_stepSizeInBytes;
             break;
         }
         m_position += m_stepSizeInBytes;
     }
     m_position -= maxlen;
     return maxlen;
 }
 
 qint16 VoiceAnalyzer::getValueInt16(const uchar *ptr)
 {
     qint16 realValue = 0;
     if (m_format.sampleSize() == 8)
     {
         const qint16 value = *reinterpret_cast<const quint8*>(ptr);
         if (m_format.sampleType() == QAudioFormat::UnSignedInt) {
             realValue = value - M_MAX_AMPLITUDE_8BIT_SIGNED - 1;
         } else if (m_format.sampleType() == QAudioFormat::SignedInt) {
             realValue = value;
         }
     } else if (m_format.sampleSize() == 16) {
         qint16 value = 0;
         if (m_format.byteOrder() == QAudioFormat::LittleEndian)
             value = qFromLittleEndian<quint16>(ptr);
         else
             value = qFromBigEndian<quint16>(ptr);
         if (m_format.sampleType() == QAudioFormat::UnSignedInt) {
             realValue = value - M_MAX_AMPLITUDE_16BIT_SIGNED;
         } else if (m_format.sampleType() == QAudioFormat::SignedInt) {
             realValue = value;
         }
     }
     return realValue;
 }
 
 void VoiceAnalyzer::setCutOffPercentage(qreal cutoff)
 {
     cutoff = CutOffScaler*cutoff;
     if (m_format.sampleSize() == 8) {
         float t = cutoff*m_totalSampleCount*M_MAX_AMPLITUDE_8BIT_SIGNED;
         m_fftHelper->setCutOffForDensity(t);
     }
     else if (m_format.sampleSize() == 16) {
         float t = cutoff*m_totalSampleCount*M_MAX_AMPLITUDE_16BIT_SIGNED;
         m_fftHelper->setCutOffForDensity(t);
     }
 }
 
 qreal VoiceAnalyzer::frequency()
 {
     return m_frequency;
 }
 
 int VoiceAnalyzer::getMaximumVoiceDifference()
 {
     return m_maximumVoiceDifference;
 }
 
 int VoiceAnalyzer::getMaximumPrecisionPerNote()
 {
     return PrecisionPerNote;
 }
 
 void VoiceAnalyzer::analyzeVoice()
 {
     m_fftHelper->calculateFFT(m_samples);
     int index = m_fftHelper->getMaximumDensityIndex();
     
     if (index == -1) {
         
         
         emit lowVoice();
         qDebug() << "low voice";
         return;
     }
     
     
     
     qreal stepSizeInFrequency = (qreal)m_format.sampleRate()
             / (m_totalSampleCount * m_stepSize);
     qreal newFrequency = qreal(index) * stepSizeInFrequency;
     
     int correctIndex = qRound(m_frequency / stepSizeInFrequency);
     qreal value = 0;
     
     
     
     
     
     
     if (m_frequency > newFrequency * TargetFrequencyParameter) {
         
         qDebug() << "compare" << "low" << newFrequency << m_frequency - stepSizeInFrequency * correctIndex << (m_frequency - stepSizeInFrequency * correctIndex) / stepSizeInFrequency;
         value = -m_maximumVoiceDifference;
     }
     
     
     else if (m_frequency*TargetFrequencyParameter < newFrequency) {
         
         qDebug() << "compare" << "high" << newFrequency << m_frequency - stepSizeInFrequency * correctIndex << (m_frequency - stepSizeInFrequency * correctIndex) / stepSizeInFrequency;
         value = m_maximumVoiceDifference;
     }
     
     else {
         
         
         
         
         
         
         value = log(newFrequency / (stepSizeInFrequency * correctIndex))
                 * 12 / M_LN2;
         qDebug() << "compare" << value << newFrequency << m_frequency - stepSizeInFrequency * correctIndex << (m_frequency - stepSizeInFrequency * correctIndex) / stepSizeInFrequency;
     }
     
     QVariant valueVar(value); 
     emit voiceDifference(valueVar);
     
     if (correctIndex == index) {
         emit(correctFrequency());
     }
 }
 
 qint64 VoiceAnalyzer::readData(char *data, qint64 maxlen)
 {
     Q_UNUSED(data);
     Q_UNUSED(maxlen);
     return 0;
 }