mirror of
https://invent.kde.org/multimedia/kdenlive
synced 2025-12-07 00:39:58 +01:00
Compare commits
258 Commits
work/embed
...
feature/ja
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46f72f66f3 | ||
|
|
a2379633d9 | ||
|
|
cb43038a7f | ||
|
|
c89f6b3879 | ||
|
|
68e53bfef2 | ||
|
|
e5c206765e | ||
|
|
5bb198126e | ||
|
|
38628dd8a1 | ||
|
|
f4f42c4b21 | ||
|
|
2ee712f9e2 | ||
|
|
2747a209dc | ||
|
|
519dce4464 | ||
|
|
8ed5bce13f | ||
|
|
0d341548a5 | ||
|
|
beb0f220dd | ||
|
|
235dfe6bff | ||
|
|
629b0cb6de | ||
|
|
45b0b4dc6c | ||
|
|
0478cf276a | ||
|
|
bd3fcb13bb | ||
|
|
b710e8548d | ||
|
|
2682ac5f8c | ||
|
|
80291b4246 | ||
|
|
d9fff06f74 | ||
|
|
ca5587224b | ||
|
|
f3bca8009f | ||
|
|
1b5f18dfbc | ||
|
|
08614d7be6 | ||
|
|
74d4a56cea | ||
|
|
691dbadbe8 | ||
|
|
b4cd8e5882 | ||
|
|
96d873bb2b | ||
|
|
8ca44cc603 | ||
|
|
de22d39e22 | ||
|
|
c16e330840 | ||
|
|
4dd620b9c3 | ||
|
|
910e49c805 | ||
|
|
0097caf273 | ||
|
|
f2e2aa0b76 | ||
|
|
f8cc04f775 | ||
|
|
774302c6eb | ||
|
|
f663bdd328 | ||
|
|
a5c32c8155 | ||
|
|
ab3b5e2ca7 | ||
|
|
2af433083b | ||
|
|
bf992aef55 | ||
|
|
7095920970 | ||
|
|
39b7a78218 | ||
|
|
d2cce9bf4b | ||
|
|
2515ab2622 | ||
|
|
5330aaa9f0 | ||
|
|
b378528894 | ||
|
|
dca8b03ba3 | ||
|
|
142e3e41d6 | ||
|
|
b3391222fa | ||
|
|
04b5d88d68 | ||
|
|
b381719994 | ||
|
|
532be6179d | ||
|
|
9e46e2872d | ||
|
|
a01e37d60c | ||
|
|
5eaf1c602a | ||
|
|
f9c8e5d62a | ||
|
|
4b3cd3c847 | ||
|
|
f7573287d1 | ||
|
|
5948ee0151 | ||
|
|
b4633f1d6b | ||
|
|
fe5586ec6d | ||
|
|
9bcec2a7dd | ||
|
|
d431251c58 | ||
|
|
31012828bb | ||
|
|
7c9855c7b5 | ||
|
|
952681475b | ||
|
|
28ea8dd68e | ||
|
|
ca5c8bf210 | ||
|
|
10d1c827c1 | ||
|
|
066d37fc43 | ||
|
|
1efbcfc92a | ||
|
|
2e654bcec3 | ||
|
|
06a8be51bc | ||
|
|
ce50b7deb5 | ||
|
|
ffd37b37a2 | ||
|
|
32813818d1 | ||
|
|
81e7614799 | ||
|
|
256767b764 | ||
|
|
e467a6a777 | ||
|
|
b67fb9481a | ||
|
|
f205a50f86 | ||
|
|
53fa655121 | ||
|
|
40d9edcf5c | ||
|
|
c6b1beff20 | ||
|
|
fa086ec448 | ||
|
|
6129f0b16e | ||
|
|
0436f22757 | ||
|
|
8c96562a49 | ||
|
|
c030d42f97 | ||
|
|
6e93ff5fe7 | ||
|
|
029d2db59a | ||
|
|
95c1b91921 | ||
|
|
ed31bc0d0d | ||
|
|
fc8ccb61a4 | ||
|
|
c8a693b178 | ||
|
|
b90c5ccfbf | ||
|
|
e3bf2a0eda | ||
|
|
958cb77341 | ||
|
|
f3efa339af | ||
|
|
0496d7f664 | ||
|
|
6bccc67355 | ||
|
|
70c198ad88 | ||
|
|
a121e94dd9 | ||
|
|
6c8b8a6e03 | ||
|
|
f43ef86a0f | ||
|
|
2abbfe2ef0 | ||
|
|
0438a0682c | ||
|
|
12cd43cfd4 | ||
|
|
8afe094faa | ||
|
|
c3cc3abc21 | ||
|
|
e5752ab314 | ||
|
|
a84ecc52ea | ||
|
|
1ddbf38fad | ||
|
|
88fab6e0fd | ||
|
|
8c55aa37ab | ||
|
|
2dd1db6676 | ||
|
|
bc55890a55 | ||
|
|
ed37f7bad2 | ||
|
|
39465e1126 | ||
|
|
cabd83dcf4 | ||
|
|
79634a597c | ||
|
|
d06b38d7e6 | ||
|
|
f50634a4de | ||
|
|
a44e4ced83 | ||
|
|
0088750869 | ||
|
|
bc95cc02c1 | ||
|
|
aa58692458 | ||
|
|
77b5c4becd | ||
|
|
bea3e057f8 | ||
|
|
3701617d75 | ||
|
|
b3ee29e687 | ||
|
|
9256029a01 | ||
|
|
cfedab39f0 | ||
|
|
43dcc8bcec | ||
|
|
2e19960808 | ||
|
|
456efd6445 | ||
|
|
fe9248af58 | ||
|
|
28406cbc78 | ||
|
|
d855ca0cca | ||
|
|
fde4c3affb | ||
|
|
382b731ef8 | ||
|
|
bc24dd0547 | ||
|
|
3c9c2762bc | ||
|
|
798becb879 | ||
|
|
00118ecfed | ||
|
|
3e0341727e | ||
|
|
195ea6b6ef | ||
|
|
3ac7926509 | ||
|
|
e687ac0352 | ||
|
|
a0d3941b85 | ||
|
|
0ec4289e12 | ||
|
|
61c33cd043 | ||
|
|
62fc36de3a | ||
|
|
599909d0e4 | ||
|
|
1cb68ab635 | ||
|
|
5937575100 | ||
|
|
e5e694dff0 | ||
|
|
d850307c9a | ||
|
|
4f4107b7ed | ||
|
|
7827dd337b | ||
|
|
c5bc2ae79a | ||
|
|
9d1821169d | ||
|
|
0e4afc2943 | ||
|
|
3e3ba91918 | ||
|
|
6563c7d774 | ||
|
|
af159d08a5 | ||
|
|
713ff1305f | ||
|
|
c38e2a4b64 | ||
|
|
bb1b54efbd | ||
|
|
f5d066e640 | ||
|
|
7286e6bbfe | ||
|
|
f44e280f07 | ||
|
|
4ec7e33d13 | ||
|
|
d7b73a8940 | ||
|
|
ec3d586dd2 | ||
|
|
1fd99b2bea | ||
|
|
a5e26b7450 | ||
|
|
eb31061223 | ||
|
|
1027d24a85 | ||
|
|
d7a1012232 | ||
|
|
8da0f0e570 | ||
|
|
bd2f5b7d94 | ||
|
|
b467a5bcf0 | ||
|
|
208203d4ee | ||
|
|
84b8184f91 | ||
|
|
b614e0429e | ||
|
|
ea66901d0c | ||
|
|
970988acb4 | ||
|
|
b3dc6db630 | ||
|
|
fcbbb78fd1 | ||
|
|
053a3b4aee | ||
|
|
6ddd3c58f7 | ||
|
|
295356483d | ||
|
|
9287b050c7 | ||
|
|
0aecfd55f1 | ||
|
|
1967f92980 | ||
|
|
e7a961b5ec | ||
|
|
99d8b834ac | ||
|
|
094eb7d607 | ||
|
|
4e52df362c | ||
|
|
138d8b03b3 | ||
|
|
e5290c8a58 | ||
|
|
390c76e78d | ||
|
|
fc574c4476 | ||
|
|
d2097f7e93 | ||
|
|
a8e3e3808d | ||
|
|
0f0174667a | ||
|
|
c4b42d2f39 | ||
|
|
a250c9e3e4 | ||
|
|
e5cd254489 | ||
|
|
388f59a181 | ||
|
|
c24494ec5e | ||
|
|
9cb70f8336 | ||
|
|
6566ed80d2 | ||
|
|
454e2fc50a | ||
|
|
4121442268 | ||
|
|
c08f3ceeba | ||
|
|
c76747d282 | ||
|
|
04ec462b4f | ||
|
|
15afb87437 | ||
|
|
f6bf704d93 | ||
|
|
0f78a3d97a | ||
|
|
e1f2a9fd5e | ||
|
|
50451496ba | ||
|
|
9b72f1df9f | ||
|
|
10246b5d17 | ||
|
|
b3ef3d6bba | ||
|
|
20dce6e949 | ||
|
|
ffaffc8d31 | ||
|
|
10ad1edcda | ||
|
|
77d8cca359 | ||
|
|
4db534e109 | ||
|
|
5257097d15 | ||
|
|
eb19dab9a3 | ||
|
|
81ffc86247 | ||
|
|
0021e7d518 | ||
|
|
6b24f25d50 | ||
|
|
6b9c3cb660 | ||
|
|
09efe0bca0 | ||
|
|
fc88c34243 | ||
|
|
37b8773983 | ||
|
|
4fa2235ac5 | ||
|
|
a010eb0e39 | ||
|
|
c5b60ba999 | ||
|
|
cde2d7e3db | ||
|
|
65d0c1f96a | ||
|
|
6fbcf1b7dd | ||
|
|
4c60e68058 | ||
|
|
b1f3c36559 | ||
|
|
97571645de | ||
|
|
051ddbf0d5 | ||
|
|
419d8e22c7 |
82
cmake/modules/FindJack.cmake
Normal file
82
cmake/modules/FindJack.cmake
Normal file
@@ -0,0 +1,82 @@
|
||||
# - Try to find jack-2.6
|
||||
# Once done this will define
|
||||
#
|
||||
# JACK_FOUND - system has jack
|
||||
# JACK_INCLUDE_DIRS - the jack include directory
|
||||
# JACK_LIBRARIES - Link these to use jack
|
||||
# JACK_DEFINITIONS - Compiler switches required for using jack
|
||||
#
|
||||
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
|
||||
# Modified for other libraries by Lasse Kärkkäinen <tronic>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
|
||||
if (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(JACK_FOUND TRUE)
|
||||
else (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
|
||||
include(UsePkgConfig)
|
||||
pkgconfig(jack _JACK_INCLUDEDIR _JACK_LIBDIR _JACK_LDFLAGS _JACK_CFLAGS)
|
||||
else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_JACK jack)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
|
||||
find_path(JACK_INCLUDE_DIR
|
||||
NAMES
|
||||
jack/jack.h
|
||||
PATHS
|
||||
${_JACK_INCLUDEDIR}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/opt/local/include
|
||||
/sw/include
|
||||
)
|
||||
|
||||
find_library(JACK_LIBRARY
|
||||
NAMES
|
||||
jack
|
||||
PATHS
|
||||
${_JACK_LIBDIR}
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib
|
||||
)
|
||||
|
||||
if (JACK_LIBRARY AND JACK_INCLUDE_DIR)
|
||||
set(JACK_FOUND TRUE)
|
||||
|
||||
set(JACK_INCLUDE_DIRS
|
||||
${JACK_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
set(JACK_LIBRARIES
|
||||
${JACK_LIBRARIES}
|
||||
${JACK_LIBRARY}
|
||||
)
|
||||
|
||||
endif (JACK_LIBRARY AND JACK_INCLUDE_DIR)
|
||||
|
||||
if (JACK_FOUND)
|
||||
if (NOT JACK_FIND_QUIETLY)
|
||||
message(STATUS "Found jack: ${JACK_LIBRARY}")
|
||||
endif (NOT JACK_FIND_QUIETLY)
|
||||
else (JACK_FOUND)
|
||||
if (JACK_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find JACK")
|
||||
endif (JACK_FIND_REQUIRED)
|
||||
endif (JACK_FOUND)
|
||||
|
||||
# show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(JACK_INCLUDE_DIRS JACK_LIBRARIES)
|
||||
|
||||
endif (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
|
||||
|
||||
BIN
icons/hi16-action-kdenlive-toggle-jack-mon.png
Normal file
BIN
icons/hi16-action-kdenlive-toggle-jack-mon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 315 B |
BIN
icons/hi16-action-kdenlive-toggle-jack.png
Normal file
BIN
icons/hi16-action-kdenlive-toggle-jack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 849 B |
BIN
icons/ox16-action-kdenlive-toggle-jack-mon.png
Normal file
BIN
icons/ox16-action-kdenlive-toggle-jack-mon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 315 B |
BIN
icons/ox16-action-kdenlive-toggle-jack.png
Normal file
BIN
icons/ox16-action-kdenlive-toggle-jack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 849 B |
@@ -10,9 +10,11 @@ if(APPLE)
|
||||
endif(APPLE)
|
||||
macro_optional_find_package(Nepomuk)
|
||||
macro_optional_find_package(QJSON)
|
||||
macro_optional_find_package(Jack)
|
||||
|
||||
option(WITH_V4L "Build capture support with Video4Linux" ON)
|
||||
option(WITH_JogShuttle "Build Jog/Shuttle support" ON)
|
||||
option(WITH_JACK "Build Jack support" ON)
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +35,12 @@ if(WITH_JogShuttle)
|
||||
endif(HAVE_LINUX_INPUT_H)
|
||||
endif(WITH_JogShuttle)
|
||||
|
||||
if(WITH_JACK)
|
||||
if(JACK_FOUND)
|
||||
set(BUILD_Jack TRUE)
|
||||
endif(JACK_FOUND)
|
||||
endif(WITH_JACK)
|
||||
|
||||
macro_log_feature(QT_QTOPENGL_FOUND
|
||||
"QtOpenGL"
|
||||
"Qt bindings for the OpenGL library"
|
||||
@@ -282,6 +290,10 @@ if(BUILD_JogShuttle)
|
||||
)
|
||||
endif(BUILD_JogShuttle)
|
||||
|
||||
if(BUILD_Jack)
|
||||
list(APPEND kdenlive_SRCS jackdevice.cpp)
|
||||
endif(BUILD_Jack)
|
||||
|
||||
kde4_add_kcfg_files(kdenlive_SRCS kdenlivesettings.kcfgc)
|
||||
qt4_add_dbus_adaptor(kdenlive_SRCS
|
||||
org.kdenlive.MainWindow.xml
|
||||
@@ -376,6 +388,12 @@ if(BUILD_JogShuttle)
|
||||
add_definitions(-DUSE_JOGSHUTTLE)
|
||||
endif(BUILD_JogShuttle)
|
||||
|
||||
if(BUILD_Jack)
|
||||
add_definitions(-DUSE_JACK)
|
||||
include_directories(${JACK_INCLUDE_DIRS})
|
||||
target_link_libraries(kdenlive ${JACK_LIBRARIES})
|
||||
endif(BUILD_Jack)
|
||||
|
||||
install(TARGETS kdenlive DESTINATION ${BIN_INSTALL_DIR})
|
||||
install(FILES
|
||||
kdenliveui.rc
|
||||
|
||||
@@ -29,6 +29,25 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
bool AbstractRender::isAudioEngineActive(AudioEngine::Type engine)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
if (engine == AudioEngine::Jack)
|
||||
if (&JACKDEV && JACKDEV.isValid())
|
||||
return true;
|
||||
|
||||
if (engine == AudioEngine::Mlt)
|
||||
if (!&JACKDEV || (&JACKDEV && !JACKDEV.isValid()))
|
||||
return true;
|
||||
#else
|
||||
if (engine == AudioEngine::Mlt)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
AbstractMonitor::AbstractMonitor(Kdenlive::MONITORID id, MonitorManager *manager, QWidget *parent):
|
||||
QWidget(parent),
|
||||
videoSurface(NULL),
|
||||
|
||||
@@ -30,9 +30,49 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef USE_JACK
|
||||
#include "jackdevice.h"
|
||||
#endif
|
||||
|
||||
class MonitorManager;
|
||||
class VideoContainer;
|
||||
|
||||
/* transport slave namespace */
|
||||
namespace Slave
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Internal = 0,
|
||||
Jack
|
||||
};
|
||||
};
|
||||
|
||||
/* audio engine namespace */
|
||||
namespace AudioEngine
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Mlt = 0,
|
||||
Jack
|
||||
};
|
||||
};
|
||||
|
||||
/* render role namespace */
|
||||
namespace Rndr
|
||||
{
|
||||
enum Role
|
||||
{
|
||||
NoRole = (1<<0),
|
||||
OpenCloseJackEngineRole = (1<<1),
|
||||
OpenCloseSlaveRole = (1<<2)
|
||||
};
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(RndrRole, Rndr::Role);
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(RndrRole);
|
||||
|
||||
|
||||
|
||||
class AbstractRender: public QObject
|
||||
{
|
||||
Q_OBJECT public:
|
||||
@@ -41,11 +81,12 @@ Q_OBJECT public:
|
||||
* @param name A unique identifier for this renderer
|
||||
* @param winid The parent widget identifier (required for SDL display). Set to 0 for OpenGL rendering
|
||||
* @param profile The MLT profile used for the renderer (default one will be used if empty). */
|
||||
explicit AbstractRender(Kdenlive::MONITORID name, QWidget *parent = 0)
|
||||
explicit AbstractRender(Kdenlive::MONITORID name, RndrRole role, QWidget *parent = 0)
|
||||
: QObject(parent),
|
||||
sendFrameForAnalysis(false),
|
||||
analyseAudio(false),
|
||||
m_name(name)
|
||||
m_name(name),
|
||||
m_role(role)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -63,9 +104,31 @@ Q_OBJECT public:
|
||||
/** @brief Someone needs us to send again a frame. */
|
||||
virtual void sendFrameUpdate() = 0;
|
||||
|
||||
/** @brief Checks if appropriate role is set. */
|
||||
virtual bool hasRole(Rndr::Role role) {return m_role.testFlag(role);}
|
||||
|
||||
/** @brief Checks if appropriate slave is active.*/
|
||||
virtual bool isSlaveActive(Slave::Type slave) {return (m_activeSlave == slave);}
|
||||
|
||||
/** @brief Enable appropriate slave.*/
|
||||
virtual void enableSlave(Slave::Type slave) {slave = slave;}
|
||||
|
||||
/** @brief Checks if appropriate audio engine is active.*/
|
||||
virtual bool isAudioEngineActive(AudioEngine::Type engine);
|
||||
|
||||
/** @brief Set playback sync monitoring state */
|
||||
virtual void setPlaybackSyncMonEnabled(bool state) {state = state;}
|
||||
|
||||
/** @brief Update configuration */
|
||||
virtual void updateConfiguration() {}
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
|
||||
protected:
|
||||
RndrRole m_role;
|
||||
Slave::Type m_activeSlave;
|
||||
|
||||
signals:
|
||||
/** @brief The renderer refreshed the current frame. */
|
||||
void frameUpdated(const QImage &);
|
||||
|
||||
@@ -154,7 +154,7 @@ void DvdWizardChapters::createMonitor(DVDFORMAT format)
|
||||
{
|
||||
QString profile = DvdWizardVob::getDvdProfile(format);
|
||||
if (m_monitor == NULL) {
|
||||
m_monitor = new Monitor(Kdenlive::dvdMonitor, m_manager, profile, this);
|
||||
m_monitor = new Monitor(Kdenlive::dvdMonitor, m_manager, Rndr::NoRole, profile, this);
|
||||
//m_monitor->start();
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(m_monitor);
|
||||
|
||||
562
src/jackdevice.cpp
Normal file
562
src/jackdevice.cpp
Normal file
@@ -0,0 +1,562 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 by Ed Rogalsky (ed.rogalsky@gmail.com) *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* 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, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
***************************************************************************/
|
||||
|
||||
#include "jackdevice.h"
|
||||
#include "qmath.h"
|
||||
|
||||
|
||||
JackDevice::JackDevice(Mlt::Profile * profile) :
|
||||
m_valid(false),
|
||||
m_shutdown(false),
|
||||
m_transportEnabled(false),
|
||||
m_loopState(0),
|
||||
m_playbackSyncDiff(0),
|
||||
m_playbackSyncDiffMaxValue(1),
|
||||
m_playbackSyncMonEnabled(true),
|
||||
m_playbackSyncMonAction(None)
|
||||
{
|
||||
m_mltProfile = profile;
|
||||
}
|
||||
|
||||
JackDevice& JackDevice::singleton(Mlt::Profile * profile)
|
||||
{
|
||||
static JackDevice* instance = 0;
|
||||
|
||||
if (!instance && profile != 0) {
|
||||
instance = new JackDevice(profile);
|
||||
}
|
||||
|
||||
return *instance;
|
||||
}
|
||||
|
||||
JackDevice::~JackDevice()
|
||||
{
|
||||
// TODO Auto-generated destructor stub
|
||||
|
||||
|
||||
}
|
||||
|
||||
void* JackDevice::runTransportThread(void* device)
|
||||
{
|
||||
((JackDevice*)device)->updateTransportState();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void JackDevice::updateTransportState()
|
||||
{
|
||||
pthread_mutex_lock(&m_transportLock);
|
||||
while (m_valid && !m_shutdown) {
|
||||
jack_position_t jackpos;
|
||||
jack_transport_state_t state = JackTransportStopped;
|
||||
int position = 0;
|
||||
|
||||
/* don't call client if already invalidated */
|
||||
if (!m_shutdown) {
|
||||
state = jack_transport_query(m_client, &jackpos);
|
||||
/* convert jack (audio) to kdenlive video position */
|
||||
position = toVideoPosition(jackpos);
|
||||
}
|
||||
|
||||
if(state != m_state) {
|
||||
m_nextState = m_state = state;
|
||||
|
||||
switch (state) {
|
||||
case JackTransportRolling:
|
||||
/* fire playback started event */
|
||||
emit playbackStarted(position);
|
||||
break;
|
||||
case JackTransportStopped:
|
||||
/* fire playback stopped event */
|
||||
emit playbackStopped(position);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cond_wait(&m_transportCondition, &m_transportLock);
|
||||
}
|
||||
pthread_mutex_unlock(&m_transportLock);
|
||||
}
|
||||
|
||||
int JackDevice::toVideoPosition(const jack_position_t &position)
|
||||
{
|
||||
double jacktime = 0;
|
||||
int frame = 0;
|
||||
double framerate = m_mltProfile->fps();
|
||||
|
||||
jacktime = (double) position.frame / (double) position.frame_rate;
|
||||
frame = qFloor(framerate * jacktime);
|
||||
/* return video frame number */
|
||||
return frame;
|
||||
}
|
||||
|
||||
jack_nframes_t JackDevice::toAudioPosition(const int &position, const jack_nframes_t &framerate)
|
||||
{
|
||||
return (jack_nframes_t)(((double) framerate * (double) position) / (double) m_mltProfile->fps());
|
||||
}
|
||||
|
||||
bool JackDevice::isValid()
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
void JackDevice::open(const QString &name, int channels, int buffersize)
|
||||
{
|
||||
kDebug() << "open client";
|
||||
if (m_valid == true)
|
||||
return;
|
||||
|
||||
jack_options_t options = JackNullOption;
|
||||
jack_status_t status;
|
||||
|
||||
// open client
|
||||
m_client = jack_client_open(name.toUtf8().constData(), options, &status);
|
||||
|
||||
if(m_client == NULL)
|
||||
// AUD_THROW(AUD_ERROR_JACK, clientopen_error);
|
||||
return;
|
||||
|
||||
// set callbacks
|
||||
jack_set_process_callback(m_client, JackDevice::jack_process, this);
|
||||
jack_on_shutdown(m_client, JackDevice::jack_shutdown, this);
|
||||
jack_set_sync_callback(m_client, JackDevice::jack_sync, this);
|
||||
|
||||
/* store the number of channels */
|
||||
m_channels = channels;
|
||||
/* register our output channels which are called ports in jack */
|
||||
m_ports = new jack_port_t*[m_channels];
|
||||
|
||||
// try
|
||||
{
|
||||
char portname[64];
|
||||
for(int i = 0; i < m_channels; i++)
|
||||
{
|
||||
sprintf(portname, "out_%d", i+1);
|
||||
m_ports[i] = jack_port_register(m_client, portname,
|
||||
JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsOutput, 0);
|
||||
if(m_ports[i] == NULL) {
|
||||
// AUD_THROW(AUD_ERROR_JACK, port_error);
|
||||
jack_client_close(m_client);
|
||||
delete[] m_ports;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// catch(AUD_Exception&)
|
||||
// {
|
||||
// jack_client_close(m_client);
|
||||
// delete[] m_ports;
|
||||
// throw;
|
||||
// }
|
||||
|
||||
m_ringbuffers = new jack_ringbuffer_t*[m_channels];
|
||||
for(int i = 0; i < m_channels; i++)
|
||||
m_ringbuffers[i] = jack_ringbuffer_create(buffersize * sizeof(float));
|
||||
|
||||
resetLooping();
|
||||
|
||||
/* connect signals */
|
||||
connect(this, SIGNAL(currentPositionChanged(int)),
|
||||
this, SLOT(processLooping()));
|
||||
connect(this, SIGNAL(currentPositionChanged(int)),
|
||||
this, SLOT(monitorPlaybackSync(int)));
|
||||
|
||||
|
||||
m_valid = true;
|
||||
m_sync = 0;
|
||||
/* store current transport state */
|
||||
m_nextState = m_state = jack_transport_query(m_client, NULL);
|
||||
/* store jacks audio sample rate */
|
||||
m_frameRate = jack_get_sample_rate(m_client);
|
||||
|
||||
pthread_mutex_init(&m_transportLock, NULL);
|
||||
pthread_cond_init(&m_transportCondition, NULL);
|
||||
|
||||
// activate the client
|
||||
if(jack_activate(m_client))
|
||||
{
|
||||
jack_client_close(m_client);
|
||||
delete[] m_ports;
|
||||
for(int i = 0; i < m_channels; i++)
|
||||
jack_ringbuffer_free(m_ringbuffers[i]);
|
||||
delete[] m_ringbuffers;
|
||||
pthread_mutex_destroy(&m_transportLock);
|
||||
pthread_cond_destroy(&m_transportCondition);
|
||||
|
||||
// AUD_THROW(AUD_ERROR_JACK, activate_error);
|
||||
m_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const char** ports = jack_get_ports(m_client, NULL, NULL,
|
||||
JackPortIsPhysical | JackPortIsInput);
|
||||
if(ports != NULL)
|
||||
{
|
||||
for(int i = 0; i < m_channels && ports[i]; i++)
|
||||
jack_connect(m_client, jack_port_name(m_ports[i]), ports[i]);
|
||||
|
||||
free(ports);
|
||||
}
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
pthread_create(&m_transportThread, &attr, runTransportThread, this);
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
/* debug */
|
||||
kDebug()<< "// jack device open ..." << endl;
|
||||
}
|
||||
|
||||
void JackDevice::close()
|
||||
{
|
||||
if(m_valid) {
|
||||
m_valid = false;
|
||||
pthread_mutex_lock(&m_transportLock);
|
||||
pthread_cond_signal(&m_transportCondition);
|
||||
pthread_mutex_unlock(&m_transportLock);
|
||||
pthread_join(m_transportThread, NULL);
|
||||
pthread_cond_destroy(&m_transportCondition);
|
||||
pthread_mutex_destroy(&m_transportLock);
|
||||
|
||||
/* close the client */
|
||||
jack_client_close(m_client);
|
||||
delete[] m_ports;
|
||||
|
||||
/* free and delete ringbuffers */
|
||||
for(int i = 0; i < m_channels; i++)
|
||||
jack_ringbuffer_free(m_ringbuffers[i]);
|
||||
delete[] m_ringbuffers;
|
||||
|
||||
/* disconnect signals */
|
||||
disconnect(this, SIGNAL(currentPositionChanged(int)),
|
||||
this, SLOT(processLooping()));
|
||||
disconnect(this, SIGNAL(currentPositionChanged(int)),
|
||||
this, SLOT(monitorPlaybackSync(int)));
|
||||
|
||||
/* debug */
|
||||
kDebug()<< "// jack device closed ..." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool JackDevice::probe()
|
||||
{
|
||||
jack_client_t *client;
|
||||
jack_status_t status;
|
||||
jack_options_t options = JackNoStartServer;
|
||||
|
||||
/* try to connect to jackd */
|
||||
client = jack_client_open("kdenliveprobe", options, &status, NULL );
|
||||
|
||||
/* close client if connection successful */
|
||||
if (status == 0) {
|
||||
jack_client_close(client);
|
||||
}
|
||||
|
||||
/* return status */
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
void JackDevice::jack_shutdown(void *data)
|
||||
{
|
||||
JackDevice* device = (JackDevice*)data;
|
||||
device->m_shutdown = true;
|
||||
emit device->shutdown();
|
||||
}
|
||||
|
||||
int JackDevice::jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data)
|
||||
{
|
||||
JackDevice* device = (JackDevice*)data;
|
||||
int result = 0;
|
||||
|
||||
/* no job if transport is disabled */
|
||||
if (!device->m_transportEnabled)
|
||||
return 1;
|
||||
|
||||
if(state == JackTransportStopped) {
|
||||
if (!device->m_sync) {
|
||||
/*start seeking */
|
||||
device->m_sync = 1;
|
||||
emit device->playbackSync(device->toVideoPosition(*pos));
|
||||
} else if (device->m_sync == 1) {
|
||||
/* sync position */
|
||||
if (device->m_currentPosition == device->toVideoPosition(*pos)) {
|
||||
device->m_sync = 2;
|
||||
} else {
|
||||
device->m_sync = 0;
|
||||
}
|
||||
}
|
||||
} else if (state == JackTransportStarting) {
|
||||
if (!device->m_sync) {
|
||||
/*start seeking */
|
||||
device->m_sync = 1;
|
||||
emit device->playbackSync(device->toVideoPosition(*pos));
|
||||
} else if (device->m_sync == 1) {
|
||||
/* sync position */
|
||||
if (device->m_currentPosition == device->toVideoPosition(*pos)) {
|
||||
device->m_sync = 2;
|
||||
} else {
|
||||
device->m_sync = 0;
|
||||
}
|
||||
}
|
||||
} else if (state == JackTransportRolling) {
|
||||
/* give up sync and just start playing */
|
||||
device->m_sync = 0;
|
||||
result = 1;
|
||||
}
|
||||
|
||||
if (device->m_sync > 1) {
|
||||
/* FIXME: optimize sync */
|
||||
#if 0
|
||||
for(int i = 0; i < device->m_channels; i++)
|
||||
jack_ringbuffer_reset(device->m_ringbuffers[i]);
|
||||
#endif
|
||||
device->m_sync = 0;
|
||||
result = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int JackDevice::jack_process(jack_nframes_t frames, void *data)
|
||||
{
|
||||
JackDevice* device = (JackDevice*)data;
|
||||
int channels = device->m_channels;
|
||||
|
||||
/* TODO: review later */
|
||||
#if 0
|
||||
if (device->m_sync) {
|
||||
size_t bufsize = (frames * sizeof(float));
|
||||
/* play silence while syncing */
|
||||
for(int i = 0; i < channels; i++)
|
||||
memset(jack_port_get_buffer(device->m_ports[i], frames), 0, bufsize);
|
||||
|
||||
/* FIXME: optimize sync */
|
||||
// if (device->m_sync > 1) {
|
||||
// for(int i = 0; i < device->m_channels; i++)
|
||||
// jack_ringbuffer_reset(device->m_ringbuffers[i]);
|
||||
// device->m_sync = 3;
|
||||
// }
|
||||
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* convert nr of frames to nr of bytes */
|
||||
size_t jacksize = (frames * sizeof(float));
|
||||
/* copy audio data into jack buffers */
|
||||
for (int i = 0; i < channels; i++) {
|
||||
size_t ringsize;
|
||||
char * buffer;
|
||||
|
||||
ringsize = jack_ringbuffer_read_space(device->m_ringbuffers[i]);
|
||||
buffer = (char*)jack_port_get_buffer(device->m_ports[i], frames);
|
||||
jack_ringbuffer_read(device->m_ringbuffers[i], buffer,
|
||||
ringsize < jacksize ? ringsize : jacksize );
|
||||
/* fill the rest with zeros to prevent noise */
|
||||
if(ringsize < jacksize)
|
||||
memset(buffer + ringsize, 0, jacksize - ringsize);
|
||||
}
|
||||
}
|
||||
|
||||
if(device->m_transportEnabled) {
|
||||
if(pthread_mutex_trylock(&(device->m_transportLock)) == 0) {
|
||||
if (device->m_state != jack_transport_query(device->m_client, NULL))
|
||||
pthread_cond_signal(&(device->m_transportCondition));
|
||||
pthread_mutex_unlock(&(device->m_transportLock));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void JackDevice::updateBuffers(Mlt::Frame& frame)
|
||||
{
|
||||
/* no job while syncing */
|
||||
if (m_sync) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!frame.is_valid() || frame.get_int("test_audio") != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mlt_audio_format format = mlt_audio_float;
|
||||
int freq = (int)m_frameRate;
|
||||
int samples = 0;
|
||||
|
||||
/* get the audio buffers */
|
||||
float *buffer = (float*)frame.get_audio(format, freq, m_channels, samples);
|
||||
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* process audio */
|
||||
size_t size = samples * sizeof(float);
|
||||
/* write into output ringbuffer */
|
||||
for (int i = 0; i < m_channels; i++)
|
||||
{
|
||||
if (jack_ringbuffer_write_space(m_ringbuffers[i]) >= size)
|
||||
jack_ringbuffer_write(m_ringbuffers[i], (char*)(buffer + i * samples), size);
|
||||
}
|
||||
}
|
||||
|
||||
void JackDevice::processLooping()
|
||||
{
|
||||
if (m_loopState == 1) {
|
||||
m_loopState = 2;
|
||||
} else if (m_loopState == 2) {
|
||||
if (m_currentPosition == m_loopOut ) {
|
||||
if (m_loopInfinite) {
|
||||
seekPlayback(m_loopIn, false);
|
||||
} else {
|
||||
stopPlayback();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_loopState = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void JackDevice::startPlayback(bool resetLoop)
|
||||
{
|
||||
if (m_valid) {
|
||||
/* reset looping state */
|
||||
if (resetLoop)
|
||||
resetLooping();
|
||||
/* start jack transport */
|
||||
jack_transport_start(m_client);
|
||||
m_nextState = JackTransportRolling;
|
||||
}
|
||||
}
|
||||
|
||||
void JackDevice::stopPlayback(bool resetLoop)
|
||||
{
|
||||
if (m_valid) {
|
||||
/* reset looping state */
|
||||
if (resetLoop)
|
||||
resetLooping();
|
||||
/* stop jack transport */
|
||||
jack_transport_stop(m_client);
|
||||
m_nextState = JackTransportStopped;
|
||||
}
|
||||
}
|
||||
|
||||
void JackDevice::seekPlayback(int time, bool resetLoop)
|
||||
{
|
||||
if (m_valid) {
|
||||
if(time >= 0) {
|
||||
/* reset looping state */
|
||||
if (resetLoop)
|
||||
resetLooping();
|
||||
/* locate jack transport */
|
||||
jack_transport_locate(m_client, toAudioPosition(time, m_frameRate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JackDevice::loopPlayback(int in, int out, bool infinite)
|
||||
{
|
||||
m_loopIn = in;
|
||||
m_loopOut = out;
|
||||
m_loopInfinite = infinite;
|
||||
|
||||
if (!doesPlayback())
|
||||
startPlayback(false);
|
||||
seekPlayback(m_loopIn, false);
|
||||
m_loopState = 1;
|
||||
}
|
||||
|
||||
void JackDevice::resetLooping()
|
||||
{
|
||||
m_loopIn = 0;
|
||||
m_loopOut = 0;
|
||||
m_loopInfinite = false;
|
||||
m_loopState = 0;
|
||||
}
|
||||
|
||||
int JackDevice::getPlaybackPosition()
|
||||
{
|
||||
jack_position_t position;
|
||||
jack_transport_query(m_client, &position);
|
||||
return toVideoPosition(position);
|
||||
}
|
||||
|
||||
void JackDevice::setCurrentPosition(int position)
|
||||
{
|
||||
m_currentPosition = position;
|
||||
emit currentPositionChanged(position);
|
||||
}
|
||||
|
||||
void JackDevice::monitorPlaybackSync(int position)
|
||||
{
|
||||
if (!m_playbackSyncMonEnabled)
|
||||
return;
|
||||
|
||||
/* get playback sync difference */
|
||||
int diff = position - getPlaybackPosition();
|
||||
if (m_playbackSyncDiff != diff) {
|
||||
/* save diff */
|
||||
m_playbackSyncDiff = diff;
|
||||
/* fire diff event */
|
||||
emit playbackSyncDiffChanged(diff);
|
||||
}
|
||||
|
||||
/* if action defined */
|
||||
if (m_playbackSyncMonAction != None) {
|
||||
bool flag = qAbs(diff) > m_playbackSyncDiffMaxValue;
|
||||
if (flag && m_nextState == JackTransportRolling) {
|
||||
if (m_playbackSyncMonAction == Stop) {
|
||||
stopPlayback();
|
||||
} else if (m_playbackSyncMonAction == Resync) {
|
||||
seekPlayback(getPlaybackPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JackDevice::setTransportEnabled(bool state)
|
||||
{
|
||||
m_transportEnabled = state;
|
||||
}
|
||||
|
||||
bool JackDevice::doesPlayback()
|
||||
{
|
||||
return (m_nextState != JackTransportStopped);
|
||||
}
|
||||
|
||||
void JackDevice::setPlaybackSyncMonEnabled(bool state)
|
||||
{
|
||||
m_playbackSyncMonEnabled = state;
|
||||
}
|
||||
|
||||
void JackDevice::setPlaybackSyncMonAction(SyncAction action)
|
||||
{
|
||||
m_playbackSyncMonAction = action;
|
||||
}
|
||||
|
||||
void JackDevice::setPlaybackSyncDiffMaxValue(int value)
|
||||
{
|
||||
m_playbackSyncDiffMaxValue = value;
|
||||
}
|
||||
|
||||
#include "jackdevice.moc"
|
||||
329
src/jackdevice.h
Normal file
329
src/jackdevice.h
Normal file
@@ -0,0 +1,329 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 by Ed Rogalsky (ed.rogalsky@gmail.com) *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* 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, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef JACKDEVICE_H_
|
||||
#define JACKDEVICE_H_
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
|
||||
#include <mlt++/Mlt.h>
|
||||
#include <QObject>
|
||||
#include <qstring.h>
|
||||
#include <KDebug>
|
||||
|
||||
|
||||
class JackDevice : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**
|
||||
* Singleton constructor.
|
||||
*/
|
||||
static JackDevice& singleton(Mlt::Profile * profile = 0);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~JackDevice();
|
||||
|
||||
/**
|
||||
* Sync action enum.
|
||||
*/
|
||||
enum SyncAction {
|
||||
None = 0,
|
||||
Stop,
|
||||
Resync
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the slave is still valid for processing.
|
||||
*/
|
||||
bool isValid();
|
||||
|
||||
/**
|
||||
* Open and init slave.
|
||||
*/
|
||||
void open(const QString &name, int channels, int buffersize);
|
||||
|
||||
/**
|
||||
* Close slave.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Set transport state.
|
||||
*/
|
||||
void setTransportEnabled(bool state);
|
||||
|
||||
/**
|
||||
* Return transport state.
|
||||
*/
|
||||
inline bool isTransportEnabled() {return m_transportEnabled;}
|
||||
|
||||
/**
|
||||
* Convert jack audio frame position to kdenlive video position.
|
||||
*/
|
||||
int toVideoPosition(const jack_position_t &position);
|
||||
|
||||
/**
|
||||
* Convert kdenlive video to jack audio frame position.
|
||||
*/
|
||||
jack_nframes_t toAudioPosition(const int &position, const jack_nframes_t &framerate);
|
||||
|
||||
/**
|
||||
* Checks if jackd is started.
|
||||
*/
|
||||
bool probe();
|
||||
|
||||
/**
|
||||
* Starts jack transport playback.
|
||||
*/
|
||||
void startPlayback(bool resetLoop = true);
|
||||
|
||||
/**
|
||||
* Stops jack transport playback.
|
||||
*/
|
||||
void stopPlayback(bool resetLoop = true);
|
||||
|
||||
/**
|
||||
* Seeks jack transport playback.
|
||||
* \param time The time to seek to.
|
||||
*/
|
||||
void seekPlayback(int time, bool resetLoop = true);
|
||||
|
||||
/**
|
||||
* Loop playback.
|
||||
* \param in The loop start time.
|
||||
* \param out The loop stop time.
|
||||
* \param infinite If true loop forever.
|
||||
*/
|
||||
void loopPlayback(int in, int out, bool infinite = true);
|
||||
|
||||
/**
|
||||
* Retrieves the jack transport playback time.
|
||||
* \return The current time position.
|
||||
*/
|
||||
int getPlaybackPosition();
|
||||
|
||||
/**
|
||||
* Returns whether jack transport plays back.
|
||||
* \return Whether jack transport plays back.
|
||||
*/
|
||||
bool doesPlayback();
|
||||
|
||||
/**
|
||||
* Set playback sync monitoring state.
|
||||
*/
|
||||
void setPlaybackSyncMonEnabled(bool state);
|
||||
|
||||
/**
|
||||
* Set playback sync monitoring action.
|
||||
*/
|
||||
void setPlaybackSyncMonAction(SyncAction action);
|
||||
|
||||
/**
|
||||
* Set playback sync diff max value.
|
||||
*/
|
||||
void setPlaybackSyncDiffMaxValue(int value);
|
||||
|
||||
/**
|
||||
* Updates the buffers.
|
||||
*/
|
||||
void updateBuffers(Mlt::Frame& frame);
|
||||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Standard constructor.
|
||||
*/
|
||||
JackDevice(Mlt::Profile * profile);
|
||||
|
||||
private:
|
||||
/**
|
||||
* The output ports of jack.
|
||||
*/
|
||||
jack_port_t** m_ports;
|
||||
|
||||
/**
|
||||
* The jack client.
|
||||
*/
|
||||
jack_client_t* m_client;
|
||||
|
||||
/**
|
||||
* The jack ring buffer.
|
||||
*/
|
||||
jack_ringbuffer_t** m_ringbuffers;
|
||||
|
||||
/**
|
||||
* Number of audio channels.
|
||||
*/
|
||||
int m_channels;
|
||||
|
||||
/**
|
||||
* Whether the device is valid.
|
||||
*/
|
||||
volatile bool m_valid;
|
||||
|
||||
/**
|
||||
* Whether the device is shutdown by jack.
|
||||
*/
|
||||
volatile bool m_shutdown;
|
||||
|
||||
/**
|
||||
* The transport synchronization thread.
|
||||
*/
|
||||
pthread_t m_transportThread;
|
||||
|
||||
/**
|
||||
* Mutex for transport.
|
||||
*/
|
||||
pthread_mutex_t m_transportLock;
|
||||
|
||||
/**
|
||||
* Condition for transport.
|
||||
*/
|
||||
pthread_cond_t m_transportCondition;
|
||||
|
||||
/**
|
||||
* Kdenlives current position.
|
||||
*/
|
||||
int m_currentPosition;
|
||||
|
||||
/**
|
||||
* Set transport status.
|
||||
*/
|
||||
volatile bool m_transportEnabled;
|
||||
|
||||
/**
|
||||
* Loop start position.
|
||||
*/
|
||||
int m_loopIn;
|
||||
|
||||
/**
|
||||
* Loop stop position.
|
||||
*/
|
||||
int m_loopOut;
|
||||
|
||||
/**
|
||||
* Flag infinite loop [true = infinite|false = single].
|
||||
*/
|
||||
bool m_loopInfinite;
|
||||
|
||||
/**
|
||||
* Loop state.
|
||||
*/
|
||||
int m_loopState;
|
||||
|
||||
/**
|
||||
* Next Jack Transport state (-1 if not expected to change).
|
||||
*/
|
||||
jack_transport_state_t m_nextState;
|
||||
|
||||
/**
|
||||
* Current jack transport status.
|
||||
*/
|
||||
jack_transport_state_t m_state;
|
||||
|
||||
/**
|
||||
* Jacks audio frame rate.
|
||||
*/
|
||||
jack_nframes_t m_frameRate;
|
||||
|
||||
/**
|
||||
* Syncronisation state.
|
||||
*/
|
||||
int m_sync;
|
||||
|
||||
/**
|
||||
* Playback sync difference.
|
||||
*/
|
||||
int m_playbackSyncDiff;
|
||||
|
||||
/**
|
||||
* Playback sync difference max value.
|
||||
*/
|
||||
int m_playbackSyncDiffMaxValue;
|
||||
|
||||
/**
|
||||
* Playback sync monitoring status.
|
||||
*/
|
||||
bool m_playbackSyncMonEnabled;
|
||||
|
||||
/**
|
||||
* Playback sync monitoring Action.
|
||||
*/
|
||||
SyncAction m_playbackSyncMonAction;
|
||||
|
||||
/**
|
||||
* Reset looping.
|
||||
*/
|
||||
void resetLooping();
|
||||
/**
|
||||
* Transport thread function.
|
||||
* \param device The this pointer.
|
||||
* \return NULL.
|
||||
*/
|
||||
static void* runTransportThread(void* device);
|
||||
|
||||
/**
|
||||
* Updates the transport state.
|
||||
*/
|
||||
void updateTransportState();
|
||||
|
||||
/**
|
||||
* Invalidates the jack device.
|
||||
* \param data The jack device that gets invalidet by jack.
|
||||
*/
|
||||
static void jack_shutdown(void *data);
|
||||
|
||||
/**
|
||||
* Mixes the next bytes into the buffer.
|
||||
* \param length The length in samples to be filled.
|
||||
* \param data A pointer to the jack device.
|
||||
* \return 0 what shows success.
|
||||
*/
|
||||
static int jack_process(jack_nframes_t length, void *data);
|
||||
|
||||
static int jack_sync(jack_transport_state_t state, jack_position_t* pos, void* data);
|
||||
|
||||
|
||||
/** ----------------------------- **/
|
||||
Mlt::Profile *m_mltProfile;
|
||||
|
||||
public slots:
|
||||
void setCurrentPosition(int position);
|
||||
|
||||
private slots:
|
||||
void processLooping();
|
||||
void monitorPlaybackSync(int position);
|
||||
|
||||
signals:
|
||||
void playbackStarted(int position);
|
||||
void playbackSync(int position);
|
||||
void playbackStopped(int position);
|
||||
void playbackSyncDiffChanged(int diff);
|
||||
void currentPositionChanged(int postition);
|
||||
void shutdown();
|
||||
};
|
||||
|
||||
#define JACKDEV JackDevice::singleton()
|
||||
|
||||
#endif /* JACKDEVICE_H */
|
||||
@@ -194,6 +194,16 @@
|
||||
<label>Automatically split audio and video.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
|
||||
<entry name="syncdiffmonaction" type="Int">
|
||||
<label>Playback sync diff monitor action.</label>
|
||||
<default>2</default>
|
||||
</entry>
|
||||
|
||||
<entry name="syncdiffmaxvalue" type="Int">
|
||||
<label>Playback sync diff max value.</label>
|
||||
<default>1</default>
|
||||
</entry>
|
||||
</group>
|
||||
|
||||
<group name="sdl">
|
||||
@@ -601,6 +611,16 @@
|
||||
<label>Snap movements to clips, guides and markers.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
|
||||
<entry name="jacktransport" type="Bool">
|
||||
<label>Enable jack transport in timeline.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
|
||||
<entry name="jacktransportmon" type="Bool">
|
||||
<label>Enable jack transport monitoring.</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
|
||||
<entry name="transitionfollowcursor" type="Bool">
|
||||
<label>When editing a composite transition, move timeline cursor for better preview.</label>
|
||||
|
||||
@@ -361,6 +361,7 @@ void KdenliveSettingsDialog::initDevices()
|
||||
m_configSdl.kcfg_audio_driver->addItem(i18n("OSS with DMA access"), "dma");
|
||||
m_configSdl.kcfg_audio_driver->addItem(i18n("Esound daemon"), "esd");
|
||||
m_configSdl.kcfg_audio_driver->addItem(i18n("ARTS daemon"), "artsc");
|
||||
m_configSdl.kcfg_audio_driver->addItem(i18n("JACK"), "jack");
|
||||
#endif
|
||||
|
||||
if (!KdenliveSettings::audiodrivername().isEmpty())
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
<Action name="edit_clip" />
|
||||
<Action name="delete_clip" />
|
||||
<Separator />
|
||||
<Action name="connect_jack" />
|
||||
<Action name="disconnect_jack" />
|
||||
<Separator />
|
||||
<Action name="project_clean" />
|
||||
<Action name="project_render" />
|
||||
<Action name="project_adjust_profile" />
|
||||
@@ -126,6 +129,7 @@
|
||||
<Action name="show_audio_thumbs" />
|
||||
<Action name="show_markers" />
|
||||
<Action name="snap" />
|
||||
<Action name="jack_transport" />
|
||||
<Separator />
|
||||
<Action name="zoom_in" />
|
||||
<Action name="zoom_out" />
|
||||
|
||||
@@ -66,6 +66,9 @@
|
||||
#include "databackup/backupwidget.h"
|
||||
#include "utils/resourcewidget.h"
|
||||
|
||||
#ifdef USE_JACK
|
||||
#include "jackdevice.h"
|
||||
#endif
|
||||
|
||||
#include <KApplication>
|
||||
#include <KAction>
|
||||
@@ -127,6 +130,10 @@
|
||||
|
||||
static const char version[] = VERSION;
|
||||
|
||||
#ifdef USE_JACK
|
||||
static const int STS_BAR_JACK_MON_ID = 0;
|
||||
#endif
|
||||
|
||||
namespace Mlt
|
||||
{
|
||||
class Producer;
|
||||
@@ -234,7 +241,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
|
||||
|
||||
m_clipMonitorDock = new QDockWidget(i18n("Clip Monitor"), this);
|
||||
m_clipMonitorDock->setObjectName("clip_monitor");
|
||||
m_clipMonitor = new Monitor(Kdenlive::clipMonitor, m_monitorManager, QString(), m_timelineArea);
|
||||
m_clipMonitor = new Monitor(Kdenlive::clipMonitor, m_monitorManager, Rndr::NoRole, QString(), m_timelineArea);
|
||||
m_clipMonitorDock->setWidget(m_clipMonitor);
|
||||
|
||||
// Connect the project list
|
||||
@@ -252,7 +259,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
|
||||
|
||||
m_projectMonitorDock = new QDockWidget(i18n("Project Monitor"), this);
|
||||
m_projectMonitorDock->setObjectName("project_monitor");
|
||||
m_projectMonitor = new Monitor(Kdenlive::projectMonitor, m_monitorManager, QString());
|
||||
m_projectMonitor = new Monitor(Kdenlive::projectMonitor, m_monitorManager, Rndr::OpenCloseJackEngineRole | Rndr::OpenCloseSlaveRole, QString());
|
||||
m_projectMonitorDock->setWidget(m_projectMonitor);
|
||||
|
||||
#ifndef Q_WS_MAC
|
||||
@@ -267,6 +274,23 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
|
||||
#endif /* ! Q_WS_MAC */
|
||||
m_monitorManager->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor);
|
||||
|
||||
#ifdef USE_JACK
|
||||
connect(m_monitorManager, SIGNAL(monitorStarted(AbstractMonitor&)),
|
||||
this, SLOT(slotEnableJackTransportButton(AbstractMonitor&)));
|
||||
connect(m_monitorManager, SIGNAL(monitorStopped(AbstractMonitor&)),
|
||||
this, SLOT(slotDisableJackTransportButton(AbstractMonitor&)));
|
||||
connect(m_monitorManager, SIGNAL(monitorStarted(AbstractMonitor&)),
|
||||
this, SLOT(slotEnableJackTransportMonButton(AbstractMonitor&)));
|
||||
connect(m_monitorManager, SIGNAL(monitorStopped(AbstractMonitor&)),
|
||||
this, SLOT(slotDisableJackTransportMonButton(AbstractMonitor&)));
|
||||
connect(&JACKDEV, SIGNAL(playbackSyncDiffChanged(int)),
|
||||
this, SLOT(slotUpdateJackSyncDiff(int)));
|
||||
connect(this, SIGNAL(jackTransportStateChanged(bool)),
|
||||
m_monitorManager, SLOT(slotOnJackTransportStateChanged(bool)));
|
||||
/* fire startup event for proper init */
|
||||
jackTransportStateChanged(KdenliveSettings::jacktransport());
|
||||
#endif
|
||||
|
||||
m_notesDock = new QDockWidget(i18n("Project Notes"), this);
|
||||
m_notesDock->setObjectName("notes_widget");
|
||||
m_notesWidget = new NotesWidget();
|
||||
@@ -574,6 +598,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString &
|
||||
|
||||
connect(m_projectMonitorDock, SIGNAL(visibilityChanged(bool)), m_projectMonitor, SLOT(refreshMonitor(bool)));
|
||||
connect(m_clipMonitorDock, SIGNAL(visibilityChanged(bool)), m_clipMonitor, SLOT(refreshMonitor(bool)));
|
||||
connect(m_recMonitorDock, SIGNAL(visibilityChanged(bool)), m_recMonitor, SLOT(refreshRecMonitor(bool)));
|
||||
connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
|
||||
connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
|
||||
|
||||
@@ -1175,6 +1200,24 @@ void MainWindow::setupActions()
|
||||
m_buttonSnap->setChecked(KdenliveSettings::snaptopoints());
|
||||
connect(m_buttonSnap, SIGNAL(triggered()), this, SLOT(slotSwitchSnap()));
|
||||
|
||||
#ifdef USE_JACK
|
||||
m_buttonJackTransport = new KAction(KIcon("kdenlive-toggle-jack"), i18n("Enable jack transport"), this);
|
||||
toolbar->addAction(m_buttonJackTransport);
|
||||
m_buttonJackTransport->setShortcut(Qt::SHIFT + Qt::Key_T);
|
||||
m_buttonJackTransport->setCheckable(true);
|
||||
m_buttonJackTransport->setChecked(KdenliveSettings::jacktransport());
|
||||
m_buttonJackTransport->setDisabled(true);
|
||||
connect(m_buttonJackTransport, SIGNAL(triggered()), this, SLOT(slotSwitchJackTransport()));
|
||||
|
||||
m_buttonJackTransportMon = new KAction(KIcon("kdenlive-toggle-jack-mon"), i18n("Enable jack transport monitoring"), this);
|
||||
toolbar->addAction(m_buttonJackTransportMon);
|
||||
m_buttonJackTransportMon->setShortcut(Qt::SHIFT + Qt::Key_E);
|
||||
m_buttonJackTransportMon->setCheckable(true);
|
||||
m_buttonJackTransportMon->setChecked(KdenliveSettings::jacktransportmon());
|
||||
m_buttonJackTransportMon->setDisabled(true);
|
||||
connect(m_buttonJackTransportMon, SIGNAL(triggered()), this, SLOT(slotSwitchJackTransportMon()));
|
||||
#endif
|
||||
|
||||
actionWidget = toolbar->widgetForAction(m_buttonAutomaticSplitAudio);
|
||||
actionWidget->setMaximumWidth(max);
|
||||
actionWidget->setMaximumHeight(max - 4);
|
||||
@@ -1195,6 +1238,16 @@ void MainWindow::setupActions()
|
||||
actionWidget->setMaximumWidth(max);
|
||||
actionWidget->setMaximumHeight(max - 4);
|
||||
|
||||
#ifdef USE_JACK
|
||||
actionWidget = toolbar->widgetForAction(m_buttonJackTransport);
|
||||
actionWidget->setMaximumWidth(max);
|
||||
actionWidget->setMaximumHeight(max - 4);
|
||||
|
||||
actionWidget = toolbar->widgetForAction(m_buttonJackTransportMon);
|
||||
actionWidget->setMaximumWidth(max);
|
||||
actionWidget->setMaximumHeight(max - 4);
|
||||
#endif
|
||||
|
||||
m_messageLabel = new StatusBarMessageLabel(this);
|
||||
m_messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding);
|
||||
|
||||
@@ -1202,6 +1255,10 @@ void MainWindow::setupActions()
|
||||
statusBar()->addWidget(m_statusProgressBar, 0);
|
||||
statusBar()->addPermanentWidget(toolbar);
|
||||
|
||||
#ifdef USE_JACK
|
||||
statusBar()->insertPermanentFixedItem(" 0", STS_BAR_JACK_MON_ID);
|
||||
#endif
|
||||
|
||||
m_timeFormatButton = new KSelectAction("00:00:00:00 / 00:00:00:00", this);
|
||||
m_timeFormatButton->addAction(i18n("hh:mm:ss:ff"));
|
||||
m_timeFormatButton->addAction(i18n("Frames"));
|
||||
@@ -1229,6 +1286,9 @@ void MainWindow::setupActions()
|
||||
collection.addAction("show_audio_thumbs", m_buttonAudioThumbs);
|
||||
collection.addAction("show_markers", m_buttonShowMarkers);
|
||||
collection.addAction("snap", m_buttonSnap);
|
||||
#ifdef USE_JACK
|
||||
collection.addAction("jack_transport", m_buttonJackTransport);
|
||||
#endif
|
||||
collection.addAction("zoom_fit", m_buttonFitZoom);
|
||||
collection.addAction("zoom_in", m_zoomIn);
|
||||
collection.addAction("zoom_out", m_zoomOut);
|
||||
@@ -1370,6 +1430,18 @@ void MainWindow::setupActions()
|
||||
insertTimeline->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_I);
|
||||
connect(insertTimeline, SIGNAL(triggered(bool)), this, SLOT(slotInsertZoneToTimeline()));
|
||||
|
||||
#ifdef USE_JACK
|
||||
KAction *connectJack = collection.addAction("connect_jack");
|
||||
connectJack->setText(i18n("Jack connect"));
|
||||
connectJack->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_A);
|
||||
connect(connectJack, SIGNAL(triggered(bool)), this, SLOT(slotConnectJack()));
|
||||
|
||||
KAction *disconnectJack = collection.addAction("disconnect_jack");
|
||||
disconnectJack->setText(i18n("Jack disconnect"));
|
||||
disconnectJack->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_D);
|
||||
connect(disconnectJack, SIGNAL(triggered(bool)), this, SLOT(slotDisconnectJack()));
|
||||
#endif
|
||||
|
||||
KAction *resizeStart = new KAction(KIcon(), i18n("Resize Item Start"), this);
|
||||
collection.addAction("resize_timeline_clip_start", resizeStart);
|
||||
resizeStart->setShortcut(Qt::Key_1);
|
||||
@@ -2554,6 +2626,16 @@ void MainWindow::slotUpdateProjectDuration(int pos)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotUpdateJackSyncDiff(int diff)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
statusBar()->changeItem(QString::number(diff), STS_BAR_JACK_MON_ID);
|
||||
#else
|
||||
/* prevent warning */
|
||||
Q_UNUSED(diff)
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::slotUpdateDocumentState(bool modified)
|
||||
{
|
||||
if (!m_activeDocument) return;
|
||||
@@ -2827,6 +2909,11 @@ void MainWindow::updateConfiguration()
|
||||
if (m_activeDocument)
|
||||
m_activeDocument->clipManager()->checkAudioThumbs();
|
||||
}
|
||||
|
||||
if (m_monitorManager) {
|
||||
m_monitorManager->updateConfiguration();
|
||||
}
|
||||
|
||||
m_buttonAudioThumbs->setChecked(KdenliveSettings::audiothumbnails());
|
||||
m_buttonVideoThumbs->setChecked(KdenliveSettings::videothumbnails());
|
||||
m_buttonShowMarkers->setChecked(KdenliveSettings::showmarkers());
|
||||
@@ -2882,6 +2969,110 @@ void MainWindow::slotSwitchSnap()
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::slotSwitchJackTransport()
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
AbstractRender* abstrRender = m_monitorManager->activeRenderer();
|
||||
bool jacktransport = KdenliveSettings::jacktransport();
|
||||
|
||||
if (abstrRender) {
|
||||
if(abstrRender->hasRole(Rndr::OpenCloseSlaveRole) &&
|
||||
abstrRender->isAudioEngineActive(AudioEngine::Jack) && !jacktransport) {
|
||||
KdenliveSettings::setJacktransport(true);
|
||||
abstrRender->enableSlave(Slave::Jack);
|
||||
} else {
|
||||
KdenliveSettings::setJacktransport(false);
|
||||
abstrRender->enableSlave(Slave::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
m_buttonJackTransport->setChecked(KdenliveSettings::jacktransport());
|
||||
/* fire event on state change */
|
||||
jackTransportStateChanged(KdenliveSettings::jacktransport());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::slotSwitchJackTransportMon()
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
AbstractRender* abstrRender = m_monitorManager->activeRenderer();
|
||||
bool monEnabled = KdenliveSettings::jacktransportmon();
|
||||
|
||||
if (abstrRender) {
|
||||
if(abstrRender->hasRole(Rndr::OpenCloseSlaveRole) && !monEnabled) {
|
||||
KdenliveSettings::setJacktransportmon(true);
|
||||
abstrRender->setPlaybackSyncMonEnabled(true);
|
||||
} else {
|
||||
KdenliveSettings::setJacktransportmon(false);
|
||||
abstrRender->setPlaybackSyncMonEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_buttonJackTransportMon->setChecked(KdenliveSettings::jacktransportmon());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::slotEnableJackTransportButton(AbstractMonitor& monitor)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
AbstractRender* abstrRender = monitor.abstractRender();
|
||||
if (abstrRender) {
|
||||
if(abstrRender->hasRole(Rndr::OpenCloseSlaveRole) &&
|
||||
abstrRender->isAudioEngineActive(AudioEngine::Jack)) {
|
||||
/* if jack transport enabled slave to jack */
|
||||
if (KdenliveSettings::jacktransport()) {
|
||||
abstrRender->enableSlave(Slave::Jack);
|
||||
}
|
||||
/* enable toggle button */
|
||||
m_buttonJackTransport->setDisabled(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::slotDisableJackTransportButton(AbstractMonitor& monitor)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
AbstractRender* abstrRender = monitor.abstractRender();
|
||||
if (abstrRender) {
|
||||
if (abstrRender->isSlaveActive(Slave::Jack)) {
|
||||
/* disable jack slave */
|
||||
abstrRender->enableSlave(Slave::Internal);
|
||||
}
|
||||
/* disable toggle button */
|
||||
m_buttonJackTransport->setDisabled(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::slotEnableJackTransportMonButton(AbstractMonitor& monitor)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
AbstractRender* abstrRender = monitor.abstractRender();
|
||||
if (abstrRender) {
|
||||
if(abstrRender->hasRole(Rndr::OpenCloseSlaveRole) &&
|
||||
abstrRender->isAudioEngineActive(AudioEngine::Jack)) {
|
||||
/* enable toggle button */
|
||||
m_buttonJackTransportMon->setDisabled(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::slotDisableJackTransportMonButton(AbstractMonitor& monitor)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
AbstractRender* abstrRender = monitor.abstractRender();
|
||||
if (abstrRender) {
|
||||
/* disable toggle button */
|
||||
m_buttonJackTransportMon->setDisabled(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::slotDeleteItem()
|
||||
{
|
||||
if (QApplication::focusWidget() &&
|
||||
@@ -4690,6 +4881,23 @@ void MainWindow::slotSaveTimelineClip()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotConnectJack()
|
||||
{
|
||||
m_monitorManager->slotOpenAudioEngine(AudioEngine::Jack);
|
||||
AbstractRender* abstrRender = m_monitorManager->activeRenderer();
|
||||
|
||||
if (abstrRender && abstrRender->hasRole(Rndr::OpenCloseSlaveRole)) {
|
||||
if(abstrRender->isAudioEngineActive(AudioEngine::Jack)) {
|
||||
abstrRender->enableSlave(Slave::Jack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::slotDisconnectJack()
|
||||
{
|
||||
m_monitorManager->slotCloseAudioEngine(AudioEngine::Jack);
|
||||
}
|
||||
|
||||
void MainWindow::slotProcessImportKeyframes(GRAPHICSRECTITEM type, const QString& data, int maximum)
|
||||
{
|
||||
if (type == AVWIDGET) {
|
||||
|
||||
@@ -253,6 +253,10 @@ private:
|
||||
KAction *m_buttonRazorTool;
|
||||
KAction *m_buttonSpacerTool;
|
||||
KAction *m_buttonSnap;
|
||||
#ifdef USE_JACK
|
||||
KAction *m_buttonJackTransport;
|
||||
KAction *m_buttonJackTransportMon;
|
||||
#endif
|
||||
KAction *m_saveAction;
|
||||
KAction *m_closeAction;
|
||||
QSlider *m_zoomSlider;
|
||||
@@ -360,6 +364,7 @@ private slots:
|
||||
void slotConnectMonitors();
|
||||
void slotUpdateClip(const QString &id);
|
||||
void slotUpdateMousePosition(int pos);
|
||||
void slotUpdateJackSyncDiff(int diff);
|
||||
void slotUpdateProjectDuration(int pos);
|
||||
void slotAddEffect(const QDomElement &effect);
|
||||
void slotEditProfiles();
|
||||
@@ -571,8 +576,27 @@ private slots:
|
||||
/** @brief Set MLT's consumer interpolation method */
|
||||
void slotSetInterpolation(int ix);
|
||||
|
||||
/* TODO: @eddrog find solution - moc has a problem with #ifdef in slots */
|
||||
/** @brief Connect slave to jackd */
|
||||
void slotConnectJack();
|
||||
/** @brief Disconnect slave from jackd */
|
||||
void slotDisconnectJack();
|
||||
/** @brief Switch jack transport enabled state */
|
||||
void slotSwitchJackTransport();
|
||||
/** @brief Switch jack transport monitoring enabled state */
|
||||
void slotSwitchJackTransportMon();
|
||||
/** @brief Enable jack transport button */
|
||||
void slotEnableJackTransportButton(AbstractMonitor& monitor);
|
||||
/** @brief Disable jack transport button */
|
||||
void slotDisableJackTransportButton(AbstractMonitor& monitor);
|
||||
/** @brief Enable jack transport monitoring button */
|
||||
void slotEnableJackTransportMonButton(AbstractMonitor& monitor);
|
||||
/** @brief Disable jack transport monitoring button */
|
||||
void slotDisableJackTransportMonButton(AbstractMonitor& monitor);
|
||||
|
||||
signals:
|
||||
Q_SCRIPTABLE void abortRenderJob(const QString &url);
|
||||
void jackTransportStateChanged(bool enabled);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ static void rec_consumer_frame_preview(mlt_consumer, MltDeviceCapture * self, ml
|
||||
|
||||
|
||||
MltDeviceCapture::MltDeviceCapture(QString profile, VideoSurface *surface, QWidget *parent) :
|
||||
AbstractRender(Kdenlive::recordMonitor, parent),
|
||||
AbstractRender(Kdenlive::recordMonitor, Rndr::NoRole, parent),
|
||||
doCapture(0),
|
||||
sendFrameForAnalysis(false),
|
||||
processingImage(false),
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#define SEEK_INACTIVE (-1)
|
||||
|
||||
|
||||
Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profile, QWidget *parent) :
|
||||
Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, RndrRole role, QString profile, QWidget *parent) :
|
||||
AbstractMonitor(id, manager, parent)
|
||||
, render(NULL)
|
||||
, m_currentClip(NULL)
|
||||
@@ -91,7 +91,7 @@ Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profil
|
||||
m_toolbar->addAction(KIcon("kdenlive-zone-end"), i18n("Set zone end"), this, SLOT(slotSetZoneEnd()));
|
||||
}
|
||||
|
||||
m_toolbar->addAction(KIcon("media-seek-backward"), i18n("Rewind"), this, SLOT(slotRewind()));
|
||||
m_rewindAction = m_toolbar->addAction(KIcon("media-seek-backward"), i18n("Rewind"), this, SLOT(slotRewind()));
|
||||
//m_toolbar->addAction(KIcon("media-skip-backward"), i18n("Rewind 1 frame"), this, SLOT(slotRewindOneFrame()));
|
||||
|
||||
QToolButton *playButton = new QToolButton(m_toolbar);
|
||||
@@ -105,7 +105,7 @@ Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profil
|
||||
m_toolbar->addWidget(playButton);
|
||||
|
||||
//m_toolbar->addAction(KIcon("media-skip-forward"), i18n("Forward 1 frame"), this, SLOT(slotForwardOneFrame()));
|
||||
m_toolbar->addAction(KIcon("media-seek-forward"), i18n("Forward"), this, SLOT(slotForward()));
|
||||
m_forwardAction = m_toolbar->addAction(KIcon("media-seek-forward"), i18n("Forward"), this, SLOT(slotForward()));
|
||||
|
||||
playButton->setDefaultAction(m_playAction);
|
||||
|
||||
@@ -154,17 +154,17 @@ Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profil
|
||||
|
||||
bool monitorCreated = false;
|
||||
#ifdef Q_WS_MAC
|
||||
createOpenGlWidget(videoBox, profile);
|
||||
createOpenGlWidget(videoBox, profile, role);
|
||||
monitorCreated = true;
|
||||
//m_glWidget->setFixedSize(width, height);
|
||||
#elif defined(USE_OPENGL)
|
||||
if (KdenliveSettings::openglmonitors()) {
|
||||
monitorCreated = createOpenGlWidget(videoBox, profile);
|
||||
monitorCreated = createOpenGlWidget(videoBox, profile, role);
|
||||
}
|
||||
#endif
|
||||
if (!monitorCreated) {
|
||||
createVideoSurface();
|
||||
render = new Render(m_id, (int) videoSurface->winId(), profile, this);
|
||||
render = new Render(m_id, (int) videoSurface->winId(), role, profile, this);
|
||||
connect(videoSurface, SIGNAL(refreshMonitor()), render, SLOT(doRefresh()));
|
||||
}
|
||||
#ifdef USE_OPENGL
|
||||
@@ -184,6 +184,7 @@ Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profil
|
||||
connect(m_audioSlider, SIGNAL(valueChanged(int)), this, SLOT(slotSetVolume(int)));
|
||||
connect(render, SIGNAL(durationChanged(int)), this, SLOT(adjustRulerSize(int)));
|
||||
connect(render, SIGNAL(rendererStopped(int)), this, SLOT(rendererStopped(int)));
|
||||
connect(render, SIGNAL(rendererStarted()), this, SLOT(rendererStarted()));
|
||||
connect(render, SIGNAL(rendererPosition(int)), this, SLOT(seekCursor(int)));
|
||||
|
||||
if (id != Kdenlive::clipMonitor) {
|
||||
@@ -230,9 +231,9 @@ QWidget *Monitor::container()
|
||||
}
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
bool Monitor::createOpenGlWidget(QWidget *parent, const QString &profile)
|
||||
bool Monitor::createOpenGlWidget(QWidget *parent, const QString &profile, RndrRole role)
|
||||
{
|
||||
render = new Render(id(), 0, profile, this);
|
||||
render = new Render(id(), 0, role, profile, this);
|
||||
m_glWidget = new VideoGLWidget(parent);
|
||||
if (m_glWidget == NULL) {
|
||||
// Creation failed, we are in trouble...
|
||||
@@ -483,6 +484,22 @@ void Monitor::slotSwitchFullScreen()
|
||||
videoBox->switchFullScreen();
|
||||
}
|
||||
|
||||
void Monitor::slotOnJackTransportStateChanged(bool enabled)
|
||||
{
|
||||
/* no action if not connected to jack */
|
||||
if (!render->isAudioEngineActive(AudioEngine::Jack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
m_forwardAction->setDisabled(true);
|
||||
m_rewindAction->setDisabled(true);
|
||||
} else {
|
||||
m_forwardAction->setDisabled(false);
|
||||
m_rewindAction->setDisabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Monitor::mouseReleaseEvent(QMouseEvent * event)
|
||||
{
|
||||
@@ -693,6 +710,12 @@ void Monitor::slotZoneEnd()
|
||||
|
||||
void Monitor::slotRewind(double speed)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
/* in jack mode only speed 1 and forward direction is possible */
|
||||
if (render->isSlaveActive(Slave::Jack)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
slotActivateMonitor();
|
||||
if (speed == 0) {
|
||||
double currentspeed = render->playSpeed();
|
||||
@@ -717,7 +740,14 @@ void Monitor::slotRewind(double speed)
|
||||
|
||||
void Monitor::slotForward(double speed)
|
||||
{
|
||||
slotActivateMonitor();
|
||||
#ifdef USE_JACK
|
||||
/* in jack mode only speed 1 and forward direction is possible */
|
||||
if (render->isSlaveActive(Slave::Jack)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
slotActivateMonitor();
|
||||
if (speed == 0) {
|
||||
double currentspeed = render->playSpeed();
|
||||
if (currentspeed <= 0) render->play(1);
|
||||
@@ -772,6 +802,12 @@ void Monitor::rendererStopped(int pos)
|
||||
m_playAction->setIcon(m_playIcon);
|
||||
}
|
||||
|
||||
void Monitor::rendererStarted()
|
||||
{
|
||||
m_playAction->setIcon(m_pauseIcon);
|
||||
}
|
||||
|
||||
|
||||
void Monitor::adjustRulerSize(int length)
|
||||
{
|
||||
if (length > 0) m_length = length;
|
||||
@@ -784,7 +820,11 @@ void Monitor::adjustRulerSize(int length)
|
||||
|
||||
void Monitor::stop()
|
||||
{
|
||||
if (render) render->stop();
|
||||
if (render) {
|
||||
render->stop();
|
||||
}
|
||||
/* on stopping monitor reset icon */
|
||||
m_playAction->setIcon(m_playIcon);
|
||||
}
|
||||
|
||||
void Monitor::start()
|
||||
|
||||
@@ -68,7 +68,7 @@ class Monitor : public AbstractMonitor
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profile = QString(), QWidget *parent = 0);
|
||||
Monitor(Kdenlive::MONITORID id, MonitorManager *manager, RndrRole role, QString profile = QString(), QWidget *parent = 0);
|
||||
~Monitor();
|
||||
Render *render;
|
||||
AbstractRender *abstractRender();
|
||||
@@ -130,6 +130,8 @@ private:
|
||||
KIcon m_pauseIcon;
|
||||
TimecodeDisplay *m_timePos;
|
||||
QAction *m_playAction;
|
||||
QAction *m_rewindAction;
|
||||
QAction *m_forwardAction;
|
||||
/** Has to be available so we can enable and disable it. */
|
||||
QAction *m_loopClipAction;
|
||||
QMenu *m_contextMenu;
|
||||
@@ -146,7 +148,7 @@ private:
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
VideoGLWidget *m_glWidget;
|
||||
bool createOpenGlWidget(QWidget *parent, const QString &profile);
|
||||
bool createOpenGlWidget(QWidget *parent, const QString &profile, RndrRole role);
|
||||
#endif
|
||||
|
||||
GenTime getSnapForPos(bool previous);
|
||||
@@ -159,6 +161,7 @@ private:
|
||||
private slots:
|
||||
void seekCursor(int pos);
|
||||
void rendererStopped(int pos);
|
||||
void rendererStarted();
|
||||
void slotExtractCurrentFrame();
|
||||
void slotSetThumbFrame();
|
||||
void slotSetSizeOneToOne();
|
||||
@@ -215,6 +218,7 @@ public slots:
|
||||
void slotSetSelectedClip(Transition *item);
|
||||
void slotMouseSeek(int eventDelta, bool fast);
|
||||
void slotSwitchFullScreen();
|
||||
void slotOnJackTransportStateChanged(bool enabled);
|
||||
|
||||
signals:
|
||||
void renderPosition(int);
|
||||
|
||||
@@ -102,15 +102,19 @@ bool MonitorManager::activateMonitor(Kdenlive::MONITORID name, bool forceRefresh
|
||||
for (int i = 0; i < m_monitorsList.count(); ++i) {
|
||||
if (m_monitorsList.at(i)->id() == name) {
|
||||
m_activeMonitor = m_monitorsList.at(i);
|
||||
} else {
|
||||
m_monitorsList.at(i)->stop();
|
||||
/* fire monitor stopped event */
|
||||
emit monitorStopped(*m_monitorsList.at(i));
|
||||
}
|
||||
else m_monitorsList.at(i)->stop();
|
||||
}
|
||||
if (m_activeMonitor) {
|
||||
m_activeMonitor->blockSignals(true);
|
||||
m_activeMonitor->parentWidget()->raise();
|
||||
m_activeMonitor->blockSignals(false);
|
||||
m_activeMonitor->blockSignals(false);
|
||||
m_activeMonitor->start();
|
||||
|
||||
/* fire monitor started event */
|
||||
emit monitorStarted(*m_activeMonitor);
|
||||
}
|
||||
emit checkColorScopes();
|
||||
return (m_activeMonitor != NULL);
|
||||
@@ -281,5 +285,30 @@ QString MonitorManager::getProjectFolder() const
|
||||
return m_document->projectFolder().path(KUrl::AddTrailingSlash);
|
||||
}
|
||||
|
||||
void MonitorManager::slotOpenAudioEngine(AudioEngine::Type engine)
|
||||
{
|
||||
m_projectMonitor->render->openAudioEngine(engine);
|
||||
}
|
||||
|
||||
void MonitorManager::slotCloseAudioEngine(AudioEngine::Type engine)
|
||||
{
|
||||
m_projectMonitor->render->closeAudioEngine(engine);
|
||||
}
|
||||
|
||||
void MonitorManager::slotOnJackTransportStateChanged(bool enabled)
|
||||
{
|
||||
m_projectMonitor->slotOnJackTransportStateChanged(enabled);
|
||||
}
|
||||
|
||||
void MonitorManager::updateConfiguration()
|
||||
{
|
||||
AbstractRender *abstrRender = NULL;
|
||||
|
||||
for (int i = 0; i < m_monitorsList.size(); i++) {
|
||||
abstrRender = m_monitorsList[i]->abstractRender();
|
||||
if (abstrRender)
|
||||
abstrRender->updateConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
#include "monitormanager.moc"
|
||||
|
||||
@@ -52,6 +52,8 @@ public:
|
||||
void setDocument(KdenliveDoc *doc);
|
||||
/** @brief Change an MLT consumer property for both monitors. */
|
||||
void setConsumerProperty(const QString &name, const QString &value);
|
||||
/** @brief Update configuration for available monitors. */
|
||||
void updateConfiguration();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -82,6 +84,13 @@ public slots:
|
||||
void slotSwitchMonitors(bool activateClip);
|
||||
void slotUpdateAudioMonitoring();
|
||||
|
||||
/** @brief Open defined audio engine */
|
||||
void slotOpenAudioEngine(AudioEngine::Type engine);
|
||||
/** @brief Close defined audio engine */
|
||||
void slotCloseAudioEngine(AudioEngine::Type engine);
|
||||
/** @brief Action handler for JackTransportStateChanged event */
|
||||
void slotOnJackTransportStateChanged(bool enabled);
|
||||
|
||||
private slots:
|
||||
void slotRefreshCurrentMonitor(const QString &id);
|
||||
|
||||
@@ -99,6 +108,11 @@ signals:
|
||||
/** @brief When the active monitor renderer was deleted, reset color scopes */
|
||||
void clearScopes();
|
||||
|
||||
/** @brief Inform if monitor is started */
|
||||
void monitorStarted(AbstractMonitor & mon);
|
||||
|
||||
/** @brief Inform if monitor is stopped */
|
||||
void monitorStopped(AbstractMonitor & mon);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1000,7 +1000,7 @@ void RecMonitor::refreshRecMonitor(bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
//if (!m_isActive) activateMonitor();
|
||||
|
||||
slotActivateMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class QPaintEvent;
|
||||
class QResizeEvent;
|
||||
class QMouseEvent;
|
||||
|
||||
class RegionGrabber : public QWidget
|
||||
class RegionGrabber : public QObject, public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
310
src/renderer.cpp
310
src/renderer.cpp
@@ -26,10 +26,15 @@
|
||||
#include "renderer.h"
|
||||
#include "kdenlivesettings.h"
|
||||
#include "kthumb.h"
|
||||
#include "kapplication.h"
|
||||
#include "definitions.h"
|
||||
#include "slideshowclip.h"
|
||||
#include "profilesdialog.h"
|
||||
|
||||
#ifdef USE_JACK
|
||||
#include "jackdevice.h"
|
||||
#endif
|
||||
|
||||
#include <mlt++/Mlt.h>
|
||||
|
||||
#include <KDebug>
|
||||
@@ -51,6 +56,17 @@
|
||||
|
||||
#define SEEK_INACTIVE (-1)
|
||||
|
||||
#include <QThread>
|
||||
// Can't believe I need to do this to sleep.
|
||||
class SleepThread : QThread
|
||||
{
|
||||
public:
|
||||
virtual void run() {};
|
||||
static void msleep(unsigned long msecs) {
|
||||
QThread::msleep(msecs);
|
||||
}
|
||||
};
|
||||
|
||||
static void kdenlive_callback(void* /*ptr*/, int level, const char* fmt, va_list vl)
|
||||
{
|
||||
if (level > MLT_LOG_ERROR) return;
|
||||
@@ -71,6 +87,13 @@ void Render::consumer_frame_show(mlt_consumer, Render * self, mlt_frame frame_pt
|
||||
if (self->sendFrameForAnalysis && frame_ptr->convert_image) {
|
||||
self->emitFrameUpdated(frame);
|
||||
}
|
||||
|
||||
#ifdef USE_JACK
|
||||
if (self->isAudioEngineActive(AudioEngine::Jack)) {
|
||||
JACKDEV.updateBuffers(frame);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self->analyseAudio) {
|
||||
self->showAudio(frame);
|
||||
}
|
||||
@@ -102,17 +125,25 @@ void Render::consumer_gl_frame_show(mlt_consumer consumer, Render * self, mlt_fr
|
||||
emit self->rendererPosition((int) mlt_consumer_position(consumer));
|
||||
return;
|
||||
}
|
||||
|
||||
Mlt::Frame frame(frame_ptr);
|
||||
if (frame.get_double("_speed") == 0) self->emitConsumerStopped();
|
||||
else if (frame.get_double("_speed") < 0.0 && mlt_frame_get_position(frame_ptr) <= 0) {
|
||||
self->pause();
|
||||
self->emitConsumerStopped(true);
|
||||
}
|
||||
|
||||
#ifdef USE_JACK
|
||||
if (self->isAudioEngineActive(AudioEngine::Jack)) {
|
||||
JACKDEV.updateBuffers(frame);
|
||||
}
|
||||
#endif
|
||||
|
||||
emit self->mltFrameReceived(new Mlt::Frame(frame_ptr));
|
||||
}
|
||||
|
||||
Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWidget *parent) :
|
||||
AbstractRender(rendererName, parent),
|
||||
Render::Render(Kdenlive::MONITORID rendererName, int winid, RndrRole role, QString profile, QWidget *parent) :
|
||||
AbstractRender(rendererName, role, parent),
|
||||
requestedSeekPosition(SEEK_INACTIVE),
|
||||
showFrameSemaphore(1),
|
||||
externalConsumer(false),
|
||||
@@ -135,9 +166,11 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi
|
||||
if (profile.isEmpty())
|
||||
profile = KdenliveSettings::current_profile();
|
||||
buildConsumer(profile);
|
||||
|
||||
m_mltProducer = m_blackClip->cut(0, 1);
|
||||
m_mltConsumer->connect(*m_mltProducer);
|
||||
m_mltProducer->set_speed(0.0);
|
||||
|
||||
m_refreshTimer.setSingleShot(true);
|
||||
m_refreshTimer.setInterval(100);
|
||||
connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
|
||||
@@ -148,7 +181,13 @@ Render::Render(Kdenlive::MONITORID rendererName, int winid, QString profile, QWi
|
||||
|
||||
Render::~Render()
|
||||
{
|
||||
closeMlt();
|
||||
#ifdef USE_JACK
|
||||
/* isDeviceActive ()*/
|
||||
if (hasRole(Rndr::OpenCloseJackEngineRole)) {
|
||||
closeAudioEngine(AudioEngine::Jack);
|
||||
}
|
||||
#endif
|
||||
closeMlt();
|
||||
delete m_mltProfile;
|
||||
}
|
||||
|
||||
@@ -324,6 +363,30 @@ void Render::buildConsumer(const QString &profileName)
|
||||
|
||||
m_mltConsumer->set("frequency", 48000);
|
||||
m_mltConsumer->set("real_time", KdenliveSettings::mltthreads());
|
||||
|
||||
#ifdef USE_JACK
|
||||
/* create the jack device singleton instance */
|
||||
JackDevice::singleton(m_mltProfile);
|
||||
bool jackdStarted = &JACKDEV && JACKDEV.probe();
|
||||
bool rolesValid = hasRole(Rndr::OpenCloseJackEngineRole);
|
||||
|
||||
if (jackdStarted && rolesValid) {
|
||||
/* open jack audio engine */
|
||||
openAudioEngine(AudioEngine::Jack);
|
||||
} else if (!jackdStarted && rolesValid) {
|
||||
/* show error message in explicite jack mode */
|
||||
if (!audioDriver.isEmpty() && audioDriver == "jack") {
|
||||
KMessageBox::error(qApp->activeWindow(),
|
||||
i18n("Can't connect to jackd. Please start jackd and restart kdenlive!"));
|
||||
}
|
||||
} else if (jackdStarted && !rolesValid) {
|
||||
/* stop consumer */
|
||||
if (!m_mltConsumer->is_stopped())
|
||||
m_mltConsumer->stop();
|
||||
/* disable audio */
|
||||
m_mltConsumer->set("audio_off", 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Mlt::Producer *Render::invalidProducer(const QString &id)
|
||||
@@ -424,11 +487,25 @@ void Render::seek(const GenTime &time)
|
||||
seek(pos);
|
||||
}
|
||||
|
||||
void Render::seek(int time)
|
||||
void Render::seek(int time, bool fromMaster)
|
||||
{
|
||||
/* limit time */
|
||||
time = qMax(0, time);
|
||||
time = qMin(m_mltProducer->get_playtime(), time);
|
||||
|
||||
#ifdef USE_JACK
|
||||
if (!fromMaster && isSlaveActive(Slave::Jack)) {
|
||||
if (isAudioEngineActive(AudioEngine::Jack)) {
|
||||
JACKDEV.seekPlayback(time < 0 ? 0 : time);
|
||||
}
|
||||
/* return */
|
||||
return;
|
||||
} else if (fromMaster && isSlaveActive(Slave::Jack)) {
|
||||
// m_mltProducer->set_speed(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
resetZoneMode();
|
||||
time = qMax(0, time);
|
||||
time = qMin(m_mltProducer->get_playtime(), time);
|
||||
if (requestedSeekPosition == SEEK_INACTIVE) {
|
||||
requestedSeekPosition = time;
|
||||
m_mltConsumer->purge();
|
||||
@@ -1624,12 +1701,31 @@ void Render::setActiveMonitor()
|
||||
if (!m_isActive) emit activateMonitor(m_name);
|
||||
}
|
||||
|
||||
void Render::switchPlay(bool play)
|
||||
void Render::switchPlay(bool play, bool fromMaster)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
requestedSeekPosition = SEEK_INACTIVE;
|
||||
if (!m_mltProducer || !m_mltConsumer || !m_isActive)
|
||||
return;
|
||||
|
||||
#ifdef USE_JACK
|
||||
if (!fromMaster && isSlaveActive(Slave::Jack)) {
|
||||
if (isAudioEngineActive(AudioEngine::Jack)) {
|
||||
if (play) {
|
||||
JACKDEV.startPlayback();
|
||||
} else {
|
||||
JACKDEV.stopPlayback();
|
||||
}
|
||||
}
|
||||
/* return */
|
||||
return;
|
||||
} else if (fromMaster && isSlaveActive(Slave::Jack)) {
|
||||
// position = JACKDEV.getPlaybackPosition();
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(fromMaster)
|
||||
#endif
|
||||
|
||||
if (m_isZoneMode) resetZoneMode();
|
||||
if (play && m_paused) {
|
||||
if (m_name == Kdenlive::clipMonitor && m_mltConsumer->position() == m_mltProducer->get_out()) m_mltProducer->seek(0);
|
||||
@@ -1699,6 +1795,21 @@ void Render::playZone(const GenTime & startTime, const GenTime & stopTime)
|
||||
requestedSeekPosition = SEEK_INACTIVE;
|
||||
if (!m_mltProducer || !m_mltConsumer || !m_isActive)
|
||||
return;
|
||||
|
||||
#ifdef USE_JACK
|
||||
if (isSlaveActive(Slave::Jack) && isAudioEngineActive(AudioEngine::Jack)) {
|
||||
/* calc loop in/out */
|
||||
int loopIn = (int)(startTime.frames(m_fps));
|
||||
int loopOut = (int)(stopTime.frames(m_fps));
|
||||
/* internal processing compat */
|
||||
m_isZoneMode = true;
|
||||
/* start looping */
|
||||
JACKDEV.loopPlayback(loopIn, loopOut, m_isLoopMode);
|
||||
/* return */
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_mltProducer->set("out", (int)(stopTime.frames(m_fps)));
|
||||
m_mltProducer->seek((int)(startTime.frames(m_fps)));
|
||||
m_paused = false;
|
||||
@@ -1850,6 +1961,15 @@ void Render::emitConsumerStopped(bool forcePause)
|
||||
{
|
||||
// This is used to know when the playing stopped
|
||||
if (m_mltProducer && (forcePause || (!m_paused && m_mltProducer->get_speed() == 0))) {
|
||||
|
||||
#ifdef USE_JACK
|
||||
if (isSlaveActive(Slave::Jack) && isAudioEngineActive(AudioEngine::Jack)) {
|
||||
if (m_mltConsumer->position() == m_mltProducer->get_out()) {
|
||||
JACKDEV.stopPlayback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
double pos = m_mltProducer->position();
|
||||
m_paused = true;
|
||||
if (m_isLoopMode) play(m_loopStart);
|
||||
@@ -4819,5 +4939,181 @@ bool Render::checkX11Grab()
|
||||
return result.contains("x11grab");
|
||||
}
|
||||
|
||||
void Render::openAudioEngine(AudioEngine::Type engine)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
if (engine == AudioEngine::Jack) {
|
||||
/* stop consumer */
|
||||
if (!m_mltConsumer->is_stopped())
|
||||
m_mltConsumer->stop();
|
||||
/* disable audio */
|
||||
m_mltConsumer->set("audio_off", 1);
|
||||
/* connect to jackd and open device */
|
||||
if (&JACKDEV && !JACKDEV.isValid()) {
|
||||
JACKDEV.open("kdenlive", 2, 204800 * 6);
|
||||
/* connect shutdown event handler */
|
||||
connect(&JACKDEV, SIGNAL(shutdown()),
|
||||
this, SLOT(slotOnDeviceShutdown()));
|
||||
|
||||
/* set sync diff monitoring action */
|
||||
JackDevice::SyncAction syncAction =
|
||||
(JackDevice::SyncAction)KdenliveSettings::syncdiffmonaction();
|
||||
JACKDEV.setPlaybackSyncMonAction(syncAction);
|
||||
/* set sync diff max value */
|
||||
int syncDiffMax = KdenliveSettings::syncdiffmaxvalue();
|
||||
JACKDEV.setPlaybackSyncDiffMaxValue(syncDiffMax);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* prevent warnings */
|
||||
engine = engine;
|
||||
}
|
||||
}
|
||||
|
||||
void Render::closeAudioEngine(AudioEngine::Type engine)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
if (engine == AudioEngine::Jack) {
|
||||
if (isAudioEngineActive(AudioEngine::Jack)) {
|
||||
/* close jack slave */
|
||||
enableSlave(Slave::Internal);
|
||||
/* disconnect shutdown event handler */
|
||||
disconnect(&JACKDEV, SIGNAL(shutdown()),
|
||||
this, SLOT(slotOnDeviceShutdown()));
|
||||
/* disconnect from jackd and close device */
|
||||
JACKDEV.close();
|
||||
/* TODO: on jack client shutdown event => close dev */
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* prevent warning */
|
||||
engine = engine;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Render::enableSlave(Slave::Type slave)
|
||||
{
|
||||
/* if slave is equal return */
|
||||
if (isSlaveActive(slave))
|
||||
return;
|
||||
|
||||
#ifdef USE_JACK
|
||||
/* close current slave */
|
||||
if (isSlaveActive(Slave::Jack)) {
|
||||
if (&JACKDEV && JACKDEV.isTransportEnabled()) {
|
||||
/* disable transport */
|
||||
JACKDEV.setTransportEnabled(false);
|
||||
/* disconnect transport callbacks */
|
||||
disconnect(&JACKDEV, SIGNAL(playbackStarted(int)),
|
||||
this, SLOT(slotOnSlavePlaybackStarted(int)));
|
||||
disconnect(&JACKDEV, SIGNAL(playbackSync(int)),
|
||||
this, SLOT(slotOnSlavePlaybackSync(int)));
|
||||
disconnect(&JACKDEV, SIGNAL(playbackStopped(int)),
|
||||
this, SLOT(slotOnSlavePlaybackStopped(int)));
|
||||
disconnect(this, SIGNAL(rendererPosition(int)),
|
||||
&JACKDEV, SLOT(setCurrentPosition(int)));
|
||||
|
||||
/* stop jack playback */
|
||||
JACKDEV.stopPlayback();
|
||||
/* stop mlt playback and relocate to stopped jack pos */
|
||||
if (m_mltProducer->get_speed() != 0)
|
||||
slotOnSlavePlaybackStopped(JACKDEV.getPlaybackPosition());
|
||||
/* DEBUG */
|
||||
kDebug() << "// JACK Slave disabled";
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* DEBUG */
|
||||
kDebug() << "// INTERNAL Slave disabled";
|
||||
}
|
||||
|
||||
#ifdef USE_JACK
|
||||
if ((slave == Slave::Jack) && hasRole(Rndr::OpenCloseSlaveRole)
|
||||
&& isAudioEngineActive(AudioEngine::Jack)) {
|
||||
/* connect transport callbacks */
|
||||
connect(&JACKDEV, SIGNAL(playbackStarted(int)),
|
||||
this, SLOT(slotOnSlavePlaybackStarted(int)));
|
||||
connect(&JACKDEV, SIGNAL(playbackSync(int)),
|
||||
this, SLOT(slotOnSlavePlaybackSync(int)));
|
||||
connect(&JACKDEV, SIGNAL(playbackStopped(int)),
|
||||
this, SLOT(slotOnSlavePlaybackStopped(int)));
|
||||
connect(this, SIGNAL(rendererPosition(int)),
|
||||
&JACKDEV, SLOT(setCurrentPosition(int)));
|
||||
|
||||
/* enable transport */
|
||||
JACKDEV.setTransportEnabled(true);
|
||||
/* stop playback and relocate */
|
||||
JACKDEV.stopPlayback();
|
||||
JACKDEV.seekPlayback(seekFramePosition());
|
||||
/* change slave */
|
||||
m_activeSlave = slave;
|
||||
/* DEBUG */
|
||||
kDebug() << "// JACK Slave enabled";
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* default to INTERNAL */
|
||||
m_activeSlave = Slave::Internal;
|
||||
/* DEBUG */
|
||||
kDebug() << "// INTERNAL Slave enabled";
|
||||
}
|
||||
}
|
||||
|
||||
void Render::setPlaybackSyncMonEnabled(bool state)
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
if (isAudioEngineActive(AudioEngine::Jack)) {
|
||||
JACKDEV.setPlaybackSyncMonEnabled(state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Render::updateConfiguration()
|
||||
{
|
||||
#ifdef USE_JACK
|
||||
if (isAudioEngineActive(AudioEngine::Jack)) {
|
||||
/* set sync diff monitoring action */
|
||||
JackDevice::SyncAction syncAction =
|
||||
(JackDevice::SyncAction)KdenliveSettings::syncdiffmonaction();
|
||||
JACKDEV.setPlaybackSyncMonAction(syncAction);
|
||||
/* set sync diff max value */
|
||||
int syncDiffMax = KdenliveSettings::syncdiffmaxvalue();
|
||||
JACKDEV.setPlaybackSyncDiffMaxValue(syncDiffMax);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Render::slotOnSlavePlaybackStarted(int position)
|
||||
{
|
||||
position = position;
|
||||
switchPlay(true, true);
|
||||
rendererStarted();
|
||||
}
|
||||
|
||||
void Render::slotOnSlavePlaybackSync(int position)
|
||||
{
|
||||
if (m_mltProducer->get_speed() != 0)
|
||||
switchPlay(false, true);
|
||||
seek(position, true);
|
||||
m_paused = true;
|
||||
}
|
||||
|
||||
void Render::slotOnSlavePlaybackStopped(int position)
|
||||
{
|
||||
switchPlay(false, true);
|
||||
seek(position, true);
|
||||
m_paused = true;
|
||||
emit rendererStopped(position);
|
||||
}
|
||||
|
||||
void Render::slotOnDeviceShutdown()
|
||||
{
|
||||
closeAudioEngine(AudioEngine::Jack);
|
||||
}
|
||||
|
||||
#include "renderer.moc"
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
#include <QSemaphore>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
class QTimer;
|
||||
class QPixmap;
|
||||
|
||||
class KComboBox;
|
||||
@@ -105,20 +107,36 @@ class Render: public AbstractRender
|
||||
enum FailStates { OK = 0,
|
||||
APP_NOEXIST
|
||||
};
|
||||
/** @brief Build a MLT Renderer
|
||||
|
||||
/** @brief Build a MLT Renderer
|
||||
* @param rendererName A unique identifier for this renderer
|
||||
* @param winid The parent widget identifier (required for SDL display). Set to 0 for OpenGL rendering
|
||||
* @param profile The MLT profile used for the renderer (default one will be used if empty). */
|
||||
Render(Kdenlive::MONITORID rendererName, int winid, QString profile = QString(), QWidget *parent = 0);
|
||||
Render(Kdenlive::MONITORID rendererName, int winid, RndrRole role, QString profile = QString(), QWidget *parent = 0);
|
||||
|
||||
/** @brief Destroy the MLT Renderer. */
|
||||
virtual ~Render();
|
||||
|
||||
/** @brief Seeks the renderer clip to the given time. */
|
||||
void seek(const GenTime &time);
|
||||
void seek(int time);
|
||||
void seek(int time, bool fromMaster = false);
|
||||
void seekToFrameDiff(int diff);
|
||||
|
||||
/** @brief Open appropriate audio engine */
|
||||
void openAudioEngine(AudioEngine::Type engine);
|
||||
|
||||
/** @brief Close appropriate audio engine */
|
||||
void closeAudioEngine(AudioEngine::Type engine);
|
||||
|
||||
/** @brief Enable appropriate transport slave */
|
||||
void enableSlave(Slave::Type slave);
|
||||
|
||||
/** @brief Set playback sync monitoring state */
|
||||
void setPlaybackSyncMonEnabled(bool state);
|
||||
|
||||
/** @brief Update configuration */
|
||||
void updateConfiguration();
|
||||
|
||||
QPixmap getImageThumbnail(const KUrl &url, int width, int height);
|
||||
|
||||
/** @brief Sets the current MLT producer playlist.
|
||||
@@ -146,7 +164,7 @@ class Render: public AbstractRender
|
||||
* The speed is relative to normal playback, e.g. 1.0 is normal speed, 0.0
|
||||
* is paused, -1.0 means play backwards. It does not specify start/stop */
|
||||
void play(double speed);
|
||||
void switchPlay(bool play);
|
||||
void switchPlay(bool play, bool fromMaster = false);
|
||||
void pause();
|
||||
|
||||
/** @brief Stops playing.
|
||||
@@ -459,6 +477,7 @@ signals:
|
||||
void durationChanged(int);
|
||||
void rendererPosition(int);
|
||||
void rendererStopped(int);
|
||||
void rendererStarted();
|
||||
/** @brief The clip is not valid, should be removed from project. */
|
||||
void removeInvalidClip(const QString &, bool replaceProducer);
|
||||
/** @brief The proxy is not valid, should be deleted.
|
||||
@@ -504,6 +523,15 @@ public slots:
|
||||
void seekToFrame(int pos);
|
||||
/** @brief Starts a timer to query for a refresh. */
|
||||
void doRefresh();
|
||||
|
||||
/** @brief Slave playback started event handler */
|
||||
void slotOnSlavePlaybackStarted(int position);
|
||||
/** @brief Slave playback sync event handler */
|
||||
void slotOnSlavePlaybackSync(int position);
|
||||
/** @brief Slave playback stopped event handler */
|
||||
void slotOnSlavePlaybackStopped(int position);
|
||||
/** @brief Audio device shutdown event handler */
|
||||
void slotOnDeviceShutdown();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>361</width>
|
||||
<height>222</height>
|
||||
<width>386</width>
|
||||
<height>252</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -92,6 +92,83 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Playback sync diff max. value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="kcfg_syncdiffmaxvalue">
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Playback sync diff monitoring action</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="kcfg_syncdiffmonaction">
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>none</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>stop</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>resync</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
|
||||
Reference in New Issue
Block a user