mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 02:02:38 +02:00
634 lines
20 KiB
QML
634 lines
20 KiB
QML
//
|
|
// AudioScope.qml
|
|
//
|
|
// Created by Luis Cuenca on 11/22/2017
|
|
// Copyright 2017 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
import QtQuick 2.5
|
|
import QtQuick.Controls 1.4
|
|
import stylesUit 1.0
|
|
import controlsUit 1.0 as HifiControlsUit
|
|
|
|
Item {
|
|
id: root
|
|
width: parent.width
|
|
height: parent.height
|
|
|
|
property var _scopeInputData
|
|
property var _scopeOutputLeftData
|
|
property var _scopeOutputRightData
|
|
|
|
property var _triggerInputData
|
|
property var _triggerOutputLeftData
|
|
property var _triggerOutputRightData
|
|
|
|
property var _triggerValues: QtObject{
|
|
property int x: parent.width/2
|
|
property int y: parent.height/3
|
|
}
|
|
|
|
property var _triggered: false
|
|
property var _steps
|
|
property var _refreshMs: 32
|
|
property var _framesPerSecond: AudioScope.getFramesPerSecond()
|
|
property var _isFrameUnits: true
|
|
|
|
property var _holdStart: QtObject{
|
|
property int x: 0
|
|
property int y: 0
|
|
}
|
|
|
|
property var _holdEnd: QtObject{
|
|
property int x: 0
|
|
property int y: 0
|
|
}
|
|
|
|
property var _timeBeforeHold: 300
|
|
property var _pressedTime: 0
|
|
property var _isPressed: false
|
|
|
|
property var _recOpacity : 0.0
|
|
property var _recSign : 0.05
|
|
|
|
property var _outputLeftState: false
|
|
property var _outputRightState: false
|
|
|
|
property var _wavFilePath: ""
|
|
|
|
function isHolding() {
|
|
return (_pressedTime > _timeBeforeHold);
|
|
}
|
|
|
|
function updateMeasureUnits() {
|
|
timeButton.text = _isFrameUnits ? "Display Frames" : "Milliseconds";
|
|
fiveLabel.text = _isFrameUnits ? "5" : "" + (Math.round(1000 * 5.0/_framesPerSecond));
|
|
twentyLabel.text = _isFrameUnits ? "20" : "" + (Math.round(1000 * 20.0/_framesPerSecond));
|
|
fiftyLabel.text = _isFrameUnits ? "50" : "" + (Math.round(1000 * 50.0/_framesPerSecond));
|
|
}
|
|
|
|
function collectScopeData() {
|
|
if (inputCh.checked) {
|
|
_scopeInputData = AudioScope.scopeInput;
|
|
}
|
|
if (outputLeftCh.checked) {
|
|
_scopeOutputLeftData = AudioScope.scopeOutputLeft;
|
|
}
|
|
if (outputRightCh.checked) {
|
|
_scopeOutputRightData = AudioScope.scopeOutputRight;
|
|
}
|
|
}
|
|
|
|
function collectTriggerData() {
|
|
if (inputCh.checked) {
|
|
_triggerInputData = AudioScope.triggerInput;
|
|
}
|
|
if (outputLeftCh.checked) {
|
|
_triggerOutputLeftData = AudioScope.triggerOutputLeft;
|
|
}
|
|
if (outputRightCh.checked) {
|
|
_triggerOutputRightData = AudioScope.triggerOutputRight;
|
|
}
|
|
}
|
|
|
|
function setRecordingLabelOpacity(opacity) {
|
|
_recOpacity = opacity;
|
|
recCircle.opacity = _recOpacity;
|
|
recText.opacity = _recOpacity;
|
|
}
|
|
|
|
function updateRecordingLabel() {
|
|
_recOpacity += _recSign;
|
|
if (_recOpacity > 1.0 || _recOpacity < 0.0) {
|
|
_recOpacity = _recOpacity > 1.0 ? 1.0 : 0.0;
|
|
_recSign *= -1;
|
|
}
|
|
setRecordingLabelOpacity(_recOpacity);
|
|
}
|
|
|
|
function pullFreshValues() {
|
|
if (AudioScriptingInterface.getRecording()) {
|
|
updateRecordingLabel();
|
|
}
|
|
|
|
if (!AudioScope.getPause()) {
|
|
if (!_triggered) {
|
|
collectScopeData();
|
|
}
|
|
}
|
|
if (inputCh.checked || outputLeftCh.checked || outputRightCh.checked) {
|
|
mycanvas.requestPaint();
|
|
}
|
|
}
|
|
|
|
function startRecording() {
|
|
_wavFilePath = (new Date()).toISOString(); // yyyy-mm-ddThh:mm:ss.sssZ
|
|
_wavFilePath = _wavFilePath.replace(/[\-:]|\.\d*Z$/g, "").replace("T", "-") + ".wav";
|
|
// Using controller recording default directory
|
|
_wavFilePath = Recording.getDefaultRecordingSaveDirectory() + _wavFilePath;
|
|
if (!AudioScriptingInterface.startRecording(_wavFilePath)) {
|
|
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Error creating: "+_wavFilePath}));
|
|
updateRecordingUI(false);
|
|
}
|
|
}
|
|
|
|
function stopRecording() {
|
|
AudioScriptingInterface.stopRecording();
|
|
setRecordingLabelOpacity(0.0);
|
|
Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Saved: "+_wavFilePath}));
|
|
}
|
|
|
|
function updateRecordingUI(isRecording) {
|
|
if (!isRecording) {
|
|
recordButton.text = "Record";
|
|
recordButton.color = hifi.buttons.black;
|
|
outputLeftCh.checked = _outputLeftState;
|
|
outputRightCh.checked = _outputRightState;
|
|
} else {
|
|
recordButton.text = "Stop";
|
|
recordButton.color = hifi.buttons.red;
|
|
_outputLeftState = outputLeftCh.checked;
|
|
_outputRightState = outputRightCh.checked;
|
|
outputLeftCh.checked = true;
|
|
outputRightCh.checked = true;
|
|
}
|
|
}
|
|
|
|
function toggleRecording() {
|
|
if (AudioScriptingInterface.getRecording()) {
|
|
updateRecordingUI(false);
|
|
stopRecording();
|
|
} else {
|
|
updateRecordingUI(true);
|
|
startRecording();
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
interval: _refreshMs; running: true; repeat: true
|
|
onTriggered: pullFreshValues()
|
|
}
|
|
|
|
Canvas {
|
|
id: mycanvas
|
|
anchors.fill:parent
|
|
|
|
onPaint: {
|
|
|
|
function displayMeasureArea(ctx) {
|
|
|
|
ctx.fillStyle = Qt.rgba(0.1, 0.1, 0.1, 1);
|
|
ctx.fillRect(_holdStart.x, 0, _holdEnd.x - _holdStart.x, height);
|
|
|
|
ctx.lineWidth = "2";
|
|
ctx.strokeStyle = "#555555";
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(_holdStart.x, 0);
|
|
ctx.lineTo(_holdStart.x, height);
|
|
ctx.moveTo(_holdEnd.x, 0);
|
|
ctx.lineTo(_holdEnd.x, height);
|
|
|
|
ctx.moveTo(_holdStart.x, _holdStart.y);
|
|
ctx.lineTo(_holdEnd.x, _holdStart.y);
|
|
ctx.moveTo(_holdEnd.x, _holdEnd.y);
|
|
ctx.lineTo(_holdStart.x, _holdEnd.y);
|
|
|
|
ctx.stroke();
|
|
}
|
|
|
|
function displayTrigger(ctx, lineWidth, color) {
|
|
var crossSize = 3;
|
|
var holeSize = 2;
|
|
|
|
ctx.lineWidth = lineWidth;
|
|
ctx.strokeStyle = color;
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(_triggerValues.x - (crossSize + holeSize), _triggerValues.y);
|
|
ctx.lineTo(_triggerValues.x - holeSize, _triggerValues.y);
|
|
ctx.moveTo(_triggerValues.x + holeSize, _triggerValues.y);
|
|
ctx.lineTo(_triggerValues.x + (crossSize + holeSize), _triggerValues.y);
|
|
|
|
ctx.moveTo(_triggerValues.x, _triggerValues.y - (crossSize + holeSize));
|
|
ctx.lineTo(_triggerValues.x, _triggerValues.y - holeSize);
|
|
ctx.moveTo(_triggerValues.x, _triggerValues.y + holeSize);
|
|
ctx.lineTo(_triggerValues.x, _triggerValues.y + (crossSize + holeSize));
|
|
|
|
ctx.stroke();
|
|
}
|
|
|
|
function displayBackground(ctx, datawidth, steps, lineWidth, color) {
|
|
var verticalPadding = 100;
|
|
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = lineWidth;
|
|
|
|
ctx.moveTo(0, height/2);
|
|
ctx.lineTo(datawidth, height/2);
|
|
|
|
var gap = datawidth/steps;
|
|
for (var i = 0; i < steps; i++) {
|
|
ctx.moveTo(i*gap + 1, verticalPadding);
|
|
ctx.lineTo(i*gap + 1, height-verticalPadding);
|
|
}
|
|
ctx.moveTo(datawidth-1, verticalPadding);
|
|
ctx.lineTo(datawidth-1, height-verticalPadding);
|
|
|
|
ctx.stroke();
|
|
}
|
|
|
|
function drawScope(ctx, data, width, color) {
|
|
ctx.beginPath();
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = width;
|
|
var x = 0;
|
|
for (var i = 0; i < data.length-1; i++) {
|
|
ctx.moveTo(x, data[i] + height/2);
|
|
ctx.lineTo(++x, data[i+1] + height/2);
|
|
}
|
|
ctx.stroke();
|
|
}
|
|
|
|
function getMeasurementText(dist) {
|
|
var datasize = _scopeInputData.length;
|
|
var value = 0;
|
|
if (fiveFrames.checked) {
|
|
value = (_isFrameUnits) ? 5.0*dist/datasize : (Math.round(1000 * 5.0/_framesPerSecond))*dist/datasize;
|
|
} else if (twentyFrames.checked) {
|
|
value = (_isFrameUnits) ? 20.0*dist/datasize : (Math.round(1000 * 20.0/_framesPerSecond))*dist/datasize;
|
|
} else if (fiftyFrames.checked) {
|
|
value = (_isFrameUnits) ? 50.0*dist/datasize : (Math.round(1000 * 50.0/_framesPerSecond))*dist/datasize;
|
|
}
|
|
value = Math.abs(Math.round(value*100)/100);
|
|
var measureText = "" + value + (_isFrameUnits ? " frames" : " milliseconds");
|
|
return measureText;
|
|
}
|
|
|
|
function drawMeasurements(ctx, color) {
|
|
ctx.fillStyle = color;
|
|
ctx.font = "normal 16px sans-serif";
|
|
var fontwidth = 8;
|
|
var measureText = getMeasurementText(_holdEnd.x - _holdStart.x);
|
|
if (_holdStart.x < _holdEnd.x) {
|
|
ctx.fillText("" + height/2 - _holdStart.y, _holdStart.x-40, _holdStart.y);
|
|
ctx.fillText("" + height/2 - _holdEnd.y, _holdStart.x-40, _holdEnd.y);
|
|
ctx.fillText(measureText, _holdEnd.x+10, _holdEnd.y);
|
|
} else {
|
|
ctx.fillText("" + height/2 - _holdStart.y, _holdStart.x+10, _holdStart.y);
|
|
ctx.fillText("" + height/2 - _holdEnd.y, _holdStart.x+10, _holdEnd.y);
|
|
ctx.fillText(measureText, _holdEnd.x-fontwidth*measureText.length, _holdEnd.y);
|
|
}
|
|
}
|
|
|
|
var ctx = getContext("2d");
|
|
|
|
ctx.fillStyle = Qt.rgba(0, 0, 0, 1);
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
if (isHolding()) {
|
|
displayMeasureArea(ctx);
|
|
}
|
|
|
|
var guideLinesColor = "#555555"
|
|
var guideLinesWidth = "1"
|
|
|
|
displayBackground(ctx, _scopeInputData.length, _steps, guideLinesWidth, guideLinesColor);
|
|
|
|
var triggerWidth = "3"
|
|
var triggerColor = "#EFB400"
|
|
|
|
if (AudioScope.getAutoTrigger()) {
|
|
displayTrigger(ctx, triggerWidth, triggerColor);
|
|
}
|
|
|
|
var scopeWidth = "2"
|
|
var scopeInputColor = "#00B4EF"
|
|
var scopeOutputLeftColor = "#BB0000"
|
|
var scopeOutputRightColor = "#00BB00"
|
|
|
|
if (!_triggered) {
|
|
if (inputCh.checked) {
|
|
drawScope(ctx, _scopeInputData, scopeWidth, scopeInputColor);
|
|
}
|
|
if (outputLeftCh.checked) {
|
|
drawScope(ctx, _scopeOutputLeftData, scopeWidth, scopeOutputLeftColor);
|
|
}
|
|
if (outputRightCh.checked) {
|
|
drawScope(ctx, _scopeOutputRightData, scopeWidth, scopeOutputRightColor);
|
|
}
|
|
} else {
|
|
if (inputCh.checked) {
|
|
drawScope(ctx, _triggerInputData, scopeWidth, scopeInputColor);
|
|
}
|
|
if (outputLeftCh.checked) {
|
|
drawScope(ctx, _triggerOutputLeftData, scopeWidth, scopeOutputLeftColor);
|
|
}
|
|
if (outputRightCh.checked) {
|
|
drawScope(ctx, _triggerOutputRightData, scopeWidth, scopeOutputRightColor);
|
|
}
|
|
}
|
|
|
|
if (isHolding()) {
|
|
drawMeasurements(ctx, "#eeeeee");
|
|
}
|
|
|
|
if (_isPressed) {
|
|
_pressedTime += _refreshMs;
|
|
}
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
id: hitbox
|
|
anchors.fill: mycanvas
|
|
hoverEnabled: true
|
|
onPressed: {
|
|
_isPressed = true;
|
|
_pressedTime = 0;
|
|
_holdStart.x = mouseX;
|
|
_holdStart.y = mouseY;
|
|
}
|
|
onPositionChanged: {
|
|
_holdEnd.x = mouseX;
|
|
_holdEnd.y = mouseY;
|
|
}
|
|
onReleased: {
|
|
if (!isHolding() && AudioScope.getAutoTrigger()) {
|
|
_triggerValues.x = mouseX
|
|
_triggerValues.y = mouseY
|
|
AudioScope.setTriggerValues(mouseX, mouseY-height/2);
|
|
}
|
|
_isPressed = false;
|
|
_pressedTime = 0;
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: activated
|
|
boxSize: 20
|
|
anchors.top: parent.top;
|
|
anchors.left: parent.left;
|
|
anchors.topMargin: 8;
|
|
anchors.leftMargin: 20;
|
|
checked: AudioScope.getVisible();
|
|
onCheckedChanged: {
|
|
AudioScope.setVisible(checked);
|
|
activelabel.text = AudioScope.getVisible() ? "On" : "Off"
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Label {
|
|
id: activelabel
|
|
text: AudioScope.getVisible() ? "On" : "Off"
|
|
anchors.top: activated.top;
|
|
anchors.left: activated.right;
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: outputLeftCh
|
|
boxSize: 20
|
|
text: "Output L"
|
|
anchors.horizontalCenter: parent.horizontalCenter;
|
|
anchors.top: parent.top;
|
|
anchors.topMargin: 8;
|
|
onCheckedChanged: {
|
|
AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked);
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Label {
|
|
text: "Channels";
|
|
anchors.horizontalCenter: outputLeftCh.horizontalCenter;
|
|
anchors.bottom: outputLeftCh.top;
|
|
anchors.bottomMargin: 8;
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: inputCh
|
|
boxSize: 20
|
|
text: "Input Mono"
|
|
anchors.bottom: outputLeftCh.bottom;
|
|
anchors.right: outputLeftCh.left;
|
|
anchors.rightMargin: 40;
|
|
onCheckedChanged: {
|
|
AudioScope.setLocalEcho(checked);
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: outputRightCh
|
|
boxSize: 20
|
|
text: "Output R"
|
|
anchors.bottom: outputLeftCh.bottom;
|
|
anchors.left: outputLeftCh.right;
|
|
anchors.leftMargin: 40;
|
|
onCheckedChanged: {
|
|
AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked);
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Button {
|
|
id: recordButton;
|
|
text: "Record";
|
|
color: hifi.buttons.black;
|
|
colorScheme: hifi.colorSchemes.dark;
|
|
anchors.right: parent.right;
|
|
anchors.bottom: parent.bottom;
|
|
anchors.rightMargin: 30;
|
|
anchors.bottomMargin: 8;
|
|
width: 95;
|
|
height: 55;
|
|
onClicked: {
|
|
toggleRecording();
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Button {
|
|
id: pauseButton;
|
|
color: hifi.buttons.black;
|
|
colorScheme: hifi.colorSchemes.dark;
|
|
anchors.right: recordButton.left;
|
|
anchors.bottom: parent.bottom;
|
|
anchors.rightMargin: 30;
|
|
anchors.bottomMargin: 8;
|
|
height: 55;
|
|
width: 95;
|
|
text: " Pause ";
|
|
onClicked: {
|
|
AudioScope.togglePause();
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: twentyFrames
|
|
boxSize: 20
|
|
anchors.left: parent.horizontalCenter;
|
|
anchors.bottom: parent.bottom;
|
|
anchors.bottomMargin: 8;
|
|
onCheckedChanged: {
|
|
if (checked){
|
|
fiftyFrames.checked = false;
|
|
fiveFrames.checked = false;
|
|
AudioScope.selectAudioScopeTwentyFrames();
|
|
_steps = 20;
|
|
AudioScope.setPause(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Label {
|
|
id:twentyLabel
|
|
anchors.left: twentyFrames.right;
|
|
anchors.verticalCenter: twentyFrames.verticalCenter;
|
|
}
|
|
|
|
HifiControlsUit.Button {
|
|
id: timeButton;
|
|
color: hifi.buttons.black;
|
|
colorScheme: hifi.colorSchemes.dark;
|
|
text: "Display Frames";
|
|
anchors.horizontalCenter: twentyFrames.horizontalCenter;
|
|
anchors.bottom: twentyFrames.top;
|
|
anchors.bottomMargin: 8;
|
|
height: 26;
|
|
onClicked: {
|
|
_isFrameUnits = !_isFrameUnits;
|
|
updateMeasureUnits();
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: fiveFrames
|
|
boxSize: 20
|
|
anchors.horizontalCenter: parent.horizontalCenter;
|
|
anchors.bottom: parent.bottom;
|
|
anchors.bottomMargin: 8;
|
|
anchors.horizontalCenterOffset: -50;
|
|
checked: true;
|
|
onCheckedChanged: {
|
|
if (checked) {
|
|
fiftyFrames.checked = false;
|
|
twentyFrames.checked = false;
|
|
AudioScope.selectAudioScopeFiveFrames();
|
|
_steps = 5;
|
|
AudioScope.setPause(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Label {
|
|
id:fiveLabel
|
|
anchors.left: fiveFrames.right;
|
|
anchors.verticalCenter: fiveFrames.verticalCenter;
|
|
}
|
|
|
|
HifiControlsUit.CheckBox {
|
|
id: fiftyFrames
|
|
boxSize: 20
|
|
anchors.horizontalCenter: parent.horizontalCenter;
|
|
anchors.bottom: parent.bottom;
|
|
anchors.bottomMargin: 8;
|
|
anchors.horizontalCenterOffset: 70;
|
|
onCheckedChanged: {
|
|
if (checked) {
|
|
twentyFrames.checked = false;
|
|
fiveFrames.checked = false;
|
|
AudioScope.selectAudioScopeFiftyFrames();
|
|
_steps = 50;
|
|
AudioScope.setPause(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Label {
|
|
id:fiftyLabel
|
|
anchors.left: fiftyFrames.right;
|
|
anchors.verticalCenter: fiftyFrames.verticalCenter;
|
|
}
|
|
|
|
HifiControlsUit.Switch {
|
|
id: triggerSwitch;
|
|
height: 26;
|
|
anchors.left: parent.left;
|
|
anchors.bottom: parent.bottom;
|
|
anchors.leftMargin: 75;
|
|
anchors.bottomMargin: 8;
|
|
labelTextOff: "Off";
|
|
labelTextOn: "On";
|
|
onCheckedChanged: {
|
|
if (!checked) AudioScope.setPause(false);
|
|
AudioScope.setPause(false);
|
|
AudioScope.setAutoTrigger(checked);
|
|
AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2);
|
|
}
|
|
}
|
|
|
|
HifiControlsUit.Label {
|
|
text: "Trigger";
|
|
anchors.left: triggerSwitch.left;
|
|
anchors.leftMargin: -15;
|
|
anchors.bottom: triggerSwitch.top;
|
|
}
|
|
|
|
Rectangle {
|
|
id: recordIcon;
|
|
width:110;
|
|
height:40;
|
|
anchors.right: parent.right;
|
|
anchors.top: parent.top;
|
|
anchors.topMargin: 8;
|
|
color: "transparent"
|
|
|
|
Text {
|
|
id: recText
|
|
text: "REC"
|
|
color: "red"
|
|
font.pixelSize: 30;
|
|
anchors.left: recCircle.right;
|
|
anchors.leftMargin: 10;
|
|
opacity: _recOpacity;
|
|
y: -8;
|
|
}
|
|
|
|
Rectangle {
|
|
id: recCircle;
|
|
width: 25;
|
|
height: 25;
|
|
radius: width*0.5
|
|
opacity: _recOpacity;
|
|
color: "red";
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
_steps = AudioScope.getFramesPerScope();
|
|
AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2);
|
|
activated.checked = true;
|
|
inputCh.checked = true;
|
|
updateMeasureUnits();
|
|
}
|
|
|
|
Connections {
|
|
target: AudioScope
|
|
function onPauseChanged() {
|
|
if (!AudioScope.getPause()) {
|
|
pauseButton.text = "Pause";
|
|
pauseButton.color = hifi.buttons.black;
|
|
AudioScope.setTriggered(false);
|
|
_triggered = false;
|
|
} else {
|
|
pauseButton.text = "Continue";
|
|
pauseButton.color = hifi.buttons.blue;
|
|
}
|
|
}
|
|
function onTriggered() {
|
|
_triggered = true;
|
|
collectTriggerData();
|
|
AudioScope.setPause(true);
|
|
}
|
|
}
|
|
}
|