Add Sound Module

This commit is contained in:
Christopher T. Johnson
2023-12-19 10:01:09 -05:00
parent 11726900f7
commit bb5287743c
7 changed files with 177 additions and 99 deletions

View File

@@ -2,3 +2,4 @@ from .books import Book
from .person import PersonDialog
from .read import EditDialog
from .session import SessionDialog
from .sounds import SoundOff

View File

@@ -1,5 +1,4 @@
import json
import os
import re
from typing import cast
@@ -31,7 +30,6 @@ from PyQt6.QtGui import (
QTextDocument,
QTextListFormat,
)
from PyQt6.QtMultimedia import QAudioOutput, QMediaDevices, QMediaPlayer
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PyQt6.QtWidgets import QDialog, QPushButton
@@ -45,16 +43,12 @@ class EditDialog(QDialog, Ui_Dialog):
sessionSignal = pyqtSignal()
displayedWord = pyqtSignal(int)
newParagraph = pyqtSignal(int, int)
soundEffect = QMediaPlayer()
def __init__(self, session, person_id: int) -> None:
def __init__(self, parent, session, person_id: int) -> None:
self.session = session
super(EditDialog, self).__init__()
super(EditDialog, self).__init__(parent)
print(self.parent())
self.person_id = person_id
if not QResource.registerResource(
os.path.join(os.path.dirname(__file__), "../ui/resources.rcc"), "/"
):
raise Exception("Unable to register resources.rcc")
styleSheet = QResource(":/display.css").data().decode("utf-8")
self.setupUi(self)
#
@@ -63,16 +57,6 @@ class EditDialog(QDialog, Ui_Dialog):
#
# End overrides
#
audioOutput = QAudioOutput()
dev = None
for output in QMediaDevices.audioOutputs():
if output.id().data().decode("UTF-8") == "virt-input":
dev = output
break
if dev:
audioOutput.setDevice(dev)
self.audioOutput = audioOutput
self.soundEffect.setAudioOutput(audioOutput)
self.load_book(self.person_id)
blockNumber = self.block
self.paraEdit.setReadOnly(True)
@@ -101,10 +85,6 @@ class EditDialog(QDialog, Ui_Dialog):
#
self.displayedWord.connect(self.session.addWord)
self.newParagraph.connect(self.session.addBlock)
self.soundEffect.errorOccurred.connect(self.mediaError)
self.soundEffect.playbackStateChanged.connect(self.changedState)
self.soundEffect.mediaStatusChanged.connect(self.changedStatus)
return
#
@@ -140,33 +120,6 @@ class EditDialog(QDialog, Ui_Dialog):
self.setDefEdit(selection, word_id, definition)
return
@pyqtSlot(QMediaPlayer.MediaStatus)
def changedStatus(self, status):
if status == QMediaPlayer.MediaStatus.LoadedMedia:
self.soundEffect.play()
audioOutput = self.soundEffect.audioOutput()
if not audioOutput:
self.soundEffect.setAudioOutput(self.audioOutput)
audioOutput = self.audioOutput
audioDevice = audioOutput.device()
print(status)
return
@pyqtSlot(QMediaPlayer.PlaybackState)
def changedState(self, status):
audioOutput = self.soundEffect.audioOutput()
if not audioOutput:
return
audioDevice = audioOutput.device()
print(status)
return
@pyqtSlot(QMediaPlayer.Error, str)
def mediaError(self, error, string):
print(error)
print(string)
return
@pyqtSlot()
def sessionAction(self) -> None:
self.sessionSignal.emit()
@@ -214,13 +167,8 @@ class EditDialog(QDialog, Ui_Dialog):
print("Looking for audio")
for entry in self.phonetics:
if len(entry["audio"]) > 0:
self.soundEffect.setSource(QUrl(entry["audio"]))
if (
self.soundEffect.mediaStatus()
== QMediaPlayer.MediaStatus.LoadedMedia
):
self.soundEffect.play()
return
# self.parent().playAlert.emit()
self.parent().playSound.emit(entry["audio"])
return
@pyqtSlot()

View File

@@ -29,6 +29,7 @@ class SessionDialog(QDialog, Ui_Dialog):
sessionStart = None
sessionEnd = None
blocks = QStandardItemModel()
session_id = -1
def __init__(self) -> None:
super(SessionDialog, self).__init__()
@@ -112,41 +113,14 @@ class SessionDialog(QDialog, Ui_Dialog):
self.sessionEnd = datetime.now()
query = QSqlQuery()
query.prepare(
"INSERT INTO sessions "
"(person_id, start, stop, notes) "
"VALUES (:person_id, :start, :stop, :notes)"
"UPDATE sessions "
"SET start=:start , SET stop=:stop, SET notes=:notes "
"WHERE sesion_id = :session_id"
)
query.bindValue(":person_id", self.person_id)
query.bindValue(":session_id", self.session_id)
query.bindValue(":start", self.sessionStart.isoformat())
query.bindValue(":stop", self.sessionEnd.isoformat())
query.bindValue(":notes", self.textEdit.toPlainText())
if not query.exec():
query_error(query)
session_id = query.lastInsertId()
model = self.wordView.model()
sql = "INSERT INTO session_word (session_id,word_id, important) VALUES "
parameters = ["(?,?,?)" for x in range(model.rowCount())]
sql += ", ".join(parameters)
query.prepare(sql)
for row in range(model.rowCount()):
query.addBindValue(session_id)
query.addBindValue(model.item(row).data(SessionDialog.WordIdRole))
query.addBindValue(
model.item(row).data(SessionDialog.WordImportantRole)
)
if not query.exec():
query_error(query)
sql = (
"INSERT INTO session_block (session_id, section_id, block) VALUES "
)
parameters = ["(?,?,?)" for x in range(self.blocks.rowCount())]
sql += ",".join(parameters)
query.prepare(sql)
for row in range(self.blocks.rowCount()):
query.addBindValue(session_id)
item = self.blocks.item(row)
query.addBindValue(item.data(SessionDialog.SectionIdRole))
query.addBindValue(item.data(SessionDialog.BlockRole))
if not query.exec():
query_error(query)
super().accept()
@@ -157,6 +131,19 @@ class SessionDialog(QDialog, Ui_Dialog):
if state:
if not self.sessionStart:
self.sessionStart = datetime.now()
if self.session_id <= 0:
query = QSqlQuery()
query.prepare(
"INSERT INTO sessions "
"(person_id, start) "
"VALUES (:person_id, :start)"
)
query.bindValue(":person_id", self.person_id)
query.bindValue(":start", self.sessionStart.isoformat())
if not query.exec():
query_error(query)
self.session_id = query.lastInsertId()
self.startTime = datetime.now()
self.timer.start()
else:
@@ -197,13 +184,13 @@ class SessionDialog(QDialog, Ui_Dialog):
raise Exception(f"Word_id({word_id}) not found in DB")
word = QStandardItem()
word.setData(query.value("word"), Qt.ItemDataRole.DisplayRole)
word.setData(query.value("word_id"), SessionDialog.WordIdRole)
word.setData(word_id, SessionDialog.WordIdRole)
word.setData(0, SessionDialog.WordImportantRole)
model = self.wordView.model()
matches = model.match(
model.createIndex(0, 0),
SessionDialog.WordIdRole,
query.value("word_id"),
word_id,
1,
Qt.MatchFlag.MatchExactly,
)
@@ -211,6 +198,15 @@ class SessionDialog(QDialog, Ui_Dialog):
return
self.wordView.model().appendRow(word)
self.wordView.model().sort(0)
query.prepare(
"INSERT INTO session_word "
"(session_id, word_id, important) "
"VALUES (:session_id, :word_id, 0)"
)
query.bindValue(":session_id", self.session_id)
query.bindValue(":word_id", word_id)
if not query.exec():
query_error(query)
else:
print(f"Not active: {word_id}")
return
@@ -255,4 +251,14 @@ class SessionDialog(QDialog, Ui_Dialog):
)
self.textBrowser.textCursor().insertBlock()
self.textBrowser.ensureCursorVisible()
query.prepare(
"INSERT INTO session_block "
"(session_id, section_id, block) "
"VALUES (:session_id, :section_id, :block)"
)
query.bindValue(":session_id", self.session_id)
query.bindValue(":section_id", section_id)
query.bindValue(":block", block)
if not query.exec():
query_error(query)
return

108
lib/sounds.py Normal file
View File

@@ -0,0 +1,108 @@
from PyQt6.QtCore import QObject, Qt, QUrl, pyqtSlot
from PyQt6.QtMultimedia import (
QAudioDevice,
QAudioOutput,
QMediaDevices,
QMediaPlayer,
QSoundEffect,
)
# from PyQt6.QtWidgets import QWidget
class SoundOff(QObject):
def __init__(self):
super().__init__()
#
# Setup devices
#
self.virtualDevice = None
dev = None
for output in QMediaDevices.audioOutputs():
if output.id().data().decode("utf-8") == "virt-input":
self.virtualDevice = output
if output.isDefault():
self.localDevice = output
self.alertEffect = QSoundEffect()
self.alertEffect.setSource(QUrl("qrc:/beep.wav"))
self.alertEffect.setAudioDevice(self.localDevice)
self.alertEffect.setVolume(0.25)
self.alertEffect.setLoopCount(1)
self.localPlayer = QMediaPlayer()
self.localPlayer.setObjectName("localPlayer")
self.localOutput = QAudioOutput()
self.localOutput.setDevice(self.localDevice)
self.localPlayer.setAudioOutput(self.localOutput)
if self.virtualDevice:
self.virtualPlayer = QMediaPlayer()
self.virtualPlayer.setObjectName("virtualPlayer")
self.virtualOutput = QAudioOutput()
self.virtualOutput.setDevice(self.virtualDevice)
self.virtualPlayer.setAudioOutput(self.virtualOutput)
#
# Connections
#
self.localPlayer.errorOccurred.connect(self.mediaError)
self.localPlayer.mediaStatusChanged.connect(self.mediaStatus)
self.localPlayer.playbackStateChanged.connect(self.playbackState)
if self.virtualDevice:
self.virtualPlayer.errorOccurred.connect(self.mediaError)
self.virtualPlayer.mediaStatusChanged.connect(self.mediaStatus)
self.virtualPlayer.playbackStateChanged.connect(self.playbackState)
@pyqtSlot()
def alert(self):
self.alertEffect.play()
return
@pyqtSlot(QMediaPlayer.Error, str)
def mediaError(self, error, string):
print(error)
print(str)
return
@pyqtSlot(QMediaPlayer.MediaStatus)
def mediaStatus(self, status):
if status == QMediaPlayer.MediaStatus.LoadedMedia:
self.sender().play()
return
@pyqtSlot(QMediaPlayer.PlaybackState)
def playbackState(self, state):
return
#
# Communications slots
#
@pyqtSlot()
def soundAlert(self):
self.alertEffect.play()
return
@pyqtSlot(str)
def playSound(self, url):
src = QUrl(url)
if not self.localPlayer.audioOutput():
self.localPlayer.setAudioOutput(self.localOutput)
self.localPlayer.setSource(src)
self.localPlayer.setPosition(0)
if (
self.localPlayer.mediaStatus()
== QMediaPlayer.MediaStatus.LoadedMedia
):
self.localPlayer.play()
if not self.virtualDevice:
return
return
self.virtualPlayer.setSource(src)
self.virtualPlayer.setPosition(0)
if not self.virtualPlayer.audioOutput():
self.virtualPlayer.setAudioOutput(self.virtualOutput)
if (
self.virtualPlayer.mediaStatus()
== QMediaPlayer.MediaStatus.LoadedMedia
):
self.virtualPlayer.play()
return