diff --git a/src/activities/canal_lock/CanalLock.qml b/src/activities/canal_lock/CanalLock.qml index 81f396eac..6b73b4687 100644 --- a/src/activities/canal_lock/CanalLock.qml +++ b/src/activities/canal_lock/CanalLock.qml @@ -1,471 +1,471 @@ /* GCompris - canal_lock.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin (GTK+ version) * Bruno Coudoin (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 "." ActivityBase { id: activity onStart: focus = true onStop: {} property string url: "qrc:/gcompris/src/activities/canal_lock/resource/" pageComponent: Item { id: background anchors.fill: parent property int running: 0 signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } onStart: water.state = 'down' Image { id: sky source: activity.url + "sky.svg" sourceSize.width: parent.width anchors.top: parent.top height: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.6 } Image { source: activity.url + "sun.svg" sourceSize.width: Math.min(120 * ApplicationInfo.ratio, parent.width * 0.15) x: 10 y: 10 } Image { source: activity.url + "cloud1.svg" sourceSize.width: Math.min(120 * ApplicationInfo.ratio, parent.width * 0.15) anchors.top: parent.top anchors.left: parent.left anchors.leftMargin: parent.width * 0.18 anchors.topMargin: parent.height * 0.1 } Image { source: activity.url + "cloud2.svg" sourceSize.width: Math.min(130 * ApplicationInfo.ratio, parent.width * 0.2) anchors.top: parent.top anchors.left: parent.left anchors.leftMargin: parent.width * 0.25 anchors.topMargin: parent.height * 0.02 } Image { source: activity.url + "ground.svg" sourceSize.width: parent.width anchors.bottom: parent.bottom height: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.3 } Image { id: canal source: activity.url + "canal_lock.svg" anchors.fill: parent sourceSize.width: parent.width fillMode: Image.PreserveAspectFit Image { source: activity.url + "canal_left.svg" anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left width: (background.width - parent.paintedWidth) / 2 + 1 sourceSize.height: parent.paintedHeight } Image { source: activity.url + "canal_right.svg" anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right width: (background.width - parent.paintedWidth) / 2 + 1 sourceSize.height: parent.paintedHeight } Rectangle { id: water anchors.bottom: parent.bottom anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.23 anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenterOffset: parent.paintedWidth * 0.035 color: "#4f76d6" width: parent.paintedWidth * 0.205 height: minHeight state: "undef" property int maxHeight: parent.paintedHeight * 0.33 property int minHeight: canal.paintedHeight * 0.15 property int duration: 3500 Behavior on height { NumberAnimation { duration: water.duration } } onStateChanged: { if( water.state == "undef") return - activity.audioEffects.append(activity.url + 'water_fill.wav') + activity.audioEffects.play(activity.url + 'water_fill.wav') if( water.state == 'up' && boat.state == 'middleDown') boat.state = 'middleUp' else if( water.state == 'down' && boat.state == 'middleUp') boat.state = 'middleDown' } states: [ State { name: "undef" PropertyChanges { target: water; height: water.minHeight} }, State { name: "down" PropertyChanges { target: water; height: water.minHeight} }, State { name: "up" PropertyChanges { target: water; height: water.maxHeight} } ] } Lock { id: lock1 color: "#dfb625" anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.03 anchors.horizontalCenterOffset: - parent.paintedWidth * 0.16 minHeight: canal.paintedHeight * 0.05 maxHeight: parent.paintedHeight * 0.18 duration: 0 MouseArea { anchors.fill: parent anchors.margins: -20 * ApplicationInfo.ratio onClicked: { if(background.running) return lock1.duration = 400 if(lock1.state == 'close' && door2.state == 'close' && lock2.state == 'close') { activity.audioEffects.play(activity.url + 'lock.wav') lock1.state = 'open' water.state = 'down' } else if(lock1.state == 'open') { activity.audioEffects.play(activity.url + 'lock.wav') lock1.state = 'close' } else { activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") } } } } Lock { id: lock2 color: "#dfb625" anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.03 anchors.horizontalCenterOffset: parent.paintedWidth * 0.22 minHeight: canal.paintedHeight * 0.05 maxHeight: parent.paintedHeight * 0.18 duration: 0 MouseArea { anchors.fill: parent anchors.margins: -20 * ApplicationInfo.ratio onClicked: { if(background.running) return lock2.duration = lock1.duration if(lock2.state == 'close' && door1.state == 'close' && lock1.state == 'close') { activity.audioEffects.play(activity.url + 'lock.wav') lock2.state = 'open' water.state = 'up' } else if(lock2.state == 'open') { activity.audioEffects.play(activity.url + 'lock.wav') lock2.state = 'close' } else { activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") } } } } Lock { id: door1 color: "#31cb25" anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.2 anchors.horizontalCenterOffset: - parent.paintedWidth * 0.07 minHeight: canal.paintedHeight * 0.05 maxHeight: canal.paintedHeight * 0.4 duration: 0 MouseArea { anchors.fill: parent anchors.margins: -20 * ApplicationInfo.ratio onClicked: { if(background.running) return door1.duration = water.duration if(door1.state == 'close' && water.state == 'down') { door1.state = 'open' leftLight.state = 'green' activity.audioEffects.play(activity.url + 'door_open.wav') } else if(door1.state == 'open') { door1.state = 'close' leftLight.state = 'red' activity.audioEffects.play(activity.url + 'door_close.wav') } else { activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") } } } } Lock { id: door2 color: "#31cb25" anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.2 anchors.horizontalCenterOffset: parent.paintedWidth * 0.14 minHeight: canal.paintedHeight * 0.15 maxHeight: canal.paintedHeight * 0.4 duration: 0 MouseArea { anchors.fill: parent anchors.margins: -20 * ApplicationInfo.ratio onClicked: { if(background.running) return door2.duration = water.duration if(door2.state == 'close' && water.state == 'up') { door2.state = 'open' rightLight.state = 'green' activity.audioEffects.play(activity.url + 'door_open.wav') } else if(door2.state == 'open') { door2.state = 'close' rightLight.state = 'red' activity.audioEffects.play(activity.url + 'door_close.wav') } else { activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") } } } } Image { id: leftLight source: activity.url + "light_red.svg" anchors.bottom: parent.bottom anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.46 anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenterOffset: - parent.paintedWidth * 0.18 sourceSize.height: parent.paintedHeight * 0.1 states: [ State { name: "green" PropertyChanges { target: leftLight source: activity.url + "light_green.svg" } }, State { name: "red" PropertyChanges { target: leftLight source: activity.url + "light_red.svg" } } ] } Image { id: rightLight source: activity.url + "light_red.svg" anchors.bottom: parent.bottom anchors.bottomMargin: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.60 anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenterOffset: parent.paintedWidth * 0.20 sourceSize.height: parent.paintedHeight * 0.1 mirror: true states: [ State { name: "green" PropertyChanges { target: rightLight source: activity.url + "light_green.svg" } }, State { name: "red" PropertyChanges { target: rightLight source: activity.url + "light_red.svg" } } ] } Image { id: boat source: activity.url + "boat1.svg" sourceSize.width: water.width * 0.74 anchors { bottom: parent.bottom bottomMargin: leftPositionY horizontalCenter: parent.horizontalCenter horizontalCenterOffset: leftPositionX onHorizontalCenterOffsetChanged: { if(boat.anchors.horizontalCenterOffset == boat.rightPositionX) { boat.source = activity.url + "boat2.svg" bonus.good("flower") } else if(boat.anchors.horizontalCenterOffset == boat.leftPositionX) { boat.source = activity.url + "boat1.svg" } } } state: 'left' property int leftPositionX: - (parent.paintedWidth / 2) * 0.8 property int leftPositionY: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.37 property int middlePositionX: canal.paintedWidth * 0.035 property int rightPositionX: (parent.paintedWidth / 2) * 0.7 property int rightPositionY: (background.height - canal.paintedHeight) / 2 + canal.paintedHeight * 0.55 // > 0.5 < 0.6 property int duration: 0 Behavior on anchors.horizontalCenterOffset { NumberAnimation { duration: boat.duration onRunningChanged: background.running ? background.running++ : background.running-- } } Behavior on anchors.bottomMargin { NumberAnimation { duration: boat.duration onRunningChanged: background.running ? background.running++ : background.running-- } } MouseArea { anchors.fill: parent onClicked: { if(background.running) return boat.duration = water.duration var prevState = boat.state if(boat.state == "left" && door1.state == "open") boat.state = "middleDown" else if(boat.state == "middleUp" && door2.state == "open") boat.state = "right" else if(boat.state == "right" && door2.state == "open") boat.state = "middleUp" else if(boat.state == "middleDown" && door1.state == "open") boat.state = "left" if(prevState !== boat.state) activity.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/water.wav') } } states: [ State { name: "left" PropertyChanges { target: boat anchors.horizontalCenterOffset: boat.leftPositionX anchors.bottomMargin: boat.leftPositionY } }, State { name: "middleDown" PropertyChanges { target: boat anchors.horizontalCenterOffset: boat.middlePositionX anchors.bottomMargin: boat.leftPositionY } }, State { name: "middleUp" PropertyChanges { target: boat anchors.horizontalCenterOffset: boat.middlePositionX anchors.bottomMargin: boat.rightPositionY } }, State { name: "right" PropertyChanges { target: boat anchors.horizontalCenterOffset: boat.rightPositionX anchors.bottomMargin: boat.rightPositionY } } ] } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home } onHelpClicked: { displayDialog(dialogHelp) } onHomeClicked: activity.home() } Bonus { id: bonus } } } diff --git a/src/activities/melody/CMakeLists.txt b/src/activities/melody/CMakeLists.txt index da44b356d..729fbd2e4 100644 --- a/src/activities/melody/CMakeLists.txt +++ b/src/activities/melody/CMakeLists.txt @@ -1 +1 @@ -GCOMPRIS_ADD_RCC(activities/melody *.qml *.svg *.js resource/*.svg resource/*.${COMPRESSED_AUDIO}) +GCOMPRIS_ADD_RCC(activities/melody *.qml *.svg *.js resource/*.svg resource/*.wav) diff --git a/src/activities/melody/Melody.qml b/src/activities/melody/Melody.qml index 79d5ca622..e525d0d25 100644 --- a/src/activities/melody/Melody.qml +++ b/src/activities/melody/Melody.qml @@ -1,248 +1,253 @@ /* GCompris - melody.qml * * Copyright (C) 2015 Bruno Coudoin * * Authors: * Jose JORGE (GTK+ version) * Bruno Coudoin (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" ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: items.url + 'xylofon_background.svg' sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property string url: "qrc:/gcompris/src/activities/melody/resource/" property var question property var questionToPlay property var answer property alias questionInterval: questionPlayer.interval property int numberOfLevel: 10 + property bool running: false } onStart: { bar.level = 1 score.numberOfSubLevels = 5 score.currentSubLevel = 1 initLevel() + items.running = true } onStop: { knock.stop() questionPlayer.stop() + items.running = false } Image { id: xylofon anchors { fill: parent margins: 10 * ApplicationInfo.ratio } source: items.url + 'xylofon.svg' sourceSize.width: parent.width * 0.7 fillMode: Image.PreserveAspectFit } Repeater { id: parts model: 4 Image { id: part source: items.url + 'xylofon_part' + (index + 1) + '.svg' rotation: - 80 anchors.horizontalCenter: xylofon.horizontalCenter anchors.horizontalCenterOffset: (- xylofon.paintedWidth) * 0.3 + xylofon.paintedWidth * index * 0.22 anchors.verticalCenter: xylofon.verticalCenter anchors.verticalCenterOffset: - xylofon.paintedHeight * 0.1 sourceSize.width: xylofon.paintedWidth * 0.5 fillMode: Image.PreserveAspectFit property alias anim: anim SequentialAnimation { id: anim NumberAnimation { target: part property: "scale" from: 1; to: 0.95 duration: 150 easing.type: Easing.InOutQuad } NumberAnimation { target: part property: "scale" from: 0.95; to: 1 duration: 150 easing.type: Easing.OutElastic } } MouseArea { anchors.fill: parent enabled: !questionPlayer.running onClicked: { anim.start() background.playNote(index) items.answer.push(index) background.checkAnswer() } } } } function playNote(index) { - activity.audioEffects.append(ApplicationInfo.getAudioFilePath(items.url + - 'xylofon_son' + (index + 1) + '.$CA')) + activity.audioEffects.play(ApplicationInfo.getAudioFilePath(items.url + + 'xylofon_son' + (index + 1) + ".wav")) } Timer { id: knock interval: 1000 repeat: false onTriggered: { questionPlayer.start() } } Timer { id: questionPlayer onTriggered: { var partIndex = items.questionToPlay.shift() if(partIndex !== undefined) { parts.itemAt(partIndex).anim.start() background.playNote(partIndex) start() } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | repeat } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: { score.currentSubLevel = 1 if(bar.level == 1) { bar.level = items.numberOfLevel } else { bar.level-- } initLevel(); } onNextLevelClicked: parent.nextLevel() onHomeClicked: activity.home() onRepeatClicked: parent.repeat() } Bonus { id: bonus onWin: { parent.nextSubLevel() parent.repeat() } onLoose: parent.repeat() } Score { id: score anchors.bottom: undefined anchors.right: parent.right anchors.rightMargin: 10 * ApplicationInfo.ratio anchors.top: parent.top } function initLevel() { items.question = [] questionPlayer.stop() var numberOfParts = 4 if(bar.level < 3) numberOfParts = 2 else if(bar.level < 5) numberOfParts = 3 for(var i = 0; i < bar.level + 2; ++i) { items.question.push(Math.floor(Math.random() * numberOfParts)) } items.questionInterval = 1200 - Math.min(500, 100 * bar.level) items.answer = [] } function nextSubLevel() { if(score.currentSubLevel < score.numberOfSubLevels) { score.currentSubLevel++ initLevel() return } nextLevel() } function nextLevel() { score.currentSubLevel = 1 if(items.numberOfLevel === bar.level ) { bar.level = 1 } else { bar.level++ } initLevel(); } function repeat() { - questionPlayer.stop() - activity.audioEffects.play(ApplicationInfo.getAudioFilePath(items.url + 'knock.ogg')) - items.questionToPlay = items.question.slice() - items.answer = [] - knock.start() + if(items.running == true) { + questionPlayer.stop() + activity.audioEffects.play(ApplicationInfo.getAudioFilePath(items.url + 'knock.wav')) + items.questionToPlay = items.question.slice() + items.answer = [] + knock.start() + } } function checkAnswer() { if(items.answer.join() == items.question.join()) bonus.good('lion') else if(items.answer.length >= items.question.length) bonus.bad('lion') } } } diff --git a/src/activities/melody/resource/README b/src/activities/melody/resource/README index 399a44bd3..e025a0be4 100644 --- a/src/activities/melody/resource/README +++ b/src/activities/melody/resource/README @@ -1,14 +1,18 @@ Copyright: 2000, 2008 Bruno Coudoin and others License: GPLv3 Files: xylofon_background.svg -xylofon_melody.ogg xylofon_part1.svg xylofon_part2.svg xylofon_part3.svg xylofon_part4.svg -xylofon_son1.ogg -xylofon_son2.ogg -xylofon_son3.ogg -xylofon_son4.ogg +xylofon_son1.wav +xylofon_son2.wav +xylofon_son3.wav +xylofon_son4.wav xylofon.svg + +Copyright: 2018 Timothée Giet +License: GPLv3 +Files: +knock.wav diff --git a/src/activities/melody/resource/knock.ogg b/src/activities/melody/resource/knock.ogg deleted file mode 100644 index 4d2d9f88c..000000000 Binary files a/src/activities/melody/resource/knock.ogg and /dev/null differ diff --git a/src/activities/melody/resource/knock.wav b/src/activities/melody/resource/knock.wav new file mode 100644 index 000000000..0bea4b10a Binary files /dev/null and b/src/activities/melody/resource/knock.wav differ diff --git a/src/activities/melody/resource/xylofon_son1.ogg b/src/activities/melody/resource/xylofon_son1.ogg deleted file mode 100644 index eaed42153..000000000 Binary files a/src/activities/melody/resource/xylofon_son1.ogg and /dev/null differ diff --git a/src/activities/melody/resource/xylofon_son1.wav b/src/activities/melody/resource/xylofon_son1.wav new file mode 100644 index 000000000..a685d3809 Binary files /dev/null and b/src/activities/melody/resource/xylofon_son1.wav differ diff --git a/src/activities/melody/resource/xylofon_son2.ogg b/src/activities/melody/resource/xylofon_son2.ogg deleted file mode 100644 index 38fb0c1fd..000000000 Binary files a/src/activities/melody/resource/xylofon_son2.ogg and /dev/null differ diff --git a/src/activities/melody/resource/xylofon_son2.wav b/src/activities/melody/resource/xylofon_son2.wav new file mode 100644 index 000000000..5bbf2a6a9 Binary files /dev/null and b/src/activities/melody/resource/xylofon_son2.wav differ diff --git a/src/activities/melody/resource/xylofon_son3.ogg b/src/activities/melody/resource/xylofon_son3.ogg deleted file mode 100644 index 17548cb88..000000000 Binary files a/src/activities/melody/resource/xylofon_son3.ogg and /dev/null differ diff --git a/src/activities/melody/resource/xylofon_son3.wav b/src/activities/melody/resource/xylofon_son3.wav new file mode 100644 index 000000000..25a15beaf Binary files /dev/null and b/src/activities/melody/resource/xylofon_son3.wav differ diff --git a/src/activities/melody/resource/xylofon_son4.ogg b/src/activities/melody/resource/xylofon_son4.ogg deleted file mode 100644 index 3a92c17fd..000000000 Binary files a/src/activities/melody/resource/xylofon_son4.ogg and /dev/null differ diff --git a/src/activities/melody/resource/xylofon_son4.wav b/src/activities/melody/resource/xylofon_son4.wav new file mode 100644 index 000000000..fdfe875ee Binary files /dev/null and b/src/activities/melody/resource/xylofon_son4.wav differ diff --git a/src/core/ActivityBase.qml b/src/core/ActivityBase.qml index b0e0663c7..dcb17a028 100644 --- a/src/core/ActivityBase.qml +++ b/src/core/ActivityBase.qml @@ -1,227 +1,227 @@ /* GCompris - ActivityBase.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 "qrc:/gcompris/src/core/core.js" as Core /** * The base QML component for activities in GCompris. * @ingroup components * * Each activity should be derived from this component. It is responsible for * * * basic common key handling, * * unified audio handling, * * screen switching dynamics (from/to Menu/DialogHelp/etc.) * * The following common keys are handled so far: * * * @c Ctrl+q: Exit the application. * * @c Ctrl+b: Toggle the bar. * * @c Ctrl+f: Toggle fullscreen. * * @c Ctrl+m: Toggle audio effects. * * @c Ctrl+w: Exit the current activity and return to the menu. * * @c Ctrl+p: Make a screenshot. * * @c Back: Return to the home screen (corresponds to the 'Back' button on * Android). * * Cf. Template.qml for a sample skeleton activity. * * Cf. * [the wiki](http://gcompris.net/wiki/Qt_Quick_development_process#Adding_a_new_activity) * for further information about creating a new activity. * * @inherit QtQuick.Item */ Item { id: page /** * type:Item * Parent object. */ property Item main: parent; /** * type:Component * The top-level component containing the visible viewport of an activity. * * Put all you want to present the user into this container. Mostly * implemented using a Rectangle or Image component, itself * containing further graphical elements. You are pretty free of doing * whatever you want inside this component. * * Also common elements as Bar, Score, DialogHelp, etc. should be placed * inside this element. */ property Component pageComponent /** * type:QtObject * Reference to the menu activity. * * Populated automatically during activity-loading. */ property QtObject menu /** * type:QtObject * Reference to the ActivityInfo object of the activity. * * Populated automatically during activity-loading. */ property QtObject activityInfo /** * type:GCAudio * The global audio item for voices. * * Because of problems synchronizing multiple Audio objects between * global/menu/main and individual activities, activities should refrain * from implementing additional Audio elements. * * Instead append to this global object to play your voices after the * intro music. - * @sa GCAudio audioEffects + * @sa GCAudio audioVoices */ property GCAudio audioVoices /** - * type:GCAudio + * type:GCSfx * The global audio item for audio effects. * - * Append to it to play your effects. - * @sa GCAudio audioEffects + * Use it to play your effects. + * @sa GCSfx audioEffects */ property GCSfx audioEffects /** * type:Loading * The global loading object. * * Start it to signal heavy computation in case of GUI freezes. * @sa Loading */ property Loading loading /** * Emitted when the user wants to return to the Home/Menu screen. */ signal home /** * Emitted when the user wants to return several views back in the * page stack. */ signal back(Item to) /** * Emitted every time the activity has been started. * * Initialize your activity upon this signal. */ signal start /** * Emitted when the activity is about to stop * * Shutdown whatever you need to upon this signal. */ signal stop /** * Emitted when dialog @p dialog should be shown * * Emit this signal when you want to show another dialog, e.g. on * Bar.onHelpClicked * * @param dialog Dialog to show. */ signal displayDialog(Item dialog) /** * Emitted when multiple @p dialogs should be pushed on the page-stack * * Emit this signal when you want to stack >1 views. The last one will be * shown the intermediated ones will be kept on the page stack for later * pop() calls. * * @param dialogs Array of dialogs to push; */ signal displayDialogs(var dialogs) onBack: menu ? menu.back(to) : "" onHome: menu ? menu.home() : "" onDisplayDialog: menu ? menu.displayDialog(dialog) : "" onDisplayDialogs: menu ? menu.displayDialogs(dialogs) : "" Keys.forwardTo: activity.children Keys.onEscapePressed: home(); Keys.onPressed: { if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_Q) { // Ctrl+Q exit the application Core.quit(main); } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_B) { // Ctrl+B toggle the bar ApplicationSettings.isBarHidden = !ApplicationSettings.isBarHidden; } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_F) { // Ctrl+F toggle fullscreen ApplicationSettings.isFullscreen = !ApplicationSettings.isFullscreen } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_M) { // Ctrl+M toggle sound // We mute / unmute both channels in sync ApplicationSettings.isAudioVoicesEnabled = !ApplicationSettings.isAudioVoicesEnabled - ApplicationSettings.isAudioEffectsEnabled = ApplicationSettings.isAudioVoicesEnabled + ApplicationSettings.isAudioEffectsEnabled = !ApplicationSettings.isAudioEffectsEnabled } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_W) { // Ctrl+W exit the current activity home() } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_P) { // Ctrl+P Screenshot ApplicationInfo.screenshot("/tmp/" + activityInfo.name.split('/')[0] + ".png") } } Keys.onReleased: { if (event.key === Qt.Key_Back) { event.accepted = true home() } } Loader { id: activity sourceComponent: pageComponent anchors.fill: parent } Loader { id: demoPageLoader source: ApplicationSettings.activationMode == 1 ? "BuyMeOverlayInapp.qml" : "BuyMeOverlay.qml" anchors.fill: parent active: !activityInfo.demo && ApplicationSettings.isDemoMode } } diff --git a/src/core/GCSfx.qml b/src/core/GCSfx.qml index d5db6a347..d9ff27dba 100644 --- a/src/core/GCSfx.qml +++ b/src/core/GCSfx.qml @@ -1,211 +1,95 @@ /* GCompris - GCSoundEffect.qml * * Copyright (C) 2018 Timothée Giet * * Authors: * Johnny Jazeix (GCAudio base, 2014) * Timothée Giet * * 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 sfx playback. * @ingroup components * * Wrapper component around QtQuick's SoundEffect element, handling all sfx * playback in GCompris uniformly. * - * It maintains a queue of sfx-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 sfx voice will be localized, replace the locale part - * of the file by '$LOCALE'. - * - * To makes sure that all sfx sources are normalized with respect to - * ApplicationInfo.CompressedSoundEffect replace the 'ogg' part of the file by - * '$CA'. * * @inherit QtQuick.Item */ Item { id: gcsfx /** * type:bool * Whether sfx should be muted. */ property bool muted /** * type:url * URL to the sfx source to be played back. */ property alias source: sfx.source /** * type:string * Status of the fx player. */ property alias status: sfx.status - - /** - * type:var - * Current playback state. - * - * Possible values taken from SoundEffect.status - */ - property var playbackState: (sfx.error == SoundEffect.NoError) ? - sfx.playbackState : SoundEffect.StoppedState; - - /** - * type:list - * Playback queue. - */ - property var files: [] - - /** - * Emitted in case of error. - */ - signal error - - /** - * Emitted when playback of all scheduled sfx sources has finished. - */ - signal done - + /** * When mute is changed we set the volume to 0 to mute a potential playing * sound. */ onMutedChanged: muted ? sfx.volume = 0 : sfx.volume = 1 /** * Plays back the sfx resource @p file. * * @param type:string file [optional] URL to an sfx source. * @returns @c true if playback has been started, @c false if file does not * exist or sfx is muted */ function play(file) { - if(!fileId.exists(file) || muted) - 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) { sfx.play() } - return true } /** * Stops sfx playback. */ function stop() { - if(sfx.playbackState != SoundEffect.StoppedState) + if(sfx.playing) sfx.stop() } - /** - * Schedules a @p file for sfx 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 sfx file to be played back. - * @returns @c true upon success, or @c false if @p file does not exist or - * sfx is muted - */ - function append(file) { - if(!fileId.exists(file) || muted) - return false - - if(sfx.playbackState !== SoundEffect.PlayingState - || sfx.status === SoundEffect.EndOfMedia - || sfx.status === SoundEffect.NoMedia - || sfx.status === SoundEffect.InvalidMedia) { - // Setting the source to "" on Linux fix a case where the sound is no more played - source = "" - source = file - 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) { - sfx.source = "" - gcsfxdone() - } else { - sfx.source = "" - sfx.source = nextFile - if(!muted) - sfx.play() - } - } - SoundEffect { id: sfx } - Timer { - id: silenceTimer - repeat: false - onTriggered: { - interval = 0 - _playNextFile() - } - } - - File { - id: fileId - } - /// @endcond }