1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-21 23:00:06 +02:00

Bug1651: NoteTrack sync-lock and crash fixes...

Simplify the logic of duplication of NoteTrack.  Duplicates are always in
serialized state.  Un-serialization can happen on demand in any of the
NoteTrack operations that require a defined sequence.

Changing the duration of the sequence after paste is needed,
as it was also needed, when I fixed Stretch at commit
90eb4ec142f7d575d0870dd9c755589bff520cbe.  I don't know if this should be
considered a bug in Allegro that we are compensating.
This commit is contained in:
Paul Licameli 2017-05-24 20:28:44 -04:00
parent b3a70b5993
commit a8ac80eda9
6 changed files with 179 additions and 161 deletions

View File

@ -2130,7 +2130,7 @@ void AudioIO::PrepareMidiIterator(bool send, double offset)
// Iterator not yet intialized, must add each track...
for (i = 0; i < nTracks; i++) {
NoteTrack *t = mMidiPlaybackTracks[i];
Alg_seq_ptr seq = t->GetSequence();
Alg_seq_ptr seq = &t->GetSeq();
// mark sequence tracks as "in use" since we're handing this
// off to another thread and want to make sure nothing happens
// to the data until playback finishes. This is just a sanity check.
@ -2387,7 +2387,7 @@ void AudioIO::StopStream()
int nTracks = mMidiPlaybackTracks.size();
for (int i = 0; i < nTracks; i++) {
NoteTrack *t = mMidiPlaybackTracks[i];
Alg_seq_ptr seq = t->GetSequence();
Alg_seq_ptr seq = &t->GetSeq();
seq->set_in_use(false);
}

View File

@ -7240,14 +7240,6 @@ void AudacityProject::OnScoreAlign()
// Make a copy of the note track in case alignment is canceled or fails
auto holder = nt->Duplicate();
auto alignedNoteTrack = static_cast<NoteTrack*>(holder.get());
// Duplicate() on note tracks serializes seq to a buffer, but we need
// the seq, so Duplicate again and discard the track with buffer. The
// test is here in case Duplicate() is changed in the future.
if (alignedNoteTrack->GetSequence() == NULL) {
holder = alignedNoteTrack->Duplicate();
alignedNoteTrack = static_cast<NoteTrack*>(holder.get());
wxASSERT(alignedNoteTrack->GetSequence());
}
// Remove offset from NoteTrack because audio is
// mixed starting at zero and incorporating clip offsets.
if (alignedNoteTrack->GetOffset() < 0) {
@ -7287,7 +7279,7 @@ void AudacityProject::OnScoreAlign()
#ifndef SKIP_ACTUAL_SCORE_ALIGNMENT
result = scorealign((void *) &mix, &mixer_process,
2 /* channels */, 44100.0 /* srate */, endTime,
alignedNoteTrack->GetSequence(), &progress, params);
&alignedNoteTrack->GetSeq(), &progress, params);
#else
result = SA_SUCCESS;
#endif

View File

@ -127,15 +127,35 @@ NoteTrack::~NoteTrack()
{
}
Alg_seq &NoteTrack::GetSeq() const
{
if (!mSeq) {
if (!mSerializationBuffer)
mSeq = std::make_unique<Alg_seq>();
else {
std::unique_ptr<Alg_track> alg_track
{ Alg_seq::unserialize
( mSerializationBuffer.get(), mSerializationLength ) };
wxASSERT(alg_track->get_type() == 's');
mSeq.reset( static_cast<Alg_seq*>(alg_track.release()) );
// Preserve the invariant that at most one of the representations is
// valid
mSerializationBuffer.reset();
mSerializationLength = 0;
}
}
wxASSERT(mSeq);
return *mSeq;
}
Track::Holder NoteTrack::Duplicate() const
{
auto duplicate = std::make_unique<NoteTrack>(mDirManager);
duplicate->Init(*this);
// Duplicate on NoteTrack moves data from mSeq to mSerializationBuffer
// and from mSerializationBuffer to mSeq on alternate calls. Duplicate
// to the undo stack and Duplicate back to the project should result
// in serialized blobs on the undo stack and traversable data in the
// project object.
// The duplicate begins life in serialized state. Often the duplicate is
// pushed on the Undo stack. Then we want to un-serialize it (or a further
// copy) only on demand after an Undo.
if (mSeq) {
SonifyBeginSerialize();
wxASSERT(!mSerializationBuffer);
@ -145,15 +165,19 @@ Track::Holder NoteTrack::Duplicate() const
&duplicate->mSerializationLength);
duplicate->mSerializationBuffer.reset( (char*)buffer );
SonifyEndSerialize();
} else if (mSerializationBuffer) {
SonifyBeginUnserialize();
}
else if (mSerializationBuffer) {
// Copy already serialized data.
wxASSERT(!mSeq);
std::unique_ptr<Alg_track> alg_track{ Alg_seq::unserialize(mSerializationBuffer.get(),
mSerializationLength) };
wxASSERT(alg_track->get_type() == 's');
duplicate->mSeq.reset(static_cast<Alg_seq*>(alg_track.release()));
SonifyEndUnserialize();
} else wxFAIL_MSG("neither mSeq nor mSerializationBuffer were present"); // bug if neither mSeq nor mSerializationBuffer
duplicate->mSerializationLength = this->mSerializationLength;
duplicate->mSerializationBuffer.reset
( new char[ this->mSerializationLength ] );
memcpy( duplicate->mSerializationBuffer.get(),
this->mSerializationBuffer.get(), this->mSerializationLength );
}
else {
// We are duplicating a default-constructed NoteTrack, and that's okay
}
// copy some other fields here
duplicate->SetBottomNote(mBottomNote);
duplicate->SetPitchHeight(mPitchHeight);
@ -180,7 +204,7 @@ double NoteTrack::GetStartTime() const
double NoteTrack::GetEndTime() const
{
return GetStartTime() + (mSeq ? mSeq->get_real_dur() : 0.0);
return GetStartTime() + GetSeq().get_real_dur();
}
@ -188,25 +212,13 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
const TimeWarper &warper,
double semitones)
{
// Since this is a duplicate and duplicates convert mSeq to
// a text string for saving as XML, we probably have to
// duplicate again to get back an mSeq
double offset = this->GetOffset(); // track is shifted this amount
if (!mSeq) { // replace saveme with an (unserialized) duplicate
Track::Holder unt{ Duplicate() };
const auto nt = static_cast<NoteTrack*>(unt.get());
wxASSERT(!mSeq && nt->mSeq && !nt->mSerializationBuffer);
// swap mSeq and Buffer between this and nt
nt->mSerializationBuffer = std::move(mSerializationBuffer);
nt->mSerializationLength = mSerializationLength;
mSerializationLength = 0;
mSeq = std::move(nt->mSeq);
}
mSeq->convert_to_seconds(); // make sure time units are right
auto &seq = GetSeq();
seq.convert_to_seconds(); // make sure time units are right
t1 -= offset; // adjust time range to compensate for track offset
t0 -= offset;
if (t1 > mSeq->get_dur()) { // make sure t0, t1 are within sequence
t1 = mSeq->get_dur();
if (t1 > seq.get_dur()) { // make sure t0, t1 are within sequence
t1 = seq.get_dur();
if (t0 >= t1) return;
}
Alg_iterator iter(mSeq.get(), false);
@ -219,8 +231,8 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
}
iter.end();
// now, use warper to warp the tempo map
mSeq->convert_to_beats(); // beats remain the same
Alg_time_map_ptr map = mSeq->get_time_map();
seq.convert_to_beats(); // beats remain the same
Alg_time_map_ptr map = seq.get_time_map();
map->insert_beat(t0, map->time_to_beat(t0));
map->insert_beat(t1, map->time_to_beat(t1));
int i, len = map->length();
@ -229,7 +241,7 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
beat.time = warper.Warp(beat.time + offset) - offset;
}
// about to redisplay, so might as well convert back to time now
mSeq->convert_to_seconds();
seq.convert_to_seconds();
}
// Draws the midi channel toggle buttons within the given rect.
@ -348,11 +360,6 @@ void NoteTrack::SetSequence(std::unique_ptr<Alg_seq> &&seq)
mSeq = std::move(seq);
}
Alg_seq* NoteTrack::GetSequence()
{
return mSeq.get();
}
void NoteTrack::PrintSequence()
{
FILE *debugOutput;
@ -360,6 +367,8 @@ void NoteTrack::PrintSequence()
debugOutput = fopen("debugOutput.txt", "wt");
fprintf(debugOutput, "Importing MIDI...\n");
// This is called for debugging purposes. Do not compute mSeq on demand
// with GetSeq()
if (mSeq) {
int i = 0;
@ -416,15 +425,23 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
THROW_INCONSISTENCY_EXCEPTION;
double len = t1-t0;
//auto delta = -(
//( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
//);
auto newTrack = std::make_unique<NoteTrack>(mDirManager);
newTrack->Init(*this);
mSeq->convert_to_seconds();
newTrack->mSeq.reset(mSeq->cut(t0 - GetOffset(), len, false));
auto &seq = GetSeq();
seq.convert_to_seconds();
newTrack->mSeq.reset(seq.cut(t0 - GetOffset(), len, false));
newTrack->SetOffset(GetOffset());
// Not needed
// Alg_seq::cut seems to handle this
//AddToDuration( delta );
// What should be done with the rest of newTrack's members?
//(mBottomNote, mDirManager, mLastMidiPosition,
// mSerializationBuffer, mSerializationLength, mVisibleChannels)
@ -444,8 +461,9 @@ Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
newTrack->Init(*this);
mSeq->convert_to_seconds();
newTrack->mSeq.reset(mSeq->copy(t0 - GetOffset(), len, false));
auto &seq = GetSeq();
seq.convert_to_seconds();
newTrack->mSeq.reset(seq.copy(t0 - GetOffset(), len, false));
newTrack->SetOffset(GetOffset());
// What should be done with the rest of newTrack's members?
@ -460,13 +478,23 @@ bool NoteTrack::Trim(double t0, double t1)
{
if (t1 < t0)
return false;
mSeq->convert_to_seconds();
auto &seq = GetSeq();
//auto delta = -(
//( GetEndTime() - std::min( GetEndTime(), t1 ) ) +
//( std::max(t0, GetStartTime()) - GetStartTime() )
//);
seq.convert_to_seconds();
// DELETE way beyond duration just in case something is out there:
mSeq->clear(t1 - GetOffset(), mSeq->get_dur() + 10000.0, false);
seq.clear(t1 - GetOffset(), seq.get_dur() + 10000.0, false);
// Now that stuff beyond selection is cleared, clear before selection:
mSeq->clear(0.0, t0 - GetOffset(), false);
seq.clear(0.0, t0 - GetOffset(), false);
// want starting time to be t0
SetOffset(t0);
// Not needed
// Alg_seq::clear seems to handle this
//AddToDuration( delta );
return true;
}
@ -477,8 +505,15 @@ void NoteTrack::Clear(double t0, double t1)
double len = t1-t0;
if (mSeq)
mSeq->clear(t0 - GetOffset(), len, false);
auto &seq = GetSeq();
//auto delta = -(
//( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
//);
seq.clear(t0 - GetOffset(), len, false);
// Not needed
// Alg_seq::clear seems to handle this
// AddToDuration( delta );
}
void NoteTrack::Paste(double t, const Track *src)
@ -497,19 +532,27 @@ void NoteTrack::Paste(double t, const Track *src)
return;
NoteTrack* other = (NoteTrack*)src;
if (other->mSeq == NULL)
// THROW_INCONSISTENCY_EXCEPTION; // ?
return;
if(!mSeq)
mSeq = std::make_unique<Alg_seq>();
if (other->GetOffset() > 0) {
mSeq->convert_to_seconds();
mSeq->insert_silence(t - GetOffset(), other->GetOffset());
t += other->GetOffset();
double delta = 0.0;
auto &seq = GetSeq();
auto offset = other->GetOffset();
if ( offset > 0 ) {
seq.convert_to_seconds();
seq.insert_silence( t - GetOffset(), offset );
t += offset;
// Is this needed or does Alg_seq::insert_silence take care of it?
//delta += offset;
}
mSeq->paste(t - GetOffset(), other->mSeq.get());
// This seems to be needed:
delta += std::max( 0.0, t - GetEndTime() );
// This, not:
//delta += other->GetSeq().get_real_dur();
seq.paste(t - GetOffset(), &other->GetSeq());
AddToDuration( delta );
}
void NoteTrack::Silence(double t0, double t1)
@ -519,20 +562,25 @@ void NoteTrack::Silence(double t0, double t1)
auto len = t1 - t0;
mSeq->convert_to_seconds();
auto &seq = GetSeq();
seq.convert_to_seconds();
// XXX: do we want to set the all param?
// If it's set, then it seems like notes are silenced if they start or end in the range,
// otherwise only if they start in the range. --Poke
mSeq->silence(t0 - GetOffset(), len, false);
seq.silence(t0 - GetOffset(), len, false);
}
void NoteTrack::InsertSilence(double t, double len)
{
if (len <= 0)
if (len < 0)
THROW_INCONSISTENCY_EXCEPTION;
mSeq->convert_to_seconds();
mSeq->insert_silence(t - GetOffset(), len);
auto &seq = GetSeq();
seq.convert_to_seconds();
seq.insert_silence(t - GetOffset(), len);
// is this needed?
// AddToDuration( len );
}
// Call this function to manipulate the underlying sequence data. This is
@ -540,22 +588,24 @@ void NoteTrack::InsertSilence(double t, double len)
bool NoteTrack::Shift(double t) // t is always seconds
{
if (t > 0) {
auto &seq = GetSeq();
// insert an even number of measures
mSeq->convert_to_beats();
seq.convert_to_beats();
// get initial tempo
double tempo = mSeq->get_tempo(0.0);
double beats_per_measure = mSeq->get_bar_len(0.0);
double tempo = seq.get_tempo(0.0);
double beats_per_measure = seq.get_bar_len(0.0);
int m = ROUND(t * tempo / beats_per_measure);
// need at least 1 measure, so if we rounded down to zero, fix it
if (m == 0) m = 1;
// compute NEW tempo so that m measures at NEW tempo take t seconds
tempo = beats_per_measure * m / t; // in beats per second
mSeq->insert_silence(0.0, beats_per_measure * m);
mSeq->set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m);
mSeq->write("afterShift.gro");
seq.insert_silence(0.0, beats_per_measure * m);
seq.set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m);
seq.write("afterShift.gro");
} else if (t < 0) {
mSeq->convert_to_seconds();
mSeq->clear(0, t, true);
auto &seq = GetSeq();
seq.convert_to_seconds();
seq.clear(0, t, true);
} else { // offset is zero, no modifications
return false;
}
@ -564,28 +614,35 @@ bool NoteTrack::Shift(double t) // t is always seconds
QuantizedTimeAndBeat NoteTrack::NearestBeatTime( double time ) const
{
wxASSERT(mSeq);
// Alg_seq knows nothing about offset, so remove offset time
double seq_time = time - GetOffset();
double beat;
seq_time = mSeq->nearest_beat_time(seq_time, &beat);
auto &seq = GetSeq();
seq_time = seq.nearest_beat_time(seq_time, &beat);
// add the offset back in to get "actual" audacity track time
return { seq_time + GetOffset(), beat };
}
void NoteTrack::AddToDuration( double delta )
{
auto &seq = GetSeq();
#if 0
// PRL: Would this be better ?
seq.set_real_dur( seq.get_real_dur() + delta );
#else
seq.convert_to_seconds();
seq.set_dur( seq.get_dur() + delta );
#endif
}
bool NoteTrack::StretchRegion
( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur )
{
bool result = mSeq->stretch_region( t0.second, t1.second, newDur );
auto &seq = GetSeq();
bool result = seq.stretch_region( t0.second, t1.second, newDur );
if (result) {
const auto oldDur = t1.first - t0.first;
#if 0
// PRL: Would this be better ?
mSeq->set_real_dur(mSeq->get_real_dur() + newDur - oldDur);
#else
mSeq->convert_to_seconds();
mSeq->set_dur(mSeq->get_dur() + newDur - oldDur);
#endif
AddToDuration( newDur - oldDur );
}
return result;
}
@ -605,24 +662,21 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
cleanup.reset();
double offset = GetOffset();
if (offset == 0)
return mSeq.get();
return &GetSeq();
// make a copy, deleting events that are shifted before time 0
double start = -offset;
if (start < 0) start = 0;
// notes that begin before "start" are not included even if they
// extend past "start" (because "all" parameter is set to false)
cleanup.reset( mSeq->copy(start, mSeq->get_dur() - start, false) );
cleanup.reset( GetSeq().copy(start, GetSeq().get_dur() - start, false) );
auto seq = cleanup.get();
if (offset > 0) {
{
// Cheat a little
NoteTrack *pMutable = const_cast< NoteTrack * >(this);
// swap cleanup and mSeq so that Shift operates on the NEW copy
swap(pMutable->mSeq, cleanup);
auto cleanup2 = finally( [&] { swap(pMutable->mSeq, cleanup); } );
swap( this->mSeq, cleanup );
auto cleanup2 = finally( [&] { swap( this->mSeq, cleanup ); } );
pMutable->Shift(offset);
const_cast< NoteTrack *>( this )->Shift(offset);
}
#ifdef OLD_CODE
// now shift events by offset. This must be done with an integer
@ -665,27 +719,28 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
#endif
} else {
auto &mySeq = GetSeq();
// if offset is negative, it might not be a multiple of beats, but
// we want to preserve the relative positions of measures. I.e. we
// should shift barlines and time signatures as well as notes.
// Insert a time signature at the first bar-line if necessary.
// Translate start from seconds to beats and call it beat:
double beat = mSeq->get_time_map()->time_to_beat(start);
// Find the time signature in mSeq in effect at start (beat):
int i = mSeq->time_sig.find_beat(beat);
double beat = mySeq.get_time_map()->time_to_beat(start);
// Find the time signature in mySeq in effect at start (beat):
int i = mySeq.time_sig.find_beat(beat);
// i is where you would insert a NEW time sig at beat,
// Case 1: beat coincides with a time sig at i. Time signature
// at beat means that there is a barline at beat, so when beat
// is shifted to 0, the relative barline positions are preserved
if (mSeq->time_sig.length() > 0 &&
within(beat, mSeq->time_sig[i].beat, ALG_EPS)) {
if (mySeq.time_sig.length() > 0 &&
within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
// beat coincides with time signature change, so offset must
// be a multiple of beats
/* do nothing */ ;
// Case 2: there is no time signature before beat.
} else if (i == 0 && (mSeq->time_sig.length() == 0 ||
mSeq->time_sig[i].beat > beat)) {
} else if (i == 0 && (mySeq.time_sig.length() == 0 ||
mySeq.time_sig[i].beat > beat)) {
// If beat does not fall on an implied barline, we need to
// insert a time signature.
double measures = beat / 4.0;
@ -702,7 +757,7 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
// Case 3: i-1 must be the effective time sig position
} else {
i -= 1; // index the time signature in effect at beat
Alg_time_sig_ptr tsp = &(mSeq->time_sig[i]);
Alg_time_sig_ptr tsp = &(mySeq.time_sig[i]);
double beats_per_measure = (tsp->num * 4) / tsp->den;
double measures = (beat - tsp->beat) / beats_per_measure;
int imeasures = ROUND(measures);
@ -738,12 +793,13 @@ bool NoteTrack::ExportAllegro(const wxString &f) const
double offset = GetOffset();
bool in_seconds;
gPrefs->Read(wxT("/FileFormats/AllegroStyle"), &in_seconds, true);
auto &seq = GetSeq();
if (in_seconds) {
mSeq->convert_to_seconds();
seq.convert_to_seconds();
} else {
mSeq->convert_to_beats();
seq.convert_to_beats();
}
return mSeq->write(f.mb_str(), offset);
return seq.write(f.mb_str(), offset);
}
@ -811,28 +867,15 @@ void NoteTrack::WriteXML(XMLWriter &xmlFile) const
// may throw
{
std::ostringstream data;
// Normally, Duplicate is called in pairs -- once to put NoteTrack
// on the Undo stack, and again to move from the Undo stack to an
// "active" editable state. For efficiency, we do not do a "real"
// Duplicate followed by serialization into a binary blob. Instead,
// we combine the Duplicate with serialization or unserialization.
// Serialization and Unserialization happen on alternate calls to
// Duplicate and (usually) produce the right results at the right
// time.
// It turns out that this optimized Duplicate is a little too
// clever. There is at least one case where a track can be duplicated
// and then AutoSave'd. (E.g. do an "Insert Silence" effect on a
// NoteTrack.) In this case, mSeq will be NULL. To avoid a crash
// and perform WriteXML, we may need to restore NoteTracks from binary
// blobs to regular data structures (with an Alg_seq member).
Track::Holder holder;
const NoteTrack *saveme = this;
if (!mSeq) { // replace saveme with an (unserialized) duplicate
if (!mSeq) {
// replace saveme with an (unserialized) duplicate, which is
// destroyed at end of function.
holder = Duplicate();
saveme = static_cast<NoteTrack*>(holder.get());
wxASSERT(saveme->mSeq);
}
saveme->mSeq->write(data, true);
saveme->GetSeq().write(data, true);
xmlFile.StartTag(wxT("notetrack"));
xmlFile.WriteAttr(wxT("name"), saveme->mName);
this->NoteTrackBase::WriteXMLAttributes(xmlFile);

View File

@ -65,8 +65,6 @@ class AUDACITY_DLL_API NoteTrack final
: public NoteTrackBase
{
public:
friend class TrackArtist;
NoteTrack(const std::shared_ptr<DirManager> &projDirManager);
virtual ~NoteTrack();
@ -79,6 +77,8 @@ class AUDACITY_DLL_API NoteTrack final
double GetStartTime() const override;
double GetEndTime() const override;
Alg_seq &GetSeq() const;
void WarpAndTransposeNotes(double t0, double t1,
const TimeWarper &warper, double semitones);
@ -86,7 +86,6 @@ class AUDACITY_DLL_API NoteTrack final
bool LabelClick(const wxRect &rect, int x, int y, bool right);
void SetSequence(std::unique_ptr<Alg_seq> &&seq);
Alg_seq* GetSequence();
void PrintSequence();
Alg_seq *MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const;
@ -208,21 +207,17 @@ class AUDACITY_DLL_API NoteTrack final
else
mVisibleChannels = CHANNEL_BIT(c);
}
private:
std::unique_ptr<Alg_seq> mSeq; // NULL means no sequence
// when Duplicate() is called, assume that it is to put a copy
// of the track into the undo stack or to redo/copy from the
// stack to the project object. We want copies to the stack
// to be serialized (therefore compact) representations, so
// copy will set mSeq to NULL and serialize to the following
// variables. If this design is correct, the track will be
// duplicated again (in the event of redo) back to the project
// at which point we will unserialize the data back to the
// mSeq variable. (TrackArtist should check to make sure this
// flip-flop from mSeq to mSerializationBuffer happened an
// even number of times, otherwise mSeq will be NULL).
mutable std::unique_ptr<char[]> mSerializationBuffer; // NULL means no buffer
long mSerializationLength;
void AddToDuration( double delta );
// These are mutable to allow NoteTrack to switch details of representation
// in logically const methods
// At most one of the two pointers is not null at any time.
// Both are null in a newly constructed NoteTrack.
mutable std::unique_ptr<Alg_seq> mSeq;
mutable std::unique_ptr<char[]> mSerializationBuffer;
mutable long mSerializationLength;
#ifdef EXPERIMENTAL_MIDI_OUT
float mVelocity; // velocity offset

View File

@ -2730,7 +2730,7 @@ void TrackArtist::DrawNoteBackground(const NoteTrack *track, wxDC &dc,
int left = TIME_TO_X(track->GetOffset());
if (left < sel.x) left = sel.x; // clip on left
int right = TIME_TO_X(track->GetOffset() + track->mSeq->get_real_dur());
int right = TIME_TO_X(track->GetOffset() + track->GetSeq().get_real_dur());
if (right > sel.x + sel.width) right = sel.x + sel.width; // clip on right
// need overlap between MIDI data and the background region
@ -2772,7 +2772,7 @@ void TrackArtist::DrawNoteBackground(const NoteTrack *track, wxDC &dc,
}
// draw bar lines
Alg_seq_ptr seq = track->mSeq.get();
Alg_seq_ptr seq = &track->GetSeq();
// We assume that sliding a NoteTrack around slides the barlines
// along with the notes. This means that when we write out a track
// as Allegro or MIDI without the offset, we'll need to insert an
@ -2824,19 +2824,7 @@ void TrackArtist::DrawNoteTrack(const NoteTrack *track,
const double h = X_TO_TIME(rect.x);
const double h1 = X_TO_TIME(rect.x + rect.width);
Alg_seq_ptr seq = track->mSeq.get();
if (!seq) {
wxASSERT(track->mSerializationBuffer);
// JKC: Previously this indirected via seq->, a NULL pointer.
// This was actually OK, since unserialize is a static function.
// Alg_seq:: is clearer.
std::unique_ptr<Alg_track> alg_track{ Alg_seq::unserialize(track->mSerializationBuffer.get(),
track->mSerializationLength) };
wxASSERT(alg_track->get_type() == 's');
const_cast<NoteTrack*>(track)->mSeq.reset(seq = static_cast<Alg_seq*>(alg_track.release()));
track->mSerializationBuffer.reset();
}
wxASSERT(seq);
Alg_seq_ptr seq = &track->GetSeq();
if (!track->GetSelected())
sel0 = sel1 = 0.0;

View File

@ -62,7 +62,7 @@ bool ImportMIDI(const wxString &fName, NoteTrack * dest)
dest->SetName(trackNameBase);
mf.Close();
// the mean pitch should be somewhere in the middle of the display
Alg_iterator iterator(dest->GetSequence(), false);
Alg_iterator iterator( &dest->GetSeq(), false );
iterator.begin();
// for every event
Alg_event_ptr evt;