libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

cmMidiFile.c 59KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmPrefix.h"
  4. #include "cmGlobal.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmCtx.h"
  8. #include "cmFile.h"
  9. #include "cmRptFile.h"
  10. #include "cmMem.h"
  11. #include "cmMallocDebug.h"
  12. #include "cmLinkedHeap.h"
  13. #include "cmTime.h"
  14. #include "cmText.h"
  15. #include "cmMidi.h"
  16. #include "cmMidiFile.h"
  17. #include "cmSvgWriter.h"
  18. #ifdef cmBIG_ENDIAN
  19. #define mfSwap16(v) (v)
  20. #define mfSwap32(v) (v)
  21. #else
  22. #define mfSwap16(v) cmSwap16(v)
  23. #define mfSwap32(v) cmSwap32(v)
  24. #endif
  25. typedef struct
  26. {
  27. unsigned cnt; // count of track records
  28. cmMidiTrackMsg_t* base; // pointer to first track recd
  29. cmMidiTrackMsg_t* last; // pointer to last track recd
  30. } _cmMidiTrack_t;
  31. typedef struct
  32. {
  33. cmErr_t err; // this objects error object
  34. cmLHeapH_t lhH; // linked heap used for all dynamically alloc'd data space
  35. cmFileH_t fh; // cmFile handle (only used in fmMidiFileOpen() and cmMidiFileWrite())
  36. unsigned short fmtId; // midi file type id: 0,1,2
  37. unsigned short ticksPerQN; // ticks per quarter note or 0 if smpteFmtId is valid
  38. cmMidiByte_t smpteFmtId; // smpte format or 0 if ticksPerQN is valid
  39. cmMidiByte_t smpteTicksPerFrame; // smpte ticks per frame or 0 if ticksPerQN is valid
  40. unsigned short trkN; // track count
  41. _cmMidiTrack_t* trkV; // track vector
  42. char* fn; // file name or NULL if this object did not originate from a file
  43. unsigned msgN; // count of msg's in msgV[]
  44. cmMidiTrackMsg_t** msgV; // sorted msg list
  45. bool msgVDirtyFl; // msgV[] needs to be refreshed from trkV[] because new msg's were inserted.
  46. unsigned nextUid; // next available msg uid
  47. } _cmMidiFile_t;
  48. cmMidiFileH_t cmMidiFileNullHandle = cmSTATIC_NULL_HANDLE;
  49. const cmMidiTrackMsg_t** _cmMidiFileMsgArray( _cmMidiFile_t* p );
  50. _cmMidiFile_t* _cmMidiFileHandleToPtr( cmMidiFileH_t h )
  51. {
  52. _cmMidiFile_t* p = (_cmMidiFile_t*)h.h;
  53. assert( p != NULL );
  54. return p;
  55. }
  56. cmMfRC_t _cmMidiFileRead8( _cmMidiFile_t* mfp, cmMidiByte_t* p )
  57. {
  58. if( cmFileReadUChar(mfp->fh,p,1) != kOkFileRC )
  59. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI byte read failed.");
  60. return kOkMfRC;
  61. }
  62. cmMfRC_t _cmMidiFileRead16( _cmMidiFile_t* mfp, unsigned short* p )
  63. {
  64. if( cmFileReadUShort(mfp->fh,p,1) != kOkFileRC )
  65. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI short read failed.");
  66. *p = mfSwap16(*p);
  67. return kOkMfRC;
  68. }
  69. cmMfRC_t _cmMidiFileRead24( _cmMidiFile_t* mfp, unsigned* p )
  70. {
  71. *p = 0;
  72. int i = 0;
  73. for(; i<3; ++i)
  74. {
  75. unsigned char c;
  76. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  77. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI 24 bit integer read failed.");
  78. *p = (*p << 8) + c;
  79. }
  80. //*p =mfSwap32(*p);
  81. return kOkMfRC;
  82. }
  83. cmMfRC_t _cmMidiFileRead32( _cmMidiFile_t* mfp, unsigned* p )
  84. {
  85. if( cmFileReadUInt(mfp->fh,p,1) != kOkFileRC )
  86. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI integer read failed.");
  87. *p = mfSwap32(*p);
  88. return kOkMfRC;
  89. }
  90. cmMfRC_t _cmMidiFileReadText( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  91. {
  92. if( byteN == 0 )
  93. return kOkMfRC;
  94. char* t = cmLhAllocZ(mfp->lhH,char,byteN+1);
  95. t[byteN] = 0;
  96. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  97. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read text failed.");
  98. tmp->u.text = t;
  99. tmp->byteCnt = byteN;
  100. return kOkMfRC;
  101. }
  102. cmMfRC_t _cmMidiFileReadRecd( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  103. {
  104. char* t = cmLhAllocZ(mfp->lhH,char,byteN);
  105. if( cmFileReadChar(mfp->fh,t,byteN) != kOkFileRC )
  106. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read record failed.");
  107. tmp->byteCnt = byteN;
  108. tmp->u.voidPtr = t;
  109. return kOkMfRC;
  110. }
  111. cmMfRC_t _cmMidiFileReadVarLen( _cmMidiFile_t* mfp, unsigned* p )
  112. {
  113. unsigned char c;
  114. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  115. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read variable length integer failed.");
  116. if( !(c & 0x80) )
  117. *p = c;
  118. else
  119. {
  120. *p = c & 0x7f;
  121. do
  122. {
  123. if( cmFileReadUChar(mfp->fh,&c,1) != kOkFileRC )
  124. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI read variable length integer failed.");
  125. *p = (*p << 7) + (c & 0x7f);
  126. }while( c & 0x80 );
  127. }
  128. return kOkMfRC;
  129. }
  130. cmMidiTrackMsg_t* _cmMidiFileAllocMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status )
  131. {
  132. cmMidiTrackMsg_t* tmp = cmLhAllocZ(mfp->lhH,cmMidiTrackMsg_t, 1 );
  133. // set the generic track record fields
  134. tmp->dtick = dtick;
  135. tmp->status = status;
  136. tmp->metaId = kInvalidMetaMdId;
  137. tmp->trkIdx = trkIdx;
  138. tmp->byteCnt = 0;
  139. tmp->uid = mfp->nextUid++;
  140. return tmp;
  141. }
  142. cmMfRC_t _cmMidiFileAppendTrackMsg( _cmMidiFile_t* mfp, unsigned short trkIdx, unsigned dtick, cmMidiByte_t status, cmMidiTrackMsg_t** trkMsgPtrPtr )
  143. {
  144. cmMidiTrackMsg_t* tmp = _cmMidiFileAllocMsg( mfp, trkIdx, dtick, status );
  145. // link new record onto track record chain
  146. if( mfp->trkV[trkIdx].base == NULL )
  147. mfp->trkV[trkIdx].base = tmp;
  148. else
  149. mfp->trkV[trkIdx].last->link = tmp;
  150. mfp->trkV[trkIdx].last = tmp;
  151. mfp->trkV[trkIdx].cnt++;
  152. *trkMsgPtrPtr = tmp;
  153. return kOkMfRC;
  154. }
  155. cmMfRC_t _cmMidiFileReadSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp, unsigned byteN )
  156. {
  157. cmMfRC_t rc = kOkMfRC;
  158. cmMidiByte_t b = 0;
  159. if( byteN == cmInvalidCnt )
  160. {
  161. long offs;
  162. if( cmFileTell(mfp->fh,&offs) != kOkFileRC )
  163. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI File 'tell' failed.");
  164. byteN = 0;
  165. // get the length of the sys-ex msg
  166. while( !cmFileEof(mfp->fh) && (b != kSysComEoxMdId) )
  167. {
  168. if((rc = _cmMidiFileRead8(mfp,&b)) != kOkMfRC )
  169. return rc;
  170. ++byteN;
  171. }
  172. // verify that the EOX byte was found
  173. if( b != kSysComEoxMdId )
  174. return cmErrMsg(&mfp->err,kMissingEoxMfRC,"MIDI file missing 'end-of-sys-ex'.");
  175. // rewind to the beginning of the msg
  176. if( cmFileSeek(mfp->fh,kBeginFileFl,offs) != kOkFileRC )
  177. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file seek failed on sys-ex read.");
  178. }
  179. // allocate memory to hold the sys-ex msg
  180. cmMidiByte_t* mp = cmLhAllocZ(mfp->lhH,cmMidiByte_t, byteN );
  181. // read the sys-ex msg from the file into msg memory
  182. if( cmFileReadUChar(mfp->fh,mp,byteN) != kOkFileRC )
  183. return cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI sys-ex read failed.");
  184. tmp->byteCnt = byteN;
  185. tmp->u.sysExPtr = mp;
  186. return rc;
  187. }
  188. cmMfRC_t _cmMidiFileReadChannelMsg( _cmMidiFile_t* mfp, cmMidiByte_t* rsPtr, cmMidiByte_t status, cmMidiTrackMsg_t* tmp )
  189. {
  190. cmMfRC_t rc = kOkMfRC;
  191. cmMidiChMsg_t* p = cmLhAllocZ(mfp->lhH,cmMidiChMsg_t,1);
  192. unsigned useRsFl = status <= 0x7f;
  193. cmMidiByte_t statusCh = useRsFl ? *rsPtr : status;
  194. if( useRsFl )
  195. p->d0 = status;
  196. else
  197. *rsPtr = status;
  198. tmp->byteCnt = sizeof(cmMidiChMsg_t);
  199. tmp->status = statusCh & 0xf0;
  200. p->ch = statusCh & 0x0f;
  201. p->durMicros = 0;
  202. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  203. if( byteN==kInvalidMidiByte || byteN > 2 )
  204. return cmErrMsg(&mfp->err,kInvalidStatusMfRC,"Invalid status:0x%x %i byte cnt:%i.",tmp->status,tmp->status,byteN);
  205. unsigned i;
  206. for(i=useRsFl; i<byteN; ++i)
  207. {
  208. cmMidiByte_t* b = i==0 ? &p->d0 : &p->d1;
  209. if((rc = _cmMidiFileRead8(mfp,b)) != kOkMfRC )
  210. return rc;
  211. }
  212. // convert note-on velocity=0 to note off
  213. if( tmp->status == kNoteOnMdId && p->d1==0 )
  214. tmp->status = kNoteOffMdId;
  215. tmp->u.chMsgPtr = p;
  216. return rc;
  217. }
  218. cmMfRC_t _cmMidiFileReadMetaMsg( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  219. {
  220. cmMidiByte_t metaId;
  221. cmMfRC_t rc;
  222. unsigned byteN = 0;
  223. if((rc = _cmMidiFileRead8(mfp,&metaId)) != kOkMfRC )
  224. return rc;
  225. if((rc = _cmMidiFileReadVarLen(mfp,&byteN)) != kOkMfRC )
  226. return rc;
  227. //printf("mt: %i 0x%x n:%i\n",metaId,metaId,byteN);
  228. switch( metaId )
  229. {
  230. case kSeqNumbMdId: rc = _cmMidiFileRead16(mfp,&tmp->u.sVal); break;
  231. case kTextMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  232. case kCopyMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  233. case kTrkNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  234. case kInstrNameMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  235. case kLyricsMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  236. case kMarkerMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  237. case kCuePointMdId: rc = _cmMidiFileReadText(mfp,tmp,byteN); break;
  238. case kMidiChMdId: rc = _cmMidiFileRead8(mfp,&tmp->u.bVal); break;
  239. case kEndOfTrkMdId: break;
  240. case kTempoMdId: rc = _cmMidiFileRead24(mfp,&tmp->u.iVal); break;
  241. case kSmpteMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiSmpte_t)); break;
  242. case kTimeSigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiTimeSig_t)); break;
  243. case kKeySigMdId: rc = _cmMidiFileReadRecd(mfp,tmp,sizeof(cmMidiKeySig_t)); break;
  244. case kSeqSpecMdId: rc = _cmMidiFileReadSysEx(mfp,tmp,byteN); break;
  245. default:
  246. cmFileSeek(mfp->fh,kCurFileFl,byteN);
  247. rc = cmErrMsg(&mfp->err,kUnknownMetaIdMfRC,"Unknown meta status:0x%x %i.",metaId,metaId);
  248. }
  249. tmp->metaId = metaId;
  250. return rc;
  251. }
  252. cmMfRC_t _cmMidiFileReadTrack( _cmMidiFile_t* mfp, unsigned short trkIdx )
  253. {
  254. cmMfRC_t rc = kOkMfRC;
  255. unsigned dticks = 0;
  256. cmMidiByte_t status;
  257. cmMidiByte_t runstatus = 0;
  258. bool contFl = true;
  259. while( contFl && (rc==kOkMfRC))
  260. {
  261. cmMidiTrackMsg_t* tmp = NULL;
  262. // read the tick count
  263. if((rc = _cmMidiFileReadVarLen(mfp,&dticks)) != kOkMfRC )
  264. return rc;
  265. // read the status byte
  266. if((rc = _cmMidiFileRead8(mfp,&status)) != kOkMfRC )
  267. return rc;
  268. //printf("%5i st:%i 0x%x\n",dticks,status,status);
  269. // append a track msg
  270. if((rc = _cmMidiFileAppendTrackMsg( mfp, trkIdx, dticks, status, &tmp )) != kOkMfRC )
  271. return rc;
  272. // switch on status
  273. switch( status )
  274. {
  275. // handle sys-ex msg
  276. case kSysExMdId:
  277. rc = _cmMidiFileReadSysEx(mfp,tmp,cmInvalidCnt);
  278. break;
  279. // handle meta msg
  280. case kMetaStId:
  281. rc = _cmMidiFileReadMetaMsg(mfp,tmp);
  282. // ignore unknown meta messages
  283. if( rc == kUnknownMetaIdMfRC )
  284. rc = kOkMfRC;
  285. contFl = tmp->metaId != kEndOfTrkMdId;
  286. break;
  287. default:
  288. // handle channel msg
  289. rc = _cmMidiFileReadChannelMsg(mfp,&runstatus,status,tmp);
  290. }
  291. }
  292. return rc;
  293. }
  294. cmMfRC_t _cmMidiFileReadHdr( _cmMidiFile_t* mfp )
  295. {
  296. cmMfRC_t rc;
  297. unsigned fileId;
  298. unsigned chunkByteN;
  299. // read the file id
  300. if((rc = _cmMidiFileRead32(mfp,&fileId)) != kOkMfRC )
  301. return rc;
  302. // verify the file id
  303. if( fileId != 'MThd' )
  304. return cmErrMsg(&mfp->err,kNotAMidiFileMfRC,"");
  305. // read the file chunk byte count
  306. if((rc = _cmMidiFileRead32(mfp,&chunkByteN)) != kOkMfRC )
  307. return rc;
  308. // read the format id
  309. if((rc = _cmMidiFileRead16(mfp,&mfp->fmtId)) != kOkMfRC )
  310. return rc;
  311. // read the track count
  312. if((rc = _cmMidiFileRead16(mfp,&mfp->trkN)) != kOkMfRC )
  313. return rc;
  314. // read the ticks per quarter note
  315. if((rc = _cmMidiFileRead16(mfp,&mfp->ticksPerQN)) != kOkMfRC )
  316. return rc;
  317. // if the division field was given in smpte
  318. if( mfp->ticksPerQN & 0x8000 )
  319. {
  320. mfp->smpteFmtId = (mfp->ticksPerQN & 0x7f00) >> 8;
  321. mfp->smpteTicksPerFrame = (mfp->ticksPerQN & 0xFF);
  322. mfp->ticksPerQN = 0;
  323. }
  324. // allocate and zero the track array
  325. if( mfp->trkN )
  326. mfp->trkV = cmLhAllocZ(mfp->lhH, _cmMidiTrack_t, mfp->trkN);
  327. return rc;
  328. }
  329. void _cmMidiFileDrop( _cmMidiFile_t* p )
  330. {
  331. unsigned i;
  332. unsigned n = 0;
  333. for(i=0; i<p->trkN; ++i)
  334. {
  335. _cmMidiTrack_t* trk = p->trkV + i;
  336. cmMidiTrackMsg_t* m0 = NULL;
  337. cmMidiTrackMsg_t* m = trk->base;
  338. for(; m!=NULL; m=m->link)
  339. {
  340. if( cmIsFlag(m->flags,kDropTrkMsgFl) )
  341. {
  342. ++n;
  343. if( m0 == NULL )
  344. trk->base = m->link;
  345. else
  346. m0->link = m->link;
  347. }
  348. else
  349. {
  350. m0 = m;
  351. }
  352. }
  353. }
  354. }
  355. int _cmMidiFileSortFunc( const void *p0, const void* p1 )
  356. {
  357. if( (*(cmMidiTrackMsg_t**)p0)->atick == (*(cmMidiTrackMsg_t**)p1)->atick )
  358. return 0;
  359. return (*(cmMidiTrackMsg_t**)p0)->atick < (*(cmMidiTrackMsg_t**)p1)->atick ? -1 : 1;
  360. }
  361. // Set the absolute accumulated ticks (atick) value of each track message.
  362. // The absolute accumulated ticks gives a global time ordering for all
  363. // messages in the file.
  364. void _cmMidiFileSetAccumulateTicks( _cmMidiFile_t* p )
  365. {
  366. cmMidiTrackMsg_t* nextTrkMsg[ p->trkN ]; // next msg in each track
  367. unsigned long long atick = 0;
  368. unsigned i;
  369. bool fl = true;
  370. // iniitalize nextTrkTick[] and nextTrkMsg[] to the first msg in each track
  371. for(i=0; i<p->trkN; ++i)
  372. if((nextTrkMsg[i] = p->trkV[i].base) != NULL )
  373. nextTrkMsg[i]->atick = nextTrkMsg[i]->dtick;
  374. while(1)
  375. {
  376. unsigned k = cmInvalidIdx;
  377. // find the trk which has the next msg (min atick time)
  378. for(i=0; i<p->trkN; ++i)
  379. if( nextTrkMsg[i]!=NULL && (k==cmInvalidIdx || nextTrkMsg[i]->atick < nextTrkMsg[k]->atick) )
  380. k = i;
  381. // no next msg was found - we're done
  382. if( k == cmInvalidIdx )
  383. break;
  384. if( fl && nextTrkMsg[k]->dtick > 0 )
  385. {
  386. fl = false;
  387. nextTrkMsg[k]->dtick = 1;
  388. nextTrkMsg[k]->atick = 1;
  389. }
  390. // store the current atick
  391. atick = nextTrkMsg[k]->atick;
  392. // advance the selected track to it's next message
  393. nextTrkMsg[k] = nextTrkMsg[k]->link;
  394. // set the selected tracks next atick time
  395. if( nextTrkMsg[k] != NULL )
  396. nextTrkMsg[k]->atick = atick + nextTrkMsg[k]->dtick;
  397. }
  398. }
  399. void _cmMidiFileSetAbsoluteTime( _cmMidiFile_t* mfp )
  400. {
  401. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
  402. double microsPerQN = 60000000/120; // default tempo;
  403. double microsPerTick = microsPerQN / mfp->ticksPerQN;
  404. unsigned long long amicro = 0;
  405. unsigned i;
  406. for(i=0; i<mfp->msgN; ++i)
  407. {
  408. cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)msgV[i]; // cast away const
  409. unsigned dtick = 0;
  410. if( i > 0 )
  411. {
  412. // atick must have already been set and sorted
  413. assert( mp->atick >= msgV[i-1]->atick );
  414. dtick = mp->atick - msgV[i-1]->atick;
  415. }
  416. amicro += microsPerTick * dtick;
  417. mp->amicro = amicro;
  418. // track tempo changes
  419. if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
  420. microsPerTick = mp->u.iVal / mfp->ticksPerQN;
  421. }
  422. }
  423. cmMfRC_t _cmMidiFileClose( _cmMidiFile_t* mfp )
  424. {
  425. cmMfRC_t rc = kOkMfRC;
  426. if( mfp == NULL )
  427. return rc;
  428. cmMemPtrFree(&mfp->msgV);
  429. if( cmFileIsValid( mfp->fh ) )
  430. if( cmFileClose( &mfp->fh ) != kOkFileRC )
  431. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file close failed.");
  432. if( cmLHeapIsValid( mfp->lhH ) )
  433. cmLHeapDestroy(&mfp->lhH);
  434. cmMemPtrFree(&mfp);
  435. return rc;
  436. }
  437. void _cmMidiFileLinearize( _cmMidiFile_t* mfp )
  438. {
  439. unsigned trkIdx,i,j;
  440. if( mfp->msgVDirtyFl == false )
  441. return;
  442. mfp->msgVDirtyFl = false;
  443. // get the total trk msg count
  444. mfp->msgN = 0;
  445. for(trkIdx=0; trkIdx<mfp->trkN; ++trkIdx)
  446. mfp->msgN += mfp->trkV[ trkIdx ].cnt;
  447. // allocate the trk msg index vector: msgV[]
  448. mfp->msgV = cmMemResizeZ(cmMidiTrackMsg_t*, mfp->msgV, mfp->msgN);
  449. // store a pointer to every trk msg in msgV[]
  450. for(i=0,j=0; i<mfp->trkN; ++i)
  451. {
  452. cmMidiTrackMsg_t* m = mfp->trkV[i].base;
  453. for(; m!=NULL; m=m->link)
  454. {
  455. assert( j < mfp->msgN );
  456. mfp->msgV[j++] = m;
  457. }
  458. }
  459. // set the atick value in each msg
  460. _cmMidiFileSetAccumulateTicks(mfp);
  461. // sort msgV[] in ascending order on atick
  462. qsort( mfp->msgV, mfp->msgN, sizeof(cmMidiTrackMsg_t*), _cmMidiFileSortFunc );
  463. // set the amicro value in each msg
  464. _cmMidiFileSetAbsoluteTime(mfp);
  465. }
  466. // Note that p->msgV[] should always be accessed through this function
  467. // to guarantee that the p->msgVDirtyFl is checked and msgV[] is updated
  468. // in case msgV[] is out of sync (due to inserted msgs (see cmMidiFileInsertTrackMsg())
  469. // with trkV[].
  470. const cmMidiTrackMsg_t** _cmMidiFileMsgArray( _cmMidiFile_t* p )
  471. {
  472. _cmMidiFileLinearize(p);
  473. // this cast is needed to eliminate an apparently needless 'incompatible type' warning
  474. return (const cmMidiTrackMsg_t**)p->msgV;
  475. }
  476. cmMfRC_t _cmMidiFileCreate( cmCtx_t* ctx, cmMidiFileH_t* hp )
  477. {
  478. cmMfRC_t rc = kOkMfRC;
  479. _cmMidiFile_t* p = NULL;
  480. if((rc = cmMidiFileClose(hp)) != kOkMfRC )
  481. return rc;
  482. // allocate the midi file object
  483. if(( p = cmMemAllocZ( _cmMidiFile_t, 1)) == NULL )
  484. return rc = cmErrMsg(&ctx->err,kMemAllocFailMfRC,"MIDI file memory allocation failed.");
  485. cmErrSetup(&p->err,&ctx->rpt,"MIDI File");
  486. // allocate the linked heap
  487. if( cmLHeapIsValid( p->lhH = cmLHeapCreate( 1024, ctx )) == false )
  488. rc = cmErrMsg(&p->err,kMemAllocFailMfRC,"MIDI heap allocation failed.");
  489. if( rc != kOkMfRC )
  490. _cmMidiFileClose(p);
  491. else
  492. hp->h = p;
  493. return rc;
  494. }
  495. cmMfRC_t cmMidiFileOpen( cmCtx_t* ctx, cmMidiFileH_t* hp, const char* fn )
  496. {
  497. cmMfRC_t rc = kOkMfRC;
  498. unsigned short trkIdx = 0;
  499. if((rc = _cmMidiFileCreate(ctx,hp)) != kOkMfRC )
  500. return rc;
  501. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(*hp);
  502. // open the file
  503. if(cmFileOpen(&p->fh,fn,kReadFileFl | kBinaryFileFl,p->err.rpt) != kOkFileRC )
  504. {
  505. rc = cmErrMsg(&p->err,kFileFailMfRC,"MIDI file open failed.");
  506. goto errLabel;
  507. }
  508. // read header and setup track array
  509. if(( rc = _cmMidiFileReadHdr(p)) != kOkMfRC )
  510. goto errLabel;
  511. while( !cmFileEof(p->fh) && trkIdx < p->trkN )
  512. {
  513. unsigned chkId = 0,chkN=0;
  514. // read the chunk id
  515. if((rc = _cmMidiFileRead32(p,&chkId)) != kOkMfRC )
  516. goto errLabel;
  517. // read the chunk size
  518. if((rc = _cmMidiFileRead32(p,&chkN)) != kOkMfRC )
  519. goto errLabel;
  520. // if this is not a trk chunk then skip it
  521. if( chkId != (unsigned)'MTrk')
  522. {
  523. //if( fseek( p->fp, chkN, SEEK_CUR) != 0 )
  524. if( cmFileSeek(p->fh,kCurFileFl,chkN) != kOkFileRC )
  525. {
  526. rc = cmErrMsg(&p->err,kFileFailMfRC,"MIDI file seek failed.");
  527. goto errLabel;
  528. }
  529. }
  530. else
  531. {
  532. if((rc = _cmMidiFileReadTrack(p,trkIdx)) != kOkMfRC )
  533. goto errLabel;
  534. ++trkIdx;
  535. }
  536. }
  537. // store the file name
  538. p->fn = cmLhAllocZ(p->lhH,char,strlen(fn)+1);
  539. assert( p->fn != NULL );
  540. strcpy(p->fn,fn);
  541. p->msgVDirtyFl = true;
  542. _cmMidiFileLinearize(p);
  543. errLabel:
  544. if( cmFileClose(&p->fh) != kOkFileRC )
  545. rc = cmErrMsg(&p->err,kFileFailMfRC,"MIDI file close failed.");
  546. if( rc != kOkMfRC )
  547. {
  548. _cmMidiFileClose(p);
  549. hp->h = NULL;
  550. }
  551. return rc;
  552. }
  553. cmMfRC_t cmMidiFileCreate( cmCtx_t* ctx, cmMidiFileH_t* hp, unsigned trkN, unsigned ticksPerQN )
  554. {
  555. cmMfRC_t rc = kOkMfRC;
  556. if((rc = _cmMidiFileCreate(ctx,hp)) != kOkMfRC )
  557. return rc;
  558. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(*hp);
  559. p->ticksPerQN = ticksPerQN;
  560. p->fmtId = 1;
  561. p->trkN = trkN;
  562. p->trkV = cmLhAllocZ(p->lhH, _cmMidiTrack_t, p->trkN);
  563. return rc;
  564. }
  565. cmMfRC_t cmMidiFileClose( cmMidiFileH_t* hp )
  566. {
  567. cmMfRC_t rc = kOkMfRC;
  568. if( hp==NULL || cmMidiFileIsValid(*hp)==false )
  569. return kOkMfRC;
  570. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(*hp);
  571. if((rc = _cmMidiFileClose(p)) != kOkMfRC )
  572. return rc;
  573. hp->h = NULL;
  574. return rc;
  575. }
  576. cmMfRC_t _cmMidiFileWrite8( _cmMidiFile_t* mfp, unsigned char v )
  577. {
  578. cmMfRC_t rc = kOkMfRC;
  579. if( cmFileWriteUChar(mfp->fh,&v,1) != kOkFileRC )
  580. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file byte write failed.");
  581. return rc;
  582. }
  583. cmMfRC_t _cmMidiFileWrite16( _cmMidiFile_t* mfp, unsigned short v )
  584. {
  585. cmMfRC_t rc = kOkMfRC;
  586. v = mfSwap16(v);
  587. if( cmFileWriteUShort(mfp->fh,&v,1) != kOkFileRC )
  588. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file short integer write failed.");
  589. return rc;
  590. }
  591. cmMfRC_t _cmMidiFileWrite24( _cmMidiFile_t* mfp, unsigned v )
  592. {
  593. cmMfRC_t rc = kOkMfRC;
  594. unsigned mask = 0xff0000;
  595. int i;
  596. for(i = 2; i>=0; --i)
  597. {
  598. unsigned char c = (v & mask) >> (i*8);
  599. mask >>= 8;
  600. if( cmFileWriteUChar(mfp->fh,&c,1) != kOkFileRC )
  601. {
  602. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file 24 bit integer write failed.");
  603. goto errLabel;
  604. }
  605. }
  606. errLabel:
  607. return rc;
  608. }
  609. cmMfRC_t _cmMidiFileWrite32( _cmMidiFile_t* mfp, unsigned v )
  610. {
  611. cmMfRC_t rc = kOkMfRC;
  612. v = mfSwap32(v);
  613. if( cmFileWriteUInt(mfp->fh,&v,1) != kOkFileRC )
  614. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file integer write failed.");
  615. return rc;
  616. }
  617. cmMfRC_t _cmMidiFileWriteRecd( _cmMidiFile_t* mfp, const void* v, unsigned byteCnt )
  618. {
  619. cmMfRC_t rc = kOkMfRC;
  620. if( cmFileWriteChar(mfp->fh,v,byteCnt) != kOkFileRC )
  621. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file write record failed.");
  622. return rc;
  623. }
  624. cmMfRC_t _cmMidiFileWriteVarLen( _cmMidiFile_t* mfp, unsigned v )
  625. {
  626. cmMfRC_t rc = kOkMfRC;
  627. unsigned buf = v & 0x7f;
  628. while((v >>= 7) > 0 )
  629. {
  630. buf <<= 8;
  631. buf |= 0x80;
  632. buf += (v & 0x7f);
  633. }
  634. while(1)
  635. {
  636. unsigned char c = (unsigned char)(buf & 0xff);
  637. if( cmFileWriteUChar(mfp->fh,&c,1) != kOkFileRC )
  638. {
  639. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"MIDI file variable length integer write failed.");
  640. goto errLabel;
  641. }
  642. if( buf & 0x80 )
  643. buf >>= 8;
  644. else
  645. break;
  646. }
  647. errLabel:
  648. return rc;
  649. }
  650. cmMfRC_t _cmMidiFileWriteHdr( _cmMidiFile_t* mfp )
  651. {
  652. cmMfRC_t rc;
  653. unsigned fileId = 'MThd';
  654. unsigned chunkByteN = 6;
  655. // write the file id ('MThd')
  656. if((rc = _cmMidiFileWrite32(mfp,fileId)) != kOkMfRC )
  657. return rc;
  658. // write the file chunk byte count (always 6)
  659. if((rc = _cmMidiFileWrite32(mfp,chunkByteN)) != kOkMfRC )
  660. return rc;
  661. // write the MIDI file format id (0,1,2)
  662. if((rc = _cmMidiFileWrite16(mfp,mfp->fmtId)) != kOkMfRC )
  663. return rc;
  664. // write the track count
  665. if((rc = _cmMidiFileWrite16(mfp,mfp->trkN)) != kOkMfRC )
  666. return rc;
  667. unsigned short v = 0;
  668. // if the ticks per quarter note field is valid ...
  669. if( mfp->ticksPerQN )
  670. v = mfp->ticksPerQN;
  671. else
  672. {
  673. // ... otherwise the division field was given in smpte
  674. v = mfp->smpteFmtId << 8;
  675. v += mfp->smpteTicksPerFrame;
  676. }
  677. if((rc = _cmMidiFileWrite16(mfp,v)) != kOkMfRC )
  678. return rc;
  679. return rc;
  680. }
  681. cmMfRC_t _cmMidiFileWriteSysEx( _cmMidiFile_t* mfp, cmMidiTrackMsg_t* tmp )
  682. {
  683. cmMfRC_t rc = kOkMfRC;
  684. if((rc = _cmMidiFileWrite8(mfp,kSysExMdId)) != kOkMfRC )
  685. goto errLabel;
  686. if( cmFileWriteUChar(mfp->fh,tmp->u.sysExPtr,tmp->byteCnt) != kOkFileRC )
  687. rc = cmErrMsg(&mfp->err,kFileFailMfRC,"Sys-ex msg write failed.");
  688. errLabel:
  689. return rc;
  690. }
  691. cmMfRC_t _cmMidiFileWriteChannelMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tmp, cmMidiByte_t* runStatus )
  692. {
  693. cmMfRC_t rc = kOkMfRC;
  694. unsigned byteN = cmMidiStatusToByteCount(tmp->status);
  695. cmMidiByte_t status = tmp->status + tmp->u.chMsgPtr->ch;
  696. if( status != *runStatus )
  697. {
  698. *runStatus = status;
  699. if((rc = _cmMidiFileWrite8(mfp,status)) != kOkMfRC )
  700. goto errLabel;
  701. }
  702. if(byteN>=1)
  703. if((rc = _cmMidiFileWrite8(mfp,tmp->u.chMsgPtr->d0)) != kOkMfRC )
  704. goto errLabel;
  705. if(byteN>=2)
  706. if((rc = _cmMidiFileWrite8(mfp,tmp->u.chMsgPtr->d1)) != kOkMfRC )
  707. goto errLabel;
  708. errLabel:
  709. return rc;
  710. }
  711. cmMfRC_t _cmMidiFileWriteMetaMsg( _cmMidiFile_t* mfp, const cmMidiTrackMsg_t* tmp )
  712. {
  713. cmMfRC_t rc;
  714. if((rc = _cmMidiFileWrite8(mfp,kMetaStId)) != kOkMfRC )
  715. return rc;
  716. if((rc = _cmMidiFileWrite8(mfp,tmp->metaId)) != kOkMfRC )
  717. return rc;
  718. switch( tmp->metaId )
  719. {
  720. case kSeqNumbMdId:
  721. if((rc = _cmMidiFileWrite8(mfp,sizeof(tmp->u.sVal))) == kOkMfRC )
  722. rc = _cmMidiFileWrite16(mfp,tmp->u.sVal);
  723. break;
  724. case kTempoMdId:
  725. if((rc = _cmMidiFileWrite8(mfp,3)) == kOkMfRC )
  726. rc = _cmMidiFileWrite24(mfp,tmp->u.iVal);
  727. break;
  728. case kSmpteMdId:
  729. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiSmpte_t))) == kOkMfRC )
  730. rc = _cmMidiFileWriteRecd(mfp,tmp->u.smptePtr,sizeof(cmMidiSmpte_t));
  731. break;
  732. case kTimeSigMdId:
  733. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiTimeSig_t))) == kOkMfRC )
  734. rc = _cmMidiFileWriteRecd(mfp,tmp->u.timeSigPtr,sizeof(cmMidiTimeSig_t));
  735. break;
  736. case kKeySigMdId:
  737. if((rc = _cmMidiFileWrite8(mfp,sizeof(cmMidiKeySig_t))) == kOkMfRC )
  738. rc = _cmMidiFileWriteRecd(mfp,tmp->u.keySigPtr,sizeof(cmMidiKeySig_t));
  739. break;
  740. case kSeqSpecMdId:
  741. if((rc = _cmMidiFileWriteVarLen(mfp,sizeof(tmp->byteCnt))) == kOkMfRC )
  742. rc = _cmMidiFileWriteRecd(mfp,tmp->u.sysExPtr,tmp->byteCnt);
  743. break;
  744. case kMidiChMdId:
  745. if((rc = _cmMidiFileWrite8(mfp,sizeof(tmp->u.bVal))) == kOkMfRC )
  746. rc = _cmMidiFileWrite8(mfp,tmp->u.bVal);
  747. break;
  748. case kEndOfTrkMdId:
  749. rc = _cmMidiFileWrite8(mfp,0);
  750. break;
  751. case kTextMdId:
  752. case kCopyMdId:
  753. case kTrkNameMdId:
  754. case kInstrNameMdId:
  755. case kLyricsMdId:
  756. case kMarkerMdId:
  757. case kCuePointMdId:
  758. {
  759. unsigned n = tmp->u.text==NULL ? 0 : strlen(tmp->u.text);
  760. if((rc = _cmMidiFileWriteVarLen(mfp,n)) == kOkMfRC && n>0 )
  761. rc = _cmMidiFileWriteRecd(mfp,tmp->u.text,n);
  762. }
  763. break;
  764. default:
  765. {
  766. // ignore unknown meta messages
  767. }
  768. }
  769. return rc;
  770. }
  771. cmMfRC_t _cmMidiFileInsertEotMsg( _cmMidiFile_t* p, unsigned trkIdx )
  772. {
  773. _cmMidiTrack_t* trk = p->trkV + trkIdx;
  774. cmMidiTrackMsg_t* m0 = NULL;
  775. cmMidiTrackMsg_t* m = trk->base;
  776. // locate the current EOT msg on this track
  777. for(; m!=NULL; m=m->link)
  778. {
  779. if( m->status == kMetaStId && m->metaId == kEndOfTrkMdId )
  780. {
  781. // If this EOT msg is the last msg in the track ...
  782. if( m->link == NULL )
  783. {
  784. assert( m == trk->last );
  785. return kOkMfRC; // ... then there is nothing else to do
  786. }
  787. // If this EOT msg is not the last in the track ...
  788. if( m0 != NULL )
  789. m0->link = m->link; // ... then unlink it
  790. break;
  791. }
  792. m0 = m;
  793. }
  794. // if we get here then the last msg in the track was not an EOT msg
  795. // if there was no previously allocated EOT msg
  796. if( m == NULL )
  797. {
  798. m = _cmMidiFileAllocMsg(p, trkIdx, 1, kMetaStId );
  799. m->metaId = kEndOfTrkMdId;
  800. trk->cnt += 1;
  801. }
  802. // link an EOT msg as the last msg on the track
  803. // if the track is currently empty
  804. if( m0 == NULL )
  805. {
  806. trk->base = m;
  807. trk->last = m;
  808. }
  809. else // link the msg as the last on on the track
  810. {
  811. assert( m0 == trk->last);
  812. m0->link = m;
  813. m->link = NULL;
  814. trk->last = m;
  815. }
  816. return kOkMfRC;
  817. }
  818. cmMfRC_t _cmMidiFileWriteTrack( _cmMidiFile_t* mfp, unsigned trkIdx )
  819. {
  820. cmMfRC_t rc = kOkMfRC;
  821. cmMidiTrackMsg_t* tmp = mfp->trkV[trkIdx].base;
  822. cmMidiByte_t runStatus = 0;
  823. // be sure there is a EOT msg at the end of this track
  824. if((rc = _cmMidiFileInsertEotMsg(mfp, trkIdx )) != kOkMfRC )
  825. return rc;
  826. for(; tmp != NULL; tmp=tmp->link)
  827. {
  828. // write the msg tick count
  829. if((rc = _cmMidiFileWriteVarLen(mfp,tmp->dtick)) != kOkMfRC )
  830. return rc;
  831. // switch on status
  832. switch( tmp->status )
  833. {
  834. // handle sys-ex msg
  835. case kSysExMdId:
  836. rc = _cmMidiFileWriteSysEx(mfp,tmp);
  837. break;
  838. // handle meta msg
  839. case kMetaStId:
  840. rc = _cmMidiFileWriteMetaMsg(mfp,tmp);
  841. break;
  842. default:
  843. // handle channel msg
  844. rc = _cmMidiFileWriteChannelMsg(mfp,tmp,&runStatus);
  845. }
  846. }
  847. return rc;
  848. }
  849. cmMfRC_t cmMidiFileWrite( cmMidiFileH_t h, const char* fn )
  850. {
  851. cmMfRC_t rc = kOkMfRC;
  852. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  853. unsigned i;
  854. // create the output file
  855. if( cmFileOpen(&mfp->fh,fn,kWriteFileFl,mfp->err.rpt) != kOkFileRC )
  856. return cmErrMsg(&mfp->err,kFileFailMfRC,"The MIDI file '%s' could not be created.",cmStringNullGuard(fn));
  857. // write the file header
  858. if((rc = _cmMidiFileWriteHdr(mfp)) != kOkMfRC )
  859. {
  860. rc = cmErrMsg(&mfp->err,rc,"The file header write failed on the MIDI file '%s'.",cmStringNullGuard(fn));
  861. goto errLabel;
  862. }
  863. for(i=0; i < mfp->trkN; ++i )
  864. {
  865. unsigned chkId = 'MTrk';
  866. long offs0,offs1;
  867. // write the track chunk id ('MTrk')
  868. if((rc = _cmMidiFileWrite32(mfp,chkId)) != kOkMfRC )
  869. goto errLabel;
  870. cmFileTell(mfp->fh,&offs0);
  871. // write the track chunk size as zero
  872. if((rc = _cmMidiFileWrite32(mfp,0)) != kOkMfRC )
  873. goto errLabel;
  874. if((rc = _cmMidiFileWriteTrack(mfp,i)) != kOkMfRC )
  875. goto errLabel;
  876. cmFileTell(mfp->fh,&offs1);
  877. cmFileSeek(mfp->fh,kBeginFileFl,offs0);
  878. _cmMidiFileWrite32(mfp,offs1-offs0-4);
  879. cmFileSeek(mfp->fh,kBeginFileFl,offs1);
  880. }
  881. errLabel:
  882. cmFileClose(&mfp->fh);
  883. return rc;
  884. }
  885. bool cmMidiFileIsValid( cmMidiFileH_t h )
  886. { return h.h != NULL; }
  887. unsigned cmMidiFileTrackCount( cmMidiFileH_t h )
  888. {
  889. _cmMidiFile_t* mfp;
  890. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  891. return cmInvalidCnt;
  892. return mfp->trkN;
  893. }
  894. unsigned cmMidiFileType( cmMidiFileH_t h )
  895. {
  896. _cmMidiFile_t* mfp;
  897. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  898. return cmInvalidId;
  899. return mfp->fmtId;
  900. }
  901. const char* cmMidiFileName( cmMidiFileH_t h )
  902. {
  903. _cmMidiFile_t* mfp;
  904. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  905. return NULL;
  906. return mfp->fn;
  907. }
  908. unsigned cmMidiFileTicksPerQN( cmMidiFileH_t h )
  909. {
  910. _cmMidiFile_t* mfp;
  911. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  912. return cmInvalidCnt;
  913. return mfp->ticksPerQN;
  914. }
  915. cmMidiByte_t cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h )
  916. {
  917. _cmMidiFile_t* mfp;
  918. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  919. return kInvalidMidiByte;
  920. if( mfp->ticksPerQN != 0 )
  921. return 0;
  922. return mfp->smpteTicksPerFrame;
  923. }
  924. cmMidiByte_t cmMidiFileSmpteFormatId( cmMidiFileH_t h )
  925. {
  926. _cmMidiFile_t* mfp;
  927. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  928. return kInvalidMidiByte;
  929. if( mfp->ticksPerQN != 0 )
  930. return 0;
  931. return mfp->smpteFmtId;
  932. }
  933. unsigned cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx )
  934. {
  935. _cmMidiFile_t* mfp;
  936. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  937. return cmInvalidCnt;
  938. return mfp->trkV[trackIdx].cnt;
  939. }
  940. const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx )
  941. {
  942. _cmMidiFile_t* mfp;
  943. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  944. return NULL;
  945. return mfp->trkV[trackIdx].base;
  946. }
  947. unsigned cmMidiFileMsgCount( cmMidiFileH_t h )
  948. {
  949. _cmMidiFile_t* mfp;
  950. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  951. return cmInvalidCnt;
  952. return mfp->msgN;
  953. }
  954. const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h )
  955. {
  956. _cmMidiFile_t* mfp;
  957. if((mfp = _cmMidiFileHandleToPtr(h)) == NULL )
  958. return NULL;
  959. return _cmMidiFileMsgArray(mfp);
  960. }
  961. cmMidiTrackMsg_t* _cmMidiFileUidToMsg( _cmMidiFile_t* mfp, unsigned uid )
  962. {
  963. unsigned i;
  964. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
  965. for(i=0; i<mfp->msgN; ++i)
  966. if( msgV[i]->uid == uid )
  967. return (cmMidiTrackMsg_t*)msgV[i];
  968. return NULL;
  969. }
  970. cmMfRC_t cmMidiFileSetVelocity( cmMidiFileH_t h, unsigned uid, cmMidiByte_t vel )
  971. {
  972. cmMidiTrackMsg_t* r;
  973. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  974. assert( mfp != NULL );
  975. if((r = _cmMidiFileUidToMsg(mfp,uid)) == NULL )
  976. return cmErrMsg(&mfp->err,kUidNotFoundMfRC,"The MIDI file uid %i could not be found.",uid);
  977. if( cmMidiIsNoteOn(r->status) == false && cmMidiIsNoteOff(r->status,0)==false )
  978. return cmErrMsg(&mfp->err,kUidNotANoteMsgMfRC,"Cannot set velocity on a non-Note-On/Off msg.");
  979. cmMidiChMsg_t* chm = (cmMidiChMsg_t*)r->u.chMsgPtr;
  980. chm->d1 = vel;
  981. return kOkMfRC;
  982. }
  983. // Returns NULL if uid is not found or if it the first msg on the track.
  984. cmMidiTrackMsg_t* _cmMidiFileMsgBeforeUid( _cmMidiFile_t* p, unsigned uid )
  985. {
  986. cmMidiTrackMsg_t* m;
  987. if((m = _cmMidiFileUidToMsg(p,uid)) == NULL )
  988. return NULL;
  989. assert( m->trkIdx < p->trkN );
  990. cmMidiTrackMsg_t* m0 = NULL;
  991. cmMidiTrackMsg_t* m1 = p->trkV[ m->trkIdx ].base;
  992. for(; m1!=NULL; m1 = m1->link)
  993. {
  994. if( m1->uid == uid )
  995. break;
  996. m0 = m1;
  997. }
  998. return m0;
  999. }
  1000. unsigned _cmMidiFileIsMsgFirstOnTrack( _cmMidiFile_t* p, unsigned uid )
  1001. {
  1002. unsigned i;
  1003. for(i=0; i<p->trkN; ++i)
  1004. if( p->trkV[i].base!=NULL && p->trkV[i].base->uid == uid )
  1005. return i;
  1006. return cmInvalidIdx;
  1007. }
  1008. cmMfRC_t cmMidiFileInsertMsg( cmMidiFileH_t h, unsigned uid, int dtick, cmMidiByte_t ch, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 )
  1009. {
  1010. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  1011. assert( mfp != NULL );
  1012. cmMidiTrackMsg_t* ref = NULL;
  1013. unsigned trkIdx = cmInvalidIdx;
  1014. // if dtick is positive ...
  1015. if( dtick >= 0 )
  1016. {
  1017. ref = _cmMidiFileUidToMsg(mfp,uid); // ... then get the ref. msg.
  1018. trkIdx = ref->trkIdx;
  1019. }
  1020. else // if dtick is negative ...
  1021. {
  1022. // ... get get the msg before the ref. msg.
  1023. if((ref = _cmMidiFileMsgBeforeUid(mfp,uid)) != NULL )
  1024. trkIdx = ref->trkIdx;
  1025. else
  1026. {
  1027. // ... the ref. msg was first in the track so there is no msg before it
  1028. trkIdx = _cmMidiFileIsMsgFirstOnTrack(mfp,uid);
  1029. }
  1030. }
  1031. // verify that the reference msg was found
  1032. if( trkIdx == cmInvalidIdx )
  1033. return cmErrMsg(&mfp->err,kUidNotFoundMfRC,"The UID (%i) reference note could not be located.",uid);
  1034. assert( trkIdx < mfp->trkN );
  1035. // complete the msg setup
  1036. _cmMidiTrack_t* trk = mfp->trkV + trkIdx;
  1037. cmMidiTrackMsg_t* m = _cmMidiFileAllocMsg(mfp, trkIdx, abs(dtick), status );
  1038. cmMidiChMsg_t* c = cmLhAllocZ(mfp->lhH,cmMidiChMsg_t,1);
  1039. m->u.chMsgPtr = c;
  1040. c->ch = ch;
  1041. c->d0 = d0;
  1042. c->d1 = d1;
  1043. // if 'm' is prior to the first msg in the track
  1044. if( ref == NULL )
  1045. {
  1046. // ... then make 'm' the first msg in the first msg
  1047. m->link = trk->base;
  1048. trk->base = m;
  1049. // 'm' is before ref and the track cannot be empty (because ref is in it) 'm'
  1050. // can never be the last msg in the list
  1051. }
  1052. else // ref is the msg before 'm'
  1053. {
  1054. m->link = ref->link;
  1055. ref->link = m;
  1056. // if ref was the last msg in the trk ...
  1057. if( trk->last == ref )
  1058. trk->last = m; //... then 'm' is now the last msg in the trk
  1059. }
  1060. trk->cnt += 1;
  1061. mfp->msgVDirtyFl = true;
  1062. return kOkMfRC;
  1063. }
  1064. cmMfRC_t cmMidiFileInsertTrackMsg( cmMidiFileH_t h, unsigned trkIdx, const cmMidiTrackMsg_t* msg )
  1065. {
  1066. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
  1067. // validate the track index
  1068. if( trkIdx >= p->trkN )
  1069. return cmErrMsg(&p->err,kInvalidTrkIndexMfRC,"The track index (%i) is invalid.",trkIdx);
  1070. // allocate a new track record
  1071. cmMidiTrackMsg_t* m = (cmMidiTrackMsg_t*)cmLhAllocZ(p->lhH,char,sizeof(cmMidiTrackMsg_t)+msg->byteCnt);
  1072. // fill the track record
  1073. m->uid = p->nextUid++;
  1074. m->atick = msg->atick;
  1075. m->status = msg->status;
  1076. m->metaId = msg->metaId;
  1077. m->trkIdx = trkIdx;
  1078. m->byteCnt = msg->byteCnt;
  1079. memcpy(&m->u,&msg->u,sizeof(msg->u));
  1080. // copy the exernal data
  1081. if( msg->byteCnt > 0 )
  1082. {
  1083. m->u.voidPtr = (m+1);
  1084. memcpy((void*)m->u.voidPtr,msg->u.voidPtr,msg->byteCnt);
  1085. }
  1086. cmMidiTrackMsg_t* m0 = NULL; // msg before insertion
  1087. cmMidiTrackMsg_t* m1 = p->trkV[trkIdx].base; // msg after insertion
  1088. // locate the track record before and after the new msg based on 'atick' value
  1089. for(; m1!=NULL; m1=m1->link)
  1090. {
  1091. if( m1->atick > m->atick )
  1092. {
  1093. if( m0 == NULL )
  1094. p->trkV[trkIdx].base = m;
  1095. else
  1096. m0->link = m;
  1097. m->link = m1;
  1098. break;
  1099. }
  1100. m0 = m1;
  1101. }
  1102. // if the new track record was not inserted then it is the last msg
  1103. if( m1 == NULL )
  1104. {
  1105. assert(m0 == p->trkV[trkIdx].last);
  1106. // link in the new msg
  1107. if( m0 != NULL )
  1108. m0->link = m;
  1109. // the new msg always becomes the last msg
  1110. p->trkV[trkIdx].last = m;
  1111. // if the new msg is the first msg inserted in this track
  1112. if( p->trkV[trkIdx].base == NULL )
  1113. p->trkV[trkIdx].base = m;
  1114. }
  1115. // set the dtick field of the new msg
  1116. if( m0 != NULL )
  1117. {
  1118. assert( m->atick >= m0->atick );
  1119. m->dtick = m->atick - m0->atick;
  1120. }
  1121. // update the dtick field of the msg following the new msg
  1122. if( m1 != NULL )
  1123. {
  1124. assert( m1->atick >= m->atick );
  1125. m1->dtick = m1->atick - m->atick;
  1126. }
  1127. p->trkV[trkIdx].cnt += 1;
  1128. p->msgVDirtyFl = true;
  1129. return kOkMfRC;
  1130. }
  1131. cmMfRC_t cmMidiFileInsertTrackChMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, cmMidiByte_t status, cmMidiByte_t d0, cmMidiByte_t d1 )
  1132. {
  1133. cmMidiTrackMsg_t m;
  1134. cmMidiChMsg_t cm;
  1135. memset(&m,0,sizeof(m));
  1136. memset(&cm,0,sizeof(cm));
  1137. cm.ch = status & 0x0f;
  1138. cm.d0 = d0;
  1139. cm.d1 = d1;
  1140. m.atick = atick;
  1141. m.status = status & 0xf0;
  1142. m.byteCnt = sizeof(cm);
  1143. m.u.chMsgPtr = &cm;
  1144. assert( m.status >= kNoteOffMdId && m.status <= kPbendMdId );
  1145. return cmMidiFileInsertTrackMsg(h,trkIdx,&m);
  1146. }
  1147. cmMfRC_t cmMidFileInsertTrackTempoMsg( cmMidiFileH_t h, unsigned trkIdx, unsigned atick, unsigned bpm )
  1148. {
  1149. cmMidiTrackMsg_t m;
  1150. memset(&m,0,sizeof(m));
  1151. m.atick = atick;
  1152. m.status = kMetaStId;
  1153. m.metaId = kTempoMdId;
  1154. m.u.iVal = 60000000/bpm; // convert BPM to microsPerQN
  1155. return cmMidiFileInsertTrackMsg(h,trkIdx,&m);
  1156. }
  1157. unsigned cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned long long offsUSecs, unsigned* msgUsecsPtr, unsigned* microsPerTickPtr )
  1158. {
  1159. _cmMidiFile_t* p;
  1160. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  1161. return cmInvalidIdx;
  1162. if( p->msgN == 0 )
  1163. return cmInvalidIdx;
  1164. unsigned mi;
  1165. double microsPerQN = 60000000.0/120.0;
  1166. double microsPerTick = microsPerQN / p->ticksPerQN;
  1167. double accUSecs = 0;
  1168. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
  1169. for(mi=0; mi<p->msgN; ++mi)
  1170. {
  1171. const cmMidiTrackMsg_t* mp = msgV[mi];
  1172. if( mp->amicro >= offsUSecs )
  1173. break;
  1174. }
  1175. if( mi == p->msgN )
  1176. return cmInvalidIdx;
  1177. if( msgUsecsPtr != NULL )
  1178. *msgUsecsPtr = round(accUSecs - offsUSecs);
  1179. if( microsPerTickPtr != NULL )
  1180. *microsPerTickPtr = round(microsPerTick);
  1181. return mi;
  1182. }
  1183. /*
  1184. 1.Move closest previous tempo msg to begin.
  1185. 2.The first msg in each track must be the first msg >= begin.time
  1186. 3.Remove all msgs > end.time - except the 'endMsg' for each note/pedal that is active at end time.
  1187. */
  1188. unsigned _cmMidiFileIsEndMsg( cmMidiTrackMsg_t* m, cmMidiTrackMsg_t** endMsgArray, unsigned n )
  1189. {
  1190. unsigned i = 0;
  1191. for(; i<n; ++i)
  1192. if( endMsgArray[i] == m )
  1193. return i;
  1194. return cmInvalidIdx;
  1195. }
  1196. bool _cmMidiFileAllEndMsgFound( cmMidiTrackMsg_t** noteMsgArray, unsigned n0, cmMidiTrackMsg_t** pedalMsgArray, unsigned n1 )
  1197. {
  1198. unsigned i=0;
  1199. for(; i<n0; ++i)
  1200. if( noteMsgArray[i] != NULL )
  1201. return false;
  1202. for(i=0; i<n1; ++i)
  1203. if( pedalMsgArray[i] != NULL )
  1204. return false;
  1205. return true;
  1206. }
  1207. double cmMidiFileDurSecs( cmMidiFileH_t h )
  1208. {
  1209. _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  1210. if( mfp->msgN == 0 )
  1211. return 0;
  1212. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(mfp);
  1213. return msgV[ mfp->msgN-1 ]->amicro / 1000000.0;
  1214. }
  1215. void _cmMidiFileSetDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1 )
  1216. {
  1217. // calculate the duration of the sounding note
  1218. ((cmMidiChMsg_t*)m0->u.chMsgPtr)->durMicros = m1->amicro - m0->amicro;
  1219. // set the note-off msg pointer
  1220. ((cmMidiChMsg_t*)m0->u.chMsgPtr)->end = m1;
  1221. }
  1222. bool _cmMidiFileCalcNoteDur( cmMidiTrackMsg_t* m0, cmMidiTrackMsg_t* m1, int noteGateFl, int sustainGateFl, bool sostGateFl )
  1223. {
  1224. // if the note is being kept sounding because the key is still depressed,
  1225. // the sustain pedal is down or it is being held by the sostenuto pedal ....
  1226. if( noteGateFl>0 || sustainGateFl>0 || sostGateFl )
  1227. return false; // ... do nothing
  1228. _cmMidiFileSetDur(m0,m1);
  1229. return true;
  1230. }
  1231. void cmMidiFileCalcNoteDurations( cmMidiFileH_t h, unsigned flags )
  1232. {
  1233. _cmMidiFile_t* p;
  1234. bool warningFl = cmIsFlag(flags,kWarningsMfFl);
  1235. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  1236. return;
  1237. if( p->msgN == 0 )
  1238. return;
  1239. unsigned mi = cmInvalidId;
  1240. cmMidiTrackMsg_t* noteM[ kMidiNoteCnt * kMidiChCnt ]; // ptr to note-on or NULL if the note is not sounding
  1241. cmMidiTrackMsg_t* sustV[ kMidiChCnt ]; // ptr to last sustain pedal down msg or NULL if susteain pedal is not down
  1242. cmMidiTrackMsg_t* sostV[ kMidiChCnt ]; // ptr to last sost. pedal down msg or NULL if sost. pedal is not down
  1243. int noteGateM[ kMidiNoteCnt * kMidiChCnt ]; // true if the associated note key is depressed
  1244. bool sostGateM[ kMidiNoteCnt * kMidiChCnt ]; // true if the associated note was active when the sost. pedal went down
  1245. int sustGateV[ kMidiChCnt]; // true if the associated sustain pedal is down
  1246. int sostGateV[ kMidiChCnt]; // true if the associated sostenuto pedal is down
  1247. unsigned i,j;
  1248. unsigned n = 0;
  1249. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
  1250. // initialize the state tracking variables
  1251. for(i=0; i<kMidiChCnt; ++i)
  1252. {
  1253. sustV[i] = NULL;
  1254. sustGateV[i] = 0;
  1255. sostV[i] = NULL;
  1256. sostGateV[i] = 0;
  1257. for(j=0; j<kMidiNoteCnt; ++j)
  1258. {
  1259. noteM[ i*kMidiNoteCnt + j ] = NULL;
  1260. noteGateM[ i*kMidiNoteCnt + j ] = 0;
  1261. sostGateM[ i*kMidiNoteCnt + j ] = false;
  1262. }
  1263. }
  1264. // for each midi event
  1265. for(mi=0; mi<p->msgN; ++mi)
  1266. {
  1267. cmMidiTrackMsg_t* m = (cmMidiTrackMsg_t*)msgV[mi]; // cast away const
  1268. // verify that time is also incrementing
  1269. assert( mi==0 || (mi>0 && m->amicro >= msgV[mi-1]->amicro) );
  1270. // ignore all non-channel messages
  1271. if( !cmMidiIsChStatus( m->status ) )
  1272. continue;
  1273. cmMidiByte_t ch = m->u.chMsgPtr->ch; // get the midi msg channel
  1274. cmMidiByte_t d0 = m->u.chMsgPtr->d0; // get the midi msg data value
  1275. // if this is a note-on msg
  1276. if( cmMidiFileIsNoteOn(m) )
  1277. {
  1278. unsigned k = ch*kMidiNoteCnt + d0;
  1279. // there should be no existing sounding note instance for this pitch
  1280. if( noteGateM[k] == 0 && noteM[k] != NULL )
  1281. {
  1282. if( warningFl )
  1283. cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-off instance for note on:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
  1284. if( cmIsFlag(flags,kDropReattacksMfFl) )
  1285. {
  1286. m->flags |= kDropTrkMsgFl;
  1287. n += 1;
  1288. }
  1289. }
  1290. // if this is a re-attack
  1291. if( noteM[k] != NULL )
  1292. noteGateM[k] += 1;
  1293. else // this is a new attack
  1294. {
  1295. noteM[k] = m;
  1296. noteGateM[k] = 1;
  1297. }
  1298. }
  1299. else
  1300. // if this is a note-off msg
  1301. if( cmMidiFileIsNoteOff(m) )
  1302. {
  1303. unsigned k = ch*kMidiNoteCnt + d0;
  1304. cmMidiTrackMsg_t* m0 = noteM[k];
  1305. if( m0 == NULL )
  1306. cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-on instance for note-off:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
  1307. else
  1308. {
  1309. // a key was released - so it should not already be up
  1310. if( noteGateM[k]==0 )
  1311. cmErrWarnMsg(&p->err,kMissingNoteOffMfRC,"%i : Missing note-on for note-off:%s",m->uid,cmMidiToSciPitch(d0,NULL,0));
  1312. else
  1313. {
  1314. noteGateM[k] -= 1; // update the note gate state
  1315. // update the sounding note status
  1316. if( _cmMidiFileCalcNoteDur(m0, m, noteGateM[k], sustGateV[ch], sostGateM[k]) )
  1317. noteM[k] = NULL;
  1318. }
  1319. }
  1320. }
  1321. else
  1322. // This is a sustain-pedal down msg
  1323. if( cmMidiFileIsSustainPedalDown(m) )
  1324. {
  1325. // if the sustain channel is already down
  1326. if( warningFl && sustGateV[ch] )
  1327. cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal went down twice with no intervening release.",m->uid);
  1328. sustGateV[ch] += 1;
  1329. if( sustV[ch] == NULL )
  1330. sustV[ch] = m;
  1331. }
  1332. else
  1333. // This is a sustain-pedal up msg
  1334. if( cmMidiFileIsSustainPedalUp(m) )
  1335. {
  1336. // if the sustain channel is already up
  1337. if( sustGateV[ch]==0 )
  1338. cmErrWarnMsg(&p->err,kSustainPedalMfRC,"%i : The sustain pedal release message was received with no previous pedal down.",m->uid);
  1339. if( sustGateV[ch] >= 1 )
  1340. {
  1341. sustGateV[ch] -= 1;
  1342. if( sustGateV[ch] == 0 )
  1343. {
  1344. unsigned k = ch*kMidiNoteCnt;
  1345. // for each sounding note on this channel
  1346. for(; k<ch*kMidiNoteCnt+kMidiNoteCnt; ++k)
  1347. if( noteM[k]!=NULL && _cmMidiFileCalcNoteDur(noteM[k], m, noteGateM[k], sustGateV[ch], sostGateM[k]) )
  1348. noteM[k] = NULL;
  1349. if( sustV[ch] != NULL )
  1350. {
  1351. _cmMidiFileSetDur(sustV[ch],m);
  1352. ((cmMidiChMsg_t*)sustV[ch]->u.chMsgPtr)->end = m; // set the pedal-up msg ptr. in the pedal-down msg.
  1353. sustV[ch] = NULL;
  1354. }
  1355. }
  1356. }
  1357. }
  1358. else
  1359. // This is a sostenuto pedal-down msg
  1360. if( cmMidiFileIsSostenutoPedalDown(m) )
  1361. {
  1362. // if the sustain channel is already down
  1363. if( warningFl && sostGateV[ch] )
  1364. cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal went down twice with no intervening release.",m->uid);
  1365. // record the notes that are active when the sostenuto pedal went down
  1366. unsigned k = ch * kMidiNoteCnt;
  1367. for(i=0; i<kMidiNoteCnt; ++i)
  1368. sostGateM[k+i] = noteGateM[k+i] > 0;
  1369. sostGateV[ch] += 1;
  1370. }
  1371. else
  1372. // This is a sostenuto pedal-up msg
  1373. if( cmMidiFileIsSostenutoPedalUp(m) )
  1374. {
  1375. // if the sustain channel is already up
  1376. if( sostGateV[ch]==0 )
  1377. cmErrWarnMsg(&p->err,kSostenutoPedalMfRC,"%i : The sostenuto pedal release message was received with no previous pedal down.",m->uid);
  1378. if( sostGateV[ch] >= 1 )
  1379. {
  1380. sostGateV[ch] -= 1;
  1381. if( sostGateV[ch] == 0 )
  1382. {
  1383. unsigned k = ch*kMidiNoteCnt;
  1384. // for each note on this channel
  1385. for(; k<ch*kMidiNoteCnt+kMidiNoteCnt; ++k)
  1386. {
  1387. sostGateM[k] = false;
  1388. if( noteM[k]!=NULL && _cmMidiFileCalcNoteDur(noteM[k], m, noteGateM[k], sustGateV[ch], sostGateM[k]) )
  1389. noteM[k] = NULL;
  1390. }
  1391. if( sostV[ch] != NULL )
  1392. {
  1393. _cmMidiFileSetDur(sostV[ch],m);
  1394. ((cmMidiChMsg_t*)sostV[ch]->u.chMsgPtr)->end = m; // set the pedal-up msg ptr. in the pedal-down msg.
  1395. sostV[ch] = NULL;
  1396. }
  1397. }
  1398. }
  1399. }
  1400. } // for each midi file event
  1401. if( warningFl )
  1402. {
  1403. unsigned sustChN = 0; // count of channels where the sustain pedal was left on at the end of the file
  1404. unsigned sostChN = 0; // sostenuto
  1405. unsigned sustInstN = 0; // count of sustain on with no previous sustain off
  1406. unsigned sostInstN = 0; // sostenuto on
  1407. unsigned noteN = 0; // count of notes left on at the end of the file
  1408. unsigned noteInstN = 0; // count of reattacks
  1409. // initialize the state tracking variables
  1410. for(i=0; i<kMidiChCnt; ++i)
  1411. {
  1412. if( sustV[i]!=NULL )
  1413. sustChN += 1;
  1414. sustInstN += sustGateV[i];
  1415. if( sostV[i] != NULL )
  1416. sostChN += 1;
  1417. sostInstN += sostGateV[i] = 0;
  1418. for(j=0; j<kMidiNoteCnt; ++j)
  1419. {
  1420. noteN += noteM[ i*kMidiNoteCnt + j ] != NULL;
  1421. noteInstN += noteGateM[ i*kMidiNoteCnt + j ];
  1422. }
  1423. }
  1424. cmErrWarnMsg(&p->err,kEventTerminationMfRC,"note:%i inst:%i sustain: %i inst: %i sost: %i inst: %i",noteN,noteInstN,sustChN,sustInstN,sostChN,sostInstN);
  1425. }
  1426. // drop
  1427. if( cmIsFlag(flags,kDropReattacksMfFl) )
  1428. _cmMidiFileDrop(p);
  1429. }
  1430. void cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks )
  1431. {
  1432. _cmMidiFile_t* p;
  1433. unsigned mi;
  1434. if((p = _cmMidiFileHandleToPtr(h)) == NULL )
  1435. return;
  1436. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
  1437. if( p->msgN == 0 )
  1438. return;
  1439. for(mi=0; mi<p->msgN; ++mi)
  1440. {
  1441. cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)msgV[mi]; // cast away const
  1442. // locate the first msg which has a non-zero delta tick
  1443. if( mp->dtick > 0 )
  1444. {
  1445. mp->dtick = ticks;
  1446. break;
  1447. }
  1448. }
  1449. }
  1450. unsigned cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m )
  1451. { return sizeof(cmMidiTrackMsg_t) + m->byteCnt; }
  1452. cmMidiTrackMsg_t* cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt )
  1453. {
  1454. unsigned n = sizeof(cmMidiTrackMsg_t) + m->byteCnt;
  1455. if( n < bufByteCnt )
  1456. {
  1457. assert(0);
  1458. return NULL;
  1459. }
  1460. // copy the cmMidiTrackMsg_t into the buffer
  1461. memcpy(buf, m, sizeof(cmMidiTrackMsg_t));
  1462. if( m->byteCnt > 0 )
  1463. {
  1464. // copy any linked data into the buffer
  1465. memcpy(buf + sizeof(cmMidiTrackMsg_t), m->u.voidPtr, m->byteCnt );
  1466. // fixup the linked data ptr
  1467. cmMidiTrackMsg_t* mp = (cmMidiTrackMsg_t*)buf;
  1468. mp->u.voidPtr = buf + sizeof(cmMidiTrackMsg_t);
  1469. }
  1470. return (cmMidiTrackMsg_t*)buf;
  1471. }
  1472. void _cmMidiFilePrintHdr( const _cmMidiFile_t* mfp, cmRpt_t* rpt )
  1473. {
  1474. if( mfp->fn != NULL )
  1475. cmRptPrintf(rpt,"%s ",mfp->fn);
  1476. cmRptPrintf(rpt,"fmt:%i ticksPerQN:%i tracks:%i\n",mfp->fmtId,mfp->ticksPerQN,mfp->trkN);
  1477. cmRptPrintf(rpt," UID trk dtick atick amicro type ch D0 D1\n");
  1478. cmRptPrintf(rpt,"----- --- ---------- ---------- ---------- : ---- --- --- ---\n");
  1479. }
  1480. void _cmMidiFilePrintMsg( cmRpt_t* rpt, const cmMidiTrackMsg_t* tmp )
  1481. {
  1482. cmRptPrintf(rpt,"%5i %3i %10u %10llu %10llu : ",
  1483. tmp->uid,
  1484. tmp->trkIdx,
  1485. tmp->dtick,
  1486. tmp->atick,
  1487. tmp->amicro );
  1488. if( tmp->status == kMetaStId )
  1489. {
  1490. switch( tmp->metaId )
  1491. {
  1492. case kTempoMdId:
  1493. cmRptPrintf(rpt,"%s bpm %i", cmMidiMetaStatusToLabel(tmp->metaId),60000000 / tmp->u.iVal);
  1494. break;
  1495. case kTimeSigMdId:
  1496. cmRptPrintf(rpt,"%s %i %i", cmMidiMetaStatusToLabel(tmp->metaId), tmp->u.timeSigPtr->num,tmp->u.timeSigPtr->den);
  1497. break;
  1498. default:
  1499. cmRptPrintf(rpt,"%s ", cmMidiMetaStatusToLabel(tmp->metaId));
  1500. }
  1501. }
  1502. else
  1503. {
  1504. cmRptPrintf(rpt,"%4s %3i %3i %3i",
  1505. cmMidiStatusToLabel(tmp->status),
  1506. tmp->u.chMsgPtr->ch,
  1507. tmp->u.chMsgPtr->d0,
  1508. tmp->u.chMsgPtr->d1);
  1509. }
  1510. if( cmMidiIsChStatus(tmp->status) && cmMidiIsNoteOn(tmp->status) && (tmp->u.chMsgPtr->d1>0) )
  1511. cmRptPrintf(rpt," %4s ",cmMidiToSciPitch(tmp->u.chMsgPtr->d0,NULL,0));
  1512. cmRptPrintf(rpt,"\n");
  1513. }
  1514. void cmMidiFilePrintMsgs( cmMidiFileH_t h, cmRpt_t* rpt )
  1515. {
  1516. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(h);
  1517. unsigned mi;
  1518. _cmMidiFilePrintHdr(p,rpt);
  1519. const cmMidiTrackMsg_t** msgV = _cmMidiFileMsgArray(p);
  1520. for(mi=0; mi<p->msgN; ++mi)
  1521. {
  1522. const cmMidiTrackMsg_t* mp = msgV[mi];
  1523. if( mp != NULL )
  1524. _cmMidiFilePrintMsg(rpt,mp);
  1525. }
  1526. }
  1527. void cmMidiFilePrintTracks( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt )
  1528. {
  1529. const _cmMidiFile_t* mfp = _cmMidiFileHandleToPtr(h);
  1530. _cmMidiFilePrintHdr(mfp,rpt);
  1531. int i = trkIdx == cmInvalidIdx ? 0 : trkIdx;
  1532. int n = trkIdx == cmInvalidIdx ? mfp->trkN : trkIdx+1;
  1533. for(; i<n; ++i)
  1534. {
  1535. cmRptPrintf(rpt,"Track:%i\n",i);
  1536. cmMidiTrackMsg_t* tmp = mfp->trkV[i].base;
  1537. while( tmp != NULL )
  1538. {
  1539. _cmMidiFilePrintMsg(rpt,tmp);
  1540. tmp = tmp->link;
  1541. }
  1542. }
  1543. }
  1544. void cmMidiFileTestPrint( void* printDataPtr, const char* fmt, va_list vl )
  1545. { vprintf(fmt,vl); }
  1546. cmMidiFileDensity_t* cmMidiFileNoteDensity( cmMidiFileH_t h, unsigned* cntRef )
  1547. {
  1548. int msgN = cmMidiFileMsgCount(h);
  1549. const cmMidiTrackMsg_t** msgs = cmMidiFileMsgArray(h);
  1550. cmMidiFileDensity_t* dV = cmMemAllocZ(cmMidiFileDensity_t,msgN);
  1551. int i,j,k;
  1552. for(i=0,k=0; i<msgN && k<msgN; ++i)
  1553. if( msgs[i]->status == kNoteOnMdId && msgs[i]->u.chMsgPtr->d1 > 0 )
  1554. {
  1555. dV[k].uid = msgs[i]->uid;
  1556. dV[k].amicro = msgs[i]->amicro;
  1557. // count the number of notes occuring in the time window
  1558. // between this note and one second prior to this note.
  1559. for(j=i; j>=0; --j)
  1560. {
  1561. if( msgs[i]->amicro - msgs[j]->amicro > 1000000 )
  1562. break;
  1563. dV[k].density += 1;
  1564. }
  1565. k += 1;
  1566. }
  1567. if( cntRef != NULL )
  1568. *cntRef = k;
  1569. return dV;
  1570. }
  1571. cmMfRC_t cmMidiFileGenPlotFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outFn )
  1572. {
  1573. cmMfRC_t rc = kOkMfRC;
  1574. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  1575. cmFileH_t fH = cmFileNullHandle;
  1576. unsigned i = 0;
  1577. const cmMidiTrackMsg_t** m = NULL;
  1578. unsigned mN = 0;
  1579. if((rc = cmMidiFileOpen(ctx, &mfH, midiFn )) != kOkMfRC )
  1580. return cmErrMsg(&ctx->err,rc,"The MIDI file object could not be opened from '%s'.",cmStringNullGuard(midiFn));
  1581. _cmMidiFile_t* p = _cmMidiFileHandleToPtr(mfH);
  1582. if( (m = cmMidiFileMsgArray(mfH)) == NULL || (mN = cmMidiFileMsgCount(mfH)) == 0 )
  1583. {
  1584. rc = cmErrMsg(&p->err,kFileFailMfRC,"The MIDI file object appears to be empty.");
  1585. goto errLabel;
  1586. }
  1587. cmMidiFileCalcNoteDurations( mfH, 0 );
  1588. if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  1589. return cmErrMsg(&p->err,kFileFailMfRC,"Unable to create the file '%s'.",cmStringNullGuard(outFn));
  1590. for(i=0; i<mN; ++i)
  1591. if( (m[i]!=NULL) && cmMidiIsChStatus(m[i]->status) && cmMidiIsNoteOn(m[i]->status) && (m[i]->u.chMsgPtr->d1>0) )
  1592. cmFilePrintf(fH,"n %f %f %i %s\n",m[i]->amicro/1000000.0,m[i]->u.chMsgPtr->durMicros/1000000.0,m[i]->uid,cmMidiToSciPitch(m[i]->u.chMsgPtr->d0,NULL,0));
  1593. errLabel:
  1594. cmMidiFileClose(&mfH);
  1595. cmFileClose(&fH);
  1596. return rc;
  1597. }
  1598. cmMfRC_t cmMidiFileGenSvgFile( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outSvgFn, const cmChar_t* cssFn, bool standAloneFl, bool panZoomFl )
  1599. {
  1600. cmMfRC_t rc = kOkMfRC;
  1601. cmSvgH_t svgH = cmSvgNullHandle;
  1602. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  1603. unsigned msgN = 0;
  1604. const cmMidiTrackMsg_t** msgs = NULL;
  1605. unsigned noteHeight = 10;
  1606. double micros_per_sec = 1000.0;
  1607. unsigned i;
  1608. if((rc = cmMidiFileOpen(ctx,&mfH,midiFn)) != kOkMfRC )
  1609. {
  1610. rc = cmErrMsg(&ctx->err,rc,"Unable to open the MIDI file '%s'.",cmStringNullGuard(midiFn));
  1611. goto errLabel;
  1612. }
  1613. cmMidiFileCalcNoteDurations( mfH, 0 );
  1614. msgN = cmMidiFileMsgCount(mfH);
  1615. msgs = cmMidiFileMsgArray(mfH);
  1616. if( cmSvgWriterAlloc(ctx,&svgH) != kOkSvgRC )
  1617. {
  1618. rc = cmErrMsg(&ctx->err,kSvgFailMfRC,"Unable to create the MIDI SVG output file '%s'.",cmStringNullGuard(outSvgFn));
  1619. goto errLabel;
  1620. }
  1621. for(i=0; i<msgN && rc==kOkMfRC; ++i)
  1622. if( msgs[i]->status == kNoteOnMdId && msgs[i]->u.chMsgPtr->d1 > 0 )
  1623. {
  1624. const cmMidiTrackMsg_t* m = msgs[i];
  1625. if( cmSvgWriterRect(svgH, m->amicro/micros_per_sec, m->u.chMsgPtr->d0 * noteHeight, m->u.chMsgPtr->durMicros/micros_per_sec, noteHeight-1, "note" ) != kOkSvgRC )
  1626. rc = kSvgFailMfRC;
  1627. const cmChar_t* t0 = cmMidiToSciPitch(m->u.chMsgPtr->d0,NULL,0);
  1628. if( cmSvgWriterText(svgH, (m->amicro + (m->u.chMsgPtr->durMicros/2)) / micros_per_sec, m->u.chMsgPtr->d0 * noteHeight, t0, "text" ) != kOkSvgRC )
  1629. rc = kSvgFailMfRC;
  1630. }
  1631. if( rc != kOkMfRC )
  1632. {
  1633. cmErrMsg(&ctx->err,rc,"SVG Shape insertion failed.");
  1634. goto errLabel;
  1635. }
  1636. unsigned dN = 0;
  1637. cmMidiFileDensity_t* dV = cmMidiFileNoteDensity( mfH, &dN );
  1638. double t0 = 0;
  1639. double y0 = 64.0;
  1640. cmChar_t* tx = NULL;
  1641. for(i=0; i<dN; ++i)
  1642. {
  1643. const cmMidiTrackMsg_t* m;
  1644. if((m = _cmMidiFileUidToMsg( _cmMidiFileHandleToPtr(mfH), dV[i].uid )) == NULL )
  1645. rc = cmErrMsg(&ctx->err,kUidNotFoundMfRC,"The MIDI msg form UID:%i was not found.",dV[i].uid);
  1646. else
  1647. {
  1648. double t1 = m->amicro / micros_per_sec;
  1649. double y1 = dV[i].density * noteHeight;
  1650. cmSvgWriterLine(svgH, t0, y0, t1, y1, "density" );
  1651. cmSvgWriterText(svgH, t1, y1, tx = cmTsPrintfP(tx,"%i",dV[i].density),"dtext");
  1652. t0 = t1;
  1653. y0 = y1;
  1654. }
  1655. }
  1656. cmMemFree(dV);
  1657. cmMemFree(tx);
  1658. if( rc == kOkMfRC )
  1659. if( cmSvgWriterWrite(svgH,cssFn,outSvgFn, standAloneFl, panZoomFl) != kOkSvgRC )
  1660. rc = cmErrMsg(&ctx->err,kSvgFailMfRC,"SVG file write to '%s' failed.",cmStringNullGuard(outSvgFn));
  1661. errLabel:
  1662. cmMidiFileClose(&mfH);
  1663. cmSvgWriterFree(&svgH);
  1664. return rc;
  1665. }
  1666. cmMfRC_t cmMidiFileReport( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* outTextFn )
  1667. {
  1668. cmMidiFileH_t mfH = cmMidiFileNullHandle;
  1669. cmRptFileH_t rptH = cmRptFileNullHandle;
  1670. cmMfRC_t rc;
  1671. if((rc = cmMidiFileOpen(ctx,&mfH,midiFn)) != kOkMfRC )
  1672. {
  1673. rc = cmErrMsg(&ctx->err,rc,"Unable to open the MIDI file: %s\n",cmStringNullGuard(midiFn));
  1674. goto errLabel;
  1675. }
  1676. if(( rc = cmRptFileCreate(ctx,&rptH, outTextFn, NULL )) != kOkRfRC )
  1677. {
  1678. rc = cmErrMsg(&ctx->err,rc,"Unable to open the report file: %s\n",cmStringNullGuard(outTextFn));
  1679. goto errLabel;
  1680. }
  1681. cmMidiFilePrintMsgs(mfH, cmRptFileRpt(rptH) );
  1682. errLabel:
  1683. cmRptFileClose(&rptH);
  1684. cmMidiFileClose(&mfH);
  1685. return rc;
  1686. }
  1687. void cmMidiFilePrintControlNumbers( cmCtx_t* ctx, const char* fn )
  1688. {
  1689. cmMidiFileH_t h = cmMidiFileNullHandle;
  1690. cmMfRC_t rc;
  1691. if((rc = cmMidiFileOpen(ctx, &h, fn )) != kOkMfRC )
  1692. {
  1693. cmErrMsg(&ctx->err,rc,"MIDI file open failed on '%s'.",fn);
  1694. goto errLabel;
  1695. }
  1696. const cmMidiTrackMsg_t** mm;
  1697. unsigned n = cmMidiFileMsgCount(h);
  1698. if((mm = cmMidiFileMsgArray(h)) != NULL )
  1699. {
  1700. unsigned j;
  1701. for(j=0; j<n; ++j)
  1702. {
  1703. const cmMidiTrackMsg_t* m = mm[j];
  1704. if( m->status == kCtlMdId && m->u.chMsgPtr->d0==66 )
  1705. printf("%i %i\n",m->u.chMsgPtr->d0,m->u.chMsgPtr->d1);
  1706. }
  1707. }
  1708. errLabel:
  1709. cmMidiFileClose(&h);
  1710. }
  1711. void cmMidiFileTest( const char* fn, cmCtx_t* ctx )
  1712. {
  1713. cmMfRC_t rc;
  1714. cmMidiFileH_t h = cmMidiFileNullHandle;
  1715. if((rc = cmMidiFileOpen(ctx,&h,fn)) != kOkMfRC )
  1716. {
  1717. printf("Error:%i Unable to open the cmMidi file: %s\n",rc,fn);
  1718. return;
  1719. }
  1720. cmMidiFileCalcNoteDurations( h, 0 );
  1721. if( 1 )
  1722. {
  1723. //cmMidiFileTickToMicros( h );
  1724. //cmMidiFileTickToSamples(h,96000,false);
  1725. cmMidiFilePrintMsgs(h,&ctx->rpt);
  1726. }
  1727. if( 0 )
  1728. {
  1729. //cmMidiFilePrint(h,cmMidiFileTrackCount(h)-1,&ctx->rpt);
  1730. //cmMidiFilePrint(h,cmInvalidIdx,&ctx->rpt);
  1731. cmMidiFilePrintControlNumbers(ctx, fn );
  1732. }
  1733. if( 0 )
  1734. {
  1735. printf("Tracks:%i\n",cmMidiFileTrackCount(h));
  1736. unsigned i = 0;
  1737. for(i=0; i<cmMidiFileMsgCount(h); ++i)
  1738. {
  1739. cmMidiTrackMsg_t* tmp = (cmMidiTrackMsg_t*)cmMidiFileMsgArray(h)[i];
  1740. if( tmp->status==kMetaStId && tmp->metaId == kTempoMdId )
  1741. {
  1742. double bpm = 60000000.0/tmp->u.iVal;
  1743. printf("Tempo:%i %f\n",tmp->u.iVal,bpm);
  1744. tmp->u.iVal = floor( 60000000.0/69.0 );
  1745. break;
  1746. }
  1747. }
  1748. cmMidiFileWrite(h,"/home/kevin/temp/test0.mid");
  1749. }
  1750. cmMidiFileClose(&h);
  1751. }