1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "audiodecoder.h" |
52 | #include <stdio.h> |
53 | |
54 | AudioDecoder::AudioDecoder(bool isPlayback, bool isDelete) |
55 | : m_cout(stdout, QIODevice::WriteOnly) |
56 | { |
57 | m_isPlayback = isPlayback; |
58 | m_isDelete = isDelete; |
59 | |
60 | // Make sure the data we receive is in correct PCM format. |
61 | // Our wav file writer only supports SignedInt sample type. |
62 | QAudioFormat format; |
63 | format.setChannelCount(2); |
64 | format.setSampleSize(16); |
65 | format.setSampleRate(48000); |
66 | format.setCodec("audio/pcm" ); |
67 | format.setSampleType(QAudioFormat::SignedInt); |
68 | m_decoder.setAudioFormat(format); |
69 | |
70 | connect(sender: &m_decoder, signal: &QAudioDecoder::bufferReady, |
71 | receiver: this, slot: &AudioDecoder::bufferReady); |
72 | connect(sender: &m_decoder, signal: QOverload<QAudioDecoder::Error>::of(ptr: &QAudioDecoder::error), |
73 | receiver: this, slot: QOverload<QAudioDecoder::Error>::of(ptr: &AudioDecoder::error)); |
74 | connect(sender: &m_decoder, signal: &QAudioDecoder::stateChanged, |
75 | receiver: this, slot: &AudioDecoder::stateChanged); |
76 | connect(sender: &m_decoder, signal: &QAudioDecoder::finished, |
77 | receiver: this, slot: &AudioDecoder::finished); |
78 | connect(sender: &m_decoder, signal: &QAudioDecoder::positionChanged, |
79 | receiver: this, slot: &AudioDecoder::updateProgress); |
80 | connect(sender: &m_decoder, signal: &QAudioDecoder::durationChanged, |
81 | receiver: this, slot: &AudioDecoder::updateProgress); |
82 | |
83 | connect(sender: &m_soundEffect, signal: &QSoundEffect::statusChanged, |
84 | receiver: this, slot: &AudioDecoder::playbackStatusChanged); |
85 | connect(sender: &m_soundEffect, signal: &QSoundEffect::playingChanged, |
86 | receiver: this, slot: &AudioDecoder::playingChanged); |
87 | |
88 | m_progress = -1.0; |
89 | } |
90 | |
91 | void AudioDecoder::setSourceFilename(const QString &fileName) |
92 | { |
93 | m_decoder.setSourceFilename(fileName); |
94 | } |
95 | |
96 | void AudioDecoder::start() |
97 | { |
98 | m_decoder.start(); |
99 | } |
100 | |
101 | void AudioDecoder::stop() |
102 | { |
103 | m_decoder.stop(); |
104 | } |
105 | |
106 | void AudioDecoder::setTargetFilename(const QString &fileName) |
107 | { |
108 | m_targetFilename = fileName; |
109 | } |
110 | |
111 | void AudioDecoder::bufferReady() |
112 | { |
113 | // read a buffer from audio decoder |
114 | QAudioBuffer buffer = m_decoder.read(); |
115 | if (!buffer.isValid()) |
116 | return; |
117 | |
118 | if (!m_fileWriter.isOpen() && !m_fileWriter.open(fileName: m_targetFilename, format: buffer.format())) { |
119 | m_decoder.stop(); |
120 | return; |
121 | } |
122 | |
123 | m_fileWriter.write(buffer); |
124 | } |
125 | |
126 | void AudioDecoder::error(QAudioDecoder::Error error) |
127 | { |
128 | switch (error) { |
129 | case QAudioDecoder::NoError: |
130 | return; |
131 | case QAudioDecoder::ResourceError: |
132 | m_cout << "Resource error\n" ; |
133 | break; |
134 | case QAudioDecoder::FormatError: |
135 | m_cout << "Format error\n" ; |
136 | break; |
137 | case QAudioDecoder::AccessDeniedError: |
138 | m_cout << "Access denied error\n" ; |
139 | break; |
140 | case QAudioDecoder::ServiceMissingError: |
141 | m_cout << "Service missing error\n" ; |
142 | break; |
143 | } |
144 | |
145 | emit done(); |
146 | } |
147 | |
148 | void AudioDecoder::stateChanged(QAudioDecoder::State newState) |
149 | { |
150 | switch (newState) { |
151 | case QAudioDecoder::DecodingState: |
152 | m_cout << "Decoding...\n" ; |
153 | break; |
154 | case QAudioDecoder::StoppedState: |
155 | m_cout << "Decoding stopped\n" ; |
156 | break; |
157 | } |
158 | } |
159 | |
160 | void AudioDecoder::finished() |
161 | { |
162 | if (!m_fileWriter.close()) |
163 | m_cout << "Failed to finilize output file\n" ; |
164 | |
165 | m_cout << "Decoding finished\n" ; |
166 | |
167 | if (m_isPlayback) { |
168 | m_cout << "Starting playback\n" ; |
169 | m_soundEffect.setSource(QUrl::fromLocalFile(localfile: m_targetFilename)); |
170 | m_soundEffect.play(); |
171 | } else { |
172 | emit done(); |
173 | } |
174 | } |
175 | |
176 | void AudioDecoder::playbackStatusChanged() |
177 | { |
178 | if (m_soundEffect.status() == QSoundEffect::Error) { |
179 | m_cout << "Playback error\n" ; |
180 | emit done(); |
181 | } |
182 | } |
183 | |
184 | void AudioDecoder::playingChanged() |
185 | { |
186 | if (!m_soundEffect.isPlaying()) { |
187 | m_cout << "Playback finished\n" ; |
188 | if (m_isDelete) |
189 | QFile::remove(fileName: m_targetFilename); |
190 | emit done(); |
191 | } |
192 | } |
193 | |
194 | void AudioDecoder::updateProgress() |
195 | { |
196 | qint64 position = m_decoder.position(); |
197 | qint64 duration = m_decoder.duration(); |
198 | qreal progress = m_progress; |
199 | if (position >= 0 && duration > 0) |
200 | progress = position / (qreal)duration; |
201 | |
202 | if (progress > m_progress + 0.1) { |
203 | m_cout << "Decoding progress: " << (int)(progress * 100.0) << "%\n" ; |
204 | m_progress = progress; |
205 | } |
206 | } |
207 | |