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