1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //
5 //  time_stretch.cpp
6 //  Copyright (C) 2010-2020 Tim E. Real (terminator356 on users dot sourceforge dot net)
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; version 2 of
11 //  the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 //=========================================================
23 
24 #include <stdio.h>
25 #include "muse_math.h"
26 
27 #include "time_stretch.h"
28 #include "xml.h"
29 
30 // For debugging output: Uncomment the fprintf section.
31 #define ERROR_TIMESTRETCH(dev, format, args...) fprintf(dev, format, ##args)
32 #define INFO_TIMESTRETCH(dev, format, args...) // fprintf(dev, format, ##args)
33 #define DEBUG_TIMESTRETCH(dev, format, args...) // fprintf(dev, format, ##args)
34 
35 namespace MusECore {
36 
37 //---------------------------------------------------------
38 //   StretchList
39 //---------------------------------------------------------
40 
StretchList()41 StretchList::StretchList()
42 {
43   _isStretched = false;
44   _isResampled = false;
45   _isPitchShifted = false;
46   _startFrame = 0;
47   _endFrame = 0;
48   _stretchedEndFrame = 0;
49   _squishedEndFrame = 0;
50   _stretchRatio = 1.0;
51   _samplerateRatio = 1.0;
52   _pitchRatio = 1.0;
53 
54   // Ensure that there is always an item at frame zero.
55   insert(std::pair<const MuseFrame_t, StretchListItem>
56     (0, StretchListItem(1.0, 1.0, 1.0,
57                         StretchListItem::StretchEvent |
58                         StretchListItem::SamplerateEvent |
59                         StretchListItem::PitchEvent)));
60 
61   // Technically it is normalized now, since StretchListItem
62   //  constructor fills in zeros for the frame values.
63   _isNormalized = true;
64 }
65 
~StretchList()66 StretchList::~StretchList()
67 {
68 }
69 
add(StretchListItem::StretchEventType type,MuseFrame_t frame,double value,bool do_normalize)70 void StretchList::add(StretchListItem::StretchEventType type, MuseFrame_t frame, double value, bool do_normalize)
71 {
72   // Some '1.0' values will be filled in if neccessary by normalize() below.
73   double str = 1.0;
74   double srr = 1.0;
75   double psr = 1.0;
76   switch(type)
77   {
78     case StretchListItem::StretchEvent:
79       str = value;
80     break;
81 
82     case StretchListItem::SamplerateEvent:
83       srr = value;
84     break;
85 
86     case StretchListItem::PitchEvent:
87       psr = value;
88     break;
89   }
90 
91   std::pair<iStretchListItem, bool> res =
92     insert(std::pair<const MuseFrame_t, StretchListItem>
93       (frame, StretchListItem(str, srr, psr, type)));
94 
95   // Item already exists? Assign.
96   if(!res.second)
97   {
98     // Set the type's value. But leave the others alone.
99     switch(type)
100     {
101       case StretchListItem::StretchEvent:
102         res.first->second._stretchRatio = value;
103       break;
104 
105       case StretchListItem::SamplerateEvent:
106         res.first->second._samplerateRatio = value;
107       break;
108 
109       case StretchListItem::PitchEvent:
110         res.first->second._pitchRatio = value;
111       break;
112     }
113 
114     // Combine the type.
115     res.first->second._type |= type;
116   }
117 
118   // Mark as invalidated, normalization is required.
119   _isNormalized = false;
120 
121   if(do_normalize)
122     normalizeListFrames();
123 }
124 
add(MuseFrame_t frame,const StretchListItem & e,bool do_normalize)125 void StretchList::add(MuseFrame_t frame, const StretchListItem& e, bool do_normalize)
126 {
127   std::pair<iStretchListItem, bool> res = insert(std::pair<const MuseFrame_t, StretchListItem> (frame, e));
128 
129   // Item already exists? Assign.
130   if(!res.second)
131   {
132     //res.first->second = e;
133     res.first->second._stretchRatio = e._stretchRatio;
134     res.first->second._samplerateRatio = e._samplerateRatio;
135     res.first->second._pitchRatio = e._pitchRatio;
136   }
137 
138   // Mark as invalidated, normalization is required.
139   _isNormalized = false;
140 
141   if(do_normalize)
142     normalizeListFrames();
143 }
144 
del(int types,MuseFrame_t frame,bool do_normalize)145 void StretchList::del(int types, MuseFrame_t frame, bool do_normalize)
146 {
147   // Do not delete the item at zeroth frame.
148   if(frame == 0)
149     return;
150 
151   iStretchListItem e = find(frame);
152   if(e == end())
153   {
154     ERROR_TIMESTRETCH(stderr, "StretchList::del(%ld): not found\n", frame);
155     return;
156   }
157 
158   del(types, e, do_normalize);
159 }
160 
del(int types,iStretchListItem item,bool do_normalize)161 void StretchList::del(int types, iStretchListItem item, bool do_normalize)
162 {
163   // Do not delete the item at zeroth frame.
164   if(item->first == 0)
165     return;
166 
167   // We must restore any previous event's ratio to 1.0.
168   // This is crucial so that when the last user marker is finally removed, the special zeroth marker (non-user)
169   //  will be set to 1.0 ratio and the converters are not required anymore and can be deleted.
170   if(types & StretchListItem::StretchEvent)
171   {
172     iStretchListItem prevStretchTyped = previousEvent(StretchListItem::StretchEvent, item);
173     if(prevStretchTyped != end())
174       prevStretchTyped->second._stretchRatio = 1.0;
175   }
176   if(types & StretchListItem::SamplerateEvent)
177   {
178     iStretchListItem prevSamplerateTyped = previousEvent(StretchListItem::SamplerateEvent, item);
179     if(prevSamplerateTyped != end())
180       prevSamplerateTyped->second._samplerateRatio = 1.0;
181   }
182   if(types & StretchListItem::PitchEvent)
183   {
184     iStretchListItem prevPitchTyped = previousEvent(StretchListItem::PitchEvent, item);
185     if(prevPitchTyped != end())
186       prevPitchTyped->second._stretchRatio = 1.0;
187   }
188 
189   item->second._type &= ~types;
190 
191   if(item->second._type == 0)
192     erase(item);
193 
194   // Mark as invalidated, normalization is required.
195   _isNormalized = false;
196 
197   if(do_normalize)
198     normalizeListFrames();
199 }
200 
201 
normalizeFrames()202 void StretchList::normalizeFrames()
203 {
204   _stretchedEndFrame = stretch(_endFrame);
205   _squishedEndFrame = squish(_endFrame);
206 }
207 
normalizeRatios()208 void StretchList::normalizeRatios()
209 {
210 
211 }
212 
normalizeListFrames()213 void StretchList::normalizeListFrames()
214 {
215   double dtime;
216   double factor;
217   double duntime;
218   MuseFrame_t dframe;
219 
220   MuseFrame_t thisFrame, prevFrame;
221   double prevNewFrame;
222   double prevNewUnFrame;
223   double prevNewStretchFrame;
224   double prevNewUnStretchFrame;
225   double prevNewSamplerateFrame;
226   double prevNewUnSamplerateFrame;
227 
228   double prevStretch;
229   double prevSamplerate;
230   double prevPitch;
231 
232   // If any intrinsic value has a stretch or samplerate other than 1.0,
233   //  the map is stretched, a stretcher or samplerate converter must be engaged.
234   _isStretched = (_stretchRatio != 1.0);
235   _isResampled = (_samplerateRatio != 1.0);
236   _isPitchShifted = (_pitchRatio != 1.0);
237   for(iterator ise = begin(); ise != end(); ++ise)
238   {
239     thisFrame = ise->first;
240     StretchListItem& se = ise->second;
241 
242     // The policy is such that if there are user items (non zeroth item) of a given type,
243     //  the list is said to be in that state (stretched, resampled, shifted etc), even if all
244     //  the items' ratios are 1.0.
245     // Ignore the special zeroth frame.
246     // If the zeroth frame is the only item, its ratios must (should) all be at 1.0 right now
247     //  so they will be ignored.
248     if(thisFrame != 0)
249     {
250       if(((se._type & StretchListItem::StretchEvent))) //&& se._stretchRatio != 1.0))
251         _isStretched = true;
252       if(((se._type & StretchListItem::SamplerateEvent))) //&& se._samplerateRatio != 1.0))
253         _isResampled = true;
254       if(((se._type & StretchListItem::PitchEvent))) //&& se._pitchRatio != 1.0))
255         _isPitchShifted = true;
256     }
257 
258     if(ise == begin())
259     {
260       prevFrame = prevNewUnFrame = prevNewFrame =
261         prevNewStretchFrame = prevNewUnStretchFrame =
262         prevNewSamplerateFrame = prevNewUnSamplerateFrame =
263         se._finSquishedFrame = se._finStretchedFrame =
264         se._stretchStretchedFrame = se._stretchSquishedFrame =
265         se._samplerateStretchedFrame = se._samplerateSquishedFrame = thisFrame;
266 
267       prevStretch = se._stretchRatio;
268       prevSamplerate = se._samplerateRatio;
269       prevPitch = se._pitchRatio;
270     }
271     else
272     {
273       dframe = thisFrame - prevFrame;
274 
275       factor = (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch);
276       dtime = double(dframe) * factor;
277       se._finStretchedFrame = prevNewFrame + dtime;
278       prevNewFrame = se._finStretchedFrame;
279 
280       duntime = double(dframe) / factor;
281       se._finSquishedFrame = prevNewUnFrame + duntime;
282       prevNewUnFrame = se._finSquishedFrame;
283 
284 
285       factor = 1.0 / (_stretchRatio * prevStretch);
286       dtime = double(dframe) * factor;
287       se._stretchStretchedFrame = prevNewStretchFrame + dtime;
288       prevNewStretchFrame = se._stretchStretchedFrame;
289 
290       duntime = double(dframe) / factor;
291       se._stretchSquishedFrame = prevNewUnStretchFrame + duntime;
292       prevNewUnStretchFrame = se._stretchSquishedFrame;
293 
294 
295 
296       factor = (_samplerateRatio * prevSamplerate);
297       dtime = double(dframe) * factor;
298       se._samplerateStretchedFrame = prevNewSamplerateFrame + dtime;
299       prevNewSamplerateFrame = se._samplerateStretchedFrame;
300 
301       duntime = double(dframe) / factor;
302       se._samplerateSquishedFrame = prevNewUnSamplerateFrame + duntime;
303       prevNewUnSamplerateFrame = se._samplerateSquishedFrame;
304 
305 
306       prevFrame = thisFrame;
307 
308       if(se._type & StretchListItem::StretchEvent)
309         prevStretch = se._stretchRatio;
310       else
311         se._stretchRatio = prevStretch;
312 
313       if(se._type & StretchListItem::SamplerateEvent)
314         prevSamplerate = se._samplerateRatio;
315       else
316         se._samplerateRatio = prevSamplerate;
317 
318       if(se._type & StretchListItem::PitchEvent)
319         prevPitch = se._pitchRatio;
320       else
321         se._pitchRatio = prevPitch;
322     }
323   }
324 
325   // TODO
326   normalizeFrames();
327 
328   // Mark as validated, normalization is done.
329   _isNormalized = true;
330 
331 #ifdef DEBUG_TIMESTRETCH
332   dump();
333 #endif
334 }
335 
normalizeListRatios()336 void StretchList::normalizeListRatios()
337 {
338 
339 }
340 
341 //---------------------------------------------------------
342 //   clear
343 //---------------------------------------------------------
344 
clear()345 void StretchList::clear()
346 {
347   StretchList_t::clear();
348 
349   // Ensure that there is always an item at frame zero.
350   insert(std::pair<const MuseFrame_t, StretchListItem>
351     (0, StretchListItem(1.0, 1.0, 1.0,
352                         StretchListItem::StretchEvent |
353                         StretchListItem::SamplerateEvent |
354                         StretchListItem::PitchEvent)));
355 
356   // Technically it is normalized now, since StretchListItem
357   //  constructor fills in zeros for the frame values.
358   _isNormalized = true;
359 }
360 
361 //---------------------------------------------------------
362 //   eraseRange
363 //---------------------------------------------------------
364 
eraseRange(int types,MuseFrame_t sframe,MuseFrame_t eframe)365 void StretchList::eraseRange(int types, MuseFrame_t sframe, MuseFrame_t eframe)
366 {
367   if(sframe >= eframe)
368     return;
369   iStretchListItem se = lower_bound(sframe);
370   if(se == end())
371     return;
372   iStretchListItem ee = upper_bound(eframe);
373 
374   for(iStretchListItem ise = se; ise != ee; )
375   {
376     // Do not delete the item at zeroth frame.
377     if(ise->first == 0)
378     {
379       ++ise;
380       continue;
381     }
382 
383     ise->second._type &= ~types;
384     if(ise->second._type == 0)
385     {
386       iStretchListItem ise_save = ise;
387       erase(ise);
388       ise = ise_save;
389     }
390     else
391       ++ise;
392   }
393 
394   // Mark as invalidated, normalization is required.
395   _isNormalized = false;
396 
397   normalizeListFrames();
398 }
399 
400 //---------------------------------------------------------
401 //   read
402 //---------------------------------------------------------
403 
read(Xml & xml)404 void StretchList::read(Xml& xml)
405       {
406       bool ok;
407       for (;;) {
408             Xml::Token token = xml.parse();
409             const QString& tag = xml.s1();
410             switch (token) {
411                   case Xml::Error:
412                   case Xml::End:
413                         return;
414                   case Xml::Attribut:
415                           ERROR_TIMESTRETCH(stderr, "stretchlist unknown tag %s\n", tag.toLatin1().constData());
416                         break;
417                   case Xml::Text:
418                         {
419                           int len = tag.length();
420                           int i = 0;
421                           for(;;)
422                           {
423                                 while(i < len && (tag[i] == ',' || tag[i] == ' ' || tag[i] == '\n'))
424                                   ++i;
425                                 if(i == len)
426                                       break;
427 
428                                 QString fs;
429                                 while(i < len && tag[i] != ' ')
430                                 {
431                                   fs.append(tag[i]);
432                                   ++i;
433                                 }
434                                 if(i == len)
435                                       break;
436 
437                                 MuseFrame_t frame = fs.toLong(&ok);
438                                 if(!ok)
439                                 {
440                                   ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading frame string: %s\n", fs.toLatin1().constData());
441                                   break;
442                                 }
443 
444 
445                                 while(i < len && (tag[i] == ' ' || tag[i] == '\n'))
446                                   ++i;
447                                 if(i == len)
448                                       break;
449                                 QString stretchStr;
450                                 while(i < len && tag[i] != ' ' && tag[i] != ',')
451                                 {
452                                   stretchStr.append(tag[i]);
453                                   ++i;
454                                 }
455                                 double stretchVal = stretchStr.toDouble(&ok);
456                                 if(!ok)
457                                 {
458                                   ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading stretch ratio string: %s\n", stretchStr.toLatin1().constData());
459                                   break;
460                                 }
461 
462 
463                                 while(i < len && (tag[i] == ' ' || tag[i] == '\n'))
464                                   ++i;
465                                 if(i == len)
466                                       break;
467                                 QString SRStr;
468                                 while(i < len && tag[i] != ' ' && tag[i] != ',')
469                                 {
470                                   SRStr.append(tag[i]);
471                                   ++i;
472                                 }
473                                 double SRVal = SRStr.toDouble(&ok);
474                                 if(!ok)
475                                 {
476                                   ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading samplerate ratio string: %s\n", SRStr.toLatin1().constData());
477                                   break;
478                                 }
479 
480                                 while(i < len && (tag[i] == ' ' || tag[i] == '\n'))
481                                   ++i;
482                                 if(i == len)
483                                       break;
484                                 QString pitchStr;
485                                 while(i < len && tag[i] != ' ' && tag[i] != ',')
486                                 {
487                                   pitchStr.append(tag[i]);
488                                   ++i;
489                                 }
490                                 double pitchVal = pitchStr.toDouble(&ok);
491                                 if(!ok)
492                                 {
493                                   ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading pitch ratio string: %s\n", pitchStr.toLatin1().constData());
494                                   break;
495                                 }
496 
497 
498                                 while(i < len && (tag[i] == ' ' || tag[i] == '\n'))
499                                   ++i;
500                                 if(i == len)
501                                       break;
502                                 QString typeStr;
503                                 while(i < len && tag[i] != ' ' && tag[i] != ',')
504                                 {
505                                   typeStr.append(tag[i]);
506                                   ++i;
507                                 }
508                                 int typeVal = typeStr.toInt(&ok);
509                                 if(!ok)
510                                 {
511                                   ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading type string: %s\n", typeStr.toLatin1().constData());
512                                   break;
513                                 }
514 
515                                 // Defer normalize until tag end.
516                                 add(frame, StretchListItem(stretchVal, SRVal, pitchVal, typeVal), false);
517 
518                                 if(i == len)
519                                       break;
520                           }
521                         }
522                         break;
523                   case Xml::TagEnd:
524                         if (tag == "stretchlist")
525                         {
526                               normalizeListFrames();
527                               return;
528                         }
529                   default:
530                         break;
531                   }
532             }
533 
534       }
535 
536 //---------------------------------------------------------
537 //   write
538 //---------------------------------------------------------
539 
write(int level,Xml & xml) const540 void StretchList::write(int level, Xml& xml) const
541 {
542   if(empty())
543     return;
544 
545   xml.tag(level++, "stretchlist");
546   int i = 0;
547   QString seStr("%1 %2 %3 %4 %5, ");
548   for (const_iterator ise = cbegin(); ise != cend(); ++ise) {
549         xml.nput(level,
550                   seStr.arg(ise->first)
551                       .arg(ise->second._stretchRatio)
552                       .arg(ise->second._samplerateRatio)
553                       .arg(ise->second._pitchRatio)
554                       .arg(ise->second._type)
555                       .toLatin1().constData());
556         ++i;
557         if (i >= 3) {
558               xml.put(level, "");
559               i = 0;
560               }
561         }
562   if (i)
563         xml.put(level, "");
564   xml.etag(level--, "stretchlist");
565 }
566 
567 //---------------------------------------------------------
568 //   dump
569 //---------------------------------------------------------
570 
dump() const571 void StretchList::dump() const
572 {
573   INFO_TIMESTRETCH(stderr, "\nStretchList: isNormalized:%d\n", _isNormalized);
574   for(const_iterator i = cbegin(); i != cend(); ++i)
575   {
576     INFO_TIMESTRETCH(stderr, "frame:%6ld StretchRatio:%f SamplerateRatio:%f PitchRatio:%f "
577                     "stretchedFrame:%f squishedFrame:%f\n",
578       i->first, i->second._stretchRatio, i->second._samplerateRatio, i->second._pitchRatio,
579       i->second._finStretchedFrame, i->second._finSquishedFrame);
580   }
581 }
582 
583 // ------------------------------------------
584 //  Intrinsic functions:
585 //-------------------------------------------
586 
setStartFrame(MuseFrame_t frame,bool do_normalize)587 void StretchList::setStartFrame(MuseFrame_t frame, bool do_normalize)
588 {
589   _startFrame = frame;
590 
591   // Mark as invalidated, normalization is required.
592   _isNormalized = false;
593 
594   if(do_normalize)
595     normalizeListFrames();
596 }
597 
setEndFrame(MuseFrame_t frame,bool do_normalize)598 void StretchList::setEndFrame(MuseFrame_t frame, bool do_normalize)
599 {
600   _endFrame = frame;
601 
602   // Mark as invalidated, normalization is required.
603   _isNormalized = false;
604 
605   if(do_normalize)
606     normalizeListFrames();
607 }
608 
setStretchedEndFrame(MuseFrame_t frame,bool do_normalize)609 void StretchList::setStretchedEndFrame(MuseFrame_t frame, bool do_normalize)
610 {
611   _stretchedEndFrame = frame;
612 
613   // Mark as invalidated, normalization is required.
614   _isNormalized = false;
615 
616   if(do_normalize)
617     normalizeListFrames();
618 }
619 
setSquishedEndFrame(MuseFrame_t frame,bool do_normalize)620 void StretchList::setSquishedEndFrame(MuseFrame_t frame, bool do_normalize)
621 {
622   _squishedEndFrame = frame;
623 
624   // Mark as invalidated, normalization is required.
625   _isNormalized = false;
626 
627   if(do_normalize)
628     normalizeListFrames();
629 }
630 
ratio(StretchListItem::StretchEventType type) const631 double StretchList::ratio(StretchListItem::StretchEventType type) const
632 {
633   switch(type)
634   {
635     case StretchListItem::StretchEvent:
636       return _stretchRatio;
637     break;
638 
639     case StretchListItem::SamplerateEvent:
640       return _samplerateRatio;
641     break;
642 
643     case StretchListItem::PitchEvent:
644       return _pitchRatio;
645     break;
646   }
647   return 1.0;
648 }
649 
setRatio(StretchListItem::StretchEventType type,double ratio,bool do_normalize)650 void StretchList::setRatio(StretchListItem::StretchEventType type, double ratio, bool do_normalize)
651 {
652   switch(type)
653   {
654     case StretchListItem::StretchEvent:
655       _stretchRatio = ratio;
656     break;
657 
658     case StretchListItem::SamplerateEvent:
659       _samplerateRatio = ratio;
660     break;
661 
662     case StretchListItem::PitchEvent:
663       _pitchRatio = ratio;
664     break;
665   }
666 
667   // Mark as invalidated, normalization is required.
668   _isNormalized = false;
669 
670   if(do_normalize)
671     normalizeListFrames();
672 }
673 
674 // ------------------------------------------
675 //  List functions:
676 //-------------------------------------------
677 
findEvent(int types,MuseFrame_t frame)678 iStretchListItem StretchList::findEvent(int types, MuseFrame_t frame)
679 {
680   iStretchListItemPair res = equal_range(frame);
681   for(iStretchListItem ise = res.first; ise != res.second; ++ise)
682   {
683     if(ise->second._type & types)
684       return ise;
685   }
686   return end();
687 }
688 
cFindEvent(int types,MuseFrame_t frame) const689 ciStretchListItem StretchList::cFindEvent(int types, MuseFrame_t frame) const
690 {
691   const StretchList* sl = this;
692   ciStretchListItemPair res = sl->equal_range(frame);  // FIXME Calls non-const version unless cast ??
693   for(ciStretchListItem ise = res.first; ise != res.second; ++ise)
694   {
695     if(ise->second._type & types)
696       return ise;
697   }
698   return sl->end();
699 }
700 
previousEvent(int types,iStretchListItem item)701 iStretchListItem StretchList::previousEvent(int types, iStretchListItem item)
702 {
703   iStretchListItem i = item;
704   while(i != begin())
705   {
706     --i;
707     if(i->second._type & types)
708       return i;
709   }
710   return end();
711 }
712 
cPreviousEvent(int types,ciStretchListItem item) const713 ciStretchListItem StretchList::cPreviousEvent(int types, ciStretchListItem item) const
714 {
715   ciStretchListItem i = item;
716   while(i != cbegin())
717   {
718     --i;
719     if(i->second._type & types)
720       return i;
721   }
722   return cend();
723 }
724 
nextEvent(int types,iStretchListItem item)725 iStretchListItem StretchList::nextEvent(int types, iStretchListItem item)
726 {
727   iStretchListItem i = item;
728   while(i != end())
729   {
730     ++i;
731     if(i->second._type & types)
732       return i;
733   }
734   return end();
735 }
736 
cNextEvent(int types,ciStretchListItem item) const737 ciStretchListItem StretchList::cNextEvent(int types, ciStretchListItem item) const
738 {
739   const StretchList* sl = this;
740   ciStretchListItem i = item;
741   while(i != sl->end())
742   {
743     ++i;
744     if(i->second._type & types)
745       return i;
746   }
747   return sl->end();
748 }
749 
750 
751 
752 //---------------------------------------------------------
753 //   ratioAt
754 //---------------------------------------------------------
755 
ratioAt(StretchListItem::StretchEventType type,MuseFrame_t frame) const756 double StretchList::ratioAt(StretchListItem::StretchEventType type, MuseFrame_t frame) const
757 {
758   // If the zeroth frame is the only item, its ratios must (should) all be at 1.0 right now
759   //  so they will be ignored.
760   if(size() == 1)
761     return 1.0;
762 
763   const_iterator i = upper_bound(frame);
764   if(i == cbegin())
765     return 1.0;
766   --i;
767 
768   switch(type)
769   {
770     case StretchListItem::StretchEvent:
771       return i->second._stretchRatio;
772     break;
773 
774     case StretchListItem::SamplerateEvent:
775       return i->second._samplerateRatio;
776     break;
777 
778     case StretchListItem::PitchEvent:
779       return i->second._pitchRatio;
780     break;
781   }
782 
783   return 1.0;
784 }
785 
786 //---------------------------------------------------------
787 //   setRatioAt
788 //---------------------------------------------------------
789 
setRatioAt(StretchListItem::StretchEventType type,MuseFrame_t frame,double ratio,bool do_normalize)790 void StretchList::setRatioAt(StretchListItem::StretchEventType type, MuseFrame_t frame, double ratio, bool do_normalize)
791 {
792   add(type, frame, ratio, do_normalize);
793 }
794 
setRatioAt(StretchListItem::StretchEventType type,iStretchListItem item,double ratio,bool do_normalize)795 void StretchList::setRatioAt(StretchListItem::StretchEventType type, iStretchListItem item, double ratio, bool do_normalize)
796 {
797   item->second._type |= type;
798   switch(type)
799   {
800     case StretchListItem::StretchEvent:
801       item->second._stretchRatio = ratio;
802     break;
803 
804     case StretchListItem::SamplerateEvent:
805       item->second._samplerateRatio = ratio;
806     break;
807 
808     case StretchListItem::PitchEvent:
809       item->second._pitchRatio = ratio;
810     break;
811   }
812 
813   // Mark as invalidated, normalization is required.
814   _isNormalized = false;
815 
816   if(do_normalize)
817     normalizeListFrames();
818 }
819 
addRatioAt(StretchListItem::StretchEventType type,MuseFrame_t frame,double ratio,bool do_normalize)820 void StretchList::addRatioAt(StretchListItem::StretchEventType type, MuseFrame_t frame, double ratio, bool do_normalize)
821 {
822   add(type, frame, ratio, do_normalize);
823 }
824 
delRatioAt(int types,MuseFrame_t frame,bool do_normalize)825 void StretchList::delRatioAt(int types, MuseFrame_t frame, bool do_normalize)
826 {
827   del(types, frame, do_normalize);
828 }
829 
830 //---------------------------------------------------------
831 //   stretch
832 //---------------------------------------------------------
833 
stretch(MuseFrame_t frame,int type) const834 double StretchList::stretch(MuseFrame_t frame, int type) const
835 {
836   MuseFrame_t prevFrame;
837   double prevNewFrame = frame;
838   double prevStretch;
839   double prevSamplerate;
840   double dtime = 0.0;
841 
842   const_iterator i = upper_bound(frame);
843   if(i == cbegin())
844     return frame;
845 
846   --i;
847   prevFrame = i->first;
848   prevStretch = i->second._stretchRatio;
849   prevSamplerate = i->second._samplerateRatio;
850   const MuseFrame_t dframe = frame - prevFrame;
851 
852   // Full conversion requested.
853   if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent))
854   {
855     prevNewFrame = i->second._finStretchedFrame;
856     dtime = double(dframe) * (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch);
857   }
858   // Stretch only.
859   else if(type & StretchListItem::StretchEvent)
860   {
861     prevNewFrame = i->second._stretchStretchedFrame;
862     dtime   = double(dframe) / (_stretchRatio * prevStretch);
863   }
864   // Samplerate only.
865   else if(type & StretchListItem::SamplerateEvent)
866   {
867     prevNewFrame = i->second._samplerateStretchedFrame;
868     dtime   = double(dframe) * _samplerateRatio * prevSamplerate;
869   }
870 
871   return prevNewFrame + dtime;
872 }
873 
stretch(double frame,int type) const874 double StretchList::stretch(double frame, int type) const
875 {
876   MuseFrame_t prevFrame;
877   double prevNewFrame = frame;
878   double prevStretch;
879   double prevSamplerate;
880   double dtime = 0.0;
881 
882   const_iterator i = upper_bound(frame);
883   if(i == cbegin())
884     return frame;
885 
886   --i;
887   prevFrame = i->first;
888   prevStretch = i->second._stretchRatio;
889   prevSamplerate = i->second._samplerateRatio;
890   const double dframe = frame - (double)prevFrame;
891 
892   // Full conversion requested.
893   if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent))
894   {
895     prevNewFrame = i->second._finStretchedFrame;
896     dtime = dframe * (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch);
897   }
898   // Stretch only.
899   else if(type & StretchListItem::StretchEvent)
900   {
901     prevNewFrame = i->second._stretchStretchedFrame;
902     dtime   = dframe / (_stretchRatio * prevStretch);
903   }
904   // Samplerate only.
905   else if(type & StretchListItem::SamplerateEvent)
906   {
907     prevNewFrame = i->second._samplerateStretchedFrame;
908     dtime   = dframe * _samplerateRatio * prevSamplerate;
909   }
910 
911   return prevNewFrame + dtime;
912 }
913 
squish(MuseFrame_t frame,int type) const914 double StretchList::squish(MuseFrame_t frame, int type) const
915 {
916   MuseFrame_t prevFrame;
917   double prevNewUnFrame = frame;
918   double prevStretch;
919   double prevSamplerate;
920   double dtime = 0.0;
921 
922   const_iterator i = upper_bound(frame);
923   if(i == cbegin())
924     return frame;
925 
926   --i;
927   prevFrame = i->first;
928   prevStretch = i->second._stretchRatio;
929   prevSamplerate = i->second._samplerateRatio;
930   const MuseFrame_t dframe = frame - prevFrame;
931 
932   // Full conversion requested.
933   if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent))
934   {
935     prevNewUnFrame = i->second._finSquishedFrame;
936     dtime = double(dframe) * (_stretchRatio * prevStretch) / (_samplerateRatio * prevSamplerate);
937   }
938   // Stretch only.
939   else if(type & StretchListItem::StretchEvent)
940   {
941     prevNewUnFrame = i->second._stretchSquishedFrame;
942     dtime   = double(dframe) * (_stretchRatio * prevStretch);
943   }
944   // Samplerate only.
945   else if(type & StretchListItem::SamplerateEvent)
946   {
947     prevNewUnFrame = i->second._samplerateSquishedFrame;
948     dtime   = double(dframe) / (_samplerateRatio * prevSamplerate);
949   }
950 
951   return prevNewUnFrame + dtime;
952 }
953 
squish(double frame,int type) const954 double StretchList::squish(double frame, int type) const
955 {
956   MuseFrame_t prevFrame;
957   double prevNewUnFrame = frame;
958   double prevStretch;
959   double prevSamplerate;
960   double dtime = 0.0;
961 
962   const_iterator i = upper_bound(frame);
963   if(i == cbegin())
964     return frame;
965 
966   --i;
967   prevFrame = i->first;
968   prevStretch = i->second._stretchRatio;
969   prevSamplerate = i->second._samplerateRatio;
970   const double dframe = frame - (double)prevFrame;
971 
972   // Full conversion requested.
973   if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent))
974   {
975     prevNewUnFrame = i->second._finSquishedFrame;
976     dtime = dframe * (_stretchRatio * prevStretch) / (_samplerateRatio * prevSamplerate);
977   }
978   // Stretch only.
979   else if(type & StretchListItem::StretchEvent)
980   {
981     prevNewUnFrame = i->second._stretchSquishedFrame;
982     dtime   = dframe * (_stretchRatio * prevStretch);
983   }
984   // Samplerate only.
985   else if(type & StretchListItem::SamplerateEvent)
986   {
987     prevNewUnFrame = i->second._samplerateSquishedFrame;
988     dtime   = dframe / (_samplerateRatio * prevSamplerate);
989   }
990 
991   return prevNewUnFrame + dtime;
992 }
993 
994 //---------------------------------------------------------
995 //   unStretch
996 //---------------------------------------------------------
997 
unStretch(double frame,int type) const998 MuseFrame_t StretchList::unStretch(double frame, int type) const
999 {
1000   if(empty())
1001     return frame;
1002 
1003   MuseFrame_t prevFrame;
1004   double prevNewFrame = frame;
1005   double prevStretch;
1006   double prevSamplerate;
1007   double factor = 1.0;
1008 
1009   const_iterator e;
1010   for(e = cbegin(); e != cend(); ++e)
1011   {
1012     if((type & StretchListItem::StretchEvent) &&    // Full conversion requested.
1013         (type & StretchListItem::SamplerateEvent))
1014     {
1015       if(frame < e->second._finStretchedFrame)
1016         break;
1017       else
1018         continue;
1019     }
1020     else if(type & StretchListItem::StretchEvent)   // Only stretch conversion requested.
1021     {
1022       if(frame < e->second._stretchStretchedFrame)
1023         break;
1024       else
1025         continue;
1026     }
1027     else if(type & StretchListItem::SamplerateEvent) // Only samplerate conversion requested.
1028     {
1029       if(frame < e->second._samplerateStretchedFrame)
1030         break;
1031       else
1032         continue;
1033     }
1034   }
1035 
1036   if(e == cbegin())
1037     return frame;
1038 
1039   --e;
1040   prevFrame = e->first;
1041   prevStretch = e->second._stretchRatio;
1042   prevSamplerate = e->second._samplerateRatio;
1043 
1044 
1045   // Full conversion requested.
1046   if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent))
1047   {
1048     prevNewFrame = e->second._finStretchedFrame;
1049     factor = (_stretchRatio * prevStretch) / (_samplerateRatio * prevSamplerate);
1050   }
1051   // Stretch only.
1052   else if(type & StretchListItem::StretchEvent)
1053   {
1054     prevNewFrame = e->second._stretchStretchedFrame;
1055     factor = (_stretchRatio * prevStretch);
1056   }
1057   // Samplerate only.
1058   else if(type & StretchListItem::SamplerateEvent)
1059   {
1060     prevNewFrame = e->second._samplerateStretchedFrame;
1061     factor = 1.0 / (_samplerateRatio * prevSamplerate);
1062   }
1063 
1064   return prevFrame + lrint((frame - prevNewFrame) * factor);
1065 }
1066 
1067 //---------------------------------------------------------
1068 //   unStretch
1069 //---------------------------------------------------------
1070 
unSquish(double frame,int type) const1071 MuseFrame_t StretchList::unSquish(double frame, int type) const
1072 {
1073   if(empty())
1074     return frame;
1075 
1076   MuseFrame_t prevFrame;
1077   double prevNewUnFrame = frame;
1078   double prevStretch;
1079   double prevSamplerate;
1080   double factor = 1.0;
1081 
1082   const_iterator e;
1083   for(e = cbegin(); e != cend(); ++e)
1084   {
1085     if((type & StretchListItem::StretchEvent) &&    // Full conversion requested.
1086         (type & StretchListItem::SamplerateEvent))
1087     {
1088       if(frame < e->second._finSquishedFrame)
1089         break;
1090       else
1091         continue;
1092     }
1093     else if(type & StretchListItem::StretchEvent)   // Only stretch conversion requested.
1094     {
1095       if(frame < e->second._stretchSquishedFrame)
1096         break;
1097       else
1098         continue;
1099     }
1100     else if(type & StretchListItem::SamplerateEvent) // Only samplerate conversion requested.
1101     {
1102       if(frame < e->second._samplerateSquishedFrame)
1103         break;
1104       else
1105         continue;
1106     }
1107   }
1108 
1109   if(e == cbegin())
1110     return frame;
1111 
1112   --e;
1113   prevFrame = e->first;
1114   prevStretch = e->second._stretchRatio;
1115   prevSamplerate = e->second._samplerateRatio;
1116 
1117   // Full conversion requested.
1118   if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent))
1119   {
1120     prevNewUnFrame = e->second._finSquishedFrame;
1121     factor = (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch);
1122   }
1123   // Stretch only.
1124   else if(type & StretchListItem::StretchEvent)
1125   {
1126     prevNewUnFrame = e->second._stretchSquishedFrame;
1127     factor = 1.0 / (_stretchRatio * prevStretch);
1128   }
1129   // Samplerate only.
1130   else if(type & StretchListItem::SamplerateEvent)
1131   {
1132     prevNewUnFrame = e->second._samplerateSquishedFrame;
1133     factor = (_samplerateRatio * prevSamplerate);
1134   }
1135 
1136   // FIXME: Hm, lrint? Try returning double.
1137   return prevFrame + lrint((frame - prevNewUnFrame) * factor);
1138 }
1139 
testDelListOperation(int types,MuseFrame_t frame) const1140 StretchListInfo StretchList::testDelListOperation(int types, MuseFrame_t frame) const
1141 {
1142   // The policy is such that if (after deletion) there are still user items (non zeroth item) of a given type,
1143   //  the list is said to still be in that state (stretched, resampled, shifted etc),
1144   //  even if all the items' ratios are 1.0.
1145   StretchListInfo info;
1146   MuseFrame_t fr;
1147   // If any intrinsic value has a stretch or samplerate other than 1.0,
1148   //  the map is stretched, a stretcher or samplerate converter must be engaged.
1149   info._isStretched = (_stretchRatio != 1.0);
1150   info._isResampled = (_samplerateRatio != 1.0);
1151   info._isPitchShifted = (_pitchRatio != 1.0);
1152   for(const_iterator ise = cbegin(); ise != cend(); ++ise)
1153   {
1154     fr = ise->first;
1155     // Ignore the special zeroth frame.
1156     // If the zeroth frame is the only item, its ratios must (should) all be at 1.0 right now
1157     //  so they will be ignored.
1158     if(fr == 0)
1159       continue;
1160 
1161     const StretchListItem& se = ise->second;
1162     if(((se._type & StretchListItem::StretchEvent) &&
1163        //(types & StretchListItem::StretchEvent) &&
1164        (!(types & StretchListItem::StretchEvent) ||
1165        fr != frame))) //&&
1166        //se._stretchRatio != 1.0))
1167       info._isStretched = true;
1168 
1169     if(((se._type & StretchListItem::SamplerateEvent) &&
1170        //(types & StretchListItem::SamplerateEvent) &&
1171        (!(types & StretchListItem::SamplerateEvent) ||
1172        fr != frame))) //&&
1173        //se._samplerateRatio != 1.0))
1174       info._isResampled = true;
1175 
1176     if(((se._type & StretchListItem::PitchEvent) &&
1177        //(types & StretchListItem::PitchEvent) &&
1178        (!(types & StretchListItem::PitchEvent) ||
1179        fr != frame))) //&&
1180        //se._pitchRatio != 1.0))
1181       info._isPitchShifted = true;
1182   }
1183   return info;
1184 }
1185 
1186 } // namespace MusECore
1187