Commit 803bb02e authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Audio thumbnails are back.

parent 99b0c48e
......@@ -2068,3 +2068,8 @@ void Bin::slotResetInfoMessage()
}
}
void Bin::emitMessage(const QString &text, MessageType type)
{
emit displayMessage(text, type);
}
......@@ -415,6 +415,8 @@ public:
void editMasterEffect(ClipController *ctl);
/** @brief Returns current project's folder for storing items. */
QUrl projectFolder() const;
/** @brief Display a message about an operation in status bar. */
void emitMessage(const QString &, MessageType);
private slots:
void slotAddClip();
......@@ -553,6 +555,7 @@ signals:
/** @brief Trigger timecode format refresh where needed. */
void refreshTimeCode();
void masterClipSelected(ClipController *, Monitor *);
void displayMessage(const QString &, MessageType);
};
#endif
......@@ -29,6 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "kdenlivesettings.h"
#include "project/projectcommands.h"
#include "mltcontroller/clipcontroller.h"
#include "lib/audio/audioStreamInfo.h"
#include "mltcontroller/clippropertiescontroller.h"
#include <QDomElement>
......@@ -36,7 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QDir>
#include <QDebug>
#include <QCryptographicHash>
#include <QtConcurrent>
#include <KLocalizedString>
......@@ -47,6 +48,7 @@ ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *control
, audioFrameCache()
, m_audioThumbCreated(false)
, m_gpuProducer(NULL)
, m_abortAudioThumb(false)
{
m_clipStatus = StatusReady;
m_thumbnail = thumb;
......@@ -55,6 +57,9 @@ ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *control
getFileHash();
setParent(parent);
bin()->loadSubClips(id, m_controller->getSubClips());
if (KdenliveSettings::audiothumbnails()) {
QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
}
}
ProjectClip::ProjectClip(const QDomElement& description, QIcon thumb, ProjectFolder* parent) :
......@@ -63,6 +68,7 @@ ProjectClip::ProjectClip(const QDomElement& description, QIcon thumb, ProjectFol
, audioFrameCache()
, m_audioThumbCreated(false)
, m_gpuProducer(NULL)
, m_abortAudioThumb(false)
{
Q_ASSERT(description.hasAttribute("id"));
m_clipStatus = StatusWaiting;
......@@ -267,6 +273,7 @@ void ProjectClip::setProducer(ClipController *controller, bool replaceProducer)
m_clipStatus = StatusReady;
bin()->emitItemUpdated(this);
getFileHash();
if (KdenliveSettings::audiothumbnails()) QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
}
Mlt::Producer *ProjectClip::producer()
......@@ -502,6 +509,7 @@ void ProjectClip::setJobStatus(AbstractClipJob::JOBTYPE jobType, ClipJobStatus s
m_jobProgress = status;
if ((status == JobAborted || status == JobCrashed || status == JobDone) || !statusMessage.isEmpty()) {
m_jobMessage = statusMessage;
bin()->emitMessage(statusMessage, OperationCompletedMessage);
}
}
bin()->emitItemUpdated(this);
......@@ -651,3 +659,126 @@ void ProjectClip::slotExtractImage(QList <int> frames)
delete frame;
}
}
void ProjectClip::slotCreateAudioThumbs()
{
Mlt::Producer *prod = producer();
AudioStreamInfo *audioInfo = m_controller->audioInfo();
if (audioInfo == NULL) return;
QString clipHash = hash();
if (clipHash.isEmpty()) return;
QString audioPath = bin()->projectFolder().path() + "/thumbs/" + clipHash + ".thumb";
double lengthInFrames = prod->get_playtime();
int frequency = audioInfo->samplingRate();
if (frequency <= 0) frequency = 48000;
int channels = audioInfo->channels();
if (channels <= 0) channels = 2;
int arrayWidth = 20;
double frame = 0.0;
int maxVolume = 0;
audioByteArray storeIn;
QFile f(audioPath);
if (QFileInfo(audioPath).size() > 0 && f.open(QIODevice::ReadOnly)) {
bool reading = true;
const QByteArray channelarray = f.readAll();
f.close();
if (channelarray.size() != arrayWidth*(frame + lengthInFrames) * channels) {
//qDebug() << "--- BROKEN THUMB FOR: " << url.fileName() << " ---------------------- ";
f.remove();
reading = false;
}
//qDebug() << "reading audio thumbs from file";
if (reading) {
int h1 = arrayWidth * channels;
int h2 = (int) frame * h1;
for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !m_abortAudioThumb; ++z) {
int h3 = 0;
for (int c = 0; c < channels; ++c) {
QByteArray audioArray(arrayWidth, '\x00');
for (int i = 0; i < arrayWidth; ++i) {
audioArray[i] = channelarray.at(h2 + h3 + i);
if (audioArray.at(i) > maxVolume) maxVolume = audioArray.at(i);
}
h3 += arrayWidth;
storeIn[z][c] = audioArray;
}
h2 += h1;
}
if (!m_abortAudioThumb) {
setProducerProperty("audio_max", QString::number(maxVolume - 64));
updateAudioThumbnail(storeIn);
}
return;
}
}
if (!f.open(QIODevice::WriteOnly)) {
//qDebug() << "++++++++ ERROR WRITING TO FILE: " << audioPath;
//qDebug() << "++++++++ DISABLING AUDIO THUMBS";
KdenliveSettings::setAudiothumbnails(false);
return;
}
QString service = prod->get("mlt_service");
Mlt::Producer *audioProducer = new Mlt::Producer(*prod->profile(), service.toUtf8().constData(), prod->get("resource"));
if (!audioProducer->is_valid()) {
//qDebug() << "++++++++ INVALID CLIP: " << url.path();
delete audioProducer;
return;
}
audioProducer->set("video_index", "-1");
//if (KdenliveSettings::normaliseaudiothumbs()) {
//Mlt::Filter m_convert(prof, "volume");
//m_convert.set("gain", "normalise");
//producer.attach(m_convert);
//}
int last_val = 0;
setJobStatus(AbstractClipJob::THUMBJOB, JobWaiting, 0, i18n("Creating audio thumbnails"));
double framesPerSecond = audioProducer->get_fps();
mlt_audio_format audioFormat = mlt_audio_s16;
for (int z = (int) frame; z < (int)(frame + lengthInFrames) && !m_abortAudioThumb; ++z) {
int val = (int)((z - frame) / (frame + lengthInFrames) * 100.0);
if (last_val != val && val > 1) {
setJobStatus(AbstractClipJob::THUMBJOB, JobWorking, val);
last_val = val;
}
audioProducer->seek(z);
Mlt::Frame *mlt_frame = audioProducer->get_frame();
if (mlt_frame && mlt_frame->is_valid()) {
int samples = mlt_sample_calculator(framesPerSecond, frequency, mlt_frame->get_position());
qint16* pcm = static_cast<qint16*>(mlt_frame->get_audio(audioFormat, frequency, channels, samples));
for (int c = 0; c < channels; ++c) {
QByteArray audioArray;
audioArray.resize(arrayWidth);
for (int i = 0; i < audioArray.size(); ++i) {
double pcmval = *(pcm + c + i * samples / audioArray.size());
if (pcmval >= 0) {
pcmval = sqrt(pcmval) / 2.83 + 64;
audioArray[i] = pcmval;
if (pcmval > maxVolume) maxVolume = pcmval;
}
else {
pcmval = -sqrt(-pcmval) / 2.83 + 64;
audioArray[i] = pcmval;
if (-pcmval > maxVolume) maxVolume = -pcmval;
}
}
f.write(audioArray);
storeIn[z][c] = audioArray;
}
} else {
f.write(QByteArray(arrayWidth, '\x00'));
}
delete mlt_frame;
}
f.close();
delete audioProducer;
setJobStatus(AbstractClipJob::THUMBJOB, JobDone, 0, i18n("Audio thumbnails done"));
if (m_abortAudioThumb) {
f.remove();
} else {
updateAudioThumbnail(storeIn);
setProducerProperty("audio_max", QString::number(maxVolume - 64));
}
}
......@@ -193,9 +193,9 @@ public:
void removeEffect(const ProfileInfo pInfo, int ix);
public slots:
//TODO
void updateAudioThumbnail(const audioByteArray& data);
void slotExtractImage(QList <int> frames);
void slotCreateAudioThumbs();
protected:
bool m_hasLimitedDuration;
......@@ -211,6 +211,7 @@ private:
bool m_audioThumbCreated;
/** @brief Store clip url temporarily while the clip controller has not been created. */
QUrl m_temporaryUrl;
bool m_abortAudioThumb;
signals:
void gotAudioData();
......
......@@ -1582,6 +1582,8 @@ void MainWindow::connectDocument()
connect(trackView->projectView(), SIGNAL(zoomOut()), this, SLOT(slotZoomOut()));
connect(trackView, SIGNAL(setZoom(int)), this, SLOT(slotSetZoom(int)));
connect(trackView->projectView(), SIGNAL(displayMessage(QString,MessageType)), m_messageLabel, SLOT(setMessage(QString,MessageType)));
connect(pCore->bin(), SIGNAL(displayMessage(QString,MessageType)), m_messageLabel, SLOT(setMessage(QString,MessageType)));
//connect(trackView->projectView(), SIGNAL(showClipFrame(DocClipBase*,QPoint,bool,int)), m_clipMonitor, SLOT(slotSetClipProducer(DocClipBase*,QPoint,bool,int)));
connect(trackView->projectView(), SIGNAL(playMonitor()), m_projectMonitor, SLOT(slotPlay()));
......
......@@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "clipcontroller.h"
#include "bincontroller.h"
#include "mltcontroller/effectscontroller.h"
#include "lib/audio/audioStreamInfo.h"
#include "timeline/timeline.h"
#include "renderer.h"
......@@ -38,6 +39,7 @@ ClipController::ClipController(BinController *bincontroller, Mlt::Producer& prod
, m_hasLimitedDuration(true)
, m_properties(new Mlt::Properties(producer.get_properties()))
, selectedEffectIndex(1)
, m_audioInfo(NULL)
{
m_masterProducer = &producer;
m_effectList = EffectsList(true);
......@@ -65,6 +67,7 @@ ClipController::ClipController(BinController *bincontroller) : QObject()
, m_clipType(Unknown)
, m_properties(NULL)
, selectedEffectIndex(1)
, m_audioInfo(NULL)
{
m_masterProducer = NULL;
m_effectList = EffectsList(true);
......@@ -79,6 +82,11 @@ double ClipController::dar() const
return m_binController->dar();
}
AudioStreamInfo *ClipController::audioInfo() const
{
return m_audioInfo;
}
void ClipController::addMasterProducer(Mlt::Producer &producer)
{
m_properties = new Mlt::Properties(producer.get_properties());
......@@ -140,6 +148,7 @@ void ClipController::getInfoForProducer()
m_clipType = WebVfx;
}
else m_clipType = Unknown;
if (m_audioIndex > -1) m_audioInfo = new AudioStreamInfo(m_masterProducer, m_audioIndex);
}
bool ClipController::hasLimitedDuration() const
......@@ -648,3 +657,7 @@ bool ClipController::hasEffects() const
return !m_effectList.isEmpty();
}
......@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class QPixmap;
class BinController;
class AudioStreamInfo;
/**
* @class ClipController
......@@ -167,10 +168,13 @@ public:
void changeEffectState(const QList <int> indexes, bool disable);
void updateEffect(const ProfileInfo pInfo, const QDomElement &old, const QDomElement &e, int ix);
bool hasEffects() const;
/** @brief Returns info about clip audio */
AudioStreamInfo *audioInfo() const;
private:
Mlt::Producer *m_masterProducer;
Mlt::Properties *m_properties;
AudioStreamInfo *m_audioInfo;
EffectsList m_effectList;
QString m_service;
QUrl m_url;
......
......@@ -37,7 +37,7 @@ class AbstractClipJob : public QObject
Q_OBJECT
public:
enum JOBTYPE { NOJOBTYPE = 0, PROXYJOB = 1, CUTJOB = 2, MLTJOB = 3, TRANSCODEJOB = 4, FILTERCLIPJOB = 5 };
enum JOBTYPE { NOJOBTYPE = 0, PROXYJOB = 1, CUTJOB = 2, MLTJOB = 3, TRANSCODEJOB = 4, FILTERCLIPJOB = 5, THUMBJOB = 5 };
AbstractClipJob(JOBTYPE type, ClipType cType, const QString &id);
virtual ~ AbstractClipJob();
ClipType clipType;
......
......@@ -103,13 +103,9 @@ ClipItem::ClipItem(ProjectClip *clip, const ItemInfo& info, double fps, double s
connect(&m_startThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetStartThumb()));
m_endThumbTimer.setSingleShot(true);
connect(&m_endThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetEndThumb()));
//connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int,QImage)), this, SLOT(slotThumbReady(int,QImage)));
connect(m_binClip, SIGNAL(thumbReady(int,QImage)), this, SLOT(slotThumbReady(int,QImage)));
//connect(m_clip->thumbProducer(), SIGNAL(thumbsCached()), this, SLOT(slotRefreshClip()));
//connect(m_clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
if (generateThumbs) QTimer::singleShot(200, this, SLOT(slotFetchThumbs()));
}
} else if (m_clipType == Color) {
m_baseColor = m_binClip->getProducerColorProperty("resource");
} else if (m_clipType == Image || m_clipType == Text) {
......@@ -117,8 +113,8 @@ ClipItem::ClipItem(ProjectClip *clip, const ItemInfo& info, double fps, double s
//connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int,QImage)), this, SLOT(slotThumbReady(int,QImage)));
} else if (m_clipType == Audio) {
m_baseColor = QColor(141, 215, 166);
connect(m_binClip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
}
connect(m_binClip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
m_paintColor = m_baseColor;
}
......@@ -503,7 +499,6 @@ void ClipItem::slotFetchThumbs()
frames.append((int)(m_speedIndependantInfo.cropStart + m_speedIndependantInfo.cropDuration).frames(m_fps) - 1);
}
//TODO
if (!frames.isEmpty()) {
QtConcurrent::run(m_binClip, &ProjectClip::slotExtractImage, frames);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment