Update copyright headers
[qt:qt.git] / demos / mobile / guitartuner / src / voicegenerator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of The Qt Company Ltd nor the names of its
21 **     contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include "voicegenerator.h"
42
43 const int BufferSizeMilliseconds = 100;
44
45 VoiceGenerator::VoiceGenerator(const QAudioFormat &format,
46                                qreal frequency, qreal amplitude,
47                                QObject *parent) :
48     QIODevice(parent),
49     m_format(format),
50     m_amplitude(0.5)
51 {
52     Q_ASSERT(m_format.sampleSize() % 8 == 0);
53     int sampleBytes = m_format.channels() * (m_format.sampleSize() / 8);
54     // + 1 to round up, just to be sure that all samples fit.
55     qint64 samplesInBuffer = m_format.sampleRate()
56                              * BufferSizeMilliseconds / 1000 + 1;
57     qint64 length = samplesInBuffer * sampleBytes;
58     m_buffer.resize(length);
59     m_max_position = 0;
60     m_position = 0;
61     m_amplitude = amplitude;
62     setFrequency(frequency);
63 }
64
65 VoiceGenerator::~VoiceGenerator()
66 {
67 }
68
69 /**
70   * Opens the parent QIODevice.
71   */
72 void VoiceGenerator::start()
73 {
74     open(QIODevice::ReadOnly);
75 }
76
77 /**
78   * Closes the parent QIODevice. Resets the m_position to zero.
79   */
80 void VoiceGenerator::stop()
81 {
82     close();
83     m_position = 0;
84 }
85
86 /**
87   * Sets the frequency to new frequency.
88   */
89 void VoiceGenerator::setFrequency(qreal frequency)
90 {
91     Q_ASSERT(1 / frequency < BufferSizeMilliseconds);
92     this->m_frequency = frequency;
93     refreshData();
94 }
95
96 /**
97   * Sets the amplitude for the voice.
98   */
99 void VoiceGenerator::setAmplitude(qreal amplitude)
100 {
101     Q_ASSERT(amplitude >= 0);
102     m_amplitude = amplitude;
103     refreshData();
104 }
105
106 /**
107   * Returns the current frequency.
108   */
109 qreal VoiceGenerator::frequency()
110 {
111     return m_frequency;
112 }
113
114 /**
115   * Generates voice data corresponding a sine voice with target frequency.
116   * The number of data generated is calculated
117   * and stored to m_max_position.
118   */
119 void VoiceGenerator::refreshData()
120 {
121     const int channelBytes = m_format.sampleSize() / 8;
122     const int sampleSize = m_format.channels() * channelBytes;
123     const qint64 voiceOscillationsInBuffer = BufferSizeMilliseconds
124                                          * m_frequency / 1000;
125     const qint64 voiceSamplesInBuffer = voiceOscillationsInBuffer
126                                    * m_format.sampleRate() / m_frequency;
127     m_max_position = voiceSamplesInBuffer * sampleSize;
128     qint64 dataGenerationLength = m_buffer.size();
129
130
131     Q_ASSERT(m_max_position % (sampleSize) == 0);
132     Q_ASSERT(dataGenerationLength <= m_buffer.size());
133
134     short *t = (short*)m_buffer.data();
135
136
137 /*
138     int te ;
139     static float fpos = 0.0f;
140     //dataGenerationLength>>=1;           // in words
141     for (int f=0; f<dataGenerationLength; f++) {
142       te = (short)((sinf(fpos))* (65536.0f/2.0f));
143       fpos += m_frequency/2000.0f;
144       t[f]= te;
145     };
146
147     m_amplitude = 1.0f; */
148     uchar *ptr = reinterpret_cast<uchar *>(m_buffer.data());
149     int sampleIndex = 0;
150     while (dataGenerationLength > 0) {
151         qreal realValue = 0;
152         if (sampleIndex < voiceSamplesInBuffer) {
153             realValue = m_amplitude
154                        *qSin(2.0f * M_PI * m_frequency
155                              * qreal(sampleIndex % m_format.sampleRate())
156                                / m_format.sampleRate());
157         }
158         for (int i=0; i<m_format.channels(); ++i) {
159             setValue(ptr, realValue);
160             ptr += channelBytes;
161             dataGenerationLength -= channelBytes;
162         }
163         ++sampleIndex;
164     }
165 }
166
167 /**
168   * Stores the realValue into bytes pointed by ptr as an int value.
169   * Align-safe.
170   */
171 void VoiceGenerator::setValue(uchar *ptr, qreal realValue) {
172     if (m_format.sampleSize() == 8)
173     {
174         quint8 value = 0;
175         if (m_format.sampleType() == QAudioFormat::UnSignedInt) {
176             value = static_cast<quint8>(
177                         qRound((1.0 + realValue) / 2
178                                * M_MAX_AMPLITUDE_8BIT_UNSIGNED));
179         } else if (m_format.sampleType() == QAudioFormat::SignedInt) {
180             value = static_cast<qint8>(
181                         qRound(realValue
182                                * M_MAX_AMPLITUDE_8BIT_SIGNED));
183         }
184         *reinterpret_cast<quint8*>(ptr) = value;
185     } else if (m_format.sampleSize() == 16) {
186         quint16 value = 0;
187         if (m_format.sampleType() == QAudioFormat::UnSignedInt) {
188             value = static_cast<quint16>(
189                         qRound((1.0 + realValue) / 2
190                                * M_MAX_AMPLITUDE_16BIT_UNSIGNED));
191         } else if (m_format.sampleType() == QAudioFormat::SignedInt) {
192             value = static_cast<qint16>(
193                         qRound(realValue
194                                * M_MAX_AMPLITUDE_16BIT_SIGNED));
195         }
196         if (m_format.byteOrder() == QAudioFormat::LittleEndian)
197             qToLittleEndian<qint16>(value, ptr);
198         else
199             qToBigEndian<qint16>(value, ptr);
200     }
201 }
202
203 qint64 VoiceGenerator::bytesAvailable() const
204 {
205     return m_max_position + QIODevice::bytesAvailable();
206 }
207
208 /**
209   * Called by the QIODevice. Puts maxlen amount of voice
210   * samples into the data array.
211   */
212 qint64 VoiceGenerator::readData(char *data, qint64 maxlen)
213 {
214     qint64 total = 0;
215     qint64 chunk = 0;
216     while (total < maxlen) {
217         if (maxlen - total >= m_max_position - m_position) {
218             // the needed buffer is longer than the currently
219             // available buffer from m_position to the m_max_position
220             chunk = m_max_position - m_position;
221             memcpy(data, m_buffer.constData() + m_position, chunk);
222             m_position = 0;
223         }
224         else {
225             // we can copy the needed data directly, and the loop will end
226             chunk = maxlen - total;
227             memcpy(data, m_buffer.constData() + m_position, chunk);
228             m_position = (m_position + chunk) % m_max_position;
229         }
230         data += chunk;
231         total += chunk;
232     }
233     return total;
234 }
235
236 /**
237   * Empty implementation for writeData, since no data is provided
238   * for the VoiceGenerator class.
239   */
240 qint64 VoiceGenerator::writeData(const char *data, qint64 maxlen)
241 {
242     Q_UNUSED(data);
243     Q_UNUSED(maxlen);
244
245     return 0;
246 }