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 QtMultimedia module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qaudio_symbian_p.h"
43 #include <mmffourcc.h>
44
45 QT_BEGIN_NAMESPACE
46
47 namespace SymbianAudio {
48 namespace Utils {
49
50 //-----------------------------------------------------------------------------
51 // Static data
52 //-----------------------------------------------------------------------------
53
54 // Sample rate / frequency
55
56 typedef TMMFSampleRate SampleRateNative;
57 typedef int SampleRateQt;
58
59 const int SampleRateCount = 12;
60
61 const SampleRateNative SampleRateListNative[SampleRateCount] = {
62 EMMFSampleRate8000Hz
63 , EMMFSampleRate11025Hz
64 , EMMFSampleRate12000Hz
65 , EMMFSampleRate16000Hz
66 , EMMFSampleRate22050Hz
67 , EMMFSampleRate24000Hz
68 , EMMFSampleRate32000Hz
69 , EMMFSampleRate44100Hz
70 , EMMFSampleRate48000Hz
71 , EMMFSampleRate64000Hz
72 , EMMFSampleRate88200Hz
73 , EMMFSampleRate96000Hz
74 };
75
76 const SampleRateQt SampleRateListQt[SampleRateCount] = {
77 8000
78 , 11025
79 , 12000
80 , 16000
81 , 22050
82 , 24000
83 , 32000
84 , 44100
85 , 48000
86 , 64000
87 , 88200
88 , 96000
89 };
90
91 // Channels
92
93 typedef TMMFMonoStereo ChannelsNative;
94 typedef int ChannelsQt;
95
96 const int ChannelsCount = 2;
97
98 const ChannelsNative ChannelsListNative[ChannelsCount] = {
99 EMMFMono
100 , EMMFStereo
101 };
102
103 const ChannelsQt ChannelsListQt[ChannelsCount] = {
104 1
105 , 2
106 };
107
108 // Encoding
109
110 const int EncodingCount = 6;
111
112 const TUint32 EncodingFourCC[EncodingCount] = {
113 KMMFFourCCCodePCM8 // 0
114 , KMMFFourCCCodePCMU8 // 1
115 , KMMFFourCCCodePCM16 // 2
116 , KMMFFourCCCodePCMU16 // 3
117 , KMMFFourCCCodePCM16B // 4
118 , KMMFFourCCCodePCMU16B // 5
119 };
120
121 // The characterised DevSound API specification states that the iEncoding
122 // field in TMMFCapabilities is ignored, and that the FourCC should be used
123 // to specify the PCM encoding.
124 // See "SGL.GT0287.102 Multimedia DevSound Baseline Compatibility.doc" in the
125 // mm_info/mm_docs repository.
126 const TMMFSoundEncoding EncodingNative[EncodingCount] = {
127 EMMFSoundEncoding16BitPCM // 0
128 , EMMFSoundEncoding16BitPCM // 1
129 , EMMFSoundEncoding16BitPCM // 2
130 , EMMFSoundEncoding16BitPCM // 3
131 , EMMFSoundEncoding16BitPCM // 4
132 , EMMFSoundEncoding16BitPCM // 5
133 };
134
135
136 const int EncodingSampleSize[EncodingCount] = {
137 8 // 0
138 , 8 // 1
139 , 16 // 2
140 , 16 // 3
141 , 16 // 4
142 , 16 // 5
143 };
144
145 const QAudioFormat::Endian EncodingByteOrder[EncodingCount] = {
146 QAudioFormat::LittleEndian // 0
147 , QAudioFormat::LittleEndian // 1
148 , QAudioFormat::LittleEndian // 2
149 , QAudioFormat::LittleEndian // 3
150 , QAudioFormat::BigEndian // 4
151 , QAudioFormat::BigEndian // 5
152 };
153
154 const QAudioFormat::SampleType EncodingSampleType[EncodingCount] = {
155 QAudioFormat::SignedInt // 0
156 , QAudioFormat::UnSignedInt // 1
157 , QAudioFormat::SignedInt // 2
158 , QAudioFormat::UnSignedInt // 3
159 , QAudioFormat::SignedInt // 4
160 , QAudioFormat::UnSignedInt // 5
161 };
162
163
164 //-----------------------------------------------------------------------------
165 // Private functions
166 //-----------------------------------------------------------------------------
167
168 // Helper functions for implementing parameter conversions
169
170 template<typename Input>
findValue(const Input * inputArray,int length,Input input,int & index)171 bool findValue(const Input *inputArray, int length, Input input, int &index) {
172 bool result = false;
173 for (int i=0; !result && i<length; ++i)
174 if (inputArray[i] == input) {
175 index = i;
176 result = true;
177 }
178 return result;
179 }
180
181 template<typename Input, typename Output>
convertValue(const Input * inputArray,const Output * outputArray,int length,Input input,Output & output)182 bool convertValue(const Input *inputArray, const Output *outputArray,
183 int length, Input input, Output &output) {
184 int index;
185 const bool result = findValue<Input>(inputArray, length, input, index);
186 if (result)
187 output = outputArray[index];
188 return result;
189 }
190
191 /**
192 * Macro which is used to generate the implementation of the conversion
193 * functions. The implementation is just a wrapper around the templated
194 * convertValue function, e.g.
195 *
196 * CONVERSION_FUNCTION_IMPL(SampleRate, Qt, Native)
197 *
198 * expands to
199 *
200 * bool SampleRateQtToNative(int input, TMMFSampleRate &output) {
201 * return convertValue<SampleRateQt, SampleRateNative>
202 * (SampleRateListQt, SampleRateListNative, SampleRateCount,
203 * input, output);
204 * }
205 */
206 #define CONVERSION_FUNCTION_IMPL(FieldLc, Field, Input, Output) \
207 bool FieldLc##Input##To##Output(Field##Input input, Field##Output &output) { \
208 return convertValue<Field##Input, Field##Output>(Field##List##Input, \
209 Field##List##Output, Field##Count, input, output); \
210 }
211
212 //-----------------------------------------------------------------------------
213 // Local helper functions
214 //-----------------------------------------------------------------------------
215
CONVERSION_FUNCTION_IMPL(sampleRate,SampleRate,Qt,Native)216 CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Qt, Native)
217 CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Native, Qt)
218 CONVERSION_FUNCTION_IMPL(channels, Channels, Qt, Native)
219 CONVERSION_FUNCTION_IMPL(channels, Channels, Native, Qt)
220
221 bool sampleInfoQtToNative(int inputSampleSize,
222 QAudioFormat::Endian inputByteOrder,
223 QAudioFormat::SampleType inputSampleType,
224 TUint32 &outputFourCC,
225 TMMFSoundEncoding &outputEncoding) {
226
227 bool found = false;
228
229 for (int i=0; i<EncodingCount && !found; ++i) {
230 if ( EncodingSampleSize[i] == inputSampleSize
231 && EncodingByteOrder[i] == inputByteOrder
232 && EncodingSampleType[i] == inputSampleType) {
233 outputFourCC = EncodingFourCC[i];
234 outputEncoding = EncodingNative[i]; // EMMFSoundEncoding16BitPCM
235 found = true;
236 }
237 }
238
239 return found;
240 }
241
capabilitiesNativeToQt(const TMMFCapabilities & caps,const TFourCC & fourcc,QList<int> & frequencies,QList<int> & channels,QList<int> & sampleSizes,QList<QAudioFormat::Endian> & byteOrders,QList<QAudioFormat::SampleType> & sampleTypes)242 void capabilitiesNativeToQt(const TMMFCapabilities &caps,
243 const TFourCC &fourcc,
244 QList<int> &frequencies,
245 QList<int> &channels,
246 QList<int> &sampleSizes,
247 QList<QAudioFormat::Endian> &byteOrders,
248 QList<QAudioFormat::SampleType> &sampleTypes) {
249
250 frequencies.clear();
251 sampleSizes.clear();
252 byteOrders.clear();
253 sampleTypes.clear();
254 channels.clear();
255
256 for (int i=0; i<SampleRateCount; ++i)
257 if (caps.iRate & SampleRateListNative[i])
258 frequencies += SampleRateListQt[i];
259
260 for (int i=0; i<ChannelsCount; ++i)
261 if (caps.iChannels & ChannelsListNative[i])
262 channels += ChannelsListQt[i];
263
264 for (int i=0; i<EncodingCount; ++i) {
265 if (fourcc == EncodingFourCC[i]) {
266 sampleSizes += EncodingSampleSize[i];
267 byteOrders += EncodingByteOrder[i];
268 sampleTypes += EncodingSampleType[i];
269 }
270 }
271 }
272
formatQtToNative(const QAudioFormat & inputFormat,TUint32 & outputFourCC,TMMFCapabilities & outputFormat)273 bool formatQtToNative(const QAudioFormat &inputFormat,
274 TUint32 &outputFourCC,
275 TMMFCapabilities &outputFormat) {
276
277 bool result = false;
278
279 // Need to use temporary variables because TMMFCapabilities fields are all
280 // TInt, rather than MMF enumerated types.
281 TMMFSampleRate outputSampleRate;
282 TMMFMonoStereo outputChannels;
283 TMMFSoundEncoding outputEncoding;
284
285 if (inputFormat.codec() == QLatin1String("audio/pcm")) {
286 result =
287 sampleRateQtToNative(inputFormat.frequency(), outputSampleRate)
288 && channelsQtToNative(inputFormat.channels(), outputChannels)
289 && sampleInfoQtToNative(inputFormat.sampleSize(),
290 inputFormat.byteOrder(),
291 inputFormat.sampleType(),
292 outputFourCC,
293 outputEncoding);
294 }
295
296 if (result) {
297 outputFormat.iRate = outputSampleRate;
298 outputFormat.iChannels = outputChannels;
299 outputFormat.iEncoding = outputEncoding;
300 }
301
302 return result;
303 }
304
stateNativeToQt(State nativeState)305 QAudio::State stateNativeToQt(State nativeState)
306 {
307 switch (nativeState) {
308 case ClosedState:
309 return QAudio::StoppedState;
310 case InitializingState:
311 return QAudio::StoppedState;
312 case ActiveState:
313 return QAudio::ActiveState;
314 case IdleState:
315 return QAudio::IdleState;
316 case SuspendedPausedState:
317 case SuspendedStoppedState:
318 return QAudio::SuspendedState;
319 default:
320 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid state");
321 return QAudio::StoppedState; // suppress compiler warning
322 }
323 }
324
bytesToSamples(const QAudioFormat & format,qint64 length)325 qint64 bytesToSamples(const QAudioFormat &format, qint64 length)
326 {
327 return length / ((format.sampleSize() / 8) * format.channels());
328 }
329
samplesToBytes(const QAudioFormat & format,qint64 samples)330 qint64 samplesToBytes(const QAudioFormat &format, qint64 samples)
331 {
332 return samples * (format.sampleSize() / 8) * format.channels();
333 }
334
335 } // namespace Utils
336
337
338 //-----------------------------------------------------------------------------
339 // DevSoundWrapper
340 //-----------------------------------------------------------------------------
341
DevSoundWrapper(QAudio::Mode mode,QObject * parent)342 DevSoundWrapper::DevSoundWrapper(QAudio::Mode mode, QObject *parent)
343 : QObject(parent)
344 , m_mode(mode)
345 , m_state(StateIdle)
346 , m_devsound(0)
347 , m_fourcc(0)
348 {
349 QT_TRAP_THROWING(m_devsound = CMMFDevSound::NewL());
350
351 switch (mode) {
352 case QAudio::AudioOutput:
353 m_nativeMode = EMMFStatePlaying;
354 break;
355
356 case QAudio::AudioInput:
357 m_nativeMode = EMMFStateRecording;
358 break;
359
360 default:
361 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
362 }
363
364 getSupportedCodecs();
365 }
366
~DevSoundWrapper()367 DevSoundWrapper::~DevSoundWrapper()
368 {
369 delete m_devsound;
370 }
371
supportedCodecs() const372 const QList<QString>& DevSoundWrapper::supportedCodecs() const
373 {
374 return m_supportedCodecs;
375 }
376
initialize(const QString & codec)377 void DevSoundWrapper::initialize(const QString& codec)
378 {
379 Q_ASSERT(StateInitializing != m_state);
380 m_state = StateInitializing;
381 if (QLatin1String("audio/pcm") == codec) {
382 m_fourcc = KMMFFourCCCodePCM16;
383 TRAPD(err, m_devsound->InitializeL(*this, m_fourcc, m_nativeMode));
384 if (KErrNone != err) {
385 m_state = StateIdle;
386 emit initializeComplete(err);
387 }
388 } else {
389 emit initializeComplete(KErrNotSupported);
390 }
391 }
392
supportedFrequencies() const393 const QList<int>& DevSoundWrapper::supportedFrequencies() const
394 {
395 Q_ASSERT(StateInitialized == m_state);
396 return m_supportedFrequencies;
397 }
398
supportedChannels() const399 const QList<int>& DevSoundWrapper::supportedChannels() const
400 {
401 Q_ASSERT(StateInitialized == m_state);
402 return m_supportedChannels;
403 }
404
supportedSampleSizes() const405 const QList<int>& DevSoundWrapper::supportedSampleSizes() const
406 {
407 Q_ASSERT(StateInitialized == m_state);
408 return m_supportedSampleSizes;
409 }
410
supportedByteOrders() const411 const QList<QAudioFormat::Endian>& DevSoundWrapper::supportedByteOrders() const
412 {
413 Q_ASSERT(StateInitialized == m_state);
414 return m_supportedByteOrders;
415 }
416
supportedSampleTypes() const417 const QList<QAudioFormat::SampleType>& DevSoundWrapper::supportedSampleTypes() const
418 {
419 Q_ASSERT(StateInitialized == m_state);
420 return m_supportedSampleTypes;
421 }
422
isFormatSupported(const QAudioFormat & format) const423 bool DevSoundWrapper::isFormatSupported(const QAudioFormat &format) const
424 {
425 Q_ASSERT(StateInitialized == m_state);
426 return m_supportedCodecs.contains(format.codec())
427 && m_supportedFrequencies.contains(format.frequency())
428 && m_supportedChannels.contains(format.channels())
429 && m_supportedSampleSizes.contains(format.sampleSize())
430 && m_supportedSampleTypes.contains(format.sampleType())
431 && m_supportedByteOrders.contains(format.byteOrder());
432 }
433
samplesProcessed() const434 int DevSoundWrapper::samplesProcessed() const
435 {
436 int result = 0;
437 if (StateInitialized == m_state) {
438 switch (m_mode) {
439 case QAudio::AudioInput:
440 result = m_devsound->SamplesRecorded();
441 break;
442 case QAudio::AudioOutput:
443 result = m_devsound->SamplesPlayed();
444 break;
445 }
446 }
447 return result;
448 }
449
setFormat(const QAudioFormat & format)450 bool DevSoundWrapper::setFormat(const QAudioFormat &format)
451 {
452 Q_ASSERT(StateInitialized == m_state);
453 bool result = false;
454 TUint32 fourcc;
455 TMMFCapabilities nativeFormat;
456 if (Utils::formatQtToNative(format, fourcc, nativeFormat)) {
457 TMMFCapabilities currentNativeFormat = m_devsound->Config();
458 nativeFormat.iBufferSize = currentNativeFormat.iBufferSize;
459 TRAPD(err, m_devsound->SetConfigL(nativeFormat));
460 result = (KErrNone == err);
461 }
462 return result;
463 }
464
start()465 bool DevSoundWrapper::start()
466 {
467 Q_ASSERT(StateInitialized == m_state);
468 int err = KErrArgument;
469 switch (m_mode) {
470 case QAudio::AudioInput:
471 TRAP(err, m_devsound->RecordInitL());
472 break;
473 case QAudio::AudioOutput:
474 TRAP(err, m_devsound->PlayInitL());
475 break;
476 }
477 return (KErrNone == err);
478 }
479
pause()480 bool DevSoundWrapper::pause()
481 {
482 Q_ASSERT(StateInitialized == m_state);
483 const bool canPause = isResumeSupported();
484 if (canPause)
485 m_devsound->Pause();
486 else
487 stop();
488 return canPause;
489 }
490
resume()491 void DevSoundWrapper::resume()
492 {
493 Q_ASSERT(StateInitialized == m_state);
494 Q_ASSERT(isResumeSupported());
495 // TODO: QTBUG-13625
496 }
497
stop()498 void DevSoundWrapper::stop()
499 {
500 m_devsound->Stop();
501 }
502
bufferProcessed()503 void DevSoundWrapper::bufferProcessed()
504 {
505 Q_ASSERT(StateInitialized == m_state);
506 switch (m_mode) {
507 case QAudio::AudioInput:
508 m_devsound->RecordData();
509 break;
510 case QAudio::AudioOutput:
511 m_devsound->PlayData();
512 break;
513 }
514 }
515
getSupportedCodecs()516 void DevSoundWrapper::getSupportedCodecs()
517 {
518 /*
519 * TODO: once we support formats other than PCM, this function should
520 * convert the array of FourCC codes into MIME types for each codec.
521 *
522 RArray<TFourCC> fourcc;
523 QT_TRAP_THROWING(CleanupClosePushL(&fourcc));
524
525 TMMFPrioritySettings settings;
526 switch (mode) {
527 case QAudio::AudioOutput:
528 settings.iState = EMMFStatePlaying;
529 m_devsound->GetSupportedInputDataTypesL(fourcc, settings);
530 break;
531
532 case QAudio::AudioInput:
533 settings.iState = EMMFStateRecording;
534 m_devsound->GetSupportedInputDataTypesL(fourcc, settings);
535 break;
536
537 default:
538 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
539 }
540
541 CleanupStack::PopAndDestroy(); // fourcc
542 */
543
544 m_supportedCodecs.append(QLatin1String("audio/pcm"));
545 }
546
populateCapabilities()547 void DevSoundWrapper::populateCapabilities()
548 {
549 m_supportedFrequencies.clear();
550 m_supportedChannels.clear();
551 m_supportedSampleSizes.clear();
552 m_supportedByteOrders.clear();
553 m_supportedSampleTypes.clear();
554
555 const TMMFCapabilities caps = m_devsound->Capabilities();
556
557 for (int i=0; i<Utils::SampleRateCount; ++i)
558 if (caps.iRate & Utils::SampleRateListNative[i])
559 m_supportedFrequencies += Utils::SampleRateListQt[i];
560
561 for (int i=0; i<Utils::ChannelsCount; ++i)
562 if (caps.iChannels & Utils::ChannelsListNative[i])
563 m_supportedChannels += Utils::ChannelsListQt[i];
564
565 for (int i=0; i<Utils::EncodingCount; ++i) {
566 if (m_fourcc == Utils::EncodingFourCC[i]) {
567 m_supportedSampleSizes += Utils::EncodingSampleSize[i];
568 m_supportedByteOrders += Utils::EncodingByteOrder[i];
569 m_supportedSampleTypes += Utils::EncodingSampleType[i];
570 }
571 }
572 }
573
isResumeSupported() const574 bool DevSoundWrapper::isResumeSupported() const
575 {
576 // TODO: QTBUG-13625
577 return false;
578 }
579
InitializeComplete(TInt aError)580 void DevSoundWrapper::InitializeComplete(TInt aError)
581 {
582 Q_ASSERT(StateInitializing == m_state);
583 if (KErrNone == aError) {
584 m_state = StateInitialized;
585 populateCapabilities();
586 } else {
587 m_state = StateIdle;
588 }
589 emit initializeComplete(aError);
590 }
591
ToneFinished(TInt aError)592 void DevSoundWrapper::ToneFinished(TInt aError)
593 {
594 Q_UNUSED(aError)
595 // This class doesn't use DevSound's tone playback functions, so should
596 // never receive this callback.
597 Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
598 }
599
BufferToBeFilled(CMMFBuffer * aBuffer)600 void DevSoundWrapper::BufferToBeFilled(CMMFBuffer *aBuffer)
601 {
602 Q_ASSERT(QAudio::AudioOutput == m_mode);
603 emit bufferToBeProcessed(aBuffer);
604 }
605
PlayError(TInt aError)606 void DevSoundWrapper::PlayError(TInt aError)
607 {
608 Q_ASSERT(QAudio::AudioOutput == m_mode);
609 emit processingError(aError);
610 }
611
BufferToBeEmptied(CMMFBuffer * aBuffer)612 void DevSoundWrapper::BufferToBeEmptied(CMMFBuffer *aBuffer)
613 {
614 Q_ASSERT(QAudio::AudioInput == m_mode);
615 emit bufferToBeProcessed(aBuffer);
616 }
617
RecordError(TInt aError)618 void DevSoundWrapper::RecordError(TInt aError)
619 {
620 Q_ASSERT(QAudio::AudioInput == m_mode);
621 emit processingError(aError);
622 }
623
ConvertError(TInt aError)624 void DevSoundWrapper::ConvertError(TInt aError)
625 {
626 Q_UNUSED(aError)
627 // This class doesn't use DevSound's format conversion functions, so
628 // should never receive this callback.
629 Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback");
630 }
631
DeviceMessage(TUid aMessageType,const TDesC8 & aMsg)632 void DevSoundWrapper::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg)
633 {
634 Q_UNUSED(aMessageType)
635 Q_UNUSED(aMsg)
636 // Ignore this callback.
637 }
638
639
640 } // namespace SymbianAudio
641
642 QT_END_NAMESPACE
643
644
645