| 1 |
#include <qmath.h> |
| 2 |
#include <cmath> |
| 3 |
#include "helper/ControlCommand.h" |
| 4 |
#include "EchoesAnalysis.h" |
| 5 |
#include "EchoesSegment.h" |
| 6 |
#include "EchoesStyleModel.h" |
| 7 |
#include "SequenceModel.h" |
| 8 |
#include "ControlItem.h" |
| 9 |
#include "TrackItem.h" |
| 10 |
#include "envelope/Envelope.h" |
| 11 |
#include "Utility.h" |
| 12 |
|
| 13 |
#include "EchoesStyleGenerator.h" |
| 14 |
|
| 15 |
using namespace stand::echoes; |
| 16 |
|
| 17 |
EchoesStyleGenerator::EchoesStyleGenerator(EchoesStyleModel *style) |
| 18 |
{ |
| 19 |
setStyle(style); |
| 20 |
} |
| 21 |
|
| 22 |
EchoesStyleGenerator::~EchoesStyleGenerator() |
| 23 |
{ |
| 24 |
} |
| 25 |
|
| 26 |
EchoesStyleModel *EchoesStyleGenerator::style() |
| 27 |
{ |
| 28 |
return _style; |
| 29 |
} |
| 30 |
|
| 31 |
void EchoesStyleGenerator::setStyle(EchoesStyleModel *style) |
| 32 |
{ |
| 33 |
_style = style; |
| 34 |
} |
| 35 |
|
| 36 |
stand::view::helper::ControlCommand *EchoesStyleGenerator::styleCommand(stand::model::SequenceModel *sequence, int trackId, int tickBegin, int tickEnd, int tickResolution, double msFramePeriod) |
| 37 |
{ |
| 38 |
if(!sequence) |
| 39 |
{ |
| 40 |
qDebug("EchoesStyleGenerator::styleCommand(); // invalid args."); |
| 41 |
return NULL; |
| 42 |
} |
| 43 |
QList<EchoesPhrase> phrases = EchoesAnalysis::phraseFromSequence(sequence, trackId, tickBegin, tickEnd); |
| 44 |
|
| 45 |
if(phrases.empty()) |
| 46 |
{ |
| 47 |
qDebug("EchoesStyleGenerator::styleCommand(); // Range(%d to %d) has no data.", tickBegin, tickEnd); |
| 48 |
return NULL; |
| 49 |
} |
| 50 |
|
| 51 |
// 'segments' is the list of corresponding segments to phrases. |
| 52 |
QList<EchoesSegment> segments; |
| 53 |
for(int i = 0; i < phrases.size(); i++) |
| 54 |
{ |
| 55 |
segments.append(_style->findNearest(phrases.at(i))); |
| 56 |
} |
| 57 |
|
| 58 |
stand::view::helper::ControlCommand *parent = new stand::view::helper::ControlCommand; |
| 59 |
for(int i = 0; i < phrases.size(); i++) |
| 60 |
{ |
| 61 |
_calculatePhrase(phrases[i], segments[i], sequence, trackId, tickResolution, msFramePeriod, parent); |
| 62 |
} |
| 63 |
return parent; |
| 64 |
} |
| 65 |
|
| 66 |
stand::view::helper::ControlCommand *EchoesStyleGenerator::_calculatePhrase(const EchoesPhrase &phrase, const EchoesSegment &source, stand::model::SequenceModel *sequence, int trackId, int tickResolution, double framePeriod, stand::view::helper::ControlCommand *parent) |
| 67 |
{ |
| 68 |
stand::view::helper::ControlCommand *pitch = new stand::view::helper::ControlCommand(parent); |
| 69 |
stand::view::helper::ControlCommand *volume = new stand::view::helper::ControlCommand(parent); |
| 70 |
stand::model::TrackItem *track = sequence->track(trackId); |
| 71 |
stand::model::ControlItem *pitchControl = track->control(track->data(track->PitchControlId, 0).toInt()); |
| 72 |
stand::model::ControlItem *volumeControl = track->control(track->data(track->VolumeControlId, 0).toInt()); |
| 73 |
|
| 74 |
pitch->control = pitchControl; |
| 75 |
volume->control = volumeControl; |
| 76 |
int tickBegin = sequence->tick(phrase.notes().first().msPosition()); |
| 77 |
int tickEnd = sequence->tick(phrase.notes().last().msPosition() + phrase.notes().last().msLength()); |
| 78 |
pitch->oldP = pitchControl->contour(tickBegin, tickEnd); |
| 79 |
volume->oldP = volumeControl->contour(tickBegin ,tickEnd); |
| 80 |
|
| 81 |
int firstIndex = 0, lastIndex = 0; |
| 82 |
QList<_Data> data; |
| 83 |
while(lastIndex < phrase.notes().size()) |
| 84 |
{ |
| 85 |
const EchoesNote &first = phrase.notes().at(firstIndex); |
| 86 |
const EchoesNote &last = phrase.notes().at(lastIndex); |
| 87 |
|
| 88 |
// ������������������������������ |
| 89 |
if(first.msPosition() + first.msLength() + 20.0 < last.msPosition()) |
| 90 |
{ |
| 91 |
firstIndex++; |
| 92 |
continue; |
| 93 |
} |
| 94 |
QPair<EchoesNote, EchoesNote> pair = source.phrase()->find(first, last); |
| 95 |
_Data d = {QPair<EchoesNote, EchoesNote>(first, last), pair, pair.first.noteNumber(), pair.second.noteNumber() - pair.first.noteNumber(), source.pitch(), source.volume()}; |
| 96 |
data.append(d); |
| 97 |
firstIndex = lastIndex; |
| 98 |
lastIndex++; |
| 99 |
} |
| 100 |
|
| 101 |
_morph(pitch, volume, data, tickResolution, framePeriod, sequence); |
| 102 |
|
| 103 |
return pitch; |
| 104 |
} |
| 105 |
|
| 106 |
void EchoesStyleGenerator::_morph(stand::view::helper::ControlCommand *pitch, stand::view::helper::ControlCommand *volume, QList<_Data> &data, int tickResolution, double framePeriod, stand::model::SequenceModel * sequence) |
| 107 |
{ |
| 108 |
int size = (data.last().target.second.msPosition() + data.last().target.second.msLength() - data.first().target.first.msPosition()) / framePeriod + 0.5; |
| 109 |
|
| 110 |
double *pitchContour = new double[size]; |
| 111 |
double *volumeContour = new double[size]; |
| 112 |
|
| 113 |
for(int i = 0; i < size; i++) |
| 114 |
{ |
| 115 |
pitchContour[i] = 0; |
| 116 |
volumeContour[i] = -24.0; |
| 117 |
} |
| 118 |
|
| 119 |
for(int i = 0; i < data.size(); i++) |
| 120 |
{ |
| 121 |
for(double ms = data[i].target.first.msPosition(); ms < data[i].target.second.msPosition(); ms += framePeriod) |
| 122 |
{ |
| 123 |
int index = ms / framePeriod + 0.5; |
| 124 |
double rate = (ms - data[i].target.first.msPosition()) / (data[i].target.second.msPosition() - data[i].target.first.msPosition()); |
| 125 |
double localMs = rate * (data[i].source.second.msPosition() - data[i].source.first.msPosition()) + data[i].source.first.msPosition(); |
| 126 |
index = qMax(0, qMin(index, size - 1)); |
| 127 |
pitchContour[index] += rate * data[i].pitch->at(localMs) / stand::utility::NoteFrequency(data[i].source.first.noteNumber()) * stand::utility::NoteFrequency(data[i].target.first.noteNumber()); |
| 128 |
volumeContour[index] += rate * data[i].volume->at(localMs); |
| 129 |
} |
| 130 |
|
| 131 |
for(double ms = data[i].target.second.msPosition(); ms <= data[i].target.second.msPosition() + data[i].target.second.msLength(); ms += framePeriod) |
| 132 |
{ |
| 133 |
int index = ms / framePeriod + 0.5; |
| 134 |
double rate = 1.0 - (ms - data[i].target.second.msPosition())/ data[i].target.second.msLength(); |
| 135 |
double localMs = (1.0 - rate) * data[i].source.second.msLength() + data[i].source.second.msPosition(); |
| 136 |
index = qMax(0, qMin(index, size - 1)); |
| 137 |
pitchContour[index] += rate * data[i].pitch->at(localMs) / stand::utility::NoteFrequency(data[i].source.second.noteNumber()) * stand::utility::NoteFrequency(data[i].target.second.noteNumber()); |
| 138 |
volumeContour[index] += rate * data[i].volume->at(localMs); |
| 139 |
} |
| 140 |
} |
| 141 |
|
| 142 |
stand::utility::envelope::Envelope pitchEnvelope(pitchContour, size, framePeriod, true); |
| 143 |
stand::utility::envelope::Envelope volumeEnvelope(volumeContour, size, framePeriod, true); |
| 144 |
|
| 145 |
int tickBegin = sequence->tick(data.first().target.first.msPosition()); |
| 146 |
int tickEnd = sequence->tick(data.last().target.second.msPosition() + data.last().target.second.msLength()); |
| 147 |
for(int tick = tickBegin / tickResolution * tickResolution; tick <= tickEnd; tick += tickResolution) |
| 148 |
{ |
| 149 |
double ms = sequence->ms(tick) - data.first().target.first.msPosition(); |
| 150 |
double f0 = pitchEnvelope.at(ms); |
| 151 |
double vol = volumeEnvelope.at(ms); |
| 152 |
pitch->newP.append(stand::utility::ControlPoint(tick, f0)); |
| 153 |
volume->newP.append(stand::utility::ControlPoint(tick, vol)); |
| 154 |
} |
| 155 |
|
| 156 |
} |
| 157 |
|