1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5
6 #include "mumble_pch.hpp"
7
8 #include "AudioOutputSample.h"
9
10 #include "Audio.h"
11
SoundFile(const QString & fname)12 SoundFile::SoundFile(const QString &fname) {
13 siInfo.frames = 0;
14 siInfo.channels = 1;
15 siInfo.samplerate = 0;
16 siInfo.sections = 0;
17 siInfo.seekable = 0;
18 siInfo.format = 0;
19
20 sfFile = NULL;
21
22 qfFile.setFileName(fname);
23
24 if (qfFile.open(QIODevice::ReadOnly)) {
25 static SF_VIRTUAL_IO svi = {&SoundFile::vio_get_filelen, &SoundFile::vio_seek, &SoundFile::vio_read, &SoundFile::vio_write, &SoundFile::vio_tell};
26
27 sfFile = sf_open_virtual(&svi, SFM_READ, &siInfo, this);
28 }
29 }
30
~SoundFile()31 SoundFile::~SoundFile() {
32 if (sfFile)
33 sf_close(sfFile);
34 }
35
isOpen() const36 bool SoundFile::isOpen() const {
37 return (sfFile != NULL) && qfFile.isOpen();
38 }
39
channels() const40 int SoundFile::channels() const {
41 return siInfo.channels;
42 }
43
samplerate() const44 int SoundFile::samplerate() const {
45 return siInfo.samplerate;
46 }
47
error() const48 int SoundFile::error() const {
49 return sf_error(sfFile);
50 }
51
strError() const52 QString SoundFile::strError() const {
53 return QLatin1String(sf_strerror(sfFile));
54 }
55
seek(sf_count_t frames,int whence)56 sf_count_t SoundFile::seek(sf_count_t frames, int whence) {
57 return sf_seek(sfFile, frames, whence);
58 }
59
read(float * ptr,sf_count_t items)60 sf_count_t SoundFile::read(float *ptr, sf_count_t items) {
61 return sf_read_float(sfFile, ptr, items);
62 }
63
vio_get_filelen(void * user_data)64 sf_count_t SoundFile::vio_get_filelen(void *user_data) {
65 SoundFile *sf = reinterpret_cast<SoundFile *>(user_data);
66
67 if (! sf->qfFile.isOpen())
68 return -1;
69
70 return (sf->qfFile.size());
71 }
72
vio_seek(sf_count_t offset,int whence,void * user_data)73 sf_count_t SoundFile::vio_seek(sf_count_t offset, int whence, void *user_data) {
74 SoundFile *sf = reinterpret_cast<SoundFile *>(user_data);
75
76 if (! sf->qfFile.isOpen())
77 return -1;
78
79 if (whence == SEEK_SET) {
80 sf->qfFile.seek(offset);
81 } else if (whence == SEEK_END) {
82 sf->qfFile.seek(sf->qfFile.size() - offset);
83 } else {
84 sf->qfFile.seek(sf->qfFile.pos() + offset);
85 }
86 return sf->qfFile.pos();
87 }
88
vio_read(void * ptr,sf_count_t count,void * user_data)89 sf_count_t SoundFile::vio_read(void *ptr, sf_count_t count, void *user_data) {
90 SoundFile *sf = reinterpret_cast<SoundFile *>(user_data);
91
92 if (! sf->qfFile.isOpen())
93 return -1;
94
95 return sf->qfFile.read(reinterpret_cast<char *>(ptr), count);
96 }
97
vio_write(const void * ptr,sf_count_t count,void * user_data)98 sf_count_t SoundFile::vio_write(const void *ptr, sf_count_t count, void *user_data) {
99 SoundFile *sf = reinterpret_cast<SoundFile *>(user_data);
100
101 if (! sf->qfFile.isOpen())
102 return -1;
103
104 return sf->qfFile.write(reinterpret_cast<const char *>(ptr), count);
105 }
106
vio_tell(void * user_data)107 sf_count_t SoundFile::vio_tell(void *user_data) {
108 SoundFile *sf = reinterpret_cast<SoundFile *>(user_data);
109
110 if (! sf->qfFile.isOpen())
111 return -1;
112
113 return sf->qfFile.pos();
114 }
115
AudioOutputSample(const QString & name,SoundFile * psndfile,bool loop,unsigned int freq)116 AudioOutputSample::AudioOutputSample(const QString &name, SoundFile *psndfile, bool loop, unsigned int freq) : AudioOutputUser(name) {
117 int err;
118
119 sfHandle = psndfile;
120 iOutSampleRate = freq;
121
122 // Check if the file is good
123 if (sfHandle->channels() <= 0 || sfHandle->channels() > 2) {
124 sfHandle = NULL;
125 return;
126 }
127
128 /* qWarning() << "Channels: " << sfHandle->channels();
129 qWarning() << "Samplerate: " << sfHandle->samplerate();
130 qWarning() << "Target Sr.: " << freq;
131 qWarning() << "Format: " << sfHandle->format() << endl; */
132
133 // If the frequencies don't match initialize the resampler
134 if (sfHandle->samplerate() != static_cast<int>(freq)) {
135 srs = speex_resampler_init(1, sfHandle->samplerate(), iOutSampleRate, 3, &err);
136 if (err != RESAMPLER_ERR_SUCCESS) {
137 qWarning() << "Initialize " << sfHandle->samplerate() << " to " << iOutSampleRate << " resampler failed!";
138 srs = NULL;
139 sfHandle = NULL;
140 return;
141 }
142 } else {
143 srs = NULL;
144 }
145
146 iLastConsume = iBufferFilled = 0;
147 bLoop = loop;
148 bEof = false;
149 }
150
~AudioOutputSample()151 AudioOutputSample::~AudioOutputSample() {
152 if (srs)
153 speex_resampler_destroy(srs);
154
155 delete sfHandle;
156 sfHandle = NULL;
157 }
158
loadSndfile(const QString & filename)159 SoundFile* AudioOutputSample::loadSndfile(const QString &filename) {
160 SoundFile *sf;
161
162 // Create the filehandle and do a quick check if everything is ok
163 sf = new SoundFile(filename);
164
165 if (! sf->isOpen()) {
166 qWarning() << "File " << filename << " failed to open";
167 delete sf;
168 return NULL;
169 }
170
171 if (sf->error() != SF_ERR_NO_ERROR) {
172 qWarning() << "File " << filename << " couldn't be loaded: " << sf->strError();
173 delete sf;
174 return NULL;
175 }
176
177 if (sf->channels() <= 0 || sf->channels() > 2) {
178 qWarning() << "File " << filename << " contains " << sf->channels() << " Channels, only 1 or 2 are supported.";
179 delete sf;
180 return NULL;
181 }
182 return sf;
183 }
184
browseForSndfile(QString defaultpath)185 QString AudioOutputSample::browseForSndfile(QString defaultpath) {
186 QString file = QFileDialog::getOpenFileName(NULL, tr("Choose sound file"), defaultpath, QLatin1String("*.wav *.ogg *.ogv *.oga *.flac"));
187 if (! file.isEmpty()) {
188 SoundFile *sf = AudioOutputSample::loadSndfile(file);
189 if (sf == NULL) {
190 QMessageBox::critical(NULL,
191 tr("Invalid sound file"),
192 tr("The file '%1' cannot be used by Mumble. Please select a file with a compatible format and encoding.").arg(Qt::escape(file)));
193 return QString();
194 }
195 delete sf;
196 }
197 return file;
198 }
199
needSamples(unsigned int snum)200 bool AudioOutputSample::needSamples(unsigned int snum) {
201 // Forward the buffer
202 for (unsigned int i=iLastConsume;i<iBufferFilled;++i)
203 pfBuffer[i-iLastConsume]=pfBuffer[i];
204 iBufferFilled -= iLastConsume;
205 iLastConsume = snum;
206
207 // Check if we can satisfy request with current buffer
208 if (iBufferFilled >= snum)
209 return true;
210
211 // Calculate the required buffersize to hold the results
212 unsigned int iInputFrames = static_cast<unsigned int>(ceilf(static_cast<float>(snum * sfHandle->samplerate()) / static_cast<float>(iOutSampleRate)));
213 unsigned int iInputSamples = iInputFrames * sfHandle->channels();
214
215 bool mix = sfHandle->channels() > 1;
216 STACKVAR(float, fOut, iInputSamples);
217 STACKVAR(float, fMix, iInputFrames);
218
219 bool eof = false;
220 sf_count_t read;
221 do {
222 resizeBuffer(iBufferFilled + snum);
223
224 // If we need to resample or mix write to the buffer on stack
225 float *pOut = (srs || mix) ? fOut : pfBuffer + iBufferFilled;
226
227 // Try to read all samples needed to satifsy this request
228 if ((read = sfHandle->read(pOut, iInputSamples)) < iInputSamples) {
229 if (sfHandle->error() != SF_ERR_NO_ERROR || !bLoop) {
230 // We reached the eof or encountered an error, stuff with zeroes
231 memset(pOut, 0, sizeof(float) * (iInputSamples - read));
232 read = iInputSamples;
233 eof = true;
234 } else {
235 sfHandle->seek(SEEK_SET, 0);
236 }
237 }
238
239 if (mix) { // Mix the channels (only two channels)
240 read /= 2;
241 // If we need to resample after this write to extra buffer
242 pOut = srs ? fMix : pfBuffer + iBufferFilled;
243 for (unsigned int i = 0; i < read; i++)
244 pOut[i] = (fOut[i*2] + fOut[i*2 + 1]) * 0.5f;
245
246 }
247
248 spx_uint32_t inlen = static_cast<unsigned int>(read);
249 spx_uint32_t outlen = snum;
250 if (srs) // If necessary resample
251 speex_resampler_process_float(srs, 0, pOut, &inlen, pfBuffer + iBufferFilled, &outlen);
252
253 iBufferFilled += outlen;
254 } while (iBufferFilled < snum);
255
256 if (eof && !bEof) {
257 emit playbackFinished();
258 bEof = true;
259 }
260
261 return !eof;
262 }
263