[kde-doc-english] [kwave] /: implemented playback via Qt Multimedia
Thomas Eschenbacher
Thomas.Eschenbacher at gmx.de
Fri Jan 1 17:54:17 UTC 2016
Git commit f83d0a8413ba20a66b54c0154a69ae0f894b8d98 by Thomas Eschenbacher.
Committed on 01/01/2016 at 17:45.
Pushed by eschenbacher into branch 'master'.
implemented playback via Qt Multimedia
M +1 -1 .kdev4/kwave.kdev4
M +1 -0 CHANGES
M +9 -0 CMakeLists.txt
M +2 -0 LICENSES
M +3 -0 config.h.cmake
M +6 -0 doc/en/index.docbook
M +1 -0 doxy.cfg.in
M +1 -0 kwave.ebuild.in
M +1 -1 kwave/FileContext.cpp
M +5 -0 kwave/FileContext.h
M +1 -0 libkwave/PlayBackParam.h
M +5 -0 libkwave/PlayBackTypesMap.cpp
M +9 -5 libkwave/PlaybackController.cpp
M +5 -0 plugins/playback/CMakeLists.txt
M +1 -1 plugins/playback/PlayBack-ALSA.cpp
M +0 -1 plugins/playback/PlayBack-PulseAudio.cpp
M +0 -1 plugins/playback/PlayBack-PulseAudio.h
A +589 -0 plugins/playback/PlayBack-Qt.cpp [License: GPL (v2+)]
A +248 -0 plugins/playback/PlayBack-Qt.h [License: GPL (v2+)]
M +10 -0 plugins/playback/PlayBackPlugin.cpp
http://commits.kde.org/kwave/f83d0a8413ba20a66b54c0154a69ae0f894b8d98
diff --git a/.kdev4/kwave.kdev4 b/.kdev4/kwave.kdev4
index ae69129..ff13b22 100644
--- a/.kdev4/kwave.kdev4
+++ b/.kdev4/kwave.kdev4
@@ -21,7 +21,7 @@ Install Directory=file:///usr
[CustomDefinesAndIncludes][ProjectPath0]
Defines=\x00\x00\x00\x02\x00\x00\x00\x18\x00_\x00_\x00F\x00U\x00N\x00C\x00T\x00I\x00O\x00N\x00_\x00_\x00\x00\x00\n\x00\x00\x00\x00\x18\x00_\x00_\x00F\x00U\x00N\x00C\x00T\x00I\x00O\x00N\x00_\x00_\x00\x00\x00\x10\x00_\x00_\x00L\x00I\x00N\x00E\x00_\x00_\x00\x00\x00\n\x00\x00\x00\x00\x10\x00_\x00_\x00L\x00I\x00N\x00E\x00_\x00_
-Includes=\x00\x00\x00!\x00\x00\x00"\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00G\x00e\x00n\x00t\x00o\x00o\x00\x00\x00:\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00C\x00o\x00n\x00c\x00u\x00r\x00r\x00e\x00n\x00t\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00C\x00o\x00r\x00e\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00B\x00u\x00s\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00c\x00l\x00a\x00r\x00a\x00t\x00i\x00v\x00e\x00\x00\x006\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00s\x00i\x00g\x00n\x00e\x00r\x00\x00\x00J\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00s\x00i\x00g\x00n\x00e\x00r\x00C\x00o\x00m\x00p\x00o\x00n\x00e\x00n\x00t\x00s\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00G\x00u\x00i\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00N\x00e\x00t\x00w\x00o\x00r\x00k\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00O\x00p\x00e\x00n\x00G\x00L\x00\x00\x00F\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00O\x00p\x00e\x00n\x00G\x00L\x00E\x00x\x00t\x00e\x00n\x00s\x00i\x00o\x00n\x00s\x00\x00\x00D\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00H\x00e\x00a\x00d\x00e\x00r\x00s\x00\x00\x00D\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00S\x00u\x00p\x00p\x00o\x00r\x00t\x00\x00\x00>\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00r\x00i\x00n\x00t\x00S\x00u\x00p\x00p\x00o\x00r\x00t\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00m\x00l\x00\x00\x000\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00\x00\x00B\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00P\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00\x00\x008\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00T\x00e\x00s\x00t\x00\x00\x00>\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00c\x00r\x00i\x00p\x00t\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00q\x00l\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00v\x00g\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00T\x00e\x00s\x00t\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00U\x00i\x00T\x00o\x00o\x00l\x00s\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00e\x00b\x00K\x00i\x00t\x00\x00\x00@\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00e\x00b\x00K\x00i\x00t\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x008\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x001\x001\x00E\x00x\x00t\x00r\x00a\x00s\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x00m\x00l\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x00m\x00l\x00P\x00a\x00t\x00t\x00e\x00r\x00n\x00s\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00I\x001\x008\x00n\x00/\x00\x00\x00@\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00A\x00d\x00d\x00o\x00n\x00s\x00/
+Includes=\x00\x00\x00#\x00\x00\x00"\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00G\x00e\x00n\x00t\x00o\x00o\x00\x00\x00:\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00C\x00o\x00n\x00c\x00u\x00r\x00r\x00e\x00n\x00t\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00C\x00o\x00r\x00e\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00B\x00u\x00s\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00c\x00l\x00a\x00r\x00a\x00t\x00i\x00v\x00e\x00\x00\x006\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00s\x00i\x00g\x00n\x00e\x00r\x00\x00\x00J\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00s\x00i\x00g\x00n\x00e\x00r\x00C\x00o\x00m\x00p\x00o\x00n\x00e\x00n\x00t\x00s\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00G\x00u\x00i\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00N\x00e\x00t\x00w\x00o\x00r\x00k\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00O\x00p\x00e\x00n\x00G\x00L\x00\x00\x00F\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00O\x00p\x00e\x00n\x00G\x00L\x00E\x00x\x00t\x00e\x00n\x00s\x00i\x00o\x00n\x00s\x00\x00\x00D\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00H\x00e\x00a\x00d\x00e\x00r\x00s\x00\x00\x00D\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00S\x00u\x00p\x00p\x00o\x00r\x00t\x00\x00\x00>\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00r\x00i\x00n\x00t\x00S\x00u\x00p\x00p\x00o\x00r\x00t\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00m\x00l\x00\x00\x000\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00\x00\x00B\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00P\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00\x00\x008\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00T\x00e\x00s\x00t\x00\x00\x00>\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00c\x00r\x00i\x00p\x00t\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00q\x00l\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00v\x00g\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00T\x00e\x00s\x00t\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00U\x00i\x00T\x00o\x00o\x00l\x00s\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00e\x00b\x00K\x00i\x00t\x00\x00\x00@\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00e\x00b\x00K\x00i\x00t\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x008\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x001\x001\x00E\x00x\x00t\x00r\x00a\x00s\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x00m\x00l\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x00m\x00l\x00P\x00a\x00t\x00t\x00e\x00r\x00n\x00s\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00I\x001\x008\x00n\x00/\x00\x00\x00@\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00A\x00d\x00d\x00o\x00n\x00s\x00/\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00M\x00u\x00l\x00t\x00i\x00m\x00e\x00d\x00i\x00a\x00/\x00\x00\x00J\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00M\x00u\x00l\x00t\x00i\x00m\x00e\x00d\x00i\x00a\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00/
Path=.
[Defines And Includes][Compiler]
diff --git a/CHANGES b/CHANGES
index 3854793..dc7f81e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,7 @@
0.9.x [2015-xx-xx]
* ported to KDE Frameworks 5 (KF5) / Qt5
+ * playback via Qt Multimedia Audio
* bugfix: saved plugin parameter lists with escaped characters were not
unescaped when loading again
* compile fix for armv7l
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d870a00..b176ce6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,7 @@ PROJECT(kwave)
# OPTION(WITH_OSS "enable playback/recording via OSS [default=on]" ON)
# OPTION(WITH_OPTIMIZED_MEMCPY "enable optimized memcpy [default=on]" ON)
# OPTION(WITH_PULSEAUDIO "enable playback/recording via PulseAudio [default=on]" ON)
+# OPTION(WITH_QT_AUDIO "enable playback via Qt Multimedia [default=on]" ON)
#############################################################################
### toplevel build targets: ###
@@ -180,9 +181,17 @@ SET(CMAKE_AUTOMOC_RELAXED_MODE FALSE)
FIND_PACKAGE(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Concurrent
Core
+ Multimedia
Widgets
)
+# Qt Multimedia support
+OPTION(WITH_QT_AUDIO "enable playback via Qt Multimedia [default=on]" ON)
+IF (WITH_QT_AUDIO)
+ SET(HAVE_QT_AUDIO_SUPPORT on)
+ENDIF (WITH_QT_AUDIO)
+
+
#############################################################################
### KF5 support ###
diff --git a/LICENSES b/LICENSES
index 32bd664..ad239e8 100644
--- a/LICENSES
+++ b/LICENSES
@@ -724,6 +724,8 @@ COMPLETE LIST OF FILES AND THEIR LICENSE
plugins/playback/PlayBack-OSS.h GPL2+
plugins/playback/PlayBack-PulseAudio.cpp GPL2+
plugins/playback/PlayBack-PulseAudio.h GPL2+
+ plugins/playback/PlayBack-Qt.cpp GPL2+
+ plugins/playback/PlayBack-Qt.h GPL2+
plugins/playback/PlayBackPlugin.cpp GPL2+
plugins/playback/PlayBackPlugin.h GPL2+
diff --git a/config.h.cmake b/config.h.cmake
index c1796cb..95505bc 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -39,6 +39,9 @@
/* support playback/recording via PulseAudio */
#cmakedefine HAVE_PULSEAUDIO_SUPPORT
+/* support playback via Qt */
+#cmakedefine HAVE_QT_AUDIO_SUPPORT
+
/* support libsamplerate */
#cmakedefine HAVE_LIBSAMPLERATE
diff --git a/doc/en/index.docbook b/doc/en/index.docbook
index f85344c..a9d0348 100644
--- a/doc/en/index.docbook
+++ b/doc/en/index.docbook
@@ -770,6 +770,12 @@
[<literal>on</literal>/<literal>off</literal>,
default=<literal>on</literal>]
</para></listitem>
+ <listitem><para>
+ <literal>WITH_QT_AUDIO</literal>
+ enable playback via Qt Multimedia
+ [<literal>on</literal>/<literal>off</literal>,
+ default=<literal>on</literal>]
+ </para></listitem>
</itemizedlist>
</para>
diff --git a/doxy.cfg.in b/doxy.cfg.in
index caf41b8..3a7f496 100644
--- a/doxy.cfg.in
+++ b/doxy.cfg.in
@@ -1955,6 +1955,7 @@ PREDEFINED = DEBUG \
HAVE_OSS_SUPPORT \
HAVE_OGG_OPUS \
HAVE_OGG_VORBIS \
+ HAVE_QT_AUDIO_SUPPORT \
HAVE_SYSCONF \
HAVE_SYSINFO \
HAVE_SYSINFO_MEMUNIT
diff --git a/kwave.ebuild.in b/kwave.ebuild.in
index 433c7ca..f747725 100644
--- a/kwave.ebuild.in
+++ b/kwave.ebuild.in
@@ -61,6 +61,7 @@ DEPEND="${RDEPEND}
dev-qt/qtconcurrent:5
dev-qt/qtcore:5
dev-qt/qtgui:5
+ dev-qt/qtmultimedia:5
dev-qt/qtwidgets:5
"
diff --git a/kwave/FileContext.cpp b/kwave/FileContext.cpp
index 0ff33ef..ac7a33b 100644
--- a/kwave/FileContext.cpp
+++ b/kwave/FileContext.cpp
@@ -20,11 +20,11 @@
#include <errno.h>
#include <new>
+#include <QApplication>
#include <QFile>
#include <QLocale>
#include <QPointer>
#include <QTextStream>
-#include <QApplication>
#include <QMdiSubWindow>
#include <QStandardPaths>
diff --git a/kwave/FileContext.h b/kwave/FileContext.h
index d84c15e..fc51f95 100644
--- a/kwave/FileContext.h
+++ b/kwave/FileContext.h
@@ -173,6 +173,11 @@ namespace Kwave
*/
bool closeFile();
+ protected:
+ friend class App;
+ friend class TopWidget;
+ friend class UsageGuard;
+
/**
* increments the usage count of this context, prevents it from
* being deleted
diff --git a/libkwave/PlayBackParam.h b/libkwave/PlayBackParam.h
index afa59fb..dc3c3ef 100644
--- a/libkwave/PlayBackParam.h
+++ b/libkwave/PlayBackParam.h
@@ -31,6 +31,7 @@ namespace Kwave
typedef enum {
PLAYBACK_NONE = 0, /**< none selected */
PLAYBACK_JACK, /**< Jack sound daemon */
+ PLAYBACK_QT_AUDIO, /**< Qt Multimedia */
PLAYBACK_PULSEAUDIO, /**< PulseAudio Sound Server */
PLAYBACK_ALSA, /**< ALSA native */
PLAYBACK_OSS, /**< OSS native or ALSA OSS emulation */
diff --git a/libkwave/PlayBackTypesMap.cpp b/libkwave/PlayBackTypesMap.cpp
index ae14cec..99ae2be 100644
--- a/libkwave/PlayBackTypesMap.cpp
+++ b/libkwave/PlayBackTypesMap.cpp
@@ -44,6 +44,11 @@ void Kwave::PlayBackTypesMap::fill()
_(I18N_NOOP("Pulse Audio")) );
#endif /* HAVE_PULSEAUDIO_SUPPORT */
+#ifdef HAVE_QT_AUDIO_SUPPORT
+ append(index++, Kwave::PLAYBACK_QT_AUDIO, _("qt_audio"),
+ _(I18N_NOOP("Qt Multimedia Audio")) );
+#endif /* HAVE_QT_AUDIO_SUPPORT */
+
if (!index) qWarning("no playback method defined!");
}
diff --git a/libkwave/PlaybackController.cpp b/libkwave/PlaybackController.cpp
index 7c63e08..1d61d7d 100644
--- a/libkwave/PlaybackController.cpp
+++ b/libkwave/PlaybackController.cpp
@@ -541,11 +541,15 @@ void Kwave::PlaybackController::run_wrapper(const QVariant ¶ms)
//***************************************************************************
void Kwave::PlaybackController::closeDevice()
{
- QMutexLocker lock_for_delete(&m_lock_device);
-
- if (!m_device) return; // already closed
- delete m_device;
- m_device = 0;
+ Kwave::PlayBackDevice *dev = 0;
+ if (m_device) {
+ // NOTE: we could get a recursion here if we delete with the lock
+ // held, if the device calls processEvents during shutdown
+ QMutexLocker lock_for_delete(&m_lock_device);
+ dev = m_device;
+ m_device = 0;
+ }
+ delete dev;
}
//***************************************************************************
diff --git a/plugins/playback/CMakeLists.txt b/plugins/playback/CMakeLists.txt
index b23de45..d7b5787 100644
--- a/plugins/playback/CMakeLists.txt
+++ b/plugins/playback/CMakeLists.txt
@@ -34,6 +34,11 @@ IF (HAVE_PULSEAUDIO_SUPPORT)
ENDIF (NOT HAVE_POLL_H)
ENDIF (HAVE_PULSEAUDIO_SUPPORT)
+IF (HAVE_QT_AUDIO_SUPPORT)
+ SET(PLAYBACK_SOURCES ${PLAYBACK_SOURCES} PlayBack-Qt.cpp)
+ SET(PLAYBACK_REQUIRED_LIBS ${PLAYBACK_REQUIRED_LIBS} Qt5::Multimedia)
+ENDIF (HAVE_QT_AUDIO_SUPPORT)
+
SET(plugin_playback_LIB_SRCS
PlayBackDialog.cpp
PlayBackPlugin.cpp
diff --git a/plugins/playback/PlayBack-ALSA.cpp b/plugins/playback/PlayBack-ALSA.cpp
index 5b9ed64..de8d575 100644
--- a/plugins/playback/PlayBack-ALSA.cpp
+++ b/plugins/playback/PlayBack-ALSA.cpp
@@ -554,7 +554,7 @@ QString Kwave::PlayBackALSA::open(const QString &device, double rate,
unsigned int channels, unsigned int bits,
unsigned int bufbase)
{
- qDebug("PlayBackALSA::open(device=%s,rate=%0.1f,channels=%u, bits=%u, "
+ qDebug("PlayBackALSA::open(device=%s, rate=%0.1f, channels=%u, bits=%u, "
"bufbase=%u)", DBG(device), rate, channels, bits, bufbase);
m_device_name = device;
diff --git a/plugins/playback/PlayBack-PulseAudio.cpp b/plugins/playback/PlayBack-PulseAudio.cpp
index fd2f536..850656a 100644
--- a/plugins/playback/PlayBack-PulseAudio.cpp
+++ b/plugins/playback/PlayBack-PulseAudio.cpp
@@ -39,7 +39,6 @@
#include <KUser>
#include "libkwave/FileInfo.h"
-#include "libkwave/SampleEncoderLinear.h"
#include "libkwave/String.h"
#include "libkwave/Utils.h"
#include "libkwave/memcpy.h"
diff --git a/plugins/playback/PlayBack-PulseAudio.h b/plugins/playback/PlayBack-PulseAudio.h
index b7b8901..5939764 100644
--- a/plugins/playback/PlayBack-PulseAudio.h
+++ b/plugins/playback/PlayBack-PulseAudio.h
@@ -34,7 +34,6 @@
#include <QList>
#include <QMap>
#include <QMutex>
-#include <QSemaphore>
#include <QString>
#include <QWaitCondition>
diff --git a/plugins/playback/PlayBack-Qt.cpp b/plugins/playback/PlayBack-Qt.cpp
new file mode 100644
index 0000000..b5e639a
--- /dev/null
+++ b/plugins/playback/PlayBack-Qt.cpp
@@ -0,0 +1,589 @@
+/***************************************************************************
+ PlayBack-Qt.cpp - playback device for Qt Multimedia
+ -------------------
+ begin : Thu Nov 12 2015
+ copyright : (C) 2015 by Thomas Eschenbacher
+ email : Thomas.Eschenbacher at gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "config.h"
+#ifdef HAVE_QT_AUDIO_SUPPORT
+
+#include <errno.h>
+#include <algorithm>
+#include <limits>
+
+#include <QApplication>
+#include <QAudioDeviceInfo>
+#include <QAudioFormat>
+#include <QAudioOutput>
+#include <QObject>
+#include <QSysInfo>
+
+#include <KLocalizedString>
+
+#include "libkwave/SampleEncoderLinear.h"
+#include "libkwave/String.h"
+#include "libkwave/Utils.h"
+
+#include "PlayBack-Qt.h"
+
+/** gui name of the default device */
+#define DEFAULT_DEVICE (i18n("Default device") + _("|sound_note"))
+
+//***************************************************************************
+Kwave::PlayBackQt::PlayBackQt()
+ :QObject(), Kwave::PlayBackDevice(),
+ m_lock(QMutex::Recursive),
+ m_device_name_map(),
+ m_available_devices(),
+ m_output(0),
+ m_buffer_size(0),
+ m_encoder(0)
+{
+}
+
+//***************************************************************************
+Kwave::PlayBackQt::~PlayBackQt()
+{
+ close();
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::createEncoder(const QAudioFormat &format)
+{
+ // discard the old encoder
+ delete m_encoder;
+ m_encoder = 0;
+
+ // get the sample format
+ Kwave::SampleFormat::Format sample_format = Kwave::SampleFormat::Unknown;
+ switch (format.sampleType()) {
+ case QAudioFormat::SignedInt:
+ sample_format = Kwave::SampleFormat::Signed;
+ break;
+ case QAudioFormat::UnSignedInt:
+ sample_format = Kwave::SampleFormat::Unsigned;
+ break;
+ default:
+ sample_format = Kwave::SampleFormat::Unknown;
+ break;
+ }
+ if (sample_format == Kwave::SampleFormat::Unknown) {
+ qWarning("PlayBackQt: unsupported sample format %d",
+ static_cast<int>(format.sampleType()));
+ return;
+ }
+
+ unsigned int bits = 0;
+ switch (format.sampleSize()) {
+ case 8: bits = 8; break;
+ case 16: bits = 16; break;
+ case 24: bits = 24; break;
+ case 32: bits = 32; break;
+ default: bits = 0; break;
+ }
+ if (bits == 0) {
+ qWarning("PlayBackQt: unsupported bits per sample: %d",
+ static_cast<int>(format.sampleSize()));
+ return;
+ }
+
+ Kwave::byte_order_t endian = Kwave::UnknownEndian;
+ switch (format.byteOrder()) {
+ case QAudioFormat::BigEndian: endian = Kwave::BigEndian; break;
+ case QAudioFormat::LittleEndian: endian = Kwave::LittleEndian; break;
+ default: endian = Kwave::UnknownEndian; break;
+ }
+ if (endian == Kwave::UnknownEndian) {
+ qWarning("PlayBackQt: unsupported byte order in audio format: %d",
+ static_cast<int>(format.byteOrder()));
+ return;
+ }
+
+ // create the sample encoder
+ m_encoder = new Kwave::SampleEncoderLinear(sample_format, bits, endian);
+}
+
+//***************************************************************************
+QString Kwave::PlayBackQt::open(const QString &device, double rate,
+ unsigned int channels, unsigned int bits,
+ unsigned int bufbase)
+{
+ qDebug("PlayBackQt::open(device='%s', rate=%0.1f,channels=%u, bits=%u, "
+ "bufbase=%u)", DBG(device), rate, channels, bits, bufbase);
+
+ if ((rate < 1.0f) || !channels || !bits || !bufbase)
+ return i18n("One or more invalid/out of range arguments.");
+
+ // close the previous device
+ close();
+
+ QMutexLocker _lock(&m_lock); // context: main thread
+
+ // make sure we have a valid list of devices
+ scanDevices();
+
+ const QAudioDeviceInfo info(deviceInfo(device));
+ if (info.isNull()) {
+ return i18n("The audio device '%1' is unknown or no longer connected",
+ device.section(QLatin1Char('|'), 0, 0));
+ }
+
+ // find a supported sample format
+ const QAudioFormat preferred_format(info.preferredFormat());
+ QAudioFormat format(preferred_format);
+ format.setSampleSize(Kwave::toInt(bits));
+ format.setChannelCount(Kwave::toInt(channels));
+ format.setSampleRate(Kwave::toInt(rate));
+
+ // find a replacement format with matching codec, channels, bits and rate
+ if (!format.isValid() || !info.isFormatSupported(format))
+ format = info.nearestFormat(format);
+
+ if (format.codec() != _("audio/pcm"))
+ return i18n("PCM playback is not supported");
+
+ if (format.sampleSize() != Kwave::toInt(bits))
+ return i18n("%1 bits per sample are not supported", bits);
+
+ if (format.channelCount() != Kwave::toInt(channels))
+ return i18n("%1 channels playback is not supported", channels);
+
+ if (format.sampleRate() != Kwave::toInt(rate))
+ return i18n("Playback rate %1 Hz is not supported", Kwave::toInt(rate));
+
+ if ( (format.sampleType() != QAudioFormat::SignedInt) &&
+ (format.sampleType() != QAudioFormat::UnSignedInt) )
+ return i18n("Integer sample format is not supported");
+
+ // create a sample encoder
+ createEncoder(format);
+ Q_ASSERT(m_encoder);
+ if (!m_encoder) return i18n("Out of memory");
+
+ // create a new Qt output device
+ m_output = new QAudioOutput(format, this);
+ Q_ASSERT(m_output);
+ if (!m_output) return i18n("Out of memory");
+
+ // connect the state machine and the notification engine
+ connect(m_output, SIGNAL(stateChanged(QAudio::State)),
+ this, SLOT(stateChanged(QAudio::State)));
+
+ // calculate the buffer size in bytes
+ if (bufbase < 8)
+ bufbase = 8;
+ m_buffer_size = (1U << bufbase);
+ qDebug(" buffer size = %u", m_buffer_size);
+
+ // in the rare case that out backend already gives us a period size,
+ // check out buffer size against it
+ m_buffer_size = qMax(m_buffer_size, Kwave::toUint(m_output->periodSize()));
+
+ m_buffer.start(m_buffer_size, 0);
+
+ // open the output device for writing
+ m_output->start(&m_buffer);
+
+ // calculate an appropriate timeout, based on the period and buffer sizes
+ const int period_size = m_output->periodSize();
+ qDebug(" period_size = %d", period_size);
+ unsigned int bytes_per_frame = m_encoder->rawBytesPerSample() * channels;
+ unsigned int buffer_size = qMax(qMax<int>(period_size, m_buffer_size),
+ m_output->bufferSize());
+ unsigned int buffer_frames =
+ ((buffer_size * 2) + (bytes_per_frame - 1)) / bytes_per_frame;
+ int timeout = qMax(Kwave::toInt((1000 * buffer_frames) / rate), 100);
+ qDebug(" timeout = %d ms", timeout);
+ m_buffer.setTimeout(timeout);
+
+ if (m_output->error() != QAudio::NoError) {
+ qDebug("error no: %d", int(m_output->error()));
+ return i18n("Opening the Qt Multimedia device '%1' failed", device);
+ }
+
+ return QString();
+}
+
+//***************************************************************************
+int Kwave::PlayBackQt::write(const Kwave::SampleArray &samples)
+{
+ QByteArray frame;
+
+ {
+ QMutexLocker _lock(&m_lock); // context: worker thread
+
+ if (!m_encoder || !m_output) return -EIO;
+
+ int bytes_per_sample = m_encoder->rawBytesPerSample();
+ int bytes_raw = samples.size() * bytes_per_sample;
+
+ frame.resize(bytes_raw);
+ frame.fill(char(0));
+ m_encoder->encode(samples, samples.size(), frame);
+ }
+
+ qint64 written = m_buffer.writeData(frame.constData(), frame.size());
+ if (written != frame.size()) {
+ qDebug("WARNING: Kwave::PlayBackQt::write: written=%lld/%d",
+ written, frame.size());
+ return -EIO;
+ }
+
+ return 0;
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::stateChanged(QAudio::State state)
+{
+ Q_ASSERT(m_output);
+ if (!m_output) return;
+
+ if (m_output->error() != QAudio::NoError) {
+ qDebug("PlaybBackQt::stateChanged(%d), ERROR=%d, "
+ "buffer free=%d",
+ static_cast<int>(state),
+ static_cast<int>(m_output->error()),
+ m_output->bytesFree()
+ );
+ }
+ switch (state) {
+ case QAudio::ActiveState:
+ qDebug("PlaybBackQt::stateChanged(ActiveState)");
+ break;
+ case QAudio::SuspendedState:
+ qDebug("PlaybBackQt::stateChanged(SuspendedState)");
+ break;
+ case QAudio::StoppedState: {
+ qDebug("PlaybBackQt::stateChanged(StoppedState)");
+ break;
+ }
+ case QAudio::IdleState:
+ qDebug("PlaybBackQt::stateChanged(IdleState)");
+ break;
+ default:
+ qWarning("PlaybBackQt::stateChanged(%d)",
+ static_cast<int>(state));
+ break;
+ }
+}
+
+//***************************************************************************
+QAudioDeviceInfo Kwave::PlayBackQt::deviceInfo(const QString &device) const
+{
+ // check for default device
+ if (!device.length() || (device == DEFAULT_DEVICE))
+ return QAudioDeviceInfo::defaultOutputDevice();
+
+ // check if the device name is known
+ if (m_device_name_map.isEmpty() || !m_device_name_map.contains(device))
+ return QAudioDeviceInfo();
+
+ // translate the path into a Qt audio output device name
+ // iterate over all available devices
+ const QString dev_name = m_device_name_map[device];
+ foreach (const QAudioDeviceInfo &dev, m_available_devices) {
+ if (dev.deviceName() == dev_name)
+ return QAudioDeviceInfo(dev);
+ }
+
+ // fallen through: return empty info
+ return QAudioDeviceInfo();
+}
+
+//***************************************************************************
+int Kwave::PlayBackQt::close()
+{
+ qDebug("Kwave::PlayBackQt::close()");
+
+ QMutexLocker _lock(&m_lock); // context: main thread
+
+ if (m_output && m_encoder) {
+ unsigned int pad_bytes_cnt = m_output->periodSize();
+ unsigned int bytes_per_frame = m_output->format().bytesPerFrame();
+ unsigned int pad_samples_cnt = pad_bytes_cnt / bytes_per_frame;
+ Kwave::SampleArray pad_samples(pad_samples_cnt);
+ QByteArray pad_bytes(pad_bytes_cnt, char(0));
+ m_encoder->encode(pad_samples, pad_samples_cnt, pad_bytes);
+
+ m_buffer.drain(pad_bytes);
+
+ // stopping the engine might block, so we need to do this unlocked
+ qDebug("Kwave::PlayBackQt::close() - flushing..., state=%d",
+ m_output->state());
+ while (
+ m_output &&
+ (m_output->state() == QAudio::ActiveState) &&
+ m_buffer.bytesAvailable()
+ ) {
+ m_lock.unlock();
+ qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
+ m_lock.lock();
+ }
+ qDebug("Kwave::PlayBackQt::close() - flushing done.");
+ m_lock.unlock();
+ m_output->stop();
+ m_buffer.stop();
+ m_lock.lock();
+ }
+
+ delete m_output;
+ m_output = 0;
+
+ delete m_encoder;
+ m_encoder = 0;
+
+ m_device_name_map.clear();
+ m_available_devices.clear();
+
+ qDebug("Kwave::PlayBackQt::close() - DONE");
+ return 0;
+}
+
+//***************************************************************************
+QStringList Kwave::PlayBackQt::supportedDevices()
+{
+ QMutexLocker _lock(&m_lock); // context: main thread
+
+ // re-validate the list if necessary
+ if (m_device_name_map.isEmpty() || m_available_devices.isEmpty())
+ scanDevices();
+
+ QStringList list = m_device_name_map.keys();
+
+ // move the "default" device to the start of the list
+ if (list.contains(DEFAULT_DEVICE))
+ list.move(list.indexOf(DEFAULT_DEVICE), 0);
+
+ if (!list.isEmpty()) list.append(_("#TREE#"));
+
+ return list;
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::scanDevices()
+{
+ m_available_devices.clear();
+ m_device_name_map.clear();
+
+ // get the list of available audio output devices from Qt
+ foreach (const QAudioDeviceInfo &device,
+ QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
+ {
+ QString qt_name = device.deviceName();
+
+ // for debugging: list all devices
+// qDebug("name='%s'", DBG(qt_name));
+
+ // device name not available ?
+ if (!qt_name.length()) {
+ qWarning("PlayBackQt::supportedDevices() "
+ "=> BUG: device with no name?");
+ continue;
+ }
+
+ QString gui_name = qt_name + _("|sound_note");
+ if (m_device_name_map.contains(gui_name)) {
+ qWarning("PlayBackQt::supportedDevices() "
+ "=> BUG: duplicate device name: '%s'", DBG(gui_name));
+ continue;
+ }
+
+ m_available_devices.append(device);
+ m_device_name_map[gui_name] = qt_name;
+ }
+}
+
+//***************************************************************************
+QString Kwave::PlayBackQt::fileFilter()
+{
+ return _("");
+}
+
+//***************************************************************************
+QList<unsigned int> Kwave::PlayBackQt::supportedBits(const QString &device)
+{
+ QMutexLocker _lock(&m_lock); // context: main thread
+
+ QList<unsigned int> list;
+ const QAudioDeviceInfo info(deviceInfo(device));
+
+ // no devices at all -> empty list
+ if (info.isNull()) return list;
+
+ // iterate over all supported sample sizes
+ foreach (int bits, info.supportedSampleSizes()) {
+ if (!list.contains(bits) && (bits > 0))
+ list << Kwave::toUint(bits);
+ }
+
+ std::sort(list.begin(), list.end(), std::greater<unsigned int>());
+ return list;
+}
+
+//***************************************************************************
+int Kwave::PlayBackQt::detectChannels(const QString &device,
+ unsigned int &min, unsigned int &max)
+{
+ QMutexLocker _lock(&m_lock); // context: main thread
+
+ const QAudioDeviceInfo info(deviceInfo(device));
+
+ max = std::numeric_limits<unsigned int>::min();
+ min = std::numeric_limits<unsigned int>::max();
+
+ // no devices at all -> empty
+ if (info.isNull()) return -1;
+
+ // iterate over all supported sample sizes
+ foreach (int channels, info.supportedChannelCounts()) {
+ if (channels <= 0) continue;
+ unsigned int c = Kwave::toUint(channels);
+ if (c < 1) continue;
+ if (c < min) min = c;
+ if (c > max) max = c;
+ }
+
+ return (max > 0) ? max : -1;
+}
+
+//***************************************************************************
+//***************************************************************************
+
+//***************************************************************************
+Kwave::PlayBackQt::Buffer::Buffer()
+ :QIODevice(),
+ m_lock(QMutex::Recursive),
+ m_sem_free(0),
+ m_sem_filled(0),
+ m_raw_buffer(),
+ m_timeout(1000),
+ m_pad_data(),
+ m_pad_ofs(0)
+{
+}
+
+//***************************************************************************
+Kwave::PlayBackQt::Buffer::~Buffer()
+{
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::Buffer::start(unsigned int buf_size, int timeout)
+{
+ m_raw_buffer.clear();
+ m_sem_filled.acquire(m_sem_filled.available());
+ m_sem_free.acquire(m_sem_free.available());
+ m_sem_free.release(buf_size);
+ m_timeout = timeout;
+ m_pad_data.clear();
+ m_pad_ofs = 0;
+
+ open(QIODevice::ReadOnly);
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::Buffer::setTimeout(int timeout)
+{
+ QMutexLocker _lock(&m_lock); // context: main thread
+ m_timeout = timeout;
+ qDebug("Kwave::PlayBackQt::Buffer::setTimeout(%d)", timeout);
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::Buffer::drain(QByteArray &padding)
+{
+ m_pad_data = padding;
+ m_pad_ofs = 0;
+}
+
+//***************************************************************************
+void Kwave::PlayBackQt::Buffer::stop()
+{
+ close();
+}
+
+//***************************************************************************
+qint64 Kwave::PlayBackQt::Buffer::readData(char *data, qint64 len)
+{
+ qint64 read_bytes = -1;
+ qint64 requested = len;
+
+// qDebug("Kwave::PlayBackQt::Buffer::readData(..., len=%lld", requested);
+ if (len == 0) return 0;
+ if (len < 0) return -1;
+
+ while (len > 0) {
+ int count = qMin(qMax<qint64>(m_sem_filled.available(), 1), len);
+ if (Q_LIKELY(m_sem_filled.tryAcquire(count, m_timeout))) {
+// qDebug(" read: locking...");
+ QMutexLocker _lock(&m_lock); // context: qt streaming engine
+// qDebug(" read: locked, count=%lld", len);
+ m_sem_free.release(count);
+ if (read_bytes < 0) read_bytes = 0;
+ read_bytes += count;
+ len -= count;
+ while (count--)
+ *(data++) = m_raw_buffer.dequeue();
+ } else break;
+ }
+
+ // if we are at the end of the stream: do some padding to satisfy Qt
+ while ( (read_bytes < requested) &&
+ !m_pad_data.isEmpty() && (m_pad_ofs < m_pad_data.size()) )
+ {
+ *(data++) = 0;
+ read_bytes++;
+ m_pad_ofs++;
+ }
+
+ if (read_bytes != requested)
+ qDebug("Kwave::PlayBackQt::Buffer::readData(...) -> read=%lld/%lld",
+ read_bytes, requested);
+
+ return read_bytes;
+}
+
+//***************************************************************************
+qint64 Kwave::PlayBackQt::Buffer::writeData(const char *data, qint64 len)
+{
+
+ qint64 written_bytes = 0;
+ while (len) {
+ int count = qMin(qMax<qint64>(m_sem_free.available(), 1), len);
+ if (Q_LIKELY(m_sem_free.tryAcquire(count, m_timeout * 10))) {
+ QMutexLocker _lock(&m_lock); // context: kwave worker thread
+ m_sem_filled.release(count);
+ written_bytes += count;
+ len -= count;
+ while (count--)
+ m_raw_buffer.enqueue(*(data++));
+ } else break;
+ }
+
+ return written_bytes;
+}
+
+//***************************************************************************
+qint64 Kwave::PlayBackQt::Buffer::bytesAvailable() const
+{
+ return QIODevice::bytesAvailable() +
+ m_sem_filled.available() +
+ m_pad_data.size() -
+ m_pad_ofs;
+}
+
+#endif /* HAVE_QT_AUDIO_SUPPORT */
+
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/playback/PlayBack-Qt.h b/plugins/playback/PlayBack-Qt.h
new file mode 100644
index 0000000..d578bad
--- /dev/null
+++ b/plugins/playback/PlayBack-Qt.h
@@ -0,0 +1,248 @@
+/***************************************************************************
+ PlayBack-Qt.h - playback device for Qt Multimedia
+ -------------------
+ begin : Thu Nov 12 2015
+ copyright : (C) 2015 by Thomas Eschenbacher
+ email : Thomas.Eschenbacher at gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PLAY_BACK_QT_H
+#define PLAY_BACK_QT_H
+
+#include "config.h"
+#ifdef HAVE_QT_AUDIO_SUPPORT
+
+#include <QAudio>
+#include <QAudioDeviceInfo>
+#include <QByteArray>
+#include <QIODevice>
+#include <QList>
+#include <QMap>
+#include <QMutex>
+#include <QObject>
+#include <QQueue>
+#include <QSemaphore>
+#include <QString>
+
+#include "libkwave/PlayBackDevice.h"
+#include "libkwave/SampleArray.h"
+
+class QAudioOutput;
+class QIODevice;
+
+namespace Kwave
+{
+
+ class SampleEncoder;
+
+ class PlayBackQt: public QObject,
+ public Kwave::PlayBackDevice
+ {
+ Q_OBJECT
+ public:
+
+ /** Default constructor */
+ PlayBackQt();
+
+ /** Destructor */
+ virtual ~PlayBackQt();
+
+ /**
+ * Opens the device for playback.
+ * @see PlayBackDevice::open
+ */
+ virtual QString open(const QString &device, double rate,
+ unsigned int channels, unsigned int bits,
+ unsigned int bufbase);
+
+ /**
+ * Writes an array of samples to the output device.
+ * @see PlayBackDevice::write
+ */
+ virtual int write(const Kwave::SampleArray &samples);
+
+ /**
+ * Closes the output device.
+ * @see PlayBackDevice::close
+ */
+ virtual int close();
+
+ /** return a string list with supported device names */
+ virtual QStringList supportedDevices();
+
+ /** return a string suitable for a "File Open..." dialog */
+ virtual QString fileFilter();
+
+ /**
+ * returns a list of supported bits per sample resolutions
+ * of a given device.
+ *
+ * @param device filename of the device
+ * @return list of supported bits per sample, or empty on errors
+ */
+ virtual QList<unsigned int> supportedBits(const QString &device);
+
+ /**
+ * Detect the minimum and maximum number of channels.
+ * If the detection fails, minimum and maximum are set to zero.
+ *
+ * @param device filename of the device
+ * @param min receives the lowest supported number of channels
+ * @param max receives the highest supported number of channels
+ * @return zero or positive number if ok, negative error number if failed
+ */
+ virtual int detectChannels(const QString &device,
+ unsigned int &min, unsigned int &max);
+
+ private slots:
+
+ /**
+ * connected to the audio output device and gets notified in case
+ * of state changes like start/stop of the stream or errors.
+ * @param state the new device state, like active, stopped, idle etc.
+ */
+ void stateChanged(QAudio::State state);
+
+ private:
+
+ /**
+ * creates a sample encoder for playback, for linear
+ * formats
+ * @param format the preferred format of the audio output device
+ */
+ void createEncoder(const QAudioFormat &format);
+
+ /** scan all Qt audio output devices, re-creates m_device_list */
+ void scanDevices();
+
+ /**
+ * Gets the full device info of a playback device, identified by
+ * the device name.
+ *
+ * @param device name of the device or empty string for default
+ * @return a QAudioDeviceInfo
+ */
+ QAudioDeviceInfo deviceInfo(const QString &device) const;
+
+ private:
+
+ class Buffer : public QIODevice
+ {
+ public:
+ /** constructor */
+ Buffer();
+
+ /** destructor */
+ virtual ~Buffer();
+
+ /**
+ * start filling the buffer
+ * @param buf_size size of the buffer in bytes
+ * @param timeout read/write timeout [ms]
+ */
+ void start(unsigned int buf_size, int timeout);
+
+ /**
+ * set a new read/write timeout
+ * @note does not influence currently waiting reads/writes
+ * @param timeout a new read/write timeout [ms]
+ */
+ void setTimeout(int timeout);
+
+ /**
+ * drain the sink, at the end of playback:
+ * provide padding to provide data for a full period
+ * @param padding array of bytes used for padding
+ */
+ void drain(QByteArray &padding);
+
+ /** stop filling the buffer */
+ void stop();
+
+ /**
+ * read data out from the buffer, called from the Qt audio device
+ * side
+ * @param data pointer to a buffer (of bytes) to receive the data
+ * @param len number of bytes to read
+ * @return number of bytes that have been read
+ */
+ virtual qint64 readData(char *data, qint64 len);
+
+ /**
+ * write data into the buffer, called from our own worker thread
+ * @param data pointer to a buffer (of bytes) to write
+ * @param len number of bytes to write
+ * @return number of bytes written
+ */
+ virtual qint64 writeData(const char *data, qint64 len);
+
+ /** returns the number of bytes available for reading */
+ virtual qint64 bytesAvailable() const;
+
+ private:
+
+ /** mutex for locking the queue */
+ QMutex m_lock;
+
+ /** semaphore with free buffer space */
+ QSemaphore m_sem_free;
+
+ /** semaphore with filled buffer space */
+ QSemaphore m_sem_filled;
+
+ /** raw buffer with audio data */
+ QQueue<char> m_raw_buffer;
+
+ /** read timeout [ms] */
+ int m_timeout;
+
+ /** buffer with padding data */
+ QByteArray m_pad_data;
+
+ /** read pointer within m_pad_data */
+ int m_pad_ofs;
+ };
+
+ private:
+
+ /** mutex for locking the streaming thread against main thread */
+ QMutex m_lock;
+
+ /**
+ * dictionary for translating verbose device names
+ * into Qt audio output device names
+ * (key = verbose name, data = Qt output device name)
+ */
+ QMap<QString, QString> m_device_name_map;
+
+ /** list of available Qt output devices */
+ QList<QAudioDeviceInfo> m_available_devices;
+
+ /** Qt audio output instance */
+ QAudioOutput *m_output;
+
+ /** buffer size in bytes */
+ unsigned int m_buffer_size;
+
+ /** encoder for converting from samples to raw format */
+ Kwave::SampleEncoder *m_encoder;
+
+ Kwave::PlayBackQt::Buffer m_buffer;
+ };
+}
+
+#endif /* HAVE_QT_AUDIO_SUPPORT */
+
+#endif /* PLAY_BACK_QT_H */
+
+//***************************************************************************
+//***************************************************************************
diff --git a/plugins/playback/PlayBackPlugin.cpp b/plugins/playback/PlayBackPlugin.cpp
index bd38179..b420ce9 100644
--- a/plugins/playback/PlayBackPlugin.cpp
+++ b/plugins/playback/PlayBackPlugin.cpp
@@ -60,6 +60,7 @@
#include "PlayBack-ALSA.h"
#include "PlayBack-OSS.h"
#include "PlayBack-PulseAudio.h"
+#include "PlayBack-Qt.h"
#include "PlayBackDialog.h"
#include "PlayBackPlugin.h"
@@ -230,6 +231,10 @@ QList<Kwave::playback_method_t> Kwave::PlayBackPlugin::supportedMethods()
{
QList<Kwave::playback_method_t> methods;
+#ifdef HAVE_QT_AUDIO_SUPPORT
+ methods.append(Kwave::PLAYBACK_QT_AUDIO);
+#endif /* HAVE_QT_AUDIO_SUPPORT */
+
#ifdef HAVE_PULSEAUDIO_SUPPORT
methods.append(Kwave::PLAYBACK_PULSEAUDIO);
#endif /* HAVE_PULSEAUDIO_SUPPORT */
@@ -255,6 +260,11 @@ Kwave::PlayBackDevice *Kwave::PlayBackPlugin::createDevice(
static_cast<int>(method) );
switch (method) {
+#ifdef HAVE_QT_AUDIO_SUPPORT
+ case Kwave::PLAYBACK_QT_AUDIO:
+ return new Kwave::PlayBackQt();
+#endif /* HAVE_QT_AUDIO_SUPPORT */
+
#ifdef HAVE_PULSEAUDIO_SUPPORT
case Kwave::PLAYBACK_PULSEAUDIO:
return new Kwave::PlayBackPulseAudio(
More information about the kde-doc-english
mailing list