Programmable real-time audio signal processing application
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmGrTlFltk.cpp 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. //| Copyright: (C) 2019-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include <FL/Fl.H>
  4. #include <Fl/fl_draw.h>
  5. #include <FL/Fl_Widget.H>
  6. #include <FL/Fl_Double_Window.H>
  7. #include <Fl/Fl_Output.H>
  8. #include <Fl/Fl_Menu_Bar.H>
  9. #include <vector>
  10. #include "Fl_CbLinker.h"
  11. #include "cmGlobal.h"
  12. #include "cmFloatTypes.h"
  13. #include "cmRpt.h"
  14. #include "cmErr.h"
  15. #include "cmCtx.h"
  16. #include "cmMem.h"
  17. #include "cmMallocDebug.h"
  18. #include "cmLinkedHeap.h"
  19. #include "cmText.h"
  20. #include "cmThread.h"
  21. #include "cmPrefs.h"
  22. #include "cmTime.h"
  23. #include "cmMidi.h"
  24. #include "cmMidiFile.h"
  25. #include "cmAudioFile.h"
  26. #include "cmAudioFileMgr.h"
  27. #include "cmSymTbl.h"
  28. #include "cmTimeLine.h"
  29. #include "cmScore.h"
  30. #include "cmGr.h"
  31. #include "cmGrDevCtx.h"
  32. #include "cmGrPlot.h"
  33. #include "cmGrPage.h"
  34. #include "cmGrFltk.h"
  35. #include "gvHashFunc.h"
  36. #include "cmGrTlFltk.h"
  37. #include "cmGrPlotAudio.h"
  38. #include "cmdIf.h"
  39. cmGrTlFltk::cmGrTlFltk(cmCtx_t* ctx, cmdIf* cp, Fl_Menu_Bar* menu, int x, int y, int w, int h)
  40. : cmGrPlotFltk(ctx,x,y,w,h),_srate(0),_cmdIf(cp),_menuBar(menu),
  41. _seqMenuIdx(cmInvalidIdx),_seqCnt(0),_seqItemArray(NULL),
  42. _markCnt(0),
  43. _samplesMetricId(cmInvalidId),_secondsMetricId(cmInvalidId),
  44. _selMarkPlotObjH(cmGrPlObjNullHandle),
  45. _curSeqId(cmInvalidId)
  46. {
  47. _createMenu();
  48. //_seqMenuIdx = _menuBar->find_index("&Seq");
  49. cmErrSetup(&_err,&ctx->rpt,"cmGrTlFltk");
  50. if( cmGrPageIsValid(pageHandle()) == false )
  51. return;
  52. // set the arrangement of 'views' on the 'page'
  53. // (2 rows, 1 column)
  54. initViews(kViewCnt,1);
  55. unsigned vwIdx = kAudioVwIdx;
  56. cmGrPgH_t grPgH = pageHandle();
  57. cmGrVwH_t grVwH = cmGrPageViewHandle( grPgH, vwIdx);
  58. cmGrH_t grH = cmGrViewGrHandle( grVwH );
  59. cmGrAxH_t grAxH = cmGrAxNullHandle;
  60. cmGrVExt_t limExt;
  61. // register plot hash mark labelling functions
  62. _samplesMetricId = cmGrPageLabelFuncRegister( grPgH, _s_roundHashValueFunc, this, "Round" );
  63. _secondsMetricId = cmGrPageLabelFuncRegister( grPgH, _s_minSecMsHashValueFunc, this, "Min:Sec:Ms" );
  64. unsigned pitchLabelFuncId = cmGrPageLabelFuncRegister( grPgH, _s_midiSciPitchValueFunc, this, "Pitch" );
  65. //unsigned timeLabelFuncId = _secondsMetricId;
  66. cmGrVExtSetD(&limExt,0,-1,0,1);
  67. cmGrObjSetWorldLimitExt(grH, cmGrRootObjH(grH), &limExt, kTopGrFl | kBottomGrFl );
  68. grAxH = cmGrViewAxisHandle(grVwH, kBottomGrIdx);
  69. cmGrAxisSetCfg( grAxH, cmClrFlag(cmGrAxisCfg( grAxH ), kHashMarkGrFl | kHashLabelGrFl ));
  70. //grAxH = cmGrViewAxisHandle(grVwH, kTopGrIdx );
  71. //cmGrAxisSetLabelFunc( grAxH, timeLabelFuncId );
  72. grAxH = cmGrViewAxisHandle(grVwH, kRightGrIdx);
  73. cmGrAxisSetCfg( grAxH, cmClrFlag(cmGrAxisCfg( grAxH ), kHashLabelGrFl ));
  74. //cmGrViewSetLabelFunc( grVwH, kTopGrIdx, timeLabelFuncId );
  75. cmGrViewSetCfg( grVwH, cmSetFlag(cmGrViewCfg(grVwH),kSelectHorzGrFl) );
  76. vwIdx = kMidiVwIdx;
  77. grVwH = cmGrPageViewHandle( grPgH, vwIdx);
  78. grH = cmGrViewGrHandle( grVwH );
  79. cmGrVExtSetD(&limExt,0,0,0,127);
  80. cmGrObjSetWorldLimitExt(grH, cmGrRootObjH(grH), &limExt, kTopGrFl | kBottomGrFl );
  81. grAxH = cmGrViewAxisHandle(grVwH, kTopGrIdx);
  82. cmGrAxisSetCfg( grAxH, cmClrFlag(cmGrAxisCfg( grAxH ), kHashMarkGrFl | kHashLabelGrFl ));
  83. //grAxH = cmGrViewAxisHandle(grVwH, kBottomGrIdx );
  84. //cmGrAxisSetLabelFunc( grAxH, timeLabelFuncId );
  85. grAxH = cmGrViewAxisHandle(grVwH, kRightGrIdx);
  86. cmGrAxisSetCfg( grAxH, cmClrFlag(cmGrAxisCfg( grAxH ), kHashLabelGrFl ));
  87. grAxH = cmGrViewAxisHandle(grVwH, kLeftGrIdx );
  88. cmGrAxisSetLabelFunc( grAxH, pitchLabelFuncId );
  89. //cmGrViewSetLabelFunc( grVwH, kTopGrIdx, timeLabelFuncId );
  90. cmGrViewSetLabelFunc( grVwH, kLeftGrIdx, pitchLabelFuncId );
  91. cmGrViewSetCfg( grVwH, cmSetFlag(cmGrViewCfg(grVwH),kSelectHorzGrFl) );
  92. cmGrSetSync( grH, cmGrViewGrHandle( cmGrPageViewHandle( grPgH, kAudioVwIdx ) ), kWorldSyncGrFl | kViewSyncGrFl | kSelectSyncGrFl | kHorzSyncGrFl );
  93. cmGrSetSync( cmGrViewGrHandle( cmGrPageViewHandle( grPgH, kAudioVwIdx ) ), grH, kWorldSyncGrFl | kViewSyncGrFl | kSelectSyncGrFl | kHorzSyncGrFl );
  94. setTimeAxisMetric(kSecondsMetricId);
  95. }
  96. cmGrTlFltk::~cmGrTlFltk()
  97. {
  98. cmMemFree(_seqItemArray);
  99. }
  100. void cmGrTlFltk::_insertTimeLineObj( const cmTlUiMsg_t* m )
  101. {
  102. cmGrPlH_t plH = plotHandle();
  103. cmGrPgH_t pgH = pageHandle();
  104. cmGrPlObjH_t parentObjH = cmGrPlObjNullHandle;
  105. cmGrPlObjH_t xAnchorObjH = cmGrPlObjNullHandle;
  106. cmGrPlObjH_t yAnchorObjH = cmGrPlObjNullHandle;
  107. cmGrPlObjH_t objH = cmGrPlObjNullHandle;
  108. const cmTlObj_t* top = _cmdIf->tlObjIdToPtr(m->objId);
  109. assert(top != NULL);
  110. if( top==NULL)
  111. return;
  112. unsigned parentObjId = (top!=NULL && top->ref!=NULL) ? top->ref->uid : cmInvalidId;
  113. const cmTlMidiEvt_t* mep = NULL;
  114. const cmTlAudioFile_t* afp = NULL;
  115. unsigned vwIdx = kAudioVwIdx;
  116. cmGrPlObjTypeId_t objTypeId = kRectGrPlId;
  117. cmReal_t x = top->begSmpIdx;
  118. cmReal_t y = -1.0;
  119. cmReal_t w = top->durSmpCnt;
  120. cmReal_t h = 2.0;
  121. const cmChar_t* label = top->name;
  122. unsigned flags = kNoDragGrPlFl;
  123. bool pedalFl = false;
  124. cmGrVExt_t wext;
  125. cmGrVExtSetNull(&wext);
  126. // convert cmTlUiMsg_t into parameters for a call to cmGrPlotObjCreate().
  127. switch( top->typeId )
  128. {
  129. case kMidiFileTlId:
  130. {
  131. vwIdx = kMidiVwIdx;
  132. y = 0;
  133. h = 127;
  134. flags |= kNoFillGrPlFl | kNoSelectGrPlFl;
  135. cmGrVExtSet(&wext,0,0,top->durSmpCnt,h);
  136. if( parentObjId != cmInvalidId )
  137. xAnchorObjH = cmGrPlotObjectIdToHandle( plH, parentObjId );
  138. //printf("midi file id:%i x:%f y:%f w:%f h:%f %s\n",m->objId,x,y,w,h,cmStringNullGuard(label));
  139. }
  140. break;
  141. case kMidiEvtTlId:
  142. {
  143. mep = _cmdIf->tlMidiEvtObjPtr(top);
  144. vwIdx = kMidiVwIdx;
  145. y = 127; // all non-note-on msg's get put to the top of the display
  146. h = 1;
  147. w = 100; // arbitrary msg duration
  148. xAnchorObjH = cmGrPlotObjectIdToHandle( plH, parentObjId );
  149. parentObjH = cmGrPlotObjectIdToHandle( plH, mep->midiFileObjId );
  150. assert( cmHandlesAreNotEqual(xAnchorObjH,cmGrPlObjNullHandle) );
  151. assert( cmHandlesAreNotEqual(parentObjH,cmGrPlObjNullHandle) );
  152. const cmMidiTrackMsg_t* mtm = mep->msg;
  153. cmMidiByte_t status = mtm->status;
  154. if( cmMidiIsNoteOn(status) )
  155. {
  156. y = mtm->u.chMsgPtr->d0;
  157. w = top->durSmpCnt;
  158. label = cmMidiToSciPitch(mtm->u.chMsgPtr->d0,NULL,0);
  159. }
  160. else
  161. {
  162. if( cmMidiIsSustainPedalDown(status,mtm->u.chMsgPtr->d0,mtm->u.chMsgPtr->d1) )
  163. {
  164. y = 64;
  165. w = top->durSmpCnt;
  166. flags += kNoFillGrPlFl;
  167. label = "Pedal";
  168. pedalFl= true;
  169. }
  170. else
  171. {
  172. if( status == kMetaStId )
  173. label = cmMidiMetaStatusToLabel(mtm->metaId);
  174. else
  175. label = cmMidiStatusToLabel(status);
  176. }
  177. }
  178. }
  179. break;
  180. case kAudioFileTlId:
  181. afp = _cmdIf->tlAudioFileObjPtr(top);
  182. if( parentObjId != cmInvalidId )
  183. xAnchorObjH = cmGrPlotObjectIdToHandle( plH, parentObjId );
  184. cmGrVExtSet(&wext,0,-1,top->durSmpCnt,h);
  185. //printf("audio file id:%i x:%f y:%f w:%f h:%f %s\n",m->objId,x,y,w,h,cmStringNullGuard(label));
  186. break;
  187. case kAudioEvtTlId:
  188. objTypeId = kVLineGrPlId;
  189. break;
  190. case kMarkerTlId:
  191. if( _cmdIf->tlMarkerObjPtr(top)->typeId == kMidiOnsetMarkTlId )
  192. vwIdx = kMidiVwIdx;
  193. objTypeId = kVLineGrPlId;
  194. xAnchorObjH = cmGrPlotObjectIdToHandle( plH, parentObjId );
  195. //flags |= kNoFillGrPlFl | kBorderSelGrPlFl | kNoFocusGrPlFl;
  196. w = 0;
  197. break;
  198. default:
  199. { assert(0); }
  200. }
  201. cmGrVwH_t vwH = cmGrPageViewHandle( pgH, vwIdx );
  202. cmGrH_t cvH = cmGrViewGrHandle( vwH );
  203. //const cmChar_t* anchLabel = cmHandlesAreEqual(xAnchorObjH,cmGrPlObjNullHandle) ? NULL : cmGrPlotObjLabel(xAnchorObjH);
  204. //const cmChar_t* parentLabel = cmHandlesAreEqual(parentObjH,cmGrPlObjNullHandle) ? NULL : cmGrPlotObjLabel(parentObjH);
  205. //printf("type:%i id:%i x:%f y:%f w:%f h:%f %s parent:%s xachor:%s\n",m->typeId,m->objId,x,y,w,h,cmStringNullGuard(label),cmStringNullGuard(parentLabel),cmStringNullGuard(anchLabel));
  206. // Create the object
  207. if( cmGrPlotObjCreate(plH, cvH, &objH, m->objId, parentObjH, xAnchorObjH, yAnchorObjH, objTypeId, flags, x, y, w, h, label, cmGrVExtIsNull(&wext)?NULL:&wext ) != kOkGrPlRC )
  208. {
  209. cmErrMsg(&_err,kInsertObjFailRC,"Insert failed on the object labelled '%s'.", cmStringNullGuard(top->name));
  210. return;
  211. }
  212. // set the plot obj's user arg. to be the time line element pointer
  213. cmGrPlotObjSetUserPtr(objH,(void*)top);
  214. // if the sequence is changing then invalidate the currently selected marker object
  215. if( m->seqId != _curSeqId )
  216. {
  217. _curSeqId = m->seqId;
  218. _selMarkPlotObjH = cmGrPlObjNullHandle;
  219. }
  220. // Modify the objects attributes
  221. if( cmGrPlotObjIsValid(objH) )
  222. {
  223. switch( top->typeId )
  224. {
  225. case kMidiEvtTlId:
  226. cmGrPlotObjSetFontSize(objH,9);
  227. if( pedalFl )
  228. cmGrPlotObjSetLineColor( objH, kEnablePlGrId, kGreenGrId );
  229. else
  230. cmGrPlotObjSetLineColor( objH, kEnablePlGrId, cmGrPlotObjFillColor(objH,kEnablePlGrId) );
  231. _setMidiEventLabels(objH,_getMenuCheckFlags());
  232. break;
  233. case kAudioFileTlId:
  234. if( afp->fn != NULL )
  235. _cmdIf->audioFileLoad(afp->fn,m->objId);
  236. break;
  237. case kMarkerTlId:
  238. {
  239. cmGrColor_t color = kBlackGrId;
  240. const cmTlMarker_t* mop = _cmdIf->tlMarkerObjPtr(top);
  241. switch( mop->typeId)
  242. {
  243. case kAudioMarkTlId:
  244. {
  245. unsigned n = cmGrColorMapEleCount( cvH, kGrDefaultColorMapId );
  246. unsigned ci = _markCnt++ % n;
  247. color = cmGrColorMap(cvH,kGrDefaultColorMapId)[ci];
  248. }
  249. break;
  250. case kAudioOnsetMarkTlId:
  251. color = kDarkRedGrId;
  252. break;
  253. case kMidiOnsetMarkTlId:
  254. color = kDarkBlueGrId;
  255. break;
  256. default:
  257. { assert(0); }
  258. }
  259. cmGrPlotObjSetLabelAttr( objH, kNorthJsGrFl | kWestJsGrFl | kTopJsGrFl | kRightJsGrFl, 0, color );
  260. cmGrPlotObjSetLineColor( objH, kEnablePlGrId, color );;
  261. cmGrPlotObjSetPhysExt(objH, 1, 0, 1, 0 );
  262. // create the marker end indicator
  263. objH = cmGrPlObjNullHandle;
  264. if( cmGrPlotObjCreate(plH, cvH, &objH, cmInvalidId, parentObjH, xAnchorObjH, yAnchorObjH, objTypeId, flags | kNoSelectGrPlFl, x+top->durSmpCnt, y, w, h, "", NULL ) != kOkGrPlRC )
  265. cmErrMsg(&_err,kInsertObjFailRC,"Insert failed ending marker line labelled '%s'.", cmStringNullGuard(top->name));
  266. else
  267. cmGrPlotObjSetLineColor( objH, kEnablePlGrId, color );
  268. }
  269. break;
  270. default:
  271. break;
  272. }
  273. }
  274. }
  275. cmTlUiMsgTypeId_t cmGrTlFltk::recvTimeLineMsg( const void* msg, unsigned msgByteCnt )
  276. {
  277. cmTlUiMsg_t m;
  278. cmTimeLineDecode(msg,msgByteCnt,&m);
  279. switch( m.msgId )
  280. {
  281. case kInitMsgTlId:
  282. {
  283. // remove all objects from all views
  284. cmGrPageClear(pageHandle());
  285. _srate = m.srate;
  286. _updateSeqMenu(m.seqCnt,m.seqId);
  287. }
  288. break;
  289. case kDoneMsgTlId:
  290. //size(w(),h()+1);
  291. break;
  292. case kFinalMsgTlId:
  293. break;
  294. case kInsertMsgTlId:
  295. _insertTimeLineObj(&m);
  296. break;
  297. default:
  298. { assert(0); }
  299. }
  300. return m.msgId;
  301. }
  302. void cmGrTlFltk::recvAudioFileLoad( unsigned fileId )
  303. {
  304. cmGrPlH_t plH = plotHandle();
  305. cmGrPlObjH_t grPlObjH = cmGrPlotObjectIdToHandle( plH, fileId );
  306. cmAfmFileH_t afH = _cmdIf->audioFileHandle( fileId );
  307. unsigned chIdx = 0;
  308. if( cmAfmFileIsValid(afH) == false )
  309. {
  310. cmErrMsg(&_err,kAudioObjFailRC,"Unable to locate audio file plot graphic object for id:%i.", fileId);
  311. return;
  312. }
  313. if( cmGrPlotAudioFileObjCreate( grPlObjH, afH, chIdx ) != kOkGrPlRC )
  314. {
  315. const cmChar_t* audioFn = cmAudioFileName(cmAfmFileHandle(afH));
  316. cmErrMsg(&_err,kAudioObjFailRC,"Create audio file graphic object failed for '%s'.", cmStringNullGuard(audioFn));
  317. return;
  318. }
  319. redraw();
  320. }
  321. void cmGrTlFltk::setTimeAxisMetric( timeAxisMetricId_t metricId )
  322. {
  323. unsigned timeLabelFuncId = cmInvalidId;
  324. switch( metricId )
  325. {
  326. case kSamplesMetricId: timeLabelFuncId = _samplesMetricId; break;
  327. case kSecondsMetricId: timeLabelFuncId = _secondsMetricId; break;
  328. default:
  329. { assert(0); }
  330. }
  331. cmGrPgH_t pgH = pageHandle();
  332. cmGrVwH_t vwH = cmGrPageViewHandle( pgH, kAudioVwIdx);
  333. cmGrAxH_t axH = cmGrViewAxisHandle( vwH, kTopGrIdx);
  334. cmGrViewSetLabelFunc( vwH, kTopGrIdx, timeLabelFuncId );
  335. cmGrAxisSetLabelFunc( axH, timeLabelFuncId );
  336. vwH = cmGrPageViewHandle( pgH, kMidiVwIdx);
  337. axH = cmGrViewAxisHandle( vwH, kBottomGrIdx);
  338. cmGrViewSetLabelFunc( vwH, kBottomGrIdx, timeLabelFuncId );
  339. cmGrAxisSetLabelFunc( axH, timeLabelFuncId );
  340. }
  341. void cmGrTlFltk::toggleMarkerText()
  342. {
  343. cmGrPlH_t plH = plotHandle();
  344. unsigned n = cmGrPlotObjectCount(plH);
  345. unsigned i;
  346. for(i=0; i<n; ++i)
  347. {
  348. cmGrPlObjH_t poH = cmGrPlotObjectIndexToHandle(plH,i);
  349. if( cmGrPlotObjIsValid(poH) )
  350. cmGrPlotObjTogCfgFlags(poH,kNoLabelGrPlFl);
  351. }
  352. redraw();
  353. }
  354. unsigned cmGrTlFltk::timeLineSelectedMarkerId() const
  355. {
  356. const cmTlObj_t* top;
  357. if( cmGrPlotObjIsValid(_selMarkPlotObjH)
  358. && cmIsFlag(cmGrPlotObjStateFlags(_selMarkPlotObjH),kSelectGrPlFl)
  359. && (top = (const cmTlObj_t*)cmGrPlotObjUserPtr(_selMarkPlotObjH)) != NULL
  360. && (top->typeId==kMarkerTlId || top->typeId==kMidiEvtTlId) )
  361. {
  362. return top->uid;
  363. }
  364. return cmInvalidId;
  365. }
  366. void cmGrTlFltk::setAudioFileCursor( unsigned smpIdx )
  367. {
  368. if( cmGrPlotObjIsValid(_selMarkPlotObjH) )
  369. {
  370. cmGrPlObjH_t poh;
  371. // get the audio file plot object handle
  372. if(cmGrPlotObjIsValid(poh = cmGrPlotObjXAnchor(_selMarkPlotObjH)))
  373. {
  374. cmGrVExt_t vext;
  375. cmGrVPt_t pt0,pt1;
  376. // get the canvas handle
  377. cmGrPgH_t pgH = pageHandle();
  378. cmGrVwH_t vwH = cmGrPageViewHandle( pgH, kAudioVwIdx );
  379. cmGrH_t cvH = cmGrViewGrHandle( vwH );
  380. cmGrPlotObjVExt(poh,&vext); // get the extents of the audio file object
  381. smpIdx += vext.loc.x; // offset the current sample index to put the index in global time
  382. cmGrViewExtents(cvH, &vext ); // get the current view extents
  383. cmGrVPtSet(&pt0,smpIdx,cmGrVExtMinY(&vext)); // setup the selection points
  384. cmGrVPtSet(&pt1,smpIdx,cmGrVExtMaxY(&vext));
  385. cmGrSetSelectPoints(cvH, &pt0, &pt1 ); // apply the selection points to form a cursor line
  386. }
  387. }
  388. }
  389. void cmGrTlFltk::selectBar( unsigned barNumb )
  390. {
  391. cmGrPlH_t plH = plotHandle();
  392. unsigned n = cmGrPlotObjectCount(plH);
  393. unsigned i;
  394. for(i=0; i<n; ++i)
  395. {
  396. cmGrPlObjH_t poH = cmGrPlotObjectIndexToHandle(plH,i);
  397. const cmTlMarker_t* mkp;
  398. const cmTlObj_t* top;
  399. if( cmGrPlotObjIsValid(poH)
  400. && (top = (const cmTlObj_t*)cmGrPlotObjUserPtr(poH)) != NULL
  401. && top->typeId == kMarkerTlId
  402. && (mkp = _cmdIf->tlMarkerObjPtr(top)) != NULL
  403. && mkp->bar == barNumb ) // <-------- there is an apparent weakness in selecting a marker based
  404. { // only on the bar number - because the intention is to pick a
  405. // bar line marker but it may be (i have not actually checked)
  406. // that other objects might have a given bar number but not be
  407. // a bar line object.
  408. unsigned flags = cmGrPlotObjStateFlags(poH);
  409. cmGrPlotObjSetStateFlags(poH,cmSetFlag(flags,kFocusGrPlFl | kSelectGrPlFl));
  410. redraw();
  411. _selMarkPlotObjH = poH;
  412. _cmdIf->onTimeLineMarkerSelected(mkp->obj.uid);
  413. }
  414. }
  415. }
  416. bool cmGrTlFltk::on_plot_object( cmGrPlotCbArg_t* arg )
  417. {
  418. if( arg->selId==kStateChangeGrPlId
  419. && cmIsFlag(arg->deltaFlags,kSelectGrPlFl)
  420. && cmIsFlag(cmGrPlotObjStateFlags(arg->objH),kSelectGrPlFl) )
  421. {
  422. const cmTlObj_t* top;
  423. if((top = (const cmTlObj_t*)cmGrPlotObjUserPtr(arg->objH)) != NULL)
  424. {
  425. const cmChar_t* s = NULL;
  426. switch(top->typeId)
  427. {
  428. case kAudioFileTlId:
  429. {
  430. const cmTlAudioFile_t* afp;
  431. if((afp = _cmdIf->tlAudioFileObjPtr(top)) != NULL && afp->fn != NULL)
  432. s = afp->fn;
  433. }
  434. break;
  435. case kMidiFileTlId:
  436. {
  437. const cmTlMidiFile_t* mfp;
  438. if((mfp = _cmdIf->tlMidiFileObjPtr(top)) != NULL && mfp->fn != NULL)
  439. s = mfp->fn;
  440. }
  441. break;
  442. case kMidiEvtTlId:
  443. {
  444. const cmTlMidiEvt_t* mep;
  445. if((mep = _cmdIf->tlMidiEvtObjPtr(top)) != NULL )
  446. {
  447. _selMarkPlotObjH = arg->objH;
  448. callback()(this,user_data());
  449. _cmdIf->onTimeLineMidiEvtSelected(mep->obj.uid);
  450. }
  451. }
  452. break;
  453. case kMarkerTlId:
  454. {
  455. const cmTlMarker_t* mkp;
  456. if((mkp = _cmdIf->tlMarkerObjPtr(top)) != NULL)
  457. {
  458. if(mkp->text != NULL)
  459. s = mkp->text;
  460. _selMarkPlotObjH = arg->objH;
  461. callback()(this,user_data());
  462. _cmdIf->onTimeLineMarkerSelected(mkp->obj.uid);
  463. }
  464. }
  465. break;
  466. default:
  467. break;
  468. }
  469. if( s == NULL )
  470. s = cmGrPlotObjLabel(arg->objH);
  471. if( s == NULL )
  472. s = "";
  473. setStatusText(s);
  474. }
  475. }
  476. return true;
  477. }
  478. void cmGrTlFltk::_s_seqMenuCallback( Fl_Widget* w, void* vp )
  479. {
  480. item_t* ip = (item_t*)vp;
  481. assert( ip->id < ip->p->_seqCnt );
  482. ip->p->_cmdIf->selectSequence(ip->id);
  483. }
  484. void cmGrTlFltk::_s_roundHashValueFunc( void* arg, cmChar_t* label, unsigned labelCharCnt, cmGrV_t value )
  485. {
  486. snprintf(label,labelCharCnt,"%i",(int)round(value));
  487. }
  488. void cmGrTlFltk::_s_minSecMsHashValueFunc( void* arg, cmChar_t* label, unsigned labelCharCnt, cmGrV_t value )
  489. {
  490. cmGrTlFltk* p = (cmGrTlFltk*)arg;
  491. int min=0,sec=0,ms=0;
  492. double smpPerMin = p->_srate * 60.0;
  493. double smpPerMs = p->_srate / 1000.0;
  494. if( value > smpPerMin )
  495. {
  496. min = (int)floor( value / smpPerMin );
  497. value -= min * smpPerMin;
  498. }
  499. if( value > p->_srate )
  500. {
  501. sec = (int)floor( value / p->_srate );
  502. value -= sec * p->_srate;
  503. }
  504. if( value > smpPerMs )
  505. {
  506. ms = (int)floor( value / smpPerMs );
  507. value -= ms * smpPerMs;
  508. }
  509. snprintf(label,labelCharCnt,"%i:%2i:%2i",min,sec,ms);
  510. }
  511. void cmGrTlFltk::_s_midiSciPitchValueFunc( void* arg, cmChar_t* label, unsigned labelCharCnt, cmGrV_t value )
  512. {
  513. assert( label != NULL && labelCharCnt > 0 );
  514. if( labelCharCnt > 0 )
  515. label[0] = 0;
  516. if( 0 <= value && value <= 127 )
  517. cmMidiToSciPitch((cmMidiByte_t)floor(value), label, labelCharCnt );
  518. else
  519. {
  520. if( labelCharCnt > 3 )
  521. strcpy(label,"?");
  522. }
  523. }
  524. void cmGrTlFltk::_updateSeqMenu(int newSeqCnt, unsigned seqId)
  525. {
  526. if(_seqMenuIdx == -1 )
  527. return;
  528. //const Fl_Menu_Item* seq_mip = _menuBar->menu() + _seqMenuIdx;
  529. //int sz = seq_mip->size();
  530. // if the count of time-line sequences does not match the new count of sequences
  531. if( 1 /*sz != newSeqCnt*/ )
  532. {
  533. int i;
  534. // erase the current sequence sub-menu
  535. _menuBar->clear_submenu(_seqMenuIdx);
  536. // create an array to link the menu items to the sequence control id's
  537. _seqItemArray = cmMemResizeZ(item_t,_seqItemArray,newSeqCnt);
  538. // create each menu items and item map record
  539. for(i=0; i<newSeqCnt; ++i)
  540. {
  541. _seqItemArray[i].id = i;
  542. _seqItemArray[i].p = this;
  543. _menuBar->insert(_seqMenuIdx + i + 1,cmTsPrintf("%i",i),0,_s_seqMenuCallback,_seqItemArray+i,FL_MENU_TOGGLE);
  544. }
  545. }
  546. // set the menu check boxes to indicate the selected sequence
  547. int i;
  548. for(i=0; i<newSeqCnt; ++i)
  549. {
  550. Fl_Menu_Item* mip = (Fl_Menu_Item*)_menuBar->menu() + _seqMenuIdx + i + 1;
  551. if( i == (int)seqId )
  552. mip->set();
  553. else
  554. mip->clear();
  555. }
  556. // track the current sequence count
  557. _seqCnt = newSeqCnt;
  558. }
  559. void cmGrTlFltk::_createMenu( )
  560. {
  561. _seqMenuIdx = _menuBar->add("Seq", 0,NULL,0,FL_SUBMENU);
  562. int idx = _menuBar->add("Time Line",0,NULL,0,FL_SUBMENU);
  563. const char* titleArray[] = { "Samples", "Seconds", "Marker Text", "Pitch", "Velocity","Id", "Gen Onset","Del Onset" };
  564. bool checkFl[] = { false, false, false, true, true, true, false, false };
  565. bool onFl[] = { false, false, false, true, false, false, false, false };
  566. int i;
  567. for(i=0; i<kMenuItemCnt; ++i)
  568. {
  569. int flag = checkFl[i] ? FL_MENU_TOGGLE : 0;
  570. _menuArray[i].id = i;
  571. _menuArray[i].p = this;
  572. _menuBar->insert(idx+1+i,titleArray[i],0,_s_menuCallback, _menuArray + i, flag );
  573. if( onFl[i] )
  574. {
  575. Fl_Menu_Item* mip = (Fl_Menu_Item*)_menuBar->menu() + idx + i + 1;
  576. mip->set();
  577. }
  578. }
  579. }
  580. bool cmGrTlFltk::_isMenuChecked( int id )
  581. {
  582. unsigned i;
  583. // locate the menu item assoc'd with id
  584. for(i=0; i<kMenuItemCnt; ++i)
  585. if( _menuArray[i].id == id )
  586. break;
  587. assert( i < kMenuItemCnt );
  588. int menuIdx;
  589. if(( menuIdx = _menuBar->find_index("Time Line")) == -1 )
  590. return false;
  591. // The menu items and _menuArray[] were initialized in the same order
  592. // therefore the offset from the base of both should be the same.
  593. Fl_Menu_Item* mip = (Fl_Menu_Item*)_menuBar->menu() + menuIdx + i + 1;
  594. assert( (item_t*)mip->user_data() == _menuArray + i );
  595. return mip->value() != 0;
  596. }
  597. unsigned cmGrTlFltk::_getMenuCheckFlags()
  598. {
  599. unsigned flags = 0;
  600. flags |= _isMenuChecked(kViewPitchMId) ? kPitchChkFl : 0;
  601. flags |= _isMenuChecked(kViewVelocityMId) ? kVelChkFl : 0;
  602. flags |= _isMenuChecked(kViewIdMId) ? kIdChkFl : 0;
  603. return flags;
  604. }
  605. void cmGrTlFltk::_setLabels()
  606. {
  607. cmGrPlH_t plH = plotHandle();
  608. unsigned flags = _getMenuCheckFlags();
  609. unsigned n = cmGrPlotObjectCount(plH);
  610. unsigned i;
  611. for(i=0; i<n; ++i)
  612. {
  613. cmGrPlObjH_t poH = cmGrPlotObjectIndexToHandle(plH,i);
  614. _setMidiEventLabels(poH,flags);
  615. }
  616. }
  617. void cmGrTlFltk::_setMidiEventLabels( cmGrPlObjH_t poH, unsigned flags)
  618. {
  619. cmTlObj_t* top;
  620. if( cmGrPlotObjIsValid(poH) && (top = (cmTlObj_t*)cmGrPlotObjUserPtr(poH))!=NULL && top->typeId==kMidiEvtTlId )
  621. {
  622. const cmMidiTrackMsg_t* mep = ((const cmTlMidiEvt_t*)top)->msg;
  623. if( mep->status == kNoteOnMdId )
  624. {
  625. int bufN = 255;
  626. cmChar_t buf[ bufN+1 ];
  627. buf[bufN] = 0;
  628. buf[0] = 0;
  629. if( cmIsFlag(flags,kPitchChkFl) )
  630. snprintf(buf+strlen(buf),bufN-strlen(buf),"%s ",cmMidiToSciPitch(mep->u.chMsgPtr->d0,NULL,0));
  631. if( cmIsFlag(flags,kVelChkFl) )
  632. snprintf(buf+strlen(buf),bufN-strlen(buf),"%i ",mep->u.chMsgPtr->d1);
  633. if( cmIsFlag(flags,kIdChkFl) )
  634. snprintf(buf+strlen(buf),bufN-strlen(buf),"%i ",mep->uid);
  635. cmGrPlotObjSetLabel(poH, buf );
  636. }
  637. }
  638. }
  639. void cmGrTlFltk::_s_menuCallback(Fl_Widget* w, void* arg )
  640. {
  641. item_t* ip = (item_t*)arg;
  642. cmGrTlFltk* p = ip->p;
  643. unsigned long id = ip->id;
  644. switch( id )
  645. {
  646. case kViewSamplesMId:
  647. case kViewSecondsMId:
  648. {
  649. cmGrTlFltk::timeAxisMetricId_t metricId = id==kViewSamplesMId ? cmGrTlFltk::kSamplesMetricId : cmGrTlFltk::kSecondsMetricId;
  650. p->setTimeAxisMetric( metricId );
  651. p->redraw();
  652. }
  653. break;
  654. case kViewMarkTextMId:
  655. p->toggleMarkerText();
  656. break;
  657. case kViewVelocityMId:
  658. case kViewPitchMId:
  659. case kViewIdMId:
  660. p->_setLabels();
  661. p->redraw();
  662. break;
  663. case kGenOnsetMarksMId:
  664. p->_cmdIf->generateOnsetMarks();
  665. break;
  666. case kDelOnsetMarksMId:
  667. p->_cmdIf->deleteOnsetMarks();
  668. break;
  669. }
  670. }