Warning, /education/gcompris/src/core/GCAudio.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - GCAudio.qml 0002 * 0003 * SPDX-FileCopyrightText: 2014 Johnny Jazeix <jazeix@gmail.com> 0004 * 0005 * Authors: 0006 * Johnny Jazeix <jazeix@gmail.com> 0007 * 0008 * SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 import QtQuick 2.12 0011 import QtMultimedia 5.12 0012 import GCompris 1.0 0013 0014 /** 0015 * A QML component for audio playback. 0016 * @ingroup components 0017 * 0018 * Wrapper component around QtQuick's Audio element, handling all audio 0019 * playback in GCompris uniformly. 0020 * 0021 * It maintains a queue of audio-sources (@ref files) that are played back 0022 * sequentially, to which the user can enqueue files that should be scheduled 0023 * for playback. 0024 * 0025 * To make sure an audio voice will be localized, replace the locale part 0026 * of the file by '$LOCALE'. 0027 * 0028 * To makes sure that all audio sources are normalized with respect to 0029 * ApplicationInfo.CompressedAudio replace the 'ogg' part of the file by 0030 * '$CA'. 0031 * 0032 * @inherit QtQuick.Item 0033 */ 0034 Item { 0035 id: gcaudio 0036 0037 /** 0038 * type:bool 0039 * Whether audio should be muted. 0040 */ 0041 property bool muted 0042 0043 /** 0044 * type:url 0045 * URL to the audio source to be played back. 0046 */ 0047 property alias source: audio.source 0048 0049 /** 0050 * type: positive real number less than 1 0051 * Determines intensity of the audio. 0052 */ 0053 property alias volume: audio.volume 0054 0055 /** 0056 * type:bool 0057 * Whether the audio element contains audio. 0058 */ 0059 property bool hasAudio: audio.hasAudio 0060 0061 /** 0062 * type:string 0063 * Detailed error message in case of playback errors. 0064 */ 0065 property alias errorString: audio.errorString 0066 0067 /** 0068 * type:bool 0069 * check if the player is for background music 0070 */ 0071 property bool isBackgroundMusic: false 0072 0073 /** 0074 * type:array 0075 * background music metadata 0076 */ 0077 property var metaDataMusic: ["", "", "", ""] 0078 0079 /** 0080 * Trigger this signal externally to play the next audio in the "files". This, in turn, stops the currently playing audio and check the necessary 0081 * conditions (see onStopped signal in "audio" element) and decides what needs to be done for the next audio. 0082 */ 0083 signal nextAudio() 0084 onNextAudio: stop() 0085 0086 /** 0087 * type:var 0088 * Current playback state. 0089 * 0090 * Possible values taken from Audio.status 0091 */ 0092 property var playbackState: (audio.error === Audio.NoError) ? 0093 audio.playbackState : Audio.StoppedState; 0094 0095 /** 0096 * type:list 0097 * Playback queue. 0098 */ 0099 property var files: [] 0100 0101 /** 0102 * Emitted in case of error. 0103 */ 0104 signal error 0105 0106 /** 0107 * Emitted when playback of all scheduled audio sources has finished. 0108 */ 0109 signal done 0110 0111 //Pauses the currently playing audio 0112 function pause() { 0113 if(playbackState === Audio.PlayingState) 0114 audio.pause() 0115 } 0116 0117 //Resumes the current audio if it had been paused 0118 function resume() { 0119 if(playbackState === Audio.PausedState || playbackState === Audio.StoppedState) 0120 audio.play() 0121 } 0122 0123 // Helper to know if there is already an audio playing 0124 function isPlaying() { 0125 return playbackState === Audio.PlayingState; 0126 } 0127 /** 0128 * Plays back the audio resource @p file. 0129 * 0130 * @param type:string file [optional] URL to an audio source. 0131 * @returns @c true if playback has been started, @c false if file does not 0132 * exist or audio is muted 0133 */ 0134 function play(file) { 0135 if(!fileId.exists(file) || muted) 0136 return false 0137 0138 if(file) { 0139 // Setting the source to "" on Linux fix a case where the sound is no more played if you play twice the same sound in a row 0140 source = "" 0141 source = file 0142 } 0143 if(!muted) { 0144 audio.play() 0145 } 0146 return true 0147 } 0148 0149 /** 0150 * Stops audio playback. 0151 */ 0152 function stop() { 0153 if(audio.playbackState != Audio.StoppedState) 0154 audio.stop() 0155 } 0156 0157 /** 0158 * Schedules a @p file for audio playback. 0159 * 0160 * If there is no playback currently running, the new source will be 0161 * played back immediately. Otherwise it is appended to the file queue of 0162 * sources. 0163 * 0164 * @param type:string file File to the audio file to be played back. 0165 * @returns @c true upon success, or @c false if @p file does not exist or 0166 * audio is muted 0167 */ 0168 function append(file) { 0169 if(!fileId.exists(file) || muted) 0170 return false 0171 0172 if(audio.playbackState !== Audio.PlayingState 0173 || audio.status === Audio.EndOfMedia 0174 || audio.status === Audio.NoMedia 0175 || audio.status === Audio.InvalidMedia) { 0176 files.push(file) 0177 silenceTimer.interval = 35 0178 silenceTimer.start() 0179 } else { 0180 files.push(file) 0181 } 0182 return true 0183 } 0184 0185 /** 0186 * Adds a pause of the given duration in ms before playing of the next file. 0187 * 0188 * @param type:int duration_ms Pause in milliseconds. 0189 */ 0190 function silence(duration_ms) { 0191 silenceTimer.interval = duration_ms 0192 } 0193 0194 /** 0195 * Flushes the list of scheduled files. 0196 * @sa files 0197 */ 0198 function clearQueue() { 0199 while(files.length > 0) { 0200 files.pop(); 0201 } 0202 } 0203 0204 /// @cond INTERNAL_DOCS 0205 0206 function _playNextFile() { 0207 if(files.length == 0) { 0208 gcaudio.done() 0209 return 0210 } 0211 0212 var nextFile = files.shift() 0213 if(nextFile === '') { 0214 source = "" 0215 gcaudio.done() 0216 } else { 0217 // on Ubuntu Touch, emptying the source result in an Audio error which triggers that method again endlessly 0218 if (ApplicationInfo.platform !== ApplicationInfo.UbuntuTouchOS) { 0219 source = "" 0220 } 0221 source = nextFile 0222 if(!muted) 0223 audio.play() 0224 } 0225 } 0226 0227 Audio { 0228 id: audio 0229 muted: gcaudio.muted 0230 onError: { 0231 // This file cannot be played, remove it from the source asap 0232 source = "" 0233 if(files.length) 0234 silenceTimer.start() 0235 else 0236 gcaudio.error() 0237 } 0238 onStopped: { 0239 if(files.length) 0240 silenceTimer.start() 0241 else 0242 gcaudio.done() 0243 } 0244 metaData.onMetaDataChanged: { 0245 if(isBackgroundMusic) { 0246 metaDataMusic = [metaData.title, metaData.contributingArtist, metaData.year, metaData.copyright] 0247 } 0248 } 0249 } 0250 0251 Timer { 0252 id: silenceTimer 0253 repeat: false 0254 onTriggered: { 0255 interval = 0 0256 _playNextFile() 0257 } 0258 } 0259 0260 File { 0261 id: fileId 0262 } 0263 0264 /// @endcond 0265 0266 }