1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520 |
- // Allegro: music representation system, with
- // extensible in-memory sequence structure
- // upward compatible with MIDI
- // implementations in C++ and Serpent
- // external, text-based representation
- // compatible with Aura
- //
- /* CHANGE LOG:
- 04 apr 03 -- fixed bug in add_track that caused infinite loop
- */
- #include "assert.h"
- #include "stdlib.h"
- #include "stdio.h"
- #include "string.h"
- #include "memory.h"
- #include <iostream>
- #include <fstream>
- using namespace std;
- #include "allegro.h"
- #include "algrd_internal.h"
- #include "algsmfrd_internal.h"
- // #include "trace.h" -- only needed for debugging
- #include "math.h"
- #define STREQL(x, y) (strcmp(x, y) == 0)
- #define MAX(x, y) ((x) > (y) ? (x) : (y))
- #define ROUND(x) ((int) ((x) + 0.5))
- // 4311 is type cast ponter to long warning
- // 4996 is warning against strcpy
- // 4267 is size_t to long warning
- //#pragma warning(disable: 4311 4996 4267)
- Alg_atoms symbol_table;
- Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables
- Serial_write_buffer Alg_track::ser_write_buf;
- bool within(double d1, double d2, double epsilon)
- {
- d1 -= d2;
- return d1 < epsilon && d1 > -epsilon;
- }
- char *heapify(const char *s)
- {
- char *h = new char[strlen(s) + 1];
- strcpy(h, s);
- return h;
- }
- void Alg_atoms::expand()
- {
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- Alg_attribute *new_atoms = new Alg_attribute[maxlen];
- // now do copy
- memcpy(new_atoms, atoms, len * sizeof(Alg_attribute));
- if (atoms) delete[] atoms;
- atoms = new_atoms;
- }
- // insert_new -- insert an attribute name and type
- //
- // attributes are stored as a string consisting of the type
- // (a char) followed by the attribute name. This makes it
- // easy to retrieve the type or the name or both.
- //
- Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type)
- {
- if (len == maxlen) expand();
- char *h = new char[strlen(name) + 2];
- strcpy(h + 1, name);
- *h = attr_type;
- atoms[len++] = h;
- return h;
- }
- Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr)
- {
- // should use hash algorithm
- for (int i = 0; i < len; i++) {
- if (STREQL(attr, atoms[i])) {
- return atoms[i];
- }
- }
- return insert_new(attr + 1, attr[0]);
- }
- Alg_attribute Alg_atoms::insert_string(const char *name)
- {
- char attr_type = name[strlen(name) - 1];
- for (int i = 0; i < len; i++) {
- if (attr_type == atoms[i][0] &&
- STREQL(name, atoms[i] + 1)) {
- return atoms[i];
- }
- }
- return insert_new(name, attr_type);
- }
- void Alg_parameter::copy(Alg_parameter_ptr parm)
- {
- *this = *parm; // copy all fields
- // if the value is a string, copy the string
- if (attr_type() == 's') {
- s = heapify(s);
- }
- }
- void Alg_parameter::show()
- {
- switch (attr[0]) {
- case 'r':
- printf("%s:%g", attr_name(), r);
- break;
- case 's':
- printf("%s:%s", attr_name(), s);
- break;
- case 'i':
- printf("%s:%ld", attr_name(), i);
- break;
- case 'l':
- printf("%s:%s", attr_name(), (l ? "t" : "f"));
- break;
- case 'a':
- printf("%s:%s", attr_name(), a);
- break;
- }
- }
- Alg_parameter::~Alg_parameter()
- {
- if (attr_type() == 's' && s) {
- delete[] s;
- }
- }
- void Alg_parameters::insert_real(Alg_parameters **list, const char *name,
- double r)
- {
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.r = r;
- assert(a->parm.attr_type() == 'r');
- }
- void Alg_parameters::insert_string(Alg_parameters **list, const char *name,
- const char *s)
- {
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- // string is deleted when parameter is deleted
- a->parm.s = heapify(s);
- assert(a->parm.attr_type() == 's');
- }
- void Alg_parameters::insert_integer(Alg_parameters **list, const char *name,
- long i)
- {
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.i = i;
- assert(a->parm.attr_type() == 'i');
- }
- void Alg_parameters::insert_logical(Alg_parameters **list, const char *name,
- bool l)
- {
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.l = l;
- assert(a->parm.attr_type() == 'l');
- }
- void Alg_parameters::insert_atom(Alg_parameters **list, const char *name,
- const char *s)
- {
- Alg_parameters_ptr a = new Alg_parameters(*list);
- *list = a;
- a->parm.set_attr(symbol_table.insert_string(name));
- a->parm.a = symbol_table.insert_string(s);
- assert(a->parm.attr_type() == 'a');
- }
- Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list,
- const char *name)
- {
- while (*list) {
- if (STREQL((*list)->parm.attr_name(), name)) {
- Alg_parameters_ptr p = *list;
- *list = p->next;
- p->next = nullptr;
- return p; // caller should free this pointer
- }
- list = &((*list)->next);
- }
- return nullptr;
- }
- Alg_parameter_ptr Alg_parameters::find(Alg_attribute attr)
- {
- assert(attr);
- Alg_parameters_ptr temp = this;
- while (temp) {
- if (temp->parm.attr == attr) {
- return &(temp->parm);
- }
- }
- return nullptr;
- }
- int Alg_event::get_type_code()
- {
- if (!is_note()) {
- const char* attr = get_attribute();
- if (STREQL(attr, "gater")) // volume change
- return ALG_GATE;
- if (STREQL(attr, "bendr")) // pitch bend
- return ALG_BEND;
- if (strncmp(attr, "control", 7) == 0) // control change
- // note that midi control changes have attributes of the form
- // "control<n>" where n is the decimal number (as a character string)
- // of the midi controller, e.g. control2 is the breath controller.
- // We don't check for decimal numbers in the range 0-127, so any
- // attribute that begins with "control" is an ALG_CONTROL:
- return ALG_CONTROL;
- if (STREQL(attr, "programi")) // program change
- return ALG_PROGRAM;
- if (STREQL(attr, "pressurer")) // pressure change
- return ALG_PRESSURE;
- if (STREQL(attr, "keysigi")) // key signature
- return ALG_KEYSIG;
- if (STREQL(attr, "timesig_numi")) // time signature numerator
- return ALG_TIMESIG_NUM;
- if (STREQL(attr, "timesig_deni")) // time signature denominator
- return ALG_TIMESIG_DEN;
- return ALG_OTHER;
- }
- return ALG_NOTE; // it is a note
- }
- void Alg_event::set_parameter(Alg_parameter_ptr new_parameter)
- {
- Alg_parameter_ptr parm;
- if (is_note()) {
- Alg_note_ptr note = (Alg_note_ptr) this;
- parm = note->parameters->find(new_parameter->attr);
- if (!parm) {
- note->parameters = new Alg_parameters(note->parameters);
- parm = &(note->parameters->parm);
- }
- } else { // update
- Alg_update_ptr update = (Alg_update_ptr) this;
- parm = &(update->parameter);
- }
- parm->copy(new_parameter); // copy entire parameter
- }
- void Alg_event::set_string_value(const char *a, const char *value)
- {
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 's');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.s = value;
- set_parameter(&parm);
- parm.s = nullptr; // do this to prevent string from being freed
- }
- void Alg_event::set_real_value(const char *a, double value)
- {
- assert(a); // must be non-null
- // attr is like a, but it has the type code prefixed for
- // fast lookup, and it is a unique string in symbol_table
- // e.g. a="attackr" -> attr="rattackr"
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'r');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.r = value;
- set_parameter(&parm);
- // since type is 'r' we don't have to NULL the string
- }
- void Alg_event::set_logical_value(const char *a, bool value)
- {
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'l');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.l = value;
- set_parameter(&parm);
- // since type is 'l' we don't have to NULL the string
- }
- void Alg_event::set_integer_value(const char *a, long value)
- {
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'i');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.i = value;
- set_parameter(&parm);
- // since tpye is 'i' we don't have to NULL the string
- }
- void Alg_event::set_atom_value(const char *a, const char *value)
- {
- assert(a); // must be non-null
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(attr[0] == 'a');
- Alg_parameter parm;
- parm.set_attr(attr);
- parm.a = value;
- set_parameter(&parm);
- /* since type is 'a' we don't have to null the string */
- }
- float Alg_event::get_pitch()
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->pitch;
- }
- float Alg_event::get_loud()
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->loud;
- }
- double Alg_event::get_start_time()
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->time;
- }
- double Alg_event::get_end_time()
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->time + note->dur;
- }
- double Alg_event::get_duration()
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- return note->dur;
- }
- void Alg_event::set_pitch(float p)
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- note->pitch = p;
- }
- void Alg_event::set_loud(float l)
- {
- assert(is_note());
- Alg_note *note = (Alg_note *) this;
- note->loud = l;
- }
- void Alg_event::set_duration(double d)
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- note->dur = d;
- }
- bool Alg_event::has_attribute(const char *a)
- {
- assert(is_note());
- assert(a); // must be non-null
- Alg_note* note = (Alg_note *) this;
- Alg_attribute attr = symbol_table.insert_string(a);
- Alg_parameter_ptr parm = note->parameters->find(attr);
- return parm != nullptr;
- }
- char Alg_event::get_attribute_type(const char *a)
- {
- assert(is_note());
- assert(a);
- return a[strlen(a) - 1];
- }
- const char *Alg_event::get_string_value(const char *a, const char *value)
- {
- assert(is_note());
- assert(a); // must be non-null
- Alg_note* note = (Alg_note *) this;
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(a[0] == 's'); // must be of type string
- Alg_parameter_ptr parm = note->parameters->find(attr);
- if (parm) return parm->s;
- return value;
- }
- double Alg_event::get_real_value(const char *a, double value)
- {
- assert(is_note());
- assert(a);
- Alg_note* note = (Alg_note *) this;
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(a[0] == 'r'); // must be of type real
- Alg_parameter_ptr parm = note->parameters->find(attr);
- if (parm) return parm->r;
- return value;
- }
- bool Alg_event::get_logical_value(const char *a, bool value)
- {
- assert(is_note());
- assert(a);
- Alg_note* note = (Alg_note *) this;
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(a[0] == 'l'); // must be of type logical
- Alg_parameter_ptr parm = note->parameters->find(attr);
- if (parm) return parm->l;
- return value;
- }
- long Alg_event::get_integer_value(const char *a, long value)
- {
- assert(is_note());
- assert(a);
- Alg_note* note = (Alg_note *) this;
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(a[0] == 'i'); // must be of type integer
- Alg_parameter_ptr parm = note->parameters->find(attr);
- if (parm) return parm->i;
- return value;
- }
- const char *Alg_event::get_atom_value(const char *a, const char *value)
- {
- assert(is_note());
- assert(a);
- Alg_note* note = (Alg_note *) this;
- Alg_attribute attr = symbol_table.insert_string(a);
- assert(a[0] == 'a'); // must be of type atom
- Alg_parameter_ptr parm = note->parameters->find(attr);
- if (parm) return parm->a;
- // if default is a string, convert to an atom (unique
- // string in symbol table) and return it
- return (value == nullptr ? nullptr :
- symbol_table.insert_string(value));
- }
- void Alg_event::delete_attribute(const char *a)
- {
- assert(is_note());
- Alg_note* note = (Alg_note *) this;
- Alg_parameters::remove_key(&(note->parameters), a);
- }
- const char *Alg_event::get_attribute()
- // Note: this returns a string, not an Alg_attribute
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- return update->parameter.attr_name();
- }
- char Alg_event::get_update_type()
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- return update->parameter.attr_type();
- }
- const char *Alg_event::get_string_value()
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 's');
- return update->parameter.s;
- }
- double Alg_event::get_real_value()
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'r');
- return update->parameter.r;
- }
- bool Alg_event::get_logical_value()
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'l');
- return update->parameter.l;
- }
- long Alg_event::get_integer_value()
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'i');
- return update->parameter.i;
- }
- const char *Alg_event::get_atom_value()
- {
- assert(is_update());
- Alg_update* update = (Alg_update *) this;
- assert(get_update_type() == 'a');
- return update->parameter.a;
- }
- bool Alg_event::overlap(double t, double len, bool all)
- {
- // event starts within region
- if (time >= t && time <= t + len - ALG_EPS)
- return true;
- if (all && is_note()) {
- double dur = ((Alg_note_ptr) this)->dur;
- // note overlaps with region
- if (time < t && time + dur - ALG_EPS > t)
- return true;
- }
- // does not overlap
- return false;
- }
- Alg_note::Alg_note(Alg_note_ptr note)
- {
- *this = *note; // copy all fields
- // parameters is now a shared pointer. We need to copy the
- // parameters
- Alg_parameters_ptr next_param_ptr = parameters;
- while (next_param_ptr) {
- Alg_parameters_ptr new_params = new Alg_parameters(next_param_ptr->next);
- new_params->parm.copy(&(next_param_ptr->parm)); // copy the attribute and value
- next_param_ptr = new_params->next;
- }
- }
- Alg_note::~Alg_note()
- {
- while (parameters) {
- Alg_parameters_ptr to_delete = parameters;
- parameters = parameters->next;
- delete to_delete;
- }
- }
- void Alg_note::show()
- {
- printf("Alg_note: time %g, chan %ld, dur %g, key %ld, "
- "pitch %g, loud %g, attributes ",
- time, chan, dur, key, pitch, loud);
- Alg_parameters_ptr parms = parameters;
- while (parms) {
- parms->parm.show();
- printf(" ");
- parms = parms->next;
- }
- printf("\n");
- }
- Alg_update::Alg_update(Alg_update_ptr update)
- {
- *this = *update; // copy all fields
- // parameter requires careful copy to possibly duplicate string value:
- this->parameter.copy(&(update->parameter));
- }
- void Alg_update::show()
- {
- printf("Alg_update: ");
- parameter.show();
- printf("\n");
- }
- void Alg_events::expand()
- {
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- Alg_event_ptr *new_events = new Alg_event_ptr[maxlen];
- // now do copy
- memcpy(new_events, events, len * sizeof(Alg_event_ptr));
- if (events) delete[] events;
- events = new_events;
- }
- void Alg_events::insert(Alg_event_ptr event)
- {
- if (maxlen <= len) {
- expand();
- }
- // Note: if the new event is the last one, the assignment
- // events[i] = event; (below) will never execute, so just
- // in case, we do the assignment here. events[len] will
- // be replaced during the memmove() operation below if
- // this is not the last event.
- events[len] = event;
- len++;
- // find insertion point: (this could be a binary search)
- for (int i = 0; i < len; i++) {
- if (events[i]->time > event->time) {
- // insert event at i
- memmove(&events[i + 1], &events[i],
- sizeof(Alg_event_ptr) * (len - i - 1));
- events[i] = event;
- return;
- }
- }
- }
- Alg_event_ptr Alg_events::uninsert(long index)
- {
- assert(0 <= index && index < len);
- Alg_event_ptr event = events[index];
- //printf("memmove: %x from %x (%d)\n", events + index, events + index + 1,
- // sizeof(Alg_event_ptr) * (len - index - 1));
- memmove(events + index, events + index + 1,
- sizeof(Alg_event_ptr) * (len - index - 1));
- len--;
- return event;
- }
- void Alg_events::append(Alg_event_ptr event)
- {
- if (maxlen <= len) {
- expand();
- }
- events[len++] = event;
- // keep track of last note_off time
- if (event->is_note()) {
- Alg_note_ptr note = (Alg_note_ptr) event;
- double note_off = note->time + note->dur;
- if (note_off > last_note_off)
- last_note_off = note_off;
- }
- }
- Alg_events::~Alg_events()
- {
- assert(!in_use);
- // individual events are not deleted, only the array
- if (events) {
- delete[] events;
- }
- }
- Alg_event_list::Alg_event_list(Alg_track *owner)
- {
- events_owner = owner;
- sequence_number = owner->sequence_number;
- beat_dur = 0.0; real_dur = 0.0; type = 'e';
- }
- Alg_event_ptr const &Alg_event_list::operator [](int i)
- {
- assert(i >= 0 && i < len);
- return events[i];
- }
- Alg_event_list::~Alg_event_list()
- {
- // note that the events contained in the list are not destroyed
- }
- void Alg_event_list::set_start_time(Alg_event *event, double t)
- {
- // For Alg_event_list, find the owner and do the update there
- // For Alg_track, change the time and move the event to the right place
- // For Alg_seq, find the track and do the update there
-
- long index = 0, i;
- Alg_track_ptr track_ptr = nullptr;
- if (type == 'e') { // this is an Alg_event_list
- // make sure the owner has not changed its event set
- assert(events_owner &&
- sequence_number == events_owner->sequence_number);
- // do the update on the owner
- events_owner->set_start_time(event, t);
- return;
- } else if (type == 't') { // this is an Alg_track
- // find the event in the track
- track_ptr = (Alg_track_ptr) this;
- // this should be a binary search since events are in time order
- // probably there should be member function to do the search
- for (index = 0; index < length(); index++) {
- if ((*track_ptr)[index] == event) goto found_event;
- }
- } else { // type == 's', an Alg_seq
- Alg_seq_ptr seq = (Alg_seq_ptr) this;
- for (i = 0; i < seq->tracks(); i++) {
- track_ptr = seq->track(i);
- // if you implemented binary search, you could call it
- // instead of this loop too.
- for (index = 0; index < track_ptr->length(); index++) {
- if ((*track_ptr)[index] == event) goto found_event;
- }
- }
- }
- assert(false); // event not found seq or track!
- found_event:
- // at this point, track[index] == event
- // we could be clever and figure out exactly what notes to move
- // but it is simpler to just remove the event and reinsert it:
- track_ptr->uninsert(index);
- event->time = t;
- track_ptr->insert(event);
- }
- void Alg_beats::expand()
- {
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- Alg_beat_ptr new_beats = new Alg_beat[maxlen];
- // now do copy
- memcpy(new_beats, beats, len * sizeof(Alg_beat));
- if (beats) delete[] beats;
- beats = new_beats;
- }
- void Alg_beats::insert(long i, Alg_beat_ptr beat)
- {
- assert(i >= 0 && i <= len);
- if (maxlen <= len) {
- expand();
- }
- memmove(&beats[i + 1], &beats[i], sizeof(Alg_beat) * (len - i));
- memcpy(&beats[i], beat, sizeof(Alg_beat));
- len++;
- }
- Alg_time_map::Alg_time_map(Alg_time_map *map)
- {
- refcount = 0;
- assert(map->beats[0].beat == 0 && map->beats[0].time == 0);
- assert(map->beats.len > 0);
- // new_beats[0] = map->beats[0];
- // this is commented because
- // both new_beats[0] and map->beats[0] should be (0, 0)
- for (int i = 1; i < map->beats.len; i++) {
- beats.insert(i, &map->beats[i]);
- }
- last_tempo = map->last_tempo;
- last_tempo_flag = map->last_tempo_flag;
- }
- void Alg_time_map::show()
- {
- printf("Alg_time_map: ");
- for (int i = 0; i < beats.len; i++) {
- Alg_beat &b = beats[i];
- printf("(%g, %g) ", b.time, b.beat);
- }
- printf("last tempo: %g\n", last_tempo);
- }
- long Alg_time_map::locate_time(double time)
- {
- int i = 0;
- while ((i < beats.len) && (time > beats[i].time)) {
- i++;
- }
- return i;
- }
- long Alg_time_map::locate_beat(double beat)
- {
- int i = 0;
- while ((i < beats.len) && (beat > beats[i].beat)) {
- i++;
- }
- return i;
- }
- double Alg_time_map::beat_to_time(double beat)
- {
- Alg_beat_ptr mbi;
- Alg_beat_ptr mbi1;
- if (beat <= 0) {
- return beat;
- }
- int i = locate_beat(beat);
- // case 1: beat is between two time/beat pairs
- if (0 < i && i < beats.len) {
- mbi = &beats[i - 1];
- mbi1 = &beats[i];
- // case 2: beat is beyond last time/beat pair
- } else if (i == beats.len) {
- if (last_tempo_flag) {
- return beats[i - 1].time +
- (beat - beats[i - 1].beat) / last_tempo;
- } else if (i == 1) {
- return beat * 60.0 / ALG_DEFAULT_BPM;
- // so we use that as default allegro tempo too
- } else {
- mbi = &beats[i - 2];
- mbi1 = &beats[i - 1];
- }
- // case 3: beat is at time 0
- } else /* if (i == 0) */ {
- return beats[0].time;
- }
- // whether we extrapolate or interpolate, the math is the same
- double time_dif = mbi1->time - mbi->time;
- double beat_dif = mbi1->beat - mbi->beat;
- return mbi->time + (beat - mbi->beat) * time_dif / beat_dif;
- }
- double Alg_time_map::time_to_beat(double time)
- {
- Alg_beat_ptr mbi;
- Alg_beat_ptr mbi1;
- if (time <= 0.0) return time;
- int i = locate_time(time);
- if (i == beats.len) {
- if (last_tempo_flag) {
- return beats[i - 1].beat +
- (time - beats[i - 1].time) * last_tempo;
- } else if (i == 1) {
- return time * (ALG_DEFAULT_BPM / 60.0);
- } else {
- mbi = &beats[i - 2];
- mbi1 = &beats[i - 1];
- }
- } else {
- mbi = &beats[i - 1];
- mbi1 = & beats[i];
- }
- double time_dif = mbi1->time - mbi->time;
- double beat_dif = mbi1->beat - mbi->beat;
- return mbi->beat + (time - mbi->time) * beat_dif / time_dif;
- }
- void Alg_time_map::insert_beat(double time, double beat)
- {
- int i = locate_time(time); // i is insertion point
- if (i < beats.len && within(beats[i].time, time, 0.000001)) {
- // replace beat if time is already in the map
- beats[i].beat = beat;
- } else {
- Alg_beat point;
- point.beat = beat;
- point.time = time;
- beats.insert(i, &point);
- }
- // beats[i] contains new beat
- // make sure we didn't generate a zero tempo.
- // if so, space beats by one microbeat as necessary
- long j = i;
- if (j == 0) j = 1; // do not adjust beats[0]
- while (j < beats.len &&
- beats[j - 1].beat + 0.000001 >= beats[j].beat) {
- beats[j].beat = beats[j - 1].beat + 0.000001;
- j++;
- }
- }
- bool Alg_time_map::insert_tempo(double tempo, double beat)
- {
- tempo = tempo / 60.0; // convert to beats per second
- // change the tempo at the given beat until the next beat event
- if (beat < 0) return false;
- double time = beat_to_time(beat);
- long i = locate_time(time);
- if (i >= beats.len || !within(beats[i].time, time, 0.000001)) {
- insert_beat(time, beat);
- }
- // now i is index of beat where tempo will change
- if (i == beats.len - 1) {
- last_tempo = tempo;
- // printf("last_tempo to %g\n", last_tempo);
- last_tempo_flag = true;
- } else { // adjust all future beats
- // compute the difference in beats
- double diff = beats[i + 1].beat - beats[i].beat;
- // convert beat difference to seconds at new tempo
- diff = diff / tempo;
- // figure out old time difference:
- double old_diff = beats[i + 1].time - time;
- // compute difference too
- diff = diff - old_diff;
- // apply new_diff to score and beats
- i++;
- while (i < beats.len) {
- beats[i].time = beats[i].time + diff;
- i++;
- }
- }
- return true;
- }
- double Alg_time_map::get_tempo(double beat)
- {
- Alg_beat_ptr mbi;
- Alg_beat_ptr mbi1;
- // if beat < 0, there is probably an error; return something nice anyway
- if (beat < 0) return ALG_DEFAULT_BPM / 60.0;
- long i = locate_beat(beat);
- // this code is similar to beat_to_time() so far, but we want to get
- // beyond beat if possible because we want the tempo FOLLOWING beat
- // (Consider the case beat == 0.0)
- if (i < beats.len && beat >= beats[i].beat) i++;
- // case 1: beat is between two time/beat pairs
- if (i < beats.len) {
- mbi = &beats[i - 1];
- mbi1 = &beats[i];
- // case 2: beat is beyond last time/beat pair
- } else /* if (i == beats.len) */ {
- if (last_tempo_flag) {
- return last_tempo;
- } else if (i == 1) {
- return ALG_DEFAULT_BPM / 60.0;
- } else {
- mbi = &beats[i - 2];
- mbi1 = &beats[i - 1];
- }
- }
- double time_dif = mbi1->time - mbi->time;
- double beat_dif = mbi1->beat - mbi->beat;
- return beat_dif / time_dif;
- }
- bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat)
- {
- if (start_beat >= end_beat) return false;
- // algorithm: insert a beat event if necessary at start_beat
- // and at end_beat
- // delete intervening map elements
- // change the tempo
- insert_beat(beat_to_time(start_beat), start_beat);
- insert_beat(beat_to_time(end_beat), end_beat);
- long start_x = locate_beat(start_beat) + 1;
- long stop_x = locate_beat(end_beat);
- while (stop_x < beats.len) {
- beats[start_x] = beats[stop_x];
- start_x++;
- stop_x++;
- }
- beats.len = start_x; // truncate the map to new length
- return insert_tempo(tempo, start_beat);
- }
- bool Alg_time_map::stretch_region(double b0, double b1, double dur)
- {
- // find current duration
- double t0 = beat_to_time(b0);
- double t1 = beat_to_time(b1);
- double old_dur = t1 - t0;
- if (old_dur <= 0 || dur <= 0) return false;
- double scale = dur / old_dur; // larger scale => slower
- // insert a beat if necessary at b0 and b1
- insert_beat(t0, b0);
- insert_beat(t1, b1);
- long start_x = locate_beat(b0);
- long stop_x = locate_beat(b1);
- double orig_time = beats[start_x].time;
- double prev_time = orig_time;
- for (int i = start_x + 1; i < beats.len; i++) {
- double delta = beats[i].time - orig_time;
- if (i <= stop_x) { // change tempo to next Alg_beat
- delta *= scale;
- }
- orig_time = beats[i].time;
- prev_time += delta;
- beats[i].time = prev_time;
- }
- return true;
- }
- void Alg_time_map::trim(double start, double end, bool units_are_seconds)
- {
- // extract the time map from start to end and shift to time zero
- // start and end are time in seconds if units_are_seconds is true
- int i = 0; // index into beats
- int start_index; // index of first breakpoint after start
- int count = 1;
- double initial_beat = start;
- double final_beat = end;
- if (units_are_seconds) {
- initial_beat = time_to_beat(start);
- final_beat = time_to_beat(end);
- } else {
- start = beat_to_time(initial_beat);
- end = beat_to_time(final_beat);
- }
- while (i < length() && beats[i].time < start) i++;
- // now i is index into beats of the first breakpoint after start
- // beats[0] is (0,0) and remains that way
- // copy beats[start_index] to beats[1], etc.
- // skip any beats at or near (start,initial_beat), using count
- // to keep track of how many entries there are
- start_index = i;
- while (i < length() && beats[i].time < end) {
- if (beats[i].time - start > ALG_EPS &&
- beats[i].beat - initial_beat > ALG_EPS) {
- beats[i].time = beats[i].time - start;
- beats[i].beat = beats[i].beat - initial_beat;
- beats[i - start_index + 1] = beats[i];
- count = count + 1;
- } else {
- start_index = start_index + 1;
- }
- i = i + 1;
- }
- // set last tempo data
- // we last examined beats[i-1] and copied it to
- // beats[i - start_index]. Next tempo should come
- // from beats[i] and store in beats[i - start_index + 1]
- // case 1: there is at least one breakpoint beyond end
- // => interpolate to put a breakpoint at end
- // case 2: no more breakpoints => set last tempo data
- if (i < length()) {
- // we know beats[i].time >= end, so case 1 applies
- beats[i - start_index + 1].time = end - start;
- beats[i - start_index + 1].beat = final_beat - initial_beat;
- count = count + 1;
- }
- // else we'll just use stored last tempo (if any)
- beats.len = count;
- }
- void Alg_time_map::cut(double start, double len, bool units_are_seconds)
- {
- // remove portion of time map from start to start + len,
- // shifting the tail left by len. start and len are in whatever
- // units the score is in. If you cut the time_map as well as cut
- // the tracks of the sequence, then sequences will preserve the
- // association between tempo changes and events
- double end = start + len;
- double initial_beat = start;
- double final_beat = end;
- int i = 0;
- if (units_are_seconds) {
- initial_beat = time_to_beat(start);
- final_beat = time_to_beat(end);
- } else {
- start = beat_to_time(initial_beat);
- end = beat_to_time(final_beat);
- len = end - start;
- }
- double beat_len = final_beat - initial_beat;
- while (i < length() && beats[i].time < start - ALG_EPS) {
- i = i + 1;
- }
- // if no beats exist at or after start, just return; nothing to cut
- if (i == length()) return;
- // now i is index into beats of the first breakpoint on or
- // after start, insert (start, initial_beat) in map
- if (i < length() && within(beats[i].time, start, ALG_EPS)) {
- // perterb time map slightly (within alg_eps) to place
- // break point exactly at the start time
- beats[i].time = start;
- beats[i].beat = initial_beat;
- } else {
- Alg_beat point(start, initial_beat);
- beats.insert(i, &point);
- }
- // now, we're correct up to beats[i] and beats[i] happens at start.
- // find first beat after end so we can start shifting from there
- i = i + 1;
- int start_index = i;
- while (i < length() && beats[i].time < end + ALG_EPS) i++;
- // now beats[i] is the next point to be included in beats
- // but from i onward, we must shift by (-len, -beat_len)
- while (i < length()) {
- Alg_beat &b = beats[i];
- b.time = b.time - len;
- b.beat = b.beat - beat_len;
- beats[start_index] = b;
- i = i + 1;
- start_index = start_index + 1;
- }
- beats.len = start_index;
- }
- void Alg_time_map::paste(double beat, Alg_track *tr)
- {
- // insert a given time map at a given time and dur (in beats)
- Alg_time_map_ptr from_map = tr->get_time_map();
- // printf("time map paste\nfrom map\n");
- // from_map->show();
- // printf("to map\n");
- // show();
- Alg_beats &from = from_map->beats;
- double time = beat_to_time(beat);
- // Locate the point at which dur occurs
- double dur = tr->get_beat_dur();
- double tr_end_time = from_map->beat_to_time(dur);
- // add offset to make room for insert
- int i = locate_beat(beat);
- while (i < length()) {
- beats[i].beat += dur;
- beats[i].time += tr_end_time;
- i++;
- }
- // printf("after opening up\n");
- // show();
- // insert point at beginning and end of paste
- insert_beat(time, beat);
- // printf("after beginning point insert\n");
- // show();
- // insert_beat(time + tr_end_time, beat + dur);
- // printf("after ending point insert\n");
- // show();
- int j = from_map->locate_beat(dur);
- for (i = 0; i < j; i++) {
- insert_beat(from[i].time + time, // shift by time
- from[i].beat + beat); // and beat
- }
- // printf("after inserts\n");
- show();
- }
- void Alg_time_map::insert_time(double start, double len)
- {
- // find time,beat pair that determines tempo at start
- // compute beat offset = (delta beat / delta time) * len
- // add len,beat offset to each following Alg_beat
- // show();
- int i = locate_time(start); // start <= beats[i].time
- if (beats[i].time == start) i++; // start < beats[i].time
- // case 1: between beats
- if (i > 0 && i < length()) {
- double beat_offset = len * (beats[i].beat - beats[i-1].beat) /
- (beats[i].time - beats[i-1].time);
- while (i < length()) {
- beats[i].beat += beat_offset;
- beats[i].time += len;
- i++;
- }
- } // otherwise, last tempo is in effect; nothing to do
- // printf("time_map AFTER INSERT\n");
- // show();
- }
- void Alg_time_map::insert_beats(double start, double len)
- {
- int i = locate_beat(start); // start <= beats[i].beat
- if (beats[i].beat == start) i++;
- if (i > 0 && i < length()) {
- double time_offset = len * (beats[i].time - beats[i-1].time) /
- (beats[i].beat - beats[i-1].beat);
- while (i < length()) {
- beats[i].time += time_offset;
- beats[i].beat += len;
- i++;
- }
- } // otherwise, last tempo is in effect; nothing to do
- // printf("time_map AFTER INSERT\n");
- // show();
- }
- Alg_track::Alg_track(Alg_time_map *map, bool seconds)
- {
- type = 't';
- time_map = nullptr;
- units_are_seconds = seconds;
- set_time_map(map);
- }
- Alg_event_ptr Alg_track::copy_event(Alg_event_ptr event)
- {
- Alg_event *new_event;
- if (event->is_note()) {
- new_event = new Alg_note((Alg_note_ptr) event);
- } else { // update
- new_event = new Alg_update((Alg_update_ptr) event);
- }
- return new_event;
- }
- Alg_track::Alg_track(Alg_track &track)
- {
- type = 't';
- time_map = nullptr;
- for (int i = 0; i < track.length(); i++) {
- append(copy_event(track.events[i]));
- }
- set_time_map(track.time_map);
- units_are_seconds = track.units_are_seconds;
- }
- Alg_track::Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map,
- bool units_are_seconds)
- {
- type = 't';
- time_map = nullptr;
- for (int i = 0; i < event_list.length(); i++) {
- append(copy_event(event_list[i]));
- }
- set_time_map(map);
- this->units_are_seconds = units_are_seconds;
- }
- void Alg_track::serialize(void **buffer, long *bytes)
- {
- // first determine whether this is a seq or a track.
- // if it is a seq, then we will write the time map and a set of tracks
- // if it is a track, we just write the track data and not the time map
- //
- // The code will align doubles on ALIGN boundaries, and longs and
- // floats are aligned to multiples of 4 bytes.
- //
- // The format for a seq is:
- // 'ALGS' -- indicates that this is a sequence
- // long length of all seq data in bytes starting with 'ALGS'
- // long channel_offset_per_track
- // long units_are_seconds
- // time_map:
- // double last_tempo
- // long last_tempo_flag
- // long len -- number of tempo changes
- // for each tempo change (Alg_beat):
- // double time
- // double beat
- // time_sigs:
- // long len -- number of time_sigs
- // long pad
- // for each time signature:
- // double beat
- // double num
- // double den
- // tracks:
- // long len -- number of tracks
- // long pad
- // for each track:
- // 'ALGT' -- indicates this is a track
- // long length of all track data in bytes starting with 'ALGT'
- // long units_are_seconds
- // double beat_dur
- // double real_dur
- // long len -- number of events
- // for each event:
- // long selected
- // long type
- // long key
- // long channel
- // double time
- // if this is a note:
- // double pitch
- // double dur
- // double loud
- // long len -- number of parameters
- // for each parameter:
- // char attribute[] with zero pad to ALIGN
- // one of the following, depending on type:
- // double r
- // char s[] terminated by zero
- // long i
- // long l
- // char a[] terminated by zero
- // zero pad to ALIGN
- // else if this is an update
- // (same representation as parameter above)
- // zero pad to ALIGN
- //
- // The format for a track is given within the Seq format above
- assert(get_type() == 't');
- ser_write_buf.init_for_write();
- serialize_track();
- *buffer = ser_write_buf.to_heap(bytes);
- }
- void Alg_seq::serialize(void **buffer, long *bytes)
- {
- assert(get_type() == 's');
- ser_write_buf.init_for_write();
- serialize_seq();
- *buffer = ser_write_buf.to_heap(bytes);
- }
- void Serial_write_buffer::check_buffer(long needed)
- {
- if (len < (ptr - buffer) + needed) { // do we need more space?
- long new_len = len * 2; // exponential growth is important
- // initially, length is zero, so bump new_len to a starting value
- if (new_len == 0) new_len = 1024;
- // make sure new_len is as big as needed
- if (needed > new_len) new_len = needed;
- char *new_buffer = new char[new_len]; // allocate space
- ptr = new_buffer + (ptr - buffer); // relocate ptr to new buffer
- if (len > 0) { // we had a buffer already
- memcpy(new_buffer, buffer, len); // copy from old buffer
- delete buffer; // free old buffer
- }
- buffer = new_buffer; // update buffer information
- len = new_len;
- }
- }
- void Alg_seq::serialize_seq()
- {
- int i; // loop counters
- // we can easily compute how much buffer space we need until we
- // get to tracks, so expand at least that much
- long needed = 64 + 16 * time_map->beats.len + 24 * time_sig.length();
- ser_write_buf.check_buffer(needed);
- ser_write_buf.set_char('A');
- ser_write_buf.set_char('L');
- ser_write_buf.set_char('G');
- ser_write_buf.set_char('S');
- long length_offset = ser_write_buf.get_posn();
- ser_write_buf.set_int32(0); // leave room to come back and write length
- ser_write_buf.set_int32(channel_offset_per_track);
- ser_write_buf.set_int32(units_are_seconds);
- ser_write_buf.set_double(beat_dur);
- ser_write_buf.set_double(real_dur);
- ser_write_buf.set_double(time_map->last_tempo);
- ser_write_buf.set_int32(time_map->last_tempo_flag);
- ser_write_buf.set_int32(time_map->beats.len);
- for (i = 0; i < time_map->beats.len; i++) {
- ser_write_buf.set_double(time_map->beats[i].time);
- ser_write_buf.set_double(time_map->beats[i].beat);
- }
- ser_write_buf.set_int32(time_sig.length());
- ser_write_buf.pad();
- for (i = 0; i < time_sig.length(); i++) {
- ser_write_buf.set_double(time_sig[i].beat);
- ser_write_buf.set_double(time_sig[i].num);
- ser_write_buf.set_double(time_sig[i].den);
- }
- ser_write_buf.set_int32(tracks());
- ser_write_buf.pad();
- for (i = 0; i < tracks(); i++) {
- track(i)->serialize_track();
- }
- // do not include ALGS, include padding at end
- ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset);
- }
- void Alg_track::serialize_track()
- {
- // to simplify the code, copy from parameter addresses to locals
- int j;
- ser_write_buf.check_buffer(32);
- ser_write_buf.set_char('A');
- ser_write_buf.set_char('L');
- ser_write_buf.set_char('G');
- ser_write_buf.set_char('T');
- long length_offset = ser_write_buf.get_posn(); // save location for track length
- ser_write_buf.set_int32(0); // room to write track length
- ser_write_buf.set_int32(units_are_seconds);
- ser_write_buf.set_double(beat_dur);
- ser_write_buf.set_double(real_dur);
- ser_write_buf.set_int32(len);
- for (j = 0; j < len; j++) {
- ser_write_buf.check_buffer(24);
- Alg_event *event = (*this)[j];
- ser_write_buf.set_int32(event->get_selected());
- ser_write_buf.set_int32(event->get_type());
- ser_write_buf.set_int32(event->get_identifier());
- ser_write_buf.set_int32(event->chan);
- ser_write_buf.set_double(event->time);
- if (event->is_note()) {
- ser_write_buf.check_buffer(20);
- Alg_note *note = (Alg_note *) event;
- ser_write_buf.set_float(note->pitch);
- ser_write_buf.set_float(note->loud);
- ser_write_buf.set_double(note->dur);
- long parm_num_offset = ser_write_buf.get_posn();
- long parm_num = 0;
- ser_write_buf.set_int32(0); // placeholder for no. parameters
- Alg_parameters_ptr parms = note->parameters;
- while (parms) {
- serialize_parameter(&(parms->parm));
- parms = parms->next;
- parm_num++;
- }
- ser_write_buf.store_long(parm_num_offset, parm_num);
- } else {
- assert(event->is_update());
- Alg_update *update = (Alg_update *) event;
- serialize_parameter(&(update->parameter));
- }
- ser_write_buf.check_buffer(7); // maximum padding possible
- ser_write_buf.pad();
- }
- // write length, not including ALGT, including padding at end
- ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset);
- }
- void Alg_track::serialize_parameter(Alg_parameter *parm)
- {
- // add eight to account for name + zero end-of-string and the
- // possibility of adding 7 padding bytes
- long len = strlen(parm->attr_name()) + 8;
- ser_write_buf.check_buffer(len);
- ser_write_buf.set_string(parm->attr_name());
- ser_write_buf.pad();
- switch (parm->attr_type()) {
- case 'r':
- ser_write_buf.check_buffer(8);
- ser_write_buf.set_double(parm->r);
- break;
- case 's':
- ser_write_buf.check_buffer(strlen(parm->s) + 1);
- ser_write_buf.set_string(parm->s);
- break;
- case 'i':
- ser_write_buf.check_buffer(4);
- ser_write_buf.set_int32(parm->i);
- break;
- case 'l':
- ser_write_buf.check_buffer(4);
- ser_write_buf.set_int32(parm->l);
- break;
- case 'a':
- ser_write_buf.check_buffer(strlen(parm->a) + 1);
- ser_write_buf.set_string(parm->a);
- break;
- }
- }
- Alg_track *Alg_track::unserialize(void *buffer, long len)
- {
- assert(len > 8);
- ser_read_buf.init_for_read(buffer, len);
- bool alg = ser_read_buf.get_char() == 'A' &&
- ser_read_buf.get_char() == 'L' &&
- ser_read_buf.get_char() == 'G';
- assert(alg); (void)alg; // unused variable
- char c = ser_read_buf.get_char();
- if (c == 'S') {
- Alg_seq *seq = new Alg_seq;
- ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,S
- seq->unserialize_seq();
- return seq;
- } else {
- assert(c == 'T');
- Alg_track *track = new Alg_track;
- ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,T
- track->unserialize_track();
- return track;
- }
- }
- //#pragma warning(disable: 4800) // long to bool performance warning
- /* Note: this Alg_seq must have a default initialized Alg_time_map.
- * It will be filled in with data from the ser_read_buf buffer.
- */
- void Alg_seq::unserialize_seq()
- {
- ser_read_buf.check_input_buffer(48);
- bool algs = (ser_read_buf.get_char() == 'A') &&
- (ser_read_buf.get_char() == 'L') &&
- (ser_read_buf.get_char() == 'G') &&
- (ser_read_buf.get_char() == 'S');
- assert(algs); (void)algs; // unused variable
- long len = ser_read_buf.get_int32();
- assert(ser_read_buf.get_len() >= len); (void)len; // unused variable
- channel_offset_per_track = ser_read_buf.get_int32();
- units_are_seconds = ser_read_buf.get_int32() != 0;
- beat_dur = ser_read_buf.get_double();
- real_dur = ser_read_buf.get_double();
- // no need to allocate an Alg_time_map since it's done during initialization
- time_map->last_tempo = ser_read_buf.get_double();
- time_map->last_tempo_flag = ser_read_buf.get_int32() != 0;
- long beats = ser_read_buf.get_int32();
- ser_read_buf.check_input_buffer(beats * 16 + 4);
- int i;
- for (i = 0; i < beats; i++) {
- double time = ser_read_buf.get_double();
- double beat = ser_read_buf.get_double();
- time_map->insert_beat(time, beat);
- // printf("time_map: %g, %g\n", time, beat);
- }
- long time_sig_len = ser_read_buf.get_int32();
- ser_read_buf.get_pad();
- ser_read_buf.check_input_buffer(time_sig_len * 24 + 8);
- for (i = 0; i < time_sig_len; i++) {
- double beat = ser_read_buf.get_double();
- double num = ser_read_buf.get_double();
- double den = ser_read_buf.get_double();
- time_sig.insert(beat, num, den);
- }
- long tracks_num = ser_read_buf.get_int32();
- ser_read_buf.get_pad();
- add_track(tracks_num - 1); // create tracks_num tracks
- for (i = 0; i < tracks_num; i++) {
- track(i)->unserialize_track();
- }
- // assume seq started at beginning of buffer. len measures
- // bytes after 'ALGS' header, so add 4 bytes and compare to
- // current buffer position -- they should agree
- assert(ser_read_buf.get_posn() == len + 4);
- }
- void Alg_track::unserialize_track()
- {
- ser_read_buf.check_input_buffer(32);
- bool algt = (ser_read_buf.get_char() == 'A') &&
- (ser_read_buf.get_char() == 'L') &&
- (ser_read_buf.get_char() == 'G') &&
- (ser_read_buf.get_char() == 'T');
- assert(algt); (void)algt; // unused variable
- long offset = ser_read_buf.get_posn(); // stored length does not include 'ALGT'
- long bytes = ser_read_buf.get_int32();
- assert(bytes <= ser_read_buf.get_len() - offset); (void)offset; (void)bytes; // unused variable
- units_are_seconds = (bool) ser_read_buf.get_int32();
- beat_dur = ser_read_buf.get_double();
- real_dur = ser_read_buf.get_double();
- int event_count = ser_read_buf.get_int32();
- for (int i = 0; i < event_count; i++) {
- ser_read_buf.check_input_buffer(24);
- long selected = ser_read_buf.get_int32();
- char type = (char) ser_read_buf.get_int32();
- long key = ser_read_buf.get_int32();
- long channel = ser_read_buf.get_int32();
- double time = ser_read_buf.get_double();
- if (type == 'n') {
- ser_read_buf.check_input_buffer(20);
- float pitch = ser_read_buf.get_float();
- float loud = ser_read_buf.get_float();
- double dur = ser_read_buf.get_double();
- Alg_note *note =
- create_note(time, channel, key, pitch, loud, dur);
- note->set_selected(selected != 0);
- long param_num = ser_read_buf.get_int32();
- int j;
- // this builds a list of parameters in the correct order
- // (although order shouldn't matter)
- Alg_parameters_ptr *list = ¬e->parameters;
- for (j = 0; j < param_num; j++) {
- *list = new Alg_parameters(nullptr);
- unserialize_parameter(&((*list)->parm));
- list = &((*list)->next);
- }
- append(note);
- } else {
- assert(type == 'u');
- Alg_update *update = create_update(time, channel, key);
- update->set_selected(selected != 0);
- unserialize_parameter(&(update->parameter));
- append(update);
- }
- ser_read_buf.get_pad();
- }
- assert(offset + bytes == ser_read_buf.get_posn());
- }
- void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr)
- {
- Alg_attribute attr = ser_read_buf.get_string();
- parm_ptr->attr = symbol_table.insert_string(attr);
- switch (parm_ptr->attr_type()) {
- case 'r':
- ser_read_buf.check_input_buffer(8);
- parm_ptr->r = ser_read_buf.get_double();
- break;
- case 's':
- parm_ptr->s = heapify(ser_read_buf.get_string());
- break;
- case 'i':
- ser_read_buf.check_input_buffer(4);
- parm_ptr->i = ser_read_buf.get_int32();
- break;
- case 'l':
- ser_read_buf.check_input_buffer(4);
- parm_ptr->l = ser_read_buf.get_int32() != 0;
- break;
- case 'a':
- parm_ptr->a = symbol_table.insert_attribute(ser_read_buf.get_string());
- break;
- }
- }
- //#pragma warning(default: 4800)
- void Alg_track::set_time_map(Alg_time_map *map)
- {
- if (time_map) time_map->dereference();
- if (map == nullptr) {
- time_map = new Alg_time_map(); // new default map
- time_map->reference();
- } else {
- time_map = map;
- time_map->reference();
- }
- }
- void Alg_track::convert_to_beats()
- // modify all times and durations in notes to beats
- {
- if (units_are_seconds) {
- units_are_seconds = false;
- long i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr e = events[i];
- double beat = time_map->time_to_beat(e->time);
- if (e->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) e;
- n->dur = time_map->time_to_beat(n->time + n->dur) - beat;
- }
- e->time = beat;
- }
- }
- }
- void Alg_track::convert_to_seconds()
- // modify all times and durations in notes to seconds
- {
- if (!units_are_seconds) {
- last_note_off = time_map->beat_to_time(last_note_off);
- units_are_seconds = true;
- long i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr e = events[i];
- double time = time_map->beat_to_time(e->time);
- if (e->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) e;
- n->dur = time_map->beat_to_time(n->time + n->dur) - time;
- }
- e->time = time;
- }
- }
- }
- void Alg_track::set_dur(double duration)
- {
- // set beat_dur and real_dur
- if (units_are_seconds) {
- set_real_dur(duration);
- set_beat_dur(time_map->time_to_beat(duration));
- } else {
- set_beat_dur(duration);
- set_real_dur(time_map->beat_to_time(duration));
- }
- }
- Alg_note *Alg_track::create_note(double time, int channel, int identifier,
- float pitch, float loudness, double duration)
- {
- Alg_note *note = new Alg_note();
- note->time = time;
- note->chan = channel;
- note->set_identifier(identifier);
- note->pitch = pitch;
- note->loud = loudness;
- note->dur = duration;
- return note;
- }
- Alg_update *Alg_track::create_update(double time, int channel, int identifier)
- {
- Alg_update *update = new Alg_update();
- update->time = time;
- update->chan = channel;
- update->set_identifier(identifier);
- return update;
- }
- Alg_track_ptr Alg_track::cut(double t, double len, bool all)
- {
- // since we are translating notes in time, do not copy or use old timemap
- Alg_track_ptr track = new Alg_track();
- track->units_are_seconds = units_are_seconds;
- if (units_are_seconds) {
- track->set_real_dur(len);
- track->set_beat_dur(time_map->time_to_beat(t + len) -
- time_map->time_to_beat(t));
- } else {
- track->set_beat_dur(len);
- track->set_real_dur(time_map->beat_to_time(t + len) -
- time_map->beat_to_time(t));
- }
- int i;
- int new_len = 0;
- int change = 0;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- event->time -= t;
- track->append(event);
- change = 1;
- } else { // if we're not cutting this event, move it to
- // eliminate the gaps in events left by cut events
- events[new_len] = event;
- // adjust times of events after t + len
- if (event->time > t + len - ALG_EPS) {
- event->time -= len;
- change = 1;
- }
- new_len++;
- }
- }
- // Alg_event_lists based on this track become invalid
- sequence_number += change;
- this->len = new_len; // adjust length since we removed events
- return track;
- }
- Alg_track_ptr Alg_track::copy(double t, double len, bool all)
- {
- // since we are translating notes in time, do not copy or use old timemap
- Alg_track_ptr track = new Alg_track();
- track->units_are_seconds = units_are_seconds;
- if (units_are_seconds) {
- track->set_real_dur(len);
- track->set_beat_dur(time_map->time_to_beat(t + len) -
- time_map->time_to_beat(t));
- } else {
- track->set_beat_dur(len);
- track->set_real_dur(time_map->beat_to_time(t + len) -
- time_map->beat_to_time(t));
- }
- int i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- Alg_event_ptr new_event = copy_event(event);
- new_event->time -= t;
- track->append(new_event);
- }
- }
- return track;
- }
- void Alg_track::paste(double t, Alg_event_list *seq)
- {
- assert(get_type() == 't');
- // seq can be an Alg_event_list, an Alg_track, or an Alg_seq
- // if it is an Alg_event_list, units_are_seconds must match
- bool prev_units_are_seconds;
- if (seq->get_type() == 'e') {
- assert(seq->get_owner()->get_units_are_seconds() == units_are_seconds);
- prev_units_are_seconds = seq->get_owner()->get_units_are_seconds();
- } else { // make it match
- Alg_track_ptr tr = (Alg_track_ptr) seq;
- prev_units_are_seconds = tr->get_units_are_seconds();
- if (units_are_seconds) tr->convert_to_seconds();
- else tr->convert_to_beats();
- }
- double dur = (units_are_seconds ? seq->get_real_dur() :
- seq->get_beat_dur());
- // Note: in the worst case, seq may contain notes
- // that start almost anytime up to it's duration,
- // so the simplest algorithm is simply a sequence
- // of inserts. If this turns out to be too slow,
- // we can do a merge sort in the case that seq
- // is an Alg_track (if it's an Alg_event_list, we
- // are not guaranteed that the events are in time
- // order, but currently, only a true seq is allowed)
- int i;
- for (i = 0; i < length(); i++) {
- if (events[i]->time > t - ALG_EPS) {
- events[i]->time += dur;
- }
- }
- for (i = 0; i < seq->length(); i++) {
- Alg_event *new_event = copy_event((*seq)[i]);
- new_event->time += t;
- insert(new_event);
- }
- // restore track units to what they were before
- if (seq->get_type() != 'e') {
- Alg_track_ptr tr = (Alg_track_ptr) seq;
- if (prev_units_are_seconds) tr->convert_to_seconds();
- else tr->convert_to_beats();
- }
- }
- void Alg_track::merge(double t, Alg_event_list_ptr seq)
- {
- Alg_event_list_ref s = *seq;
- for (int i = 0; i < s.length(); i++) {
- Alg_event *new_event;
- if (s[i]->is_note()) {
- new_event = new Alg_note((Alg_note_ptr) s[i]);
- } else {
- new_event = new Alg_update((Alg_update_ptr) s[i]);
- }
- new_event->time += t;
- insert(new_event);
- }
- }
- void Alg_track::clear(double t, double len, bool all)
- {
- int i;
- int move_to = 0;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- delete events[i];
- } else { // if we're not clearing this event, move it to
- // eliminate the gaps in events left by cleared events
- events[move_to] = event;
- // adjust times of events after t + len. This test is based
- // on the one in Alg_event::overlap() for consistency.
- if (event->time > t + len - ALG_EPS && event->time > t)
- event->time -= len;
- move_to++;
- }
- }
- if (move_to != this->len) { // we cleared at least one note
- sequence_number++; // Alg_event_lists based on this track become invalid
- }
- this->len = move_to; // adjust length since we removed events
- }
- void Alg_track::silence(double t, double len, bool all)
- {
- int i;
- int move_to = 0;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- delete events[i];
- } else { // if we're not clearing this event, move it to
- // eliminate the gaps in events left by cleared events
- events[move_to] = event;
- move_to++;
- }
- }
- if (move_to != this->len) { // we cleared at least one note
- sequence_number++; // Alg_event_lists based on this track become invalid
- }
- this->len = move_to; // adjust length since we removed events
- }
- void Alg_track::insert_silence(double t, double len)
- {
- int i;
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->time > t - ALG_EPS) event->time += len;
- }
- }
- Alg_event_list *Alg_track::find(double t, double len, bool all,
- long channel_mask, long event_type_mask)
- {
- int i;
- Alg_event_list *list = new Alg_event_list(this);
- if (units_are_seconds) { // t and len are seconds
- list->set_real_dur(len);
- list->set_beat_dur(get_time_map()->time_to_beat(t + len) -
- get_time_map()->time_to_beat(t));
- } else { // t and len are beats
- list->set_real_dur(get_time_map()->beat_to_time(t + len) -
- get_time_map()->beat_to_time(t));
- list->set_beat_dur(len);
- }
- for (i = 0; i < length(); i++) {
- Alg_event_ptr event = events[i];
- if (event->overlap(t, len, all)) {
- if ((channel_mask == 0 ||
- (event->chan < 32 &&
- (channel_mask & (1 << event->chan)))) &&
- ((event_type_mask == 0 ||
- (event_type_mask & (1 << event->get_type_code()))))) {
- list->append(event);
- }
- }
- }
- return list;
- }
- void Alg_time_sigs::expand()
- {
- assert(maxlen >= len);
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- Alg_time_sig_ptr new_time_sigs = new Alg_time_sig[maxlen];
- // now do copy
- memcpy(new_time_sigs, time_sigs, len * sizeof(Alg_time_sig));
- if (time_sigs)
- delete[] time_sigs;
- time_sigs = new_time_sigs;
- }
- void Alg_time_sigs::insert(double beat, double num, double den, bool force)
- {
- // find insertion point:
- for (int i = 0; i < len; i++) {
- if (within(time_sigs[i].beat, beat, ALG_EPS)) {
- // overwrite location i with new info
- time_sigs[i].beat = beat;
- time_sigs[i].num = num;
- time_sigs[i].den = den;
- return;
- } else if (time_sigs[i].beat > beat) {
- if ((i > 0 && // check if redundant with prev. time sig
- time_sigs[i - 1].num == num &&
- time_sigs[i - 1].den == den &&
- within(fmod(beat - time_sigs[i - 1].beat,
- 4 * time_sigs[i-1].num / time_sigs[i-1].den),
- 0, ALG_EPS)) ||
- // check if redundant with implied initial 4/4 time sig:
- (i == 0 && num == 4 && den == 4 &&
- within(fmod(beat, 4), 0, ALG_EPS))) {
- if (!force) return; // redundant inserts can be ignored here
- }
- // make room for new event
- if (maxlen <= len) expand();
- // insert new event at i
- memmove(&time_sigs[i + 1], &time_sigs[i],
- sizeof(Alg_time_sig) * (len - i));
- time_sigs[i].beat = beat;
- time_sigs[i].num = num;
- time_sigs[i].den = den;
- len++;
- return;
- }
- }
- // if we fall out of loop, then this goes at end
- if (maxlen <= len) expand();
- time_sigs[len].beat = beat;
- time_sigs[len].num = num;
- time_sigs[len].den = den;
- len++;
- }
- void Alg_time_sigs::show()
- {
- printf("Alg_time_sig: ");
- for (int i = 0; i < len; i++) {
- printf("(%g: %g/%g) ", time_sigs[i].beat, time_sigs[i].num, time_sigs[i].den);
- }
- printf("\n");
- }
- int Alg_time_sigs::find_beat(double beat)
- {
- // index where you would insert a new time signature at beat
- int i = 0;
- while (i < len && time_sigs[i].beat < beat - ALG_EPS) i++;
- return i;
- }
- double Alg_time_sigs::get_bar_len(double beat)
- {
- int i = find_beat(beat);
- double num = 4.0;
- double den = 4.0;
- if (i != 0) {
- num = time_sigs[i - 1].num;
- den = time_sigs[i - 1].den;
- }
- return 4 * num / den;
- }
- void Alg_time_sigs::cut(double start, double end, double dur)
- {
- // remove time_sig's from start to end -- these must be
- // in beats (not seconds).
- // The duration of the whole sequence is dur (beats).
- // If the first bar line after end comes before a time signature
- // and does not fall on a bar line, insert a time signature at
- // the time of the bar line to retain relative bar line positions
- int i = find_beat(end);
- // 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 start, the relative barline positions are preserved
- if (len > 0 &&
- within(end, time_sigs[i].beat, ALG_EPS)) {
- // beat coincides with time signature change, so end is on a barline
- /* do nothing */ ;
- // Case 2: there is no time signature before end
- } else if (i == 0 && (len == 0 ||
- time_sigs[0].beat > end)) {
- // If the next time signature does not fall on a barline,
- // then end must not be on a barline, so there is a partial
- // measure from end to the next barline. We need
- // a time signature there to preserve relative barline
- // locations. It may be that the next bar after start is
- // due to another time signature, in which case we do not
- // need to insert anything.
- double measures = end / 4.0;
- double imeasures = ROUND(measures);
- if (!within(measures, imeasures, ALG_EPS)) {
- // start is not on a barline, maybe add one here:
- double bar_loc = (int(measures) + 1) * 4.0;
- if (bar_loc < dur - ALG_EPS &&
- (len == 0 || time_sigs[0].beat > bar_loc + ALG_EPS)) {
- insert(bar_loc, 4, 4, true); // forced insert
- }
- }
- // This case should never be true because if i == 0, either there
- // are no time signatures before beat (Case 2),
- // or there is one time signature at beat (Case 1)
- } else if (i == 0) {
- /* do nothing (might be good to assert(false)) */ ;
- // Case 3: i-1 must be the effective time sig position
- } else {
- // get the time signature in effect at end
- Alg_time_sig &tsp = time_sigs[i - 1];
- double beats_per_measure = (tsp.num * 4) / tsp.den;
- double measures = (end - tsp.beat) / beats_per_measure;
- int imeasures = ROUND(measures);
- if (!within(measures, imeasures, ALG_EPS)) {
- // end is not on a measure, so we need to insert a time sig
- // to force a bar line at the first measure location after
- // beat, if any
- double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1);
- // insert new time signature at bar_loc
- // It will have the same time signature, but the position will
- // force a barline to match the barline before the shift
- // However, we should not insert a barline if there is a
- // time signature earlier than the barline time
- if (i < len /* time_sigs[i] is the last one */ &&
- time_sigs[i].beat < bar_loc - ALG_EPS) {
- /* do not insert because there's already a time signature */;
- } else if (bar_loc < dur - ALG_EPS) {
- insert(bar_loc, tsp.num, tsp.den, true); // forced insert
- }
- }
- // else beat coincides with a barline, so no need for an extra
- // time signature to force barline alignment
- }
- // Figure out if time signature at start matches
- // the time signature at end. If not, we need to insert a
- // time signature at end to force the correct time signature
- // there.
- // Find time signature at start:
- double start_num = 4.0; // default if no time signature specified
- double start_den = 4.0;
- i = find_beat(start);
- // A time signature at start would go at index i, so the effective
- // time signature prior to start is at i - 1. If i == 0, the default
- // time signature is in effect prior to start.
- if (i != 0) {
- start_num = time_sigs[i - 1].num;
- start_den = time_sigs[i - 1].den;
- }
- // Find the time signature at end:
- double end_num = 4.0; // default if no time signature specified
- double end_den = 4.0;
- int j = find_beat(end);
- if (j != 0) {
- end_num = time_sigs[j - 1].num;
- end_den = time_sigs[j - 1].den;
- }
- // compare: If meter changes and there is no time signature at end,
- // insert a time signature at end
- if (end < dur - ALG_EPS &&
- (start_num != end_num || start_den != end_den) &&
- (j >= len || !within(time_sigs[j].beat, end, ALG_EPS))) {
- insert(end, end_num, end_den, true);
- }
- // Remove time signatures from start to end (not including one AT
- // end, if there is one there. Be careful with ALG_EPS on that one.)
- // since we may have inserted a time signature, find position again:
- int i0 = find_beat(start);
- int i1 = i0;
- // scan to end of cut region
- while (i1 < len && time_sigs[i1].beat < end - ALG_EPS) {
- i1++;
- }
- // scan from end to len(time_sig)
- while (i1 < len) {
- Alg_time_sig &ts = time_sigs[i1];
- ts.beat -= (end - start);
- time_sigs[i0] = ts;
- i0++;
- i1++;
- }
- len = i1;
- }
- void Alg_time_sigs::trim(double start, double end)
- {
- // remove time_sig's not in [start, end), but retain
- // barline positions relative to the notes. This means that
- // if the meter (time signature) changes between start and
- // end that we need to insert a time signature at start.
- // Also, if trim() would cause barlines to move, we need to
- // insert a time signature on a barline (timesignatures
- // imply the beginning of a bar even if the previous bar
- // does not have enough beats. Note that bars do not need
- // to have an integer number of beats).
- //
- // units must be in beats (not seconds)
- //
- // Uses Alg_time_sigs::cut() to avoid writing a special case
- double dur = end + 1000;
- if (len > 0) {
- dur = time_sigs[len - 1].beat + 1000;
- }
- cut(end, dur, dur);
- cut(0, start, dur);
- #ifdef IGNORE_THIS_OLD_CODE
- // first, skip time signatures up to start
- int i = find_beat(start);
- // 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 (len > 0 &&
- within(start, time_sigs[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 start
- } else if (i == 0 && (len == 0 ||
- time_sigs[0].beat > start)) {
- // If the next time signature does not fall on a barline,
- // then start must not be on a barline, so there is a partial
- // measure from start to the next barline. We need
- // a time signature there to preserve relative barline
- // locations. It may be that the next bar after start is
- // due to another time signature, in which case we do not
- // need to insert anything.
- double measures = start / 4.0;
- double imeasures = ROUND(measures);
- if (!within(measures, imeasures, ALG_EPS)) {
- // start is not on a barline, maybe add one here:
- double bar_loc = (int(measures) + 1) * 4.0;
- if (len == 0 || time_sigs[1].beat > bar_loc + ALG_EPS) {
- insert(bar_loc, 4, 4, true);
- }
- }
- // This case should never be true because if i == 0, either there
- // are no time signatures before beat (Case 2),
- // or there is one time signature at beat (Case 1)
- } else if (i == 0) {
- /* do nothing (might be good to assert(false)) */ ;
- // Case 3: i-1 must be the effective time sig position
- } else {
- i -= 1; // index the time signature in effect at start
- Alg_time_sig &tsp = time_sigs[i];
- double beats_per_measure = (tsp.num * 4) / tsp.den;
- double measures = (start - tsp.beat) / beats_per_measure;
- int imeasures = ROUND(measures);
- if (!within(measures, imeasures, ALG_EPS)) {
- // beat is not on a measure, so we need to insert a time sig
- // to force a bar line at the first measure location after
- // beat, if any
- double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1);
- // insert new time signature at bar_loc
- // It will have the same time signature, but the position will
- // force a barline to match the barline before the shift
- insert(bar_loc, tsp.num, tsp.den, true);
- }
- // else beat coincides with a barline, so no need for an extra
- // time signature to force barline alignment
- }
- // since we may have inserted a time signature, find position again:
- int i_in = find_beat(start);
- int i_out = 0;
-
- // put time_sig at start if necessary
- // if 0 < i_in < len, then the time sig at i_in is either
- // at start or after start.
- // If after start, then insert time sig at i_in-1 at 0.
- // Otherwise, we'll pick up time sig at i_in below.
- // If 0 == i_in < len, then the time sig at i_in is either
- // at start or after start.
- // If after start, then time sig at 0 is 4/4, but that's the
- // default, so do nothing.
- // Otherwise, we'll pick up time sig at i_in below.
- // If 0 < i_in == len, then insert time_sig at i_in-1 at start
- // If 0 == i_in == len, then 4/4 default applies and we're done.
- //
- // So the conditions for inserting time_sig[in_i-1] at 0 are:
- // (0 < i_in < len and time_sig[i] > start+ALG_EPS) OR
- // (0 < i_in == len)
- // We can rewrite this to
- // (0 < i_in) && ((i_in < len && time_sig[i_in].beat > start + ALG_EPS) ||
- // (i_in == len)))
- //
- if (0 < i_in && ((i_in < len && time_sigs[i_in].beat > start + ALG_EPS) ||
- (i_in == len))) {
- time_sigs[0] = time_sigs[i_in - 1];
- time_sigs[0].beat = 0.0;
- i_out = 1;
- }
- // copy from i_in to i_out as we scan time_sig array to end of cut region
- while (i_in < len && time_sigs[i_in].beat < end - ALG_EPS) {
- Alg_time_sig &ts = time_sigs[i_in];
- ts.beat = ts.beat - start;
- time_sigs[i_out] = ts;
- i_in++;
- i_out++;
- }
- len = i_out;
- #endif
- }
- void Alg_time_sigs::paste(double start, Alg_seq *seq)
- {
- // printf("time_sig::insert before paste\n");
- // show();
- Alg_time_sigs &from = seq->time_sig;
- // printf("time_sig::insert from\n");
- // from.show();
- // insert time signatures from seq into this time_sigs at start
- if (len == 0 && from.len == 0) {
- return; // default applies
- }
- int i = find_beat(start);
- // remember the time signature at the splice point
- double num_after_splice = 4;
- double den_after_splice = 4; // default
- double num_before_splice = 4;
- double den_before_splice = 4; // default
- // this is computed for use in aligning beats after the inserted
- // time signatures and duration. It is the position of time signature
- // in effect immediately after start (the time signature will be
- // before start or at start)
- double beat_after_splice = 0.0;
- // three cases:
- // 1) time sig at splice is at i-1
- // for this, we must have len>0 & i>0
- // two sub-cases:
- // A) i < len && time_sig[i].beat > start
- // B) i == len
- // 2) time_sig at splice is at i
- // for this, i < len && time_sig[i].beat ~= start
- // 3) time_sig at splice is default 4/4
- if (len > 0 && i > 0 &&
- ((i < len && time_sigs[i].beat > start + ALG_EPS) ||
- (i == len))) {
- // no time_signature at i
- num_after_splice = time_sigs[i-1].num;
- den_after_splice = time_sigs[i-1].den;
- beat_after_splice = time_sigs[i - 1].beat;
- num_before_splice = num_after_splice;
- den_before_splice = den_after_splice;
- } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) {
- // time_signature at i is at "start" beats
- num_after_splice = time_sigs[i].num;
- den_after_splice = time_sigs[i].den;
- beat_after_splice = start;
- if (i > 0) { // time signature before start is at i - 1
- num_before_splice = time_sigs[i-1].num;
- den_before_splice = time_sigs[i-1].den;
- }
- }
- // i is where insert will go, time_sig[i].beat >= start
- // begin by adding duration to time_sig's at i and above
- // move time signatures forward by duration of seq
- double dur = seq->get_beat_dur();
- while (i < len) {
- time_sigs[i].beat += dur;
- i++;
- }
- //printf("time_sig::insert after making space\n");
- //show();
- // If time signature of "from" is not the effective time signature
- // at start, insert a time_signature at start. This may create
- // an extra measure if seq does not begin on a measure boundary
- double num_of_insert = 4.0;
- double den_of_insert = 4.0;
- double beat_of_insert = 0.0;
- /* int first_from_index = 0; // where to start copying from TODO: LMMS commented out unused variable */
- if (from.length() > 0 && from[0].beat < ALG_EPS) {
- // there is an initial time signature in "from"
- num_of_insert = from[0].num;
- den_of_insert = from[0].den;
- // since we are handling the first time signature in from,
- // we can start copying at index == 1:
- /* first_from_index = 1; TODO: LMMS commented out unused variable */
- }
- // compare time signatures to see if we need a change at start:
- if (num_before_splice != num_of_insert ||
- den_before_splice != den_of_insert) {
- // note that this will overwrite an existing time signature if
- // it is within ALG_EPS of start -- this is correct because the
- // existing time signature will already be recorded as
- // num_after_splice and den_after_splice
- insert(start, num_of_insert, den_of_insert);
- }
- //printf("time_sig::insert after 4/4 at start\n");
- //show();
- // insert time signatures from seq offset by start
- for (i = 0; i < from.length() && from[i].beat < dur - ALG_EPS; i++) {
- num_of_insert = from[i].num; // keep latest time signature info
- den_of_insert = from[i].den;
- beat_of_insert = from[i].beat;
- insert(start + beat_of_insert, num_of_insert, den_of_insert);
- }
- //printf("time_sig::insert after pasting in sigs\n");
- //show();
- // now insert time signature at end of splice if necessary
- // if the time signature changes, we need to insert a time signature
- // immediately:
- if (num_of_insert != num_after_splice &&
- den_of_insert != den_after_splice) {
- insert(start + dur, num_after_splice, den_after_splice);
- num_of_insert = num_after_splice;
- den_of_insert = den_after_splice;
- beat_of_insert = start + dur;
- }
- // if the insert had a partial number of measures, we might need an
- // additional time signature to realign the barlines after the insert
- // To decide, we compare the beat of the first barline on or after
- // start before the splice to the beat of the first barline on or
- // after start + dur after the splice. In a sense, this is the "same"
- // barline, so it should be shifted exactly by dur.
- // First, compute the beat of the first barline on or after start:
- double beats_per_measure = (num_after_splice * 4) / den_after_splice;
- double measures = (start - beat_after_splice) / beats_per_measure;
- // Measures might be slightly negative due to rounding. Use max()
- // to eliminate any negative rounding error:
- int imeasures = int(MAX(measures, 0.0));
- double old_bar_loc = beat_after_splice + (imeasures * beats_per_measure);
- if (old_bar_loc < start) old_bar_loc += beats_per_measure;
- // now old_bar_loc is the original first bar position after start
- // Do similar calculation for position after end after the insertion:
- // beats_per_measure already calculated because signatures match
- measures = (start + dur - beat_of_insert) / beats_per_measure;
- imeasures = int(MAX(measures, 0.0));
- double new_bar_loc = beat_of_insert + (imeasures * beats_per_measure);
- if (new_bar_loc < start + dur) new_bar_loc += beats_per_measure;
- // old_bar_loc should be shifted by dur:
- old_bar_loc += dur;
- // now the two bar locations should be equal, but due to rounding,
- // they could be off by one measure
- double diff = (new_bar_loc - old_bar_loc) + beats_per_measure;
- double diff_in_measures = diff / beats_per_measure;
- // if diff_in_measures is not (approximately) integer, we need to
- // force a barline (time signature) after start + dur to maintain
- // the relationship between barliness and notes
- if (!within(diff_in_measures, ROUND(diff_in_measures), ALG_EPS)) {
- // recall that old_bar_loc is shifted by dur
- insert(old_bar_loc, num_after_splice, den_after_splice);
- }
- //printf("time_sig::insert after sig at end of splice\n");
- //show();
- }
- void Alg_time_sigs::insert_beats(double start, double dur)
- {
- int i = find_beat(start);
- // time_sigs[i] is after beat and needs to shift
- // Compute the time of the first bar at or after beat so that
- // a bar can be placed at bar_loc + dur
- double tsnum = 4.0;
- double tsden = 4.0;
- double tsbeat = 0.0; // defaults
-
- // three cases:
- // 1) time sig at splice is at i-1
- // for this, we must have len>0 & i>0
- // two sub-cases:
- // A) i < len && time_sig[i].beat > start
- // B) i == len
- // 2) time_sig at splice is at i
- // for this, i < len && time_sig[i].beat ~= start
- // 3) time_sig at splice is default 4/4
- if (len > 0 && i > 0 &&
- ((i < len && time_sigs[i].beat > start + ALG_EPS) ||
- (i == len))) {
- // no time_signature at i
- tsnum = time_sigs[i-1].num;
- tsden = time_sigs[i-1].den;
- tsbeat = time_sigs[i-1].beat;
- } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) {
- // time_signature at i is at "start" beats
- tsnum = time_sigs[i].num;
- tsden = time_sigs[i].den;
- tsbeat = start;
- i++; // we want i to be index of next time signature after start
- }
- // invariant: i is index of next time signature after start
- // increase beat times from i to len - 1 by dur
- for (int j = i; j < len; j++) {
- time_sigs[j].beat += dur;
- }
- // insert a time signature to maintain bar positions if necessary
- double beats_per_measure = (tsnum * 4) / tsden;
- double measures = dur / beats_per_measure; // shift distance
- int imeasures = ROUND(measures);
- if (!within(measures, imeasures, ALG_EPS)) {
- // shift is not a whole number of measures, so we may need to insert
- // time signature after silence
- // compute measures from time signature to next bar after time
- measures = (start - tsbeat) / beats_per_measure;
- // round up and add to tsbeat to get time of next bar
- double bar_loc = tsbeat + beats_per_measure * (int(measures) + 1);
- // translate bar_loc by len:
- bar_loc += dur; // this is where we want a bar to be, but maybe
- // there is a time signature change before bar, in which case we
- // should not insert a new time signature
- // The next time signature after start is at i if i < len
- if (i < len && time_sigs[i].beat < bar_loc) {
- /* do not insert */;
- } else {
- insert(bar_loc, tsnum, tsden);
- }
- }
- }
- double Alg_time_sigs::nearest_beat(double beat)
- {
- int i = find_beat(beat);
- // i is where we would insert time signature at beat
- // case 1: there is no time signature
- if (i == 0 && len == 0) {
- return ROUND(beat);
- // case 2: beat falls approximately on time signature
- } else if (i < len && within(time_sigs[i].beat, beat, ALG_EPS)) {
- return time_sigs[i].beat;
- // case 3: beat is after no time signature and before one
- } else if (i == 0) {
- double trial_beat = ROUND(beat);
- // it is possible that we rounded up past a time signature
- if (trial_beat > time_sigs[0].beat - ALG_EPS) {
- return time_sigs[0].beat;
- }
- return trial_beat;
- }
- // case 4: beat is after some time signature
- double trial_beat = time_sigs[i - 1].beat +
- ROUND(beat - time_sigs[i - 1].beat);
- // rounding may advance trial_beat past next time signature:
- if (i < len && trial_beat > time_sigs[i].beat - ALG_EPS) {
- return time_sigs[i].beat;
- }
- return trial_beat;
- }
- Alg_tracks::~Alg_tracks()
- {
- reset();
- }
- void Alg_tracks::expand_to(int new_max)
- {
- maxlen = new_max;
- Alg_track_ptr *new_tracks = new Alg_track_ptr[maxlen];
- // now do copy
- memcpy(new_tracks, tracks, len * sizeof(Alg_track_ptr));
- if (tracks) {
- delete[] tracks;
- }
- tracks = new_tracks;
- }
- void Alg_tracks::expand()
- {
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- expand_to(maxlen);
- }
- void Alg_tracks::append(Alg_track_ptr track)
- {
- if (maxlen <= len) {
- expand();
- }
- tracks[len] = track;
- len++;
- }
- void Alg_tracks::add_track(int track_num, Alg_time_map_ptr time_map,
- bool seconds)
- // Create a new track at index track_num.
- // If track already exists, this call does nothing.
- // If highest previous track is not at track_num-1, then
- // create tracks at len, len+1, ..., track_num.
- {
- assert(track_num >= 0);
- if (track_num == maxlen) {
- // use eponential growth to insert tracks sequentially
- expand();
- } else if (track_num > maxlen) {
- // grow to exact size for random inserts
- expand_to(track_num + 1);
- }
- if (track_num < len) return; // don't add if already there
- while (len <= track_num) {
- tracks[len] = new Alg_track(time_map, seconds);
- //printf("allocated track at %d (%x, this %x) = %x\n", len,
- // &(tracks[len]), this, tracks[len]);
- len++;
- }
- }
- void Alg_tracks::reset()
- {
- // all track events are incorporated into the seq,
- // so all we need to delete are the arrays of pointers
- for (int i = 0; i < len; i++) {
- // printf("deleting track at %d (%x, this %x) = %x\n", i, &(tracks[i]),
- // this, tracks[i]);
- delete tracks[i];
- }
- if (tracks) delete [] tracks;
- tracks = nullptr;
- len = 0;
- maxlen = 0;
- }
- void Alg_tracks::set_in_use(bool flag)
- {
- for (int i = 0; i < len; i++) {
- tracks[i]->in_use = flag;
- }
- }
- void Alg_iterator::expand_to(int new_max)
- {
- maxlen = new_max;
- Alg_pending_event_ptr new_pending_events = new Alg_pending_event[maxlen];
- // now do copy
- memcpy(new_pending_events, pending_events,
- len * sizeof(Alg_pending_event));
- if (pending_events) {
- delete[] pending_events;
- }
- pending_events = new_pending_events;
- }
- void Alg_iterator::expand()
- {
- maxlen = (maxlen + 5); // extra growth for small sizes
- maxlen += (maxlen >> 2); // add 25%
- expand_to(maxlen);
- }
- Alg_iterator::~Alg_iterator()
- {
- if (pending_events) {
- delete[] pending_events;
- }
- }
- /* in the heap, the children of N are (N+1)*2 and (N+1)*2-1, so
- * the parent of N is (N+1)/2-1. This would be easier if arrays
- * were 1-based instead of 0-based
- */
- #define HEAP_PARENT(loc) ((((loc) + 1) / 2) - 1)
- #define FIRST_CHILD(loc) (((loc) * 2) + 1)
- void Alg_iterator::show()
- {
- for (int i = 0; i < len; i++) {
- Alg_pending_event_ptr p = &(pending_events[i]);
- printf(" %d: %p[%ld]@%g on %d\n", i, p->events, p->index,
- p->offset, p->note_on);
- }
- }
- bool Alg_iterator::earlier(int i, int j)
- // see if event i is earlier than event j
- {
- // note-offs are scheduled ALG_EPS early so that if a note-off is
- // followed immediately with the same timestamp by a note-on (common
- // in MIDI files), the note-off will be scheduled first
- double t_i = pending_events[i].time;
- double t_j = pending_events[j].time;
- if (t_i < t_j) return true;
- // not sure if this case really exists or this is the best rule, but
- // we want to give precedence to note-off events
- else if (t_i == t_j && pending_events[j].note_on) return true;
- return false;
- }
- void Alg_iterator::insert(Alg_events_ptr events, long index,
- bool note_on, void *cookie, double offset)
- {
- if (len == maxlen) expand();
- pending_events[len].events = events;
- pending_events[len].index = index;
- pending_events[len].note_on = note_on;
- pending_events[len].cookie = cookie;
- pending_events[len].offset = offset;
- Alg_event_ptr event = (*events)[index];
- pending_events[len].time = (note_on ? event->time :
- event->get_end_time() - ALG_EPS) + offset;
- /* BEGIN DEBUG *
- printf("insert %p=%p[%d] @ %g\n", event, events, index,
- pending_events[len].time);
- printf(" is_note %d note_on %d time %g dur %g end_time %g offset %g\n",
- event->is_note(), note_on, event->time, event->get_duration(),
- event->get_end_time(), offset);
- }
- * END DEBUG */
- int loc = len;
- int loc_parent = HEAP_PARENT(loc);
- len++;
- // sift up:
- while (loc > 0 &&
- earlier(loc, loc_parent)) {
- // swap loc with loc_parent
- Alg_pending_event temp = pending_events[loc];
- pending_events[loc] = pending_events[loc_parent];
- pending_events[loc_parent] = temp;
- loc = loc_parent;
- loc_parent = HEAP_PARENT(loc);
- }
- }
- bool Alg_iterator::remove_next(Alg_events_ptr &events, long &index,
- bool ¬e_on, void *&cookie,
- double &offset, double &time)
- {
- if (len == 0) return false; // empty!
- events = pending_events[0].events;
- index = pending_events[0].index;
- note_on = pending_events[0].note_on;
- offset = pending_events[0].offset;
- cookie = pending_events[0].cookie;
- offset = pending_events[0].offset;
- time = pending_events[0].time;
- len--;
- pending_events[0] = pending_events[len];
- // sift down
- long loc = 0;
- long loc_child = FIRST_CHILD(loc);
- while (loc_child < len) {
- if (loc_child + 1 < len) {
- if (earlier(loc_child + 1, loc_child)) {
- loc_child++;
- }
- }
- if (earlier(loc_child, loc)) {
- Alg_pending_event temp = pending_events[loc];
- pending_events[loc] = pending_events[loc_child];
- pending_events[loc_child] = temp;
- loc = loc_child;
- loc_child = FIRST_CHILD(loc);
- } else {
- loc_child = len;
- }
- }
- // printf("After remove:"); show();
- return true;
- }
- Alg_seq::Alg_seq(const char *filename, bool smf, double *offset_ptr)
- {
- basic_initialization();
- ifstream inf(filename, smf ? ios::binary | ios::in : ios::in);
- if (inf.fail()) {
- error = alg_error_open;
- return;
- }
- if (smf) {
- error = alg_smf_read(inf, this);
- if (offset_ptr) *offset_ptr = 0.0;
- } else {
- error = alg_read(inf, this, offset_ptr);
- }
- inf.close();
- }
- Alg_seq::Alg_seq(istream &file, bool smf, double *offset_ptr)
- {
- basic_initialization();
- if (smf) {
- error = alg_smf_read(file, this);
- if (offset_ptr) *offset_ptr = 0.0;
- } else {
- error = alg_read(file, this, offset_ptr);
- }
- }
- void Alg_seq::seq_from_track(Alg_track_ref tr)
- {
- type = 's';
- // copy everything
- set_beat_dur(tr.get_beat_dur());
- set_real_dur(tr.get_real_dur());
- // copy time_map
- set_time_map(new Alg_time_map(tr.get_time_map()));
- units_are_seconds = tr.get_units_are_seconds();
- if (tr.get_type() == 's') {
- Alg_seq_ref s = *(tr.to_alg_seq());
- channel_offset_per_track = s.channel_offset_per_track;
- add_track(s.tracks() - 1);
- // copy each track
- for (int i = 0; i < tracks(); i++) {
- Alg_track_ref from_track = *(s.track(i));
- Alg_track_ref to_track = *(track(i));
- to_track.set_beat_dur(from_track.get_beat_dur());
- to_track.set_real_dur(from_track.get_real_dur());
- if (from_track.get_units_are_seconds())
- to_track.convert_to_seconds();
- for (int j = 0; j < from_track.length(); j++) {
- Alg_event_ptr event = copy_event(from_track[j]);
- to_track.append(event);
- }
- }
- } else if (tr.get_type() == 't') {
- add_track(0);
- channel_offset_per_track = 0;
- Alg_track_ptr to_track = track(0);
- to_track->set_beat_dur(tr.get_beat_dur());
- to_track->set_real_dur(tr.get_real_dur());
- for (int j = 0; j < tr.length(); j++) {
- Alg_event_ptr event = copy_event(tr[j]);
- to_track->append(event);
- }
- } else {
- assert(false); // expected track or sequence
- }
- }
- int Alg_seq::tracks()
- {
- return track_list.length();
- }
- Alg_track_ptr Alg_seq::track(int i)
- {
- assert(0 <= i && i < track_list.length());
- return &(track_list[i]);
- }
- //#pragma warning(disable: 4715) // ok not to return a value here
- Alg_event_ptr const &Alg_seq::operator[](int i)
- {
- int ntracks = track_list.length();
- int tr = 0;
- while (tr < ntracks) {
- Alg_track *a_track = track(tr);
- if (a_track && i < a_track->length()) {
- return (*a_track)[i];
- } else if (a_track) {
- i -= a_track->length();
- }
- tr++;
- }
- throw std::out_of_range{"Alg_seq::operator[] - Index out of range."};
- }
- //#pragma warning(default: 4715)
- void Alg_seq::convert_to_beats()
- {
- if (!units_are_seconds) return;
- for (int i = 0; i < tracks(); i++) {
- track(i)->convert_to_beats();
- }
- // note that the Alg_seq inherits units_are_seconds from an
- // empty track. Each track also has a (redundant) field called
- // units are seconds. These should always be consistent.
- units_are_seconds = false;
- }
- void Alg_seq::convert_to_seconds()
- {
- if (units_are_seconds) return;
- //printf("convert_to_seconds, tracks %d\n", tracks());
- //printf("last_tempo of seq: %g on map %x\n",
- // get_time_map()->last_tempo, get_time_map());
- for (int i = 0; i < tracks(); i++) {
- //printf("last_tempo of track %d: %g on %x\n", i,
- // track(i)->get_time_map()->last_tempo,
- // track(i)->get_time_map());
- track(i)->convert_to_seconds();
- }
- // update our copy of last_note_off (which may or may not be valid)
- last_note_off = time_map->beat_to_time(last_note_off);
- // note that the Alg_seq inherits units_are_seconds from an
- // empty track. Each track also has a (redundant) field called
- // units are seconds. These should always be consistent.
- units_are_seconds = true;
- }
- Alg_track_ptr Alg_seq::cut_from_track(int track_num, double start,
- double dur, bool all)
- {
- assert(track_num >= 0 && track_num < tracks());
- Alg_track_ptr tr = track(track_num);
- return tr->cut(start, dur, all);
- }
- void Alg_seq::copy_time_sigs_to(Alg_seq *dest)
- {
- // copy time signatures
- for (int i = 0; i < time_sig.length(); i++) {
- dest->time_sig.insert(time_sig[i].beat, time_sig[i].num,
- time_sig[i].den);
- }
- }
- void Alg_seq::set_time_map(Alg_time_map *map)
- {
- Alg_track::set_time_map(map);
- for (int i = 0; i < tracks(); i++) {
- track(i)->set_time_map(map);
- }
- }
- Alg_seq_ptr Alg_seq::cut(double start, double len, bool all)
- // return sequence from start to start+len and modify this
- // sequence by removing that time-span
- {
- double dur = get_dur();
- // fix parameters to fall within existing sequence
- if (start > dur) return nullptr; // nothing to cut
- if (start < 0) start = 0; // can't start before sequence starts
- if (start + len > dur) // can't cut after end:
- len = dur - start;
- Alg_seq_ptr result = new Alg_seq();
- Alg_time_map_ptr map = new Alg_time_map(get_time_map());
- result->set_time_map(map);
- copy_time_sigs_to(result);
- result->units_are_seconds = units_are_seconds;
- result->track_list.reset();
- for (int i = 0; i < tracks(); i++) {
- Alg_track_ptr cut_track = cut_from_track(i, start, len, all);
- result->track_list.append(cut_track);
- // initially, result->last_note_off is zero. We want to know the
- // maximum over all cut_tracks, so compute that here:
- result->last_note_off = MAX(result->last_note_off,
- cut_track->last_note_off);
- // since we're moving to a new sequence, change the track's time_map
- result->track_list[i].set_time_map(map);
- }
- // put units in beats to match time_sig's. Note that we need
- // two different end times. For result, we want the time of the
- // last note off, but for cutting out the time signatures in this,
- // we use len.
- double ts_start = start;
- double ts_end = start + len;
- double ts_dur = dur;
- double ts_last_note_off = start + result->last_note_off;
- if (units_are_seconds) {
- ts_start = time_map->time_to_beat(ts_start);
- ts_end = time_map->time_to_beat(ts_end);
- ts_last_note_off = time_map->time_to_beat(ts_last_note_off);
- ts_dur = time_map->time_to_beat(ts_dur);
- }
- // result is shifted from start to 0 and has length len, but
- // time_sig and time_map are copies from this. Adjust time_sig,
- // time_map, and duration fields in result. The time_sig and
- // time_map data is retained out to last_note_off so that we have
- // information for the entire duration of all the notes, even though
- // this might extend beyond the duration of the track. (Warning:
- // no info is retained for notes with negative times.)
- result->time_sig.trim(ts_start, ts_last_note_off);
- result->time_map->trim(start, start + result->last_note_off,
- result->units_are_seconds);
- // even though there might be notes sticking out beyond len, the
- // track duration is len, not last_note_off. (Warning: if all is
- // true, there may also be notes at negative offsets. These times
- // cannot be mapped between beat and time representations, so there
- // may be subtle bugs or unexpected behaviors in that case.)
- result->set_dur(len);
- // we sliced out a portion of each track, so now we need to
- // slice out the corresponding sections of time_sig and time_map
- // as well as to adjust the duration.
- time_sig.cut(ts_start, ts_end, ts_dur);
- time_map->cut(start, len, units_are_seconds);
- set_dur(dur - len);
- return result;
- }
- void Alg_seq::insert_silence_in_track(int track_num, double t, double len)
- {
- Alg_track_ptr tr = track(track_num);
- tr->insert_silence(t, len);
- }
- void Alg_seq::insert_silence(double t, double len)
- {
- for (int i = 0; i < tracks(); i++) {
- insert_silence_in_track(i, t, len);
- }
- double t_beats = t;
- double len_beats = len;
- // insert into time_sig array; use time_sig_paste,
- // which requires us to build a simple time_sig array
- if (units_are_seconds) {
- time_map->insert_time(t, len);
- t_beats = time_map->time_to_beat(t);
- len_beats = time_map->time_to_beat(t + len) - t_beats;
- } else {
- time_map->insert_beats(t_beats, len_beats);
- }
- time_sig.insert_beats(t_beats, len_beats);
- // Final duration is defined to be t + len + whatever was
- // in the sequence after t (if any). This translates to
- // t + len + max(dur - t, 0)
- set_dur(t + len + MAX(get_dur() - t, 0.0));
- }
- Alg_track_ptr Alg_seq::copy_track(int track_num, double t, double len, bool all)
- {
- return track_list[track_num].copy(t, len, all);
- }
- Alg_seq *Alg_seq::copy(double start, double len, bool all)
- {
- // fix parameters to fall within existing sequence
- if (start > get_dur()) return nullptr; // nothing to copy
- if (start < 0) start = 0; // can't copy before sequence starts
- if (start + len > get_dur()) // can't copy after end:
- len = get_dur() - start;
- // return (new) sequence from start to start + len
- Alg_seq_ptr result = new Alg_seq();
- Alg_time_map_ptr map = new Alg_time_map(get_time_map());
- result->set_time_map(map);
- copy_time_sigs_to(result);
- result->units_are_seconds = units_are_seconds;
- result->track_list.reset();
- for (int i = 0; i < tracks(); i++) {
- Alg_track_ptr copy = copy_track(i, start, len, all);
- result->track_list.append(copy);
- result->last_note_off = MAX(result->last_note_off,
- copy->last_note_off);
- // since we're copying to a new seq, change the track's time_map
- result->track_list[i].set_time_map(map);
- }
- // put units in beats to match time_sig's. Note that we need
- // two different end times. For result, we want the time of the
- // last note off, but for cutting out the time signatures in this,
- // we use len.
- double ts_start = start;
- double ts_end = start + len;
- double ts_last_note_off = start + result->last_note_off;
- if (units_are_seconds) {
- ts_start = time_map->time_to_beat(ts_start);
- ts_end = time_map->time_to_beat(ts_end);
- ts_last_note_off = time_map->time_to_beat(ts_last_note_off);
- }
- result->time_sig.trim(ts_start, ts_last_note_off);
- result->time_map->trim(start, start + result->last_note_off,
- units_are_seconds);
- result->set_dur(len);
- return result;
- }
- void Alg_seq::paste(double start, Alg_seq *seq)
- {
- // Insert seq at time, opening up space for it.
- // To manipulate time map, we need units as beats.
- // Save original form so we can convert back if necessary.
- bool units_should_be_seconds = units_are_seconds;
- bool seq_units_should_be_seconds = seq->get_units_are_seconds();
- if (units_are_seconds) {
- start = time_map->time_to_beat(start);
- convert_to_beats();
- }
- seq->convert_to_beats();
- // do a paste on each track
- int i;
- for (i = 0; i < seq->tracks(); i++) {
- if (i >= tracks()) {
- add_track(i);
- }
- track(i)->paste(start, seq->track(i));
- }
- // make sure all tracks were opened up for an insert, even if
- // there is nothing to insert
- while (i < tracks()) {
- track(i)->insert_silence(start, seq->get_dur());
- i++;
- }
- // paste in tempo track
- time_map->paste(start, seq);
- // paste in time signatures
- time_sig.paste(start, seq);
- set_dur(get_beat_dur() + seq->get_dur());
- assert(!seq->units_are_seconds && !units_are_seconds);
- if (units_should_be_seconds) {
- convert_to_seconds();
- }
- if (seq_units_should_be_seconds) {
- seq->convert_to_seconds();
- }
- }
- void Alg_seq::merge(double t, Alg_event_list_ptr seq)
- {
- // seq must be an Alg_seq:
- assert(seq->get_type() == 's');
- Alg_seq_ptr s = (Alg_seq_ptr) seq;
- for (int i = 0; i < s->tracks(); i++) {
- if (tracks() <= i) add_track(i);
- track(i)->merge(t, s->track(i));
- }
- }
- void Alg_seq::silence_track(int track_num, double start, double len, bool all)
- {
- // remove events in [time, time + len) and close gap
- Alg_track_ptr tr = track(track_num);
- tr->silence(start, len, all);
- }
- void Alg_seq::silence(double t, double len, bool all)
- {
- for (int i = 0; i < tracks(); i++) {
- silence_track(i, t, len, all);
- }
- }
- void Alg_seq::clear_track(int track_num, double start, double len, bool all)
- {
- // remove events in [time, time + len) and close gap
- Alg_track_ptr tr = track(track_num);
- tr->clear(start, len, all);
- }
- void Alg_seq::clear(double start, double len, bool all)
- {
- // Fix parameters to fall within existing sequence
- double dur = get_dur();
- if (start > dur) return; // nothing to cut
- if (start < 0) start = 0; // can't start before sequence starts
- if (start + len > dur) // can't cut after end:
- len = dur - start;
- for (int i = 0; i < tracks(); i++)
- clear_track(i, start, len, all);
- // Put units in beats to match time_sig's.
- double ts_start = start;
- double ts_end = start + len;
- double ts_dur = dur;
- if (units_are_seconds) {
- ts_start = time_map->time_to_beat(ts_start);
- ts_end = time_map->time_to_beat(ts_end);
- ts_dur = time_map->time_to_beat(ts_dur);
- }
- // we sliced out a portion of each track, so now we need to
- // slice out the corresponding sections of time_sig and time_map
- // as well as to adjust the duration.
- time_sig.cut(ts_start, ts_end, ts_dur);
- time_map->cut(start, len, units_are_seconds);
- set_dur(dur - len);
- }
- Alg_event_list_ptr Alg_seq::find_in_track(int track_num, double t, double len,
- bool all, long channel_mask,
- long event_type_mask)
- {
- return track(track_num)->find(t, len, all, channel_mask, event_type_mask);
- }
- Alg_seq::~Alg_seq()
- {
- int i, j;
- // Tracks does not delete Alg_events elements
- for (j = 0; j < track_list.length(); j++) {
- Alg_track ¬es = track_list[j];
- // Alg_events does not delete notes
- for (i = 0; i < notes.length(); i++) {
- Alg_event_ptr event = notes[i];
- delete event;
- }
- }
- }
- long Alg_seq::seek_time(double time, int track_num)
- // find index of first score event after time
- {
- long i;
- Alg_events ¬es = track_list[track_num];
- for (i = 0; i < notes.length(); i++) {
- if (notes[i]->time > time) {
- break;
- }
- }
- return i;
- }
- bool Alg_seq::insert_beat(double time, double beat)
- // insert a time,beat pair
- // return true or false (false indicates an error, no update)
- // it is an error to imply a negative tempo or to insert at
- // a negative time
- {
- if (time < 0 || beat < 0) return false;
- if (time == 0.0 && beat > 0)
- time = 0.000001; // avoid infinite tempo, offset time by 1us
- if (time == 0.0 && beat == 0.0)
- return true; // (0,0) is already in the map!
- convert_to_beats(); // beats are invariant when changing tempo
- time_map->insert_beat(time, beat);
- return true;
- }
- // input is time, return value is time
- double Alg_seq::nearest_beat_time(double time, double *beat)
- {
- double b = time_map->time_to_beat(time);
- b = time_sig.nearest_beat(b);
- if (beat) *beat = b;
- return time_map->beat_to_time(b);
- }
- bool Alg_seq::stretch_region(double b0, double b1, double dur)
- {
- bool units_should_be_seconds = units_are_seconds;
- convert_to_beats();
- bool result = time_map->stretch_region(b0, b1, dur);
- if (units_should_be_seconds) convert_to_seconds();
- return result;
- }
- bool Alg_seq::insert_tempo(double bpm, double beat)
- {
- double bps = bpm / 60.0; // convert to beats per second
- // change the tempo at the given beat until the next beat event
- if (beat < 0) return false;
- convert_to_beats(); // beats are invariant when changing tempo
- double time = time_map->beat_to_time(beat);
- long i = time_map->locate_time(time);
- if (i >= time_map->beats.len || !within(time_map->beats[i].time, time, 0.000001)) {
- insert_beat(time, beat);
- }
- // now i is index of beat where tempo will change
- if (i == time_map->beats.len - 1) {
- time_map->last_tempo = bps;
- time_map->last_tempo_flag = true;
- } else { // adjust all future beats
- // compute the difference in beats
- double diff = time_map->beats[i + 1].beat - time_map->beats[i].beat;
- // convert beat difference to seconds at new tempo
- diff = diff / bps;
- // figure out old time difference:
- double old_diff = time_map->beats[i + 1].time - time;
- // compute difference too
- diff = diff - old_diff;
- // apply new_diff to score and beats
- while (i < time_map->beats.len) {
- time_map->beats[i].time = time_map->beats[i].time + diff;
- i++;
- }
- }
- return true;
- }
- void Alg_seq::add_event(Alg_event_ptr event, int track_num)
- // add_event puts an event in a given track (track_num).
- // The track must exist. The time and duration of the
- // event are interpreted according to whether the Alg_seq
- // is currently in beats or seconds (see convert_to_beats())
- {
- track_list[track_num].insert(event);
- /*
- if (event->is_note()) {
- Alg_note_ptr n = (Alg_note_ptr) event;
- trace("note %d at %g for %g\n", n->get_identifier(), n->time, n->dur);
- }
- */
- }
- double Alg_seq::get_tempo(double beat)
- {
- return time_map->get_tempo(beat);
- }
- bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat)
- // set tempo from start_beat to end_beat
- {
- // this is an optimization, the test is repeated in Alg_time_seq::set_tempo()
- if (start_beat >= end_beat) return false;
- bool units_should_be_seconds = units_are_seconds;
- convert_to_beats();
- double dur = get_dur();
- bool result = time_map->set_tempo(bpm, start_beat, end_beat);
- // preserve sequence duration in beats when tempo changes
- set_dur(dur);
- if (units_should_be_seconds) convert_to_seconds();
- return result;
- }
- double Alg_seq::get_bar_len(double beat)
- {
- return time_sig.get_bar_len(beat);
- }
- void Alg_seq::set_time_sig(double beat, double num, double den)
- {
- time_sig.insert(beat, num, den);
- }
- void Alg_seq::beat_to_measure(double beat, long *measure, double *m_beat,
- double *num, double *den)
- {
- // return [measure, beat, num, den]
- double m = 0; // measure number
- double bpm;
- int tsx;
- bpm = 4;
- // assume 4/4 if no time signature
- double prev_beat = 0;
- double prev_num = 4;
- double prev_den = 4;
- if (beat < 0) beat = 0; // negative measures treated as zero
- for (tsx = 0; tsx < time_sig.length(); tsx++) {
- if (time_sig[tsx].beat <= beat) {
- // round m up to an integer (but allow for a small
- // numerical inaccuracy)
- m = m + (long) (0.99 + (time_sig[tsx].beat - prev_beat) / bpm);
- bpm = time_sig[tsx].num * 4 / time_sig[tsx].den;
- prev_beat = time_sig[tsx].beat;
- prev_num = time_sig[tsx].num;
- prev_den = time_sig[tsx].den;
- } else {
- m = m + (beat - prev_beat) / bpm;
- *measure = (long) m;
- *m_beat = (m - *measure) * bpm;
- *num = prev_num;
- *den = prev_den;
- return;
- }
- }
- // if we didn't return yet, compute after last time signature
- Alg_time_sig initial(0, 4, 4);
- Alg_time_sig &prev = initial;
- if (tsx > 0) { // use last time signature
- prev = time_sig[time_sig.length() - 1];
- }
- bpm = prev.num * 4 / prev.den;
- m = m + (beat - prev.beat) / bpm;
- *measure = (long) m;
- *m_beat = (m - *measure) * bpm;
- *num = prev.num;
- *den = prev.den;
- }
- /*
- void Alg_seq::set_events(Alg_event_ptr *events, long len, long max)
- {
- convert_to_seconds(); // because notes are in seconds
- notes.set_events(events, len, max);
- }
- */
- void Alg_iterator::begin_seq(Alg_seq_ptr s, void *cookie, double offset)
- {
- // keep an array of indexes into tracks
- // printf("new pending\n");
- int i;
- for (i = 0; i < s->track_list.length(); i++) {
- if (s->track_list[i].length() > 0) {
- insert(&(s->track_list[i]), 0, true, cookie, offset);
- }
- }
- }
- Alg_event_ptr Alg_iterator::next(bool *note_on, void **cookie_ptr,
- double *offset_ptr, double end_time)
- // return the next event in time from any track
- {
- bool on;
- double when;
- if (!remove_next(events_ptr, index, on, cookie, offset, when)) {
- return nullptr;
- }
- if (note_on) *note_on = on;
- Alg_event_ptr event = (*events_ptr)[index];
- if (on) {
- if (note_off_flag && event->is_note() &&
- (end_time == 0 ||
- (*events_ptr)[index]->get_end_time() + offset < end_time)) {
- // this was a note-on, so insert pending note-off
- insert(events_ptr, index, false, cookie, offset);
- }
- // for both note-ons and updates, insert next event (at index + 1)
- // DO NOT INCREMENT index: it must be preserved for request_note_off()
- if (index + 1 < events_ptr->length() &&
- (end_time == 0 || // zero means ignore end time
- // stop iterating when end time is reached
- (*events_ptr)[index + 1]->time + offset < end_time)) {
- insert(events_ptr, index + 1, true, cookie, offset);
- }
- }
- if (cookie_ptr) *cookie_ptr = cookie;
- if (offset_ptr) *offset_ptr = offset;
- return event;
- }
- void Alg_iterator::request_note_off()
- {
- assert(index >= 0 && index < events_ptr->length());
- insert(events_ptr, index, false, cookie, offset);
- }
- void Alg_iterator::end()
- {
- }
- void Alg_seq::merge_tracks()
- {
- long sum = 0;
- long i;
- for (i = 0; i < track_list.length(); i++) {
- sum = sum + track(i)->length();
- }
- // preallocate array for efficiency:
- Alg_event_ptr *notes = new Alg_event_ptr[sum];
- Alg_iterator iterator(this, false);
- iterator.begin();
- long notes_index = 0;
- Alg_event_ptr event;
- while ((event = iterator.next())) {
- notes[notes_index++] = event;
- }
- track_list.reset(); // don't need them any more
- add_track(0);
- track(0)->set_events(notes, sum, sum);
- iterator.end();
- }
- void Alg_seq::set_in_use(bool flag)
- {
- Alg_track::set_in_use(flag);
- track_list.set_in_use(flag);
- }
- // sr_letter_to_type = {"i": 'Integer', "r": 'Real', "s": 'String',
- // "l": 'Logical', "a": 'Symbol'}
|