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