bool EffectSoundTouch::ProcessNoteTrack(Track *track) { NoteTrack *nt = (NoteTrack *) track; if (nt == NULL) return false; nt->WarpAndTransposeNotes(mCurT0, mCurT1, *GetTimeWarper(), mSemitones); return true; }
bool NoteTrack::Paste(double t, Track *src) { // Paste inserts src at time t. If src has a positive offset, // the offset is treated as silence which is also inserted. If // the offset is negative, the offset is ignored and the ENTIRE // src is inserted (otherwise, we would either lose data from // src by not inserting things at negative times, or inserting // things at negative times could overlap things already in // the destination track). //Check that src is a non-NULL NoteTrack if (src == NULL || src->GetKind() != Track::Note) return false; NoteTrack* other = (NoteTrack*)src; if (other->mSeq == NULL) return false; if(!mSeq) mSeq = new Alg_seq(); if (other->GetOffset() > 0) { mSeq->convert_to_seconds(); mSeq->insert_silence(t - GetOffset(), other->GetOffset()); t += other->GetOffset(); } mSeq->paste(t - GetOffset(), other->mSeq); return true; }
void AudacityProject::OnImportMIDI(wxCommandEvent & event) { wxString path = gPrefs->Read("/DefaultOpenPath",::wxGetCwd()); wxString fileName = wxFileSelector(_("Select a MIDI file..."), path, // Path "", // Name "", // Extension _("All files (*.*)|*.*|" "MIDI files (*.mid)|*.mid|" "Allegro files (*.gro)|*.gro"), 0, // Flags this); // Parent if (fileName != "") { path =::wxPathOnly(fileName); gPrefs->Write("/DefaultOpenPath", path); NoteTrack *newTrack = new NoteTrack(&mDirManager); if (::ImportMIDI(fileName, newTrack)) { SelectNone(); mTracks->Add(newTrack); newTrack->SetSelected(true); PushState(wxString::Format(_("Imported MIDI from '%s'"), fileName.c_str())); FixScrollbars(); mTrackPanel->Refresh(false); } } }
bool NoteTrack::Cut(double t0, double t1, Track **dest) { //dest goes onto clipboard *dest = NULL; // This is redundant if (t1 <= t0) return false; double len = t1-t0; NoteTrack *newTrack = new NoteTrack(mDirManager); newTrack->Init(*this); mSeq->convert_to_seconds(); newTrack->mSeq = mSeq->cut(t0, len, false); mLen = (mLen <= len ? 0.0 : mLen - len); newTrack->mLen = len; // What should be done with the rest of newTrack's members? //(mBottomNote, mDirManager, mLastMidiPosition, // mSerializationBuffer, mSerializationLength, mVisibleChannels) *dest = newTrack; return true; }
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 NoteTrack *nt = this; double offset = nt->GetOffset(); // track is shifted this amount if (!mSeq) { // replace saveme with an (unserialized) duplicate nt = (NoteTrack *) this->Duplicate(); wxASSERT(!mSeq && nt->mSeq && !nt->mSerializationBuffer); // swap mSeq and Buffer between this and nt nt->mSerializationBuffer = mSerializationBuffer; nt->mSerializationLength = mSerializationLength; mSerializationBuffer = NULL; mSerializationLength = 0; mSeq = nt->mSeq; nt->mSeq = NULL; delete nt; // delete the duplicate } mSeq->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 (t0 >= t1) return; } Alg_iterator iter(mSeq, false); iter.begin(); Alg_event_ptr event; while ((event = iter.next()) && event->time < t1) { if (event->is_note() && event->time >= t0 && // Allegro data structure does not restrict channels to 16. // Since there is not way to select more than 16 channels, // map all channel numbers mod 16. This will have no effect // on MIDI files, but it will allow users to at least select // all channels on non-MIDI event sequence data. IsVisibleChan(event->chan % 16)) { event->set_pitch(event->get_pitch() + semitones); } } 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(); map->insert_beat(t0, map->time_to_beat(t0)); map->insert_beat(t1, map->time_to_beat(t1)); int i, len = map->length(); for (i = 0; i < len; i++) { Alg_beat &beat = map->beats[i]; beat.time = warper.Warp(beat.time + offset) - offset; } // about to redisplay, so might as well convert back to time now mSeq->convert_to_seconds(); }
void NoteTrack::WriteXML(XMLWriter &xmlFile) { 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). NoteTrack *saveme = this; if (!mSeq) { // replace saveme with an (unserialized) duplicate saveme = (NoteTrack *) this->Duplicate(); assert(saveme->mSeq); } saveme->mSeq->write(data, true); xmlFile.StartTag(wxT("notetrack")); xmlFile.WriteAttr(wxT("name"), saveme->mName); xmlFile.WriteAttr(wxT("offset"), saveme->GetOffset()); xmlFile.WriteAttr(wxT("visiblechannels"), saveme->mVisibleChannels); xmlFile.WriteAttr(wxT("height"), saveme->GetActualHeight()); xmlFile.WriteAttr(wxT("minimized"), saveme->GetMinimized()); xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected()); #ifdef EXPERIMENTAL_MIDI_OUT xmlFile.WriteAttr(wxT("velocity"), (double) saveme->mGain); #endif xmlFile.WriteAttr(wxT("bottomnote"), saveme->mBottomNote); xmlFile.WriteAttr(wxT("data"), wxString(data.str().c_str(), wxConvUTF8)); xmlFile.EndTag(wxT("notetrack")); if (this != saveme) { delete saveme; // delete the duplicate } }
bool NoteTrack::Copy(double t0, double t1, Track **dest){ //dest goes onto clipboard *dest = NULL; // This is redundant and matches WaveTrack::Copy if (t1 <= t0) return false; double len = t1-t0; NoteTrack *newTrack = new NoteTrack(mDirManager); newTrack->Init(*this); mSeq->convert_to_seconds(); newTrack->mSeq = mSeq->copy(t0 - GetOffset(), len, false); newTrack->SetOffset(GetOffset()); // What should be done with the rest of newTrack's members? //(mBottomNote, mDirManager, mLastMidiPosition, // mSerializationBuffer, mSerializationLength, mVisibleChannels) *dest = newTrack; return true; }
Track *NoteTrack::Duplicate() { NoteTrack *duplicate = new 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. if (mSeq) { SonifyBeginSerialize(); assert(!mSerializationBuffer); // serialize from this to duplicate's mSerializationBuffer mSeq->serialize((void**)&duplicate->mSerializationBuffer, &duplicate->mSerializationLength); SonifyEndSerialize(); } else if (mSerializationBuffer) { SonifyBeginUnserialize(); assert(!mSeq); Alg_track_ptr alg_track = Alg_seq::unserialize(mSerializationBuffer, mSerializationLength); assert(alg_track->get_type() == 's'); duplicate->mSeq = (Alg_seq_ptr) alg_track; SonifyEndUnserialize(); } else assert(false); // bug if neither mSeq nor mSerializationBuffer // copy some other fields here duplicate->SetBottomNote(mBottomNote); duplicate->SetPitchHeight(mPitchHeight); duplicate->mLastMidiPosition = mLastMidiPosition; duplicate->mVisibleChannels = mVisibleChannels; duplicate->SetOffset(GetOffset()); #ifdef EXPERIMENTAL_MIDI_OUT duplicate->SetGain(GetGain()); #endif return duplicate; }
bool MeshExpUtility::SaveSkinKeys(const char* n){ CFS_Memory F; int FramesPerSecond = GetFrameRate(); int TicksPerFrame = GetTicksPerFrame(); int FirstTick = ip->GetAnimRange().Start(); int LastTick = ip->GetAnimRange().End(); Point3 v; Matrix3 tm; // Write signature and version char S[MAX_PATH]; sprintf(S, "KEYEXP 3.0"); F.Wstring(S); INode *node; ObjectEntry *Current; //----------------------------------------------------------------------- // Count bones and report int NumBones = 0; Current = theObjects->head; while(Current) { /* if(Current->entry->type != OBTYPE_BONE) { Current = Current->next; continue; } */ NumBones++; Current = Current->next; } sprintf(S, "Number of Bones = %d", NumBones); F.Wstring(S); ELog.Msg(mtInformation,S); //----------------------------------------------------------------------- // Write out necessary data for motion keys sprintf(S, "Key Data"); F.Wstring(S); TimeValue t; Quat qq; Point3 tp, sp; INode* parent; Matrix3 tmp; sprintf(S, "%d %d %d", FirstTick / TicksPerFrame, LastTick / TicksPerFrame, FramesPerSecond); F.Wstring(S); Current = theObjects->head; while(Current){ /* if(Current->entry->type != OBTYPE_BONE) { Current = Current->next; continue; } */ Matrix3 tm; node = Current->entry->node; sprintf(S, "Node: %s", node->GetName()); F.Wstring(S); ELog.Msg(mtInformation,S); // Print notetrack info { int NumNT, n, i, j, NumNotes; NoteTrack* pNT; DefNoteTrack* pDNT; NoteKey* pNK; NumNT = node->NumNoteTracks(); // count all of the notes on all of the notetracks NumNotes = 0; for(n=0;n<NumNT;n++){ pNT = node->GetNoteTrack(n); if(pNT->ClassID() == Class_ID(NOTETRACK_CLASS_ID, 0)){ pDNT = (DefNoteTrack*)pNT; j = pDNT->NumKeys(); for(i=0;i<j;i++){ pNK = pDNT->keys[i]; if( (pNK->time >= FirstTick) && (pNK->time <= LastTick) ) NumNotes++; } } } sprintf(S, "Number of Notes = %d", NumNotes); F.Wstring(S); for(n=0;n<NumNT;n++){ pNT = node->GetNoteTrack(n); if(pNT->ClassID() == Class_ID(NOTETRACK_CLASS_ID, 0)){ pDNT = (DefNoteTrack*)pNT; j = pDNT->NumKeys(); for(i=0;i<j;i++){ pNK = pDNT->keys[i]; if( (pNK->time >= FirstTick) && (pNK->time <= LastTick) ){ sprintf(S, "%d: %s", (pNK->time - FirstTick) / TicksPerFrame, pNK->note); F.Wstring(S); } } } } } for(t=FirstTick;t<=LastTick;t+=TicksPerFrame){ tm = node->GetNodeTM(t); DecomposeMatrix(tm, tp, qq, sp); qq.MakeMatrix (tm); tm.SetTrans (tp); parent = node->GetParentNode(); if(parent){ tmp = parent->GetNodeTM(t); DecomposeMatrix(tmp, tp, qq, sp); qq.MakeMatrix(tmp); tmp.SetTrans(tp); tmp = Inverse(tmp); tm *= tmp; } DecomposeMatrix(tm, tp, qq, sp); sprintf(S,"%f %f %f %f",qq.x, qq.y, qq.z, qq.w); F.Wstring(S); sprintf(S,"%f %f %f",tp.x,tp.y,tp.z); F.Wstring(S); /* // Euler angles Point3 E; QuatToEuler (Quat(tm), E); fprintf (f,"%f %f %f",E.x,E.y,E.z); // Translate DecomposeMatrix (tm, tp, qq, sp); fprintf (f,"%f %f %f",tp.x,tp.y,tp.z); */ // Matrix3fprint(f, tm); } Current = Current->next; } sprintf(S, "Key Data Complete"); F.Wstring(S); ELog.Msg(mtInformation,S); F.SaveTo(n,0); return true; }
void StretchHandle::Stretch(AudacityProject *pProject, int mouseXCoordinate, int trackLeftEdge, Track *pTrack) { ViewInfo &viewInfo = pProject->GetViewInfo(); if (pTrack == NULL && mpTrack != NULL) pTrack = mpTrack.get(); if (!pTrack || pTrack->GetKind() != Track::Note) { return; } NoteTrack *pNt = static_cast<NoteTrack *>(pTrack); double moveto = std::max(0.0, viewInfo.PositionToTime(mouseXCoordinate, trackLeftEdge)); double dur, left_dur, right_dur; // check to make sure tempo is not higher than 20 beats per second // (In principle, tempo can be higher, but not infinity.) double minPeriod = 0.05; // minimum beat period // make sure target duration is not too short // Take quick exit if so, without changing the selection. auto t0 = mStretchState.mBeat0.first; auto t1 = mStretchState.mBeat1.first; switch ( mStretchState.mMode ) { case stretchLeft: { dur = t1 - moveto; if (dur < mStretchState.mRightBeats * minPeriod) return; pNt->StretchRegion ( mStretchState.mBeat0, mStretchState.mBeat1, dur ); pNt->Offset( moveto - t0 ); mStretchState.mBeat0.first = moveto; viewInfo.selectedRegion.setT0(moveto); break; } case stretchRight: { dur = moveto - t0; if (dur < mStretchState.mLeftBeats * minPeriod) return; pNt->StretchRegion ( mStretchState.mBeat0, mStretchState.mBeat1, dur ); viewInfo.selectedRegion.setT1(moveto); mStretchState.mBeat1.first = moveto; break; } case stretchCenter: { moveto = std::max(t0, std::min(t1, moveto)); left_dur = moveto - t0; right_dur = t1 - moveto; if ( left_dur < mStretchState.mLeftBeats * minPeriod || right_dur < mStretchState.mRightBeats * minPeriod ) return; pNt->StretchRegion ( mStretchState.mBeatCenter, mStretchState.mBeat1, right_dur ); pNt->StretchRegion ( mStretchState.mBeat0, mStretchState.mBeatCenter, left_dur ); mStretchState.mBeatCenter.first = moveto; break; } default: wxASSERT(false); break; } }