Merge remote-tracking branch 'upstream/master' into pull_requests

This commit is contained in:
Jonathan Noble
2019-01-25 22:42:56 +00:00
49 changed files with 1382 additions and 427 deletions
+15 -4
View File
@@ -3,29 +3,40 @@ compiler: gcc
sudo: require
dist: trusty
matrix:
include:
- env: ARCH=x86_64 APPIMGTOOL=appimagetool-x86_64.AppImage
- env: ARCH=i386 APPIMGTOOL=appimagetool-i686.AppImage
before_install:
- sudo add-apt-repository ppa:beineri/opt-qt593-trusty -y
- sudo sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y
- sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y
- sudo apt-get update -qq
install:
- sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins
- if [ "$ARCH" == "x86_64" ]; then sudo apt-get -y install qt59base qt59multimedia libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev frei0r-plugins-dev frei0r-plugins fuse; fi
- if [ "$ARCH" == "i386" ]; then sudo apt-get -y install gcc-multilib g++-multilib qt59base:i386 qt59multimedia:i386 libavformat-dev:i386 libavcodec-dev:i386 libavfilter-dev:i386 libavutil-dev:i386 libswscale-dev:i386 libswresample-dev:i386 frei0r-plugins-dev:i386 frei0r-plugins:i386 pkg-config:i386 libgl1-mesa-dev:i386 fuse:i386; fi
- source /opt/qt*/bin/qt*-env.sh
script:
- qmake CONFIG+=release PREFIX=/usr
- if [ "$ARCH" == "x86_64" ]; then qmake CONFIG+=release PREFIX=/usr; fi
- if [ "$ARCH" == "i386" ]; then qmake CONFIG+=release "QMAKE_CFLAGS+=-m32" "QMAKE_CXXFLAGS+=-m32" "QMAKE_LFLAGS+=-m32" PREFIX=/usr -spec linux-g++-32; fi
- make -j$(nproc)
- make INSTALL_ROOT=appdir -j$(nproc) install ; find appdir/
- mkdir -p appdir/usr/bin/ ; cp olive-editor appdir/usr/bin/ # FIXME; "make install" should do this
- wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
- chmod a+x linuxdeployqt-continuous-x86_64.AppImage
- wget -c -nv "https://github.com/AppImage/AppImageKit/releases/download/continuous/$APPIMGTOOL"
- chmod a+x $APPIMGTOOL
- unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH
- export VERSION=$(git rev-parse --short HEAD) # linuxdeployqt uses this for naming the file
- ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -appimage
- rm Olive*.AppImage
- ./$APPIMGTOOL 'appdir' -n -g
after_success:
- find appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq
- # curl --upload-file Olive*.AppImage https://transfer.sh/Olive-git.$(git rev-parse --short HEAD)-x86_64.AppImage
- # curl --upload-file Olive*.AppImage https://transfer.sh/Olive-git.$(git rev-parse --short HEAD)-$ARCH.AppImage
- wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh
# only create release for master
- if [ "$TRAVIS_BRANCH" != "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi
+5 -5
View File
@@ -37,31 +37,31 @@ void debug_message_handler(QtMsgType type, const QMessageLogContext &context, co
case QtDebugMsg:
fprintf(stderr, "[DEBUG] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
if (debug_file.isOpen()) debug_stream << QString("[DEBUG] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
debug_info.prepend(QString("<b>[DEBUG]</b> %1 (%2:%3, %4)<br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
debug_info.append(QString("<b>[DEBUG]</b> %1 (%2:%3, %4)<br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
fflush(stderr);
break;
case QtInfoMsg:
fprintf(stderr, "[INFO] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
if (debug_file.isOpen()) debug_stream << QString("[INFO] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
debug_info.prepend(QString("<b>[INFO]</b> %1 (%2:%3, %4)<br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
debug_info.append(QString("<b>[INFO]</b> %1 (%2:%3, %4)<br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
fflush(stderr);
break;
case QtWarningMsg:
fprintf(stderr, "[WARNING] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
if (debug_file.isOpen()) debug_stream << QString("[WARNING] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
debug_info.prepend(QString("<font color='yellow'><b>[WARNING]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
debug_info.append(QString("<font color='yellow'><b>[WARNING]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
fflush(stderr);
break;
case QtCriticalMsg:
fprintf(stderr, "[ERROR] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
if (debug_file.isOpen()) debug_stream << QString("[ERROR] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
debug_info.prepend(QString("<font color='red'><b>[ERROR]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
debug_info.append(QString("<font color='red'><b>[ERROR]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
fflush(stderr);
break;
case QtFatalMsg:
fprintf(stderr, "[FATAL] %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
if (debug_file.isOpen()) debug_stream << QString("[FATAL] %1 (%2:%3, %4)\n").arg(localMsg.constData(), context.file, QString::number(context.line), context.function);
debug_info.prepend(QString("<font color='red'><b>[FATAL]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
debug_info.append(QString("<font color='red'><b>[FATAL]</b> %1 (%2:%3, %4)</font><br>").arg(localMsg.constData(), context.file, QString::number(context.line), context.function));
fflush(stderr);
// abort();
}
+2
View File
@@ -2,6 +2,7 @@
#include <QTextEdit>
#include <QVBoxLayout>
#include <QScrollBar>
#include "debug.h"
@@ -20,6 +21,7 @@ DebugDialog::DebugDialog(QWidget *parent) : QDialog(parent) {
void DebugDialog::update_log() {
textEdit->setHtml(get_debug_str());
textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum());
}
void DebugDialog::showEvent(QShowEvent *) {
+90 -16
View File
@@ -1,6 +1,6 @@
#include "vsthost.h"
#ifdef _WIN32
#ifndef NOVST
// adapted from http://teragonaudio.com/article/How-to-make-your-own-VST-host.html
@@ -9,18 +9,33 @@
#include <QMessageBox>
#include <QFile>
#include <QXmlStreamWriter>
#include <QWindow>
#include "playback/audio.h"
#include "mainwindow.h"
#include "debug.h"
#ifdef __linux__
#include <X11/X.h>
#endif
#define BLOCK_SIZE 512
#define CHANNEL_COUNT 2
struct VSTRect {
int16_t top;
int16_t left;
int16_t bottom;
int16_t right;
};
#define effGetChunk 23
#define effSetChunk 24
// C callbacks
extern "C" {
// Main host callback
VstIntPtr VSTCALLBACK hostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) {
intptr_t hostCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) {
switch(opcode) {
case audioMasterVersion:
return 2400;
@@ -29,7 +44,7 @@ extern "C" {
break;
case audioMasterGetCurrentProcessLevel:
return 0;
// Handle other opcodes here... there will be lots of them
// handle other opcodes here... there will be lots of them
case audioMasterEndEdit: // change made
mainWindow->setWindowModified(true);
break;
@@ -44,33 +59,70 @@ extern "C" {
// Plugin's entry point
typedef AEffect *(*vstPluginFuncPtr)(audioMasterCallback host);
// Plugin's getParameter() method
typedef float (*getParameterFuncPtr)(AEffect *effect, VstInt32 index);
typedef float (*getParameterFuncPtr)(AEffect *effect, int32_t index);
// Plugin's setParameter() method
typedef void (*setParameterFuncPtr)(AEffect *effect, VstInt32 index, float value);
typedef void (*setParameterFuncPtr)(AEffect *effect, int32_t index, float value);
// Plugin's processEvents() method
typedef VstInt32 (*processEventsFuncPtr)(VstEvents *events);
typedef int32_t (*processEventsFuncPtr)(VstEvents *events);
// Plugin's process() method
typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, VstInt32 sampleFrames);
typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, int32_t sampleFrames);
void VSTHost::loadPlugin() {
QString dll_fn = file_field->get_filename(0, true);
#if defined(__APPLE__)
bundle = BundleLoad(dll_fn);
if (bundle == NULL) {
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), tr("Failed to create VST reference"));
return;
}
vstPluginFuncPtr mainEntryPoint = NULL;
mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("VSTPluginMain"));
// VST plugins previous to the 2.4 SDK used main_macho for the entry point name
if(mainEntryPoint == NULL) {
mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("main_macho"));
}
if(mainEntryPoint == NULL) {
qCritical() << "Couldn't get a pointer to VST plugin's main()";
BundleClose(bundle);
return;
}
plugin = mainEntryPoint(hostCallback);
if(plugin == NULL) {
qCritical() << "Plugin's main() returns null";
BundleClose(bundle);
return;
}
#else
modulePtr = LibLoad(dll_fn);
if(modulePtr == nullptr) {
QString dll_error;
#ifdef _WIN32
DWORD dll_err = GetLastError();
qCritical() << "Failed to load VST" << dll_fn << "-" << dll_err;
dll_error = QString::number(dll_err);
#elif __linux__
dll_error = dlerror();
#endif
qCritical() << "Failed to load VST plugin" << dll_fn << "-" << dll_error;
QString msg_err = tr("Failed to load VST plugin \"%1\": %2").arg(dll_fn, dll_error);
#ifdef _WIN32
QString msg_err = tr("Failed to load VST plugin \"%1\": %2").arg(dll_fn, QString::number(dll_err));
if (dll_err == 193) {
#ifdef _WIN64
msg_err += "\n\n" + tr("NOTE: You can't load 32-bit VST plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive.");
#elif _WIN32
msg_err += "\n\n" + tr("NOTE: You can't load 64-bit VST plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive.");
#endif
#endif
}
QMessageBox::critical(mainWindow, tr("Error loading VST plugin"), msg_err);
#endif
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), msg_err);
return;
}
@@ -78,12 +130,18 @@ void VSTHost::loadPlugin() {
reinterpret_cast<vstPluginFuncPtr>(LibAddress(modulePtr, "VSTPluginMain"));
// Instantiate the plugin
plugin = mainEntryPoint(hostCallback);
#endif
}
void VSTHost::freePlugin() {
if (plugin != nullptr) {
stopPlugin();
#if defined(__APPLE__)
CFBundleUnloadExecutable(bundle);
CFRelease(bundle);
#else
LibClose(modulePtr);
#endif
plugin = nullptr;
}
}
@@ -225,7 +283,7 @@ void VSTHost::custom_load(QXmlStreamReader &stream) {
stream.readNext();
QByteArray b = QByteArray::fromBase64(stream.text().toUtf8());
if (plugin != nullptr) {
dispatcher(plugin, effSetChunk, 0, VstInt32(b.size()), static_cast<void*>(b.data()), 0);
dispatcher(plugin, effSetChunk, 0, int32_t(b.size()), static_cast<void*>(b.data()), 0);
}
}
}
@@ -234,14 +292,26 @@ void VSTHost::save(QXmlStreamWriter &stream) {
Effect::save(stream);
if (plugin != nullptr) {
char* p = nullptr;
VstInt32 length = VstInt32(dispatcher(plugin, effGetChunk, 0, 0, &p, 0));
int32_t length = int32_t(dispatcher(plugin, effGetChunk, 0, 0, &p, 0));
QByteArray b(p, length);
stream.writeTextElement("plugindata", b.toBase64());
}
}
void VSTHost::show_interface(bool show) {
dialog->setVisible(show);
dialog->setVisible(show);
if (show) {
#if defined(_WIN32)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<HWND>(dialog->windowHandle()->winId()), 0);
#elif defined(__APPLE__)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<NSWindow*>(dialog->windowHandle()->winId()), 0);
#elif defined(__linux__)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<void*>(dialog->windowHandle()->winId()), 0);
#endif
} else {
dispatcher(plugin, effEditClose, 0, 0, nullptr, 0);
}
}
void VSTHost::uncheck_show_button() {
@@ -254,13 +324,17 @@ void VSTHost::change_plugin() {
if (plugin != nullptr) {
if (configurePluginCallbacks()) {
startPlugin();
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<HWND>(dialog->winId()), 0);
ERect* eRect = nullptr;
VSTRect* eRect = nullptr;
plugin->dispatcher(plugin, effEditGetRect, 0, 0, &eRect, 0);
dialog->setFixedWidth(eRect->right);
dialog->setFixedHeight(eRect->bottom);
} else {
#ifdef __APPLE__
CFBundleUnloadExecutable(bundle);
CFRelease(bundle);
#else
LibClose(modulePtr);
#endif
plugin = nullptr;
}
}
+7 -4
View File
@@ -1,18 +1,17 @@
#ifndef VSTHOSTWIN_H
#define VSTHOSTWIN_H
#ifdef _WIN32
#ifndef NOVST
#include "project/effect.h"
#include "io/crossplatformlib.h"
#include <vst/aeffectx.h>
#include "include/vestige.h"
// Plugin's dispatcher function
typedef VstIntPtr (*dispatcherFuncPtr)(AEffect *effect, VstInt32 opCode, VstInt32 index, VstInt32 value, void *ptr, float opt);
typedef intptr_t (*dispatcherFuncPtr)(AEffect *effect, int32_t opCode, int32_t index, int32_t value, void *ptr, float opt);
struct AEffect;
class QDialog;
class VSTHost : public Effect {
@@ -46,7 +45,11 @@ private:
float** outputs;
QDialog* dialog;
QPushButton* show_interface_btn;
#if defined(__APPLE__)
CFBundleRef bundle;
#else
ModulePtr modulePtr;
#endif
};
#endif
+43
View File
@@ -0,0 +1,43 @@
#version 110
varying vec2 vTexCoord;
uniform sampler2D tex0; // scene buffer
uniform float scale;
uniform float centerx;
uniform float centery;
uniform bool mirrorx;
uniform bool mirrory;
void main(void) {
vec2 texCoord = vTexCoord;
/*vec2 coord = vec2(
mod(vTexCoord.x*tilecount, 1.0),
mod(vTexCoord.y*tilecount, 1.0)
);
if (mirrorx && mod(vTexCoord.x*tilecount, 2.0) > 1.0) {
coord.x = 1.0 - coord.x;
}
if (mirrory && mod(vTexCoord.y*tilecount, 2.0) > 1.0) {
coord.y = 1.0 - coord.y;
}*/
float adj_scale = scale*0.01;
vec2 scaled_coords = (vTexCoord/adj_scale);
vec2 coord = scaled_coords-vec2(0.5/adj_scale, 0.5/adj_scale)+vec2(0.5, 0.5)+vec2(-centerx*0.01, -centery*0.01);
vec2 modcoord = mod(coord, 1.0);
if (mirrorx && mod(coord.x, 2.0) > 1.0) {
modcoord.x = 1.0 - modcoord.x;
}
if (mirrory && mod(coord.y, 2.0) > 1.0) {
modcoord.y = 1.0 - modcoord.y;
}
gl_FragColor = vec4(texture2D(tex0, modcoord));
}
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<effect name="Tile" category="Distort">
<row name="Scale">
<field type="double" min="0" default="50" id="scale"/>
</row>
<row name="Center">
<field type="double" default="0" id="centerx"/>
<field type="double" default="0" id="centery"/>
</row>
<row name="Mirror X">
<field type="bool" default="0" id="mirrorx"/>
</row>
<row name="Mirror Y">
<field type="bool" default="0" id="mirrory"/>
</row>
<shader vert="common.vert" frag="tile.frag"/>
</effect>
+342
View File
@@ -0,0 +1,342 @@
/*
* vestige.h - simple header to allow VeSTige compilation and eventually work
*
* Copyright (c) 2006 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* This VeSTige header is included in this package in the good-faith
* belief that it has been cleanly and legally reverse engineered
* without reference to the official VST SDK and without its
* developer(s) having agreed to the VST SDK license agreement.
*/
#include <stdint.h>
#ifndef _VESTIGE_H
#define _VESTIGE_H
#define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \
( ( (int) b ) << 16 ) | \
( ( (int) c ) << 8 ) | \
( ( (int) d ) << 0 ) )
#define audioMasterAutomate 0
#define audioMasterVersion 1
#define audioMasterCurrentId 2
#define audioMasterIdle 3
#define audioMasterPinConnected 4
// unsupported? 5
#define audioMasterWantMidi 6
#define audioMasterGetTime 7
#define audioMasterProcessEvents 8
#define audioMasterSetTime 9
#define audioMasterTempoAt 10
#define audioMasterGetNumAutomatableParameters 11
#define audioMasterGetParameterQuantization 12
#define audioMasterIOChanged 13
#define audioMasterNeedIdle 14
#define audioMasterSizeWindow 15
#define audioMasterGetSampleRate 16
#define audioMasterGetBlockSize 17
#define audioMasterGetInputLatency 18
#define audioMasterGetOutputLatency 19
#define audioMasterGetPreviousPlug 20
#define audioMasterGetNextPlug 21
#define audioMasterWillReplaceOrAccumulate 22
#define audioMasterGetCurrentProcessLevel 23
#define audioMasterGetAutomationState 24
#define audioMasterOfflineStart 25
#define audioMasterOfflineRead 26
#define audioMasterOfflineWrite 27
#define audioMasterOfflineGetCurrentPass 28
#define audioMasterOfflineGetCurrentMetaPass 29
#define audioMasterSetOutputSampleRate 30
// unsupported? 31
#define audioMasterGetSpeakerArrangement 31 // deprecated in 2.4?
#define audioMasterGetVendorString 32
#define audioMasterGetProductString 33
#define audioMasterGetVendorVersion 34
#define audioMasterVendorSpecific 35
#define audioMasterSetIcon 36
#define audioMasterCanDo 37
#define audioMasterGetLanguage 38
#define audioMasterOpenWindow 39
#define audioMasterCloseWindow 40
#define audioMasterGetDirectory 41
#define audioMasterUpdateDisplay 42
#define audioMasterBeginEdit 43 //BeginGesture
#define audioMasterEndEdit 44 //EndGesture
#define audioMasterOpenFileSelector 45
#define audioMasterCloseFileSelector 46 // currently unused
#define audioMasterEditFile 47 // currently unused
#define audioMasterGetChunkFile 48 // currently unused
#define audioMasterGetInputSpeakerArrangement 49 // currently unused
#define effFlagsHasEditor 1
#define effFlagsCanReplacing (1 << 4) // very likely
#define effFlagsIsSynth (1 << 8) // currently unused
#define effOpen 0
#define effClose 1 // currently unused
#define effSetProgram 2 // currently unused
#define effGetProgram 3 // currently unused
#define effGetProgramName 5 // currently unused
#define effGetParamName 8 // currently unused
#define effSetSampleRate 10
#define effSetBlockSize 11
#define effMainsChanged 12
#define effEditGetRect 13
#define effEditOpen 14
#define effEditClose 15
#define effEditIdle 19
#define effEditTop 20
#define effProcessEvents 25
// the next one from http://asseca.com/vst-24-specs/index.html
#define effGetPlugCategory 35
#define effGetEffectName 45
#define effGetVendorString 47
#define effGetProductString 48
#define effGetVendorVersion 49
#define effCanDo 51 // currently unused
/* from http://asseca.com/vst-24-specs/efIdle.html */
#define effIdle 53
/* from http://asseca.com/vst-24-specs/efGetParameterProperties.html */
#define effGetParameterProperties 56
#define effGetVstVersion 58 // currently unused
/* http://asseca.com/vst-24-specs/efShellGetNextPlugin.html */
#define effShellGetNextPlugin 70
/* The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0 */
#define effStartProcess 71
#define effStopProcess 72
#define effBeginSetProgram 67
#define effEndSetProgram 68
#ifdef WORDS_BIGENDIAN
// "VstP"
#define kEffectMagic 0x50747356
#else
// "PtsV"
#define kEffectMagic 0x56737450
#endif
#define kVstLangEnglish 1
#define kVstMidiType 1
struct RemoteVstPlugin;
#define kVstTransportChanged 1
#define kVstTransportPlaying (1 << 1)
#define kVstTransportCycleActive (1 << 2)
#define kVstTransportRecording (1 << 3)
#define kVstAutomationWriting (1 << 6)
#define kVstAutomationReading (1 << 7)
#define kVstNanosValid (1 << 8)
#define kVstPpqPosValid (1 << 9)
#define kVstTempoValid (1 << 10)
#define kVstBarsValid (1 << 11)
#define kVstCyclePosValid (1 << 12)
#define kVstTimeSigValid (1 << 13)
#define kVstSmpteValid (1 << 14)
#define kVstClockValid (1 << 15)
struct _VstMidiEvent
{
// 00
int type;
// 04
int byteSize;
// 08
int deltaSamples;
// 0c?
int flags;
// 10?
int noteLength;
// 14?
int noteOffset;
// 18
char midiData[4];
// 1c?
char detune;
// 1d?
char noteOffVelocity;
// 1e?
char reserved1;
// 1f?
char reserved2;
};
typedef struct _VstMidiEvent VstMidiEvent;
struct _VstEvent
{
char dump[sizeof (VstMidiEvent)];
};
typedef struct _VstEvent VstEvent;
struct _VstEvents
{
// 00
int numEvents;
// 04
void *reserved;
// 08
VstEvent * events[];
};
/* constants from http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?t=3740&sid=183f74631fee71a493316735e2b9f28b */
enum Vestige2StringConstants
{
VestigeMaxNameLen = 64,
VestigeMaxLabelLen = 128,
VestigeMaxShortLabelLen = 8,
VestigeMaxCategLabelLen = 24,
VestigeMaxFileNameLen = 100
};
/* constants from http://asseca.com/vst-24-specs/efGetPlugCategory.html */
enum VstPlugCategory
{
kPlugCategUnknown = 0,
kPlugCategEffect,
kPlugCategSynth,
kPlugCategAnalysis,
kPlugCategMastering,
kPlugCategSpacializer,
kPlugCategRoomFx,
kPlugSurroundFx,
kPlugCategRestoration,
kPlugCategOfflineProcess,
kPlugCategShell,
kPlugCategGenerator,
kPlugCategMaxCount
};
typedef struct _VstEvents VstEvents;
/* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */
struct _VstParameterProperties
{
float stepFloat; /* float step */
float smallStepFloat; /* small float step */
float largeStepFloat; /* large float step */
char label[64]; /* parameter label */
int32_t flags; /* @see VstParameterFlags */
int32_t minInteger; /* integer minimum */
int32_t maxInteger; /* integer maximum */
int32_t stepInteger; /* integer step */
int32_t largeStepInteger; /* large integer step */
char shortLabel[VestigeMaxShortLabelLen]; /* short label, recommended: 6 + delimiter */
int16_t displayIndex; /* index where this parameter should be displayed (starting with 0) */
int16_t category; /* 0: no category, else group index + 1 */
int16_t numParametersInCategory; /* number of parameters in category */
int16_t reserved; /* zero */
char categoryLabel[VestigeMaxCategLabelLen]; /* category label, e.g. "Osc 1" */
char future[16]; /* reserved for future use */
};
typedef struct _VstParameterProperties VstParameterProperties;
/* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */
enum VstParameterFlags
{
kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */
kVstParameterUsesIntegerMinMax = 1 << 1, /* minInteger, maxInteger valid */
kVstParameterUsesFloatStep = 1 << 2, /* stepFloat, smallStepFloat, largeStepFloat valid */
kVstParameterUsesIntStep = 1 << 3, /* stepInteger, largeStepInteger valid */
kVstParameterSupportsDisplayIndex = 1 << 4, /* displayIndex valid */
kVstParameterSupportsDisplayCategory = 1 << 5, /* category, etc. valid */
kVstParameterCanRamp = 1 << 6 /* set if parameter value can ramp up/down */
};
struct _AEffect
{
// Never use virtual functions!!!
// 00-03
int magic;
// dispatcher 04-07
intptr_t (* dispatcher) (struct _AEffect *, int, int, intptr_t, void *, float);
// process, quite sure 08-0b
void (* process) (struct _AEffect *, float **, float **, int);
// setParameter 0c-0f
void (* setParameter) (struct _AEffect *, int, float);
// getParameter 10-13
float (* getParameter) (struct _AEffect *, int);
// programs 14-17
int numPrograms;
// Params 18-1b
int numParams;
// Input 1c-1f
int numInputs;
// Output 20-23
int numOutputs;
// flags 24-27
int flags;
// Fill somewhere 28-2b
void *ptr1;
void *ptr2;
// Zeroes 2c-2f 30-33 34-37 38-3b
char empty3[4 + 4 + 4];
// 1.0f 3c-3f
float unkown_float;
// An object? pointer 40-43
void *ptr3;
// Zeroes 44-47
void *user;
// Id 48-4b
int32_t uniqueID;
// Version 4c-4f
int32_t version;
// processReplacing 50-53
void (* processReplacing) (struct _AEffect *, float **, float **, int);
};
typedef struct _AEffect AEffect;
typedef struct _VstTimeInfo
{
/* info from online documentation of VST provided by Steinberg */
double samplePos;
double sampleRate;
double nanoSeconds;
double ppqPos;
double tempo;
double barStartPos;
double cycleStartPos;
double cycleEndPos;
int32_t timeSigNumerator;
int32_t timeSigDenominator;
int32_t smpteOffset;
int32_t smpteFrameRate;
int32_t samplesToNextClock;
int32_t flags;
} VstTimeInfo;
typedef intptr_t (* audioMasterCallback) (AEffect *, int32_t, int32_t, intptr_t, void *, float);
#endif
+8 -13
View File
@@ -45,10 +45,9 @@ Config::Config()
upcoming_queue_size(0.5),
upcoming_queue_type(FRAME_QUEUE_TYPE_SECONDS),
loop(true),
pause_at_out_point(true),
seek_also_selects(false),
effect_textbox_lines(3),
use_software_fallback(false)
effect_textbox_lines(3),
use_software_fallback(false)
{}
void Config::load(QString path) {
@@ -158,9 +157,6 @@ void Config::load(QString path) {
} else if (stream.name() == "Loop") {
stream.readNext();
loop = (stream.text() == "1");
} else if (stream.name() == "PauseAtOutPoint") {
stream.readNext();
pause_at_out_point = (stream.text() == "1");
} else if (stream.name() == "SeekAlsoSelects") {
stream.readNext();
seek_also_selects = (stream.text() == "1");
@@ -170,10 +166,10 @@ void Config::load(QString path) {
} else if (stream.name() == "EffectTextboxLines") {
stream.readNext();
effect_textbox_lines = stream.text().toInt();
} else if (stream.name() == "UseSoftwareFallback") {
stream.readNext();
use_software_fallback = (stream.text() == "1");
}
} else if (stream.name() == "UseSoftwareFallback") {
stream.readNext();
use_software_fallback = (stream.text() == "1");
}
}
}
if (stream.hasError()) {
@@ -230,11 +226,10 @@ void Config::save(QString path) {
stream.writeTextElement("UpcomingFrameQueueSize", QString::number(upcoming_queue_size));
stream.writeTextElement("UpcomingFrameQueueType", QString::number(upcoming_queue_type));
stream.writeTextElement("Loop", QString::number(loop));
stream.writeTextElement("PauseAtOutPoint", QString::number(pause_at_out_point));
stream.writeTextElement("SeekAlsoSelects", QString::number(seek_also_selects));
stream.writeTextElement("CSSPath", css_path);
stream.writeTextElement("EffectTextboxLines", QString::number(effect_textbox_lines));
stream.writeTextElement("UseSoftwareFallback", QString::number(use_software_fallback));
stream.writeTextElement("EffectTextboxLines", QString::number(effect_textbox_lines));
stream.writeTextElement("UseSoftwareFallback", QString::number(use_software_fallback));
stream.writeEndElement(); // configuration
stream.writeEndDocument(); // doc
-1
View File
@@ -59,7 +59,6 @@ struct Config {
double upcoming_queue_size;
int upcoming_queue_type;
bool loop;
bool pause_at_out_point;
bool seek_also_selects;
QString css_path;
int effect_textbox_lines;
+21
View File
@@ -21,3 +21,24 @@ QStringList LibFilter() {
return {"*.so", "*.dylib"};
#endif
}
#ifdef __APPLE__
CFBundleRef BundleLoad(const QString &filename) {
CFStringRef bundle_str = CFStringCreateWithCString(NULL, filename.toUtf8(), kCFStringEncodingUTF8);
CFURLRef bundle_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundle_str, kCFURLPOSIXPathStyle, true);
CFBundleRef bundle = NULL;
if (bundle_url != NULL) {
bundle = CFBundleCreate(kCFAllocatorDefault, bundle_url);
} else {
qCritical() << "Failed to create VST URL";
}
CFRelease(bundle_url);
CFRelease(bundle_str);
return bundle;
}
void BundleClose(CFBundleRef bundle) {
CFBundleUnloadExecutable(bundle);
CFRelease(bundle);
}
#endif
+8
View File
@@ -18,4 +18,12 @@
ModulePtr LibLoad(const QString& filename);
QStringList LibFilter();
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
class NSWindow;
CFBundleRef BundleLoad(const QString& filename);
void BundleClose(CFBundleRef bundle);
#endif
#endif // CROSSPLATFORMLIB_H
+3 -3
View File
@@ -351,7 +351,7 @@ void ExportThread::run() {
start_time = QDateTime::currentMSecsSinceEpoch();
if (audio_enabled) {
compose_audio(nullptr, sequence, true);
compose_audio(nullptr, sequence, true, false);
}
if (video_enabled) {
do {
@@ -489,7 +489,7 @@ void ExportThread::run() {
}
void ExportThread::wake() {
mutex.lock();
mutex.lock();
waitCond.wakeAll();
mutex.unlock();
mutex.unlock();
}
+1 -3
View File
@@ -59,6 +59,7 @@ void PreviewGenerator::parse_media() {
ms.preview_done = false;
ms.file_index = i;
ms.enabled = true;
ms.infinite_length = false;
bool append = false;
@@ -77,7 +78,6 @@ void PreviewGenerator::parse_media() {
&& fmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_DNXHD) { // silly hack but this is the only scenario i've seen this
if (footage->url.contains('%')) {
// must be an image sequence
ms.infinite_length = false;
ms.video_frame_rate = 25;
} else {
ms.infinite_length = true;
@@ -85,8 +85,6 @@ void PreviewGenerator::parse_media() {
ms.video_frame_rate = 0;
}
} else {
ms.infinite_length = false;
// using ffmpeg's built-in heuristic
ms.video_frame_rate = av_q2d(av_guess_frame_rate(fmt_ctx, fmt_ctx->streams[i], nullptr));
+44 -9
View File
@@ -735,6 +735,7 @@ void MainWindow::setup_menus() {
playback_menu->addAction(tr("Go to Start"), this, SLOT(go_to_start()), QKeySequence("Home"))->setProperty("id", "gotostart");
playback_menu->addAction(tr("Previous Frame"), this, SLOT(prev_frame()), QKeySequence("Left"))->setProperty("id", "prevframe");
playback_menu->addAction(tr("Play/Pause"), this, SLOT(playpause()), QKeySequence("Space"))->setProperty("id", "playpause");
playback_menu->addAction(tr("Play In to Out"), this, SLOT(play_in_to_out()), QKeySequence("Shift+Space"))->setProperty("id", "playintoout");
playback_menu->addAction(tr("Next Frame"), this, SLOT(next_frame()), QKeySequence("Right"))->setProperty("id", "nextframe");
playback_menu->addAction(tr("Go to End"), this, SLOT(go_to_end()), QKeySequence("End"))->setProperty("id", "gotoend");
playback_menu->addSeparator();
@@ -744,9 +745,10 @@ void MainWindow::setup_menus() {
playback_menu->addAction(tr("Go to In Point"), this, SLOT(go_to_in()), QKeySequence("Shift+I"))->setProperty("id", "gotoin");
playback_menu->addAction(tr("Go to Out Point"), this, SLOT(go_to_out()), QKeySequence("Shift+O"))->setProperty("id", "gotoout");
playback_menu->addSeparator();
playback_menu->addAction(tr("Increase Speed"), this, SLOT(go_to_out()), QKeySequence("L"))->setProperty("id", "incspeed");
playback_menu->addAction(tr("Pause"), panel_sequence_viewer, SLOT(pause()), QKeySequence("K"))->setProperty("id", "pause");
playback_menu->addAction(tr("Decrease Speed"), this, SLOT(go_to_in()), QKeySequence("J"))->setProperty("id", "decspeed");
playback_menu->addAction(tr("Decrease Speed"), this, SLOT(decrease_speed()), QKeySequence("J"))->setProperty("id", "decspeed");
playback_menu->addAction(tr("Pause"), this, SLOT(pause()), QKeySequence("K"))->setProperty("id", "pause");
playback_menu->addAction(tr("Increase Speed"), this, SLOT(increase_speed()), QKeySequence("L"))->setProperty("id", "incspeed");
playback_menu->addSeparator();
loop_action = playback_menu->addAction(tr("Loop"), this, SLOT(toggle_bool_action()));
loop_action->setProperty("id", "loop");
@@ -911,11 +913,6 @@ void MainWindow::setup_menus() {
set_name_and_marker->setCheckable(true);
set_name_and_marker->setData(reinterpret_cast<quintptr>(&config.set_name_with_marker));
pause_at_out_point_action = tools_menu->addAction(tr("Pause At Out Point"), this, SLOT(toggle_bool_action()));
pause_at_out_point_action->setProperty("id", "pauseoutpoint");
pause_at_out_point_action->setCheckable(true);
pause_at_out_point_action->setData(reinterpret_cast<quintptr>(&config.pause_at_out_point));
tools_menu->addSeparator();
no_autoscroll = tools_menu->addAction(tr("No Auto-Scroll"), this, SLOT(set_autoscroll()));
@@ -1109,6 +1106,15 @@ void MainWindow::prev_frame() {
}
}
void MainWindow::play_in_to_out() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_footage_viewer) {
panel_footage_viewer->play(true);
} else {
panel_sequence_viewer->play(true);
}
}
void MainWindow::next_frame() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_footage_viewer) {
@@ -1136,6 +1142,33 @@ void MainWindow::playpause() {
}
}
void MainWindow::pause() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_footage_viewer) {
panel_footage_viewer->pause();
} else {
panel_sequence_viewer->pause();
}
}
void MainWindow::increase_speed() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_footage_viewer) {
panel_footage_viewer->increase_speed();
} else {
panel_sequence_viewer->increase_speed();
}
}
void MainWindow::decrease_speed() {
QDockWidget* focused_panel = get_focused_panel();
if (focused_panel == panel_footage_viewer) {
panel_footage_viewer->decrease_speed();
} else {
panel_sequence_viewer->decrease_speed();
}
}
void MainWindow::prev_cut() {
QDockWidget* focused_panel = get_focused_panel();
if (sequence != nullptr && (panel_timeline == focused_panel || panel_sequence_viewer == focused_panel)) {
@@ -1222,7 +1255,6 @@ void MainWindow::toolMenu_About_To_Be_Shown() {
set_bool_action_checked(enable_drop_on_media_to_replace);
set_bool_action_checked(enable_hover_focus);
set_bool_action_checked(set_name_and_marker);
set_bool_action_checked(pause_at_out_point_action);
set_bool_action_checked(seek_also_selects);
set_int_action_checked(no_autoscroll, config.autoscroll);
@@ -1494,6 +1526,9 @@ void MainWindow::nest() {
s->clips.append(copy);
}
// relink clips in new nested sequences
panel_timeline->relink_clips_using_ids(selected_clips, s->clips);
// add sequence to project
Media* m = panel_project->new_sequence(ca, s, false, nullptr);
+5 -2
View File
@@ -41,7 +41,7 @@ public slots:
protected:
void closeEvent(QCloseEvent *);
void paintEvent(QPaintEvent *event);
void paintEvent(QPaintEvent *event);
private slots:
void clear_undo_stack();
@@ -66,7 +66,11 @@ private slots:
void go_to_out();
void go_to_start();
void prev_frame();
void play_in_to_out();
void playpause();
void pause();
void increase_speed();
void decrease_speed();
void next_frame();
void go_to_end();
void prev_cut();
@@ -186,7 +190,6 @@ private:
QAction* enable_hover_focus;
QAction* set_name_and_marker;
QAction* loop_action;
QAction* pause_at_out_point_action;
QAction* seek_also_selects;
// edit menu actions
+5 -3
View File
@@ -132,7 +132,8 @@ SOURCES += \
effects/internal/frei0reffect.cpp \
project/effectloaders.cpp \
io/crossplatformlib.cpp \
effects/internal/vsthost.cpp
effects/internal/vsthost.cpp \
ui/flowlayout.cpp
HEADERS += \
mainwindow.h \
@@ -232,7 +233,8 @@ HEADERS += \
effects/internal/frei0reffect.h \
project/effectloaders.h \
io/crossplatformlib.h \
effects/internal/vsthost.h
effects/internal/vsthost.h \
ui/flowlayout.h
FORMS +=
@@ -242,7 +244,7 @@ win32 {
}
mac {
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -framework CoreFoundation
ICON = packaging/macos/olive.icns
INCLUDEPATH = /usr/local/include
}
+28 -9
View File
@@ -53,7 +53,7 @@ EffectControls::EffectControls(QWidget *parent) :
lblMultipleClipsSelected->setVisible(false);
connect(keyframeView, SIGNAL(wheel_event_signal(QWheelEvent*)), effects_area, SLOT(receive_wheel_event(QWheelEvent*)));
connect(keyframeView, SIGNAL(wheel_event_signal(QWheelEvent*)), effects_area, SLOT(receive_wheel_event(QWheelEvent*)));
connect(horizontalScrollBar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int)));
connect(horizontalScrollBar, SIGNAL(resize_move(double)), keyframeView, SLOT(resize_move(double)));
connect(horizontalScrollBar, SIGNAL(valueChanged(int)), keyframeView, SLOT(set_x_scroll(int)));
@@ -150,6 +150,15 @@ void EffectControls::scroll_to_frame(long frame) {
scroll_to_frame_internal(horizontalScrollBar, frame, zoom, keyframeView->width());
}
void EffectControls::add_effect_paste_action(QMenu *menu) {
QAction* paste_action = menu->addAction(tr("&Paste"), panel_timeline, SLOT(paste(bool)));
paste_action->setEnabled(clipboard.size() > 0 && clipboard_type == CLIPBOARD_TYPE_EFFECT);
}
void EffectControls::cut() {
copy(true);
}
void EffectControls::show_effect_menu(int type, int subtype) {
effect_menu_type = type;
effect_menu_subtype = subtype;
@@ -291,6 +300,8 @@ void EffectControls::setup_ui() {
scrollAreaLayout->setMargin(0);
effects_area = new EffectsArea(scrollAreaWidgetContents);
effects_area->setContextMenuPolicy(Qt::CustomContextMenu);
connect(effects_area, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(effects_area_context_menu()));
QVBoxLayout* effects_area_layout = new QVBoxLayout(effects_area);
effects_area_layout->setSpacing(0);
@@ -441,17 +452,25 @@ void EffectControls::setup_ui() {
hlayout->addWidget(splitter);
setWidget(contents);
setWidget(contents);
}
void EffectControls::update_scrollbar() {
verticalScrollBar->setMaximum(qMax(0, effects_area->height() - keyframeView->height() - headers->height()));
verticalScrollBar->setPageStep(verticalScrollBar->height());
verticalScrollBar->setMaximum(qMax(0, effects_area->height() - keyframeView->height() - headers->height()));
verticalScrollBar->setPageStep(verticalScrollBar->height());
}
void EffectControls::queue_post_update() {
keyframeView->update();
update_scrollbar();
keyframeView->update();
update_scrollbar();
}
void EffectControls::effects_area_context_menu() {
QMenu menu(this);
add_effect_paste_action(&menu);
menu.exec(QCursor::pos());
}
void EffectControls::load_effects() {
@@ -484,7 +503,7 @@ void EffectControls::load_effects() {
keyframeView->setEnabled(true);
headers->setVisible(true);
QTimer::singleShot(50, this, SLOT(queue_post_update()));
QTimer::singleShot(50, this, SLOT(queue_post_update()));
}
}
}
@@ -544,7 +563,7 @@ void EffectControls::audio_transition_click() {
}
void EffectControls::resizeEvent(QResizeEvent*) {
update_scrollbar();
update_scrollbar();
}
bool EffectControls::is_focused() {
@@ -569,5 +588,5 @@ EffectsArea::EffectsArea(QWidget* parent) :
{}
void EffectsArea::receive_wheel_event(QWheelEvent *e) {
QApplication::sendEvent(this, e);
QApplication::sendEvent(this, e);
}
+11 -7
View File
@@ -19,20 +19,19 @@ class QScrollBar;
class QHBoxLayout;
class EffectsArea : public QWidget {
Q_OBJECT
Q_OBJECT
public:
EffectsArea(QWidget* parent = 0);
EffectsArea(QWidget* parent = 0);
QScrollArea* parent_widget;
KeyframeView* keyframe_area;
TimelineHeader* header;
public slots:
void receive_wheel_event(QWheelEvent* e);
void receive_wheel_event(QWheelEvent* e);
};
class EffectControls : public QDockWidget
{
Q_OBJECT
public:
explicit EffectControls(QWidget *parent = 0);
~EffectControls();
@@ -44,7 +43,6 @@ public:
void set_zoom(bool in);
bool keyframe_focus();
void delete_selected_keyframes();
void copy(bool del);
bool multiple;
void scroll_to_frame(long frame);
@@ -56,7 +54,11 @@ public:
QScrollBar* verticalScrollBar;
QMutex effects_loaded;
void add_effect_paste_action(QMenu* menu);
public slots:
void cut();
void copy(bool del = false);
void update_keyframes();
private slots:
void menu_select(QAction* q);
@@ -68,8 +70,10 @@ private slots:
void deselect_all_effects(QWidget*);
void update_scrollbar();
void queue_post_update();
void update_scrollbar();
void queue_post_update();
void effects_area_context_menu();
protected:
void resizeEvent(QResizeEvent *event);
private:
+3 -3
View File
@@ -52,7 +52,7 @@ GraphEditor::GraphEditor(QWidget* parent) : QDockWidget(parent), row(nullptr) {
right_tool_widget->setLayout(right_tool_layout);
tools->addWidget(right_tool_widget);
keyframe_nav = new KeyframeNavigator();
keyframe_nav = new KeyframeNavigator(0, false);
keyframe_nav->enable_keyframes(true);
keyframe_nav->enable_keyframe_toggle(false);
left_tool_layout->addWidget(keyframe_nav);
@@ -162,8 +162,8 @@ void GraphEditor::set_row(EffectRow *r) {
slider_button->setCheckable(true);
slider_button->setChecked(field->is_enabled());
slider_button->setIcon(QIcon(":/icons/record.png"));
slider_button->setProperty("field", i);
slider_button->setIconSize(slider_button->iconSize()*0.5);
slider_button->setProperty("field", i);
slider_button->setIconSize(slider_button->iconSize()*0.5);
connect(slider_button, SIGNAL(toggled(bool)), this, SLOT(set_field_visibility(bool)));
slider_proxy_buttons.append(slider_button);
value_layout->addWidget(slider_button);
+92 -87
View File
@@ -21,6 +21,7 @@
#include "ui/timelineheader.h"
#include "ui/resizablescrollbar.h"
#include "ui/audiomonitor.h"
#include "ui/flowlayout.h"
#include "mainwindow.h"
#include "debug.h"
@@ -67,7 +68,6 @@ Timeline::Timeline(QWidget *parent) :
transition_tool_post_clip(-1),
hand_moving(false),
block_repaints(false),
last_frame(0),
scroll(0)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
@@ -442,11 +442,6 @@ void Timeline::repaint_timeline() {
if (sequence != nullptr) {
set_sb_max();
if (last_frame != sequence->playhead) {
audio_monitor->update();
last_frame = sequence->playhead;
}
}
}
}
@@ -490,7 +485,21 @@ void Timeline::select_from_playhead() {
}
void Timeline::resizeEvent(QResizeEvent *) {
// adjust maximum scrollbar
if (sequence != nullptr) set_sb_max();
// resize tool button widget to its contents
QList<QWidget*> tool_button_children = tool_button_widget->findChildren<QWidget*>();
int total_client_height = 0;
int horizontal_spacing = static_cast<FlowLayout*>(tool_button_widget->layout())->horizontalSpacing();
int vertical_spacing = static_cast<FlowLayout*>(tool_button_widget->layout())->verticalSpacing();
for (int i=0;i<tool_button_children.size();i++) {
total_client_height += tool_button_children.at(i)->height() + vertical_spacing;
}
int comp_height = tool_button_widget->height();
int cols = qCeil(double(total_client_height)/double(comp_height));
tool_button_widget->setFixedWidth((tool_button_children.at(0)->width())*cols + horizontal_spacing*(cols-1) + 1);
}
void Timeline::delete_in_out(bool ripple) {
@@ -571,8 +580,6 @@ void Timeline::delete_selection(QVector<Selection>& selections, bool ripple_dele
}
}
selections.clear();
undo_stack.push(ca);
update_ui(true);
@@ -634,14 +641,22 @@ void Timeline::snapping_clicked(bool checked) {
snapping = checked;
}
Clip* Timeline::split_clip(ComboAction* ca, int p, long frame) {
return split_clip(ca, p, frame, frame);
Clip* Timeline::split_clip(ComboAction* ca, bool transitions, int p, long frame) {
return split_clip(ca, transitions, p, frame, frame);
}
Clip* Timeline::split_clip(ComboAction* ca, int p, long frame, long post_in) {
Clip* Timeline::split_clip(ComboAction* ca, bool transitions, int p, long frame, long post_in) {
Clip* pre = sequence->clips.at(p);
if (pre != nullptr && pre->timeline_in < frame && pre->timeline_out > frame) { // guard against attempts to split at in/out points
Clip* post = pre->copy(sequence);
bool splitting_closing_dual_transition = false;
if (transitions
&& pre->get_closing_transition() != nullptr
&& pre->get_closing_transition()->secondary_clip != nullptr) {
splitting_closing_dual_transition = true;
}
Clip* post = pre->copy(sequence, transitions && !splitting_closing_dual_transition);
long new_clip_length = frame - pre->timeline_in;
@@ -651,11 +666,11 @@ Clip* Timeline::split_clip(ComboAction* ca, int p, long frame, long post_in) {
move_clip(ca, pre, pre->timeline_in, frame, pre->clip_in, pre->track);
if (pre->get_opening_transition() != nullptr) {
/*if (frame < pre->timeline_in + pre->get_opening_transition()->length && pre->get_opening_transition()->secondary_clip != nullptr) {
// if (frame < pre->timeline_in + pre->get_opening_transition()->length && pre->get_opening_transition()->secondary_clip != nullptr) {
// separate shared transition
ca->append(new SetPointer((void**) &pre->get_opening_transition()->secondary_clip, nullptr));
pre->get_opening_transition()->secondary_clip->closing_transition = pre->get_opening_transition()->copy(pre->get_opening_transition()->secondary_clip, nullptr);
}*/
// ca->append(new SetPointer((void**) &pre->get_opening_transition()->secondary_clip, nullptr));
// pre->get_opening_transition()->secondary_clip->closing_transition = pre->get_opening_transition()->copy(pre->get_opening_transition()->secondary_clip, nullptr);
// }
if (pre->get_opening_transition()->get_true_length() > new_clip_length) {
ca->append(new ModifyTransitionCommand(pre, TA_OPENING_TRANSITION, new_clip_length));
@@ -664,8 +679,24 @@ Clip* Timeline::split_clip(ComboAction* ca, int p, long frame, long post_in) {
post->sequence->hard_delete_transition(post, TA_OPENING_TRANSITION);
}
if (pre->get_closing_transition() != nullptr) {
ca->append(new DeleteTransitionCommand(pre->sequence, pre->closing_transition));
if (pre->get_closing_transition()->secondary_clip == nullptr) post->get_closing_transition()->set_length(qMin(long(post->get_closing_transition()->get_true_length()), post->getLength()));
if (splitting_closing_dual_transition) {
// just move closing transition to post clip
// WORKAROUND
ca->append(new DeleteTransitionCommand(pre->sequence, pre->closing_transition));
} else {
ca->append(new DeleteTransitionCommand(pre->sequence, pre->closing_transition));
if (post->get_closing_transition() != nullptr) {
if (pre->get_closing_transition()->secondary_clip == nullptr) {
post->get_closing_transition()->set_length(qMin(long(post->get_closing_transition()->get_true_length()), post->getLength()));
}
if (post->get_closing_transition()->get_length() > post->getLength()) {
post->get_closing_transition()->set_length(post->getLength());
}
}
}
}
return post;
@@ -695,7 +726,7 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool
QVector<int> pre_clips;
QVector<Clip*> post_clips;
Clip* post = split_clip(ca, clip, frame);
Clip* post = split_clip(ca, true, clip, frame);
// if alt is not down, split clips links too
if (post == nullptr) {
@@ -714,7 +745,7 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool
Clip* link = sequence->clips.at(l);
if ((original_clip_is_selected && is_clip_selected(link, true)) || !original_clip_is_selected) {
split_cache.append(l);
Clip* s = split_clip(ca, l, frame);
Clip* s = split_clip(ca, true, l, frame);
if (s != nullptr) {
pre_clips.append(l);
post_clips.append(s);
@@ -801,7 +832,7 @@ void Timeline::delete_areas_and_relink(ComboAction* ca, QVector<Selection>& area
// middle of clip is within deletion area
// duplicate clip
Clip* post = split_clip(ca, j, s.in, s.out);
Clip* post = split_clip(ca, true, j, s.in, s.out);
pre_clips.append(j);
post_clips.append(post);
@@ -830,8 +861,15 @@ void Timeline::delete_areas_and_relink(ComboAction* ca, QVector<Selection>& area
}
}
}
}
// deselect selected clip areas
QVector<Selection> area_copy = areas;
for (int i=0;i<area_copy.size();i++) {
const Selection& s = area_copy.at(i);
deselect_area(s.in, s.out, s.track);
}
relink_clips_using_ids(pre_clips, post_clips);
ca->append(new AddClipCommand(sequence, post_clips));
}
@@ -902,7 +940,9 @@ void Timeline::relink_clips_using_ids(QVector<int>& old_clips, QVector<Clip*>& n
for (int j=0;j<oc->linked.size();j++) {
for (int k=0;k<old_clips.size();k++) { // find clip with that ID
if (oc->linked.at(j) == old_clips.at(k)) {
new_clips.at(i)->linked.append(k);
if (new_clips.at(i) != nullptr) {
new_clips.at(i)->linked.append(k);
}
}
}
}
@@ -1180,50 +1220,12 @@ bool Timeline::split_selection(ComboAction* ca) {
for (int i=0;i<sequence->selections.size();i++) {
const Selection& s = sequence->selections.at(i);
if (s.track == clip->track) {
if (clip->timeline_in < s.in && clip->timeline_out > s.out) {
Clip* split_A = clip->copy(sequence);
split_A->clip_in += (s.in - clip->timeline_in);
split_A->timeline_in = s.in;
split_A->timeline_out = s.out;
pre_splits.append(j);
post_splits.append(split_A);
Clip* split_B = clip->copy(sequence);
split_B->clip_in += (s.out - clip->timeline_in);
split_B->timeline_in = s.out;
secondary_post_splits.append(split_B);
if (clip->get_opening_transition() != nullptr) {
split_B->sequence->hard_delete_transition(split_B, TA_OPENING_TRANSITION);
split_A->sequence->hard_delete_transition(split_A, TA_OPENING_TRANSITION);
}
if (clip->get_closing_transition() != nullptr) {
ca->append(new DeleteTransitionCommand(clip->sequence, clip->closing_transition));
split_A->sequence->hard_delete_transition(split_A, TA_CLOSING_TRANSITION);
}
move_clip(ca, clip, clip->timeline_in, s.in, clip->clip_in, clip->track);
split = true;
} else {
Clip* post_a = split_clip(ca, j, s.in);
Clip* post_b = split_clip(ca, j, s.out);
if (post_a != nullptr) {
pre_splits.append(j);
post_splits.append(post_a);
split = true;
}
if (post_b != nullptr) {
if (post_a != nullptr) {
pre_splits.append(j);
post_splits.append(post_b);
} else {
secondary_post_splits.append(post_b);
}
split = true;
}
}
Clip* post_b = split_clip(ca, true, j, s.out);
Clip* post_a = split_clip(ca, post_b == nullptr, j, s.in);
pre_splits.append(j);
post_splits.append(post_a);
secondary_post_splits.append(post_b);
split = true;
}
}
}
@@ -1233,6 +1235,10 @@ bool Timeline::split_selection(ComboAction* ca) {
// relink after splitting
relink_clips_using_ids(pre_splits, post_splits);
relink_clips_using_ids(pre_splits, secondary_post_splits);
post_splits.removeAll(nullptr);
secondary_post_splits.removeAll(nullptr);
ca->append(new AddClipCommand(sequence, post_splits));
ca->append(new AddClipCommand(sequence, secondary_post_splits));
@@ -1267,7 +1273,7 @@ void Timeline::split_at_playhead() {
for (int j=0;j<sequence->clips.size();j++) {
Clip* clip = sequence->clips.at(j);
if (clip != nullptr && is_clip_selected(clip, true)) {
Clip* s = split_clip(ca, j, sequence->playhead);
Clip* s = split_clip(ca, true, j, sequence->playhead);
if (s != nullptr) {
pre_clips.append(j);
post_clips.append(s);
@@ -1596,14 +1602,15 @@ void Timeline::setup_ui() {
horizontalLayout->setSpacing(0);
horizontalLayout->setContentsMargins(0, 0, 0, 0);
QWidget* tool_buttons = new QWidget(dockWidgetContents);
tool_buttons->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
tool_button_widget = new QWidget(dockWidgetContents);
tool_button_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout* tool_buttons_layout = new QVBoxLayout(tool_buttons);
FlowLayout* tool_buttons_layout = new FlowLayout(tool_button_widget);
// tool_buttons_layout->setSizeConstraint(QLayout::SetNoConstraint);
tool_buttons_layout->setSpacing(4);
tool_buttons_layout->setContentsMargins(0, 0, 0, 0);
toolArrowButton = new QPushButton(tool_buttons);
toolArrowButton = new QPushButton(tool_button_widget);
QIcon arrow_icon;
arrow_icon.addFile(QStringLiteral(":/icons/arrow.png"), QSize(), QIcon::Normal, QIcon::Off);
arrow_icon.addFile(QStringLiteral(":/icons/arrow-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
@@ -1614,7 +1621,7 @@ void Timeline::setup_ui() {
connect(toolArrowButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolArrowButton);
toolEditButton = new QPushButton(tool_buttons);
toolEditButton = new QPushButton(tool_button_widget);
QIcon icon1;
icon1.addFile(QStringLiteral(":/icons/beam.png"), QSize(), QIcon::Normal, QIcon::Off);
icon1.addFile(QStringLiteral(":/icons/beam-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
@@ -1625,7 +1632,7 @@ void Timeline::setup_ui() {
connect(toolEditButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolEditButton);
toolRippleButton = new QPushButton(tool_buttons);
toolRippleButton = new QPushButton(tool_button_widget);
QIcon icon2;
icon2.addFile(QStringLiteral(":/icons/ripple.png"), QSize(), QIcon::Normal, QIcon::Off);
icon2.addFile(QStringLiteral(":/icons/ripple-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
@@ -1636,7 +1643,7 @@ void Timeline::setup_ui() {
connect(toolRippleButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolRippleButton);
toolRazorButton = new QPushButton(tool_buttons);
toolRazorButton = new QPushButton(tool_button_widget);
QIcon icon4;
icon4.addFile(QStringLiteral(":/icons/razor.png"), QSize(), QIcon::Normal, QIcon::Off);
icon4.addFile(QStringLiteral(":/icons/razor-disabled.png"), QSize(), QIcon::Disabled, QIcon::Off);
@@ -1647,7 +1654,7 @@ void Timeline::setup_ui() {
connect(toolRazorButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolRazorButton);
toolSlipButton = new QPushButton(tool_buttons);
toolSlipButton = new QPushButton(tool_button_widget);
QIcon icon5;
icon5.addFile(QStringLiteral(":/icons/slip.png"), QSize(), QIcon::Normal, QIcon::On);
icon5.addFile(QStringLiteral(":/icons/slip-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1658,7 +1665,7 @@ void Timeline::setup_ui() {
connect(toolSlipButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolSlipButton);
toolSlideButton = new QPushButton(tool_buttons);
toolSlideButton = new QPushButton(tool_button_widget);
QIcon icon6;
icon6.addFile(QStringLiteral(":/icons/slide.png"), QSize(), QIcon::Normal, QIcon::On);
icon6.addFile(QStringLiteral(":/icons/slide-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1669,7 +1676,7 @@ void Timeline::setup_ui() {
connect(toolSlideButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolSlideButton);
toolHandButton = new QPushButton(tool_buttons);
toolHandButton = new QPushButton(tool_button_widget);
QIcon icon7;
icon7.addFile(QStringLiteral(":/icons/hand.png"), QSize(), QIcon::Normal, QIcon::On);
icon7.addFile(QStringLiteral(":/icons/hand-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1680,7 +1687,7 @@ void Timeline::setup_ui() {
connect(toolHandButton, SIGNAL(clicked(bool)), this, SLOT(set_tool()));
tool_buttons_layout->addWidget(toolHandButton);
toolTransitionButton = new QPushButton(tool_buttons);
toolTransitionButton = new QPushButton(tool_button_widget);
QIcon icon8;
icon8.addFile(QStringLiteral(":/icons/transition-tool.png"), QSize(), QIcon::Normal, QIcon::On);
icon8.addFile(QStringLiteral(":/icons/transition-tool-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1690,7 +1697,7 @@ void Timeline::setup_ui() {
connect(toolTransitionButton, SIGNAL(clicked(bool)), this, SLOT(transition_tool_click()));
tool_buttons_layout->addWidget(toolTransitionButton);
snappingButton = new QPushButton(tool_buttons);
snappingButton = new QPushButton(tool_button_widget);
QIcon icon9;
icon9.addFile(QStringLiteral(":/icons/magnet.png"), QSize(), QIcon::Normal, QIcon::On);
icon9.addFile(QStringLiteral(":/icons/magnet-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1701,7 +1708,7 @@ void Timeline::setup_ui() {
connect(snappingButton, SIGNAL(toggled(bool)), this, SLOT(snapping_clicked(bool)));
tool_buttons_layout->addWidget(snappingButton);
zoomInButton = new QPushButton(tool_buttons);
zoomInButton = new QPushButton(tool_button_widget);
QIcon icon10;
icon10.addFile(QStringLiteral(":/icons/zoomin.png"), QSize(), QIcon::Normal, QIcon::On);
icon10.addFile(QStringLiteral(":/icons/zoomin-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1710,7 +1717,7 @@ void Timeline::setup_ui() {
connect(zoomInButton, SIGNAL(clicked(bool)), this, SLOT(zoom_in()));
tool_buttons_layout->addWidget(zoomInButton);
zoomOutButton = new QPushButton(tool_buttons);
zoomOutButton = new QPushButton(tool_button_widget);
QIcon icon11;
icon11.addFile(QStringLiteral(":/icons/zoomout.png"), QSize(), QIcon::Normal, QIcon::On);
icon11.addFile(QStringLiteral(":/icons/zoomout-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1719,7 +1726,7 @@ void Timeline::setup_ui() {
connect(zoomOutButton, SIGNAL(clicked(bool)), this, SLOT(zoom_out()));
tool_buttons_layout->addWidget(zoomOutButton);
recordButton = new QPushButton(tool_buttons);
recordButton = new QPushButton(tool_button_widget);
QIcon icon12;
icon12.addFile(QStringLiteral(":/icons/record.png"), QSize(), QIcon::Normal, QIcon::On);
icon12.addFile(QStringLiteral(":/icons/record-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1729,7 +1736,7 @@ void Timeline::setup_ui() {
tool_buttons_layout->addWidget(recordButton);
addButton = new QPushButton(tool_buttons);
addButton = new QPushButton(tool_button_widget);
QIcon icon13;
icon13.addFile(QStringLiteral(":/icons/add-button.png"), QSize(), QIcon::Normal, QIcon::On);
icon13.addFile(QStringLiteral(":/icons/add-button-disabled.png"), QSize(), QIcon::Disabled, QIcon::On);
@@ -1738,9 +1745,7 @@ void Timeline::setup_ui() {
connect(addButton, SIGNAL(clicked()), this, SLOT(add_btn_click()));
tool_buttons_layout->addWidget(addButton);
tool_buttons_layout->addStretch();
horizontalLayout->addWidget(tool_buttons);
horizontalLayout->addWidget(tool_button_widget);
timeline_area = new QWidget(dockWidgetContents);
QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::Minimum);
@@ -1760,7 +1765,7 @@ void Timeline::setup_ui() {
editAreaLayout->setSpacing(0);
editAreaLayout->setContentsMargins(0, 0, 0, 0);
QSplitter* splitter = new QSplitter(editAreas);
splitter->setChildrenCollapsible(false);
splitter->setChildrenCollapsible(false);
splitter->setOrientation(Qt::Vertical);
QWidget* videoContainer = new QWidget(splitter);
QHBoxLayout* videoContainerLayout = new QHBoxLayout(videoContainer);
+4 -4
View File
@@ -78,9 +78,8 @@ public:
bool focused();
void set_zoom(bool in);
void copy(bool del);
void paste(bool insert);
Clip* split_clip(ComboAction* ca, int p, long frame);
Clip* split_clip(ComboAction* ca, int p, long frame, long post_in);
Clip* split_clip(ComboAction* ca, bool transitions, int p, long frame);
Clip* split_clip(ComboAction* ca, bool transitions, int p, long frame, long post_in);
bool split_selection(ComboAction* ca);
bool split_all_clips_at_point(ComboAction *ca, long point);
bool split_clip_and_relink(ComboAction* ca, int clip, long frame, bool relink);
@@ -207,6 +206,7 @@ public:
void resizeEvent(QResizeEvent *event);
public slots:
void paste(bool insert = false);
void repaint_timeline();
void toggle_show_all();
void deselect();
@@ -231,7 +231,6 @@ private:
QVector<QPushButton*> tool_buttons;
void decheck_tool_buttons(QObject* sender);
void set_tool(int tool);
long last_frame;
int scroll;
void set_sb_max();
@@ -249,6 +248,7 @@ private:
QPushButton* zoomOutButton;
QPushButton* recordButton;
QPushButton* addButton;
QWidget* tool_button_widget;
};
#endif // TIMELINE_H
+83 -25
View File
@@ -44,7 +44,8 @@ Viewer::Viewer(QWidget *parent) :
seq(nullptr),
created_sequence(false),
minimum_zoom(1.0),
cue_recording_internal(false)
cue_recording_internal(false),
playback_speed(0)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
@@ -101,15 +102,20 @@ void Viewer::set_main_sequence() {
void Viewer::reset_all_audio() {
// reset all clip audio
if (seq != nullptr) {
audio_ibuffer_frame = seq->playhead;
audio_ibuffer_timecode = (double) audio_ibuffer_frame / seq->frame_rate;
long last_frame = 0;
for (int i=0;i<seq->clips.size();i++) {
Clip* c = seq->clips.at(i);
if (c != nullptr) {
c->reset_audio();
last_frame = qMax(last_frame, c->timeline_out);
}
}
audio_ibuffer_frame = seq->playhead;
if (playback_speed < 0) {
audio_ibuffer_frame = last_frame - audio_ibuffer_frame;
}
audio_ibuffer_timecode = double(audio_ibuffer_frame) / seq->frame_rate;
}
clear_audio_ibuffer();
}
@@ -242,7 +248,11 @@ bool frame_rate_is_droppable(float rate) {
void Viewer::seek(long p) {
pause();
seq->playhead = p;
if (main_sequence) {
seq->playhead = p;
} else {
seq->playhead = qMin(seq->getEndFrame(), qMax(0L, p));
}
bool update_fx = false;
if (main_sequence) {
panel_timeline->scroll_to_frame(p);
@@ -324,15 +334,44 @@ void Viewer::toggle_play() {
}
}
void Viewer::play() {
void Viewer::increase_speed() {
int new_speed = playback_speed+1;
if (new_speed == 0) {
new_speed++;
}
set_playback_speed(new_speed);
}
void Viewer::decrease_speed() {
int new_speed = playback_speed-1;
if (new_speed == 0) {
new_speed--;
}
set_playback_speed(new_speed);
}
void Viewer::play(bool in_to_out) {
if (panel_sequence_viewer->playing) panel_sequence_viewer->pause();
if (panel_footage_viewer->playing) panel_footage_viewer->pause();
playing_in_to_out = in_to_out;
if (seq != nullptr) {
if (playing_in_to_out) {
uncue_recording();
}
bool seek_to_in = (seq->using_workarea && config.loop);
if (!is_recording_cued()
&& seq->playhead >= get_seq_out()
&& (config.loop || !main_sequence)) {
seek(get_seq_in());
&& (playing_in_to_out
|| seq->playhead >= seq->getEndFrame()
|| (seek_to_in && seq->playhead >= seq->workarea_out))) {
seek(seek_to_in ? seq->workarea_in : 0);
}
if (playback_speed == 0) {
playback_speed = 1;
}
reset_all_audio();
@@ -345,6 +384,7 @@ void Viewer::play() {
just_played = true;
set_playpause_icon(false);
start_msecs = QDateTime::currentMSecsSinceEpoch();
timer_update();
}
}
@@ -363,6 +403,7 @@ void Viewer::pause() {
just_played = false;
set_playpause_icon(true);
playback_updater.stop();
playback_speed = 0;
if (is_recording_cued()) {
uncue_recording();
@@ -432,6 +473,10 @@ void Viewer::update_parents(bool reload_fx) {
}
}
int Viewer::get_playback_speed() {
return playback_speed;
}
void Viewer::resizeEvent(QResizeEvent *) {
if (seq != nullptr) {
set_sb_max();
@@ -519,14 +564,22 @@ void Viewer::set_sb_max() {
headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width());
}
void Viewer::set_playback_speed(int s) {
pause();
playback_speed = qMin(3, qMax(s, -3));
if (playback_speed != 0) {
play();
}
}
long Viewer::get_seq_in() {
return (seq->using_workarea && seq->enable_workarea)
return ((config.loop || playing_in_to_out) && seq->using_workarea && seq->enable_workarea)
? seq->workarea_in
: 0;
}
long Viewer::get_seq_out() {
return (seq->using_workarea && seq->enable_workarea && previous_playhead < seq->workarea_out)
return ((config.loop || playing_in_to_out) && seq->using_workarea && seq->enable_workarea && previous_playhead < seq->workarea_out)
? seq->workarea_out
: seq->getEndFrame();
}
@@ -560,7 +613,7 @@ void Viewer::setup_ui() {
QHBoxLayout* current_timecode_container_layout = new QHBoxLayout(current_timecode_container);
current_timecode_container_layout->setSpacing(0);
current_timecode_container_layout->setMargin(0);
current_timecode_slider = new LabelSlider(current_timecode_container);
current_timecode_slider = new LabelSlider(current_timecode_container);
lower_control_layout->addWidget(current_timecode_container);
QWidget* playback_controls = new QWidget(lower_controls);
@@ -713,23 +766,28 @@ void Viewer::update_playhead() {
void Viewer::timer_update() {
previous_playhead = seq->playhead;
seq->playhead = qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate));
seq->playhead = qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate * playback_speed));
if (config.seek_also_selects) panel_timeline->select_from_playhead();
update_parents(config.seek_also_selects);
long end_frame = get_seq_out();
if (!recording
&& playing
&& seq->playhead >= end_frame
&& previous_playhead < end_frame) {
if (!config.pause_at_out_point && config.loop) {
seek(get_seq_in());
play();
} else if (config.pause_at_out_point || !main_sequence) {
pause();
if (playing) {
if (recording) {
if (recording_start != recording_end && seq->playhead >= recording_end) {
pause();
}
} else {
if (seq->playhead >= seq->getEndFrame()) {
pause();
}
if (seq->using_workarea && seq->playhead >= seq->workarea_out) {
if (config.loop) {
// loop
play();
} else if (playing_in_to_out) {
pause();
}
}
}
} else if (recording && recording_start != recording_end && seq->playhead >= recording_end) {
pause();
}
}
+8 -1
View File
@@ -49,7 +49,7 @@ public:
// playback functions
void seek(long p);
void play();
void play(bool in_to_out = false);
void pause();
bool playing;
long playhead_start;
@@ -67,6 +67,8 @@ public:
void reset_all_audio();
void update_parents(bool reload_fx = false);
int get_playback_speed();
ViewerWidget* viewer_widget;
Media* media;
@@ -80,6 +82,8 @@ public slots:
void go_to_in();
void previous_frame();
void toggle_play();
void increase_speed();
void decrease_speed();
void next_frame();
void go_to_out();
void go_to_end();
@@ -100,8 +104,10 @@ private:
long cached_end_frame;
QString panel_name;
double minimum_zoom;
bool playing_in_to_out;
void set_zoom_value(double d);
void set_sb_max();
void set_playback_speed(int s);
long get_seq_in();
long get_seq_out();
@@ -126,6 +132,7 @@ private:
QTimer recording_flasher;
long previous_playhead;
int playback_speed;
};
#endif // VIEWER_H
+33 -42
View File
@@ -33,7 +33,7 @@ bool audio_rendering = false;
bool recording = false;
qint8 audio_ibuffer[audio_ibuffer_size];
unsigned long audio_ibuffer_read = 0;
qint64 audio_ibuffer_read = 0;
long audio_ibuffer_frame = 0;
double audio_ibuffer_timecode = 0;
@@ -114,11 +114,12 @@ int current_audio_freq() {
return audio_rendering ? sequence->audio_frequency : audio_output->format().sampleRate();
}
unsigned long get_buffer_offset_from_frame(double framerate, long frame) {
qint64 get_buffer_offset_from_frame(double framerate, long frame) {
if (frame >= audio_ibuffer_frame) {
return static_cast<unsigned long>(((double(frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO));
int multiplier = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
return qFloor((double(frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*multiplier;
} else {
qWarning() << "Invalid values passed to get_buffer_offset_from_frame";
qWarning() << "Invalid values passed to get_buffer_offset_from_frame" << frame << "<" << audio_ibuffer_frame;
return 0;
}
}
@@ -164,47 +165,32 @@ void AudioSenderThread::run() {
lock.unlock();
}
int AudioSenderThread::send_audio_to_output(int offset, int max) {
int AudioSenderThread::send_audio_to_output(qint64 offset, int max) {
// send audio to device
unsigned long actual_write = audio_io_device->write((const char*) audio_ibuffer+offset, max);
qint64 actual_write = audio_io_device->write(reinterpret_cast<const char*>(audio_ibuffer)+offset, max);
unsigned long audio_ibuffer_limit = audio_ibuffer_read + actual_write;
qint64 audio_ibuffer_limit = audio_ibuffer_read + actual_write;
// send samples to audio monitor cache
// TODO make this work for the footage viewer - currently, enabling it causes crash due to an ASSERT
Sequence* s = nullptr;
/*if (panel_footage_viewer->playing) {
s = panel_footage_viewer->seq;
}*/
if (panel_sequence_viewer->playing) {
s = panel_sequence_viewer->seq;
}
if (s != nullptr) {
if (panel_timeline->audio_monitor->sample_cache_offset == -1) {
panel_timeline->audio_monitor->sample_cache_offset = s->playhead;
if (actual_write > 0) {
// average values and send to audio monitor
int channels = audio_output->format().channelCount();
qint64 lim = offset + actual_write;
QVector<double> averages;
averages.resize(channels);
averages.fill(0);
int counter = 0;
qint16 sample;
for (qint64 i=offset;i<lim;i+=2) {
sample = qint16(((audio_ibuffer[i+1] & 0xFF) << 8) | (audio_ibuffer[i] & 0xFF));
averages[counter] = qMax((double(sample)/32768.0), averages[counter]);
counter = (counter+1)%channels;
}
int channel_count = av_get_channel_layout_nb_channels(s->audio_layout);
long sample_cache_playhead = panel_timeline->audio_monitor->sample_cache_offset + (panel_timeline->audio_monitor->sample_cache.size()/channel_count);
unsigned long next_buffer_offset, buffer_offset_adjusted;
int i;
unsigned long buffer_offset = get_buffer_offset_from_frame(s->frame_rate, sample_cache_playhead);
if (samples.size() != channel_count) samples.resize(channel_count);
samples.fill(0);
// TODO: I don't like this, but i'm not sure if there's a smarter way to do it
while (static_cast<unsigned long>(buffer_offset) < audio_ibuffer_limit) {
sample_cache_playhead++;
next_buffer_offset = qMin(get_buffer_offset_from_frame(s->frame_rate, sample_cache_playhead), static_cast<unsigned long>(audio_ibuffer_limit));
while (buffer_offset < next_buffer_offset) {
for (i=0;i<samples.size();i++) {
buffer_offset_adjusted = buffer_offset%audio_ibuffer_size;
samples[i] = qMax(qAbs(qint16(((audio_ibuffer[buffer_offset_adjusted+1] & 0xFF) << 8) | (audio_ibuffer[buffer_offset_adjusted] & 0xFF))), samples[i]);
buffer_offset += 2;
}
}
panel_timeline->audio_monitor->sample_cache.append(samples);
buffer_offset = next_buffer_offset;
for (int i=0;i<channels;i++) {
averages[i] = log_volume(1.0-(averages[i]));
}
panel_timeline->audio_monitor->set_value(averages);
}
memset(audio_ibuffer+offset, 0, actual_write);
@@ -214,6 +200,11 @@ int AudioSenderThread::send_audio_to_output(int offset, int max) {
return actual_write;
}
double log_volume(double linear) {
// expects a value between 0 and 1 (or more if amplifying)
return (qExp(linear)-1)/(M_E-1);
}
void int32_to_char_array(qint32 i, char* array) {
memcpy(array, &i, 4);
}
@@ -278,14 +269,14 @@ void write_wave_trailer(QFile& f) {
f.seek(4);
// 4 bytes for total file size - 8 bytes
qint32 file_size = f.size() - 8;
qint32 file_size = qint32(f.size()) - 8;
int32_to_char_array(file_size, arr);
f.write(arr, 4);
f.seek(40);
// 4 bytes for data chunk size (file size - header)
file_size = f.size() - 44;
file_size = qint32(f.size()) - 44;
int32_to_char_array(file_size, arr);
f.write(arr, 4);
}
+5 -3
View File
@@ -27,9 +27,11 @@ public slots:
void notifyReceiver();
private:
QVector<qint16> samples;
int send_audio_to_output(int offset, int max);
int send_audio_to_output(qint64 offset, int max);
};
double log_volume(double linear);
extern QAudioOutput* audio_output;
extern QIODevice* audio_io_device;
extern AudioSenderThread* audio_thread;
@@ -37,7 +39,7 @@ extern QMutex audio_write_lock;
#define audio_ibuffer_size 192000
extern qint8 audio_ibuffer[audio_ibuffer_size];
extern unsigned long audio_ibuffer_read;
extern qint64 audio_ibuffer_read;
extern long audio_ibuffer_frame;
extern double audio_ibuffer_timecode;
extern bool audio_scrub;
@@ -51,7 +53,7 @@ bool is_audio_device_set();
void init_audio();
void stop_audio();
unsigned long get_buffer_offset_from_frame(double framerate, long frame);
qint64 get_buffer_offset_from_frame(double framerate, long frame);
bool start_recording();
void stop_recording();
+54 -27
View File
@@ -86,11 +86,14 @@ void apply_audio_effects(Clip* c, double timecode_start, AVFrame* frame, int nb_
#define AUDIO_BUFFER_PADDING 2048
void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests, int playback_speed) {
long timeline_in = c->get_timeline_in_with_transition();
long timeline_out = c->get_timeline_out_with_transition();
long target_frame = c->audio_target_frame;
bool temp_reverse = (playback_speed < 0);
bool reverse_audio = (c->reverse != temp_reverse);
long frame_skip = 0;
double last_fr = c->sequence->frame_rate;
if (!nests.isEmpty()) {
@@ -113,6 +116,17 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
}
}
if (temp_reverse) {
long seq_end = sequence->getEndFrame();
timeline_in = seq_end - timeline_in;
timeline_out = seq_end - timeline_out;
target_frame = seq_end - target_frame;
long temp = timeline_in;
timeline_in = timeline_out;
timeline_out = temp;
}
while (true) {
AVFrame* frame;
int nb_bytes = INT_MAX;
@@ -147,7 +161,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
if (!c->reached_end) {
int loop = 0;
if (c->reverse && !c->audio_just_reset) {
if (reverse_audio && !c->audio_just_reset) {
avcodec_flush_buffers(c->codecCtx);
c->reached_end = false;
int64_t backtrack_seek = qMax(c->reverse_target - static_cast<int64_t>(av_q2d(av_inv_q(c->stream->time_base))), static_cast<int64_t>(0));
@@ -177,7 +191,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
dout << "reached EOF while reading";
#endif
// TODO revise usage of reached_end in audio
if (!c->reverse) {
if (!reverse_audio) {
c->reached_end = true;
} else {
}
@@ -198,11 +212,11 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
#ifdef AUDIOWARNINGS
dout << "reached EOF while pulling from filtergraph";
#endif
if (!c->reverse) break;
if (!reverse_audio) break;
}
}
if (c->reverse) {
if (reverse_audio) {
if (loop > 1) {
AVFrame* rev_frame = c->queue.at(1);
if (ret != AVERROR_EOF) {
@@ -313,7 +327,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (c->reverse_target * timebase);
dout << "fsi-calc:" << c->frame_sample_index;
#endif
if (c->reverse) c->frame_sample_index = nb_bytes - c->frame_sample_index;
if (reverse_audio) c->frame_sample_index = nb_bytes - c->frame_sample_index;
c->audio_just_reset = false;
}
@@ -343,7 +357,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
}
}
if (c->reverse) frame = c->queue.at(1);
if (reverse_audio) frame = c->queue.at(1);
#ifdef AUDIOWARNINGS
dout << "j" << c->frame_sample_index << nb_bytes;
@@ -364,24 +378,31 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
if (frame->nb_samples == 0) {
break;
} else {
unsigned long buffer_timeline_out = get_buffer_offset_from_frame(c->sequence->frame_rate, timeline_out);
qint64 buffer_timeline_out = get_buffer_offset_from_frame(c->sequence->frame_rate, timeline_out);
audio_write_lock.lock();
int sample_skip = 4*qMax(0, qAbs(playback_speed)-1);
int sample_byte_size = av_get_bytes_per_sample(static_cast<AVSampleFormat>(frame->format));
while (c->frame_sample_index < nb_bytes
&& c->audio_buffer_write < audio_ibuffer_read+(audio_ibuffer_size>>1)
&& c->audio_buffer_write < buffer_timeline_out) {
int upper_byte_index = (c->audio_buffer_write+1)%audio_ibuffer_size;
int lower_byte_index = (c->audio_buffer_write)%audio_ibuffer_size;
qint16 old_sample = static_cast<qint16>((audio_ibuffer[upper_byte_index] & 0xFF) << 8 | (audio_ibuffer[lower_byte_index] & 0xFF));
qint16 new_sample = static_cast<qint16>((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF));
qint16 mixed_sample = mix_audio_sample(old_sample, new_sample);
for (int i=0;i<frame->channels;i++) {
int upper_byte_index = (c->audio_buffer_write+1)%audio_ibuffer_size;
int lower_byte_index = (c->audio_buffer_write)%audio_ibuffer_size;
qint16 old_sample = static_cast<qint16>((audio_ibuffer[upper_byte_index] & 0xFF) << 8 | (audio_ibuffer[lower_byte_index] & 0xFF));
qint16 new_sample = static_cast<qint16>((frame->data[0][c->frame_sample_index+1] & 0xFF) << 8 | (frame->data[0][c->frame_sample_index] & 0xFF));
qint16 mixed_sample = mix_audio_sample(old_sample, new_sample);
audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF);
audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF);
audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF);
audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF);
c->audio_buffer_write+=2;
c->frame_sample_index+=2;
c->audio_buffer_write+=sample_byte_size;
c->frame_sample_index+=sample_byte_size;
}
c->frame_sample_index += sample_skip;
if (c->audio_reset) break;
}
@@ -398,7 +419,7 @@ void cache_audio_worker(Clip* c, bool scrubbing, QVector<Clip*>& nests) {
if (audio_thread != nullptr) audio_thread->notifyReceiver();
}
if (c->frame_sample_index == nb_bytes) {
if (c->frame_sample_index >= nb_bytes) {
c->frame_sample_index = -1;
} else {
// assume we have no more data to send
@@ -544,7 +565,7 @@ void cache_video_worker(Clip* c, long playhead) {
}
}
void reset_cache(Clip* c, long target_frame) {
void reset_cache(Clip* c, long target_frame, int playback_speed) {
// if we seek to a whole other place in the timeline, we'll need to reset the cache with new values
if (c->media == nullptr) {
if (c->track >= 0) {
@@ -607,9 +628,12 @@ void reset_cache(Clip* c, long target_frame) {
// seek (target_frame represents timeline timecode in frames, not clip timecode)
// bool reverse = (c->reverse != temp_reverse);
int64_t timestamp = seconds_to_timestamp(c, playhead_to_clip_seconds(c, target_frame));
if (c->reverse) {
bool temp_reverse = (playback_speed < 0);
if (c->reverse != temp_reverse) {
c->reverse_target = timestamp;
timestamp -= av_q2d(av_inv_q(c->stream->time_base));
#ifdef AUDIOWARNINGS
@@ -790,7 +814,8 @@ void open_clip_worker(Clip* clip) {
// set up cache
clip->queue.append(av_frame_alloc());
if (clip->reverse) {
// if (clip->reverse) {
if (true) {
AVFrame* reverse_frame = av_frame_alloc();
reverse_frame->format = sample_format;
@@ -886,22 +911,22 @@ void open_clip_worker(Clip* clip) {
qInfo() << "Clip opened on track" << clip->track;
}
void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip*> nests) {
void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip*> nests, int playback_speed) {
if (reset) {
// note: for video, playhead is in "internal clip" frames - for audio, it's the timeline playhead
reset_cache(clip, playhead);
reset_cache(clip, playhead, playback_speed);
clip->audio_reset = false;
}
if (clip->media == nullptr) {
if (clip->track >= 0) {
cache_audio_worker(clip, scrubbing, nests);
cache_audio_worker(clip, scrubbing, nests, playback_speed);
}
} else if (clip->media->get_type() == MEDIA_TYPE_FOOTAGE) {
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
cache_video_worker(clip, playhead);
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
cache_audio_worker(clip, scrubbing, nests);
cache_audio_worker(clip, scrubbing, nests, playback_speed);
}
}
}
@@ -936,16 +961,18 @@ void Cacher::run() {
clip->open = true;
caching = true;
interrupt = false;
queued = false;
open_clip_worker(clip);
while (caching) {
clip->can_cache.wait(&clip->lock);
if (!queued) clip->can_cache.wait(&clip->lock);
queued = false;
if (!caching) {
break;
} else {
while (true) {
cache_clip_worker(clip, playhead, reset, scrubbing, nests);
cache_clip_worker(clip, playhead, reset, scrubbing, nests, playback_speed);
if (clip->multithreaded && clip->cacher->interrupt && clip->track < 0) {
clip->cacher->interrupt = false;
} else {
+6 -4
View File
@@ -11,15 +11,17 @@ class Cacher : public QThread
// Q_OBJECT
public:
Cacher(Clip* c);
void run();
void run();
bool caching;
// must be set before caching
long playhead;
bool reset;
bool scrubbing;
bool interrupt;
bool scrubbing;
bool interrupt;
bool queued;
int playback_speed;
QVector<Clip*> nests;
private:
@@ -27,7 +29,7 @@ private:
};
void open_clip_worker(Clip* clip);
void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> nest);
void cache_clip_worker(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> nest, int playback_speed);
void close_clip_worker(Clip* clip);
#endif // CACHER_H
+15 -9
View File
@@ -61,10 +61,13 @@ void open_clip(Clip* clip, bool multithreaded) {
}
} else {
clip->open = true;
clip->finished_opening = true;
}
}
void close_clip(Clip* clip, bool wait) {
clip->finished_opening = false;
// destroy opengl texture in main thread
if (clip->texture != nullptr) {
delete clip->texture;
@@ -94,25 +97,28 @@ void close_clip(Clip* clip, bool wait) {
close_clip_worker(clip);
}
} else {
if (clip->media != nullptr && clip->media->get_type() == MEDIA_TYPE_SEQUENCE)
if (clip->media != nullptr && clip->media->get_type() == MEDIA_TYPE_SEQUENCE) {
closeActiveClips(clip->media->to_sequence());
}
clip->open = false;
}
}
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip*>& nests) {
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip*>& nests, int playback_speed) {
if (clip_uses_cacher(clip)) {
if (clip->multithreaded) {
clip->cacher->playhead = playhead;
clip->cacher->reset = reset;
clip->cacher->nests = nests;
clip->cacher->scrubbing = scrubbing;
clip->cacher->playback_speed = playback_speed;
clip->cacher->queued = true;
if (reset && clip->queue.size() > 0) clip->cacher->interrupt = true;
clip->can_cache.wakeAll();
} else {
cache_clip_worker(clip, playhead, reset, scrubbing, nests);
cache_clip_worker(clip, playhead, reset, scrubbing, nests, playback_speed);
}
}
}
@@ -260,13 +266,13 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) {
uint8_t* data_buffer_1 = target_frame->data[0];
uint8_t* data_buffer_2 = nullptr;
int frame_size;
size_t frame_size;
for (int i=0;i<c->effects.size();i++) {
Effect* e = c->effects.at(i);
if (e->enable_image && e->is_enabled()) {
if (data_buffer_1 == target_frame->data[0]) {
frame_size = target_frame->linesize[0]*target_frame->height;
frame_size = size_t(target_frame->linesize[0])*size_t(target_frame->height);
data_buffer_1 = new uint8_t[frame_size];
data_buffer_2 = new uint8_t[frame_size];
@@ -278,7 +284,7 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) {
}
}
c->texture->setData(0, get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, using_db_1 ? data_buffer_1 : data_buffer_2);
c->texture->setData(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8, const_cast<const uint8_t*>(using_db_1 ? data_buffer_1 : data_buffer_2));
if (data_buffer_1 != target_frame->data[0]) {
delete [] data_buffer_1;
@@ -292,7 +298,7 @@ void get_clip_frame(Clip* c, long playhead, bool& texture_failed) {
// get more frames
QVector<Clip*> empty;
if (cache) cache_clip(c, playhead, reset, false, empty);
if (cache) cache_clip(c, playhead, reset, false, empty, false);
}
}
@@ -385,8 +391,8 @@ void closeActiveClips(Sequence *s) {
if (c != nullptr) {
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
closeActiveClips(c->media->to_sequence());
if (c->open) close_clip(c, true);
} else if (c->open) {
if (c->finished_opening) close_clip(c, true);
} else if (c->finished_opening) {
close_clip(c, true);
}
}
+2 -4
View File
@@ -12,12 +12,10 @@ struct AVFrame;
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
bool clip_uses_cacher(Clip* clip);
void open_clip(Clip* clip, bool multithreaded);
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> &nests);
void cache_clip(Clip* clip, long playhead, bool reset, bool scrubbing, QVector<Clip *> &nests, int playback_speed);
void close_clip(Clip* clip, bool wait);
void cache_audio_worker(Clip* c, bool write_A);
void cache_video_worker(Clip* c, long playhead);
void handle_media(Sequence* sequence, long playhead, bool multithreaded);
void reset_cache(Clip* c, long target_frame);
void reset_cache(Clip* c, long target_frame, int playback_speed);
void get_clip_frame(Clip* c, long playhead, bool &texture_failed);
double get_timecode(Clip* c, long playhead);
+5 -3
View File
@@ -44,7 +44,7 @@ Clip::Clip(Sequence* s) :
reset();
}
Clip* Clip::copy(Sequence* s) {
Clip* Clip::copy(Sequence* s, bool duplicate_transitions) {
Clip* copy = new Clip(s);
copy->enabled = enabled;
@@ -69,8 +69,10 @@ Clip* Clip::copy(Sequence* s) {
copy->cached_fr = (this->sequence == nullptr) ? cached_fr : this->sequence->frame_rate;
if (get_opening_transition() != nullptr && get_opening_transition()->secondary_clip == nullptr) copy->opening_transition = get_opening_transition()->copy(copy, nullptr);
if (get_closing_transition() != nullptr && get_closing_transition()->secondary_clip == nullptr) copy->closing_transition = get_closing_transition()->copy(copy, nullptr);
if (duplicate_transitions) {
if (get_opening_transition() != nullptr && get_opening_transition()->secondary_clip == nullptr) copy->opening_transition = get_opening_transition()->copy(copy, nullptr);
if (get_closing_transition() != nullptr && get_closing_transition()->secondary_clip == nullptr) copy->closing_transition = get_closing_transition()->copy(copy, nullptr);
}
copy->recalculateMaxLength();
+2 -2
View File
@@ -35,7 +35,7 @@ struct Clip
{
Clip(Sequence* s);
~Clip();
Clip* copy(Sequence* s);
Clip* copy(Sequence* s, bool duplicate_transitions = true);
void reset_audio();
void reset();
void refresh();
@@ -127,7 +127,7 @@ struct Clip
// audio playback variables
int64_t reverse_target;
int frame_sample_index;
unsigned long audio_buffer_write;
qint64 audio_buffer_write;
bool audio_reset;
bool audio_just_reset;
long audio_target_frame;
+14 -14
View File
@@ -16,6 +16,7 @@
#include "io/path.h"
#include "mainwindow.h"
#include "io/math.h"
#include "io/clipboard.h"
#include "transition.h"
#include "effects/internal/transformeffect.h"
@@ -28,7 +29,7 @@
#include "effects/internal/paneffect.h"
#include "effects/internal/shakeeffect.h"
#include "effects/internal/cornerpineffect.h"
#ifdef _WIN32
#ifndef NOVST
#include "effects/internal/vsthost.h"
#endif
#include "effects/internal/fillleftrighteffect.h"
@@ -64,7 +65,7 @@ Effect* create_effect(Clip* c, const EffectMeta* em) {
case EFFECT_INTERNAL_SHAKE: return new ShakeEffect(c, em);
case EFFECT_INTERNAL_CORNERPIN: return new CornerPinEffect(c, em);
case EFFECT_INTERNAL_FILLLEFTRIGHT: return new FillLeftRightEffect(c, em);
#ifdef _WIN32
#ifndef NOVST
case EFFECT_INTERNAL_VST: return new VSTHost(c, em);
#endif
#ifndef NOFREI0R
@@ -377,20 +378,24 @@ void Effect::show_context_menu(const QPoint& pos) {
int index = get_index_in_clip();
menu.addAction(tr("Cu&t"), panel_effect_controls, SLOT(cut()));
menu.addAction(tr("&Copy"), panel_effect_controls, SLOT(copy(bool)));
panel_effect_controls->add_effect_paste_action(&menu);
menu.addSeparator();
if (index > 0) {
QAction* move_up = menu.addAction(tr("Move &Up"));
connect(move_up, SIGNAL(triggered(bool)), this, SLOT(move_up()));
menu.addAction(tr("Move &Up"), this, SLOT(move_up()));
}
if (index < parent_clip->effects.size() - 1) {
QAction* move_down = menu.addAction(tr("Move &Down"));
connect(move_down, SIGNAL(triggered(bool)), this, SLOT(move_down()));
menu.addAction(tr("Move &Down"), this, SLOT(move_down()));
}
menu.addSeparator();
QAction* del_action = menu.addAction(tr("D&elete"));
connect(del_action, SIGNAL(triggered(bool)), this, SLOT(delete_self()));
menu.addAction(tr("D&elete"), this, SLOT(delete_self()));
menu.exec(container->title_bar->mapToGlobal(pos));
}
@@ -693,7 +698,7 @@ void Effect::startEffect() {
void Effect::endEffect() {
if (bound) glslProgram->release();
bound = false;
bound = false;
}
void Effect::process_image(double, uint8_t *, uint8_t *, int){}
@@ -912,8 +917,3 @@ qint16 mix_audio_sample(qint16 a, qint16 b) {
mixed_sample = qMax(qMin(mixed_sample, static_cast<qint32>(INT16_MAX)), static_cast<qint32>(INT16_MIN));
return static_cast<qint16>(mixed_sample);
}
double log_volume(double linear) {
// expects a value between 0 and 1 (or more if amplifying)
return (qExp(linear)-1)/(M_E-1);
}
+4 -1
View File
@@ -34,7 +34,7 @@ void load_internal_effects() {
em.internal = EFFECT_INTERNAL_PAN;
effects.append(em);
#ifdef _WIN32
#ifndef NOVST
em.name = "VST Plugin 2.x";
em.internal = EFFECT_INTERNAL_VST;
effects.append(em);
@@ -214,6 +214,9 @@ void load_frei0r_effects() {
effect_dirs.prepend(QDir::homePath() + "/.frei0r-1/lib");
#endif
QString env_path(qgetenv("FREI0R_PATH"));
if (!env_path.isEmpty()) effect_dirs.append(env_path);
QVector<QString> loaded_names;
// search for paths
+3 -3
View File
@@ -209,7 +209,7 @@ SetTimelineInOutCommand::SetTimelineInOutCommand(Sequence *s, bool enabled, long
void SetTimelineInOutCommand::undo() {
seq->using_workarea = old_enabled;
seq->enable_workarea = old_workarea_enabled;
seq->enable_workarea = old_workarea_enabled;
seq->workarea_in = old_in;
seq->workarea_out = old_out;
@@ -226,11 +226,11 @@ void SetTimelineInOutCommand::undo() {
void SetTimelineInOutCommand::redo() {
old_enabled = seq->using_workarea;
old_workarea_enabled = seq->enable_workarea;
old_workarea_enabled = seq->enable_workarea;
old_in = seq->workarea_in;
old_out = seq->workarea_out;
if (!seq->using_workarea) seq->enable_workarea = true;
if (!seq->using_workarea) seq->enable_workarea = true;
seq->using_workarea = new_enabled;
seq->workarea_in = new_in;
seq->workarea_out = new_out;
+1 -1
View File
@@ -174,7 +174,7 @@ public:
private:
Sequence* seq;
bool old_workarea_enabled;
bool old_workarea_enabled;
bool old_enabled;
long old_in;
+47 -63
View File
@@ -9,6 +9,8 @@
#include <QLinearGradient>
#include <QtMath>
#include <QDebug>
#define AUDIO_MONITOR_PEAK_HEIGHT 15
#define AUDIO_MONITOR_GAP 3
@@ -16,79 +18,61 @@ extern "C" {
#include "libavformat/avformat.h"
}
AudioMonitor::AudioMonitor(QWidget *parent) : QWidget(parent) {
reset();
AudioMonitor::AudioMonitor(QWidget *parent) :
QWidget(parent)
{
clear_timer.setInterval(500);
connect(&clear_timer, SIGNAL(timeout()), this, SLOT(clear()));
}
void AudioMonitor::reset() {
sample_cache_offset = -1;
sample_cache.clear();
update();
void AudioMonitor::set_value(const QVector<double> &ivalues) {
values = ivalues;
update();
QMetaObject::invokeMethod(&clear_timer, "start", Qt::QueuedConnection);
}
void AudioMonitor::clear() {
clear_timer.stop();
values.fill(1);
update();
}
void AudioMonitor::resizeEvent(QResizeEvent *e) {
gradient = QLinearGradient(QPoint(0, rect().top()), QPoint(0, rect().bottom()));
gradient.setColorAt(0, Qt::red);
gradient.setColorAt(0.25, Qt::yellow);
gradient.setColorAt(1, Qt::green);
QWidget::resizeEvent(e);
gradient = QLinearGradient(QPoint(0, rect().top()), QPoint(0, rect().bottom()));
gradient.setColorAt(0, Qt::red);
gradient.setColorAt(0.25, Qt::yellow);
gradient.setColorAt(1, Qt::green);
QWidget::resizeEvent(e);
}
void AudioMonitor::paintEvent(QPaintEvent *) {
if (sequence != nullptr) {
QPainter p(this);
int channel_x = AUDIO_MONITOR_GAP;
int channel_count = av_get_channel_layout_nb_channels(sequence->audio_layout);
/*if (peaks.size() != channel_count) {
peaks.resize(channel_count);
peaks.fill(false);
}*/
int channel_width = (width()/channel_count) - AUDIO_MONITOR_GAP;
long playhead_offset = -1;
int i;
for (i=0;i<channel_count;i++) {
QRect r(channel_x, AUDIO_MONITOR_PEAK_HEIGHT + AUDIO_MONITOR_GAP, channel_width, height());
p.fillRect(r, gradient);
if (sequence != nullptr && values.size() > 0) {
QPainter p(this);
int channel_x = AUDIO_MONITOR_GAP;
int channel_count = values.size();
int channel_width = (width()/channel_count) - AUDIO_MONITOR_GAP;
int i;
for (i=0;i<channel_count;i++) {
QRect r(channel_x, AUDIO_MONITOR_PEAK_HEIGHT + AUDIO_MONITOR_GAP, channel_width, height());
p.fillRect(r, gradient);
if (sample_cache_offset != -1 && sample_cache.size() > 0) {
playhead_offset = (sequence->playhead - sample_cache_offset) * channel_count;
if (playhead_offset >= 0 && playhead_offset < sample_cache.size()) {
double multiplier = 1 - qAbs((double) sample_cache.at(playhead_offset+i) / 32768.0); // 16-bit int divided to float
/*if (multiplier == (double) 0) {
peaks[i] = true;
}*/
r.setHeight(r.height()*multiplier);
} else {
reset();
}
}
bool peak = false;
/*QRect peak_rect(channel_x, 0, channel_width, AUDIO_MONITOR_PEAK_HEIGHT);
if (peaks.at(i)) {
p.fillRect(peak_rect, QColor(255, 0, 0));
} else {
p.fillRect(peak_rect, QColor(64, 0, 0));
}*/
r.setHeight(qRound(r.height()*(values.at(i))));
peak = (r.height() == 0);
p.fillRect(r, QColor(0, 0, 0, 160));
QRect peak_rect(channel_x, 0, channel_width, AUDIO_MONITOR_PEAK_HEIGHT);
if (peak) {
p.fillRect(peak_rect, QColor(255, 0, 0));
} else {
p.fillRect(peak_rect, QColor(64, 0, 0));
}
channel_x += channel_width + AUDIO_MONITOR_GAP;
}
if (playhead_offset > -1) {
// clean up used samples
bool error = false;
while (sample_cache_offset < sequence->playhead && !error) {
sample_cache_offset++;
for (i=0;i<channel_count;i++) {
if (sample_cache.isEmpty()) {
reset();
error = true;
break;
} else {
sample_cache.removeFirst();
}
}
}
}
}
p.fillRect(r, QColor(0, 0, 0, 160));
channel_x += channel_width + AUDIO_MONITOR_GAP;
}
}
}
+12 -9
View File
@@ -2,27 +2,30 @@
#define AUDIOMONITOR_H
#include <QWidget>
#include <QTimer>
class AudioMonitor : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
explicit AudioMonitor(QWidget *parent = 0);
QVector<qint16> sample_cache;
long sample_cache_offset;
void reset();
explicit AudioMonitor(QWidget *parent = 0);
void set_value(const QVector<double>& values);
protected:
void paintEvent(QPaintEvent *);
void resizeEvent(QResizeEvent *);
void paintEvent(QPaintEvent *);
void resizeEvent(QResizeEvent *);
signals:
public slots:
private:
QLinearGradient gradient;
// QVector<bool> peaks;
QLinearGradient gradient;
QVector<double> values;
QTimer clear_timer;
private slots:
void clear();
};
#endif // AUDIOMONITOR_H
+200
View File
@@ -0,0 +1,200 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtWidgets>
#include "flowlayout.h"
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
: m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0) {
return m_hSpace;
} else {
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
}
}
int FlowLayout::verticalSpacing() const
{
if (m_vSpace >= 0) {
return m_vSpace;
} else {
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
}
int FlowLayout::count() const
{
return itemList.size();
}
QLayoutItem *FlowLayout::itemAt(int index) const
{
return itemList.value(index);
}
QLayoutItem *FlowLayout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return 0;
}
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
doLayout(rect, false);
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
QSize FlowLayout::minimumSize() const
{
QSize size;
QLayoutItem *item;
foreach (item, itemList)
size = size.expandedTo(item->minimumSize());
size += QSize(2*margin(), 2*margin());
return size;
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
QLayoutItem *item;
foreach (item, itemList) {
QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
return y + lineHeight - rect.y() + bottom;
}
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
QObject *parent = this->parent();
if (!parent) {
return -1;
} else if (parent->isWidgetType()) {
QWidget *pw = static_cast<QWidget *>(parent);
return pw->style()->pixelMetric(pm, 0, pw);
} else {
return static_cast<QLayout *>(parent)->spacing();
}
}
+87
View File
@@ -0,0 +1,87 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
class FlowLayout : public QLayout
{
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
~FlowLayout();
void addItem(QLayoutItem *item) override;
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const override;
bool hasHeightForWidth() const override;
int heightForWidth(int) const override;
int count() const override;
QLayoutItem *itemAt(int index) const override;
QSize minimumSize() const override;
void setGeometry(const QRect &rect) override;
QSize sizeHint() const override;
QLayoutItem *takeAt(int index) override;
private:
int doLayout(const QRect &rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QList<QLayoutItem *> itemList;
int m_hSpace;
int m_vSpace;
};
#endif // FLOWLAYOUT_H
-2
View File
@@ -708,8 +708,6 @@ void GraphView::mouseReleaseEvent(QMouseEvent *) {
}
void GraphView::wheelEvent(QWheelEvent *event) {
dout << x_scroll << y_scroll;
bool redraw = false;
bool shift = (event->modifiers() & Qt::ShiftModifier); // scroll instead of zoom
+13 -10
View File
@@ -5,41 +5,44 @@
#include <QApplication>
#include <QDesktopWidget>
KeyframeNavigator::KeyframeNavigator(QWidget *parent) : QWidget(parent) {
KeyframeNavigator::KeyframeNavigator(QWidget *parent, bool addLeftPad) : QWidget(parent) {
key_controls = new QHBoxLayout();
key_controls->setSpacing(0);
key_controls->setMargin(0);
key_controls->addStretch();
if (addLeftPad) {
key_controls->addStretch();
}
setLayout(key_controls);
left_key_nav = new QPushButton();
left_key_nav->setIcon(QIcon(":/icons/tri-left.png"));
left_key_nav->setIconSize(left_key_nav->iconSize()*0.5);
left_key_nav->setIcon(QIcon(":/icons/tri-left.png"));
left_key_nav->setIconSize(left_key_nav->iconSize()*0.5);
left_key_nav->setVisible(false);
key_controls->addWidget(left_key_nav);
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_previous_key()));
connect(left_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
key_addremove = new QPushButton();
key_addremove->setIcon(QIcon(":/icons/diamond.png"));
key_addremove->setIconSize(key_addremove->iconSize()*0.5);
key_addremove->setIcon(QIcon(":/icons/diamond.png"));
key_addremove->setIconSize(key_addremove->iconSize()*0.5);
key_addremove->setVisible(false);
key_controls->addWidget(key_addremove);
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(toggle_key()));
connect(key_addremove, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
right_key_nav = new QPushButton();
right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
right_key_nav->setIconSize(right_key_nav->iconSize()*0.5);
right_key_nav->setIcon(QIcon(":/icons/tri-right.png"));
right_key_nav->setIconSize(right_key_nav->iconSize()*0.5);
right_key_nav->setVisible(false);
key_controls->addWidget(right_key_nav);
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(goto_next_key()));
connect(right_key_nav, SIGNAL(clicked(bool)), this, SIGNAL(clicked()));
keyframe_enable = new QPushButton(QIcon(":/icons/clock.png"), "");
keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
keyframe_enable->setIconSize(keyframe_enable->iconSize()*0.75);
keyframe_enable->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
keyframe_enable->setIconSize(keyframe_enable->iconSize()*0.75);
keyframe_enable->setCheckable(true);
keyframe_enable->setToolTip(tr("Enable Keyframes"));
connect(keyframe_enable, SIGNAL(clicked(bool)), this, SIGNAL(keyframe_enabled_changed(bool)));
+1 -1
View File
@@ -10,7 +10,7 @@ class KeyframeNavigator : public QWidget
{
Q_OBJECT
public:
KeyframeNavigator(QWidget* parent = 0);
KeyframeNavigator(QWidget* parent = 0, bool addLeftPad = true);
~KeyframeNavigator();
void enable_keyframes(bool);
void enable_keyframe_toggle(bool);
+13 -24
View File
@@ -119,7 +119,8 @@ GLuint compose_sequence(Viewer* viewer,
bool render_audio,
Effect** gizmos,
bool& texture_failed,
bool rendering) {
bool rendering,
int playback_speed) {
GLint current_fbo = 0;
if (video) {
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
@@ -169,7 +170,7 @@ GLuint compose_sequence(Viewer* viewer,
}
clip_is_active = true;
if (c->track >= 0) audio_track_count++;
} else if (c->open) {
} else if (c->finished_opening) {
close_clip(c, false);
}
} else {
@@ -181,7 +182,7 @@ GLuint compose_sequence(Viewer* viewer,
if (is_clip_active(c, playhead)) {
if (!c->open) open_clip(c, !rendering);
clip_is_active = true;
} else if (c->open) {
} else if (c->finished_opening) {
close_clip(c, false);
}
}
@@ -238,7 +239,7 @@ GLuint compose_sequence(Viewer* viewer,
c->texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
c->texture->allocateStorage(get_gl_pix_fmt_from_av(c->pix_fmt), QOpenGLTexture::UInt8);
}
get_clip_frame(c, playhead, texture_failed);
get_clip_frame(c, qMax(playhead, c->timeline_in), texture_failed);
textureID = c->texture->textureId();
break;
case MEDIA_TYPE_SEQUENCE:
@@ -258,6 +259,7 @@ GLuint compose_sequence(Viewer* viewer,
c->fbo = new QOpenGLFramebufferObject* [2];
c->fbo[0] = new QOpenGLFramebufferObject(video_width, video_height);
c->fbo[1] = new QOpenGLFramebufferObject(video_width, video_height);
ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
}
// clear fbos
@@ -268,6 +270,7 @@ GLuint compose_sequence(Viewer* viewer,
glClear(GL_COLOR_BUFFER_BIT);
c->fbo[1]->release();*/
bool fbo_switcher = false;
glViewport(0, 0, video_width, video_height);
@@ -284,7 +287,7 @@ GLuint compose_sequence(Viewer* viewer,
// for nested sequences
if (c->media->get_type()== MEDIA_TYPE_SEQUENCE) {
nests.append(c);
textureID = compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering);
textureID = compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering, false);
nests.removeLast();
fbo_switcher = true;
}
@@ -351,20 +354,6 @@ GLuint compose_sequence(Viewer* viewer,
}
// EFFECT CODE END
/*if (!nests.isEmpty()) {
nests.last()->fbo[0]->bind();
glViewport(0, 0, s->width, s->height);
} else if (rendering) {
glViewport(0, 0, s->width, s->height);
} else {
int widget_width = width();
int widget_height = height();
widget_width *= QApplication::desktop()->devicePixelRatio();
widget_height *= QApplication::desktop()->devicePixelRatio();
glViewport(0, 0, widget_width, widget_height);
}*/
if (!nests.isEmpty()) {
nests.last()->fbo[0]->bind();
}
@@ -449,15 +438,15 @@ GLuint compose_sequence(Viewer* viewer,
motion_blur_prog++;*/
}
} else {
if (render_audio || (config.enable_audio_scrubbing && audio_scrub)) {
if (render_audio || (config.enable_audio_scrubbing && audio_scrub && seq->playhead > c->timeline_in)) {
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
nests.append(c);
compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering);
compose_sequence(viewer, ctx, seq, nests, video, render_audio, gizmos, texture_failed, rendering, playback_speed);
nests.removeLast();
} else {
if (c->lock.tryLock()) {
// clip is not caching, start caching audio
cache_clip(c, playhead, c->audio_reset, !render_audio, nests);
cache_clip(c, playhead, c->audio_reset, !render_audio, nests, playback_speed);
c->lock.unlock();
}
}
@@ -496,8 +485,8 @@ GLuint compose_sequence(Viewer* viewer,
return 0;
}
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio) {
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio, int playback_speed) {
QVector<Clip*> nests;
bool texture_failed;
compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering);
compose_sequence(viewer, nullptr, seq, nests, false, render_audio, nullptr, texture_failed, audio_rendering, playback_speed);
}
+3 -2
View File
@@ -17,9 +17,10 @@ GLuint compose_sequence(Viewer* viewer,
bool render_audio,
Effect **gizmos,
bool &texture_failed,
bool rendering);
bool rendering,
int playback_speed);
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio);
void compose_audio(Viewer* viewer, Sequence* seq, bool render_audio, int playback_speed);
void viewport_render();
+5 -2
View File
@@ -110,7 +110,7 @@ void RenderThread::paint() {
gizmos = nullptr;
QVector<Clip*> nests;
compose_sequence(nullptr, ctx, seq, nests, true, false, &gizmos, texture_failed, false);
compose_sequence(nullptr, ctx, seq, nests, true, false, &gizmos, texture_failed, false, temp_reverse);
if (!save_fn.isEmpty()) {
if (texture_failed) {
@@ -138,12 +138,14 @@ void RenderThread::paint() {
glDisable(GL_TEXTURE_2D);
}
void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QString& save, GLvoid* pixels, int idivider) {
void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QString& save, GLvoid* pixels, int idivider, bool itemp_reverse) {
seq = s;
// stall any dependent actions
texture_failed = true;
temp_reverse = itemp_reverse;
if (share != nullptr && (ctx == nullptr || ctx->shareContext() != share_ctx)) {
share_ctx = share;
delete_ctx();
@@ -158,6 +160,7 @@ void RenderThread::start_render(QOpenGLContext *share, Sequence *s, const QStrin
pixel_buffer = pixels;
queued = true;
waitCond.wakeAll();
}
+2 -1
View File
@@ -22,7 +22,7 @@ public:
GLuint texColorBuffer;
Effect* gizmos;
void paint();
void start_render(QOpenGLContext* share, Sequence* s, const QString &save = nullptr, GLvoid *pixels = nullptr, int idivider = 0);
void start_render(QOpenGLContext* share, Sequence* s, const QString &save = nullptr, GLvoid *pixels = nullptr, int idivider = 0, bool itemp_reverse = false);
bool did_texture_fail();
void cancel();
@@ -47,6 +47,7 @@ private:
bool queued;
bool texture_failed;
bool running;
bool temp_reverse;
QString save_fn;
GLvoid *pixel_buffer;
};
+9
View File
@@ -22,6 +22,7 @@
#include "dialogs/newsequencedialog.h"
#include "mainwindow.h"
#include "ui/rectangleselect.h"
#include "playback/playback.h"
#include "debug.h"
#include "project/effect.h"
@@ -548,6 +549,14 @@ void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *event) {
sequence->selections.append(s);
update_ui(false);
}
} else if (panel_timeline->tool == TIMELINE_TOOL_POINTER) {
int clip_index = getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track);
if (clip_index >= 0) {
Clip* c = sequence->clips.at(clip_index);
if (c->media != nullptr && c->media->get_type() == MEDIA_TYPE_SEQUENCE) {
set_sequence(c->media->to_sequence());
}
}
}
}
+1 -1
View File
@@ -230,7 +230,7 @@ void ViewerWidget::frame_update() {
}
// render the audio
compose_audio(viewer, viewer->seq, render_audio);
compose_audio(viewer, viewer->seq, render_audio, viewer->get_playback_speed());
}
}