Programmable real-time audio signal processing application
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }