Improved audio workflow for multi stream clips

Related to #382
parent 35b18d07
Pipeline #19657 passed with stage
in 10 minutes and 10 seconds
......@@ -501,7 +501,7 @@ signals:
/** @brief A clip was updated, request panel update. */
void refreshPanel(const QString &id);
/** @brief Upon selection, activate timeline target tracks. */
void setupTargets(bool hasVideo, QList <int> audioStreams);
void setupTargets(bool hasVideo, QMap <int, QString> audioStreams);
/** @brief A drag event ended, inform timeline. */
void processDragEnd();
};
......
This diff is collapsed.
......@@ -171,7 +171,6 @@ public:
/** Cache for every audio Frame with 10 Bytes */
/** format is frame -> channel ->bytes */
QVector<uint8_t> audioFrameCache;
bool audioThumbCreated() const;
void setWaitingStatus(const QString &id);
......@@ -188,7 +187,7 @@ public:
/** @brief Delete cached audio thumb - needs to be recreated */
void discardAudioThumb();
/** @brief Get path for this clip's audio thumbnail */
const QString getAudioThumbPath(bool miniThumb = false);
const QString getAudioThumbPath(int stream, bool miniThumb = false);
/** @brief Returns true if this producer has audio and can be splitted on timeline*/
bool isSplittable() const;
......@@ -201,7 +200,7 @@ public:
/** @brief This function returns a cut to the master producer associated to the timeline clip with given ID.
Each clip must have a different master producer (see comment of the class)
*/
std::shared_ptr<Mlt::Producer> getTimelineProducer(int trackId, int clipId, PlaylistState::ClipState st, double speed = 1.0);
std::shared_ptr<Mlt::Producer> getTimelineProducer(int trackId, int clipId, PlaylistState::ClipState st, int audioStream = -1, double speed = 1.0);
/* @brief This function should only be used at loading. It takes a producer that was read from mlt, and checks whether the master producer is already in
use. If yes, then we must create a new one, because of the mixing bug. In any case, we return a cut of the master that can be used in the timeline The
......@@ -209,8 +208,7 @@ public:
- if true, then the returned cut still possibly has effect on it. You need to rebuild the effectStack based on this
- if false, then the returned cut don't have effects anymore (it's a fresh one), so you need to reload effects from the old producer
*/
std::pair<std::shared_ptr<Mlt::Producer>, bool> giveMasterAndGetTimelineProducer(int clipId, std::shared_ptr<Mlt::Producer> master,
PlaylistState::ClipState state);
std::pair<std::shared_ptr<Mlt::Producer>, bool> giveMasterAndGetTimelineProducer(int clipId, std::shared_ptr<Mlt::Producer> master, PlaylistState::ClipState state, int tid);
std::shared_ptr<Mlt::Producer> cloneProducer(bool removeEffects = false);
static std::shared_ptr<Mlt::Producer> cloneProducer(const std::shared_ptr<Mlt::Producer> &producer);
......@@ -225,6 +223,12 @@ public:
/** @brief Display Bin thumbnail given a percent
*/
void getThumbFromPercent(int percent);
/** @brief Return audio cache for a stream
*/
QVector <uint8_t> audioFrameCache(int stream = -1);
/** @brief Return FFmpeg's audio stream index for an MLT audio stream index
*/
int getAudioStreamFfmpegIndex(int mltStream);
protected:
friend class ClipModel;
......@@ -250,7 +254,7 @@ protected:
public slots:
/* @brief Store the audio thumbnails once computed. Note that the parameter is a value and not a reference, fill free to use it as a sink (use std::move to
* avoid copy). */
void updateAudioThumbnail(const QVector<uint8_t> audioLevels);
void updateAudioThumbnail();
/** @brief Delete the proxy file */
void deleteProxy();
......
......@@ -390,16 +390,16 @@ std::shared_ptr<ProjectClip> ProjectItemModel::getClipByBinID(const QString &bin
return nullptr;
}
const QVector<uint8_t> ProjectItemModel::getAudioLevelsByBinID(const QString &binId)
const QVector<uint8_t> ProjectItemModel::getAudioLevelsByBinID(const QString &binId, int stream)
{
READ_LOCK();
if (binId.contains(QLatin1Char('_'))) {
return getAudioLevelsByBinID(binId.section(QLatin1Char('_'), 0, 0));
return getAudioLevelsByBinID(binId.section(QLatin1Char('_'), 0, 0), stream);
}
for (const auto &clip : m_allItems) {
auto c = std::static_pointer_cast<AbstractProjectItem>(clip.second.lock());
if (c->itemType() == AbstractProjectItem::ClipItem && c->clipId() == binId) {
return std::static_pointer_cast<ProjectClip>(c)->audioFrameCache;
return std::static_pointer_cast<ProjectClip>(c)->audioFrameCache(stream);
}
}
return QVector<uint8_t>();
......
......@@ -68,7 +68,7 @@ public:
/** @brief Returns a clip from the hierarchy, given its id */
std::shared_ptr<ProjectClip> getClipByBinID(const QString &binId);
/** @brief Returns audio levels for a clip from its id */
const QVector <uint8_t>getAudioLevelsByBinID(const QString &binId);
const QVector <uint8_t>getAudioLevelsByBinID(const QString &binId, int stream);
/** @brief Returns a list of clips using the given url */
QStringList getClipByUrl(const QFileInfo &url) const;
......
This diff is collapsed.
......@@ -24,12 +24,42 @@ AudioStreamInfo::AudioStreamInfo(const std::shared_ptr<Mlt::Producer> &producer,
{
// Fetch audio streams
int streams = producer->get_int("meta.media.nb_streams");
int streamIndex = 1;
for (int ix = 0; ix < streams; ix++) {
char property[200];
snprintf(property, sizeof(property), "meta.media.%d.stream.type", ix);
QString type = producer->get(property);
if (type == QLatin1String("audio")) {
m_audioStreams << ix;
memset(property, 0, 200);
snprintf(property, sizeof(property), "meta.media.%d.codec.channels", ix);
int chan = producer->get_int(property);
QString channelDescription = QString("%1|").arg(streamIndex++);
switch (chan) {
case 1:
channelDescription.append(i18n("Mono "));
break;
case 2:
channelDescription.append(i18n("Stereo "));
break;
default:
channelDescription.append(i18n("%1 channels ", chan));
break;
}
// Frequency
memset(property, 0, 200);
snprintf(property, sizeof(property), "meta.media.%d.codec.sample_rate", ix);
QString frequency(producer->get(property));
if (frequency.endsWith(QLatin1String("000"))) {
frequency.chop(3);
frequency.append(i18n("kHz "));
} else {
frequency.append(i18n("Hz "));
}
channelDescription.append(frequency);
memset(property, 0, 200);
snprintf(property, sizeof(property), "meta.media.%d.codec.name", ix);
channelDescription.append(producer->get(property));
m_audioStreams.insert(ix, channelDescription);
}
}
if (audioStreamIndex > -1) {
......@@ -62,7 +92,7 @@ int AudioStreamInfo::channels() const
return m_channels;
}
QList <int> AudioStreamInfo::streams() const
QMap <int, QString> AudioStreamInfo::streams() const
{
return m_audioStreams;
}
......@@ -107,41 +137,3 @@ void AudioStreamInfo::setAudioIndex(const std::shared_ptr<Mlt::Producer> &produc
}
}
QMap<int, QString> AudioStreamInfo::streamInfo(Mlt::Properties sourceProperties)
{
QMap<int, QString> streamInfo;
char property[200];
for (int ix : m_audioStreams) {
memset(property, 0, 200);
snprintf(property, sizeof(property), "meta.media.%d.codec.channels", ix);
int chan = sourceProperties.get_int(property);
QString channelDescription;
switch (chan) {
case 1:
channelDescription = i18n("Mono ");
break;
case 2:
channelDescription = i18n("Stereo ");
break;
default:
channelDescription = i18n("%1 channels ", chan);
break;
}
// Frequency
memset(property, 0, 200);
snprintf(property, sizeof(property), "meta.media.%d.codec.sample_rate", ix);
QString frequency(sourceProperties.get(property));
if (frequency.endsWith(QLatin1String("000"))) {
frequency.chop(3);
frequency.append(i18n("kHz "));
} else {
frequency.append(i18n("Hz "));
}
channelDescription.append(frequency);
memset(property, 0, 200);
snprintf(property, sizeof(property), "meta.media.%d.codec.name", ix);
channelDescription.append(sourceProperties.get(property));
streamInfo.insert(ix, channelDescription);
}
return streamInfo;
}
......@@ -12,7 +12,7 @@ the Free Software Foundation, either version 3 of the License, or
#define AUDIOSTREAMINFO_H
#include <QString>
#include <QList>
#include <QMap>
#include <memory>
#include <mlt++/Mlt.h>
......@@ -28,7 +28,7 @@ public:
int samplingRate() const;
int channels() const;
QList <int> streams() const;
QMap <int, QString> streams() const;
int bitrate() const;
const QString &samplingFormat() const;
int audio_index() const;
......@@ -39,7 +39,7 @@ public:
private:
int m_audioStreamIndex;
QList <int> m_audioStreams;
QMap <int, QString> m_audioStreams;
int m_ffmpegAudioIndex;
int m_samplingRate;
int m_channels;
......
......@@ -267,7 +267,7 @@ void MainWindow::init()
connect(m_clipMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
connect(pCore->bin(), &Bin::findInTimeline, this, &MainWindow::slotClipInTimeline, Qt::DirectConnection);
connect(pCore->bin(), &Bin::setupTargets, this, [&] (bool hasVideo, QList <int> audioStreams) {
connect(pCore->bin(), &Bin::setupTargets, this, [&] (bool hasVideo, QMap <int, QString> audioStreams) {
getCurrentTimeline()->controller()->setTargetTracks(hasVideo, audioStreams);
}
);
......
......@@ -1032,7 +1032,7 @@ void ClipController::refreshAudioInfo()
}
}
QList <int> ClipController::audioStreams() const
QMap <int, QString> ClipController::audioStreams() const
{
if (m_audioInfo) {
return m_audioInfo->streams();
......
......@@ -208,7 +208,7 @@ public:
bool addEffect(const QString &effectId);
/** @brief Returns the list of audio streams indexes for this clip */
QList <int> audioStreams() const;
QMap <int, QString> audioStreams() const;
/** @brief Returns the count of audio streams for this clip */
int audioStreamsCount() const;
......
......@@ -595,10 +595,7 @@ ClipPropertiesController::ClipPropertiesController(ClipController *controller, Q
}
// Audio index
QMap<int, QString> audioStreamsInfo;
if (m_controller->audioInfo()) {
audioStreamsInfo = m_controller->audioInfo()->streamInfo(m_sourceProperties);
}
QMap<int, QString> audioStreamsInfo = m_controller->audioStreams();
if (!audioStreamsInfo.isEmpty()) {
QString vix = m_sourceProperties.get("audio_index");
m_originalProperties.insert(QStringLiteral("audio_index"), vix);
......@@ -617,7 +614,7 @@ ClipPropertiesController::ClipPropertiesController(ClipController *controller, Q
QMapIterator<int, QString> i(audioStreamsInfo);
while (i.hasNext()) {
i.next();
m_audioStream->addItem(QString("%1: %2").arg(i.key()).arg(i.value()), i.key());
m_audioStream->addItem(i.value(), i.key());
}
if (m_audioStream->count() > 1) {
m_audioStream->addItem(i18n("Merge all streams"), INT_MAX);
......
......@@ -1423,7 +1423,7 @@ void Monitor::slotOpenClip(const std::shared_ptr<ProjectClip> &controller, int i
delete m_audioChannelsGroup;
m_audioChannelsGroup = nullptr;
if (m_controller->audioInfo()) {
QMap<int, QString> audioStreamsInfo = m_controller->audioInfo()->streamInfo(m_controller->properties());
QMap<int, QString> audioStreamsInfo = m_controller->audioStreams();
if (audioStreamsInfo.size() > 1) {
// Multi stream clip
m_audioChannelsGroup = new QActionGroup(this);
......@@ -1432,7 +1432,7 @@ void Monitor::slotOpenClip(const std::shared_ptr<ProjectClip> &controller, int i
QAction *ac;
while (i.hasNext()) {
i.next();
ac = m_audioChannels->addAction(QString("%1: %2").arg(i.key()).arg(i.value()));
ac = m_audioChannels->addAction(i.value());
ac->setData(i.key());
ac->setCheckable(true);
if (i.key() == activeStream) {
......
......@@ -328,7 +328,7 @@ bool constructTrackFromMelt(const std::shared_ptr<TimelineItemModel> &timeline,
int cid = -1;
if (pCore->bin()->getBinClip(binId)) {
PlaylistState::ClipState st = inferState(clip, audioTrack);
cid = ClipModel::construct(timeline, binId, clip, st);
cid = ClipModel::construct(timeline, binId, clip, st, tid);
ok = timeline->requestClipMove(cid, tid, position, true, true, false, true, undo, redo);
} else {
qDebug() << "// Cannot find bin clip: " << binId << " - " << clip->get("id");
......
......@@ -69,7 +69,7 @@ ClipModel::ClipModel(const std::shared_ptr<TimelineModel> &parent, std::shared_p
});
}
int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id, PlaylistState::ClipState state, double speed, bool warp_pitch)
int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id, PlaylistState::ClipState state, int audioStream, double speed, bool warp_pitch)
{
id = (id == -1 ? TimelineModel::getNextId() : id);
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(binClipId);
......@@ -79,11 +79,13 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
videoAudio.first = videoAudio.first && binClip->hasVideo();
videoAudio.second = videoAudio.second && binClip->hasAudio();
state = stateFromBool(videoAudio);
std::shared_ptr<Mlt::Producer> cutProducer = binClip->getTimelineProducer(-1, id, state, speed);
qDebug()<<"// GET TIMELINE PROD FOR STREAM: "<<audioStream;
std::shared_ptr<Mlt::Producer> cutProducer = binClip->getTimelineProducer(-1, id, state, audioStream, speed);
std::shared_ptr<ClipModel> clip(new ClipModel(parent, cutProducer, binClipId, id, state, speed));
if (!qFuzzyCompare(speed, 1.)) {
cutProducer->parent().set("warp_pitch", warp_pitch ? 1 : 0);
}
qDebug()<<"==== BUILT CLIP STREAM: "<<clip->audioStream();
TRACE_CONSTR(clip.get(), parent, binClipId, id, state, speed);
clip->setClipState_lambda(state)();
parent->registerClip(clip);
......@@ -97,7 +99,7 @@ void ClipModel::allSnaps(std::vector<int> &snaps)
}
int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, const std::shared_ptr<Mlt::Producer> &producer,
PlaylistState::ClipState state)
PlaylistState::ClipState state, int tid)
{
// we hand the producer to the bin clip, and in return we get a cut to a good master producer
......@@ -119,7 +121,7 @@ int ClipModel::construct(const std::shared_ptr<TimelineModel> &parent, const QSt
speed = producer->parent().get_double("warp_speed");
warp_pitch = producer->parent().get_int("warp_pitch");
}
auto result = binClip->giveMasterAndGetTimelineProducer(id, producer, state);
auto result = binClip->giveMasterAndGetTimelineProducer(id, producer, state, tid);
std::shared_ptr<ClipModel> clip(new ClipModel(parent, result.first, binClipId, id, state, speed));
if (warp_pitch) {
result.first->parent().set("warp_pitch", 1);
......@@ -411,7 +413,7 @@ bool ClipModel::isAudioOnly() const
return m_currentState == PlaylistState::AudioOnly;
}
void ClipModel::refreshProducerFromBin(int trackId, PlaylistState::ClipState state, double speed, bool hasPitch)
void ClipModel::refreshProducerFromBin(int trackId, PlaylistState::ClipState state, int stream, double speed, bool hasPitch)
{
// We require that the producer is not in the track when we refresh the producer, because otherwise the modification will not be propagated. Remove the clip
// first, refresh, and then replant.
......@@ -427,7 +429,7 @@ void ClipModel::refreshProducerFromBin(int trackId, PlaylistState::ClipState sta
qDebug() << "changing speed" << in << out << m_speed;
}
std::shared_ptr<ProjectClip> binClip = pCore->projectItemModel()->getClipByBinID(m_binClipId);
std::shared_ptr<Mlt::Producer> binProducer = binClip->getTimelineProducer(trackId, m_id, state, m_speed);
std::shared_ptr<Mlt::Producer> binProducer = binClip->getTimelineProducer(trackId, m_id, state, stream, m_speed);
m_producer = std::move(binProducer);
m_producer->set_in_and_out(in, out);
if (hasPitch) {
......@@ -452,7 +454,8 @@ void ClipModel::refreshProducerFromBin(int trackId)
if (!qFuzzyCompare(getSpeed(), 1.)) {
hasPitch = m_producer->parent().get_int("warp_pitch") == 1;
}
refreshProducerFromBin(trackId, m_currentState, 0, hasPitch);
int stream = m_producer->parent().get_int("audio_index");
refreshProducerFromBin(trackId, m_currentState, stream, 0, hasPitch);
}
bool ClipModel::useTimewarpProducer(double speed, bool pitchCompensate, bool changeDuration, Fun &undo, Fun &redo)
......@@ -477,8 +480,9 @@ bool ClipModel::useTimewarpProducer(double speed, bool pitchCompensate, bool cha
revertSpeed = true;
}
bool hasPitch = getIntProperty(QStringLiteral("warp_pitch"));
auto operation = useTimewarpProducer_lambda(speed, pitchCompensate);
auto reverse = useTimewarpProducer_lambda(previousSpeed, hasPitch);
int audioStream = getIntProperty(QStringLiteral("audio_index"));
auto operation = useTimewarpProducer_lambda(speed, audioStream, pitchCompensate);
auto reverse = useTimewarpProducer_lambda(previousSpeed, audioStream, hasPitch);
if (revertSpeed || (changeDuration && oldOut >= newDuration)) {
// in that case, we are going to shrink the clip when changing the producer. We must undo that when reloading the old producer
reverse = [reverse, oldIn, oldOut, this]() {
......@@ -526,12 +530,12 @@ bool ClipModel::useTimewarpProducer(double speed, bool pitchCompensate, bool cha
return false;
}
Fun ClipModel::useTimewarpProducer_lambda(double speed, bool pitchCompensate)
Fun ClipModel::useTimewarpProducer_lambda(double speed, int stream, bool pitchCompensate)
{
QWriteLocker locker(&m_lock);
return [speed, pitchCompensate, this]() {
return [speed, stream, pitchCompensate, this]() {
qDebug() << "timeWarp producer" << speed;
refreshProducerFromBin(m_currentTrackId, m_currentState, speed, pitchCompensate);
refreshProducerFromBin(m_currentTrackId, m_currentState, stream, speed, pitchCompensate);
return true;
};
}
......@@ -553,6 +557,15 @@ int ClipModel::audioChannels() const
return pCore->projectItemModel()->getClipByBinID(m_binClipId)->audioChannels();
}
int ClipModel::audioStream() const
{
READ_LOCK();
if (pCore->projectItemModel()->getClipByBinID(m_binClipId)->audioStreamsCount() > 1) {
return m_producer->parent().get_int("audio_index");
}
return -m_producer->parent().get_int("audio_index");
}
int ClipModel::fadeIn() const
{
return m_effectStack->getFadePosition(true);
......@@ -745,6 +758,7 @@ QDomElement ClipModel::toXml(QDomDocument &document)
}
}
container.setAttribute(QStringLiteral("speed"), locale.toString(m_speed));
container.setAttribute(QStringLiteral("audioStream"), getIntProperty(QStringLiteral("audio_index")));
if (!qFuzzyCompare(m_speed, 1.)) {
container.setAttribute(QStringLiteral("warp_pitch"), getIntProperty(QStringLiteral("warp_pitch")));
}
......
......@@ -57,7 +57,7 @@ public:
@param binClip is the id of the bin clip associated
@param id Requested id of the clip. Automatic if -1
*/
static int construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id, PlaylistState::ClipState state, double speed = 1., bool warp_pitch = false);
static int construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, int id, PlaylistState::ClipState state, int audioStream = -1, double speed = 1., bool warp_pitch = false);
/* @brief Creates a clip, which references itself to the parent timeline
Returns the (unique) id of the created clip
......@@ -65,7 +65,7 @@ public:
Note that there is no guarantee that this producer is actually going to be used. It might be discarded.
*/
static int construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, const std::shared_ptr<Mlt::Producer> &producer,
PlaylistState::ClipState state);
PlaylistState::ClipState state, int tid);
/** @brief returns a property of the clip, or from it's parent if it's a cut
*/
......@@ -179,7 +179,7 @@ protected:
* @param speed corresponds to the speed we need. Leave to 0 to keep current speed. Warning: this function doesn't notify the model. Unless you know what
* you are doing, better use useTimewarProducer to change the speed
*/
void refreshProducerFromBin(int trackId, PlaylistState::ClipState state, double speed, bool hasPitch);
void refreshProducerFromBin(int trackId, PlaylistState::ClipState state, int stream, double speed, bool hasPitch);
void refreshProducerFromBin(int trackId);
/* @brief This functions replaces the current producer with a slowmotion one
......@@ -187,13 +187,14 @@ protected:
*/
bool useTimewarpProducer(double speed, bool pitchCompensate, bool changeDuration, Fun &undo, Fun &redo);
// @brief Lambda that merely changes the speed (in and out are untouched)
Fun useTimewarpProducer_lambda(double speed, bool pitchCompensate);
Fun useTimewarpProducer_lambda(double speed, int stream, bool pitchCompensate);
/** @brief Returns the marker model associated with this clip */
std::shared_ptr<MarkerListModel> getMarkerModel() const;
/** @brief Returns the number of audio channels for this clip */
int audioChannels() const;
int audioStream() const;
bool audioEnabled() const;
bool isAudioOnly() const;
......
......@@ -108,31 +108,7 @@ int GroupsModel::groupItems(const std::unordered_set<int> &ids, Fun &undo, Fun &
// We do not create a group with only one element. Instead, we return the id of that element
return *(roots.begin());
}
if (type == GroupType::AVSplit && !force) {
// additional checks for AVSplit
if (roots.size() != 2) {
// must group exactly two items
return -1;
}
auto it = roots.begin();
int cid1 = *it;
++it;
int cid2 = *it;
auto ptr = m_parent.lock();
if (!ptr) Q_ASSERT(false);
if (cid1 == cid2 || !ptr->isClip(cid1) || !ptr->isClip(cid2)) {
// invalid: we must get two different clips
return -1;
}
int tid1 = ptr->getClipTrackId(cid1);
bool isAudio1 = ptr->getTrackById(tid1)->isAudioTrack();
int tid2 = ptr->getClipTrackId(cid2);
bool isAudio2 = ptr->getTrackById(tid2)->isAudioTrack();
if (isAudio1 == isAudio2) {
// invalid: we must insert one in video the other in audio
return -1;
}
}
int gid = TimelineModel::getNextId();
auto operation = groupItems_lambda(gid, roots, type);
if (operation()) {
......
......@@ -71,7 +71,8 @@ bool TimelineFunctions::cloneClip(const std::shared_ptr<TimelineItemModel> &time
// Special case: slowmotion clips
double clipSpeed = timeline->m_allClips[clipId]->getSpeed();
bool warp_pitch = timeline->m_allClips[clipId]->getIntProperty(QStringLiteral("warp_pitch"));
bool res = timeline->requestClipCreation(timeline->getClipBinId(clipId), newId, state, clipSpeed, warp_pitch, undo, redo);
int audioStream = timeline->m_allClips[clipId]->getIntProperty(QStringLiteral("audio_index"));
bool res = timeline->requestClipCreation(timeline->getClipBinId(clipId), newId, state, audioStream, clipSpeed, warp_pitch, undo, redo);
timeline->m_allClips[newId]->m_endlessResize = timeline->m_allClips[clipId]->m_endlessResize;
// copy useful timeline properties
......@@ -1628,8 +1629,9 @@ bool TimelineFunctions::pasteTimelineClips(const std::shared_ptr<TimelineItemMod
if (!qFuzzyCompare(speed, 1.)) {
warp_pitch = prod.attribute(QStringLiteral("warp_pitch")).toInt();
}
int audioStream = prod.attribute(QStringLiteral("audioStream")).toInt();
int newId;
bool created = timeline->requestClipCreation(originalId, newId, timeline->getTrackById_const(curTrackId)->trackType(), speed, warp_pitch, timeline_undo, timeline_redo);
bool created = timeline->requestClipCreation(originalId, newId, timeline->getTrackById_const(curTrackId)->trackType(), audioStream, speed, warp_pitch, timeline_undo, timeline_redo);
if (!created) {
// Something is broken
pCore->displayMessage(i18n("Could not paste items in timeline"), InformationMessage, 500);
......
......@@ -211,6 +211,7 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles[IsDisabledRole] = "disabled";
roles[IsAudioRole] = "audio";
roles[AudioChannelsRole] = "audioChannels";
roles[AudioStreamRole] = "audioStream";
roles[IsCompositeRole] = "composite";
roles[IsLockedRole] = "locked";
roles[FadeInRole] = "fadeIn";
......@@ -287,6 +288,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
break;
case AudioChannelsRole:
return clip->audioChannels();
case AudioStreamRole:
return clip->audioStream();
case HasAudio:
return clip->audioEnabled();
case IsAudioRole:
......
This diff is collapsed.
......@@ -140,6 +140,7 @@ public:
ShowKeyframesRole,
AudioLevelsRole, /// clip only
AudioChannelsRole, /// clip only
AudioStreamRole, /// clip only
IsCompositeRole, /// track only
IsLockedRole, /// track only
HeightRole, /// track only
......@@ -411,7 +412,7 @@ protected:
@param id: return parameter for the id of the newly created clip.
@param state: The desired clip state (original, audio/video only).
*/
bool requestClipCreation(const QString &binClipId, int &id, PlaylistState::ClipState state, double speed, bool warp_pitch, Fun &undo, Fun &redo);
bool requestClipCreation(const QString &binClipId, int &id, PlaylistState::ClipState state, int audioStream, double speed, bool warp_pitch, Fun &undo, Fun &redo);
/* @brief Switch item selection status */
void setSelected(int itemId, bool sel);
......@@ -818,8 +819,8 @@ protected:
// The index of the temporary overlay track in tractor, or -1 if not connected
int m_overlayTrackCount;
// The preferred audio target for clip insertion or -1 if not defined
int m_audioTarget;
// The preferred audio target for clip insertion in the form {timeline track id, bin clip stream index}
QMap <int, int> m_audioTarget;
// The preferred video target for clip insertion or -1 if not defined
int m_videoTarget;
// Timeline editing mode
......
......@@ -40,6 +40,7 @@ Rectangle {
property int maxDuration: 0
property bool isAudio: false
property int audioChannels
property int audioStream: -1
property bool showKeyframes: false
property bool isGrabbed: false
property bool grouped: false
......@@ -582,7 +583,8 @@ Rectangle {
Text {
// Clip name text
id: label
text: (clipRoot.speed != 1.0 ? '[' + Math.round(clipRoot.speed*100) + '%] ': '') + clipName
property string clipNameString: (clipRoot.isAudio && clipRoot.audioStream > -1) ? ((clipRoot.audioStream > 10000 ? 'Merged' : clipRoot.audioStream) + '|' + clipName ) : clipName
text: (clipRoot.speed != 1.0 ? ('[' + Math.round(clipRoot.speed*100) + '%] ') : '') + clipNameString
font: miniFont
anchors {
top: labelRect.top
......@@ -668,8 +670,7 @@ Rectangle {
//style: Text.Outline
styleColor: 'black'
}
}
}
Rectangle{
//proxy
id:proxyRect
......
......@@ -45,6 +45,7 @@ Row {
height: waveform.height
channels: clipRoot.audioChannels
binId: clipRoot.binId
audioStream: Math.abs(clipRoot.audioStream)
isFirstChunk: index == 0
showItem: waveform.visible && (index * waveform.maxWidth < (clipRoot.scrollStart + scrollView.contentItem.width)) && ((index * waveform.maxWidth + width) > clipRoot.scrollStart)
format: timeline.audioThumbFormat
......
......@@ -234,6 +234,8 @@ Item{
item.canBeVideo = model.canBeVideo
item.itemType = model.clipType
item.audioChannels = model.audioChannels
item.audioStream = model.audioStream
console.log('loaded clpi with Astream: ', model.audioStream)
// Speed change triggers a new clip insert so no binding necessary
item.speed = model.speed
} else if (model.clipType ==