diff --git a/src/activities/explore_farm_animals/AnimalLevels.qml b/src/activities/explore_farm_animals/AnimalLevels.qml index cf4e7db69..567bbd3eb 100644 --- a/src/activities/explore_farm_animals/AnimalLevels.qml +++ b/src/activities/explore_farm_animals/AnimalLevels.qml @@ -1,118 +1,117 @@ /* GCompris - AnimalLevels.qml * * Copyright (C) 2015 Ayush Agrawal * * Authors: * Beth Hadley (GTK+ version) * Ayush Agrawal (Qt Quick port) * Djalil MESLI (Qt Quick port) * Johnny Jazeix (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../../core" import "explore-level.js" as Activity Image { id: animalImg width: animalWidth height: animalHeight sourceSize.width: width sourceSize.height: height fillMode: Image.PreserveAspectFit property string name: name property alias starVisible: star.visible property int questionId property string title property string description property string imageSource property string question property string audio - property string completeTask : "qrc:/gcompris/src/core/resource/sounds/completetask.wav" signal displayDescription(var animal) SequentialAnimation { id: anim running: true loops: 1 NumberAnimation { target: animalImg property: "rotation" from: 0; to: 360 duration: 400 + Math.floor(Math.random() * 400) easing.type: Easing.InOutQuad } } Image { id: star x: animalImg.width / 2.5 y: animalImg.height * 0.8 visible: false source:"qrc:/gcompris/src/core/resource/star.png" } MultiPointTouchArea { id: touchArea anchors.centerIn: parent // Make the item big enough to be clicked easily width: Math.max(parent.width, 55 * ApplicationInfo.ratio) height: Math.max(parent.height, 55 * ApplicationInfo.ratio) touchPoints: [ TouchPoint { id: point1 } ] mouseEnabled: progressbar.value != progressbar.maximumValue && !items.bonus.isPlaying onPressed: { if(items.progressbar.value >= progressbar.maximumValue) { return } var questionTargetId = items.questionOrder[Activity.items.progressbar.value] Activity.items.instruction.visible = false if (Activity.items.score.currentSubLevel == 1) { audioVoices.play(animalImg.audio); displayDescription(animalImg) star.visible = true; } else { if (questionId === questionTargetId) { animWin.start(); items.progressbar.value ++; - items.audioEffects.play(completeTask); + items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav"); Activity.nextSubSubLevel(); } else { items.bonus.bad("smiley") } } } } SequentialAnimation { id: animWin running: false loops: 1 NumberAnimation { target: animalImg property: "rotation" from: 0; to: 360 duration: 600 easing.type: Easing.InOutQuad } } } diff --git a/src/activities/explore_farm_animals/explore-level.js b/src/activities/explore_farm_animals/explore-level.js index 9470204a2..ca5897b42 100644 --- a/src/activities/explore_farm_animals/explore-level.js +++ b/src/activities/explore_farm_animals/explore-level.js @@ -1,147 +1,144 @@ /* GCompris - explore-level.js * * Copyright (C) 2015 Ayush Agrawal * * Authors: * Beth Hadley (GTK+ version) * Ayush Agrawal (Qt Quick port) * Djalil MESLI (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ .pragma library .import GCompris 1.0 as GCompris .import "qrc:/gcompris/src/core/core.js" as Core var numberOfLevels var items var url var currentLevel function start(items_,url_,levelCount_) { items = items_ url = url_ numberOfLevels = levelCount_ currentLevel = 1 items.score.currentSubLevel = 1 initLevel() } function stop() { items.audioVoices.stop() } function initLevel() { items.bar.level = currentLevel var filename = url + "board" + "/" + "board" + currentLevel + ".qml" items.dataset.source = filename items.progressbar.value = 0 items.progressbar.maximumValue = items.dataModel.count items.score.numberOfSubLevels = items.hasAudioQuestions ? 3 : 2; // randomize the questions for level 2 and 3 Core.shuffle(items.questionOrder); // Change the currentSubLevel value to 1 to be sure to update the question value // else if you are sublevel 0 and go to last level, the question is not the good one items.progressbar.value = 1 items.progressbar.value = 0 items.descriptionPanel.visible = false items.instruction.visible = true reload(); } function nextLevel() { ++items.score.currentSubLevel if(numberOfLevels <= currentLevel && items.score.numberOfSubLevels < items.score.currentSubLevel) { currentLevel = 0 } if (items.score.numberOfSubLevels < items.score.currentSubLevel) { currentLevel++ items.score.currentSubLevel = 1 } initLevel(); // Stop audio if necessary (switch from level 2 at beginning to a new level for example) items.audioVoices.stop() if (items.score.currentSubLevel == 2) { items.progressbar.value = 0; - initSubSubLevel(false); + initSubSubLevel(); } } function previousLevel() { --items.score.currentSubLevel if(currentLevel <= 1 && items.score.currentSubLevel < 1) { currentLevel = numberOfLevels items.score.currentSubLevel = items.score.numberOfSubLevels } else if(items.score.currentSubLevel < 1) { currentLevel-- items.score.currentSubLevel = items.score.numberOfSubLevels } initLevel(); // Stop audio if necessary (switch from level 2 at beginning to a new level for example) items.audioVoices.stop() if(items.score.currentSubLevel == 2 && items.hasAudioQuestions) { repeat(); } } function isComplete() { for(var i = 0 ; i < items.dataModel.count ; ++ i) { if(!items.dataModel.itemAt(i).starVisible) return false; } return true; } function initSubSubLevel(IsNext) { if(items.progressbar.value == items.dataModel.count) { items.bonus.good("smiley"); } if(items.score.currentSubLevel == 2 && items.hasAudioQuestions && getCurrentQuestion()) { - // After a good answer, play the audio effect and then play voice - if (IsNext == false) { - repeat(); - } - else { - items.nextQuestion.start(); - } + repeat(); } } function nextSubSubLevel() { + items.audioVoices.silence(2000) initSubSubLevel(true) } function reload() { for(var i = 0 ; i < items.dataModel.count ; ++ i) { items.dataModel.itemAt(i).starVisible = false; } } function repeat() { - items.audioVoices.play(getCurrentQuestion().audio); + items.audioVoices.stop() + items.audioVoices.clearQueue() + items.audioVoices.append(getCurrentQuestion().audio); } function getCurrentQuestion() { return items.dataset.item.tab[items.questionOrder[items.progressbar.value]]; } diff --git a/src/core/GCAudio.qml b/src/core/GCAudio.qml index 057e82d91..ba52bf905 100644 --- a/src/core/GCAudio.qml +++ b/src/core/GCAudio.qml @@ -1,235 +1,224 @@ /* GCompris - GCAudio.qml * * Copyright (C) 2014 Johnny Jazeix * * Authors: * Johnny Jazeix * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import QtMultimedia 5.0 import GCompris 1.0 /** * A QML component for audio playback. * @ingroup components * * Wrapper component around QtQuick's Audio element, handling all audio * playback in GCompris uniformly. * * It maintains a queue of audio-sources (@ref files) that are played back * sequentially, to which the user can enqueue files that should be scheduled * for playback. * * To make sure an audio voice will be localized, replace the locale part * of the file by '$LOCALE'. * * To makes sure that all audio sources are normalized with respect to * ApplicationInfo.CompressedAudio replace the 'ogg' part of the file by * '$CA'. * * @inherit QtQuick.Item */ Item { id: gcaudio /** * type:bool * Whether audio should be muted. */ property bool muted /** * type:url * URL to the audio source to be played back. */ property alias source: audio.source /** * type:string * Detailed error message in case of playback errors. */ property alias errorString: audio.errorString /** * type:var * Current playback state. * * Possible values taken from Audio.status */ property var playbackState: (audio.error == Audio.NoError) ? audio.playbackState : Audio.StoppedState; /** * type:list * Playback queue. */ property var files: [] /** * Emitted in case of error. */ signal error /** * Emitted when playback of all scheduled audio sources has finished. */ signal done /** * When mute is changed we set the volume to 0 to mute a potential playing * sound. */ onMutedChanged: muted ? audio.volume = 0 : audio.volume = 1 /** * Plays back the audio resource @p file. * * @param type:string file [optional] URL to an audio source. * @returns @c true if playback has been started, @c false if file does not * exist or audio is muted */ function play(file) { if(!fileId.exists(file) || muted) return false - // @FIXME There was a bug in gstreamer where wav files did freeze us on Linux. - // If you still hit this bug, uncomment this to disable wav files - // https://bugreports.qt.io/browse/QTBUG-49689 - //if(/.wav$/.test(file) && ApplicationInfo.platform == ApplicationInfo.Linux) - // return false - if(file) { // 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 source = "" source = file } if(!muted) { audio.play() } return true } /** * Stops audio playback. */ function stop() { if(audio.playbackState != Audio.StoppedState) audio.stop() } /** * Schedules a @p file for audio playback. * * If there is no playback currently running, the new source will be * played back immediately. Otherwise it is appended to the file queue of * sources. * * @param type:string file File to the audio file to be played back. * @returns @c true upon success, or @c false if @p file does not exist or * audio is muted */ function append(file) { if(!fileId.exists(file) || muted) return false - // @FIXME There was a bug in gstreamer where wav files did freeze us on Linux. - // If you still hit this bug, uncomment this to disable wav files - // https://bugreports.qt.io/browse/QTBUG-49689 - //if(/.wav$/.test(file) && ApplicationInfo.platform == ApplicationInfo.Linux) - // return false - if(audio.playbackState !== Audio.PlayingState || audio.status === Audio.EndOfMedia || audio.status === Audio.NoMedia || audio.status === Audio.InvalidMedia) { // Setting the source to "" on Linux fix a case where the sound is no more played source = "" source = file - audio.play() + files.push(file) + silenceTimer.start() } else { files.push(file) } return true } /** * Adds a pause of the given duration in ms before playing of the next file. * * @param type:int duration_ms Pause in milliseconds. */ function silence(duration_ms) { silenceTimer.interval = duration_ms } /** * Flushes the list of scheduled files. * @sa files */ function clearQueue() { while(files.length > 0) { files.pop(); } } /// @cond INTERNAL_DOCS function _playNextFile() { var nextFile = files.shift() if(!nextFile || 0 === nextFile.length) { audio.source = "" gcaudio.done() } else { audio.source = "" audio.source = nextFile if(!muted) audio.play() } } Audio { id: audio onError: { // This file cannot be played, remove it from the source asap source = "" if(files.length) silenceTimer.start() else gcaudio.error() } onStopped: { if(files.length) silenceTimer.start() else gcaudio.done() } } Timer { id: silenceTimer repeat: false onTriggered: { interval = 0 _playNextFile() } } File { id: fileId } /// @endcond }