Browse Source

Initial commit

master
kevin 11 years ago
commit
b108da1911
100 changed files with 46695 additions and 0 deletions
  1. 75
    0
      Makefile.am
  2. 377
    0
      app/cmOnset.c
  3. 55
    0
      app/cmOnset.h
  4. 876
    0
      app/cmPickup.c
  5. 96
    0
      app/cmPickup.h
  6. 635
    0
      app/cmScore.c
  7. 82
    0
      app/cmScore.h
  8. 1542
    0
      app/cmTimeLine.c
  9. 217
    0
      app/cmTimeLine.h
  10. 910
    0
      cmApBuf.c
  11. 229
    0
      cmApBuf.h
  12. 1224
    0
      cmAudDsp.c
  13. 55
    0
      cmAudDsp.h
  14. 291
    0
      cmAudDspIF.c
  15. 167
    0
      cmAudDspIF.h
  16. 147
    0
      cmAudDspLocal.c
  17. 38
    0
      cmAudDspLocal.h
  18. 316
    0
      cmAudLabelFile.c
  19. 50
    0
      cmAudLabelFile.h
  20. 942
    0
      cmAudioAggDev.c
  21. 104
    0
      cmAudioAggDev.h
  22. 285
    0
      cmAudioBuf.c
  23. 63
    0
      cmAudioBuf.h
  24. 1696
    0
      cmAudioFile.c
  25. 305
    0
      cmAudioFile.h
  26. 561
    0
      cmAudioFileDev.c
  27. 74
    0
      cmAudioFileDev.h
  28. 483
    0
      cmAudioFileMgr.c
  29. 67
    0
      cmAudioFileMgr.h
  30. 355
    0
      cmAudioNrtDev.c
  31. 65
    0
      cmAudioNrtDev.h
  32. 799
    0
      cmAudioPort.c
  33. 148
    0
      cmAudioPort.h
  34. 280
    0
      cmAudioPortFile.c
  35. 47
    0
      cmAudioPortFile.h
  36. 1415
    0
      cmAudioSys.c
  37. 298
    0
      cmAudioSys.h
  38. 87
    0
      cmComplexTypes.h
  39. 1146
    0
      cmCsv.c
  40. 149
    0
      cmCsv.h
  41. 22
    0
      cmCtx.c
  42. 53
    0
      cmCtx.h
  43. 54
    0
      cmDocMain.h
  44. 137
    0
      cmErr.c
  45. 84
    0
      cmErr.h
  46. 2455
    0
      cmFeatFile.c
  47. 279
    0
      cmFeatFile.h
  48. 555
    0
      cmFile.c
  49. 203
    0
      cmFile.h
  50. 1285
    0
      cmFileSys.c
  51. 231
    0
      cmFileSys.h
  52. 81
    0
      cmFloatTypes.h
  53. 2210
    0
      cmFrameFile.c
  54. 360
    0
      cmFrameFile.h
  55. 2
    0
      cmGlobal.c
  56. 154
    0
      cmGlobal.h
  57. 1121
    0
      cmGnuPlot.c
  58. 102
    0
      cmGnuPlot.h
  59. 2670
    0
      cmGr.c
  60. 871
    0
      cmGr.h
  61. 530
    0
      cmGrDevCtx.c
  62. 196
    0
      cmGrDevCtx.h
  63. 1471
    0
      cmGrPage.c
  64. 164
    0
      cmGrPage.h
  65. 1223
    0
      cmGrPlot.c
  66. 260
    0
      cmGrPlot.h
  67. 180
    0
      cmGrPlotAudio.c
  68. 19
    0
      cmGrPlotAudio.h
  69. 4077
    0
      cmJson.c
  70. 504
    0
      cmJson.h
  71. 235
    0
      cmKeyboard.c
  72. 37
    0
      cmKeyboard.h
  73. 910
    0
      cmLex.c
  74. 139
    0
      cmLex.h
  75. 309
    0
      cmLib.c
  76. 63
    0
      cmLib.h
  77. 358
    0
      cmLinkedHeap.c
  78. 90
    0
      cmLinkedHeap.h
  79. 33
    0
      cmMain.c
  80. 136
    0
      cmMallocDebug.c
  81. 168
    0
      cmMallocDebug.h
  82. 369
    0
      cmMath.c
  83. 67
    0
      cmMath.h
  84. 699
    0
      cmMem.c
  85. 231
    0
      cmMem.h
  86. 227
    0
      cmMidi.c
  87. 140
    0
      cmMidi.h
  88. 1036
    0
      cmMidiFile.c
  89. 163
    0
      cmMidiFile.h
  90. 567
    0
      cmMidiFilePlay.c
  91. 59
    0
      cmMidiFilePlay.h
  92. 458
    0
      cmMidiPort.c
  93. 89
    0
      cmMidiPort.h
  94. 122
    0
      cmMsgProtocol.c
  95. 233
    0
      cmMsgProtocol.h
  96. 83
    0
      cmOp.c
  97. 30
    0
      cmOp.h
  98. 1149
    0
      cmPgmOpts.c
  99. 191
    0
      cmPgmOpts.h
  100. 0
    0
      cmPrefix.h

+ 75
- 0
Makefile.am View File

@@ -0,0 +1,75 @@
1
+
2
+
3
+cmHDR = 
4
+cmSRC = 
5
+
6
+cmHDR += src/libcm/cmErr.h src/libcm/cmCtx.h src/libcm/cmRpt.h src/libcm/cmGlobal.h src/libcm/cmComplexTypes.h src/libcm/cmFloatTypes.h src/libcm/cmPrefix.h
7
+cmSRC += src/libcm/cmErr.c src/libcm/cmCtx.c src/libcm/cmRpt.c src/libcm/cmGlobal.c 
8
+
9
+cmHDR += src/libcm/cmSerialize.h src/libcm/cmSymTbl.h src/libcm/cmFileSys.h src/libcm/cmFile.h src/libcm/cmMem.h src/libcm/cmTime.h src/libcm/cmPgmOpts.h
10
+cmSRC += src/libcm/cmSerialize.c src/libcm/cmSymTbl.c src/libcm/cmFileSys.c src/libcm/cmFile.c src/libcm/cmMem.c src/libcm/cmTime.c src/libcm/cmPgmOpts.c
11
+
12
+cmHDR += src/libcm/cmLib.h src/libcm/cmText.h src/libcm/cmMath.h src/libcm/cmOp.h src/libcm/cmGnuPlot.h src/libcm/cmKeyboard.h
13
+cmSRC += src/libcm/cmLib.c src/libcm/cmText.c src/libcm/cmMath.c src/libcm/cmOp.c src/libcm/cmGnuPlot.c src/libcm/cmKeyboard.c
14
+
15
+cmHDR += src/libcm/cmLinkedHeap.h src/libcm/cmMallocDebug.h src/libcm/cmLex.h src/libcm/cmJson.h src/libcm/cmPrefs.h src/libcm/cmStack.h
16
+cmSRC += src/libcm/cmLinkedHeap.c src/libcm/cmMallocDebug.c src/libcm/cmLex.c src/libcm/cmJson.c src/libcm/cmPrefs.c src/libcm/cmStack.c
17
+
18
+cmHDR += src/libcm/cmUdpPort.h src/libcm/cmUdpNet.h src/libcm/cmVirtNet.h
19
+cmSRC += src/libcm/cmUdpPort.c src/libcm/cmUdpNet.c src/libcm/cmVirtNet.c
20
+
21
+cmHDR += src/libcm/cmAudioPort.h src/libcm/cmApBuf.h src/libcm/cmAudioAggDev.h src/libcm/cmAudioNrtDev.h src/libcm/cmThread.h	
22
+cmSRC += src/libcm/cmAudioPort.c src/libcm/cmApBuf.c src/libcm/cmAudioAggDev.c src/libcm/cmAudioNrtDev.c src/libcm/cmThread.c
23
+
24
+cmHDR += src/libcm/cmMidiFilePlay.h src/libcm/cmMidiPort.h src/libcm/cmMidiFile.h src/libcm/cmMidi.h 
25
+cmSRC += src/libcm/cmMidiFilePlay.c src/libcm/cmMidiPort.c src/libcm/cmMidiFile.c src/libcm/cmMidi.c 
26
+
27
+cmHDR += src/libcm/cmAudioFile.h src/libcm/cmAudioFileMgr.h src/libcm/cmMsgProtocol.h src/libcm/cmAudioSys.h src/libcm/cmAudioPortFile.h src/libcm/cmAudioFileDev.h 
28
+cmSRC += src/libcm/cmAudioFile.c src/libcm/cmAudioFileMgr.c src/libcm/cmMsgProtocol.c src/libcm/cmAudioSys.c src/libcm/cmAudioPortFile.c src/libcm/cmAudioFileDev.c
29
+
30
+cmHDR += src/libcm/cmFrameFile.h src/libcm/cmFeatFile.h src/libcm/cmCsv.h src/libcm/cmAudLabelFile.h src/libcm/cmTagFile.h
31
+cmSRC += src/libcm/cmFrameFile.c src/libcm/cmFeatFile.c src/libcm/cmCsv.c src/libcm/cmAudLabelFile.c src/libcm/cmTagFile.c
32
+
33
+cmSRC += src/libcm/cmGr.c src/libcm/cmGrDevCtx.c src/libcm/cmGrPage.c src/libcm/cmGrPlot.c src/libcm/cmGrPlotAudio.c
34
+cmHDR += src/libcm/cmGr.h src/libcm/cmGrDevCtx.h src/libcm/cmGrPage.h src/libcm/cmGrPlot.h src/libcm/cmGrPlotAudio.h
35
+
36
+cmHDR +=  src/libcm/dsp/cmDspSys.h src/libcm/dsp/cmDspClass.h src/libcm/dsp/cmDspValue.h src/libcm/dsp/cmDspUi.h src/libcm/dsp/cmDspPreset.h src/libcm/dsp/cmDspNet.h
37
+cmSRC +=  src/libcm/dsp/cmDspSys.c src/libcm/dsp/cmDspClass.c src/libcm/dsp/cmDspValue.c src/libcm/dsp/cmDspUi.c src/libcm/dsp/cmDspPreset.c src/libcm/dsp/cmDspNet.c
38
+
39
+cmHDR += src/libcm/dsp/cmDspBuiltIn.h  src/libcm/dsp/cmDspFx.h 
40
+cmSRC += src/libcm/dsp/cmDspBuiltIn.c  src/libcm/dsp/cmDspFx.c 
41
+
42
+cmHDR += src/libcm/dsp/cmDspPgm.h src/libcm/dsp/cmDspKr.h src/libcm/dsp/cmDspPgmPP.h src/libcm/dsp/cmDspPgmPPMain.h
43
+cmSRC += src/libcm/dsp/cmDspPgm.c src/libcm/dsp/cmDspKr.c src/libcm/dsp/cmDspPgmPP.c src/libcm/dsp/cmDspPgmPPMain.c
44
+
45
+cmHDR += src/libcm/cmAudDsp.h src/libcm/cmAudDspIF.h src/libcm/cmAudDspLocal.h
46
+cmSRC += src/libcm/cmAudDsp.c src/libcm/cmAudDspIF.c src/libcm/cmAudDspLocal.c
47
+
48
+cmHDR += src/libcm/vop/cmVectOpsTemplateUndef.h src/libcm/vop/cmVectOpsTemplateHdr.h src/libcm/vop/cmVectOpsTemplateCode.h src/libcm/vop/cmVectOpsTemplateMain.h
49
+cmHDR += src/libcm/vop/cmVectOpsRIHdr.h src/libcm/vop/cmVectOpsRICode.h 
50
+cmHDR += src/libcm/vop/cmProcTemplateUndef.h src/libcm/vop/cmProcTemplateHdr.h src/libcm/vop/cmProcTemplateCode.h src/libcm/vop/cmProcTemplateMain.h
51
+cmHDR += src/libcm/vop/cmVectOps.h src/libcm/vop/cmProcTemplate.h
52
+
53
+cmSRC += src/libcm/vop/cmVectOps.c src/libcm/vop/cmProcTemplate.c
54
+
55
+cmHDR += src/libcm/cmProcObj.h src/libcm/cmProc.h src/libcm/cmProc2.h src/libcm/cmProc3.h src/libcm/cmProcTest.h
56
+cmSRC += src/libcm/cmProcObj.c src/libcm/cmProc.c src/libcm/cmProc2.c src/libcm/cmProc3.c src/libcm/cmProcTest.c
57
+
58
+
59
+cmHDR += src/libcm/app/cmOnset.h src/libcm/app/cmTimeLine.h src/libcm/app/cmScore.h src/libcm/app/cmPickup.h src/libcm/cmRbm.h
60
+cmSRC += src/libcm/app/cmOnset.c src/libcm/app/cmTimeLine.c src/libcm/app/cmScore.c src/libcm/app/cmPickup.c src/libcm/cmRbm.c
61
+
62
+
63
+if OS_LINUX
64
+ cmSRC += src/libcm/linux/cmFileSysLinux.c  src/libcm/linux/cmAudioPortAlsa.c src/libcm/linux/cmMidiAlsa.c
65
+ cmHDR += src/libcm/linux/cmFileSysLinux.h  src/libcm/linux/cmAudioPortAlsa.h
66
+endif
67
+
68
+if OS_OSX
69
+  cmSRC += src/libcm/osx/clock_gettime_stub.c src/libcm/osx/cmMidiOsx.c src/libcm/osx/cmAudioPortOsx.c src/libcm/osx/cmFileSysOsx.c
70
+  cmHDR += src/libcm/osx/clock_gettime_stub.h	
71
+endif
72
+
73
+
74
+
75
+

+ 377
- 0
app/cmOnset.c View File

@@ -0,0 +1,377 @@
1
+#include "cmGlobal.h"
2
+#include "cmFloatTypes.h"
3
+#include "cmComplexTypes.h"
4
+#include "cmRpt.h"
5
+#include "cmErr.h"
6
+#include "cmCtx.h"
7
+#include "cmMem.h"
8
+#include "cmMallocDebug.h"
9
+#include "cmLinkedHeap.h"
10
+#include "cmSymTbl.h"
11
+#include "cmAudioFile.h"
12
+#include "cmMidi.h"
13
+#include "cmFile.h"
14
+#include "cmMath.h"
15
+
16
+#include "cmProcObj.h"
17
+#include "cmProcTemplateMain.h"
18
+#include "cmProc.h"
19
+#include "cmProc2.h"
20
+#include "cmVectOps.h"
21
+
22
+#include "cmOnset.h"
23
+
24
+typedef struct
25
+{
26
+  cmErr_t        err;
27
+  cmOnsetCfg_t   cfg;
28
+
29
+  cmCtx*         ctxPtr;
30
+  cmAudioFileRd* afRdPtr;
31
+  cmPvAnl*       pvocPtr;
32
+  
33
+  cmAudioFileH_t afH; // output audio file
34
+  cmFileH_t      txH; // output text file
35
+
36
+  unsigned       frmCnt;   // spectral frame count
37
+  cmReal_t*      sfV;      // sfV[frmCnt] spectral flux vector
38
+  cmReal_t*      dfV;      // dfV[frmCnt] onset function vector
39
+
40
+  cmAudioFileInfo_t afInfo;
41
+  unsigned          fftSmpCnt;
42
+  unsigned          hopSmpCnt;
43
+  unsigned          binCnt;
44
+
45
+} _cmOn_t;
46
+
47
+cmOnH_t cmOnsetNullHandle = cmSTATIC_NULL_HANDLE;
48
+
49
+_cmOn_t* _cmOnsetHandleToPtr( cmOnH_t h )
50
+{
51
+  _cmOn_t* p = (_cmOn_t*)h.h;
52
+  assert(p!=NULL);
53
+  return p;
54
+}
55
+
56
+cmOnRC_t _cmOnsetFinalize( _cmOn_t* p )
57
+{
58
+  cmOnRC_t rc = kOkOnRC;
59
+
60
+  if( cmPvAnlFree(&p->pvocPtr) != cmOkRC )
61
+  {
62
+    rc = cmErrMsg(&p->err,kDspProcFailOnRC,"Phase voocoder free failed.");
63
+    goto errLabel;
64
+  }
65
+
66
+  if( cmAudioFileRdFree(&p->afRdPtr) != cmOkRC )
67
+  {
68
+    rc = cmErrMsg(&p->err,kDspProcFailOnRC,"Audio file reader failed.");
69
+    goto errLabel;
70
+  }
71
+
72
+  if( cmCtxFree(&p->ctxPtr) != cmOkRC )
73
+  {
74
+    rc = cmErrMsg(&p->err,kDspProcFailOnRC,"Context proc failed.");
75
+    goto errLabel;
76
+  }
77
+
78
+  cmMemPtrFree(&p->sfV);
79
+  cmMemPtrFree(&p->dfV);
80
+  cmMemPtrFree(&p);
81
+
82
+ errLabel:
83
+  return rc;
84
+}
85
+
86
+cmOnRC_t cmOnsetInitialize( cmCtx_t* c, cmOnH_t* hp )
87
+{
88
+  cmOnRC_t rc;
89
+  if((rc = cmOnsetFinalize(hp)) != kOkOnRC )
90
+    return rc;
91
+
92
+  _cmOn_t* p = cmMemAllocZ(_cmOn_t,1);
93
+  cmErrSetup(&p->err,&c->rpt,"Onset");
94
+
95
+  // create the proc context object
96
+  if((p->ctxPtr  = cmCtxAlloc(NULL,&c->rpt,cmLHeapNullHandle,cmSymTblNullHandle)) == NULL )
97
+  {
98
+    rc = cmErrMsg(&p->err,kDspProcFailOnRC, "The ctx compoenent allocation failed.");
99
+    goto errLabel;
100
+  }
101
+
102
+  // create the audio file reader
103
+  if((p->afRdPtr = cmAudioFileRdAlloc( p->ctxPtr, NULL, 0, NULL, cmInvalidIdx, 0, cmInvalidIdx )) == NULL )
104
+  {
105
+    rc =  cmErrMsg(&p->err,kDspProcFailOnRC, "The audio file reader allocation failed.");
106
+    goto errLabel;
107
+  }
108
+
109
+  // create the phase vocoder 
110
+  if((p->pvocPtr = cmPvAnlAlloc( p->ctxPtr, NULL, 0, 0, 0, 0, 0 )) == NULL )
111
+  {
112
+    rc = cmErrMsg(&p->err,kDspProcFailOnRC,"The phase vocoder allocation failed.");
113
+    goto errLabel;
114
+  }
115
+
116
+  hp->h = p;
117
+
118
+ errLabel:
119
+  if( rc != kOkOnRC )
120
+    _cmOnsetFinalize(p);
121
+
122
+  return rc;
123
+}
124
+
125
+cmOnRC_t cmOnsetFinalize( cmOnH_t* hp )
126
+{
127
+  cmOnRC_t rc = kOkOnRC;
128
+
129
+  if( hp==NULL || cmOnsetIsValid(*hp)==false )
130
+    return kOkOnRC;
131
+
132
+  _cmOn_t* p = _cmOnsetHandleToPtr(*hp);
133
+
134
+  rc = _cmOnsetFinalize(p);
135
+
136
+  return rc;
137
+}
138
+
139
+bool     cmOnsetIsValid( cmOnH_t h )
140
+{ return h.h!=NULL; }
141
+
142
+cmOnRC_t _cmOnsetExec( _cmOn_t* p, unsigned chCnt )
143
+{
144
+  cmOnRC_t    rc     = kOkOnRC;
145
+  int         fi     = 0;
146
+  unsigned    binCnt = p->binCnt; //p->pvocPtr->binCnt;
147
+  cmReal_t    mag0V[ binCnt ];
148
+  cmSample_t  out0V[ p->hopSmpCnt ];
149
+  cmSample_t  out1V[ p->hopSmpCnt ];
150
+  cmSample_t* aoutV[chCnt];
151
+  double      prog   = 0.1;
152
+  cmReal_t    b0     = 1;
153
+  cmReal_t    b[]    = {1  };
154
+  cmReal_t    a[]    = {p->cfg.filtCoeff};
155
+  cmReal_t    d[]    = {0};
156
+  cmReal_t    maxVal = 0;
157
+
158
+  if( chCnt > 0 )
159
+    aoutV[0] = out0V;
160
+
161
+  if( chCnt > 1 )
162
+    aoutV[1] = out1V;
163
+
164
+  cmVOR_Zero(mag0V,binCnt);
165
+
166
+  // for each frame - read the next block of audio
167
+  for(; fi<p->frmCnt && cmAudioFileRdRead(p->afRdPtr) != cmEofRC; ++fi )
168
+  {
169
+    // calc the spectrum 
170
+    while( cmPvAnlExec(p->pvocPtr, p->afRdPtr->outV, p->afRdPtr->outN ) )
171
+    {
172
+      unsigned i;
173
+
174
+      // calc the spectral flux into sfV[fi].
175
+      cmReal_t sf = 0;
176
+      for(i=0; i<binCnt; ++i)
177
+      {
178
+        cmReal_t m1 = p->pvocPtr->magV[i] * 2.0;
179
+
180
+        if( m1 > maxVal )
181
+          maxVal = m1;
182
+
183
+        cmReal_t dif  = m1 - mag0V[i];   // calc. spectral flux
184
+        if( dif > 0 )                                            
185
+          sf += dif;                     // accum. flux
186
+        mag0V[i] = m1;                   // store magn. for next frame
187
+      }
188
+
189
+      p->sfV[fi] = sf;
190
+
191
+      // filter the spectral flux 
192
+      cmVOR_Filter( p->sfV + fi, 1, &sf, 1, b0, b, a, d, 1 );
193
+
194
+      if( fi >= prog*p->frmCnt )
195
+      {
196
+        cmRptPrintf(p->err.rpt,"%i ",lround(prog*10));
197
+        prog += 0.1;      
198
+      }
199
+    }
200
+  }
201
+
202
+  p->frmCnt = fi;
203
+
204
+  // normalize the spectral flux vector
205
+  cmReal_t mean   = cmVOR_Mean(p->sfV,p->frmCnt);
206
+  cmReal_t stdDev = sqrt(cmVOR_Variance(p->sfV, p->frmCnt, &mean ));
207
+  cmVOR_SubVS(p->sfV,p->frmCnt,mean);
208
+  cmVOR_DivVS(p->sfV,p->frmCnt,stdDev);
209
+  cmReal_t maxSf = cmVOR_Max(p->sfV,p->frmCnt,1);
210
+  prog = 0.1;
211
+
212
+  printf("max:%f ",maxVal);
213
+  printf("mean:%f max:%f sd:%f\n",mean,maxSf,stdDev);
214
+
215
+  // Pick peaks from the onset detection function using a subset
216
+  // of the rules from Dixon, 2006, Onset Detection Revisited.
217
+  // locate the onsets and store them in dfV[]
218
+  for(fi=0; fi<p->frmCnt; ++fi)
219
+  {
220
+    int bi = cmMax(0,         fi - p->cfg.wndFrmCnt);       // begin wnd index
221
+    int ei = cmMin(p->frmCnt, fi + p->cfg.wndFrmCnt);       // end wnd index
222
+    int nn = ei - bi;                                       // wnd frm cnt 
223
+    int wi = fi < p->cfg.wndFrmCnt ? fi : p->cfg.wndFrmCnt; // cur wnd index
224
+
225
+    // initialize the out
226
+    cmVOS_Fill(out1V,p->hopSmpCnt,p->sfV[fi]/maxSf);
227
+    cmVOS_Zero(out0V,p->hopSmpCnt);
228
+
229
+    p->dfV[fi] = 0;
230
+
231
+    // if cur index is a peak in the window
232
+    if( cmVOR_MaxIndex(p->sfV + bi, nn, 1 ) == wi )
233
+    {
234
+      // calc an extended window going backwards in time
235
+      bi = cmMax(0, fi - p->cfg.wndFrmCnt * p->cfg.preWndMult );
236
+      nn = ei - bi;
237
+
238
+      // if the cur value is greater than the mean of the extended window plus a threshold
239
+      if( p->sfV[fi] > cmVOR_Mean(p->sfV + bi, nn ) + p->cfg.threshold )
240
+      {
241
+        p->dfV[fi]              = p->sfV[fi];
242
+        out0V[ p->hopSmpCnt/2 ] = p->sfV[fi]/maxSf;
243
+
244
+        unsigned smpIdx = fi * p->hopSmpCnt + p->hopSmpCnt/2;
245
+
246
+        // write the output text file
247
+        if( cmFilePrintf(p->txH, "[ %i, %f ]\n", smpIdx, p->sfV[fi] ) != kOkFileRC )
248
+        {
249
+          rc = cmErrMsg(&p->err,kDspTextFileFailOnRC,"Text output write to '%s' failed.", cmFileName(p->txH));
250
+          goto errLabel;
251
+        }
252
+      }
253
+    }
254
+
255
+    // write the output audio file
256
+    if( cmAudioFileWriteFloat(p->afH, p->hopSmpCnt, chCnt, aoutV ) != kOkAfRC )
257
+    {
258
+      rc = cmErrMsg(&p->err,kDspAudioFileFailOnRC,"Audio file write to '%s' failed.",cmAudioFileName(p->afH));
259
+      goto errLabel;
260
+    }
261
+    
262
+    if( fi >= prog*p->frmCnt )
263
+    {
264
+      cmRptPrintf(p->err.rpt,"%i ",lround(prog*10));
265
+      prog += 0.1;      
266
+    }
267
+
268
+  }
269
+    
270
+ errLabel:
271
+  
272
+  return rc;
273
+}
274
+
275
+cmOnRC_t cmOnsetExec( cmOnH_t h, const cmOnsetCfg_t* cfg, const cmChar_t* inAudioFn, const cmChar_t* outAudioFn, const cmChar_t* outTextFn )
276
+{
277
+  cmOnRC_t rc            = kOkOnRC;
278
+  _cmOn_t* p             = _cmOnsetHandleToPtr(h);
279
+  unsigned audioOutChCnt = 2;
280
+  p->cfg = *cfg;
281
+
282
+  // get the audio file header information
283
+  if( cmAudioFileGetInfo(inAudioFn, &p->afInfo, p->err.rpt ) != kOkAfRC )
284
+  {
285
+    rc =  cmErrMsg(&p->err,kDspProcFailOnRC,"The audio file open failed on '%s'.",cmStringNullGuard(inAudioFn));
286
+    goto errLabel;
287
+  }
288
+
289
+  p->fftSmpCnt = cmNearPowerOfTwo( (unsigned)floor( p->cfg.wndMs * p->afInfo.srate / 1000.0 ) );
290
+  p->hopSmpCnt = p->fftSmpCnt / p->cfg.hopFact;
291
+  p->binCnt    = cmMin(p->fftSmpCnt/2 + 1, floor(p->cfg.maxFrqHz / (p->afInfo.srate / p->fftSmpCnt)));
292
+  p->frmCnt    = (p->afInfo.frameCnt - p->fftSmpCnt) / p->hopSmpCnt;
293
+  p->sfV       = cmMemResizeZ(cmReal_t,p->sfV,p->frmCnt);
294
+  p->dfV       = cmMemResizeZ(cmReal_t,p->dfV,p->frmCnt);
295
+
296
+  // initialize the audio file reader
297
+  if( cmAudioFileRdOpen( p->afRdPtr, p->hopSmpCnt, inAudioFn, p->cfg.audioChIdx, 0, cmInvalidIdx ) != cmOkRC )
298
+  {
299
+    rc =  cmErrMsg(&p->err,kDspProcFailOnRC, "The audio file reader open failed.");
300
+    goto errLabel;
301
+  }
302
+
303
+  // initialize the phase vocoder 
304
+  if( cmPvAnlInit( p->pvocPtr, p->hopSmpCnt, p->afInfo.srate, p->fftSmpCnt, p->hopSmpCnt, kNoCalcHzPvaFl ) != cmOkRC )
305
+  {
306
+    rc = cmErrMsg(&p->err,kDspProcFailOnRC," The phase vocoder initialization failed.");
307
+    goto errLabel;
308
+  }
309
+
310
+  // initalize the audio output file
311
+  if( outAudioFn != NULL )
312
+    if( cmAudioFileIsValid( p->afH = cmAudioFileNewCreate( outAudioFn, p->afInfo.srate, p->afInfo.bits, audioOutChCnt, NULL, p->err.rpt)) == false )
313
+    {
314
+      rc = cmErrMsg(&p->err,kDspAudioFileFailOnRC, "The audio output file '%s' could not be opened.", outAudioFn);
315
+      goto errLabel;
316
+    }
317
+
318
+  // open the text output file
319
+  if( outTextFn != NULL )
320
+  {
321
+    if( cmFileOpen( &p->txH, outTextFn, kWriteFileFl, p->err.rpt ) != kOkFileRC )
322
+    {
323
+      rc = cmErrMsg(&p->err,kDspTextFileFailOnRC, "The text output file '%s' could not be opened.",outTextFn);
324
+      goto errLabel;
325
+    }
326
+
327
+    cmFilePrint(p->txH,"{\n onsetArray : \n[\n");
328
+  }
329
+
330
+  rc = _cmOnsetExec(p,audioOutChCnt);
331
+
332
+ errLabel:
333
+  // close the output audio file
334
+  if( cmAudioFileDelete(&p->afH) != kOkAfRC )
335
+    rc = cmErrMsg(&p->err,kDspAudioFileFailOnRC,"The audio file close failed.");
336
+
337
+  // close the text file
338
+  if( cmFileIsValid(p->txH) )
339
+  {
340
+    cmFilePrint(p->txH,"]\n}\n");
341
+
342
+    if( cmFileClose(&p->txH) != kOkFileRC )
343
+      rc = cmErrMsg(&p->err,kDspTextFileFailOnRC,"The text file close failed.");
344
+  }
345
+  return rc;  
346
+}
347
+
348
+
349
+cmOnRC_t cmOnsetTest( cmCtx_t* c )
350
+{
351
+  cmOnsetCfg_t    cfg;
352
+  cmOnH_t         h          = cmOnsetNullHandle;
353
+  cmOnRC_t        rc         = kOkOnRC;
354
+  const cmChar_t* inAudioFn  = "/home/kevin/temp/onset0.wav";
355
+  const cmChar_t* outAudioFn = "/home/kevin/temp/mas/mas0.aif";
356
+  const cmChar_t* outTextFn  = "/home/kevin/temp/mas/mas0.txt";
357
+
358
+  cfg.wndMs      = 42;
359
+  cfg.hopFact    = 4;
360
+  cfg.audioChIdx = 0;
361
+  cfg.wndFrmCnt  = 3;
362
+  cfg.preWndMult = 3;
363
+  cfg.threshold  = 0.6;
364
+  cfg.maxFrqHz   = 24000;
365
+  cfg.filtCoeff  = -0.7;
366
+
367
+  if((rc = cmOnsetInitialize(c,&h)) != kOkOnRC )
368
+    goto errLabel;
369
+  
370
+  rc = cmOnsetExec(h,&cfg,inAudioFn,outAudioFn,outTextFn);
371
+  
372
+ errLabel:
373
+  cmOnsetFinalize(&h);
374
+
375
+  return rc;
376
+  
377
+}

+ 55
- 0
app/cmOnset.h View File

@@ -0,0 +1,55 @@
1
+#ifndef cmOnset_h
2
+#define cmOnset_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkOnRC = cmOkRC,
11
+    kDspProcFailOnRC,
12
+    kDspAudioFileFailOnRC,
13
+    kDspTextFileFailOnRC,
14
+  };
15
+
16
+  typedef cmRC_t     cmOnRC_t;
17
+  typedef cmHandle_t cmOnH_t;
18
+
19
+  typedef struct
20
+  {
21
+    double   wndMs;
22
+    unsigned hopFact;
23
+    unsigned audioChIdx;
24
+
25
+    unsigned wndFrmCnt;   // 
26
+    double   preWndMult;  //
27
+    double   threshold;   //
28
+    double   maxFrqHz;    //
29
+    double   filtCoeff;   //
30
+    
31
+  } cmOnsetCfg_t;
32
+
33
+  extern cmOnH_t cmOnsetNullHandle;
34
+
35
+  cmOnRC_t cmOnsetInitialize( cmCtx_t* c, cmOnH_t* hp );
36
+
37
+  cmOnRC_t cmOnsetFinalize( cmOnH_t* hp );
38
+
39
+  bool     cmOnsetIsValid( cmOnH_t h );
40
+
41
+  cmOnRC_t cmOnsetExec( 
42
+    cmOnH_t h, 
43
+    const cmOnsetCfg_t* cfg, 
44
+    const cmChar_t* inAudioFn, 
45
+    const cmChar_t* outAudioFn, 
46
+    const cmChar_t* outTextFn );
47
+
48
+  cmOnRC_t cmOnsetTest( cmCtx_t* c );
49
+
50
+#ifdef __cplusplus
51
+}
52
+#endif
53
+
54
+
55
+#endif

+ 876
- 0
app/cmPickup.c View File

@@ -0,0 +1,876 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmComplexTypes.h"
5
+#include "cmRpt.h"
6
+#include "cmErr.h"
7
+#include "cmCtx.h"
8
+#include "cmMem.h"
9
+#include "cmMallocDebug.h"
10
+#include "cmLinkedHeap.h"
11
+#include "cmSymTbl.h"
12
+#include "cmJson.h"
13
+#include "cmMidi.h"
14
+#include "cmAudioFile.h"
15
+#include "cmFile.h"
16
+#include "cmFileSys.h"
17
+#include "cmProcObj.h"
18
+#include "cmProcTemplate.h"
19
+#include "cmVectOpsTemplateMain.h"
20
+#include "cmProc.h"
21
+#include "cmProc2.h"
22
+#include "cmProc3.h"
23
+#include "cmPickup.h"
24
+#include "cmAudLabelFile.h"
25
+
26
+enum
27
+{
28
+  kRmsPuId,
29
+  kMedPuId,
30
+  kDifPuId,
31
+  kAvgPuId,
32
+  kOnsPuId,
33
+  kFltPuId,
34
+  kSupPuId,
35
+  kAtkPuId,
36
+  kRlsPuId,
37
+  kSegPuId,
38
+  kPuCnt
39
+};
40
+
41
+typedef struct
42
+{
43
+  cmErr_t   err;
44
+  unsigned  chCnt;
45
+  cmPuCh_t* chArray;
46
+  cmCtx_t   ctx;    // stored ctx used by cmAudLabelFileAllocOpen()
47
+  
48
+  // cmProc objects
49
+  cmCtx*             ctxp;
50
+  cmAudioFileRd*     afrp;
51
+  cmShiftBuf*        sbp;
52
+  cmGateDetect2*     gdp;
53
+  cmBinMtxFile_t*    mfp;
54
+
55
+  const cmChar_t*    inAudFn;
56
+  const cmChar_t*    inLabelFn;
57
+  const cmChar_t*    outMtx0Fn;
58
+  const cmChar_t*    outMtx1Fn;
59
+  const cmChar_t*    outAudFn;
60
+  cmReal_t           hopMs;
61
+
62
+  cmGateDetectParams gd0Args;
63
+  cmGateDetectParams gd1Args;
64
+} cmPu_t;
65
+
66
+cmPuH_t cmPuNullHandle = cmSTATIC_NULL_HANDLE;
67
+
68
+cmPu_t* _cmPuHandleToPtr( cmPuH_t h )
69
+{
70
+  cmPu_t* p = (cmPu_t*)h.h;
71
+  assert(p!=NULL);
72
+  return p;
73
+}
74
+
75
+cmPuRC_t cmPuAlloc( cmCtx_t* ctx, cmPuH_t* hp )
76
+{
77
+  cmPuRC_t rc;
78
+  if((rc = cmPuFree(hp)) != kOkPuRC )
79
+    return rc;
80
+
81
+  cmPu_t* p = cmMemAllocZ(cmPu_t,1);
82
+  cmErrSetup(&p->err,&ctx->rpt,"Pickup");
83
+ 
84
+  p->ctx = *ctx;
85
+  hp->h = p;
86
+
87
+  return rc;
88
+}
89
+
90
+
91
+cmPuRC_t cmPuFree( cmPuH_t* hp )
92
+{
93
+  if( hp == NULL || cmPuIsValid(*hp) == false )
94
+    return kOkPuRC;
95
+
96
+  cmPu_t* p = _cmPuHandleToPtr(*hp);
97
+
98
+  cmMemPtrFree(&p->chArray);
99
+  cmMemPtrFree(&p);
100
+  hp->h = NULL;
101
+  return kOkPuRC;
102
+}
103
+
104
+bool     cmPuIsValid( cmPuH_t h )
105
+{ return h.h != NULL; }
106
+  
107
+
108
+cmPuRC_t _cmPuReadLabelsAndCreateArray( cmPu_t* p, const cmChar_t* labelFn, cmReal_t srate )
109
+{
110
+  cmPuRC_t rc = kOkPuRC;
111
+  cmAlfH_t h  = cmAlfNullHandle;
112
+  unsigned i;
113
+
114
+  if( cmAudLabelFileAllocOpen(&p->ctx, &h, labelFn) != kOkAlfRC )
115
+    return cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune audio label file open failed on '%s'",cmStringNullGuard(labelFn));
116
+  
117
+  if((p->chCnt   = cmAudLabelFileCount(h)) == 0 )
118
+  {
119
+    rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune audio label file '%s' does not contain any segment labels.",cmStringNullGuard(labelFn));
120
+    goto errLabel;
121
+  }
122
+
123
+  p->chArray = cmMemResizeZ(cmPuCh_t,p->chArray,p->chCnt);
124
+
125
+  for(i=0; i<p->chCnt; ++i)
126
+  {
127
+    const cmAlfLabel_t* lp;
128
+    if(( lp = cmAudLabelFileLabel(h,i)) == NULL )
129
+    {
130
+      rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune label in '%s' at row %i could not be read.",cmStringNullGuard(labelFn),i+1);
131
+      goto errLabel;
132
+    }
133
+
134
+    p->chArray[i].begSmpIdx = floor(srate * lp->begSecs);
135
+    p->chArray[i].endSmpIdx = p->chArray[i].begSmpIdx; // default the segment to have 0 length.
136
+    
137
+  }
138
+
139
+ errLabel:
140
+  if( cmAudLabelFileFree(&h) != kOkAlfRC )
141
+    rc = cmErrMsg(&p->err,kAlfFileFailPuRC,"The auto-tune label file close failed.");
142
+
143
+  return rc;
144
+}
145
+
146
+cmPuRC_t      _cmPuWriteMtxFile(cmPu_t* p, bool segFl )
147
+{
148
+  cmPuRC_t rc = kOkPuRC;
149
+
150
+  cmReal_t outV[ kPuCnt ];
151
+  outV[ kRmsPuId ] = p->gdp->rms;
152
+  outV[ kMedPuId ] = p->gdp->med;
153
+  outV[ kDifPuId ] = p->gdp->dif;
154
+  outV[ kAvgPuId ] = p->gdp->avg;
155
+  outV[ kOnsPuId ] = p->gdp->ons;
156
+  outV[ kFltPuId ] = p->gdp->flt;
157
+  outV[ kSupPuId ] = p->gdp->sup;
158
+  outV[ kAtkPuId ] = p->gdp->onFl;
159
+  outV[ kRlsPuId ] = p->gdp->offFl;
160
+  outV[ kSegPuId ] = segFl;
161
+      
162
+  // write the output file - plot with cmGateDetectPlot.m
163
+  if( cmBinMtxFileExecR(p->mfp,outV,kPuCnt) != cmOkRC )
164
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Matrix file write failed.");
165
+   
166
+  return rc;
167
+}
168
+
169
+void _cmPuCalcGains( cmPu_t* p )
170
+{
171
+  unsigned i;
172
+  cmReal_t avg = 0;
173
+
174
+  if( p->chCnt == 0 )
175
+    return;
176
+
177
+  for(i=0; i<p->chCnt; ++i)
178
+    avg += p->chArray[i].gateMaxAvg;
179
+
180
+  avg /= p->chCnt;
181
+
182
+  for(i=0; i<p->chCnt; ++i)
183
+  {
184
+    cmReal_t d = p->chArray[i].gateMaxAvg==0 ? 1.0 : p->chArray[i].gateMaxAvg;
185
+    p->chArray[i].gain = avg / d;
186
+  }
187
+}
188
+
189
+cmPuCh_t* _cmPuIncrCh( cmPu_t* p, cmPuCh_t* chp, unsigned* segSmpIdxPtr )
190
+{
191
+  if( *segSmpIdxPtr != p->chArray[0].begSmpIdx )
192
+    ++chp;           
193
+
194
+  if( chp >= p->chArray + p->chCnt )
195
+    return NULL;
196
+
197
+  if( chp+1 == p->chArray + p->chCnt )
198
+    *segSmpIdxPtr = p->afrp->info.frameCnt;
199
+  else
200
+    *segSmpIdxPtr = (chp+1)->begSmpIdx;
201
+
202
+  return chp;
203
+}
204
+
205
+cmPuRC_t _cmPuCalcRerunGateDetectors( 
206
+  cmPu_t*                   p,
207
+  const cmChar_t*           outMtxFn,
208
+  const cmChar_t*           outAudFn,
209
+  const cmGateDetectParams* gdArgs, 
210
+  unsigned                  procSmpCnt, 
211
+  unsigned                  wndSmpCnt, 
212
+  unsigned                  hopSmpCnt )
213
+{
214
+  cmPuRC_t       rc         = kOkPuRC;
215
+  cmAudioFileWr* afwp       = NULL;
216
+  unsigned       outChCnt   = 1;
217
+  unsigned       outChIdx   = 0;
218
+  unsigned       bitsPerSmp = 16;
219
+  unsigned       smpIdx     = 0;
220
+  cmSample_t*    smpV       = NULL;
221
+  
222
+
223
+  // rewind the audio file reader
224
+  if( cmAudioFileRdSeek(p->afrp,0) != cmOkRC )
225
+  {
226
+    cmErrMsg(&p->err,kProcFailPuRC,"Audio file seek failed.");
227
+    goto errLabel;
228
+  }
229
+
230
+  // reset the shift buffer
231
+  if( cmShiftBufInit( p->sbp, procSmpCnt, wndSmpCnt, hopSmpCnt ) != cmOkRC )
232
+  {
233
+    cmErrMsg(&p->err,kProcFailPuRC,"Shift buffer reset failed.");
234
+    goto errLabel;
235
+  }
236
+
237
+  // reset the gate detector
238
+  if( cmGateDetectInit2( p->gdp, procSmpCnt, gdArgs ) != cmOkRC )
239
+  {
240
+    cmErrMsg(&p->err,kProcFailPuRC,"Gate detector reset failed.");
241
+    goto errLabel;
242
+  }
243
+
244
+  // create an new matrix output file
245
+  if( cmBinMtxFileInit( p->mfp, outMtxFn ) != cmOkRC )
246
+  {
247
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Output matrix file '%s' initialization failed.",cmStringNullGuard(outMtxFn));
248
+    goto errLabel;
249
+  }
250
+
251
+  // create an audio output file
252
+  if( (afwp = cmAudioFileWrAlloc(p->ctxp, NULL, procSmpCnt, outAudFn, p->afrp->info.srate, outChCnt,  bitsPerSmp )) == NULL )
253
+  {
254
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Output audio file '%s' initialization failed.",cmStringNullGuard(outAudFn));
255
+    goto errLabel;
256
+  }
257
+  
258
+  smpV = cmMemAllocZ(cmSample_t,procSmpCnt);
259
+
260
+  cmPuCh_t* chp      = p->chArray;
261
+  unsigned segSmpIdx = chp->begSmpIdx; 
262
+  bool      segFl    = false;
263
+
264
+  // for each procSmpCnt samples
265
+  for(; cmAudioFileRdRead(p->afrp) != cmEofRC; smpIdx += procSmpCnt )
266
+  {
267
+    // apply auto-gain to the audio vector
268
+    cmVOS_MultVVS(smpV,p->afrp->outN,p->afrp->outV,chp->gain);
269
+
270
+    // is this a segment boundary
271
+    if( smpIdx+procSmpCnt >= p->afrp->info.frameCnt || (smpIdx <= segSmpIdx && segSmpIdx < smpIdx + procSmpCnt) )
272
+    {
273
+      segFl = true;
274
+
275
+      if((chp = _cmPuIncrCh(p,chp, &segSmpIdx )) == NULL )
276
+        break;
277
+    }
278
+
279
+    // shift the new samples into the shift buffer
280
+    while(cmShiftBufExec(p->sbp,smpV,p->afrp->outN))
281
+    {
282
+      
283
+      // update the gate detector
284
+      cmGateDetectExec2(p->gdp,p->sbp->outV,p->sbp->outN);
285
+
286
+      if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
287
+        goto errLabel;
288
+
289
+      segFl =false;
290
+    }
291
+
292
+    // write the audio output file
293
+    if( cmAudioFileWrExec(afwp, outChIdx,smpV,p->afrp->outN ) != cmOkRC )
294
+    {
295
+      cmErrMsg(&p->err,kProcFailPuRC,"A write failed to the audio output file '%s'.",outAudFn);
296
+      goto errLabel;
297
+    }
298
+
299
+  }
300
+
301
+ errLabel:  
302
+
303
+  cmMemPtrFree(&smpV);
304
+  
305
+  if( cmAudioFileWrFree(&afwp) != cmOkRC )
306
+  {
307
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Output audio file '%s' close failed.",cmStringNullGuard(outAudFn));
308
+    goto errLabel;
309
+  }
310
+
311
+  return rc;
312
+}
313
+
314
+cmPuRC_t cmPuAutoGainCfg( 
315
+  cmPuH_t                   h,
316
+  const cmChar_t*           audioFn,
317
+  const cmChar_t*           labelFn,
318
+  const cmChar_t*           outMtx0Fn,
319
+  const cmChar_t*           outMtx1Fn,
320
+  const cmChar_t*           outAudFn,
321
+  unsigned                  procSmpCnt,
322
+  cmReal_t                  hopMs,
323
+  const cmGateDetectParams* gd0Args,
324
+  const cmGateDetectParams* gd1Args )
325
+{
326
+  cmPuRC_t        rc;
327
+  cmPu_t*         p       = _cmPuHandleToPtr(h);
328
+  int             smpIdx  = 0;
329
+  int             chIdx   = 0;
330
+  const cmReal_t  rmsMax  = 1.0;
331
+  cmReal_t        minRms  = rmsMax;
332
+  cmReal_t        gateMax = 0;
333
+  cmReal_t        gateSum = 0;
334
+  unsigned        gateCnt = 0;
335
+  cmPuCh_t*       chp     = NULL;
336
+  bool            segFl   = false;
337
+  unsigned        segSmpIdx = cmInvalidIdx;
338
+
339
+  // create a cmProc context
340
+  if((p->ctxp = cmCtxAlloc(NULL, p->err.rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL )
341
+  {
342
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Proc context create failed.");
343
+    goto errLabel;
344
+  }
345
+
346
+  // create a cmProc audio file reader
347
+  if((p->afrp =  cmAudioFileRdAlloc(p->ctxp, NULL, procSmpCnt, audioFn, chIdx, 0, cmInvalidIdx)) == NULL )
348
+  {
349
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Audio file reader creation failed on '%s'.",cmStringNullGuard(audioFn));
350
+    goto errLabel;
351
+  }
352
+
353
+  // given the sample rate calculate the hop and window size in samples
354
+  unsigned       hopSmpCnt = floor(p->afrp->info.srate * hopMs / 1000);
355
+  unsigned       wndSmpCnt = hopSmpCnt * gd0Args->medCnt;
356
+
357
+  // create a shift buffer to maintain the RMS window for the gate detector
358
+  if((p->sbp = cmShiftBufAlloc(p->ctxp,NULL,procSmpCnt,wndSmpCnt,hopSmpCnt )) == NULL )
359
+  {
360
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Shift buffer create failed.");
361
+    goto errLabel;
362
+  }
363
+
364
+  // create a gate detector
365
+  if((p->gdp = cmGateDetectAlloc2(p->ctxp,NULL,procSmpCnt,gd0Args)) == NULL )
366
+  {
367
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Gate detect create failed.");
368
+    goto errLabel;
369
+  }
370
+
371
+  // create an output file to hold the results of the gate detector
372
+  if( (p->mfp = cmBinMtxFileAlloc(p->ctxp,NULL,outMtx0Fn)) == NULL )
373
+  {
374
+    rc = cmErrMsg(&p->err,kProcFailPuRC,"Binary matrix file create failed.");
375
+    goto errLabel;
376
+  }
377
+
378
+  // read the label file and create p->chArray
379
+  if((rc = _cmPuReadLabelsAndCreateArray(p, labelFn, p->afrp->info.srate)) != kOkPuRC )
380
+    goto errLabel;
381
+
382
+  chp       = p->chArray;
383
+  segSmpIdx = chp->begSmpIdx;
384
+
385
+  // for each procSmpCnt samples
386
+  for(; cmAudioFileRdRead(p->afrp) != cmEofRC; smpIdx += procSmpCnt )
387
+  {
388
+    // if this audio frame marks a segment beginning or 
389
+    // the end-of-audio-file will occur on the next frame
390
+    if( smpIdx+procSmpCnt >= p->afrp->info.frameCnt || (smpIdx <= segSmpIdx && segSmpIdx < smpIdx + procSmpCnt) )
391
+    {
392
+      segFl = true;
393
+
394
+      // if no ending offset was located then update the gate sum
395
+      if( gateMax != 0 )
396
+      {
397
+        gateSum += gateMax;
398
+        gateCnt += 1;
399
+        gateMax  = 0;
400
+      } 
401
+      
402
+      // calc the avg max RMS value for this segment
403
+      chp->gateMaxAvg = gateCnt == 0 ? 0.0 : gateSum / gateCnt;
404
+      gateCnt = 0;
405
+      gateSum = 0;
406
+      gateMax = 0;
407
+
408
+      minRms = rmsMax; // force the segment end to be after the segment beginning
409
+
410
+      if((chp = _cmPuIncrCh(p,chp, &segSmpIdx )) == NULL )
411
+        break;
412
+    }
413
+
414
+    // shift the new samples into the shift buffer
415
+    while(cmShiftBufExec(p->sbp,p->afrp->outV,p->afrp->outN))
416
+    {
417
+      // update the gate detector
418
+      cmGateDetectExec2(p->gdp,p->sbp->outV,p->sbp->outN);
419
+
420
+      // write the output matrix file
421
+      if( _cmPuWriteMtxFile(p,segFl) != kOkPuRC )
422
+        goto errLabel;
423
+
424
+      segFl = false;
425
+
426
+      // if this frame is an RMS minimum or onset or offset
427
+      // then select it as a possible segment end.
428
+      // Note that for onsets this will effectively force the end to
429
+      // come after the onset because the onset will not be an energy minimum
430
+      // relative to subsequent frames.
431
+      if( p->gdp->rms < minRms || p->gdp->onFl || p->gdp->offFl )
432
+      {
433
+        minRms         = p->gdp->rms;
434
+        chp->endSmpIdx = smpIdx;
435
+
436
+        // count onsets
437
+        if( p->gdp->onFl )
438
+          ++chp->onCnt;
439
+
440
+        // count offsets
441
+        if( p->gdp->offFl )
442
+        {
443
+          ++chp->offCnt;
444
+          
445
+          // update the gate sum and count
446
+          gateSum += gateMax;
447
+          gateCnt += 1;
448
+          gateMax  = 0;
449
+        }
450
+      }
451
+
452
+      // track the max RMS value during this gate
453
+      if( p->gdp->gateFl && p->gdp->rms > gateMax )
454
+        gateMax = p->gdp->rms;
455
+      
456
+    }
457
+  }
458
+
459
+  // calculate the channel gains
460
+  if( rc == kOkPuRC )
461
+    _cmPuCalcGains(p);
462
+
463
+  rc = _cmPuCalcRerunGateDetectors(p,outMtx1Fn,outAudFn,gd1Args,procSmpCnt,wndSmpCnt,hopSmpCnt);
464
+
465
+  p->gd0Args = *gd0Args;
466
+  p->gd1Args = *gd1Args;
467
+
468
+ errLabel:
469
+  if( p->mfp != NULL )
470
+    cmBinMtxFileFree(&p->mfp);
471
+
472
+  if( p->gdp != NULL )
473
+    cmGateDetectFree2(&p->gdp);
474
+
475
+  if( p->sbp != NULL )
476
+    cmShiftBufFree(&p->sbp);
477
+  
478
+  if( p->afrp != NULL )
479
+    cmAudioFileRdFree(&p->afrp);
480
+
481
+  if( p->ctxp != NULL )
482
+    cmCtxFree(&p->ctxp);
483
+
484
+  return rc;
485
+}
486
+
487
+cmPuRC_t cmPuAutoGainExec( cmPuH_t h, const cmChar_t* fileDir, unsigned procSmpCnt )
488
+{
489
+  cmPu_t*         p         = _cmPuHandleToPtr(h);
490
+  const cmChar_t* inAudFn   = cmFsMakeFn(fileDir, p->inAudFn,   NULL, NULL );
491
+  const cmChar_t* inLabelFn = cmFsMakeFn(fileDir, p->inLabelFn, NULL, NULL );
492
+  const cmChar_t* outMtx0Fn = cmFsMakeFn(fileDir, p->outMtx0Fn, NULL, NULL );
493
+  const cmChar_t* outMtx1Fn = cmFsMakeFn(fileDir, p->outMtx1Fn, NULL, NULL );
494
+  const cmChar_t* outAudFn  = cmFsMakeFn(fileDir, p->outAudFn,  NULL, NULL );
495
+
496
+  cmPuRC_t rc = cmPuAutoGainCfg(h,inAudFn,inLabelFn,outMtx0Fn,outMtx1Fn,outAudFn,procSmpCnt,p->hopMs,&p->gd0Args,&p->gd1Args);
497
+
498
+  cmFsFreeFn(outAudFn);
499
+  cmFsFreeFn(outMtx1Fn);
500
+  cmFsFreeFn(outMtx0Fn);
501
+  cmFsFreeFn(inLabelFn);
502
+  cmFsFreeFn(inAudFn);
503
+
504
+  return rc;
505
+}
506
+
507
+cmPuRC_t cmPuAutoGainCfgFromJson( 
508
+  cmPuH_t             h,
509
+  const cmChar_t*     cfgDir,
510
+  cmJsonH_t           jsH,
511
+  cmJsonNode_t*       onp,
512
+  unsigned            procSmpCnt )
513
+{
514
+  cmPuRC_t rc;
515
+  if((rc = cmPuReadJson(h,jsH,onp)) != kOkPuRC )
516
+    return rc;
517
+  
518
+
519
+  return cmPuAutoGainExec(h,cfgDir,procSmpCnt);
520
+}
521
+
522
+
523
+void cmPuReport( cmPuH_t h, cmRpt_t* rpt )
524
+{
525
+  cmPu_t*         p       = _cmPuHandleToPtr(h);
526
+  
527
+  unsigned i;
528
+  for(i=0; i<p->chCnt; ++i)
529
+  {
530
+    const cmPuCh_t* chp = p->chArray + i;
531
+    cmRptPrintf(rpt,"beg:%i end:%i on:%i off:%i max:%f gain:%f\n",
532
+      chp->begSmpIdx, chp->endSmpIdx, chp->onCnt, chp->offCnt, chp->gateMaxAvg, chp->gain );
533
+  }
534
+}
535
+
536
+cmPuRC_t _cmPuJsonGainRead( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label )
537
+{
538
+  cmPuRC_t rc = kOkPuRC;
539
+  cmJsonNode_t* arp;
540
+
541
+  // locate the JSON 'gain' array
542
+  if(( arp = cmJsonFindValue(jsH,label,onp,kArrayTId)) == NULL )
543
+  {
544
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON array node %s.",cmStringNullGuard(label));
545
+    goto errLabel;
546
+  }
547
+
548
+  // get the count of elements in the 'gain' array
549
+  unsigned  arrCnt = cmJsonChildCount(arp);
550
+  cmPuCh_t* arr    = NULL;
551
+
552
+  if( arrCnt > 0 )
553
+  {
554
+    arr = cmMemAllocZ(cmPuCh_t,arrCnt);
555
+
556
+    unsigned i;
557
+    for(i=0; i<arrCnt; ++i)
558
+    {
559
+      if( i<p->chCnt )
560
+        arr[i] = p->chArray[i];
561
+
562
+      if( cmJsonRealValue( cmJsonArrayElement(arp,i), &arr[i].gain ) != kOkJsRC )
563
+      {
564
+        rc = cmErrMsg(&p->err,kJsonFailPuRC,"An error occurred while accessing a JSON 'gain' element.");
565
+        goto errLabel;
566
+      }
567
+
568
+    }
569
+  }
570
+
571
+
572
+ errLabel:
573
+
574
+  if( rc != kOkPuRC )
575
+    cmMemPtrFree(&arr);
576
+
577
+  cmMemPtrFree(&p->chArray);
578
+  p->chArray = arr;
579
+  p->chCnt   = arrCnt;
580
+
581
+  return rc;
582
+}
583
+
584
+cmPuRC_t _cmPuJsonGdParmsRead( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label, cmGateDetectParams* gdParms )
585
+{
586
+  cmPuRC_t rc = kOkPuRC;
587
+  cmJsonNode_t* gdp;
588
+  const char* errLabelPtr;
589
+  if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
590
+  {
591
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
592
+    goto errLabel;
593
+  }
594
+
595
+  if( cmJsonMemberValues(gdp, &errLabelPtr, 
596
+      "medCnt",      kIntTId,  &gdParms->medCnt,
597
+      "avgCnt",      kIntTId,  &gdParms->avgCnt,
598
+      "suprCnt",     kIntTId,  &gdParms->suprCnt,
599
+      "offCnt",      kIntTId,  &gdParms->offCnt,
600
+      "suprCoeff",   kRealTId, &gdParms->suprCoeff,
601
+      "onThreshDb",  kRealTId, &gdParms->onThreshDb,
602
+      "offThreshDb", kRealTId, &gdParms->offThreshDb,
603
+      NULL ) != kOkJsRC )
604
+  {
605
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Gate detect parameter restore failed for '%s'.",cmStringNullGuard(label));
606
+    goto errLabel;
607
+  }
608
+
609
+ errLabel:
610
+  return rc;
611
+}
612
+
613
+cmPuRC_t _cmPuJsonCfgParmsRead(cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp)
614
+{
615
+  cmPuRC_t      rc = kOkPuRC;
616
+  cmJsonNode_t* gdp = onp;
617
+  const char*   errLabelPtr;
618
+
619
+ 
620
+  //if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
621
+  //{
622
+  //  rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
623
+  //  goto errLabel;
624
+  // }
625
+
626
+  if( cmJsonMemberValues(gdp, &errLabelPtr, 
627
+      "audioFn",   kStringTId, &p->inAudFn,
628
+      "labelFn", kStringTId, &p->inLabelFn,
629
+      "outMtx0Fn", kStringTId, &p->outMtx0Fn,
630
+      "outMtx1Fn", kStringTId, &p->outMtx1Fn,
631
+      "outAudFn",  kStringTId, &p->outAudFn,
632
+      "hopMs",     kRealTId,   &p->hopMs,
633
+      NULL ) != kOkJsRC )
634
+  {
635
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Autotune cfg parameter read failed.");
636
+    goto errLabel;
637
+  }
638
+
639
+ errLabel:
640
+  return rc;
641
+
642
+}
643
+
644
+cmPuRC_t cmPuReadJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp )
645
+{
646
+  cmPuRC_t      rc = kOkPuRC;
647
+  cmPu_t*       p  = _cmPuHandleToPtr(h);
648
+  cmJsonNode_t* atp;
649
+
650
+  if(( atp = cmJsonFindValue(jsH,"cfg",onp,kObjectTId)) == NULL )
651
+  {
652
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"The JSON 'autotune' object was not found.");
653
+    goto errLabel;
654
+  }
655
+
656
+  if((rc = _cmPuJsonCfgParmsRead(p,jsH,atp)) != kOkPuRC )
657
+    goto errLabel;
658
+
659
+  if((rc = _cmPuJsonGdParmsRead(p,jsH,atp,"gdParms0",&p->gd0Args)) != kOkPuRC )
660
+    goto errLabel;
661
+
662
+  if((rc = _cmPuJsonGdParmsRead(p,jsH,atp,"gdParms1",&p->gd1Args)) != kOkPuRC )
663
+    goto errLabel;
664
+
665
+  if((rc = _cmPuJsonGainRead(p,jsH,atp,"gain")) != kOkPuRC )
666
+    goto errLabel;
667
+
668
+ errLabel:
669
+  return rc;
670
+
671
+}
672
+
673
+unsigned        cmPuChannelCount(   cmPuH_t h )
674
+{ 
675
+  cmPu_t* p = _cmPuHandleToPtr(h);
676
+  return p->chCnt;
677
+}
678
+
679
+const cmPuCh_t* cmPuChannel( cmPuH_t h, unsigned chIdx )
680
+{
681
+  cmPu_t* p = _cmPuHandleToPtr(h);
682
+  assert( chIdx < p->chCnt );
683
+  return p->chArray + chIdx;
684
+}
685
+    
686
+
687
+
688
+cmPuRC_t _cmPuJsonSetInt( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* gdp, const cmChar_t* label, int val )
689
+{
690
+  cmPuRC_t rc = kOkPuRC;
691
+
692
+  if( cmJsonReplacePairInt(jsH, gdp, label, kIntTId | kRealTId, val ) != kOkJsRC )
693
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Error setting integer JSON field: %s.",cmStringNullGuard(label));
694
+  return rc;
695
+}
696
+
697
+cmPuRC_t _cmPuJsonSetReal( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* gdp, const cmChar_t* label, cmReal_t val )
698
+{
699
+  cmPuRC_t rc = kOkPuRC;
700
+
701
+  if( cmJsonReplacePairReal(jsH, gdp, label, kIntTId | kRealTId, val ) != kOkJsRC )
702
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Error setting real JSON field: %s.",cmStringNullGuard(label));
703
+  return rc;
704
+}
705
+
706
+cmPuRC_t _cmPuJsonGdParmsUpdate( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label, const cmGateDetectParams* gdParms )
707
+{
708
+  cmPuRC_t rc = kOkPuRC;
709
+  cmJsonNode_t* gdp;
710
+
711
+  if(( gdp = cmJsonFindValue(jsH,label,onp,kObjectTId)) == NULL )
712
+  {
713
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON object node %s.",cmStringNullGuard(label));
714
+    goto errLabel;
715
+  }
716
+
717
+  _cmPuJsonSetInt(  p, jsH, gdp, "medCnt",     gdParms->medCnt);
718
+  _cmPuJsonSetInt(  p, jsH, gdp, "avgCnt",     gdParms->avgCnt);
719
+  _cmPuJsonSetInt(  p, jsH, gdp, "suprCnt",    gdParms->suprCnt);
720
+  _cmPuJsonSetInt(  p, jsH, gdp, "offCnt",     gdParms->offCnt);
721
+  _cmPuJsonSetReal( p, jsH, gdp, "suprCoeff",  gdParms->suprCoeff);
722
+  _cmPuJsonSetReal( p, jsH, gdp, "onThreshDb", gdParms->onThreshDb);
723
+  _cmPuJsonSetReal( p, jsH, gdp, "offThresDb", gdParms->offThreshDb);
724
+
725
+  rc = cmErrLastRC(&p->err);
726
+ errLabel:
727
+  return rc;
728
+
729
+}
730
+
731
+cmPuRC_t _cmPuJsonGainUpdate( cmPu_t* p, cmJsonH_t jsH, cmJsonNode_t* onp, const cmChar_t* label )
732
+{
733
+  cmPuRC_t rc = kOkPuRC;
734
+  cmJsonNode_t* arp;
735
+  unsigned i;
736
+
737
+  // locate the JSON 'gain' array
738
+  if(( arp = cmJsonFindValue(jsH,label,onp,kArrayTId)) == NULL )
739
+  {
740
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"Unable to locate the JSON array node %s.",cmStringNullGuard(label));
741
+    goto errLabel;
742
+  }
743
+
744
+  // get the count of elements in the 'gain' array
745
+  unsigned arrCnt = cmJsonChildCount(arp);
746
+
747
+  // update the existing 'gain' array elmements from p->chArray[]
748
+  for(i=0; i<arrCnt && i<p->chCnt; ++i)
749
+  {
750
+    if(cmJsonSetReal( jsH, cmJsonArrayElement(arp,i), p->chArray[i].gain ) != kOkPuRC )
751
+    {
752
+      rc = cmErrMsg(&p->err,kJsonFailPuRC,"Set JSON 'gain' array elment failed.");
753
+      goto errLabel;
754
+    }
755
+  }    
756
+
757
+  // create new elements if the array was not long enough
758
+  for(; i<p->chCnt; ++i)
759
+    if( cmJsonCreateReal(jsH,arp,p->chArray[i].gain) != kOkJsRC )
760
+    {
761
+      rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON 'gain' element create failed.");
762
+      goto errLabel;
763
+    }
764
+
765
+  // remove elements if the array begain with extra elements.
766
+  if( arrCnt > p->chCnt )
767
+  {
768
+    if( cmJsonRemoveNode( jsH, cmJsonArrayElement(arp,i), true ) != kOkJsRC )
769
+    {
770
+      rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON 'gain' element removal failed.");
771
+      goto errLabel;
772
+    }
773
+  }
774
+
775
+ errLabel:
776
+  return rc;
777
+  
778
+}
779
+
780
+cmPuRC_t cmPuWriteJson( cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp )
781
+{
782
+  cmPuRC_t      rc = kOkPuRC;
783
+  cmPu_t*       p  = _cmPuHandleToPtr(h);
784
+  cmJsonNode_t* atp;
785
+
786
+  if(( atp = cmJsonFindValue(jsH,"autoTune",onp,kObjectTId)) == NULL )
787
+  {
788
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"The JSON 'autotune' object was not found.");
789
+    goto errLabel;
790
+  }
791
+
792
+  if((rc = _cmPuJsonGdParmsUpdate(p,jsH,atp,"gdParms0",&p->gd0Args)) != kOkPuRC )
793
+    goto errLabel;
794
+
795
+  if((rc = _cmPuJsonGdParmsUpdate(p,jsH,atp,"gdParms1",&p->gd1Args)) != kOkPuRC )
796
+    goto errLabel;
797
+
798
+  if((rc = _cmPuJsonGainUpdate(p,jsH,atp,"gain")) != kOkPuRC )
799
+    goto errLabel;
800
+    
801
+ errLabel:
802
+
803
+  return rc;
804
+}
805
+
806
+cmPuRC_t cmPuWriteJsonFile( cmPuH_t h, const cmChar_t* jsonFn  )
807
+{
808
+  cmPuRC_t  rc  = kOkPuRC;
809
+  cmJsonH_t jsH = cmJsonNullHandle;
810
+  cmPu_t*   p   = _cmPuHandleToPtr(h);
811
+
812
+  // initialize a JSON tree from 'jsonFn'.
813
+  if( cmJsonInitializeFromFile(&jsH,jsonFn,&p->ctx) != kOkJsRC )
814
+  {
815
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file initialization failed on '%s'.",cmStringNullGuard(jsonFn));
816
+    goto errLabel;
817
+  }
818
+
819
+  // update the 'autoTune' object in the JSON tree
820
+  if((rc = cmPuWriteJson(h,jsH,cmJsonRoot(jsH))) != kOkPuRC )
821
+    goto errLabel;
822
+
823
+  // write the JSON tree back to 'jsonFn'.
824
+  if( cmJsonWrite(jsH,cmJsonRoot(jsH),jsonFn) != kOkJsRC )
825
+  {
826
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file save failed on '%s'.",cmStringNullGuard(jsonFn));
827
+    goto errLabel;
828
+  }
829
+
830
+ errLabel:
831
+  // release the JSON tree
832
+  if( cmJsonFinalize(&jsH) != kOkJsRC )
833
+    rc = cmErrMsg(&p->err,kJsonFailPuRC,"JSON file finalization failed on '%s'.",cmStringNullGuard(jsonFn));
834
+
835
+  return rc;
836
+}
837
+
838
+void cmPuTest(cmCtx_t* ctx)
839
+{
840
+  cmPuH_t            h          = cmPuNullHandle;
841
+  cmGateDetectParams gd0Args;
842
+  cmGateDetectParams gd1Args;
843
+  const cmChar_t*    audioFn    = "/home/kevin/media/audio/gate_detect/gate_detect0.aif";
844
+  const cmChar_t*    labelFn    = "/home/kevin/media/audio/gate_detect/gate_detect0_labels.txt";
845
+  const cmChar_t*    outMtx0Fn  = "/home/kevin/media/audio/gate_detect/gd0.mtx";
846
+  const cmChar_t*    outMtx1Fn  = "/home/kevin/media/audio/gate_detect/gd1.mtx";
847
+  const cmChar_t*    outAudFn   = "/home/kevin/media/audio/gate_detect/gd_gain0.aif";
848
+  const cmChar_t*    jsonFn     = "/home/kevin/src/kc/src/data/rsrc1.txt";
849
+  unsigned           procSmpCnt = 64;
850
+  cmReal_t           hopMs      = 10;
851
+
852
+  gd0Args.medCnt      = 5;
853
+  gd0Args.avgCnt      = 9;
854
+  gd0Args.suprCnt     = 6;
855
+  gd0Args.offCnt      = 3;
856
+  gd0Args.suprCoeff   = 1.4;
857
+  gd0Args.onThreshDb  = -53.0;
858
+  gd0Args.offThreshDb = -80.0;
859
+
860
+  gd1Args = gd0Args;
861
+  gd1Args.onThreshDb = -45;
862
+
863
+  if( cmPuAlloc(ctx, &h ) != kOkPuRC )
864
+    goto errLabel;
865
+
866
+  if( cmPuAutoGainCfg(h,audioFn,labelFn,outMtx0Fn,outMtx1Fn,outAudFn,procSmpCnt,hopMs,&gd0Args,&gd1Args) == kOkPuRC )
867
+  {
868
+    cmPuReport(h,&ctx->rpt);
869
+
870
+    cmPuWriteJsonFile( h, jsonFn  );
871
+  }
872
+ errLabel:
873
+   cmPuFree(&h);
874
+ 
875
+   
876
+}

+ 96
- 0
app/cmPickup.h View File

@@ -0,0 +1,96 @@
1
+#ifndef cmPickup_h
2
+#define cmPickup_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+
9
+  enum
10
+  {
11
+    kOkPuRC = cmOkRC,
12
+    kProcFailPuRC,
13
+    kJsonFailPuRC
14
+  };
15
+
16
+  typedef cmRC_t     cmPuRC_t;
17
+  typedef cmHandle_t cmPuH_t;
18
+
19
+  // This record holds information which is maintained on a per-pickup basis
20
+  typedef struct
21
+  {
22
+    unsigned begSmpIdx;  // during auto-gain cfg set to the first sample in the audio example file where the group of notes from this pickup begin.
23
+    unsigned endSmpIdx;  // ... end of the example notes for this pickup
24
+    unsigned midiPitch;  // midi pitch associated with this pickup
25
+    unsigned onCnt;      // during auto-gain cfg set to the count of onsets detected for this pickup
26
+    unsigned offCnt;     // ... offset detected
27
+    cmReal_t gateMaxAvg; // avg of the the max gate RMS values for all detected notes   
28
+    cmReal_t gain;       // auto-gain coeff for this pickup
29
+
30
+  } cmPuCh_t;
31
+
32
+  
33
+  extern cmPuH_t cmPuNullHandle;
34
+
35
+  cmPuRC_t cmPuAlloc( cmCtx_t* ctx, cmPuH_t* hp );
36
+  cmPuRC_t cmPuFree( cmPuH_t* hp );
37
+  bool     cmPuIsValid( cmPuH_t h );
38
+  
39
+  // Given a recorded audio file containing a set of notes recorded from each pickup,
40
+  // an Audacity label file which marks the beginning of teach set of notes,
41
+  // and a set of gate detector parameters attempt to calculate a set of gain
42
+  // coefficients to equalize the relative gain of all the pickup channels.
43
+  // This algorithm works in two passes: In the first pass the gain coefficient
44
+  // is established by finding an average RMS value among the examples and
45
+  // then increasing and decreasing the pickups to move the examples closer
46
+  // to the average.  The gain is then adjusted and a second pass is made to
47
+  // examine how well the gate detectors work with the new gain setttings.
48
+  cmPuRC_t cmPuAutoGainCfg( 
49
+    cmPuH_t                   h,
50
+    const cmChar_t*           audioFn,     // audio file containing a set of examples for each note
51
+    const cmChar_t*           labelFn,     // audicity label file with one marker preceding each set of notes
52
+    const cmChar_t*           outMtx0Fn,   // octave binary matrix file containing the intermediate and final results of the gate detector analysis after the first pass
53
+    const cmChar_t*           outMtx1Fn,   // octave binary matrix file containing the intermediate and final results of the gate detector analysis after the second pass
54
+    const cmChar_t*           outAudFn,    // audioFn rewritten with auto-gain applied
55
+    unsigned                  procSmpCnt,  // analysis DSP frame size in samples
56
+    cmReal_t                  hopMs,       // analysis window hop size in milliseconds
57
+    const cmGateDetectParams* gd0Args,     // first pass gate detector args
58
+    const cmGateDetectParams* gd1Args );   // second pass gate detector args
59
+
60
+  // Calls cmPuReadJson() and then prepends the fileDir to each of the
61
+  // file names. All the files are then read and written to this directory.
62
+  // and calls cmPuAutoGainCfg(). 
63
+  cmPuRC_t cmPuAutoGainCfgFromJson( 
64
+    cmPuH_t             h,
65
+    const cmChar_t*     fileDir,
66
+    cmJsonH_t           jsH,
67
+    cmJsonNode_t*       onp,
68
+    unsigned            procSmpCnt );
69
+
70
+
71
+  cmPuRC_t cmPuAutoGainExec( cmPuH_t h, const cmChar_t* fileDir, unsigned procSmpCnt );
72
+
73
+  // Load the 'parmsCfg', 'gdArgs' and 'gain' array from the JSON 
74
+  // 'autoTune' object contained in the JSON object 'onp'.
75
+  cmPuRC_t cmPuReadJson(      cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp );
76
+
77
+  unsigned        cmPuChannelCount(   cmPuH_t h );
78
+  const cmPuCh_t* cmPuChannel( cmPuH_t h, unsigned chIdx );
79
+    
80
+
81
+  // Update the 'autoTune' JSON object contained the JSON object 'onp'.
82
+  cmPuRC_t cmPuWriteJson(     cmPuH_t h, cmJsonH_t jsH, cmJsonNode_t* onp );
83
+  
84
+  // Update the 'autoTune' JSON object in the JSON file 'jsonFn'.
85
+  // (Same as cmPuWriteJson() except the JSON tree to update is contained in a file.)
86
+  cmPuRC_t cmPuWriteJsonFile( cmPuH_t h, const cmChar_t* jsonFn  );
87
+
88
+  void cmPuReport( cmPuH_t h, cmRpt_t* rpt );
89
+
90
+  void cmPuTest(cmCtx_t* ctx);
91
+
92
+#ifdef __cplusplus
93
+}
94
+#endif
95
+
96
+#endif

+ 635
- 0
app/cmScore.c View File

@@ -0,0 +1,635 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmRpt.h"
5
+#include "cmErr.h"
6
+#include "cmCtx.h"
7
+#include "cmMem.h"
8
+#include "cmMallocDebug.h"
9
+#include "cmMidi.h"
10
+#include "cmLex.h"
11
+#include "cmCsv.h"
12
+#include "cmMidiFile.h"
13
+#include "cmAudioFile.h"
14
+#include "cmTimeLine.h"
15
+#include "cmScore.h"
16
+
17
+/*
18
+#include "cmComplexTypes.h"
19
+#include "cmLinkedHeap.h"
20
+#include "cmSymTbl.h"
21
+#include "cmProcObj.h"
22
+#include "cmProc.h"
23
+#include "cmProcTemplate.h"
24
+*/
25
+
26
+#include "cmVectOpsTemplateMain.h"
27
+
28
+cmScH_t cmScNullHandle  = cmSTATIC_NULL_HANDLE;
29
+
30
+enum
31
+{
32
+  kLabelCharCnt = 7,
33
+
34
+  kInvalidDynScId = 0,
35
+
36
+};
37
+
38
+enum
39
+{
40
+  kTypeLabelColScIdx = 3,
41
+  kDSecsColScIdx     = 5,
42
+  kPitchColScIdx     = 11,
43
+  kBarColScIdx       = 13,
44
+  kSkipColScIdx      = 14,
45
+  kEvenColScIdx      = 15,
46
+  kTempoColScIdx     = 16,
47
+  kDynColScIdx       = 17
48
+};
49
+
50
+typedef struct
51
+{
52
+  unsigned id;
53
+  cmChar_t label[ kLabelCharCnt + 1 ];
54
+} cmScEvtRef_t; 
55
+
56
+
57
+typedef struct
58
+{
59
+  cmErr_t       err;
60
+  cmScoreEvt_t* array;
61
+  unsigned      cnt;
62
+  cmCsvH_t      cH;
63
+} cmSc_t;
64
+
65
+cmScEvtRef_t _cmScEvtRefArray[] = 
66
+{
67
+  { kTimeSigEvtScId, "tsg" },
68
+  { kKeySigEvtScId,  "ksg" },
69
+  { kTempoEvtScId,   "tmp" },
70
+  { kTrackEvtScId,   "trk" },
71
+  { kTextEvtScId,    "txt" },
72
+  { kEOTrackEvtScId, "eot" },
73
+  { kCopyEvtScId,    "cpy"},
74
+  { kBlankEvtScId,   "blk"},
75
+  { kBarEvtScId,     "bar"},
76
+  { kPgmEvtScId,     "pgm" },
77
+  { kCtlEvtScId,     "ctl" },
78
+  { kNonEvtScId,     "non" },
79
+  { kInvalidEvtScId, "***" }
80
+};
81
+
82
+cmScEvtRef_t _cmScDynRefArray[] = 
83
+{
84
+  { 1, "pppp" },
85
+  { 2, "ppp" },
86
+  { 3, "pp"  },
87
+  { 4, "p"   },
88
+  { 5, "mp"  },
89
+  { 6, "m"   },
90
+  { 7, "mf"  },
91
+  { 8, "f"   },
92
+  { 9, "ff"  },
93
+  { 10, "fff" },
94
+  { 11, "ffff"},
95
+  { kInvalidDynScId, "***" },
96
+};
97
+
98
+cmSc_t* _cmScHandleToPtr( cmScH_t h )
99
+{ 
100
+  cmSc_t* p = (cmSc_t*)h.h;
101
+  assert( p != NULL );
102
+  return p;
103
+}
104
+
105
+unsigned _cmScEvtTypeLabelToId( const cmChar_t* label )
106
+{
107
+  cmScEvtRef_t* r = _cmScEvtRefArray;
108
+  for(; r->id != kInvalidEvtScId; ++r )
109
+    if( strcmp(label,r->label) == 0 )
110
+      return r->id;
111
+  return kInvalidEvtScId;
112
+}
113
+
114
+const cmChar_t* _cmScEvtTypeIdToLabel( unsigned id )
115
+{
116
+  cmScEvtRef_t* r = _cmScEvtRefArray;
117
+  for(; r->id != kInvalidEvtScId; ++r )
118
+    if( r->id == id )
119
+      return r->label;
120
+  return NULL;
121
+}
122
+
123
+unsigned _cmScDynLabelToId( const cmChar_t* label )
124
+{
125
+  cmScEvtRef_t* r = _cmScDynRefArray;
126
+  for(; r->id != kInvalidEvtScId; ++r )
127
+    if( strcmp(label,r->label) == 0 )
128
+      return r->id;
129
+  return kInvalidDynScId;
130
+}
131
+
132
+const cmChar_t* _cmScDynIdToLabel( unsigned id )
133
+{
134
+  cmScEvtRef_t* r = _cmScDynRefArray;
135
+  for(; r->id != kInvalidDynScId; ++r )
136
+    if( r->id == id )
137
+      return r->label;
138
+  return NULL;
139
+}
140
+
141
+unsigned _cmScLexSciPitchMatcher( const cmChar_t* cp, unsigned cn )
142
+{
143
+  // first char must be "A-G"
144
+  if( strspn(cp,"ABCDEFG") != 1 )
145
+    return 0;
146
+
147
+  unsigned i = 1;
148
+
149
+  // next char could be accidental
150
+  if( cp[i] == '#' || cp[i] == 'b' )
151
+    ++i; // i==2
152
+
153
+  // the 2nd or 3rd char must be a digit
154
+  if( isdigit(cp[i]) == false )
155
+    return 0;
156
+
157
+  ++i;  // i==2 or i==3
158
+
159
+  // the 3rd or 4th char must be a digit or EOS
160
+  if( isdigit(cp[i]) == false )
161
+    return i;
162
+  
163
+  ++i;
164
+
165
+  return i;
166
+  
167
+}
168
+
169
+cmScRC_t _cmScFinalize( cmSc_t* p )
170
+{
171
+  cmScRC_t rc = kOkScRC;
172
+
173
+  if( cmCsvFinalize(&p->cH) != kOkCsvRC )
174
+    return rc;
175
+
176
+  cmMemFree(p->array);
177
+  cmMemFree(p);
178
+  return rc;
179
+}
180
+
181
+cmScRC_t _cmScParseBar( cmSc_t* p, unsigned rowIdx, int* barNumb )
182
+{
183
+  if((*barNumb = cmCsvCellInt(p->cH,rowIdx,kBarColScIdx)) == INT_MAX )
184
+    return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to parse the bar number.");
185
+  return kOkScRC;
186
+}
187
+
188
+cmScRC_t _cmScParseNoteOn( cmSc_t* p, unsigned rowIdx, cmScoreEvt_t* s, int barNumb, unsigned barNoteIdx )
189
+{
190
+  cmScRC_t        rc     = kOkScRC;
191
+  unsigned        flags  = 0;
192
+  unsigned        dynVal = kInvalidDynScId;
193
+  const cmChar_t* sciPitch;
194
+  cmMidiByte_t    midiPitch;
195
+  const cmChar_t* attr;
196
+  double          dsecs;
197
+
198
+  if((sciPitch = cmCsvCellText(p->cH,rowIdx,kPitchColScIdx)) == NULL )
199
+    return cmErrMsg(&p->err,kSyntaxErrScRC,"Expected a scientific pitch value");
200
+          
201
+  if((midiPitch = cmSciPitchToMidi(sciPitch)) == kInvalidMidiPitch)
202
+   return cmErrMsg(&p->err,kSyntaxErrScRC,"Unable to convert the scientific pitch '%s' to a MIDI value. ");
203
+
204
+  // it is possible that note delta-secs field is empty - so default to 0
205
+  if((dsecs =  cmCsvCellDouble(p->cH, rowIdx, kDSecsColScIdx )) == DBL_MAX) // Returns DBL_MAX on error.
206
+    dsecs = 0;
207
+
208
+  if((attr = cmCsvCellText(p->cH,rowIdx,kSkipColScIdx)) != NULL && *attr == 's' )
209
+    flags += kSkipScFl;
210
+
211
+  if((attr = cmCsvCellText(p->cH,rowIdx,kEvenColScIdx)) != NULL && *attr == 'e' )
212
+    flags += kEvenScFl;
213
+
214
+  if((attr = cmCsvCellText(p->cH,rowIdx,kTempoColScIdx)) != NULL && *attr == 't' )
215
+    flags += kTempoScFl;
216
+          
217
+  if((attr = cmCsvCellText(p->cH,rowIdx,kDynColScIdx)) != NULL )
218
+  {
219
+    if((dynVal = _cmScDynLabelToId(attr)) == kInvalidDynScId )
220
+      return cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown dynamic label '%s'.",cmStringNullGuard(attr));
221
+
222
+    flags += kDynScFl;
223
+  }
224
+
225
+  s->type       = kNonEvtScId;
226
+  s->pitch      = midiPitch;
227
+  s->flags      = flags;
228
+  s->dynVal     = dynVal; 
229
+  s->barNumb    = barNumb;
230
+  s->barNoteIdx = barNoteIdx;
231
+
232
+  return rc;
233
+}
234
+
235
+cmScRC_t _cmScParseFile( cmSc_t* p, cmCtx_t* ctx, const cmChar_t* fn )
236
+{
237
+  cmScRC_t rc = kOkScRC;
238
+  unsigned barNoteIdx;
239
+  int      barNumb;
240
+
241
+  if( cmCsvInitialize(&p->cH, ctx ) != kOkCsvRC )
242
+  {
243
+    rc = cmErrMsg(&p->err,kCsvFailScRC,"Score file initialization failed.");
244
+    goto errLabel;
245
+  }
246
+
247
+  if( cmCsvLexRegisterMatcher(p->cH, cmCsvLexNextAvailId(p->cH), _cmScLexSciPitchMatcher ) != kOkCsvRC )
248
+  {
249
+    rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV token matcher registration failed.");
250
+    goto errLabel;
251
+  }
252
+
253
+  if( cmCsvParseFile(p->cH, fn, 0 ) != kOkCsvRC )
254
+  {
255
+    rc = cmErrMsg(&p->err,kCsvFailScRC,"CSV file parsing failed on the file '%s'.",cmStringNullGuard(fn));
256
+    goto errLabel;
257
+  }
258
+
259
+  p->cnt   = cmCsvRowCount(p->cH);
260
+  p->array = cmMemAllocZ(cmScoreEvt_t,p->cnt);
261
+
262
+  unsigned i,j;
263
+
264
+  // skip labels line - start on line 1
265
+  for(i=1,j=0; i<p->cnt && rc==kOkScRC; ++i)
266
+  {
267
+    // get the row 'type' label
268
+    const char* typeLabel;
269
+    if((typeLabel = cmCsvCellText(p->cH,i,kTypeLabelColScIdx)) == NULL )
270
+    {
271
+      rc = cmErrMsg(&p->err,kSyntaxErrScRC,"No type label.");
272
+      break;
273
+    }
274
+
275
+    // convert the row 'type' label to an id
276
+    unsigned tid;
277
+    if((tid =  _cmScEvtTypeLabelToId(typeLabel)) == kInvalidEvtScId) 
278
+    {
279
+      rc = cmErrMsg(&p->err,kSyntaxErrScRC,"Unknown type '%s'.",cmStringNullGuard(typeLabel));
280
+      break;
281
+    }
282
+
283
+    
284
+    switch(tid)
285
+    {
286
+      case kBarEvtScId:
287
+        // parse bar lines
288
+        if((rc = _cmScParseBar(p,i,&barNumb)) == kOkScRC )
289
+          barNoteIdx = 0;
290
+        break;
291
+
292
+      case kNonEvtScId:
293
+        // parse note-on events
294
+        if((rc =  _cmScParseNoteOn(p, i, p->array + j, barNumb, barNoteIdx )) == kOkScRC )
295
+        {
296
+          if( cmIsFlag(p->array[j].flags,kSkipScFl) == false )
297
+            ++j;
298
+
299
+          ++barNoteIdx;
300
+        }
301
+        break;
302
+
303
+      default:
304
+        break;
305
+    }
306
+    
307
+  }
308
+
309
+  if( rc == kSyntaxErrScRC )
310
+  {
311
+    cmErrMsg(&p->err,rc,"Syntax error on line %i in '%s'.",i+1,cmStringNullGuard(fn));           
312
+    goto errLabel;
313
+  }
314
+
315
+  p->cnt = i;
316
+  
317
+ errLabel:
318
+
319
+  return rc;
320
+}
321
+
322
+cmScRC_t cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn )
323
+{
324
+  cmScRC_t rc = kOkScRC;
325
+  if((rc = cmScoreFinalize(hp)) != kOkScRC )
326
+    return rc;
327
+
328
+  cmSc_t* p = cmMemAllocZ(cmSc_t,1);
329
+
330
+  cmErrSetup(&p->err,&ctx->rpt,"Score");
331
+
332
+  if((rc = _cmScParseFile(p,ctx,fn)) != kOkScRC )
333
+    goto errLabel;
334
+
335
+  hp->h = p;
336
+
337
+ errLabel:
338
+  if( rc != kOkScRC )
339
+    _cmScFinalize(p);
340
+
341
+  return rc;
342
+}
343
+
344
+cmScRC_t cmScoreFinalize( cmScH_t* hp )
345
+{
346
+  cmScRC_t rc = kOkScRC;
347
+
348
+  if( hp == NULL || cmScoreIsValid(*hp) == false )
349
+    return kOkScRC;
350
+
351
+  cmSc_t* p = _cmScHandleToPtr(*hp);
352
+
353
+  if((rc = _cmScFinalize(p)) != kOkScRC )
354
+    return rc;
355
+  
356
+  hp->h = NULL;
357
+  
358
+  return rc;
359
+}
360
+
361
+bool     cmScoreIsValid( cmScH_t h )
362
+{ return h.h != NULL; }
363
+
364
+unsigned      cmScoreEvtCount( cmScH_t h )
365
+{
366
+  cmSc_t* p = _cmScHandleToPtr(h);
367
+  return p->cnt;
368
+}
369
+
370
+cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx )
371
+{
372
+  cmSc_t* p = _cmScHandleToPtr(h);
373
+  if( idx >= p->cnt )
374
+  {
375
+    cmErrMsg(&p->err,kInvalidIdxScRC,"%i is an invalid index for %i records.",idx,p->cnt);
376
+    return NULL;
377
+  }
378
+  return p->array + idx;
379
+}
380
+
381
+void cmScorePrint( cmScH_t h, cmRpt_t* rpt )
382
+{
383
+  cmSc_t* p = _cmScHandleToPtr(h);
384
+  unsigned i;
385
+  for(i=0; i<20 /*p->cnt*/; ++i)
386
+  {
387
+    cmScoreEvt_t* r = p->array + i;
388
+    switch(r->type)
389
+    {
390
+      case kNonEvtScId:
391
+        cmRptPrintf(rpt,"%5i %3i %3i %s 0x%2x %c%c%c %s\n",
392
+          i,
393
+          r->barNumb,
394
+          r->barNoteIdx,
395
+          _cmScEvtTypeIdToLabel(r->type),
396
+          r->pitch,
397
+          cmIsFlag(r->flags,kEvenScFl)  ? 'e' : ' ',
398
+          cmIsFlag(r->flags,kTempoScFl) ? 't' : ' ',
399
+          cmIsFlag(r->flags,kDynScFl)   ? 'd' : ' ',
400
+          cmIsFlag(r->flags,kDynScFl)   ? _cmScDynIdToLabel(r->dynVal) : "");          
401
+        break;
402
+
403
+      default:
404
+        break;
405
+    }
406
+  }
407
+}
408
+
409
+// Each time line note-on object is decorated (via cmTlObj_t.userDataPtr) with a
410
+// cmScSyncState_t record.  
411
+typedef struct 
412
+{
413
+  unsigned cnt;        // count of candidate sync locations
414
+  double   dist;       // edit distance to the closest sync location
415
+  unsigned scEvtIdx;   // score record this note-on is assigned to
416
+} cmScSyncState_t;
417
+
418
+void _cmScSyncTimeLineAllocFree( cmTlH_t tlH, bool allocFl )
419
+{
420
+  cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
421
+  
422
+  for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
423
+    if( mep->msg->status == kNoteOnMdId )
424
+    {
425
+      if( allocFl )
426
+        mep->obj.userDataPtr = cmMemAllocZ(cmScSyncState_t,1);      
427
+      else
428
+        cmMemPtrFree(&mep->obj.userDataPtr);
429
+    }
430
+}
431
+
432
+void _cmScPrintSyncState( cmSc_t* p, cmTlH_t tlH )
433
+{
434
+  unsigned i = 0;
435
+  double sr = cmTimeLineSampleRate(tlH);
436
+  cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
437
+  
438
+  for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId))
439
+    if( mep->msg->status == kNoteOnMdId )
440
+    {
441
+      cmScSyncState_t* ssp        = (cmScSyncState_t*)mep->obj.userDataPtr;
442
+
443
+      cmRptPrintf(p->err.rpt,"%5.3f pit:0x%2x (%3i) bar:%3i bni:%3i cnt:%3i dst:%1.6f ref:%s\n",
444
+        (mep->obj.ref->begSmpIdx - mep->obj.begSmpIdx) / (sr*60),
445
+        mep->msg->u.chMsgPtr->d0,
446
+        mep->msg->u.chMsgPtr->d0,
447
+        ssp->cnt ? p->array[ ssp->scEvtIdx ].barNumb    : 0,
448
+        ssp->cnt ? p->array[ ssp->scEvtIdx ].barNoteIdx : 0,
449
+        ssp->cnt,
450
+        ssp->dist,
451
+        cmStringNullGuard(mep->obj.ref->name));
452
+
453
+      ++i;
454
+      if( i>=300)
455
+        break;
456
+    }
457
+}
458
+
459
+double _cmScWndEditDist( cmSc_t* p,  unsigned* mtx, const unsigned* tlWnd, cmScSyncState_t* tlObjWnd[], unsigned wndCnt )
460
+{
461
+  unsigned scWnd[ wndCnt ];
462
+  unsigned scIdxWnd[ wndCnt ];
463
+  unsigned i;
464
+  unsigned wn = 0;
465
+  double   minDist = DBL_MAX;
466
+
467
+  // for each note-on score event
468
+  for(i=0; i<p->cnt; ++i)
469
+    if( p->array[i].type == kNonEvtScId )
470
+    {
471
+      // shift the score event window to the the left
472
+      memmove(scWnd,   scWnd+1,   (wndCnt-1)*sizeof(scWnd[0]));
473
+      memmove(scIdxWnd,scIdxWnd+1,(wndCnt-1)*sizeof(scIdxWnd[0]));
474
+
475
+      // insert new score event data on right
476
+      scWnd[wndCnt-1]    = p->array[i].pitch;
477
+      scIdxWnd[wndCnt-1] = i;
478
+      ++wn;
479
+
480
+      // if the window is full
481
+      if(wn >= wndCnt )
482
+      {
483
+        // score the edit distance between the time line window and the edit window
484
+        double dist = cmVOU_LevEditDist(wndCnt,mtx,scWnd,wndCnt,tlWnd,wndCnt,wndCnt);
485
+
486
+        if( dist < minDist )
487
+          minDist = dist;
488
+
489
+        // update the match information in the time line window
490
+        unsigned j;
491
+        for(j=0; j<wndCnt; ++j)
492
+        {
493
+          // if the pitch matches and the score is less than the previous score
494
+          if( scWnd[j] == tlWnd[j] && (tlObjWnd[j]->cnt == 0 || dist < tlObjWnd[j]->dist) )
495
+          {
496
+            tlObjWnd[j]->cnt      += 1;
497
+            tlObjWnd[j]->dist      = dist;
498
+            tlObjWnd[j]->scEvtIdx  = scIdxWnd[j];
499
+          }
500
+        }
501
+      }      
502
+    }
503
+  
504
+  return minDist;
505
+}
506
+
507
+cmScRC_t cmScoreSyncTimeLine( cmScH_t scH, cmTlH_t tlH, unsigned edWndCnt, cmReal_t maxSecs )
508
+{
509
+  cmSc_t*          p            = _cmScHandleToPtr(scH);
510
+  unsigned*        edWndMtx     = cmVOU_LevEditDistAllocMtx(edWndCnt);
511
+  unsigned         maxMicroSecs = floor(maxSecs*1000000);
512
+  unsigned         edWndData[ edWndCnt ];
513
+  cmScSyncState_t* edWndObj[ edWndCnt ];
514
+
515
+  // alloc a sync state record for each MIDI note-on in the time line
516
+  _cmScSyncTimeLineAllocFree(tlH, true );
517
+
518
+  // get the first time line object
519
+  cmTlObj_t*      rfp = cmTimeLineNextTypeObj(tlH,NULL,cmInvalidId,kMidiFileTlId);
520
+
521
+  // interate through the time line in search of MIDI file objects
522
+  for(; rfp != NULL; rfp = cmTimeLineNextTypeObj(tlH,rfp,cmInvalidId,kMidiFileTlId))
523
+  {
524
+    cmTlMidiFile_t* mfp         = cmTimeLineMidiFileObjPtr(tlH,rfp);
525
+    unsigned        curEdWndCnt = 0;
526
+    double          prog        = 0.1;
527
+    unsigned        progIdx     = 0;
528
+
529
+    cmRptPrintf(p->err.rpt,"MIDI File:%s\n", cmMidiFileName( mfp->h ));
530
+
531
+    // get first midi event object
532
+    cmTlMidiEvt_t* mep = cmTlNextMidiEvtObjPtr(tlH,NULL,cmInvalidId);
533
+
534
+    // iterate through the time line in search of MIDI note-on events with belong to mfp      
535
+    for(; mep != NULL; mep = cmTlNextMidiEvtObjPtr(tlH,&mep->obj,cmInvalidId) )
536
+    {
537
+      if( mep->obj.ref == rfp && mep->msg->status == kNoteOnMdId )
538
+      {
539
+        // If this notes inter-onset time is greater than maxMicroSecs
540
+        // then dispose of the current window and begin refilling it again.
541
+        if( mep->msg->dtick > maxMicroSecs )
542
+          curEdWndCnt = 0;
543
+
544
+        // shift window one slot to left
545
+        unsigned i;
546
+        for(i=0; i<edWndCnt-1; ++i)
547
+        {
548
+          edWndData[i] = edWndData[i+1];
549
+          edWndObj[i]  = edWndObj[i+1];
550
+        }
551
+        
552
+        // fill window on right
553
+        edWndData[edWndCnt-1] = mep->msg->u.chMsgPtr->d0; // d0=pitch
554
+        edWndObj[ edWndCnt-1] = (cmScSyncState_t*)mep->obj.userDataPtr;
555
+
556
+        ++curEdWndCnt;
557
+        
558
+        // if a complete window exists then update the time-line / score match state
559
+        if( curEdWndCnt >= edWndCnt )
560
+         _cmScWndEditDist( p, edWndMtx, edWndData, edWndObj, edWndCnt );       
561
+
562
+        // print the progress
563
+        ++progIdx;
564
+        if( progIdx >= prog * mfp->noteOnCnt )
565
+        {
566
+          cmRptPrintf(p->err.rpt,"%i ",(unsigned)round(prog*10));
567
+          prog += 0.1;
568
+        }
569
+      }
570
+    }
571
+    cmRptPrintf(p->err.rpt,"\n");
572
+  }
573
+
574
+  _cmScPrintSyncState(p,tlH );
575
+
576
+  // free sync state records
577
+  _cmScSyncTimeLineAllocFree(tlH,false);
578
+
579
+  cmMemFree(edWndMtx);
580
+
581
+  return kOkScRC;
582
+}
583
+
584
+
585
+cmScRC_t cmScoreSyncTimeLineTest( cmCtx_t* ctx,  const cmChar_t* timeLineJsFn, const cmChar_t* scoreCsvFn )
586
+{
587
+  cmScRC_t rc  = kOkScRC;
588
+  cmTlH_t  tlH = cmTimeLineNullHandle;
589
+  cmScH_t  scH = cmScNullHandle;
590
+  unsigned edWndCnt     = 7;
591
+  cmReal_t maxSecs = 2.0;
592
+
593
+  if((rc = cmTimeLineInitialize(ctx,&tlH,NULL,NULL)) != kOkTlRC )
594
+    return cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line initialization failed.");;
595
+
596
+  if((rc = cmTimeLineReadJson(tlH,timeLineJsFn)) != kOkTlRC )
597
+  {
598
+    rc = cmErrMsg(&ctx->err,kTimeLineFailScRC,"Time line parse failed.");;
599
+    goto errLabel;
600
+  }
601
+
602
+  //cmTimeLinePrint(tlH,&ctx->rpt);
603
+
604
+  if(1)
605
+  {
606
+    if((rc = cmScoreInitialize(ctx,&scH,scoreCsvFn))  != kOkScRC )
607
+      goto errLabel;
608
+
609
+
610
+    rc = cmScoreSyncTimeLine(scH, tlH, edWndCnt, maxSecs );
611
+
612
+  }
613
+  //cmScorePrint(scH, ctx->err.rpt );
614
+
615
+  
616
+
617
+ errLabel:
618
+  cmScoreFinalize(&scH);
619
+  cmTimeLineFinalize(&tlH);
620
+
621
+  return rc;
622
+
623
+}
624
+
625
+
626
+void cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn )
627
+{
628
+  cmScH_t h = cmScNullHandle;
629
+  if( cmScoreInitialize(ctx,&h,fn) != kOkScRC )
630
+    return;
631
+
632
+  cmScorePrint(h,&ctx->rpt);
633
+
634
+  cmScoreFinalize(&h);
635
+}

+ 82
- 0
app/cmScore.h View File

@@ -0,0 +1,82 @@
1
+#ifndef cmScore_h
2
+#define cmScore_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkScRC = cmOkRC,
11
+    kCsvFailScRC,
12
+    kSyntaxErrScRC,
13
+    kInvalidIdxScRC,
14
+    kTimeLineFailScRC
15
+  
16
+  };
17
+
18
+  enum
19
+  {
20
+    kInvalidEvtScId = 0,
21
+    kTimeSigEvtScId,
22
+    kKeySigEvtScId,
23
+    kTempoEvtScId,
24
+    kTrackEvtScId,
25
+    kTextEvtScId,
26
+    kEOTrackEvtScId,
27
+    kCopyEvtScId,
28
+    kBlankEvtScId,
29
+    kBarEvtScId,
30
+    kPgmEvtScId,
31
+    kCtlEvtScId,
32
+    kNonEvtScId
33
+  };
34
+
35
+  enum
36
+  {
37
+    kEvenScFl  = 0x01,  // This note is marked for evenness measurement
38
+    kDynScFl   = 0x02,  // This note is marked for dynamics measurement
39
+    kTempoScFl = 0x03,  // This note is marked for tempo measurement
40
+    kSkipScFl  = 0x04   // this isn't a real event (e.g. tied note) skip over it
41
+  };
42
+
43
+  typedef struct
44
+  {
45
+    unsigned     type;         // Event type
46
+    double       dsecs;        // 
47
+    cmMidiByte_t pitch;        // MIDI pitch of this note
48
+    unsigned     flags;        // Attribute flags for this event
49
+    unsigned     dynVal;       // Dynamcis value pppp to ffff (1 to 11) for this note.
50
+    unsigned     barNumb;      // bar number of this event
51
+    unsigned     barNoteIdx;   // index of this note in this bar
52
+  } cmScoreEvt_t;
53
+
54
+
55
+  typedef cmRC_t     cmScRC_t;
56
+  typedef cmHandle_t cmScH_t;
57
+  
58
+  extern cmScH_t cmScNullHandle;
59
+
60
+  // Initialize a score object from a CSV File generated from a score spreadsheet.
61
+  cmScRC_t      cmScoreInitialize( cmCtx_t* ctx, cmScH_t* hp, const cmChar_t* fn );
62
+  cmScRC_t      cmScoreFinalize( cmScH_t* hp );
63
+
64
+  bool          cmScoreIsValid( cmScH_t h );
65
+
66
+  // Access the score data.
67
+  unsigned      cmScoreEvtCount( cmScH_t h );
68
+  cmScoreEvt_t* cmScoreEvt( cmScH_t h, unsigned idx );
69
+
70
+  void          cmScorePrint( cmScH_t h, cmRpt_t* rpt );
71
+
72
+  cmScRC_t      cmScoreSyncTimeLine( cmScH_t scH, cmTlH_t tlH, unsigned editDistWndCnt, cmReal_t maxNoteOffsetSecs );
73
+
74
+  cmScRC_t      cmScoreSyncTimeLineTest( cmCtx_t* ctx,  const cmChar_t* timeLineJsFn, const cmChar_t* scoreCsvFn );
75
+
76
+  void           cmScoreTest( cmCtx_t* ctx, const cmChar_t* fn );
77
+
78
+#ifdef __cplusplus
79
+}
80
+#endif
81
+
82
+#endif

+ 1542
- 0
app/cmTimeLine.c
File diff suppressed because it is too large
View File


+ 217
- 0
app/cmTimeLine.h View File

@@ -0,0 +1,217 @@
1
+#ifndef cmTimeLine_h
2
+#define cmTimeLine_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+
9
+  typedef cmHandle_t cmTlH_t;
10
+
11
+  typedef cmRC_t cmTlRC_t;
12
+
13
+  enum
14
+  {
15
+    kOkTlRC = cmOkRC,
16
+    kLHeapFailTlRC,
17
+    kParseFailTlRC,
18
+    kJsonFailTlRC,
19
+    kDuplNameTlRC,
20
+    kRefNotFoundTlRC,
21
+    kAudioFileFailTlRC,
22
+    kMidiFileFailTlRC,
23
+    kTypeCvtFailTlRC,
24
+    kUnknownRecdTypeTlRC,
25
+    kFinalizeFailTlRC,
26
+    kInvalidSeqIdTlRC
27
+  };
28
+
29
+  typedef enum
30
+  {
31
+    kMidiFileTlId  = 0x01,
32
+    kMidiEvtTlId   = 0x02,
33
+    kAudioFileTlId = 0x03,
34
+    kAudioEvtTlId  = 0x04,
35
+    kMarkerTlId    = 0x05
36
+  } cmTlObjTypeId_t;
37
+
38
+  enum
39
+  {
40
+    kReservedTlFl = 0x01,
41
+    kNoWriteTlFl  = 0x02  // do not write this object in cmTimeLineWrite()
42
+  };
43
+
44
+  typedef void (*cmTlCb_t)( void* arg, const void* data, unsigned byteCnt );
45
+
46
+  typedef struct cmTlObj_str
47
+  {
48
+    void*               reserved;    // pt's to _cmTlObj_t 
49
+    unsigned            seqId;       // sequence this object is assigned to
50
+    const cmChar_t*     name;        // text name of this object
51
+    unsigned            uid;         // generated unique id for this object
52
+    cmTlObjTypeId_t     typeId;      // type of the object
53
+    struct cmTlObj_str* ref;         // time reference object
54
+    int                 begSmpIdx;   // start time of this object as an offset from the start time of the reference object
55
+    unsigned            durSmpCnt;   // duration of this object
56
+    int                 seqSmpIdx;   // absolute start time of this object within the sequence
57
+    const cmChar_t*     text;        // points to text assoc'd with this node (file name for audio/midi file, marker text)
58
+    unsigned            flags;       // see kXXXTlFl 
59
+    void*               userDataPtr; // user customizable data pointer 
60
+  } cmTlObj_t;
61
+
62
+  typedef struct
63
+  {
64
+    cmTlObj_t     obj;
65
+    cmMidiFileH_t h;
66
+    unsigned      noteOnCnt;
67
+    cmChar_t*     fn;
68
+  } cmTlMidiFile_t;
69
+
70
+  typedef struct
71
+  {
72
+    cmTlObj_t               obj;
73
+    unsigned                midiFileObjId;
74
+    const cmMidiTrackMsg_t* msg;       // w/ dticks converted to microsecs
75
+  } cmTlMidiEvt_t;
76
+
77
+
78
+  typedef struct
79
+  {
80
+    cmTlObj_t         obj;
81
+    cmAudioFileH_t    h;
82
+    cmAudioFileInfo_t info;
83
+    cmChar_t*         fn;
84
+  } cmTlAudioFile_t;
85
+
86
+  typedef struct
87
+  {
88
+    cmTlObj_t       obj;
89
+    cmAudioFileH_t  h;
90
+    unsigned        smpIdx;
91
+    unsigned        smpCnt;
92
+    cmChar_t*       text;
93
+  } cmTlAudioEvt_t;
94
+
95
+  typedef struct
96
+  {
97
+    cmTlObj_t       obj;
98
+    const cmChar_t* text;
99
+  } cmTlMarker_t;
100
+
101
+  extern cmTlH_t cmTimeLineNullHandle;
102
+
103
+  // 
104
+  cmTlRC_t   cmTimeLineInitialize( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg );
105
+  cmTlRC_t   cmTimeLineInitializeFromFile( cmCtx_t* ctx, cmTlH_t* hp, cmTlCb_t cbFunc, void* cbArg, const cmChar_t* fn );
106
+
107
+  cmTlRC_t   cmTimeLineFinalize( cmTlH_t* hp );
108
+
109
+  bool       cmTimeLineIsValid( cmTlH_t h );
110
+  double     cmTimeLineSampleRate( cmTlH_t h );
111
+
112
+  // Return the object following 'p' assigned to 'seqId'.
113
+  // If 'p' is NULL then return the first object assigned to seqId.
114
+  // If 'seqId' is set to cmInvalidId then return the next object on any seq.
115
+  // If no objects follow 'p' on the specified sequence then return NULL.
116
+  cmTlObj_t* cmTimeLineNextObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId );
117
+
118
+  // Same as cmTimeLineNextObj() but returns next object whose type matches 'typeId'.
119
+  cmTlObj_t* cmTimeLineNextTypeObj( cmTlH_t h, cmTlObj_t* p, unsigned seqId, unsigned typeId );
120
+
121
+  cmTlMidiFile_t*  cmTlNextMidiFileObjPtr(  cmTlH_t h, cmTlObj_t* op, unsigned seqId );
122
+  cmTlAudioFile_t* cmTlNextAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op, unsigned seqId );
123
+  cmTlMidiEvt_t*   cmTlNextMidiEvtObjPtr(   cmTlH_t h, cmTlObj_t* op, unsigned seqId );
124
+  cmTlAudioEvt_t*  cmTlNextAudioEvtObjPtr(  cmTlH_t h, cmTlObj_t* op, unsigned seqId );
125
+  cmTlMarker_t*    cmTlNextMarkerObjPtr(    cmTlH_t h, cmTlObj_t* op, unsigned seqId );
126
+
127
+
128
+  // Cast a genereic cmTlObj_t pointer to a specificy type.
129
+  cmTlMidiFile_t*  cmTimeLineMidiFileObjPtr(  cmTlH_t h, cmTlObj_t* op );
130
+  cmTlAudioFile_t* cmTimeLineAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op );
131
+  cmTlMidiEvt_t*   cmTimeLineMidiEvtObjPtr(   cmTlH_t h, cmTlObj_t* op );
132
+  cmTlAudioEvt_t*  cmTimeLineAudioEvtObjPtr(  cmTlH_t h, cmTlObj_t* op );
133
+  cmTlMarker_t*    cmTimeLineMarkerObjPtr(    cmTlH_t h, cmTlObj_t* op );
134
+
135
+  // Same as cmTimeLineXXXObjPtr() but does not generate an error when
136
+  // 'op' does not point to the correct type. These function quietly
137
+  // return NULL if the requested type does not match.
138
+  cmTlMidiFile_t*  cmTlMidiFileObjPtr(  cmTlH_t h, cmTlObj_t* op );
139
+  cmTlAudioFile_t* cmTlAudioFileObjPtr( cmTlH_t h, cmTlObj_t* op );
140
+  cmTlMidiEvt_t*   cmTlMidiEvtObjPtr(   cmTlH_t h, cmTlObj_t* op );
141
+  cmTlAudioEvt_t*  cmTlAudioEvtObjPtr(  cmTlH_t h, cmTlObj_t* op );
142
+  cmTlMarker_t*    cmTlMarkerObjPtr(    cmTlH_t h, cmTlObj_t* op );
143
+
144
+  cmTlAudioFile_t* cmTimeLineFindAudioFile( cmTlH_t h, const cmChar_t* fn );
145
+  cmTlMidiFile_t*  cmTimeLineFindMidiFile( cmTlH_t h, const cmChar_t* fn );
146
+
147
+  // 'typeId' = kAudioFileTlId, kMidiFileTId, kMarkerTlId.
148
+  // 'nameStr' and 'refObjNameStr' may be NULL.
149
+  cmTlRC_t cmTimeLineInsert( 
150
+    cmTlH_t         h, 
151
+    const cmChar_t* nameStr, 
152
+    unsigned        typeId, 
153
+    const cmChar_t* fn_or_markerStr, 
154
+    int             begSmpIdx, 
155
+    unsigned        durSmpCnt, 
156
+    const cmChar_t* refObjNameStr, 
157
+    unsigned        seqId ); 
158
+  
159
+  // See src/data/tl0.json for an example JSON file.
160
+  cmTlRC_t cmTimeLineReadJson(  cmTlH_t h, const cmChar_t* ifn );
161
+
162
+  // Return a count of sequences contained within this timeline.
163
+  unsigned cmTimeLineSeqCount( cmTlH_t h );
164
+
165
+  // Make notifications for all records belonging to the sequence.
166
+  cmTlRC_t cmTimeLineSeqNotify( cmTlH_t h, unsigned seqId );
167
+
168
+  cmTlRC_t cmTimeLineWrite( cmTlH_t h, const cmChar_t* fn );
169
+
170
+  cmTlRC_t cmTimeLinePrint( cmTlH_t h, cmRpt_t* rpt );
171
+  cmTlRC_t cmTimeLinePrintFn( cmCtx_t* ctx, const cmChar_t* fn, cmRpt_t* rpt );
172
+
173
+  cmTlRC_t cmTimeLineTest( cmCtx_t* ctx, const cmChar_t* jsFn  );
174
+
175
+  // The time-line notifies listeners of initialization and finalization
176
+  // events via calling a cmTlCbFunc_t function.  The argument to this 
177
+  // function is a serialized cmTlUiMsg_t.  The recipient of the callback
178
+  // can extract information from this message using cmTimeLineDecode()
179
+  // to form a cmTlUiMsg_t record. Note that all pointers internal to the
180
+  // cmTlUiMsg_t point into the message buffer itself.
181
+  
182
+  // id's used to indicate the type of a serialized object
183
+  typedef enum
184
+  {
185
+    kInvalidMsgTlId,
186
+    kInitMsgTlId,      // A new time-line object is begin intialized.  
187
+    kFinalMsgTlId,     // A time-line object is being finalized.
188
+    kDoneMsgTlId,      // All the objects assoc'd with a time line seq-notify have been sent.
189
+    kInsertMsgTlId,    // A time-line object was inserted.
190
+  } cmTlUiMsgTypeId_t;
191
+
192
+  typedef struct
193
+  {
194
+    cmTlUiMsgTypeId_t       msgId;         // See cmTlUiMsgTypeId_t.
195
+    unsigned                objId;         // Set to cmTlObj_t.uid
196
+    unsigned                parentObjId;   // cmTlObj_t.uid of the object this object's begSmpIdx is set relative to.
197
+    unsigned                seqId;         // 
198
+    cmTlObjTypeId_t         typeId;        // 
199
+    int                     begSmpIdx;     // Time relative to parent.
200
+    unsigned                durSmpCnt;     // Duration of the object.
201
+    const char*             label;         // Object label (points to memory inside the serialized msg.)
202
+    double                  srate;         // Only valid with kInitMsgTlId.
203
+    unsigned                seqCnt;        // Only valid with kInitMsgTlId.
204
+    const cmMidiTrackMsg_t* midiTrkMsg;    // Only valid for typeId  == kMidiEvtTlId. Internal pointers refer to memory inside the serialzed msg. buffer.
205
+    unsigned                midiFileObjId; // Only valid for typeId  == kMidiEvtTlId
206
+    const char*             textStr;       // filename for kAudioFileTlId and kMidiFileTlId, marker text for kMarkerTlId
207
+  } cmTlUiMsg_t;
208
+
209
+  // Decode a serialized cmTlObj_t as passed to the cmTlCb_t listener
210
+  // callback function.
211
+  cmTlRC_t cmTimeLineDecode( const void* msg, unsigned msgByteCnt, cmTlUiMsg_t* uiMsg );
212
+
213
+#ifdef __cplusplus
214
+}
215
+#endif
216
+
217
+#endif

+ 910
- 0
cmApBuf.c View File

@@ -0,0 +1,910 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmAudioPort.h"
8
+#include "cmApBuf.h"
9
+#include "cmThread.h"
10
+
11
+/*
12
+  This API is in general called by two types of threads:
13
+  audio devices threads and the client thread.  There
14
+  may be multiple devie threads however there is only
15
+  one client thread.
16
+
17
+  The audio device threads only call cmApBufUpdate().
18
+  cmApBufUpdate() is never called by any other threads.
19
+  A call from the audio update threads targets specific channels
20
+  (cmApCh records).  The variables within each channels that
21
+  it modifies are confined to:
22
+    on input channels:   increments ii and increments fn (data is entering the ch. buffers)
23
+    on output channels:  increments oi and decrements fn (data is leaving the ch. buffers)
24
+
25
+  The client picks up incoming audio and provides outgoing audio via
26
+  cmApBufGet(). It then informs the cmApBuf() that it has completed
27
+  the audio data transfer by calling cmApBufAdvance().
28
+
29
+  cmApBufAdvance() modifies the following internal variables:
30
+    on input channels:  increments oi and decrements fn (data has left the ch buffer)
31
+    on output channels: increments ii and increments fn (data has enterned the ch. buffer)
32
+
33
+  Based on the above scenario the channel ii and oi variables are always thread-safe
34
+  because they are only changed by a single thread. 
35
+
36
+               ii       oi     fn
37
+              ------   -----  ----
38
+  input  ch:  audio    client both
39
+  output ch:  client   audio  both
40
+  
41
+  The fn variable however is not thread-safe and therefore care must be taken as
42
+  to how it is read and updated.
43
+  
44
+  
45
+  
46
+ */
47
+
48
+enum { kInApIdx=0, kOutApIdx=1, kIoApCnt=2 };
49
+
50
+typedef struct
51
+{
52
+  unsigned      fl;   // kChApFl|kToneApFl|kMeterApFl ...
53
+  cmApSample_t* b;    // b[n]
54
+  unsigned      ii;   // next in
55
+  unsigned      oi;   // next out
56
+  unsigned      fn;   // full cnt  - count of samples currently in the buffer - incr'd by incoming, decr'd by outgoing
57
+  unsigned      phs;  // tone phase
58
+  double        hz;   // tone frequency 
59
+  double        gain; // channel gain
60
+  cmApSample_t* m;    // m[mn] meter sample sum  
61
+  unsigned      mn;   // length of m[]
62
+  unsigned      mi;   // next ele of m[] to rcv sum
63
+} cmApCh;
64
+
65
+typedef struct
66
+{
67
+  unsigned    chCnt;
68
+  cmApCh*     chArray;
69
+
70
+  unsigned    n;     // length of b[] (multiple of dspFrameCnt)  bufCnt*framesPerCycle
71
+  double      srate; // device sample rate;
72
+
73
+  unsigned    faultCnt;
74
+  unsigned    framesPerCycle;
75
+  unsigned    dspFrameCnt;
76
+
77
+} cmApIO;
78
+
79
+typedef struct
80
+{
81
+  // ioArray[] always contains 2 elements - one for input the other for output.
82
+  cmApIO     ioArray[kIoApCnt]; 
83
+} cmApDev;
84
+
85
+typedef struct
86
+{
87
+  cmApDev*   devArray;       
88
+  unsigned   devCnt;
89
+  unsigned   meterMs;
90
+
91
+  cmApSample_t* zeroBuf;    // buffer of zeros 
92
+  unsigned      zeroBufCnt; // max of all dspFrameCnt for all devices.
93
+} cmApBuf;
94
+
95
+cmApBuf _cmApBuf;
96
+
97
+
98
+cmApSample_t _cmApMeterValue( const cmApCh* cp )
99
+{
100
+  double sum = 0;
101
+  unsigned i;
102
+  for(i=0; i<cp->mn; ++i)
103
+    sum += cp->m[i];
104
+
105
+  return (cmApSample_t)sqrt(sum);
106
+}
107
+
108
+void _cmApSine( cmApCh* cp, cmApSample_t* b0, unsigned n0, cmApSample_t* b1, unsigned n1, unsigned stride, float srate )
109
+{
110
+  unsigned i;
111
+
112
+  for(i=0; i<n0; ++i,++cp->phs)
113
+    b0[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
114
+
115
+  for(i=0; i<n1; ++i,++cp->phs)
116
+    b1[i*stride] = (float)(cp->gain * sin( 2.0 * M_PI * cp->hz * cp->phs / srate ));
117
+}
118
+
119
+cmApSample_t _cmApMeter( const cmApSample_t* b, unsigned bn, unsigned stride )
120
+{
121
+  const cmApSample_t* ep  = b + bn;
122
+  cmApSample_t        sum = 0;
123
+
124
+  for(; b<ep; b+=stride)
125
+    sum += *b * *b;
126
+
127
+  return sum / bn;
128
+}
129
+
130
+void _cmApChFinalize( cmApCh* chPtr )
131
+{
132
+  cmMemPtrFree( &chPtr->b );
133
+  cmMemPtrFree( &chPtr->m );
134
+}
135
+
136
+// n=buf sample cnt mn=meter buf smp cnt
137
+void _cmApChInitialize( cmApCh* chPtr, unsigned n, unsigned mn )
138
+{
139
+  _cmApChFinalize(chPtr);
140
+
141
+  chPtr->b    = n==0 ? NULL : cmMemAllocZ( cmApSample_t, n );
142
+  chPtr->ii   = 0;
143
+  chPtr->oi   = 0;
144
+  chPtr->fn   = 0;
145
+  chPtr->fl   = (n!=0 ? kChApFl : 0);
146
+  chPtr->hz   = 1000;
147
+  chPtr->gain = 1.0;
148
+  chPtr->mn   = mn;
149
+  chPtr->m    = cmMemAllocZ(cmApSample_t,mn);
150
+  chPtr->mi   = 0;
151
+}
152
+
153
+void _cmApIoFinalize( cmApIO* ioPtr )
154
+{
155
+  unsigned i;
156
+  for(i=0; i<ioPtr->chCnt; ++i)
157
+    _cmApChFinalize( ioPtr->chArray + i );
158
+
159
+  cmMemPtrFree(&ioPtr->chArray);
160
+  ioPtr->chCnt = 0;
161
+  ioPtr->n     = 0;
162
+}
163
+
164
+void _cmApIoInitialize( cmApIO* ioPtr, double srate, unsigned framesPerCycle, unsigned chCnt, unsigned n, unsigned meterBufN, unsigned dspFrameCnt )
165
+{
166
+  unsigned i;
167
+
168
+  _cmApIoFinalize(ioPtr);
169
+
170
+  n += (n % dspFrameCnt); // force buffer size to be a multiple of dspFrameCnt
171
+  
172
+  ioPtr->chArray        = chCnt==0 ? NULL : cmMemAllocZ( cmApCh, chCnt );
173
+  ioPtr->chCnt          = chCnt;
174
+  ioPtr->n              = n;
175
+  ioPtr->faultCnt       = 0;
176
+  ioPtr->framesPerCycle = framesPerCycle;
177
+  ioPtr->srate          = srate;
178
+  ioPtr->dspFrameCnt    = dspFrameCnt;
179
+
180
+  for(i=0; i<chCnt; ++i )
181
+    _cmApChInitialize( ioPtr->chArray + i, n, meterBufN );
182
+
183
+}
184
+
185
+void _cmApDevFinalize( cmApDev* dp )
186
+{
187
+  unsigned i;
188
+  for(i=0; i<kIoApCnt; ++i)
189
+    _cmApIoFinalize( dp->ioArray+i);
190
+}
191
+
192
+void _cmApDevInitialize( cmApDev* dp, double srate, unsigned iFpC, unsigned iChCnt, unsigned iBufN, unsigned oFpC, unsigned oChCnt, unsigned oBufN, unsigned meterBufN, unsigned dspFrameCnt )
193
+{
194
+  unsigned i;
195
+
196
+  _cmApDevFinalize(dp);
197
+
198
+  for(i=0; i<kIoApCnt; ++i)
199
+  {
200
+    unsigned chCnt = i==kInApIdx ? iChCnt : oChCnt;
201
+    unsigned bufN  = i==kInApIdx ? iBufN  : oBufN;
202
+    unsigned fpc   = i==kInApIdx ? iFpC   : oFpC;
203
+    _cmApIoInitialize( dp->ioArray+i, srate, fpc, chCnt, bufN, meterBufN, dspFrameCnt );
204
+
205
+  }
206
+      
207
+} 
208
+
209
+cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs )
210
+{
211
+  cmAbRC_t rc;
212
+
213
+  if((rc = cmApBufFinalize()) != kOkAbRC )
214
+    return rc;
215
+
216
+  _cmApBuf.devArray        = cmMemAllocZ( cmApDev, devCnt );
217
+  _cmApBuf.devCnt          = devCnt;
218
+  _cmApBuf.meterMs         = meterMs;
219
+  return kOkAbRC;
220
+}
221
+
222
+cmAbRC_t cmApBufFinalize()
223
+{
224
+  unsigned i;
225
+  for(i=0; i<_cmApBuf.devCnt; ++i)    
226
+    _cmApDevFinalize(_cmApBuf.devArray + i);
227
+
228
+  cmMemPtrFree( &_cmApBuf.devArray );
229
+  cmMemPtrFree( &_cmApBuf.zeroBuf );
230
+  
231
+  _cmApBuf.devCnt          = 0;
232
+
233
+  return kOkAbRC;
234
+}
235
+
236
+cmAbRC_t cmApBufSetup( 
237
+  unsigned devIdx,
238
+  double   srate,
239
+  unsigned dspFrameCnt,
240
+  unsigned bufCnt,
241
+  unsigned inChCnt, 
242
+  unsigned inFramesPerCycle,
243
+  unsigned outChCnt, 
244
+  unsigned outFramesPerCycle)
245
+{
246
+  cmApDev* devPtr    = _cmApBuf.devArray + devIdx;
247
+  unsigned iBufN     = bufCnt * inFramesPerCycle;
248
+  unsigned oBufN     = bufCnt * outFramesPerCycle;
249
+  unsigned meterBufN = cmMax(1,floor(srate * _cmApBuf.meterMs / (1000.0 * outFramesPerCycle)));
250
+
251
+  _cmApDevInitialize( devPtr, srate, inFramesPerCycle, inChCnt, iBufN, outFramesPerCycle, outChCnt, oBufN, meterBufN, dspFrameCnt );
252
+
253
+  if( inFramesPerCycle > _cmApBuf.zeroBufCnt || outFramesPerCycle > _cmApBuf.zeroBufCnt )
254
+  {
255
+    _cmApBuf.zeroBufCnt = cmMax(inFramesPerCycle,outFramesPerCycle);
256
+    _cmApBuf.zeroBuf    = cmMemResizeZ(cmApSample_t,_cmApBuf.zeroBuf,_cmApBuf.zeroBufCnt);
257
+  }
258
+
259
+  return kOkAbRC;  
260
+}
261
+
262
+cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt )
263
+{
264
+  cmApIO*  iop = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
265
+  unsigned i;
266
+  
267
+  for(i=0; i<iop->chCnt; ++i)
268
+  {
269
+    cmApCh*  cp = iop->chArray + i;
270
+    unsigned bn = iop->n * sizeof(cmApSample_t);
271
+    memset(cp->b,0,bn);
272
+    cp->oi = 0;
273
+    cp->ii = iop->framesPerCycle * audioCycleCnt;
274
+    cp->fn = iop->framesPerCycle * audioCycleCnt;
275
+  }
276
+
277
+  return kOkAbRC;
278
+}
279
+
280
+cmAbRC_t cmApBufUpdate(
281
+  cmApAudioPacket_t*  inPktArray, 
282
+  unsigned           inPktCnt, 
283
+  cmApAudioPacket_t* outPktArray, 
284
+  unsigned           outPktCnt )
285
+{
286
+  unsigned i,j;
287
+
288
+  // copy samples from the packet to the buffer
289
+  if( inPktArray != NULL )
290
+  {
291
+    for(i=0; i<inPktCnt; ++i)
292
+    {
293
+      cmApAudioPacket_t*  pp  = inPktArray + i;           
294
+      cmApIO*             ip  = _cmApBuf.devArray[pp->devIdx].ioArray + kInApIdx; // dest io recd
295
+
296
+      // for each source packet channel and enabled dest channel
297
+      for(j=0; j<pp->chCnt; ++j)
298
+      {
299
+        cmApCh*  cp = ip->chArray + pp->begChIdx +j; // dest ch
300
+        unsigned n0 = ip->n - cp->ii;                // first dest segment
301
+        unsigned n1 = 0;                             // second dest segment
302
+
303
+        assert(pp->begChIdx + j < ip->chCnt );
304
+
305
+        // if the incoming samples  would overflow the buffer then ignore them
306
+        if( cp->fn + pp->audioFramesCnt > ip->n )
307
+        {
308
+          ++ip->faultCnt; // record input overflow 
309
+          continue;
310
+        }
311
+
312
+        // if the incoming samples would go off the end of the buffer then 
313
+        // copy in the samples in two segments (one at the end and another at begin of dest channel)
314
+        if( n0 < pp->audioFramesCnt )
315
+          n1 = pp->audioFramesCnt-n0;
316
+        else
317
+          n0 = pp->audioFramesCnt;
318
+
319
+        bool                enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
320
+        const cmApSample_t* sp    = enaFl ? ((cmApSample_t*)pp->audioBytesPtr) + j : _cmApBuf.zeroBuf;
321
+        unsigned            ssn   = enaFl ? pp->chCnt : 1;  // stride (packet samples are interleaved)
322
+        cmApSample_t*       dp    = cp->b + cp->ii;
323
+        const cmApSample_t* ep    = dp    + n0;
324
+
325
+
326
+        // update the meter
327
+        if( cmIsFlag(cp->fl,kMeterApFl) )
328
+        {
329
+          cp->m[cp->mi] = _cmApMeter(sp,pp->audioFramesCnt,pp->chCnt);
330
+          cp->mi = (cp->mi + 1) % cp->mn;
331
+        }
332
+
333
+        // if the test tone is enabled on this input channel
334
+        if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
335
+        {
336
+          _cmApSine(cp, dp, n0, cp->b, n1, 1, ip->srate );
337
+        }
338
+        else // otherwise copy samples from the packet to the buffer
339
+        {
340
+          // copy the first segment
341
+          for(; dp < ep; sp += ssn )
342
+            *dp++ = cp->gain * *sp;
343
+
344
+          // if there is a second segment
345
+          if( n1 > 0 )
346
+          {
347
+            // copy the second segment
348
+            dp = cp->b;
349
+            ep = dp + n1;
350
+            for(; dp<ep; sp += ssn )
351
+              *dp++ = cp->gain * *sp;
352
+          }
353
+        }
354
+
355
+        
356
+        // advance the input channel buffer
357
+        cp->ii  = n1>0 ? n1 : cp->ii + n0;
358
+        //cp->fn += pp->audioFramesCnt;
359
+        cmThUIntIncr(&cp->fn,pp->audioFramesCnt);
360
+      }
361
+    }
362
+  }
363
+
364
+  // copy samples from the buffer to the packet
365
+  if( outPktArray != NULL )
366
+  {
367
+    for(i=0; i<outPktCnt; ++i)
368
+    {
369
+      cmApAudioPacket_t* pp = outPktArray + i;           
370
+      cmApIO*            op = _cmApBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
371
+
372
+
373
+      // for each dest packet channel and enabled source channel
374
+      for(j=0; j<pp->chCnt; ++j)
375
+      {
376
+        cmApCh*  cp = op->chArray + pp->begChIdx + j; // dest ch
377
+        unsigned n0 = op->n - cp->oi;                 // first src segment
378
+        unsigned n1 = 0;                              // second src segment
379
+        volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
380
+
381
+        // if the outgoing samples  will underflow the buffer 
382
+        if( pp->audioFramesCnt > fn )
383
+        {
384
+          ++op->faultCnt;         // record an output underflow
385
+
386
+          // if the buffer is empty - zero the packet and return
387
+          if( fn == 0 )
388
+          {
389
+            memset( pp->audioBytesPtr, 0, pp->audioFramesCnt*sizeof(cmApSample_t));
390
+            continue;
391
+          }
392
+
393
+          // ... otherwise decrease the count of returned samples
394
+          pp->audioFramesCnt = fn;
395
+
396
+        }
397
+
398
+        // if the outgong segments would go off the end of the buffer then 
399
+        // arrange to wrap to the begining of the buffer
400
+        if( n0 < pp->audioFramesCnt )
401
+          n1 = pp->audioFramesCnt-n0;
402
+        else
403
+          n0 = pp->audioFramesCnt;
404
+
405
+        cmApSample_t* dp    = ((cmApSample_t*)pp->audioBytesPtr) + j;
406
+        bool          enaFl = cmIsFlag(cp->fl,kChApFl) && cmIsFlag(cp->fl,kMuteApFl)==false;
407
+
408
+        // if the tone is enabled on this channel
409
+        if( enaFl && cmIsFlag(cp->fl,kToneApFl) )
410
+        {
411
+          _cmApSine(cp, dp, n0, dp + n0*pp->chCnt, n1, pp->chCnt, op->srate );
412
+        }
413
+        else                    // otherwise copy samples from the output buffer to the packet
414
+        {
415
+          const cmApSample_t* sp = enaFl ? cp->b + cp->oi : _cmApBuf.zeroBuf;
416
+          const cmApSample_t* ep = sp + n0;
417
+
418
+          // copy the first segment
419
+          for(; sp < ep; dp += pp->chCnt )
420
+            *dp = cp->gain * *sp++;
421
+          
422
+          // if there is a second segment
423
+          if( n1 > 0 )
424
+          {
425
+            // copy the second segment
426
+            sp    = enaFl ? cp->b : _cmApBuf.zeroBuf;
427
+            ep    = sp + n1;
428
+            for(; sp<ep; dp += pp->chCnt )
429
+              *dp = cp->gain * *sp++;
430
+
431
+          }
432
+        }
433
+
434
+        // update the meter
435
+        if( cmIsFlag(cp->fl,kMeterApFl) )
436
+        {
437
+          cp->m[cp->mi] = _cmApMeter(((cmApSample_t*)pp->audioBytesPtr)+j,pp->audioFramesCnt,pp->chCnt);
438
+          cp->mi        = (cp->mi + 1) % cp->mn;
439
+        }
440
+
441
+        // advance the output channel buffer
442
+        cp->oi  = n1>0 ? n1 : cp->oi + n0;
443
+        //cp->fn -= pp->audioFramesCnt;
444
+        cmThUIntDecr(&cp->fn,pp->audioFramesCnt);
445
+      }
446
+    }    
447
+  }
448
+  return kOkAbRC;
449
+}
450
+
451
+unsigned cmApBufMeterMs()
452
+{ return _cmApBuf.meterMs; }
453
+
454
+unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags )
455
+{
456
+  if( devIdx == cmInvalidIdx )
457
+    return 0;
458
+
459
+  unsigned idx      = flags & kInApFl     ? kInApIdx : kOutApIdx;
460
+  return _cmApBuf.devArray[devIdx].ioArray[ idx ].chCnt; 
461
+}
462
+
463
+void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
464
+{
465
+  if( devIdx == cmInvalidIdx )
466
+    return;
467
+
468
+  unsigned idx      = flags & kInApFl     ? kInApIdx : kOutApIdx;
469
+  bool     enableFl = flags & kEnableApFl ? true : false; 
470
+  unsigned i        = chIdx != -1 ? chIdx   : 0;
471
+  unsigned n        = chIdx != -1 ? chIdx+1 : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt;
472
+  
473
+  for(; i<n; ++i)
474
+  {
475
+    cmApCh*  cp  = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + i;
476
+    cp->fl = cmEnaFlag(cp->fl, flags & (kChApFl|kToneApFl|kMeterApFl|kMuteApFl|kPassApFl), enableFl );
477
+  }
478
+  
479
+}  
480
+
481
+bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags )
482
+{
483
+  if( devIdx == cmInvalidIdx )
484
+    return false;
485
+
486
+  unsigned idx      = flags & kInApFl ? kInApIdx : kOutApIdx;
487
+  return cmIsFlag(_cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].fl,flags);
488
+}
489
+
490
+
491
+void  cmApBufEnableChannel(   unsigned devIdx, unsigned chIdx, unsigned flags )
492
+{  cmApBufSetFlag(devIdx,chIdx,flags | kChApFl); }
493
+
494
+bool  cmApBufIsChannelEnabled(   unsigned devIdx, unsigned chIdx, unsigned flags )
495
+{ return cmApBufIsFlag(devIdx, chIdx, flags | kChApFl); }
496
+
497
+void  cmApBufEnableTone(   unsigned devIdx, unsigned chIdx, unsigned flags )
498
+{ cmApBufSetFlag(devIdx,chIdx,flags | kToneApFl); }
499
+
500
+bool  cmApBufIsToneEnabled(   unsigned devIdx, unsigned chIdx, unsigned flags )
501
+{ return cmApBufIsFlag(devIdx,chIdx,flags | kToneApFl); }
502
+
503
+void  cmApBufEnableMute(   unsigned devIdx, unsigned chIdx, unsigned flags )
504
+{ cmApBufSetFlag(devIdx,chIdx,flags | kMuteApFl); }
505
+
506
+bool  cmApBufIsMuteEnabled(   unsigned devIdx, unsigned chIdx, unsigned flags )
507
+{ return cmApBufIsFlag(devIdx,chIdx,flags | kMuteApFl); }
508
+
509
+void  cmApBufEnablePass(   unsigned devIdx, unsigned chIdx, unsigned flags )
510
+{ cmApBufSetFlag(devIdx,chIdx,flags | kPassApFl); }
511
+
512
+bool  cmApBufIsPassEnabled(   unsigned devIdx, unsigned chIdx, unsigned flags )
513
+{ return cmApBufIsFlag(devIdx,chIdx,flags | kPassApFl); }
514
+
515
+void  cmApBufEnableMeter(   unsigned devIdx, unsigned chIdx, unsigned flags )
516
+{ cmApBufSetFlag(devIdx,chIdx,flags | kMeterApFl); }
517
+
518
+bool  cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags )
519
+{ return cmApBufIsFlag(devIdx,chIdx,flags | kMeterApFl); }
520
+
521
+cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags )
522
+{
523
+  if( devIdx == cmInvalidIdx )
524
+    return 0;
525
+
526
+  unsigned            idx = flags & kInApFl  ? kInApIdx : kOutApIdx;
527
+  const cmApCh*       cp  = _cmApBuf.devArray[devIdx].ioArray[idx].chArray + chIdx;
528
+  return _cmApMeterValue(cp);  
529
+} 
530
+
531
+void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain )
532
+{
533
+  if( devIdx == cmInvalidIdx )
534
+    return;
535
+
536
+  unsigned idx      = flags & kInApFl     ? kInApIdx : kOutApIdx;
537
+  unsigned i        = chIdx != -1 ? chIdx : 0;
538
+  unsigned n        = i + (chIdx != -1 ? 1     : _cmApBuf.devArray[devIdx].ioArray[idx].chCnt);
539
+
540
+  for(; i<n; ++i)
541
+    _cmApBuf.devArray[devIdx].ioArray[idx].chArray[i].gain = gain;
542
+}
543
+
544
+double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags )
545
+{
546
+  if( devIdx == cmInvalidIdx )
547
+    return 0;
548
+
549
+  unsigned idx   = flags & kInApFl  ? kInApIdx : kOutApIdx;
550
+  return  _cmApBuf.devArray[devIdx].ioArray[idx].chArray[chIdx].gain;  
551
+}
552
+
553
+
554
+unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr )
555
+{
556
+  if( devIdx == cmInvalidIdx )
557
+    return 0;
558
+
559
+  unsigned ioIdx = cmIsFlag(flags,kInApFl) ? kInApIdx : kOutApIdx;
560
+  cmApIO*  iop   = _cmApBuf.devArray[devIdx].ioArray + ioIdx;
561
+  unsigned chCnt = cmMin(iop->chCnt, meterCnt );
562
+  unsigned i;
563
+
564
+  if( faultCntPtr != NULL )
565
+    *faultCntPtr = iop->faultCnt;
566
+
567
+  for(i=0; i<chCnt; ++i)
568
+    meterArray[i] = _cmApMeterValue(iop->chArray + i);        
569
+  
570
+  return chCnt;
571
+}
572
+
573
+
574
+bool cmApBufIsDeviceReady( unsigned devIdx, unsigned flags )
575
+{
576
+  //bool     iFl = true;
577
+  //bool     oFl = true;
578
+  unsigned i   = 0;
579
+
580
+  if( devIdx == cmInvalidIdx )
581
+    return false;
582
+
583
+  if( flags & kInApFl )
584
+  {
585
+    const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
586
+    for(i=0; i<ioPtr->chCnt; ++i)
587
+      if( ioPtr->chArray[i].fn < ioPtr->dspFrameCnt )
588
+        return false;
589
+
590
+    //iFl = ioPtr->fn > ioPtr->dspFrameCnt;
591
+  }
592
+
593
+  if( flags & kOutApFl )
594
+  {
595
+    const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
596
+
597
+    for(i=0; i<ioPtr->chCnt; ++i)
598
+      if( (ioPtr->n - ioPtr->chArray[i].fn) < ioPtr->dspFrameCnt )
599
+        return false;
600
+
601
+
602
+    //oFl = (ioPtr->n - ioPtr->fn) > ioPtr->dspFrameCnt;
603
+  } 
604
+
605
+  return true;
606
+  //return iFl & oFl;  
607
+}
608
+
609
+
610
+// Note that his function returns audio samples but does NOT
611
+// change any internal states.
612
+void cmApBufGet( unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt )
613
+{
614
+  unsigned i;
615
+  if( devIdx == cmInvalidIdx )
616
+  {
617
+    for(i=0; i<bufChCnt; ++i)
618
+      bufArray[i] = NULL;
619
+    return;
620
+  }
621
+
622
+  unsigned      idx   = flags & kInApFl ? kInApIdx : kOutApIdx;
623
+  const cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + idx;
624
+  unsigned      n     = bufChCnt < ioPtr->chCnt ? bufChCnt : ioPtr->chCnt;
625
+  //unsigned      offs  = flags & kInApFl ? ioPtr->oi : ioPtr->ii; 
626
+  cmApCh*       cp    = ioPtr->chArray;
627
+
628
+  for(i=0; i<n; ++i,++cp)
629
+  {
630
+    unsigned offs = flags & kInApFl ? cp->oi : cp->ii;
631
+    bufArray[i] = cmIsFlag(cp->fl,kChApFl) ? cp->b + offs : NULL;
632
+  }
633
+  
634
+}
635
+
636
+void cmApBufGetIO(   unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt )
637
+{
638
+  cmApBufGet( iDevIdx, kInApFl, iBufArray, iBufChCnt );
639
+  cmApBufGet( oDevIdx, kOutApFl,oBufArray, oBufChCnt );
640
+
641
+  unsigned i       = 0;
642
+
643
+  if( iDevIdx != cmInvalidIdx && oDevIdx != cmInvalidIdx )
644
+  {
645
+    const cmApIO* ip       = _cmApBuf.devArray[iDevIdx].ioArray + kInApIdx;
646
+    const cmApIO* op       = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
647
+    unsigned      minChCnt = cmMin(iBufChCnt,oBufChCnt);  
648
+    unsigned      frmCnt   = cmMin(ip->dspFrameCnt,op->dspFrameCnt);
649
+    unsigned      byteCnt  = frmCnt * sizeof(cmApSample_t);
650
+
651
+    for(i=0; i<minChCnt; ++i)
652
+    {
653
+      cmApCh* ocp = op->chArray + i;
654
+      cmApCh* icp = ip->chArray + i;
655
+
656
+      if( oBufArray[i] != NULL )
657
+      {
658
+        // if either the input or output channel is marked for pass-through
659
+        if( cmAllFlags(ocp->fl,kPassApFl)  || cmAllFlags(icp->fl,kPassApFl) )
660
+        {
661
+          memcpy( oBufArray[i], iBufArray[i], byteCnt );
662
+
663
+          // set the output buffer to NULL to prevent it being over written by the client
664
+          oBufArray[i] = NULL;
665
+        }
666
+        else
667
+        {
668
+          // zero the output buffer
669
+          memset(oBufArray[i],0,byteCnt);
670
+        }
671
+      }
672
+    }
673
+  }
674
+
675
+  if( oDevIdx != cmInvalidIdx )
676
+  {
677
+    const cmApIO* op  = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
678
+    unsigned byteCnt  = op->dspFrameCnt * sizeof(cmApSample_t);
679
+
680
+    for(; i<oBufChCnt; ++i)
681
+      if( oBufArray[i] != NULL )
682
+        memset( oBufArray[i], 0, byteCnt );
683
+  }
684
+
685
+}
686
+
687
+void cmApBufAdvance( unsigned devIdx, unsigned flags )
688
+{
689
+  unsigned i;
690
+
691
+  if( devIdx == cmInvalidIdx )
692
+    return;
693
+
694
+  if( flags & kInApFl )
695
+  {
696
+    cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kInApIdx;
697
+
698
+    for(i=0; i<ioPtr->chCnt; ++i)
699
+    {
700
+      cmApCh* cp = ioPtr->chArray + i;
701
+      cp->oi     = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
702
+      //cp->fn    -= ioPtr->dspFrameCnt;
703
+      cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
704
+    }
705
+
706
+    //ioPtr->oi     = (ioPtr->oi + ioPtr->dspFrameCnt) % ioPtr->n;
707
+    //ioPtr->fn    -= ioPtr->dspFrameCnt;
708
+  }
709
+  
710
+  if( flags & kOutApFl )
711
+  {
712
+    cmApIO* ioPtr = _cmApBuf.devArray[devIdx].ioArray + kOutApIdx;
713
+    for(i=0; i<ioPtr->chCnt; ++i)
714
+    {
715
+      cmApCh* cp = ioPtr->chArray + i;
716
+      cp->ii     = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
717
+      //cp->fn    += ioPtr->dspFrameCnt;
718
+      cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
719
+    }
720
+
721
+
722
+    //ioPtr->ii     = (ioPtr->ii + ioPtr->dspFrameCnt) % ioPtr->n;
723
+    //ioPtr->fn    += ioPtr->dspFrameCnt;
724
+  }
725
+}
726
+
727
+
728
+void cmApBufInputToOutput( unsigned iDevIdx, unsigned oDevIdx )
729
+{
730
+  if( iDevIdx == cmInvalidIdx || oDevIdx == cmInvalidIdx )
731
+    return;
732
+
733
+  unsigned    iChCnt   = cmApBufChannelCount( iDevIdx, kInApFl  );
734
+  unsigned    oChCnt   = cmApBufChannelCount( oDevIdx, kOutApFl );
735
+  unsigned    chCnt    = iChCnt < oChCnt ? iChCnt : oChCnt;
736
+  
737
+  unsigned    i;
738
+
739
+  cmApSample_t* iBufPtrArray[ iChCnt ];
740
+  cmApSample_t* oBufPtrArray[ oChCnt ];
741
+
742
+
743
+  while( cmApBufIsDeviceReady( iDevIdx, kInApFl ) && cmApBufIsDeviceReady( oDevIdx, kOutApFl ) )
744
+  {
745
+    cmApBufGet( iDevIdx, kInApFl,  iBufPtrArray, iChCnt );
746
+    cmApBufGet( oDevIdx, kOutApFl, oBufPtrArray, oChCnt );
747
+
748
+    // Warning: buffer pointers to disabled channels will be set to NULL
749
+
750
+    for(i=0; i<chCnt; ++i)
751
+    {
752
+      cmApIO* ip = _cmApBuf.devArray[iDevIdx ].ioArray + kInApIdx;
753
+      cmApIO* op = _cmApBuf.devArray[oDevIdx].ioArray + kOutApIdx;
754
+
755
+      assert( ip->dspFrameCnt == op->dspFrameCnt );
756
+
757
+      unsigned    byteCnt  = ip->dspFrameCnt * sizeof(cmApSample_t);
758
+
759
+      if( oBufPtrArray[i] != NULL )
760
+      {      
761
+        // the input channel is not disabled
762
+        if( iBufPtrArray[i]!=NULL )
763
+          memcpy(oBufPtrArray[i],iBufPtrArray[i],byteCnt);    
764
+        else
765
+          // the input channel is disabled but the output is not - so fill the output with zeros
766
+          memset(oBufPtrArray[i],0,byteCnt);
767
+      }
768
+    }
769
+
770
+    cmApBufAdvance( iDevIdx, kInApFl );
771
+    cmApBufAdvance( oDevIdx, kOutApFl );
772
+  }
773
+  
774
+}
775
+
776
+void cmApBufReport( cmRpt_t* rpt )
777
+{
778
+  unsigned i,j,k;
779
+  for(i=0; i<_cmApBuf.devCnt; ++i)
780
+  {
781
+    cmRptPrintf(rpt,"%i ",i);
782
+
783
+    for(j=0; j<kIoApCnt; ++j)
784
+    {
785
+      cmApIO* ip = _cmApBuf.devArray[i].ioArray + j;
786
+
787
+      unsigned ii = 0;
788
+      unsigned oi = 0;
789
+      unsigned fn  = 0;
790
+      for(k=0; k<ip->chCnt; ++k)
791
+      {
792
+        cmApCh* cp = ip->chArray + i;
793
+        ii += cp->ii;
794
+        oi += cp->oi;
795
+        fn += cp->fn;
796
+      }
797
+
798
+        cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i ",
799
+        j==0?"IN":"OUT",
800
+        ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt);
801
+
802
+    }
803
+
804
+    cmRptPrintf(rpt,"\n");
805
+  }
806
+}
807
+
808
+/// [cmApBufExample]
809
+
810
+void cmApBufTest( cmRpt_t* rpt )
811
+{
812
+  unsigned devIdx         = 0;
813
+  unsigned devCnt         = 1 ;
814
+  unsigned dspFrameCnt    = 10;
815
+  unsigned cycleCnt       = 3;
816
+  unsigned framesPerCycle = 25;
817
+  unsigned inChCnt        = 2;
818
+  unsigned outChCnt       = inChCnt;
819
+  unsigned sigN           = cycleCnt*framesPerCycle*inChCnt;
820
+  double   srate          = 44100.0;
821
+  unsigned meterMs        = 50;
822
+
823
+  unsigned          bufChCnt= inChCnt;
824
+  cmApSample_t*     inBufArray[ bufChCnt ];
825
+  cmApSample_t*     outBufArray[ bufChCnt ];
826
+  cmApSample_t      iSig[ sigN ];
827
+  cmApSample_t      oSig[ sigN ];
828
+  cmApSample_t*     os = oSig;
829
+  cmApAudioPacket_t pkt;
830
+  int        i,j;
831
+
832
+  // create a simulated signal
833
+  for(i=0; i<sigN; ++i)
834
+  {
835
+    iSig[i]   = i;
836
+    oSig[i] = 0;
837
+  }
838
+
839
+  pkt.devIdx         = 0;
840
+  pkt.begChIdx       = 0;
841
+  pkt.chCnt          = inChCnt;
842
+  pkt.audioFramesCnt = framesPerCycle;
843
+  pkt.bitsPerSample  = 32;
844
+  pkt.flags          = 0;
845
+ 
846
+
847
+  // initialize a the audio buffer 
848
+  cmApBufInitialize(devCnt,meterMs);
849
+
850
+  // setup the buffer with the specific parameters to by used by the host audio ports
851
+  cmApBufSetup(devIdx,srate,dspFrameCnt,cycleCnt,inChCnt,framesPerCycle,outChCnt,framesPerCycle);
852
+
853
+  // simulate cylcing through sigN buffer transactions
854
+  for(i=0; i<sigN; i+=framesPerCycle*inChCnt)
855
+  {
856
+    // setup an incoming audio packet
857
+    pkt.audioFramesCnt = framesPerCycle;
858
+    pkt.audioBytesPtr  = iSig+i;
859
+
860
+    // simulate a call from the audio port with incoming samples 
861
+    // (fill the audio buffers internal input buffers)
862
+    cmApBufUpdate(&pkt,1,NULL,0);
863
+
864
+    // if all devices need to be serviced
865
+    while( cmApBufIsDeviceReady( devIdx, kInApFl | kOutApFl ))
866
+    {
867
+      // get pointers to full internal input buffers
868
+      cmApBufGet(devIdx, kInApFl, inBufArray, bufChCnt );
869
+      
870
+      // get pointers to empty internal output buffers
871
+      cmApBufGet(devIdx, kOutApFl, outBufArray, bufChCnt );
872
+
873
+      // Warning: pointers to disabled channels will be set to NULL.
874
+
875
+      // simulate a play through by copying the incoming samples to the outgoing buffers.
876
+      for(j=0; j<bufChCnt; ++j)
877
+        if( outBufArray[j] != NULL )
878
+        {
879
+          // if the input is disabled - but the output is not then zero the output buffer
880
+          if( inBufArray[j] == NULL )
881
+            memset( outBufArray[j], 0, dspFrameCnt*sizeof(cmApSample_t));
882
+          else
883
+            // copy the input to the output
884
+            memcpy( outBufArray[j], inBufArray[j], dspFrameCnt*sizeof(cmApSample_t));
885
+        }
886
+
887
+      // signal the buffer that this cycle is complete.
888
+      // (marks current internal input/output buffer empty/full)
889
+      cmApBufAdvance( devIdx, kInApFl | kOutApFl );
890
+    }
891
+
892
+    pkt.audioBytesPtr = os;
893
+    
894
+    // simulate a call from the audio port picking up outgoing samples
895
+    // (empties the audio buffers internal output buffers)
896
+    cmApBufUpdate(NULL,0,&pkt,1);
897
+
898
+    os += pkt.audioFramesCnt * pkt.chCnt;
899
+  }
900
+
901
+  for(i=0; i<sigN; ++i)
902
+    cmRptPrintf(rpt,"%f ",oSig[i]);
903
+  cmRptPrintf(rpt,"\n");
904
+
905
+  cmApBufFinalize();
906
+}
907
+
908
+/// [cmApBufExample]
909
+
910
+

+ 229
- 0
cmApBuf.h View File

@@ -0,0 +1,229 @@
1
+/// \file cmApBuf.h
2
+/// \brief  Thread-safe audio buffer class.
3
+///
4
+/// This file defines an audio buffer class which handles
5
+/// buffering incoming (recording) and outgoing (playback)
6
+/// samples in a thread-safe manner. 
7
+///
8
+/// Usage example and testing code:
9
+/// See cmApBufTest() and cmAudioSysTest().
10
+/// \snippet cmApBuf.c cmApBufExample
11
+///
12
+/// Notes on channel flags:
13
+/// Disabled channels:  kChFl is cleared
14
+///   cmApBufGet()     
15
+///      in  - return NULL buffer pointers  
16
+///      out - return NULL buffer points
17
+///
18
+///   cmApBufUpdate()
19
+///      in  - incoming samples are set to 0. 
20
+///      out - outgoing samples are set to 0.
21
+///
22
+/// Muted channels: kMuteFl is set 
23
+///   cmApBufUpdate()
24
+///      in  - incoming samples are set to 0. 
25
+///      out - outgoing samples are set to 0.
26
+///
27
+/// Tone channels: kToneFl is set 
28
+///   cmApBufUpdate()
29
+///      in  - incoming samples are filled with a 1k sine tone
30
+///      out - outgoing samples are filled with a 1k sine tone
31
+///
32
+
33
+#ifndef cmApBuf_h
34
+#define cmApBuf_h
35
+
36
+#ifdef __cplusplus
37
+extern "C" {
38
+#endif
39
+
40
+  typedef cmRC_t cmAbRC_t;  ///< Result code type
41
+  
42
+  enum
43
+  {
44
+    kOkAbRC = 0
45
+  };
46
+
47
+  /// Allocate and initialize an audio buffer.
48
+  /// devCnt - count of devices this buffer will handle.
49
+  /// meterMs - length of the meter buffers in milliseconds
50
+  cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs );
51
+
52
+  /// Deallocate and release any resource held by an audio buffer allocated via cmApBufInitialize().
53
+  cmAbRC_t cmApBufFinalize();
54
+
55
+  /// Configure a buffer for a given device.  
56
+  cmAbRC_t cmApBufSetup( 
57
+    unsigned devIdx,              ///< device to setup
58
+    double   srate,               ///< device sample rate (only required for synthesizing the correct test-tone frequency)
59
+    unsigned dspFrameCnt,         /// dspFrameCnt - count of samples in channel buffers returned via cmApBufGet() 
60
+    unsigned cycleCnt,            ///< number of audio port cycles to store 
61
+    unsigned inChCnt,             ///< input channel count on this device
62
+    unsigned inFramesPerCycle,    ///< maximum number of incoming sample frames on an audio port cycle
63
+    unsigned outChCnt,            ///< output channel count on this device
64
+    unsigned outFramesPerCycle    ///< maximum number of outgoing sample frames in an audio port cycle
65
+                         );
66
+
67
+  // Prime the buffer with 'audioCycleCnt' * outFramesPerCycle samples ready to be played
68
+  cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt );
69
+
70
+  /// This function is called asynchronously by the audio device driver to transfer incoming samples to the
71
+  /// the buffer and to send outgoing samples to the DAC. This function is 
72
+  /// intended to be called from the audio port callback function (\see cmApCallbackPtr_t).
73
+  /// This function is thread-safe under the condition where the audio device uses
74
+  /// different threads for input and output.
75
+  ///
76
+  /// Enable Flag: 
77
+  /// Input: If an input channel is disabled then the incoming samples are replaced with zeros.
78
+  /// Output: If an output channel is disabled then the packet samples are set to zeros.
79
+  ///
80
+  /// Tone Flag:
81
+  /// Input: If the tone flag is set on an input channel then the incoming samples are set to a sine tone.
82
+  /// Output: If the tone flag is set on an output channel then the packet samples are set to a sine tone.
83
+  ///
84
+  /// The enable flag has higher precedence than the tone flag therefore disabled channels
85
+  /// will be set to zero even if the tone flag is set.
86
+  cmAbRC_t cmApBufUpdate(
87
+    cmApAudioPacket_t* inPktArray,  ///< full audio packets from incoming audio (from ADC)
88
+    unsigned           inPktCnt,    ///< count of incoming audio packets
89
+    cmApAudioPacket_t* outPktArray, ///< empty audio packet for outgoing audio (to DAC)  
90
+    unsigned           outPktCnt    ///< count of outgoing audio packets
91
+                         );
92
+  /// Channel flags
93
+  enum
94
+  {
95
+    kInApFl     = 0x01,  ///< Identify an input channel
96
+    kOutApFl    = 0x02,  ///< Identify an output channel
97
+    kEnableApFl = 0x04,  ///< Set to enable a channel, Clear to disable. 
98
+
99
+    kChApFl     = 0x08,  ///< Used to enable/disable a channel
100
+    kMuteApFl   = 0x10,  ///< Mute this channel
101
+    kToneApFl   = 0x20,  ///< Generate a tone on this channel
102
+    kMeterApFl  = 0x40,  ///< Turn meter's on/off
103
+    kPassApFl   = 0x80   ///< Pass input channels throught to the output. Must use cmApBufGetIO() to implement this functionality.
104
+  
105
+  };
106
+
107
+  /// Return the meter window period as set by cmApBufInitialize()
108
+  unsigned cmApBufMeterMs();
109
+
110
+  /// Returns the channel count set via cmApBufSetup().
111
+  unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags );
112
+
113
+  /// Set chIdx to -1 to enable all channels on this device.
114
+  /// Set flags to {kInApFl | kOutApFl} | {kChApFl | kToneApFl | kMeterFl} | { kEnableApFl=on | 0=off }  
115
+  void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags );
116
+  
117
+  /// Return true if the the flags is set.
118
+  bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags );
119
+
120
+  /// Set chIdx to -1 to enable all channels on this device.
121
+  void  cmApBufEnableChannel(   unsigned devIdx, unsigned chIdx, unsigned flags );
122
+
123
+  /// Returns true if an input/output channel is enabled on the specified device.
124
+  bool  cmApBufIsChannelEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
125
+
126
+  /// Set the state of the tone generator on the specified channel.
127
+  /// Set chIdx to -1 to apply the change to all channels on this device.
128
+  /// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
129
+  void  cmApBufEnableTone(   unsigned devIdx, unsigned chIdx, unsigned flags );
130
+
131
+  /// Returns true if an input/output tone is enabled on the specified device.
132
+  bool  cmApBufIsToneEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
133
+
134
+  /// Mute a specified channel.
135
+  /// Set chIdx to -1 to apply the change to all channels on this device.
136
+  /// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
137
+  void  cmApBufEnableMute(   unsigned devIdx, unsigned chIdx, unsigned flags );
138
+
139
+  /// Returns true if an input/output channel is muted on the specified device.
140
+  bool  cmApBufIsMuteEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
141
+
142
+  /// Set the specified channel to pass through.
143
+  /// Set chIdx to -1 to apply the change to all channels on this device.
144
+  /// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
145
+  void  cmApBufEnablePass(   unsigned devIdx, unsigned chIdx, unsigned flags );
146
+
147
+  /// Returns true if pass through is enabled on the specified channel.
148
+  bool  cmApBufIsPassEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
149
+
150
+  /// Turn meter data collection on and off.
151
+  /// Set chIdx to -1 to apply the change to all channels on this device.
152
+  /// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
153
+  void  cmApBufEnableMeter(   unsigned devIdx, unsigned chIdx, unsigned flags );
154
+
155
+  /// Returns true if an input/output tone is enabled on the specified device.
156
+  bool  cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );
157
+
158
+  /// Return the meter value for the requested channel.
159
+  /// Set flags to kInApFl | kOutApFl.
160
+  cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags );
161
+
162
+  /// Set chIdx to -1 to apply the gain to all channels on the specified device.
163
+  void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain );
164
+
165
+  /// Return the current gain seting for the specified channel.
166
+  double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags ); 
167
+
168
+  /// Get the meter and fault status of the channel input or output channel array of a device.
169
+  /// Set 'flags' to { kInApFl | kOutApFl }.
170
+  /// The returns value is the count of channels actually written to meterArray.
171
+  /// If 'faultCntPtr' is non-NULL then it is set to the faultCnt of the associated devices input or output buffer.
172
+  unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr );
173
+
174
+  /// Do all enabled input/output channels on this device have samples available?
175
+  /// 'flags' can be set to either or both kInApFl and kOutApFl
176
+  bool  cmApBufIsDeviceReady( unsigned devIdx, unsigned flags ); 
177
+
178
+  /// This function is called by the application to get full incoming sample buffers and
179
+  /// to fill empty outgoing sample buffers.
180
+  /// Upon return each element in bufArray[bufChCnt] holds a pointer to a buffer assoicated 
181
+  /// with an audio channel or to NULL if the channel is disabled.
182
+  /// 'flags' can be set to kInApFl or kOutApFl but not both.
183
+  /// The buffers pointed to by bufArray[] each contain 'dspFrameCnt' samples. Where 
184
+  /// 'dspFrameCnt' was set in the earlier call to cmApBufSetup() for this device.
185
+  /// (see cmApBufInitialize()).
186
+  /// Note that this function just returns audio information it does not
187
+  /// change any cmApBuf() internal states.
188
+  void cmApBufGet(     unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt );
189
+
190
+  /// This function replaces calls to cmApBufGet() and implements pass-through and output 
191
+  /// buffer zeroing: 
192
+  /// 
193
+  /// 1) cmApBufGet(in);
194
+  /// 2) cmApBufGet(out);
195
+  /// 3) Copy through channels marked for 'pass' and set the associated oBufArray[i] channel to NULL.
196
+  /// 4) Zero all other enabled output channels.
197
+  ///
198
+  /// Notes:
199
+  /// 1) The oBufArray[] channels that are disabled or marked for pass-through will 
200
+  /// be set to NULL.
201
+  /// 2) The client is required to use this function to implement pass-through internally.
202
+  /// 3) This function just returns audio information it does not
203
+  /// change any cmApBuf() internal states.
204
+  void cmApBufGetIO(   unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt );
205
+
206
+  /// The application calls this function each time it completes processing of a bufArray[]
207
+  /// returned from cmApBufGet(). 'flags' can be set to either or both kInApFl and kOutApFl.
208
+  /// This function should only be called from the client thread.
209
+  void cmApBufAdvance( unsigned devIdx, unsigned flags );
210
+
211
+    /// Copy all available samples incoming samples from an input device to an output device.
212
+  /// The source code for this example is a good example of how an application should use cmApBufGet()
213
+  /// and cmApBufAdvance().
214
+  void cmApBufInputToOutput( unsigned inDevIdx, unsigned outDevIdx );
215
+
216
+  /// Print the current buffer state.
217
+  void cmApBufReport( cmRpt_t* rpt );
218
+
219
+  /// Run a buffer usage simulation to test the class. cmAudioPortTest.c calls this function.
220
+  void cmApBufTest( cmRpt_t* rpt );
221
+
222
+
223
+
224
+
225
+#ifdef __cplusplus
226
+}
227
+#endif
228
+
229
+#endif

+ 1224
- 0
cmAudDsp.c
File diff suppressed because it is too large
View File


+ 55
- 0
cmAudDsp.h View File

@@ -0,0 +1,55 @@
1
+#ifndef cmAudDsp_h
2
+#define cmAudDsp_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  // This API supports a serialized interface to an internal instance of 
9
+  // cmAudioSys and cmDspSys.  
10
+
11
+  enum
12
+  {
13
+    kOkAdRC = cmOkRC,
14
+    kAudioPortFailAdRC,
15
+    kAudioSysFailAdRC,
16
+    kMidiSysFailAdRC,
17
+    kDspSysFailAdRC,
18
+    kFileSysFailAdRC,
19
+    kJsonFailAdRC,
20
+    kSendMsgFailAdRC,
21
+    kInvalidCfgIdxAdRC,
22
+    kNoPgmLoadedAdRC,
23
+    kInvalidSubSysIdxAdRC,
24
+    kUnknownMsgTypeAdRC,
25
+    kAggDevSysFailAdRC,
26
+    kAggDevCreateFailAdRC,
27
+    kNrtDevSysFailAdRC,
28
+    kNetSysFailAdRC
29
+  };
30
+
31
+
32
+  typedef cmRC_t     cmAdRC_t;
33
+  typedef cmHandle_t cmAdH_t;
34
+  
35
+  extern cmAdH_t cmAdNullHandle;
36
+
37
+  // Create a audio dsp engine and send device and program information to the 
38
+  // host application.  
39
+  // cbPtr provides a function used by cmAudDsp to send messages to the client.
40
+  cmAdRC_t cmAudDspAlloc( cmCtx_t* ctx, cmAdH_t* hp, cmMsgSendFuncPtr_t cbPtr, void* cbDataPtr );
41
+  cmAdRC_t cmAudDspFree(  cmAdH_t* hp );
42
+
43
+  bool cmAudDspIsValid( cmAdH_t h );
44
+
45
+  // This function provides the primary interface for communication from the
46
+  // client program to the aud_dsp system.
47
+  cmAdRC_t cmAudDspReceiveClientMsg( cmAdH_t h, unsigned msgBytecnt, const void* msg );
48
+  
49
+
50
+#ifdef __cplusplus
51
+  }
52
+#endif
53
+
54
+
55
+#endif

+ 291
- 0
cmAudDspIF.c View File

@@ -0,0 +1,291 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmRpt.h"
5
+#include "cmErr.h"
6
+#include "cmCtx.h"
7
+#include "cmMem.h"
8
+#include "cmMallocDebug.h"
9
+#include "cmFileSys.h"
10
+#include "cmJson.h"
11
+#include "cmThread.h"
12
+#include "dsp/cmDspValue.h"
13
+#include "cmMsgProtocol.h"
14
+#include "cmAudDspIF.h"
15
+
16
+
17
+cmAiH_t cmAiNullHandle = cmSTATIC_NULL_HANDLE;
18
+
19
+typedef struct
20
+{
21
+  cmErr_t      err;
22
+  cmAdIfParm_t parms;
23
+  cmJsonH_t    jsH;
24
+} cmAi_t;
25
+
26
+cmAi_t* _cmAiHandleToPtr( cmAiH_t h )
27
+{
28
+  cmAi_t* p = (cmAi_t*)h.h;
29
+  assert(p != NULL);
30
+  return p;
31
+}
32
+
33
+
34
+// Dispatch a message to the client application.
35
+// This function is called from within cmTsQueueDequeueMsg() which is called
36
+// by cmAdIfDispatchMsgToHost().  
37
+cmRC_t _cmAiDispatchMsgToClient(void* cbDataPtr, unsigned msgByteCnt, const void* msgDataPtr )
38
+{
39
+  cmAi_t*       p    = (cmAi_t*)cbDataPtr;
40
+  cmDspUiHdr_t* m    = (cmDspUiHdr_t*)msgDataPtr;
41
+  cmRC_t        rc   = cmOkRC;
42
+
43
+  switch( m->uiId )
44
+  {
45
+    case kStatusSelAsId:
46
+      {
47
+        // handle a status mesage
48
+        const char*               base        = (const char*)msgDataPtr;
49
+        const cmAudioSysStatus_t* st          = (const cmAudioSysStatus_t*)(base + (2 * sizeof(unsigned)));
50
+        const double*             iMeterArray = (const double*)(st + 1);
51
+        const double*             oMeterArray = iMeterArray + st->iMeterCnt;
52
+        rc = p->parms.dispatchRecd.statusFunc(p->parms.dispatchRecd.cbDataPtr, st, iMeterArray, oMeterArray );
53
+      }
54
+      break;
55
+
56
+    case kSsInitSelAsId:
57
+      {
58
+        // handle an ssInit message
59
+        const cmAudioSysSsInitMsg_t* sip       = (const cmAudioSysSsInitMsg_t*)msgDataPtr;
60
+        const char*                  iDevLabel = (const char*)(sip+1);
61
+        const char*                  oDevLabel = iDevLabel + strlen(iDevLabel) + 1;
62
+        rc = p->parms.dispatchRecd.ssInitFunc(p->parms.dispatchRecd.cbDataPtr, sip, iDevLabel, oDevLabel );
63
+      }
64
+      break;
65
+
66
+    case kUiSelAsId:
67
+      {
68
+        bool          jsFl = false;
69
+        cmDsvRC_t     rc   = kOkDsvRC;
70
+
71
+        // if the value associated with this msg is a mtx then set
72
+        // its mtx data area pointer to just after the msg header.
73
+        if( cmDsvIsJson(&m->value) )
74
+        {
75
+          rc = cmDsvDeserializeJson(&m->value,p->jsH);
76
+          jsFl = true;
77
+        }
78
+        else
79
+          rc = cmDsvDeserializeInPlace(&m->value,msgByteCnt-sizeof(cmDspUiHdr_t));
80
+
81
+        if( rc != kOkDsvRC )
82
+          cmErrMsg(&p->err,kDeserialFailAiRC,"Deserialize failed.");
83
+        else
84
+          rc = p->parms.dispatchRecd.uiFunc(p->parms.dispatchRecd.cbDataPtr,m);
85
+
86
+        if( jsFl )
87
+          cmJsonClearTree(p->jsH);
88
+      }
89
+      break;
90
+
91
+    default:
92
+      cmErrMsg(&p->err,kUnknownMsgTypeAiRC,"The message type %i is unknown.",m->uiId);
93
+      break;
94
+  }
95
+
96
+  return rc;  
97
+}
98
+
99
+cmAiRC_t _cmAdIfReadCfgFile( cmAi_t* p, cmCtx_t* ctx )
100
+{
101
+  cmAiRC_t        rc            = kOkAiRC;
102
+  const cmChar_t* sysJsFn       = NULL;
103
+  cmJsonH_t       jsH           = cmJsonNullHandle;
104
+  cmJsonNode_t*   audDspNodePtr = NULL;
105
+  //const cmChar_t* errLabelPtr   = NULL;
106
+
107
+  // form the audio dsp resource file name
108
+  if((sysJsFn = cmFsMakeFn( cmFsPrefsDir(),cmAudDspSys_FILENAME,NULL,NULL)) == NULL )
109
+  {
110
+    rc = cmErrMsg(&p->err,kFileSysFailAiRC,"Unable to form the audio dsp system resource file name.");
111
+    goto errLabel;
112
+  }
113
+
114
+  // open the audio dsp resource file
115
+  if(cmJsonInitializeFromFile(&jsH,sysJsFn,ctx) != kOkJsRC )
116
+  {
117
+    rc = cmErrMsg(&p->err,kJsonFailAiRC,"Unable to open the audio dsp resource file: '%s'.",cmStringNullGuard(sysJsFn));
118
+    goto errLabel;
119
+  }
120
+
121
+  // locate the aud_dsp container object
122
+  if( cmJsonNodeMember( cmJsonRoot(jsH), "aud_dsp", &audDspNodePtr ) != kOkJsRC )
123
+  {
124
+    rc = cmErrMsg(&p->err,kJsonFailAiRC,"The audio DSP system resource file '%s' does not contain an 'aud_dsp' object.",cmStringNullGuard(sysJsFn));
125
+    goto errLabel;
126
+  }
127
+
128
+  /*
129
+  // locate the read the aud_dsp sub-elements
130
+  if( cmJsonMemberValues( audDspNodePtr, &errLabelPtr, 
131
+      "msgQueueByteCnt", kIntTId,   &p->msgQueueByteCnt,
132
+      NULL ) != kOkJsRC )
133
+  {
134
+    rc = cmErrMsg(&p->err,kJsonFailAiRC,"Syntax error while parsing the top level fields in the audio DSP system resource file:%s.",cmStringNullGuard(sysJsFn));
135
+    goto errLabel;
136
+  }
137
+  */
138
+
139
+ errLabel:
140
+  if( cmJsonFinalize(&jsH) != kOkJsRC )
141
+    rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON finalization failed.");
142
+
143
+  if( sysJsFn != NULL )
144
+    cmFsFreeFn(sysJsFn);
145
+
146
+  return rc;
147
+
148
+}
149
+
150
+
151
+cmAiRC_t _cmAdIfSendIntMsg(cmAiH_t h, unsigned selId, unsigned asSubIdx, unsigned flags, unsigned iv, double dv )
152
+{  
153
+  cmAi_t*      p = _cmAiHandleToPtr( h );
154
+  cmDspValue_t v;
155
+
156
+  if(iv == cmInvalidIdx )
157
+    cmDsvSetDouble(&v,dv);
158
+  else
159
+    cmDsvSetUInt(&v,iv);
160
+ 
161
+  if( cmMsgSend(&p->err,asSubIdx,kUiSelAsId,selId,flags,cmInvalidId,cmInvalidId,&v,p->parms.audDspFunc,p->parms.audDspFuncDataPtr) != kOkMsgRC )
162
+    return cmErrMsg(&p->err,kSendFailAiRC,"The integer message sel id:%i value:%i transmission failed.",selId,iv);
163
+    
164
+  return kOkAiRC;
165
+
166
+}
167
+
168
+cmAiRC_t _cmAdIfFree( cmAi_t* p )
169
+{
170
+  cmAiRC_t rc = kOkAiRC;
171
+
172
+  if( cmJsonFinalize(&p->jsH) != kOkJsRC )
173
+  {
174
+    rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON finalization failed.");
175
+    goto errLabel;
176
+  }
177
+
178
+  cmMemFree(p);
179
+
180
+ errLabel:
181
+  return rc;
182
+}
183
+
184
+
185
+cmAiRC_t cmAdIfAllocate( cmCtx_t* ctx, cmAiH_t* hp, const cmAdIfParm_t* parms  )
186
+{
187
+  cmAiRC_t rc;
188
+  if((rc = cmAdIfFree(hp)) != kOkAiRC )
189
+    return rc;
190
+  
191
+  cmAi_t* p = cmMemAllocZ(cmAi_t,1);
192
+
193
+  cmErrSetup(&p->err,&ctx->rpt,"Audio DSP Interface");
194
+
195
+  p->parms = *parms;
196
+
197
+  // read the system configuration file
198
+  if((rc = _cmAdIfReadCfgFile(p, ctx )) != kOkAiRC )
199
+    goto errLabel;
200
+
201
+
202
+  // initialize a JSON tree for use in deserializing JSON messages
203
+  if((rc = cmJsonInitialize( &p->jsH, ctx )) != kOkJsRC )
204
+  {
205
+    rc = cmErrMsg(&p->err,kJsonFailAiRC,"JSON initialization failed.");
206
+    goto errLabel;
207
+  }
208
+
209
+
210
+  hp->h = p;
211
+
212
+ errLabel:
213
+  if( rc != kOkAiRC )
214
+    _cmAdIfFree(p);
215
+
216
+  return rc;
217
+}
218
+
219
+cmAiRC_t cmAdIfFree( cmAiH_t* hp )
220
+{
221
+  cmAiRC_t rc = kOkAiRC;
222
+
223
+  if( hp==NULL || cmAdIfIsValid(*hp)==false )
224
+    return kOkAiRC;
225
+
226
+  cmAi_t* p = _cmAiHandleToPtr(*hp);
227
+
228
+  if((rc = _cmAdIfFree(p)) != kOkAiRC )
229
+    return rc;
230
+
231
+  hp->h = NULL;
232
+  return rc;
233
+}
234
+
235
+bool            cmAdIfIsValid( cmAiH_t h )
236
+{ return h.h != NULL; }
237
+
238
+cmAiRC_t       cmAdIfRecvAudDspMsg( cmAiH_t h, unsigned msgByteCnt, const void* msg )
239
+{
240
+  cmAi_t* p = _cmAiHandleToPtr(h);
241
+  cmAiRC_t rc = kOkAiRC;
242
+
243
+  _cmAiDispatchMsgToClient(p,msgByteCnt,msg);
244
+  return rc;
245
+}
246
+
247
+cmAiRC_t        cmAdIfDeviceReport( cmAiH_t h )
248
+{ return _cmAdIfSendIntMsg(h,kDevReportDuiId,cmInvalidIdx,0,cmInvalidIdx,0.0); }
249
+ 
250
+cmAiRC_t        cmAdIfSetAudioSysCfg(   cmAiH_t h, unsigned asCfgIdx )
251
+{ return _cmAdIfSendIntMsg(h,kSetAudioCfgDuiId,cmInvalidIdx,0,asCfgIdx,0.0); }
252
+
253
+cmAiRC_t        cmAdIfSetAudioDevice(   cmAiH_t h, unsigned asSubIdx, bool inputFl, unsigned devIdx )
254
+{ return _cmAdIfSendIntMsg(h,kSetAudioDevDuiId,asSubIdx,inputFl,devIdx,0.0); }
255
+
256
+cmAiRC_t        cmAdIfSetSampleRate(  cmAiH_t h, unsigned asSubIdx, double srate )
257
+{ return _cmAdIfSendIntMsg(h,kSetSampleRateDuiId,asSubIdx,0,cmInvalidIdx,srate); }
258
+
259
+cmAiRC_t        cmAdIfLoadProgram(   cmAiH_t h, unsigned asSubIdx, unsigned pgmIdx )
260
+{ return _cmAdIfSendIntMsg(h,kSetPgmDuiId,asSubIdx,0,pgmIdx,0.0); }
261
+  
262
+cmAiRC_t        cmAdIfEnableAudio( cmAiH_t h, bool enableFl )
263
+{ return _cmAdIfSendIntMsg(h,kEnableDuiId,cmInvalidIdx,enableFl,cmInvalidIdx,0.0); }
264
+
265
+cmAiRC_t        cmAdIfEnableStatusNotify( cmAiH_t h, bool enableFl )
266
+{ return _cmAdIfSendIntMsg(h,kSetNotifyEnableDuiId,cmInvalidIdx,enableFl,cmInvalidIdx,0.0); }
267
+
268
+cmAiRC_t        cmAdIfSendMsgToAudioDSP( 
269
+  cmAiH_t             h, 
270
+  unsigned            asSubIdx,
271
+  unsigned            msgTypeId,
272
+  unsigned            selId,
273
+  unsigned            flags,
274
+  unsigned            instId,
275
+  unsigned            instVarId,
276
+  const cmDspValue_t* valPtr )
277
+{
278
+  cmAiRC_t rc = kOkAiRC;
279
+  cmAi_t*  p  = _cmAiHandleToPtr(h);
280
+  
281
+  if( cmMsgSend( &p->err, asSubIdx, msgTypeId,selId,flags,instId,instVarId,valPtr, p->parms.audDspFunc, p->parms.audDspFuncDataPtr ) != kOkMsgRC )
282
+    rc = cmErrMsg(&p->err, kSendFailAiRC, "A UI message intened for the the audio DSP system was not successfully delivered.");
283
+
284
+  return rc;
285
+
286
+}
287
+
288
+
289
+cmAiRC_t        cmAdIfDispatchMsgToHost(  cmAiH_t h ) 
290
+{ return _cmAdIfSendIntMsg(h,kClientMsgPollDuiId,cmInvalidIdx,0,cmInvalidIdx,0.0); }
291
+

+ 167
- 0
cmAudDspIF.h View File

@@ -0,0 +1,167 @@
1
+#ifndef cmAudDspIF_h
2
+#define cmAudDspIF_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  // This API has two basic responsibilities:
9
+  //
10
+  // 1) Provides a function based interface to the audio DSP system for the
11
+  // client application.
12
+  //    The client calls these API functions to send commands to the audio DSP
13
+  //    system. Internally the cmAdIfxxx functions converts the commands to 
14
+  //    raw message packets and passes them to a transmission service
15
+  //    via cmAdIfParm_t audioDspFunc().
16
+  //
17
+  // 2) Acts as the receiver of raw message streams from whatever external
18
+  // service (e.g. cmAudDspLocal, cmAudDspUdp) is receiving raw message packets 
19
+  // from audio DSP system.  
20
+  // 
21
+  //    This process is driven by periodic calls from the client to 
22
+  //    cmAdIfDispatchMsgToHost().
23
+  //    cmAdIfDispatchMsgToHost() then generates an internal 
24
+  //    'kClientMsgPollDuiId' msg which is passed toward the 
25
+  //    cmAudDsp system.  
26
+  //    When the msg encounters a sub-system with queued msgs waiting
27
+  //    for the client a callback chain ensues which eventually
28
+  //    calls cmAdIfRecvAudDspMsg() which in turn calls the appropriate
29
+  //    client provided cmAdIfDispatch_t function (ssInitFunc,statusFunc or uiFunc).
30
+  //    Note that this entire chain of calls occurs in the client thread
31
+  //    and in the context of the cmAdIfDispatchMsgToHost() procedure.
32
+                         
33
+  
34
+
35
+  enum
36
+  {
37
+    kOkAiRC = cmOkRC,
38
+    kAudDspFailAiRC,
39
+    kLHeapFailAiRC,
40
+    kUnknownMsgTypeAiRC,
41
+    kMsgCorruptAiRC,
42
+    kSendFailAiRC,
43
+    kQueueFailAiRC,
44
+    kNoMsgAiRC,
45
+    kJsonFailAiRC,
46
+    kDeserialFailAiRC,
47
+    kFileSysFailAiRC
48
+  };
49
+
50
+  typedef cmRC_t cmAiRC_t; 
51
+
52
+  typedef cmHandle_t cmAiH_t;
53
+
54
+  // These functions are provided by the client to receive messages 
55
+  // from the audio DSP sytem. These functions are only called from the client thread
56
+  // from within cmAdIfDispatchMsgToHost().
57
+  typedef struct
58
+  {
59
+    void* cbDataPtr; // data to send as the first arg. to app. callbacks
60
+
61
+    cmRC_t (*ssInitFunc)( void* cbDataPtr, const cmAudioSysSsInitMsg_t* r, const char* iDevLabel, const char* oDevLabel );
62
+    cmRC_t (*statusFunc)( void* cbDataPtr, const cmAudioSysStatus_t* r, const double* iMeterArray, const double* oMeterArray );
63
+    cmRC_t (*uiFunc)(     void* cbDataPtr, const cmDspUiHdr_t* r );
64
+  } cmAdIfDispatch_t;
65
+
66
+  typedef struct
67
+  {
68
+    cmAdIfDispatch_t   dispatchRecd;       // client application callback pointers
69
+    cmMsgSendFuncPtr_t audDspFunc;         // the cmAdIfXXX functions use the callback to send msgs to the audio DSP system.
70
+    void*              audDspFuncDataPtr;  // data to send with the audio DSP callback function
71
+  } cmAdIfParm_t;
72
+
73
+
74
+  extern cmAiH_t cmAiNullHandle;
75
+
76
+  cmAiRC_t        cmAdIfAllocate( cmCtx_t* ctx, cmAiH_t* hp, const cmAdIfParm_t* parms  );
77
+  cmAiRC_t        cmAdIfFree( cmAiH_t* hp );
78
+
79
+  bool            cmAdIfIsValid( cmAiH_t h );
80
+
81
+  // Receive a msg from the audio DSP system. This is the main point of entry
82
+  // for all calls from the audio DSP system to the client.
83
+  // This function is provided as a callback to the owner of this cmAudDspIF
84
+  // (e.g. cmAudDspLocal, cmAudDspUdpClient) it should never need to be called
85
+  // by the client.
86
+  cmAiRC_t       cmAdIfRecvAudDspMsg( cmAiH_t h, unsigned msgByteCnt, const void* msgDataPtr);
87
+
88
+
89
+  //-------------------------------------------------------------------------
90
+  //
91
+  // The functions below are used to send commands to the audio DSP system
92
+  // from the client application. 
93
+  //
94
+
95
+  // Print a hardware report.
96
+  cmAiRC_t        cmAdIfDeviceReport( cmAiH_t h );
97
+
98
+  // Select a audio system configuration.  This must be done prior to 
99
+  // sending any other commands.
100
+  cmAiRC_t        cmAdIfSetAudioSysCfg( cmAiH_t h, unsigned asCfgIdx );
101
+
102
+  // Select an audio input or output device for a given audio sub-system.
103
+  // An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
104
+  // prior to calling this function.
105
+  cmAiRC_t        cmAdIfSetAudioDevice( cmAiH_t h, unsigned asSubIdx, bool inputFl, unsigned devIdx );
106
+
107
+  // Set the sample rate for a given audio sub-system or the entire audio system.
108
+  // Set asSubIdx to cmInvalidIdx to assign the sample rate to all sub-systems 
109
+  // of the current audio system configuration.
110
+  // An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
111
+  // prior to calling this function.
112
+  cmAiRC_t        cmAdIfSetSampleRate(  cmAiH_t h, unsigned asSubIdx, double srate );
113
+
114
+  // Select a DSP program for a given audio sub-system or the entire audio system.
115
+  // Set asSubIdx to cmInvalidIdx to load the program on all sub-systems 
116
+  // of the current audio system configuration.
117
+  // An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
118
+  // prior to calling this function.
119
+  cmAiRC_t        cmAdIfLoadProgram(    cmAiH_t h, unsigned asSubIdx, unsigned pgmIdx );
120
+
121
+  // Start the audio streaming. 
122
+  // An audio configuration must have been selected via cmAdIfSetAudioSysCfg()
123
+  // and a DSP program must have been selected via cmAdIfLoadProgram() 
124
+  // prior to calling this function.
125
+  cmAiRC_t        cmAdIfEnableAudio( cmAiH_t h, bool enableFl );
126
+
127
+  // Enable/disable periodic audio system status notifications.
128
+  cmAiRC_t        cmAdIfEnableStatusNotify( cmAiH_t h, bool enableFl );
129
+  
130
+  // Send a kUiSelAsId style message to the audio DSP system.
131
+  cmAiRC_t        cmAdIfSendMsgToAudioDSP( 
132
+    cmAiH_t             h, 
133
+    unsigned            asSubIdx,
134
+    unsigned            msgTypeId,
135
+    unsigned            selId,
136
+    unsigned            flags,
137
+    unsigned            instId,
138
+    unsigned            instVarId,
139
+    const cmDspValue_t* valPtr );
140
+
141
+  // The client application must periodically call this function to 
142
+  // receive pending messages from the audio DSP system. The 
143
+  // messages are delivered via callbacks provided by cmAdIfDispatch_t. 
144
+  // This function should only be called from the client thread.
145
+  cmAiRC_t        cmAdIfDispatchMsgToHost(  cmAiH_t h ); 
146
+
147
+  /*
148
+    Local call chain:
149
+    cmAdIfDispatchMsgToHost() -> p->parms.audDspFunc = cmAudDspLocal::_cmAdlAudDspSendFunc() 
150
+                              -> cmAudioDsp::cmAudDspReceiveClientMsg()
151
+                              -> cmAudioDsp::_cmAudDspClientMsgPoll()
152
+                              -> cmAudioSys::cmAudioSysReceiveMsg()
153
+                              -> cmThread::cmTs1p1cDequeueMsg()
154
+                              -> cmAudioSysCfg_t::clientCbFunc = cmAudDsp::_cmAudioSysToClientCallback()
155
+                              -> cmAudDsp::cmAd_t::cbFunc = cmAudDspLocal::_cmAudDspLocalCallback()
156
+                              -> cmAudDspIF::cmAdIfRecvAudDspMsg()
157
+                              -> cmAudDspIF::_cmAiDispatchMsgToClient()
158
+                              -> cmAudDspIF::cmAdIfDispatch_t.uiFunc = kcApp::_s_handleUiMsg()
159
+                              
160
+   */
161
+
162
+
163
+#ifdef __cplusplus
164
+}
165
+#endif
166
+
167
+#endif

+ 147
- 0
cmAudDspLocal.c View File

@@ -0,0 +1,147 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmRpt.h"
5
+#include "cmErr.h"
6
+#include "cmCtx.h"
7
+#include "cmMem.h"
8
+#include "cmMallocDebug.h"
9
+#include "cmJson.h"
10
+#include "dsp/cmDspValue.h"
11
+#include "cmMsgProtocol.h"
12
+#include "cmAudDsp.h"
13
+#include "cmAudDspIF.h"
14
+#include "cmAudDspLocal.h"
15
+
16
+
17
+cmAdlH_t cmAdlNullHandle = cmSTATIC_NULL_HANDLE;
18
+
19
+typedef struct
20
+{
21
+  cmErr_t err;
22
+  cmAiH_t aiH;
23
+  cmAdH_t adH;
24
+} cmAdl_t;
25
+
26
+cmAdl_t* _cmAdlHandleToPtr( cmAdlH_t h )
27
+{
28
+  cmAdl_t* p = (cmAdl_t*)h.h;
29
+  assert( p != NULL );
30
+  return p;
31
+}
32
+
33
+// Forward messages coming from the audio DSP system to the audio DSP IF
34
+// for later dispatch to the client application.
35
+cmMsgRC_t _cmAudDspLocalCallback(void* cbDataPtr, unsigned msgByteCnt, const void* msg )
36
+{
37
+  cmMsgRC_t rc = kOkMsgRC;
38
+  cmAdl_t*  p  = (cmAdl_t*)cbDataPtr;
39
+
40
+  if( cmAdIfRecvAudDspMsg(p->aiH, msgByteCnt, msg ) != kOkAiRC )
41
+  {
42
+    cmErrMsg(&p->err,kAudDspIfFailAdlRC,"Message transmission to the audio DSP interface failed.");
43
+    rc =  kSendFailMsgRC;
44
+  }
45
+
46
+  return rc;
47
+}
48
+
49
+// Forward messages from the audio DSP interface to the audio DSP system.
50
+cmMsgRC_t  _cmAdlAudDspSendFunc( void* cbDataPtr, unsigned msgByteCnt, const void* msg )
51
+{
52
+  cmMsgRC_t rc = kOkMsgRC;
53
+  cmAdl_t*  p  = (cmAdl_t*)cbDataPtr;
54
+
55
+  if( cmAudDspReceiveClientMsg( p->adH, msgByteCnt, msg ) != kOkAdRC )
56
+  {
57
+    cmErrMsg(&p->err,kAudDspFailAdlRC,"Message transmission the audio DSP system failed.");
58
+    rc = kSendFailMsgRC;
59
+  }
60
+
61
+  return rc;
62
+
63
+}
64
+
65
+cmAdlRC_t _cmAudDspLocalFree( cmAdl_t* p )
66
+{
67
+  cmAdlRC_t rc = kOkAdlRC;
68
+
69
+  if( cmAdIfFree(&p->aiH) != kOkAiRC )
70
+  {
71
+    rc = cmErrMsg(&p->err,kAudDspIfFailAdlRC,"The audio DSP interface release failed.");
72
+    goto errLabel;
73
+  }
74
+
75
+  if( cmAudDspFree(&p->adH) != kOkAdRC )
76
+  {
77
+    rc = cmErrMsg(&p->err,kAudDspFailAdlRC,"The audio DSP release failed.");
78
+    goto errLabel;
79
+  }
80
+
81
+  cmMemFree(p);
82
+ errLabel:
83
+  return rc;
84
+}
85
+
86
+
87
+
88
+cmAdlRC_t cmAudDspLocalAllocate( cmCtx_t* ctx, cmAdlH_t* hp, const cmAdIfDispatch_t* recd )
89
+{
90
+  cmAdlRC_t rc;
91
+  if((rc = cmAudDspLocalFree(hp)) != kOkAdlRC )
92
+    return rc;
93
+
94
+  cmAdl_t* p = cmMemAllocZ(cmAdl_t,1);
95
+  cmErrSetup(&p->err,&ctx->rpt,"Audio DSP Local");
96
+
97
+  cmAdIfParm_t parms;
98
+  parms.dispatchRecd      = *recd;
99
+  parms.audDspFunc        = _cmAdlAudDspSendFunc;
100
+  parms.audDspFuncDataPtr = p;
101
+
102
+  if( cmAdIfAllocate(ctx, &p->aiH, &parms  ) != kOkAiRC )
103
+  {
104
+    rc = cmErrMsg(&p->err,kAudDspIfFailAdlRC,"The audio DSP interface system allocation failed.");
105
+    goto errLabel;
106
+  }
107
+
108
+  if( cmAudDspAlloc(ctx, &p->adH, _cmAudDspLocalCallback, p ) != kOkAdRC )
109
+  {
110
+    rc = cmErrMsg(&p->err,kAudDspFailAdlRC,"The audio DSP system allocation failed.");
111
+    goto errLabel;
112
+  }
113
+
114
+  hp->h = p;
115
+
116
+ errLabel:
117
+  if( rc != kOkAdlRC )
118
+    _cmAudDspLocalFree(p);
119
+
120
+  return rc;
121
+}
122
+
123
+cmAdlRC_t cmAudDspLocalFree( cmAdlH_t* hp )
124
+{
125
+  cmAdlRC_t rc = kOkAdlRC;
126
+
127
+  if( hp == NULL || cmAudDspLocalIsValid(*hp) == false )
128
+    return kOkAdlRC;
129
+
130
+  cmAdl_t* p = _cmAdlHandleToPtr(*hp);
131
+
132
+  if((rc = _cmAudDspLocalFree(p)) != kOkAdlRC )
133
+    return rc;
134
+
135
+  hp->h = NULL;
136
+  return rc;
137
+}
138
+
139
+bool      cmAudDspLocalIsValid( cmAdlH_t h )
140
+{ return h.h != NULL; }
141
+
142
+cmAiH_t   cmAudDspLocalIF_Handle( cmAdlH_t h )
143
+{ 
144
+  cmAdl_t* p = _cmAdlHandleToPtr(h);
145
+  return p->aiH;
146
+}
147
+

+ 38
- 0
cmAudDspLocal.h View File

@@ -0,0 +1,38 @@
1
+#ifndef cmAudDspLocal_h
2
+#define cmAudDspLocal_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkAdlRC = cmOkRC,
11
+    kAudDspIfFailAdlRC,
12
+    kAudDspFailAdlRC,
13
+    kFileSysFailAdlRC,
14
+    kJsonFailAdlRC
15
+  };
16
+
17
+  typedef cmRC_t cmAdlRC_t;
18
+  typedef cmHandle_t cmAdlH_t;
19
+
20
+  extern cmAdlH_t cmAdlNullHandle;
21
+
22
+  cmAdlRC_t cmAudDspLocalAllocate( 
23
+    cmCtx_t*                ctx, 
24
+    cmAdlH_t*               hp, 
25
+    const cmAdIfDispatch_t* recd );
26
+
27
+  cmAdlRC_t cmAudDspLocalFree( cmAdlH_t* hp );
28
+
29
+  bool      cmAudDspLocalIsValid( cmAdlH_t h );
30
+
31
+  cmAiH_t   cmAudDspLocalIF_Handle( cmAdlH_t h );
32
+  
33
+
34
+#ifdef __cplusplus
35
+}
36
+#endif
37
+
38
+#endif

+ 316
- 0
cmAudLabelFile.c View File

@@ -0,0 +1,316 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmRpt.h"
5
+#include "cmErr.h"
6
+#include "cmCtx.h"
7
+#include "cmMem.h"
8
+#include "cmMallocDebug.h"
9
+#include "cmLinkedHeap.h"
10
+#include "cmFile.h"
11
+#include "cmAudLabelFile.h"
12
+
13
+cmAlfH_t cmAlfNullHandle = cmSTATIC_NULL_HANDLE;
14
+
15
+typedef struct cmAlfRecd_str
16
+{
17
+  cmAlfLabel_t r;
18
+  struct cmAlfRecd_str* link;
19
+} cmAlfRecd_t; 
20
+
21
+typedef struct
22
+{
23
+  cmErr_t      err;
24
+  cmLHeapH_t   lH;
25
+  cmFileH_t    fH;
26
+  cmAlfRecd_t* list;
27
+  int          cnt;
28
+} cmAlf_t;
29
+
30
+cmAlf_t* _cmAlfHandleToPtr( cmAlfH_t h )
31
+{
32
+  cmAlf_t* p = (cmAlf_t*)h.h;
33
+  assert( p != NULL );
34
+  return p;
35
+}
36
+
37
+cmAlfRC_t _cmAlfFree( cmAlf_t* p )
38
+{
39
+  cmAlfRC_t rc = kOkAlfRC;
40
+
41
+  cmLHeapDestroy(&p->lH);
42
+
43
+  cmMemPtrFree(&p);
44
+
45
+  return rc;
46
+}
47
+
48
+
49
+cmAlfRC_t cmAudLabelFileAlloc(       cmCtx_t* ctx, cmAlfH_t* hp )
50
+{
51
+  cmAlfRC_t rc;
52
+  if((rc = cmAudLabelFileFree(hp)) != kOkAlfRC )
53
+    return rc;
54
+
55
+  cmAlf_t* p = cmMemAllocZ(cmAlf_t,1);
56
+  cmErrSetup(&p->err,&ctx->rpt,"Audio Label File");
57
+
58
+  if(!cmLHeapIsValid( p->lH = cmLHeapCreate(1024,ctx)))
59
+  {
60
+    cmErrMsg(&p->err,kLHeapFailAlfRC,"Linked heap create failed.");
61
+    goto errLabel;
62
+  }
63
+    
64
+  hp->h = p;
65
+
66
+ errLabel:
67
+  return rc;
68
+}
69
+
70
+cmAlfRC_t cmAudLabelFileAllocOpen(   cmCtx_t* ctx, cmAlfH_t* hp, const cmChar_t* fn )
71
+{
72
+  cmAlfRC_t rc;
73
+  if((rc = cmAudLabelFileAlloc(ctx,hp)) != kOkAlfRC)
74
+    return rc;
75
+
76
+  return cmAudLabelFileOpen(*hp,fn);
77
+}
78
+
79
+cmAlfRC_t cmAudLabelFileFree( cmAlfH_t* hp )
80
+{
81
+  cmAlfRC_t rc = kOkAlfRC;
82
+
83
+  if( hp == NULL || cmAudLabelFileIsValid(*hp)==false )
84
+    return kOkAlfRC;
85
+    
86
+  cmAlf_t* p = _cmAlfHandleToPtr(*hp);
87
+
88
+  if((rc = _cmAlfFree(p)) != kOkAlfRC )
89
+    return rc;
90
+
91
+  hp->h = NULL;
92
+
93
+  return rc;
94
+}
95
+
96
+bool cmAudLabelFileIsValid( cmAlfH_t h )
97
+{ return h.h != NULL; }
98
+
99
+void _cmAlfInsert( cmAlf_t* p, cmReal_t begSecs, cmReal_t endSecs, const cmChar_t* label )
100
+{
101
+  cmAlfRecd_t* np     = p->list;
102
+  cmAlfRecd_t* pp     = NULL;
103
+  cmAlfRecd_t* ip      = cmLhAllocZ(p->lH,cmAlfRecd_t,1);
104
+
105
+  ip->r.begSecs = begSecs;
106
+  ip->r.endSecs = endSecs;
107
+  ip->r.label   = label==NULL || strlen(label)==0 ? NULL : cmLhAllocStr(p->lH,label);
108
+
109
+  // set np to the next recd and
110
+  // set pp to the prev recd
111
+  while(np != NULL )
112
+  {
113
+    if( np->r.begSecs > begSecs )
114
+      break;
115
+
116
+    pp = np;
117
+    np = np->link;
118
+  }
119
+
120
+  ip->link = np;
121
+
122
+  // if the new recd is first on the list
123
+  if( pp == NULL )
124
+    p->list  = ip;
125
+  else
126
+    pp->link = ip;
127
+
128
+  // incr the recd count
129
+  ++p->cnt;
130
+}
131
+
132
+// remove the record just after pp
133
+void _cmAlfRemove( cmAlf_t* p, cmAlfRecd_t* pp )
134
+{
135
+  // if the list is already empty
136
+  if( p->list == NULL )
137
+    return;
138
+
139
+  // if the first recd should be removed
140
+  if( pp == NULL )
141
+  {
142
+    p->list = p->list->link;
143
+  }
144
+  else
145
+  {
146
+    // if pp points to the last recd
147
+    if( pp->link == NULL )
148
+      return;
149
+
150
+    // remove pp->link from the list
151
+    pp->link = pp->link->link;
152
+  }
153
+
154
+  assert( p->cnt != 0 );
155
+  --p->cnt;
156
+
157
+}
158
+  
159
+cmAlfRC_t cmAudLabelFileOpen(   cmAlfH_t h, const cmChar_t* fn )
160
+{
161
+  cmAlfRC_t rc;
162
+  cmAlf_t*  p              = _cmAlfHandleToPtr(h);
163
+  cmChar_t* lineBuf        = NULL;
164
+  unsigned  lineBufByteCnt = 0;
165
+  unsigned  line           = 1;
166
+  cmFileH_t fH             = cmFileNullHandle;
167
+
168
+  // open the label file
169
+  if( cmFileOpen(&fH,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
170
+  {
171
+    rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label file '%s' could not be openend.",cmStringNullGuard(fn));
172
+    goto errLabel;
173
+  }
174
+
175
+  // read each line
176
+  while( cmFileGetLineAuto(fH,&lineBuf,&lineBufByteCnt) == kOkFileRC )
177
+  {
178
+    cmReal_t  begSecs;
179
+    cmReal_t  endSecs;
180
+    cmChar_t* label  = NULL;
181
+    cmChar_t* begPtr = lineBuf;
182
+    cmChar_t* endPtr = NULL;
183
+
184
+    // parse the start time in seconds
185
+    errno = 0;
186
+    begSecs = strtod(begPtr,&endPtr);
187
+    if( errno != 0 )
188
+      return cmErrMsg(&p->err,kSyntaxErrAlfRC,"Begin time conversion error on line %i in '%s'.",line,cmFileName(fH));
189
+      
190
+    // parse the end time in seconds
191
+    begPtr = endPtr;
192
+    endSecs = strtod(begPtr,&endPtr);
193
+    if( errno != 0 )
194
+      return cmErrMsg(&p->err,kSyntaxErrAlfRC,"End time conversion error on line %i in '%s'.",line,cmFileName(fH));
195
+
196
+    label = endPtr;
197
+
198
+    // eat any leading white space off the label
199
+    while( *label )
200
+    {
201
+      if( isspace(*label) )
202
+        ++label;
203
+      else
204
+        break;
205
+    }
206
+
207
+    // trim trailing space and '\n' from the label.
208
+    int i = strlen(label)-1;
209
+    for(; i>=0; --i)
210
+    {
211
+      if( isspace(label[i]) )
212
+        label[i]=0;
213
+      else
214
+        break;
215
+    }
216
+
217
+    // if the label does not exist
218
+    if( strlen(label)==0 )
219
+      label = NULL;
220
+    
221
+    // insert a new recd
222
+    _cmAlfInsert(p,begSecs,endSecs,label);
223
+
224
+    ++line;
225
+  }
226
+
227
+  cmMemPtrFree(&lineBuf);
228
+
229
+  if( cmFileClose(&fH) != kOkFileRC )
230
+    rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label file close failed.");
231
+
232
+ errLabel:
233
+  return rc;
234
+}
235
+
236
+cmAlfRC_t cmAudLabelFileInsert( cmAlfH_t h, cmReal_t begSecs, cmReal_t endSecs, const cmChar_t* label )
237
+{
238
+  cmAlfRC_t rc = kOkAlfRC;
239
+  cmAlf_t*  p  = _cmAlfHandleToPtr(h);
240
+  _cmAlfInsert(p,begSecs,endSecs,label);
241
+  return rc;
242
+}
243
+  
244
+unsigned cmAudLabelFileCount( cmAlfH_t h )
245
+{
246
+  cmAlf_t*  p  = _cmAlfHandleToPtr(h);
247
+  return p->cnt;
248
+}
249
+const cmAlfLabel_t* cmAudLabelFileLabel( cmAlfH_t h, unsigned idx )
250
+{
251
+  cmAlf_t*     p  = _cmAlfHandleToPtr(h);
252
+  cmAlfRecd_t* lp = p->list;
253
+  unsigned     i;
254
+
255
+  for(i=0; lp!=NULL && i<idx; ++i)
256
+    lp            = lp->link;
257
+
258
+  return &lp->r;
259
+}
260
+
261
+cmAlfRC_t cmAudLabelFileWrite( cmAlfH_t h, const cmChar_t* fn )
262
+{
263
+  cmAlfRC_t    rc = kOkAlfRC;
264
+  cmAlf_t*     p  = _cmAlfHandleToPtr(h);
265
+  cmAlfRecd_t* lp = p->list;
266
+  cmFileH_t    fH = cmFileNullHandle;
267
+
268
+  if( cmFileOpen(&fH,fn,kWriteFileFl,p->err.rpt) != kOkFileRC )
269
+  {
270
+    rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label output file '%s' could not be created.",cmStringNullGuard(fn));
271
+    goto errLabel;
272
+  }
273
+
274
+  for(; lp!=NULL; lp=lp->link)
275
+  {
276
+    if( cmFilePrintf(fH,"%f %f %s",lp->r.begSecs,lp->r.endSecs,lp->r.label == NULL ? "" : lp->r.label) != kOkFileRC )
277
+    {
278
+      rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label output file write failed.");
279
+      goto errLabel;
280
+    }
281
+  }
282
+
283
+ errLabel:
284
+  if( cmFileClose(&fH) != kOkFileRC )
285
+  {
286
+    rc = cmErrMsg(&p->err,kFileFailAlfRC,"The audio label output file '%s' close failed.",cmStringNullGuard(fn));
287
+    
288
+  }
289
+
290
+  return rc;
291
+}
292
+
293
+
294
+void cmAudLabelFileTest( cmCtx_t* ctx )
295
+{
296
+  const cmChar_t* fn = "/home/kevin/temp/labels.txt";
297
+  const cmChar_t* ofn = "/home/kevin/temp/labels_out.txt"; 
298
+  cmAlfH_t h = cmAlfNullHandle;
299
+
300
+  if( cmAudLabelFileAllocOpen(ctx,&h,fn) == kOkAlfRC )
301
+  {
302
+    unsigned n = cmAudLabelFileCount(h);
303
+    unsigned i;
304
+    for(i=0; i<n; ++i)
305
+    {
306
+      const cmAlfLabel_t* lp;
307
+      if((lp = cmAudLabelFileLabel(h,i)) != NULL )
308
+        cmRptPrintf(&ctx->rpt,"%f %f %s\n",lp->begSecs,lp->endSecs,lp->label);
309
+        
310
+    }
311
+
312
+    cmAudLabelFileWrite(h,ofn);
313
+
314
+    cmAudLabelFileFree(&h);
315
+  }
316
+}

+ 50
- 0
cmAudLabelFile.h View File

@@ -0,0 +1,50 @@
1
+#ifndef cmAudLabelFile_h
2
+#define cmAudLabelFile_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+enum
9
+{
10
+  kOkAlfRC = cmOkRC,
11
+  kLHeapFailAlfRC,
12
+  kFileFailAlfRC,
13
+  kSyntaxErrAlfRC,
14
+  kAlfFileFailPuRC
15
+};
16
+
17
+  typedef cmRC_t     cmAlfRC_t;
18
+  typedef cmHandle_t cmAlfH_t;
19
+
20
+  extern cmAlfH_t cmAlfNullHandle;
21
+
22
+  typedef struct
23
+  {
24
+    cmReal_t        begSecs;
25
+    cmReal_t        endSecs;
26
+    const cmChar_t* label;
27
+  } cmAlfLabel_t;
28
+
29
+  cmAlfRC_t cmAudLabelFileAlloc(     cmCtx_t* ctx, cmAlfH_t* hp );
30
+  cmAlfRC_t cmAudLabelFileAllocOpen( cmCtx_t* ctx, cmAlfH_t* hp, const cmChar_t* fn );
31
+  cmAlfRC_t cmAudLabelFileFree(    cmAlfH_t* hp );
32
+
33
+  bool      cmAudLabelFileIsValid( cmAlfH_t h );
34
+  
35
+  cmAlfRC_t cmAudLabelFileOpen(    cmAlfH_t h, const cmChar_t* fn );
36
+
37
+  cmAlfRC_t cmAudLabelFileInsert(  cmAlfH_t h, cmReal_t begSecs, cmReal_t endSecs, const cmChar_t* label );
38
+  
39
+  unsigned cmAudLabelFileCount(    cmAlfH_t h );
40
+  const cmAlfLabel_t* cmAudLabelFileLabel( cmAlfH_t h, unsigned idx );
41
+
42
+  cmAlfRC_t cmAudLabelFileWrite( cmAlfH_t h, const cmChar_t* fn );
43
+  
44
+  void cmAudLabelFileTest( cmCtx_t* ctx );
45
+
46
+#ifdef __cplusplus
47
+}
48
+#endif
49
+
50
+#endif

+ 942
- 0
cmAudioAggDev.c View File

@@ -0,0 +1,942 @@
1
+#include "cmGlobal.h"
2
+#include "cmRpt.h"
3
+#include "cmErr.h"
4
+#include "cmCtx.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmAudioPort.h"
8
+#include "cmAudioAggDev.h"
9
+#include "cmThread.h" // cmThUIntIncr()
10
+
11
+#include "cmApBuf.h"  // only needed for cmApBufTest().
12
+
13
+//#include <unistd.h> // usleep
14
+
15
+enum
16
+{
17
+  kBufArrayCnt = 2
18
+};
19
+
20
+struct cmApAgg_str;
21
+
22
+typedef struct 
23
+{
24
+  unsigned             physDevIdx;
25
+  struct cmApAgg_str*  ap;
26
+
27
+  unsigned             iChIdx;
28
+  unsigned             iChCnt;
29
+
30
+  unsigned             oChIdx;
31
+  unsigned             oChCnt;
32
+} cmApAggDev_t;
33
+
34
+typedef struct cmApAgg_str
35
+{
36
+  cmChar_t*           label;          // agg. device label
37
+  unsigned            aggDevIdx;      // agg. device index
38
+  unsigned            sysDevIdx;      // system device index
39
+  unsigned            devCnt;         // count of phys devices 
40
+  cmApAggDev_t*       devArray;       // devArray[ devCnt ] - physical device array
41
+  unsigned            iChCnt;         // sum of phys device input channels
42
+  unsigned            oChCnt;         // sum of phys device output channels
43
+  double              srate;          // agg. dev sample rate
44
+  unsigned            framesPerCycle; // agg. dev frames per cycle
45
+  unsigned            flags;          // kAgInFl | kAgOutFl
46
+  cmApCallbackPtr_t   cbFunc;         // client supplied callback func
47
+  void*               cbArg;          // client supplied callback func arg.
48
+  bool                startedFl;      // true if the agg. device is started
49
+  struct cmApAgg_str* link;           // _cmAg.list link
50
+} cmApAgg_t;
51
+
52
+typedef struct
53
+{
54
+  cmErr_t    err;
55
+  cmApAgg_t* list;
56
+} cmApAggMain_t;
57
+
58
+cmApAggMain_t _cmAg;
59
+
60
+
61
+void _cmApAggCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
62
+{
63
+  unsigned          i;
64
+  cmApAudioPacket_t pkt;
65
+
66
+  for(i=0; i<inPktCnt; ++i)
67
+  {
68
+    cmApAggDev_t* dp = (cmApAggDev_t*)inPktArray[i].userCbPtr;  
69
+    pkt           = inPktArray[i];
70
+    pkt.devIdx    = dp->ap->sysDevIdx;
71
+    pkt.begChIdx  = dp->iChIdx;
72
+    pkt.userCbPtr = dp->ap->cbArg;
73
+    dp->ap->cbFunc( &pkt, 1, NULL, 0 );    
74
+  }
75
+
76
+  for(i=0; i<outPktCnt; ++i)
77
+  {
78
+    cmApAggDev_t* dp = (cmApAggDev_t*)outPktArray[i].userCbPtr;  
79
+    pkt           = outPktArray[i];
80
+    pkt.devIdx    = dp->ap->sysDevIdx;
81
+    pkt.begChIdx  = dp->oChIdx;
82
+    pkt.userCbPtr = dp->ap->cbArg;
83
+    dp->ap->cbFunc( NULL, 0, &pkt, 1 );        
84
+  }
85
+
86
+}
87
+
88
+
89
+void _cmApAgDeleteAggDev( cmApAgg_t* ap )
90
+{
91
+  cmApAgg_t* cp = _cmAg.list;
92
+  cmApAgg_t* pp = NULL;
93
+  while( cp != NULL )
94
+  {    
95
+    if( cp == ap )
96
+    {
97
+      if( pp == NULL )
98
+        _cmAg.list = cp->link;
99
+      else
100
+        pp->link = cp->link;
101
+
102
+      cmMemFree(ap->label);
103
+      cmMemFree(ap->devArray);
104
+      cmMemFree(ap);
105
+      return;
106
+    }
107
+    pp = cp;
108
+    cp = cp->link;
109
+  }
110
+}
111
+
112
+cmAgRC_t      cmApAggAllocate( cmRpt_t* rpt )
113
+{
114
+  cmAgRC_t rc = kOkAgRC;
115
+
116
+  cmErrSetup(&_cmAg.err,rpt,"cmAudioAggDev");
117
+
118
+  _cmAg.list = NULL;
119
+  
120
+  return rc;
121
+}
122
+
123
+cmAgRC_t      cmApAggFree()
124
+{
125
+  cmAgRC_t rc = kOkAgRC;
126
+
127
+  while( _cmAg.list != NULL )
128
+    _cmApAgDeleteAggDev(_cmAg.list );
129
+
130
+  return rc;
131
+}
132
+
133
+cmAgRC_t      cmApAggInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
134
+{ 
135
+  cmApAgg_t* ap = _cmAg.list;
136
+  unsigned i;
137
+
138
+  assert( baseApDevIdx == cmApDeviceCount() );
139
+
140
+  for(i=0; ap!=NULL; ap=ap->link,++i)
141
+  {
142
+    ap->sysDevIdx = cmApDeviceCount() + i;
143
+    ap->iChCnt    = 0;
144
+    ap->oChCnt    = 0;
145
+
146
+    unsigned i;
147
+    for(i=0; i<ap->devCnt; ++i)
148
+    {
149
+      ap->devArray[i].iChIdx  = ap->iChCnt;
150
+      ap->devArray[i].oChIdx  = ap->oChCnt;
151
+      ap->devArray[i].iChCnt  = cmApDeviceChannelCount(ap->devArray[i].physDevIdx,true);
152
+      ap->devArray[i].oChCnt  = cmApDeviceChannelCount(ap->devArray[i].physDevIdx,false);
153
+      ap->iChCnt             += ap->devArray[i].iChCnt;
154
+      ap->oChCnt             += ap->devArray[i].oChCnt;
155
+    }
156
+
157
+    
158
+  }
159
+
160
+  return kOkAgRC;
161
+}
162
+
163
+cmAgRC_t      cmApAggFinalize()
164
+{ return kOkAgRC; }
165
+
166
+cmAgRC_t      cmApAggCreateDevice(
167
+  const cmChar_t* label,
168
+  unsigned       devCnt,
169
+  const unsigned physDevIdxArray[],
170
+  unsigned       flags )
171
+{
172
+  cmAgRC_t rc = kOkAgRC;
173
+  unsigned i;
174
+
175
+  if( devCnt < 2 )
176
+    return cmErrMsg(&_cmAg.err,kMustAggTwoAgRC,"Cannot aggregate less than two devices.");
177
+  /*
178
+
179
+  for(i=0; i<devCnt; ++i)
180
+  {
181
+    unsigned physDevIdx = physDevIdxArray[i];
182
+
183
+    if( cmApAggIsDeviceAggregated(physDevIdx) )
184
+      return cmErrMsg(&_cmAg.err,kDevAlreadyAggAgRC,"The physical device associated with index '%i' ('%s') has already been assigned to another aggregated device.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
185
+
186
+    if( cmApDeviceIsStarted(physDevIdx) )
187
+      return cmErrMsg(&_cmAg.err,kCantUseStartedDevAgRC,"The physical device associated with index '%i' ('%s') cannot be aggregated while it is running.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
188
+
189
+  }
190
+  */
191
+
192
+  cmApAgg_t* ap            = cmMemAllocZ(cmApAgg_t,1);
193
+  ap->label                = cmMemAllocStr(label==NULL?"Aggregated Device":label);
194
+  ap->devArray             = cmMemAllocZ(cmApAggDev_t,devCnt);
195
+  ap->aggDevIdx            = cmApAggDeviceCount();
196
+  ap->sysDevIdx            = cmInvalidIdx;
197
+  ap->devCnt               = devCnt;
198
+  ap->iChCnt               = 0;
199
+  ap->oChCnt               = 0;
200
+
201
+  for(i=0; i<devCnt; ++i)
202
+  {
203
+    ap->devArray[i].ap          = ap;
204
+    ap->devArray[i].physDevIdx  = physDevIdxArray[i];
205
+  }
206
+
207
+  ap->link = _cmAg.list;
208
+  _cmAg.list = ap;
209
+
210
+  return rc;
211
+}
212
+
213
+cmApAgg_t* _cmApAggDevIdxToPtr( unsigned aggDevIdx )
214
+{
215
+  cmApAgg_t* ap = _cmAg.list;
216
+  unsigned   i  = 0;
217
+  for(; ap!=NULL; ap=ap->link,++i)
218
+    if( ap->aggDevIdx == aggDevIdx )
219
+      return ap;
220
+  return NULL;
221
+}
222
+
223
+cmAgRC_t  _cmApAggGetAgg( unsigned aggDevIdx, cmApAgg_t** retPtrPtr )
224
+{
225
+  if((*retPtrPtr = _cmApAggDevIdxToPtr(aggDevIdx)) == NULL )
226
+    return cmErrMsg(&_cmAg.err,kInvalidDevIdxAgRC,"The aggregate system device index '%i' is invalid.");
227
+  return kOkAgRC;
228
+}
229
+
230
+
231
+bool cmApAggIsDeviceAggregated( unsigned physDevIdx )
232
+{
233
+  cmApAgg_t* ap = _cmAg.list;
234
+  for(; ap!=NULL; ap=ap->link)
235
+  {
236
+    unsigned i;
237
+    for(i=0; i<ap->devCnt; ++i)
238
+      if( ap->devArray[i].physDevIdx == physDevIdx )
239
+        return true;
240
+  }
241
+  return false;
242
+}
243
+
244
+cmAgRC_t      cmApAggDeviceCount()
245
+{
246
+  unsigned devCnt=0;
247
+  cmApAgg_t* ap = _cmAg.list;
248
+  for(; ap!=NULL; ap=ap->link)
249
+    ++devCnt;
250
+
251
+  return devCnt;
252
+}
253
+
254
+const char*   cmApAggDeviceLabel(          unsigned aggDevIdx )
255
+{
256
+  cmApAgg_t* ap;
257
+  cmAgRC_t   rc;
258
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
259
+    return ap->label;    
260
+  return NULL;
261
+}
262
+
263
+unsigned      cmApAggDeviceChannelCount(   unsigned aggDevIdx, bool inputFl )
264
+{
265
+  cmApAgg_t* ap;
266
+  cmAgRC_t   rc;
267
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
268
+    return inputFl ? ap->iChCnt : ap->oChCnt;    
269
+  return 0;
270
+}
271
+
272
+double        cmApAggDeviceSampleRate(     unsigned aggDevIdx )
273
+{
274
+  cmApAgg_t* ap;
275
+  cmAgRC_t   rc;
276
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
277
+    return ap->srate;
278
+  return 0;  
279
+}
280
+
281
+unsigned      cmApAggDeviceFramesPerCycle( unsigned aggDevIdx, bool inputFl )
282
+{
283
+  cmApAgg_t* ap;
284
+  cmAgRC_t   rc;
285
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) == kOkAgRC )
286
+    return ap->framesPerCycle;
287
+  return 0;  
288
+}
289
+
290
+cmAgRC_t      cmApAggDeviceSetup(          
291
+  unsigned             aggDevIdx, 
292
+  double               srate, 
293
+  unsigned             framesPerCycle, 
294
+  cmApCallbackPtr_t    callbackPtr,
295
+  void*                userCbPtr )
296
+{
297
+  cmApAgg_t* ap;
298
+  cmAgRC_t   rc;
299
+  unsigned   i;
300
+
301
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
302
+    return rc;
303
+
304
+  if((rc = cmApAggDeviceStop(aggDevIdx)) != kOkAgRC )
305
+    return rc;
306
+
307
+  for(i=0; i<ap->devCnt; ++i)
308
+  {
309
+    unsigned      physDevIdx = ap->devArray[i].physDevIdx;
310
+    cmApAggDev_t* devPtr     = ap->devArray + i;
311
+
312
+    if( cmApDeviceSetup( physDevIdx, srate, framesPerCycle, _cmApAggCb, devPtr ) != kOkApRC )
313
+      rc = cmErrMsg(&_cmAg.err,kPhysDevSetupFailAgRC,"The physical device (index:%i '%s') setup failed for sample rate:%f frames-per-cycle:%i.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)),srate,framesPerCycle);
314
+  }
315
+
316
+  if( rc == kOkAgRC )
317
+  {
318
+    ap->cbFunc      = callbackPtr;
319
+    ap->cbArg       = userCbPtr;
320
+  }
321
+
322
+  return rc;
323
+}
324
+
325
+cmAgRC_t      cmApAggDeviceStart( unsigned aggDevIdx )
326
+{
327
+  cmAgRC_t   rc = kOkAgRC;
328
+  cmApAgg_t* ap;
329
+  unsigned   i;
330
+
331
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
332
+    return rc;
333
+  
334
+  for(i=0; i<ap->devCnt; ++i)
335
+  {
336
+    unsigned physDevIdx = ap->devArray[i].physDevIdx;
337
+
338
+    if( cmApDeviceStart( physDevIdx ) != kOkApRC )
339
+      return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
340
+
341
+    //usleep(1000);
342
+  }
343
+
344
+  ap->startedFl = true;
345
+
346
+  return rc;
347
+}
348
+
349
+cmAgRC_t      cmApAggDeviceStop(  unsigned aggDevIdx )
350
+{
351
+  cmAgRC_t   rc = kOkAgRC;
352
+  cmApAgg_t* ap;
353
+  unsigned   i;
354
+
355
+  if((rc = _cmApAggGetAgg(aggDevIdx, &ap )) != kOkAgRC )
356
+    return rc;
357
+  
358
+  for(i=0; i<ap->devCnt; ++i)
359
+  {
360
+    unsigned physDevIdx = ap->devArray[i].physDevIdx;
361
+
362
+    if( cmApDeviceStop( physDevIdx ) != kOkApRC )
363
+      return cmErrMsg(&_cmAg.err,kPhysDevStartFailAgRC,"The physical device (index:%i '%s') start failed.",physDevIdx,cmStringNullGuard(cmApDeviceLabel(physDevIdx)));
364
+  }
365
+
366
+  ap->startedFl = false;
367
+
368
+  return rc;
369
+}
370
+
371
+bool          cmApAggDeviceIsStarted( unsigned aggDevIdx )
372
+{
373
+  cmApAgg_t* ap;
374
+
375
+  if(_cmApAggGetAgg(aggDevIdx, &ap ) != kOkAgRC )
376
+    return false;
377
+
378
+  return ap->startedFl;
379
+}
380
+
381
+
382
+
383
+
384
+
385
+
386
+typedef struct
387
+{
388
+  unsigned      bufCnt;         // 2=double buffering 3=triple buffering
389
+  unsigned      chIdx;          // first test channel
390
+  unsigned      chCnt;          // count of channels to test
391
+  unsigned      framesPerCycle; // DSP frames per cycle
392
+  unsigned      bufFrmCnt;      // count of DSP frames used by the audio buffer  (bufCnt * framesPerCycle)
393
+  unsigned      bufSmpCnt;      // count of samples used by the audio buffer     (chCnt  * bufFrmCnt)
394
+  unsigned      inDevIdx;       // input device index
395
+  unsigned      outDevIdx;      // output device index
396
+  double        srate;          // audio sample rate
397
+  unsigned      meterMs;        // audio meter buffer length
398
+
399
+  // param's and state for cmApSynthSine()
400
+  bool          synthFl;
401
+  unsigned      phase;          // sine synth phase
402
+  double        frqHz;          // sine synth frequency in Hz
403
+
404
+  // buffer and state for cmApCopyIn/Out()
405
+  cmApSample_t* buf;            // buf[bufSmpCnt] - circular interleaved audio buffer
406
+  unsigned      bufInIdx;       // next input buffer index
407
+  unsigned      bufOutIdx;      // next output buffer index
408
+  unsigned      bufFullCnt;     // count of full samples
409
+
410
+  unsigned      cbCnt;         // count the callback
411
+  unsigned      underunCnt;    // 
412
+  unsigned      overunCnt;
413
+
414
+  double*        iMeter;         // iMeter[ chCnt ]
415
+
416
+  FILE* ifp;
417
+  FILE* ofp;
418
+} cmApAggPortTestRecd;
419
+
420
+// The application can request any block of channels from the device. The packets are provided with the starting
421
+// device channel and channel count.  This function converts device channels and channel counts to buffer
422
+// channel indexes and counts.  
423
+//
424
+//  Example:
425
+//      input                            output
426
+//       i,n                              i n
427
+//  App: 0,4   0 1 2 3                ->  2 2
428
+//  Pkt  2,8       2 3 4 5 6 7 8      ->  0 2
429
+//
430
+// The return value is the count of application requested channels located in this packet.
431
+//
432
+// input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application.
433
+//        *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application
434
+//
435
+// output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples.
436
+//        *pktChIdxPtr and <return value>  describe a block of pkt buffer channels which will send/recv samples
437
+//
438
+unsigned _cmApAggDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt )
439
+{
440
+  unsigned abi = *appChIdxPtr;
441
+  unsigned aei = abi+appChCnt-1;
442
+
443
+  unsigned pbi = *pktChIdxPtr;
444
+  unsigned pei = pbi+pktChCnt-1;
445
+
446
+  // if the ch's rqstd by the app do not overlap with this packet - return false.
447
+  if( aei < pbi || abi > pei )
448
+    return 0;
449
+
450
+  // if the ch's rqstd by the app overlap with the beginning of the pkt channel block
451
+  if( abi < pbi )
452
+  {
453
+    appChCnt     -= pbi - abi;
454
+    *appChIdxPtr  = pbi - abi;
455
+    *pktChIdxPtr  = 0;
456
+  }
457
+  else
458
+  {
459
+    // the rqstd ch's begin inside the pkt channel block
460
+    pktChCnt     -= abi - pbi;
461
+    *pktChIdxPtr  = abi - pbi;
462
+    *appChIdxPtr  = 0;
463
+  }
464
+
465
+  // if the pkt channels extend beyond the rqstd ch block
466
+  if( aei < pei )
467
+    pktChCnt -= pei - aei;
468
+  else 
469
+    appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block
470
+
471
+  // the returned channel count must always be the same for both the rqstd and pkt 
472
+  return cmMin(appChCnt,pktChCnt);
473
+
474
+}
475
+
476
+
477
+// synthesize a sine signal into an interleaved audio buffer
478
+unsigned _cmApAggSynthSine( cmApAggPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz )
479
+{
480
+  long     ph = 0;
481
+  unsigned i;
482
+  unsigned bufIdx    = r->chIdx;
483
+  unsigned bufChCnt;
484
+
485
+  if( (bufChCnt =  _cmApAggDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0)
486
+    return phs;
487
+
488
+  
489
+  //if( r->cbCnt < 50 )
490
+  //  printf("ch:%i cnt:%i  ch:%i cnt:%i  bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt);
491
+ 
492
+
493
+  for(i=bufIdx; i<bufIdx+bufChCnt; ++i)
494
+  {
495
+    unsigned j;
496
+    float*   op = p + i;
497
+
498
+    ph = phs;
499
+    for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
500
+    {
501
+      *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate ));
502
+    }
503
+  }
504
+  
505
+  return ph;
506
+}
507
+
508
+// Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt]
509
+// to the internal record buffer.
510
+void _cmApAggCopyIn( cmApAggPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt  )
511
+{
512
+  unsigned i,j;
513
+
514
+  unsigned chCnt = cmMin(r->chCnt,srcChCnt);
515
+
516
+  // write the incoming sample to an output file for debugging
517
+  if( r->ifp != NULL )
518
+    if( fwrite(sp,sizeof(cmApSample_t),srcChCnt*srcFrameCnt,r->ifp) != srcChCnt*srcFrameCnt )
519
+      printf("file write fail.\n");
520
+
521
+  // zero the input meter array
522
+  for(i=0; i<r->chCnt; ++i)
523
+    r->iMeter[i] = 0;
524
+
525
+  for(i=0; i<srcFrameCnt; ++i)
526
+  {
527
+    // copy samples from the src to the buffer - both src and buffer are interleaved
528
+    for(j=0; j<chCnt; ++j)
529
+    {
530
+      r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + srcChIdx + j ];
531
+      
532
+      // record the max value in the input meter array
533
+      if( r->buf[ r->bufInIdx + j ] > r->iMeter[j] )
534
+        r->iMeter[j] = r->buf[ r->bufInIdx + j ];
535
+    }
536
+
537
+    // zero channels that are not used in the buffer
538
+    for(; j<r->chCnt; ++j)
539
+      r->buf[ r->bufInIdx + j ] = 0;
540
+
541
+    // advance to the next frame
542
+    r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
543
+  }
544
+
545
+  //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
546
+  cmThUIntIncr(&r->bufFullCnt,srcFrameCnt);
547
+
548
+  if( r->bufFullCnt > r->bufFrmCnt )
549
+  {
550
+    //printf("Input buffer overrun.\n");
551
+    ++r->overunCnt;
552
+    r->bufFullCnt = 0;
553
+  }
554
+
555
+}
556
+
557
+// Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
558
+void _cmApAggCopyOut( cmApAggPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt )
559
+{
560
+ 
561
+  // if there are not enough samples available to fill the destination 
562
+  // buffer then zero the dst buf.
563
+  if( r->bufFullCnt < dstFrameCnt )
564
+  {
565
+    //printf("Empty Output Buffer %i < %i\n",r->bufFullCnt,dstFrameCnt);
566
+    memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
567
+    ++r->underunCnt;
568
+  }
569
+  else
570
+  {
571
+    unsigned i,j;
572
+    unsigned chCnt = cmMin(dstChCnt,r->chCnt);
573
+
574
+    for(i=0; i<dstFrameCnt; ++i)
575
+    {
576
+      // copy the stored buffer samples to the dst buffer
577
+      for(j=0; j<chCnt; ++j)
578
+        dp[ (i*dstChCnt) + dstChIdx + j ] = r->buf[ r->bufOutIdx + j ];
579
+
580
+      // zero unset channels in the dst buffer
581
+      for(; j<dstChCnt; ++j)
582
+        dp[ (i*dstChCnt) + dstChIdx + j ] = 0;
583
+
584
+      r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
585
+    }
586
+
587
+    cmThUIntDecr(&r->bufFullCnt,dstFrameCnt);
588
+  }
589
+
590
+  if( r->ofp != NULL )
591
+    fwrite(dp,sizeof(cmApSample_t),dstChCnt*dstFrameCnt,r->ofp);
592
+}
593
+
594
+// Audio port callback function called from the audio device thread.
595
+void _cmApAggPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
596
+{
597
+  unsigned i;
598
+
599
+  // for each incoming audio packet
600
+
601
+  for(i=0; i<inPktCnt; ++i)
602
+  {
603
+    cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)inPktArray[i].userCbPtr; 
604
+
605
+    if( r->synthFl==false && inPktArray[i].devIdx == r->inDevIdx )
606
+    {
607
+      // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
608
+      _cmApAggCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
609
+    }
610
+    ++r->cbCnt;
611
+
612
+    //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
613
+  }
614
+
615
+  unsigned hold_phase = 0;
616
+
617
+  // for each outgoing audio packet
618
+  for(i=0; i<outPktCnt; ++i)
619
+  {
620
+    cmApAggPortTestRecd* r = (cmApAggPortTestRecd*)outPktArray[i].userCbPtr; 
621
+
622
+    if( outPktArray[i].devIdx == r->outDevIdx )
623
+    {
624
+      // zero the output buffer
625
+      memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) );
626
+      
627
+      // if the synth is enabled
628
+      if( r->synthFl )
629
+      {
630
+        unsigned tmp_phase  = _cmApAggSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz );  
631
+
632
+        // the phase will only change on packets that are actually used
633
+        if( tmp_phase != r->phase )
634
+          hold_phase = tmp_phase;
635
+      }
636
+      else
637
+      {
638
+        // copy the any audio in the internal record buffer to the playback device 
639
+        _cmApAggCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt );   
640
+      }
641
+    }
642
+
643
+    r->phase = hold_phase;
644
+
645
+    //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
646
+    // count callbacks
647
+    ++r->cbCnt;
648
+  }
649
+}
650
+
651
+
652
+// print the usage message for cmAudioPortTest.c
653
+void _cmApAggPrintUsage( cmRpt_t* rpt )
654
+{
655
+char msg[] =
656
+  "cmApAggPortTest() command switches\n"
657
+  "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n"
658
+  "\n"
659
+  "-r <srate> = sample rate\n"
660
+  "-a <chidx> = first channel\n"
661
+  "-c <chcnt> = audio channels\n"
662
+  "-b <bufcnt> = count of buffers\n"
663
+  "-f <frmcnt> = count of samples per buffer\n"
664
+  "-i <idevidx> = input device index\n"
665
+  "-o <odevidx> = output device index\n"
666
+  "-p = print report but do not start audio devices\n"
667
+  "-h = print this usage message\n";
668
+
669
+ cmRptPrintf(rpt,msg);
670
+}
671
+
672
+// Get a command line option.
673
+int _cmApAggGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
674
+{
675
+  int i = 0;
676
+  for(; i<argc; ++i)
677
+    if( strcmp(label,argv[i]) == 0 )
678
+    {
679
+      if(boolFl)
680
+        return 1;
681
+
682
+      if( i == (argc-1) )
683
+        return defaultVal;
684
+
685
+      return atoi(argv[i+1]);
686
+    }
687
+  
688
+  return defaultVal;
689
+}
690
+
691
+
692
+void _cmApBufShowMeter( cmRpt_t* rpt, unsigned devIdx )
693
+{
694
+  unsigned faultCnt = 0;
695
+  unsigned meterCnt = cmApBufChannelCount(devIdx,kInApFl);
696
+  double   meterArray[ meterCnt ];
697
+
698
+  unsigned n = cmApBufGetStatus(devIdx, kInApFl, meterArray, meterCnt, &faultCnt );
699
+  unsigned i;
700
+
701
+  cmRptPrintf(rpt,"In: actual:%i fault: %i : ",n,faultCnt);
702
+  for(i=0; i<meterCnt; ++i)
703
+    cmRptPrintf(rpt,"%i:%f ",i,meterArray[i]);
704
+  cmRptPrintf(rpt,"\n");
705
+}
706
+
707
+
708
+unsigned _cmAggGlobalInDevIdx  = 0;
709
+unsigned _cmAggGlobalOutDevIdx = 0;
710
+
711
+void _cmApAggPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
712
+{
713
+
714
+  cmApBufInputToOutput( _cmAggGlobalInDevIdx, _cmAggGlobalOutDevIdx );
715
+
716
+  cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
717
+}
718
+
719
+
720
+  void recdPrint();
721
+
722
+// Audio Port testing function
723
+int cmApAggTest( bool runFl, cmCtx_t* ctx, int argc, const char* argv[] )
724
+{
725
+  cmApAggPortTestRecd r;
726
+  unsigned         i;
727
+  cmRpt_t* rpt = &ctx->rpt;
728
+
729
+  if( _cmApAggGetOpt(argc,argv,"-h",0,true) )
730
+    _cmApAggPrintUsage(rpt);
731
+
732
+
733
+  runFl            = _cmApAggGetOpt(argc,argv,"-p",!runFl,true)?false:true;
734
+  r.chIdx          = _cmApAggGetOpt(argc,argv,"-a",0,false);
735
+  r.chCnt          = _cmApAggGetOpt(argc,argv,"-c",2,false);
736
+  r.bufCnt         = _cmApAggGetOpt(argc,argv,"-b",3,false);
737
+  r.framesPerCycle = _cmApAggGetOpt(argc,argv,"-f",512,false);
738
+  r.bufFrmCnt      = (r.bufCnt*r.framesPerCycle);
739
+  r.bufSmpCnt      = (r.chCnt  * r.bufFrmCnt);
740
+  r.synthFl        = false;
741
+  r.meterMs        = 50;
742
+
743
+  cmApSample_t buf[r.bufSmpCnt];
744
+  double      imeter[r.chCnt];
745
+
746
+  r.iMeter     = imeter;      
747
+  
748
+  r.inDevIdx   =  _cmAggGlobalInDevIdx = _cmApAggGetOpt(argc,argv,"-i",0,false);   
749
+  r.outDevIdx  =  _cmAggGlobalOutDevIdx = _cmApAggGetOpt(argc,argv,"-o",2,false); 
750
+  r.phase      = 0;
751
+  r.frqHz      = 2000;
752
+  r.srate      = 44100;
753
+  r.bufInIdx   = 0;
754
+  r.bufOutIdx  = 0;
755
+  r.bufFullCnt = 0;
756
+
757
+  r.buf        = buf;
758
+  r.cbCnt      = 0;
759
+  r.underunCnt = 0;
760
+  r.overunCnt  = 0;
761
+  r.ifp        = NULL;
762
+  r.ofp        = NULL;
763
+
764
+  
765
+  if(0)
766
+  {
767
+    if((r.ifp = fopen("/home/kevin/temp/itemp0.bin","wb")) == NULL )
768
+      cmRptPrintf(rpt,"File open failed.\n");
769
+
770
+    if((r.ofp = fopen("/home/kevin/temp/otemp0.bin","wb")) == NULL )
771
+      cmRptPrintf(rpt,"File open failed.\n");
772
+  }
773
+
774
+  cmRptPrintf(rpt,"%s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f\n",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate);
775
+
776
+  // allocate the aggregate device system
777
+  if( cmApAggAllocate(rpt) != kOkAgRC )
778
+  {
779
+    cmRptPrintf(rpt,"The aggregate device system allocation failed.\n");
780
+    return 1;
781
+  }
782
+
783
+
784
+  unsigned physDevIdxArray[] = { 0, 1 };
785
+  unsigned physDevCnt = sizeof(physDevIdxArray)/sizeof(physDevIdxArray[0]);
786
+  if( cmApAggCreateDevice("aggdev",physDevCnt,physDevIdxArray,kInAggFl | kOutAggFl) != kOkAgRC )
787
+  {
788
+    cmRptPrintf(rpt,"The aggregate device creation failed.n");
789
+    goto doneLabel;
790
+  }
791
+
792
+
793
+  // initialize the audio device interface
794
+  if( cmApInitialize(rpt) != kOkApRC )
795
+  {
796
+    cmRptPrintf(rpt,"Initialize failed.\n");
797
+    goto doneLabel;
798
+  }
799
+  
800
+  // report the current audio device configuration
801
+  for(i=0; i<cmApDeviceCount(); ++i)
802
+  {
803
+    cmRptPrintf(rpt,"%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%f %s\n",i,cmApDeviceChannelCount(i,true),cmApDeviceFramesPerCycle(i,true),cmApDeviceChannelCount(i,false),cmApDeviceFramesPerCycle(i,false),cmApDeviceSampleRate(i),cmApDeviceLabel(i));
804
+  }
805
+  // report the current audio devices using the audio port interface function
806
+  cmApReport(rpt);
807
+
808
+
809
+  if( runFl )
810
+  {
811
+    // initialize the audio bufer
812
+    cmApBufInitialize( cmApDeviceCount(), r.meterMs );
813
+
814
+    // setup the buffer for the output device
815
+    cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle );
816
+
817
+    // setup the buffer for the input device
818
+    if( r.inDevIdx != r.outDevIdx )
819
+      cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle ); 
820
+
821
+    // setup an input device
822
+    if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
823
+    {
824
+      cmRptPrintf(rpt,"In device setup failed.\n");
825
+      goto errLabel;
826
+    }
827
+
828
+    // setup an output device
829
+    if( r.inDevIdx != r.outDevIdx )
830
+    {
831
+      if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApAggPortCb2,&r) != kOkApRC )
832
+      {
833
+        cmRptPrintf(rpt,"Out device setup failed.\n");
834
+        goto errLabel;
835
+      }
836
+    }
837
+
838
+    // start the input device
839
+    if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
840
+    {
841
+      cmRptPrintf(rpt,"In device start failed.\n");
842
+      goto errLabel;
843
+    }
844
+
845
+    if( r.inDevIdx != r.outDevIdx )
846
+    {
847
+      // start the output device
848
+      if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
849
+      {
850
+        cmRptPrintf(rpt,"Out Device start failed.\n");
851
+        goto errLabel;
852
+      }
853
+    }
854
+
855
+    cmApBufEnableChannel(r.inDevIdx, -1, kInApFl | kEnableApFl );
856
+    cmApBufEnableChannel(r.outDevIdx, -1, kOutApFl | kEnableApFl );
857
+    cmApBufEnableMeter(r.inDevIdx, -1, kInApFl | kEnableApFl );
858
+
859
+    cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
860
+    char c;
861
+    while((c=getchar()) != 'q')
862
+    {
863
+      //cmApDeviceRtReport(rpt,r.outDevIdx);
864
+
865
+      switch(c)
866
+      {
867
+        case 'i':
868
+        case 'I':
869
+          cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
870
+          break;
871
+
872
+        case 'o':
873
+        case 'O':
874
+          cmApBufEnableTone(r.outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
875
+          break;
876
+
877
+        case 'p':
878
+        case 'P':
879
+          cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
880
+          break;
881
+          
882
+        case 's':
883
+          cmApBufReport(rpt);
884
+          break;
885
+
886
+        case 'm':
887
+          _cmApBufShowMeter(rpt,_cmAggGlobalInDevIdx);
888
+          
889
+          /*
890
+          cmRptPrintf(rpt,"iMeter: ");
891
+          for(i=0; i<r.chCnt; ++i)
892
+            cmRptPrintf(rpt,"%f ",r.iMeter[i]);
893
+          cmRptPrintf(rpt,"\n");
894
+          */
895
+          break;
896
+
897
+
898
+        case 'r':
899
+          recdPrint();
900
+          break;
901
+
902
+        default:
903
+          cmRptPrintf(rpt,"cb:%i\n",r.cbCnt);
904
+      }
905
+
906
+    }
907
+
908
+  errLabel:
909
+    // stop the input device
910
+    if( cmApDeviceIsStarted(r.inDevIdx) )
911
+      if( cmApDeviceStop(r.inDevIdx) != kOkApRC )
912
+        cmRptPrintf(rpt,"In device stop failed.\n");
913
+
914
+    // stop the output device
915
+    if( cmApDeviceIsStarted(r.outDevIdx) )
916
+      if( cmApDeviceStop(r.outDevIdx) != kOkApRC )
917
+        cmRptPrintf(rpt,"Out device stop failed.\n");
918
+  }
919
+
920
+ doneLabel:
921
+
922
+  // report the count of audio buffer callbacks
923
+  cmRptPrintf(rpt,"cb:%i under:%i over:%i\n", r.cbCnt, r.underunCnt, r.overunCnt );
924
+
925
+
926
+  // release any resources held by the audio port interface
927
+  if( cmApFinalize() != kOkApRC )
928
+    cmRptPrintf(rpt,"Finalize failed.\n");
929
+
930
+  if( cmApAggFree() != kOkAgRC )
931
+    cmRptPrintf(rpt,"Agg device system free failed.");
932
+
933
+  if(r.ifp != NULL)
934
+    fclose(r.ifp);
935
+
936
+  if(r.ofp != NULL)
937
+    fclose(r.ofp);
938
+
939
+  cmApBufFinalize();
940
+
941
+  return 0;
942
+}

+ 104
- 0
cmAudioAggDev.h View File

@@ -0,0 +1,104 @@
1
+#ifndef cmAudioAggDev_h
2
+#define cmAudioAggDev_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkAgRC = cmOkRC,
11
+    kMustAggTwoAgRC,
12
+    kCantUseStartedDevAgRC,
13
+    kDevAlreadyAggAgRC,
14
+    kInvalidDevIdxAgRC,
15
+    kPhysDevSetupFailAgRC,
16
+    kPhysDevStartFailAgRC,
17
+    kPhysDevStopFailAgRC
18
+  };
19
+
20
+  typedef cmRC_t cmAgRC_t;
21
+
22
+  /// Allocate/free the aggregate device management system
23
+  cmAgRC_t      cmApAggAllocate( cmRpt_t* rpt );
24
+  cmAgRC_t      cmApAggFree();
25
+
26
+
27
+  /// Called by cmAudioPort() driver to notify the aggregate device
28
+  /// system that the hardware ports have been initialized.
29
+  /// Setup the aggregate audio device  management object for this machine.
30
+  cmAgRC_t      cmApAggInitialize( cmRpt_t* rpt, unsigned baseApDevIdx );
31
+
32
+  /// Called by cmAudioPort() driver to notify the aggregate device
33
+  /// system that the hardware ports have been finalized.
34
+  /// Stop all aggregate audio devices and release any resources held 
35
+  /// by the agg. audio dev. management object.
36
+  cmAgRC_t      cmApAggFinalize();
37
+
38
+  /// Create an aggregate device from physical devices.
39
+  /// Set flags to kInApFl, kOutApFl or both to indicate whether the
40
+  /// device should aggregate input audio, output audio or both.
41
+  enum { kInAggFl = 0x01, kOutAggFl = 0x02 };
42
+  cmAgRC_t      cmApAggCreateDevice(
43
+    const cmChar_t* label,
44
+    unsigned       devCnt,
45
+    const unsigned physDevIdxArray[],
46
+    unsigned       flags );
47
+
48
+
49
+  // Return true if the specified physical device is included 
50
+  // in an aggregated device.
51
+  bool cmApAggIsDeviceAggregated( unsigned physDevIdx );
52
+
53
+
54
+  /// Return the count of aggregate audio devices attached to this machine.
55
+  cmAgRC_t      cmApAggDeviceCount();
56
+
57
+  /// Get a textual description of the device at index 'aggDevIdx'.
58
+  const char*   cmApAggDeviceLabel(          unsigned aggDevIdx );
59
+
60
+  /// Get the count of audio input or output channels on device at index 'aggDevIdx'.
61
+  unsigned      cmApAggDeviceChannelCount(   unsigned aggDevIdx, bool inputFl );
62
+
63
+  /// Get the current sample rate of a device.  Note that if the device has both
64
+  /// input and output capability then the sample rate is the same for both.
65
+  double        cmApAggDeviceSampleRate(     unsigned aggDevIdx );
66
+
67
+  /// Get the count of samples per callback for the input or output for this device.
68
+  unsigned      cmApAggDeviceFramesPerCycle( unsigned aggDevIdx, bool inputFl );
69
+
70
+  /// Configure a device.  
71
+  /// All devices must be setup before they are started.
72
+  /// framesPerCycle is the requested number of samples per audio callback. The
73
+  /// actual number of samples made from a callback may be smaller. See the note
74
+  /// regarding this in cmApAggAudioPacket_t.
75
+  /// If the device cannot support the requested configuration then the function
76
+  /// will return an error code.
77
+  /// If the device is started when this function is called then it will be 
78
+  /// automatically stopped and then restarted following the reconfiguration.
79
+  /// If the reconfiguration fails then the device may not be restared.
80
+  cmAgRC_t      cmApAggDeviceSetup(          
81
+    unsigned          aggDevIdx, 
82
+    double            srate, 
83
+    unsigned          framesPerCycle, 
84
+    cmApCallbackPtr_t callbackPtr,
85
+    void*             userCbPtr );
86
+
87
+  /// Start a device. Note that the callback may be made prior to this function returning.
88
+  cmAgRC_t      cmApAggDeviceStart( unsigned aggDevIdx );
89
+
90
+  /// Stop a device.
91
+  cmAgRC_t      cmApAggDeviceStop(  unsigned aggDevIdx );
92
+
93
+  /// Return true if the device is currently started.
94
+  bool          cmApAggDeviceIsStarted( unsigned aggDevIdx );
95
+
96
+  int cmApAggTest(  bool runFl, cmCtx_t* ctx, int argc, const char* argv[] );
97
+
98
+
99
+#ifdef __cplusplus
100
+}
101
+#endif
102
+
103
+
104
+#endif

+ 285
- 0
cmAudioBuf.c View File

@@ -0,0 +1,285 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmAudioPort.h"
9
+#include "cmAudioBuf.h"
10
+
11
+enum
12
+{
13
+  kInIdx = 0;
14
+  kOutIdx = 1;
15
+  kIoCnt = 2;
16
+};
17
+
18
+typedef struct
19
+{
20
+  cmApAudioPacket_t pkt;        //
21
+  char*             buf;        // buf[bufByteCnt]
22
+  unsigned          bufByteCnt; //
23
+} cmAudioBufDevBuf_t;
24
+
25
+typedef struct
26
+{
27
+  cmAudioBufSubDev_t* subDevArray;
28
+  unsigned            subDevCnt;
29
+  unsigned            readyCnt;    
30
+} cmAudioBufCycle_t;
31
+
32
+typedef struct
33
+{
34
+  unsigned           chCnt;       // device channel count
35
+  cmAudioBufCycle_t* cycleArray;  // subBufArray[ subBufCnt ] 
36
+  unsigned           cycleCnt;    // set by cmAudioBufSetup().subBufCnt
37
+  unsigned           faultCnt;    // count of overruns (input) underruns (output)
38
+  unsigned           iCycleIdx;   // in: next buf to rcv on abUpdate() out: rcv on abGet()
39
+  unsigned           oCycleIdx;   // in: next buf to send on abGet()   out: send on abUpdate()
40
+  unsigned           fullCnt;     // in: cnt of bufs ready for abGet() out: cnt of bufs ready for abUpdate()  
41
+} cmAudioBufIO_t;
42
+
43
+typedef struct
44
+{
45
+  cmAudioBufIO_t ioArray[kIoCnt];
46
+} cmAudioBufDev_t;
47
+
48
+typedef struct
49
+{
50
+  cmErr_t          err;
51
+  unsigned         devCnt;
52
+  cmAudioBufDev_t* devArray;       // devArray[ devCnt ]
53
+  unsigned         zeroBufByteCnt; // max of all possible buf sizes for all devices
54
+  char*            zeroBuf;        // zeroBuf[ zeroBufByteCnt ]
55
+  unsigned         updateCnt;
56
+  unsigned         nextSeqId;
57
+} cmAudioBuf_t;
58
+
59
+cmAudioBuf_t _cmBa;
60
+
61
+
62
+cmBaRC_t cmAudioBufInit( cmCtx_t* ctx, unsigned devCnt )
63
+{
64
+  cmBaRC_t rc = kOkBaRC;
65
+
66
+  cmErrSetup(_cmBa.err,&ctx->rpt,"Audio Buf");
67
+
68
+  _cmBa.devArray       = cmMemAllocZ(cmAudioBufDev_t,devCnt);
69
+  _cmBa.devCnt         = devCnt;
70
+  _cmBa.zeroBufByteCnt = 0;
71
+  _cmBa.zeroBuf        = NULL;
72
+  _cmBa.updateCnt      = 0;
73
+  _cmBa.nextSeqId      = 0;
74
+  return rc;
75
+}
76
+
77
+cmBaRC_t _cmAudioBufIoFree( cmAudioBufIO_t* iop )
78
+{
79
+  unsigned i;
80
+  for(i=0; i<iop->subBufCnt; ++i)
81
+    cmMemPtrFree(&iop->subBufArray[i].buf);
82
+
83
+  cmMemPtrFree(&iop->subBufArray);
84
+
85
+  return kOkBaRC;
86
+} 
87
+
88
+cmBaRC_t cmAudioBufFinal()
89
+{
90
+  cmBaRC_t rc = kOkBaRC;
91
+  unsigned i,j;
92
+  for(i=0; i<_cmBa.devCnt; ++i)
93
+    for(j=0; j<kIoCnt; ++j)
94
+      _cmAudioBufIoFree( _cmBa.devArray[i].ioArray + j );
95
+
96
+  cmMemPtrFree(&_cmBa.devArray);
97
+  _cmBa.devCnt = 0;
98
+  cmMemPtrFree(&_cmBa.zeroBuf);
99
+  _cmBa.zeroBufByteCnt = 0;
100
+}
101
+
102
+
103
+cmBaRC_t cmAudioBufSetup(
104
+  unsigned devIdx,
105
+  unsigned cycleCnt,
106
+  unsigned inSubDevCnt,
107
+  unsigned inChCnt,
108
+  unsigned inFrameCnt,
109
+  unsigned outChCnt,
110
+  unsigned outFrameCnt,
111
+  unsigned outSubDevCnt )
112
+{
113
+  assert(devIdx < _cmBa.devCnt );
114
+  cmAudioBufDev_t* dp = _cmBa.devArray + devIdx;
115
+  unsigned i,j;
116
+  for(i=0; i<kIoCnt; ++i)
117
+  {
118
+    cmAudioBufIO_t* iop = dp->ioArray + i;
119
+
120
+    _cmAudioBufIoFree(iop);
121
+    
122
+    iop->subBufArray = cmMemAllocZ(cmAudioBufSubBuf_t,subBufCnt);
123
+    iop->subBufCnt   = 0;
124
+    
125
+    unsigned maxSampleWidthByteCnt = 4;
126
+
127
+    // max size of any buffer arriving via cmAudioBufUpdate() for this device/direction
128
+    unsigned bufByteCnt            = frameCnt*chCnt*maxSampleWidthByteCnt;
129
+
130
+    // initialize the sub-buf array for this device/direction
131
+    for(j=0; j<subBufCnt; ++j)
132
+    {
133
+      iop->subBufArray[j].buf        = cmMemAllocZ(char,bufByteCnt);
134
+      iop->subBufArray[j].bufByteCnt = bufByteCnt;
135
+    }
136
+
137
+    // track the largest buffer size and make _cmBa.zeroBuf[] that size
138
+    if( bufByteCnt > _cmBa.zeroBufByteCnt )
139
+    {
140
+      cmMemResizeZ(char,_cmBa.zeroBuf,bufByteCnt);
141
+      _cmBa.zeroBufByteCnt = bufByteCnt;
142
+    }
143
+  }
144
+}
145
+
146
+// Called from the audio driver within incoming samples to store (inPktArray)
147
+// and empty buffers (outPktArray) to fill with outgoin samples.
148
+cmBaRC_t cmAudioBufUpdate(
149
+  cmApAudioPacket_t* inPktArray,  ///< full audio packets from incoming audio (from ADC)
150
+  unsigned           inPktCnt,    ///< count of incoming audio packets
151
+  cmApAudioPacket_t* outPktArray, ///< empty audio packet for outgoing audio (to DAC)  
152
+  unsigned           outPktCnt    ///< count of outgoing audio packets
153
+                          )
154
+{
155
+  cmBaRC_t rc = kOkBaRC;
156
+
157
+  ++_cmBa.updateCnt;
158
+
159
+  unsigned i;
160
+  for(i=0; i<inPktCnt && inPktArray!=NULL; ++i)
161
+  {
162
+    cmApAudioPacket_t* ipp = inPktArray + i;
163
+
164
+    // get a pointer to the device/direction recd
165
+    cmAudioBufIO_t*    iop = _cmBa.devArray[ ipp->devIdx ].ioArray + kInIdx;
166
+
167
+    // check for overruns
168
+    if( iop->fullCnt == iop->subBufCnt )
169
+    {
170
+      // input overrun
171
+      ++ip->faultCnt;
172
+      rc = cmErrMsg(&_cmBa.err,kBufOverrunBaRC,"Input buffer overrun.");
173
+    }
174
+    else
175
+    {
176
+      // get the next available sub-buf
177
+      cmAudioBufSubBuf_t* sbp = iop->subBufArray + iop->iSubBufIdx;
178
+
179
+      // store the packet header
180
+      sbp->pkt = *ipp;
181
+      sbp->audioBytesPtr = sbp->buf;
182
+
183
+      // calc the count of bytes of incoming packet audio
184
+      unsigned pktBufByteCnt = ipp->chCnt * ipp->audioFramesCnt * (bitsPerSample/8);
185
+      assert( pktBufByteCnt <= sbp->bufByteCnt );
186
+
187
+      // copy the samples into the buffer
188
+      memcpy(sbp->buf,ipp->audioBytesPtr,pktBufByteCnt);
189
+    
190
+      // advance the input sub-buffer
191
+      iop->iSubBufIdx = (iop->iSubBufIdx + 1) % iop->subBufCnt;
192
+
193
+      iop->fullCnt+=1;
194
+    }
195
+  }
196
+
197
+  for(i=0; i<outPktCnt && outPktArray!=NULL; ++i)
198
+  {
199
+    unsigned j;
200
+    cmApAudioPacket_t* opp = outPktArray + i;
201
+
202
+    // get a pointer to the device/direction recd
203
+    cmAudioBufIO_t*    iop = _cmBa.devArray[ opp->devIdx ].ioArray + kOutIdx;
204
+    
205
+    // calc the number of requested bytes
206
+    unsigned pktBufByteCnt = opp->chCnt * opp->audioFramesCnt * (bitsPerSample/8);
207
+
208
+    // locate the oldest sub-buf which matches the pkt begin channel index
209
+    cmAudioBufSubBuf_t* sbp  = NULL;
210
+    cmAudioBufSubBuf_t* tsbp = iop->subBufArray;
211
+    for(j=0; j<iop->subBufCnt; ++j,++tsbp)
212
+      if( tsbp->fullFl && (tsbp->pkt.begChIdx == opp->pkt.begChIdx) )
213
+        if( sbp==NULL || tsbp->seqId < sbp->seqId )
214
+          sbp = tsbp;
215
+    
216
+    if( sbp == NULL )
217
+    {
218
+      ++opp->faultCnt;
219
+      rc = cmErrMsg(&_cmBa.err,kBufOverrunBaRC,"Output buffer underrun.");
220
+
221
+      // zero the pkt buffer
222
+      memset(opp->audioBytePtr,0,pktBufByteCnt);
223
+    }
224
+    else
225
+    {
226
+      // the channel count assoc'd with a given begin channel index should always match
227
+      assert( sbp->pkt.chCnt == opp->chCnt );
228
+
229
+      // we guarantee that the sample word width will always match
230
+      assert( sbp->pkt.bitsPerSample == opp->bitsPerSample);
231
+      
232
+      // we don't want to deal with the case where the requested number of samples
233
+      // is less than the number available from a single stored buffer - this would
234
+      // require sending out a partial buffer 
235
+      assert( opp->audioFrameCnt >= sbp->pkt.audioFrameCnt );
236
+
237
+      // calc the number of bytes to copy out
238
+      unsigned bufByteCnt = sbp->pkt.chCnt * sbp->pkt.audioFramesCnt * (sbp->pkt.bitsPerSample/8);
239
+      
240
+      assert(bufByteCnt <= pktBufByteCnt );
241
+
242
+      // copy out the requested samples
243
+      memcpy(opp->audioBytesPtr,sbp->buf,cmMin(bufByteCnt,pktBufByteCnt));
244
+
245
+      opp->audioFramesCnt = sbp->pkt.audioFramesCnt;
246
+
247
+      // mark the sub-buffer as available
248
+      sbp->fullFl = false;
249
+      iop->fullCnt -= 1;;
250
+    }
251
+  }
252
+
253
+
254
+  returnr c;
255
+}
256
+
257
+bool cmAudioBufIsDeviceReady( unsigned devIdx, unsigned flags )
258
+{
259
+  unsigned i;
260
+  assert( devIdx < _cmBa.devCnt );
261
+  
262
+  // 
263
+  if( cmIsFlag(flags,kInBaFl) )
264
+    if( _cmBa.devArray[devIdx].ioArray[kInIdx].fullCnt==0)
265
+      return false;
266
+
267
+  if( cmIsFlag(flags,kOutBaFl) )
268
+    if( _cmBa.devArray[devIdx].ioArray[kOutIdx].fullCnt == _cmBa.devArray[devIdx].ioArray[kOutIdx].subBufCnt )
269
+      return false;
270
+  
271
+  return true;
272
+
273
+}
274
+  
275
+cmBaRC_t cmAudioBufGet(     
276
+  unsigned           devIdx, 
277
+  unsigned           flags, 
278
+  cmApAudioPacket_t* pktArray[], 
279
+  unsigned           pktCnt )
280
+{
281
+}
282
+
283
+cmBaRC_t cmAudioBufAdvance( unsigned devIdx, unsigned flags )
284
+{
285
+}

+ 63
- 0
cmAudioBuf.h View File

@@ -0,0 +1,63 @@
1
+#ifndef cmAudioBuf_h
2
+#define cmAudioBuf_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkBaRC = cmOkRC
11
+    kBufOverunBaRC,
12
+    kBufUnderunBaRC
13
+  };
14
+
15
+  enum
16
+  {
17
+    kInBaFl  = 0x01,
18
+    kOutBaFl = 0x02
19
+  };
20
+
21
+  typedef cmRC_t cmBaRC_t;
22
+
23
+  cmBaRC_t cmAudioBufInit( cmCtx_t* ctx, unsigned devCnt );
24
+
25
+  cmBaRC_t cmAudioBufFinal();
26
+
27
+  cmBaRC_t cmAudioBufSetup(
28
+    unsigned devIdx,
29
+    unsigned cycleCnt,
30
+    unsigned inSubDevCnt,
31
+    unsigned inChCnt,
32
+    unsigned inFrameCnt,
33
+    unsigned outChCnt,
34
+    unsigned outFrameCnt,
35
+    unsigned outSubDevCnt );
36
+
37
+  // Called from the audio driver within incoming samples to store (inPktArray)
38
+  // and empty buffers (outPktArray) to fill with outgoin samples.
39
+  cmBaRC_t cmAudioBufUpdate(
40
+    cmApAudioPacket_t* inPktArray,  ///< full audio packets from incoming audio (from ADC)
41
+    unsigned           inPktCnt,    ///< count of incoming audio packets
42
+    cmApAudioPacket_t* outPktArray, ///< empty audio packet for outgoing audio (to DAC)  
43
+    unsigned           outPktCnt    ///< count of outgoing audio packets
44
+                         );
45
+
46
+  bool cmAudioBufIsDeviceReady( unsigned devIdx, unsigned flags );
47
+  
48
+  cmBaRC_t cmAudioBufGet(     
49
+    unsigned devIdx, 
50
+    unsigned flags, 
51
+    cmApAudioPacket_t* pktArray[], 
52
+    unsigned pktCnt );
53
+
54
+  cmBaRC_t cmAudioBufAdvance( unsigned devIdx, unsigned flags );
55
+
56
+
57
+
58
+
59
+#ifdef __cplusplus
60
+  }
61
+#endif
62
+
63
+#endif

+ 1696
- 0
cmAudioFile.c
File diff suppressed because it is too large
View File


+ 305
- 0
cmAudioFile.h View File

@@ -0,0 +1,305 @@
1
+/// \file cmAudioFile.h
2
+/// \brief Audio file reader/writer class.
3
+///
4
+/// This class supports reading uncompressed AIFF and WAV files and writing uncompressed AIFF files.
5
+///  The reading and writing routines are known to work with 8,16,24, and 32 bit integer sample formats.
6
+///
7
+/// Testing and example usage for this API can be found in cmProcTest.c cmAudioReadWriteTest().
8
+///
9
+/// Usage example:
10
+/// \snippet cmAudioFile.c cmAudioFileExample
11
+
12
+#ifndef cmAudioFile_h
13
+#define cmAudioFile_h
14
+
15
+#ifdef __cplusplus
16
+extern "C" {
17
+#endif
18
+
19
+#ifndef cmAudioFile_MAX_FRAME_READ_CNT
20
+/// Maximum number of samples which will be read in one call to fread().
21
+/// This value is only significant in that an internal buffer is created on the stack
22
+/// whose size must be limited to prevent stack overflows.
23
+#define cmAudioFile_MAX_FRAME_READ_CNT (8192) 
24
+#endif
25
+
26
+
27
+  /// Audio file result codes.
28
+  enum
29
+  {
30
+    kOkAfRC = 0,
31
+    kOpenFailAfRC,
32
+    kReadFailAfRC,
33
+    kWriteFailAfRC,
34
+    kSeekFailAfRC,
35
+    kCloseFailAfRC,
36
+    kNotAiffAfRC,
37
+    kInvalidBitWidthAfRC,
38
+    kInvalidFileModeAfRC,
39
+    kInvalidHandleAfRC,
40
+    kUnknownErrAfRC
41
+  };
42
+
43
+  /// Informational flags used by audioFileInfo
44
+  enum
45
+  {
46
+    kAiffAfFl        = 0x01,    ///< this is an AIFF file 
47
+    kWavAfFl         = 0x02,    ///< this is a WAV file 
48
+    kSwapAfFl        = 0x04,    ///< file header bytes must be swapped
49
+    kAifcAfFl        = 0x08,    ///< this is an AIFC file
50
+    kSwapSamplesAfFl = 0x10     ///< file sample bytes must be swapped
51
+  };
52
+
53
+
54
+  /// Constants
55
+  enum
56
+  {
57
+    kAudioFileLabelCharCnt = 256,
58
+
59
+    kAfBextDescN       = 256,
60
+    kAfBextOriginN     = 32,
61
+    kAfBextOriginRefN  = 32,
62
+    kAfBextOriginDateN = 10,
63
+    kAfBextOriginTimeN = 8
64
+  };
65
+
66
+  /// Aiff marker record
67
+  typedef struct
68
+  {
69
+    unsigned    id;
70
+    unsigned    frameIdx;
71
+    char        label[kAudioFileLabelCharCnt];
72
+  } cmAudioFileMarker_t;
73
+
74
+  /// Broadcast WAV header record As used by ProTools audio files. See http://en.wikipedia.org/wiki/Broadcast_Wave_Format
75
+  /// When generated from Protools the timeRefLow/timeRefHigh values appear to actually refer
76
+  /// to the position on the Protools time-line rather than the wall clock time.
77
+  typedef struct
78
+  {
79
+    char     desc[      kAfBextDescN       + 1 ];
80
+    char     origin[    kAfBextOriginN     + 1 ];
81
+    char     originRef[ kAfBextOriginRefN  + 1 ];
82
+    char     originDate[kAfBextOriginDateN + 1 ];
83
+    char     originTime[kAfBextOriginTimeN + 1 ];
84
+    unsigned timeRefLow;   // sample count since midnight low word
85
+    unsigned timeRefHigh;  // sample count since midnight high word
86
+  } cmAudioFileBext_t;
87
+
88
+  /// Audio file information record used by audioFileNew and audioFileOpen
89
+  typedef struct 
90
+  {
91
+    unsigned             bits;        ///< bits per sample
92
+    unsigned             chCnt;       ///< count of audio file channels
93
+    double               srate;       ///< audio file sample rate in samples per second
94
+    unsigned             frameCnt;    ///< total number of sample frames in the audio file
95
+    unsigned             flags;       ///< informational flags 
96
+    unsigned             markerCnt;   ///< count of markers in markerArray
97
+    cmAudioFileMarker_t* markerArray; ///< array of markers 
98
+    cmAudioFileBext_t    bextRecd;    ///< only used with Broadcast WAV files
99
+  } cmAudioFileInfo_t;
100
+
101
+
102
+  
103
+  typedef cmHandle_t cmAudioFileH_t;    ///< opaque audio file handle   
104
+  extern cmAudioFileH_t cmNullAudioFileH;  ///< NULL audio file handle
105
+
106
+  /// Create an audio file handle and optionally use the handle to open an audio file.
107
+  ///
108
+  /// \param  fn         The audio file name to open or NULL to create the audio file handle without opening the file.
109
+  /// \param  infoPtr    A pointer to an audioFileInfo record to be filled when the file is open or NULL to ignore.
110
+  /// \param  rcPtr      A pointer to a result code to be set in the event of a runtime error or NULL to ignore.
111
+  /// \param  rpt        A pointer to a cmRpt_t object which error messages from this class will be directed to.
112
+  /// \retval cmAudioFileH_t A new audio file handle.
113
+  ///
114
+  cmAudioFileH_t cmAudioFileNewOpen( const cmChar_t* fn, cmAudioFileInfo_t* infoPtr, cmRC_t* rcPtr, cmRpt_t* rpt ); 
115
+
116
+  /// Open an audio file for writing
117
+  cmAudioFileH_t cmAudioFileNewCreate( const cmChar_t* fn, double srate, unsigned bits, unsigned chCnt, cmRC_t* rcPtr, cmRpt_t* rpt );
118
+
119
+
120
+  /// Open an audio file for reading using a handle returned from an earlier call to audioFileNewXXX().
121
+  ///
122
+  /// \param  h          A file handle returned from and earlier call to cmAudioFileNewOpen() or cmAudioFileNewCreate().
123
+  /// \param  fn         The audio file name to open or NULL to create the audio file handle without opening the file.
124
+  /// \param  infoPtr    A pointer to an audioFileInfo record to be filled when the file is open or NULL to ignore.
125
+  /// \retval Returns an cmRC_t value indicating the success (kOkAfRC) or failure of the call.
126
+  ///
127
+  /// If the audio file handle 'h' refers to an open file then it is automatically closed prior to being
128
+  /// reopened with the new file.
129
+  cmRC_t     cmAudioFileOpen(       cmAudioFileH_t h, const cmChar_t* fn, cmAudioFileInfo_t* infoPtr );
130
+
131
+  /// Open an audio file for writing.
132
+  cmRC_t     cmAudioFileCreate(     
133
+    cmAudioFileH_t h,    ///< Handle returned from an earlier call to cmAudioFileNewCreate() or cmAudioFileNewOpen().
134
+    const cmChar_t* fn,  ///< File name of the new file.
135
+    double srate,        ///< Sample rate of the new file.
136
+    unsigned bits,       ///< Sample word width for the new file in bits (must be 8,16,24 or 32).
137
+    unsigned chCnt       ///< Audio channel count for the new file.
138
+                                    );
139
+
140
+  /// Close a the file associated with handle 'h' but do not release the handle.
141
+  /// If the file was opened for writing (cmAudioFileCreate()) then this function will
142
+  /// write the file header prior to closing the file.
143
+  cmRC_t     cmAudioFileClose(      cmAudioFileH_t* h );
144
+
145
+  /// Close the file associated with handle 'h' (via an internal call to 
146
+  /// cmAudioFileClose()) and release the handle and any resources
147
+  /// associated with it.  This is the complement to cmAudioFileOpen/Create().
148
+  cmRC_t     cmAudioFileDelete(     cmAudioFileH_t* h );
149
+
150
+  /// Return true if the handle is not closed or deleted.
151
+  bool       cmAudioFileIsValid(    cmAudioFileH_t h );
152
+
153
+  /// Return true if the handle is open.
154
+  bool       cmAudioFileIsOpen(     cmAudioFileH_t h );
155
+
156
+  /// Return true if the current file position is at the end of the file.
157
+  bool       cmAudioFileIsEOF(      cmAudioFileH_t h );
158
+
159
+  /// Return the current file position as a frame index.
160
+  unsigned   cmAudioFileTell(       cmAudioFileH_t h );
161
+
162
+  /// Set the current file position as an offset from the first frame.
163
+  cmRC_t     cmAudioFileSeek(       cmAudioFileH_t h, unsigned frmIdx );
164
+
165
+  /// \name Sample Reading Functions.
166
+  ///@{
167
+  /// Fill a user suppled buffer with up to frmCnt samples.
168
+  /// If less than frmCnt samples are available at the specified audio file location then the unused
169
+  /// buffer space is set to zero. Check *actualFrmCntPtr for the count of samples actually available
170
+  /// in the return buffer.  Functions which do not include a begFrmIdx argument begin reading from
171
+  /// the current file location (see cmAudioFileSeek()). The buf argument is always a pointer to an
172
+  /// array of pointers of length chCnt.  Each channel buffer specified in buf[] must contain at least
173
+  /// frmCnt samples.
174
+  ///
175
+  /// \param h               An audio file handle returned from an earlier call to audioFileNew()
176
+  /// \param fn              The name of the audio file to read.
177
+  /// \param begFrmIdx       The frame index of the first sample to read. Functions that do not use this parameter begin reading at the current file location (See cmAudioFileTell()).
178
+  /// \param frmCnt          The number of samples allocated in buf.
179
+  /// \param chIdx           The index of the first channel to read.
180
+  /// \param chCnt           The count of channels to read.
181
+  /// \param buf             An array containing chCnt pointers to arrays of frmCnt samples.
182
+  /// \param actualFrmCntPtr The number of frames actually written to the return buffer (ignored if NULL)
183
+
184
+  cmRC_t     cmAudioFileReadInt(    cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int**    buf, unsigned* actualFrmCntPtr );
185
+  cmRC_t     cmAudioFileReadFloat(  cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float**  buf, unsigned* actualFrmCntPtr );
186
+  cmRC_t     cmAudioFileReadDouble( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr );
187
+
188
+  cmRC_t     cmAudioFileGetInt(    const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int**    buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
189
+  cmRC_t     cmAudioFileGetFloat(  const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float**  buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
190
+  cmRC_t     cmAudioFileGetDouble( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
191
+
192
+  ///@}
193
+
194
+  /// \name Sum the returned samples into the output buffer.
195
+  ///@{
196
+  cmRC_t     cmAudioFileReadSumInt(    cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int**    buf, unsigned* actualFrmCntPtr );
197
+  cmRC_t     cmAudioFileReadSumFloat(  cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float**  buf, unsigned* actualFrmCntPtr );
198
+  cmRC_t     cmAudioFileReadSumDouble( cmAudioFileH_t h, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr );
199
+
200
+  cmRC_t     cmAudioFileGetSumInt(    const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, int**    buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
201
+  cmRC_t     cmAudioFileGetSumFloat(  const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, float**  buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
202
+  cmRC_t     cmAudioFileGetSumDouble( const char* fn, unsigned begFrmIdx, unsigned frmCnt, unsigned chIdx, unsigned chCnt, double** buf, unsigned* actualFrmCntPtr, cmAudioFileInfo_t* afInfoPtr, cmRpt_t* rpt );
203
+  ///@}
204
+
205
+  ///@}
206
+
207
+  /// \name Sample Writing Functions
208
+  ///@{
209
+  cmRC_t    cmAudioFileWriteInt(    cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, int**    bufPtrPtr );
210
+  cmRC_t    cmAudioFileWriteFloat(  cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, float**  bufPtrPtr );
211
+  cmRC_t    cmAudioFileWriteDouble( cmAudioFileH_t h, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr );
212
+
213
+  cmRC_t    cmAudioFileWriteFileInt(    const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, int**    bufPtrPtr, cmRpt_t* rpt );
214
+  cmRC_t    cmAudioFileWriteFileFloat(  const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, float**  bufPtrPtr, cmRpt_t* rpt );
215
+  cmRC_t    cmAudioFileWriteFileDouble( const char* fn, double srate, unsigned bit, unsigned frmCnt, unsigned chCnt, double** bufPtrPtr, cmRpt_t* rpt );
216
+  ///@}
217
+
218
+
219
+
220
+  /// \name cmSample_t and cmReal_t Alias Macros
221
+  ///@{
222
+  /// Alias the cmSample_t and cmReal_t sample reading and writing functions to the appropriate
223
+  /// type based on #CM_FLOAT_SMP and #CM_FLOAT_REAL.
224
+
225
+#if CM_FLOAT_SMP == 1
226
+
227
+#define cmAudioFileReadSample      cmAudioFileReadFloat
228
+#define cmAudioFileReadSumSample   cmAudioFileReadSumFloat
229
+#define cmAudioFileGetSample       cmAudioFileGetFloat
230
+#define cmAudioFileGetSumSample    cmAudioFileGetSumFloat
231
+#define cmAudioFileWriteSample     cmAudioFileWriteFloat
232
+#define cmAudioFileWriteFileSample cmAudioFileWriteFileFloat
233
+
234
+#else
235
+
236
+#define cmAudioFileReadSample      cmAudioFileReadDouble
237
+#define cmAudioFileReadSumSample   cmAudioFileReadSumDouble
238
+#define cmAudioFileGetSample       cmAudioFileGetDouble
239
+#define cmAudioFileGetSumSample    cmAudioFileGetSumDouble
240
+#define cmAudioFileWriteSample     cmAudioFileWriteDouble
241
+#define cmAudioFileWriteFileSample cmAudioFileWriteFileDouble
242
+
243
+#endif
244
+
245
+#if CM_FLOAT_REAL == 1
246
+
247
+#define cmAudioFileReadReal      cmAudioFileReadFloat
248
+#define cmAudioFileReadSumReal   cmAudioFileReadSumFloat
249
+#define cmAudioFileGetReal       cmAudioFileGetFloat
250
+#define cmAudioFileGetSumReal    cmAudioFileGetSumFloat
251
+#define cmAudioFileWriteReal     cmAudioFileWriteFloat
252
+#define cmAudioFileWriteFileReal cmAudioFileWriteFileFloat
253
+
254
+#else
255
+
256
+#define cmAudioFileReadReal      cmAudioFileReadDouble
257
+#define cmAudioFileReadSumReal   cmAudioFileReadSumDouble
258
+#define cmAudioFileGetReal       cmAudioFileGetDouble
259
+#define cmAudioFileGetSumReal    cmAudioFileGetSumDouble
260
+#define cmAudioFileWriteReal     cmAudioFileWriteDouble
261
+#define cmAudioFileWriteFileReal cmAudioFileWriteFileDouble
262
+
263
+#endif
264
+  ///@}
265
+
266
+
267
+  /// \name Minimum, Maximum, Mean
268
+  ///@{
269
+  /// Scan an entire audio file and return the minimum, maximum and mean sample value.
270
+  /// On error *minPtr, *maxPtr, and *meanPtr are set to -acSample_MAX, cmSample_MAX, and 0 respectively
271
+  cmRC_t     cmAudioFileMinMaxMean( cmAudioFileH_t h, unsigned chIdx, cmSample_t* minPtr, cmSample_t* maxPtr, cmSample_t* meanPtr );
272
+  cmRC_t     cmAudioFileMinMaxMeanFn( const cmChar_t* fn, unsigned chIdx, cmSample_t* minPtr, cmSample_t* maxPtr, cmSample_t* meanPtr, cmRpt_t* rpt );
273
+  ///@}
274
+
275
+  /// Return the file name associated with a audio file handle.
276
+  const cmChar_t* cmAudioFileName( cmAudioFileH_t h );
277
+
278
+  /// Given an error code return the associated message.
279
+  const char* cmAudioFileErrorMsg( unsigned rc );
280
+
281
+  /// \name Get information about an audio file
282
+  ///@{
283
+
284
+  /// Return the cmAudioFileInfo_t record associated with a file.
285
+  cmRC_t     cmAudioFileGetInfo(   const cmChar_t* fn, cmAudioFileInfo_t* infoPtr, cmRpt_t* rpt );
286
+  
287
+  /// Print the cmAudioFileInfo_t to a file.
288
+  void       cmAudioFilePrintInfo( const cmAudioFileInfo_t* infoPtr, cmRpt_t* );
289
+
290
+  /// Print the file header information and frmCnt sample values beginning at frame index frmIdx.
291
+  cmRC_t     cmAudioFileReport(   cmAudioFileH_t h,  cmRpt_t* rpt, unsigned frmIdx, unsigned frmCnt );
292
+
293
+    /// Print the file header information and  frmCnt sample values beginning at frame index frmIdx.
294
+  cmRC_t     cmAudioFileReportFn( const cmChar_t* fn, unsigned frmIdx, unsigned frmCnt, cmRpt_t* rpt );
295
+  ///@}
296
+
297
+  /// Testing and example routine for functions in cmAudioFile.h.
298
+  /// Also see cmProcTest.c cmAudioFileReadWriteTest()
299
+  void       cmAudioFileTest( const cmChar_t* audioFn, const cmChar_t* outFn, cmRpt_t* rpt );
300
+
301
+#ifdef __cplusplus
302
+}
303
+#endif
304
+
305
+#endif

+ 561
- 0
cmAudioFileDev.c View File

@@ -0,0 +1,561 @@
1
+#include "cmGlobal.h"
2
+#include "cmFloatTypes.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmAudioFile.h"
8
+#include "cmThread.h"
9
+#include "cmAudioPort.h"
10
+#include "cmAudioFileDev.h"
11
+
12
+#include <unistd.h> // usleep()
13
+
14
+#ifdef OS_OSX
15
+#include "osx/clock_gettime_stub.h"
16
+#endif
17
+
18
+#ifdef OS_LINUX
19
+#include <time.h> // clock_gettime()
20
+#endif
21
+
22
+
23
+cmAfdH_t cmAfdNullHandle = cmSTATIC_NULL_HANDLE;
24
+
25
+#define cmAfd_Billion (1000000000)
26
+#define cmAfd_Million    (1000000)
27
+
28
+typedef struct
29
+{
30
+  cmErr_t           err;             // error object 
31
+  cmApCallbackPtr_t callbackPtr;     // client callback function
32
+  void*             cbDataPtr;       // argument to be passed with the client callback 
33
+
34
+  unsigned          devIdx;
35
+  cmChar_t*         label;
36
+  cmChar_t*         oFn; 
37
+  unsigned          oBits;
38
+  unsigned          oChCnt;
39
+
40
+  cmAudioFileH_t    iAfH;            // audio input file handle
41
+  cmAudioFileH_t    oAfH;            // audio output file handle
42
+  cmThreadH_t       tH;              // thread handle
43
+
44
+  double            srate;           // file device sample rate
45
+  unsigned          framesPerCycle;  // count of samples sent/recv'd from the client on each callback
46
+
47
+  cmApAudioPacket_t iPkt;            // audio packet used sent to the client via callbackPtr.
48
+  cmApAudioPacket_t oPkt;            //
49
+  cmApSample_t**    iChArray;        // audio buffer channel arrays used with cmAudioFile
50
+  cmApSample_t**    oChArray;        //
51
+
52
+  bool              runFl;            // set to true as long as the thread should continue looping
53
+  bool              rewindFl;         // set to true when the input file should rewind
54
+  unsigned          readErrCnt;       // count of read errors from the input file
55
+  bool              eofFl;            // set to true when the input file reaches the EOF
56
+  unsigned          writeErrCnt;      // count of write errors from the output file
57
+
58
+  long              nanosPerCycle;    // nano-seconds per cycle
59
+  struct timespec   baseTime;      
60
+  struct timespec   nextTime;         // next execution time 
61
+
62
+  unsigned          cycleCnt;         // count of cycles completed
63
+
64
+} cmAfd_t;
65
+
66
+cmAfd_t* _cmAfdHandleToPtr( cmAfdH_t h )
67
+{
68
+  cmAfd_t* p = (cmAfd_t*)h.h;
69
+  assert(p != NULL );
70
+  return p;
71
+}
72
+
73
+// 
74
+void _cmAudioFileDevExec( cmAfd_t* p )
75
+{
76
+  unsigned iPktCnt = 0;
77
+  unsigned oPktCnt = p->oPkt.chCnt!=0;
78
+
79
+  // if the input device is enabled
80
+  if( p->iPkt.chCnt )
81
+  {
82
+    unsigned actualFrmCnt = p->framesPerCycle;
83
+
84
+    // if the input file has reached EOF - zero the input buffer
85
+    if( p->eofFl )
86
+      memset(p->iPkt.audioBytesPtr,0,p->framesPerCycle*sizeof(cmApSample_t));
87
+    else
88
+    {
89
+      // otherwise fill the input buffer from the input file
90
+      if( cmAudioFileReadSample(p->iAfH, p->framesPerCycle, p->iPkt.begChIdx, p->iPkt.chCnt, p->iChArray, &actualFrmCnt) != kOkAfRC )
91
+        ++p->readErrCnt;
92
+
93
+      // if the input file reachged EOF the set p->eofFl
94
+      if( (actualFrmCnt < p->framesPerCycle) && cmAudioFileIsEOF(p->iAfH) )
95
+        p->eofFl = true;
96
+    }
97
+
98
+    iPktCnt = actualFrmCnt>0;
99
+  }
100
+
101
+  // callback to the client to provde incoming samples and receive outgoing samples
102
+  p->callbackPtr(iPktCnt ? &p->iPkt : NULL, iPktCnt, oPktCnt ? &p->oPkt : NULL, oPktCnt );
103
+
104
+  // if the output device is enabled
105
+  if( p->oPkt.chCnt )
106
+  {
107
+    // write the output samples
108
+    if( cmAudioFileWriteSample( p->oAfH, p->framesPerCycle, p->oPkt.chCnt, p->oChArray ) != kOkAfRC )
109
+      ++p->writeErrCnt;
110
+  }
111
+
112
+  ++p->cycleCnt;
113
+
114
+}
115
+
116
+// incrment p->nextTime to the next execution time
117
+void _cmAfdIncrNextTime( cmAfd_t* p )
118
+{
119
+  long nsec            = p->nextTime.tv_nsec + p->nanosPerCycle;
120
+
121
+  if( nsec < cmAfd_Billion )
122
+    p->nextTime.tv_nsec = nsec;
123
+  else
124
+  {
125
+    p->nextTime.tv_sec  += 1;
126
+    p->nextTime.tv_nsec  = nsec - cmAfd_Billion;
127
+  }
128
+}
129
+
130
+// calc the time between t1 and t0 - t1 is assummed to come after t0 in order to produce a positive result
131
+long  _cmAfdDiffMicros( const struct timespec* t0, const struct timespec* t1 )
132
+{
133
+  long u0 = t0->tv_sec * cmAfd_Million;
134
+  long u1 = t1->tv_sec * cmAfd_Million;
135
+  
136
+  u0 += t0->tv_nsec / 1000;
137
+  u1 += t1->tv_nsec / 1000;
138
+
139
+  return u1 - u0;
140
+}
141
+
142
+//  thread callback function 
143
+bool _cmAudioDevThreadFunc(void* param)
144
+{
145
+  cmAfd_t* p       = (cmAfd_t*)param;
146
+  struct timespec t0;
147
+
148
+  // if this is the first time this callback has been called after a call to cmAudioFileDevStart().
149
+  if( p->cycleCnt == 0 )
150
+  {
151
+    // get the baseTime - all other times will be relative to this time
152
+    clock_gettime(CLOCK_REALTIME,&p->baseTime);
153
+    p->nextTime = p->baseTime;
154
+    p->nextTime.tv_sec = 0;
155
+    _cmAfdIncrNextTime(p);
156
+  }
157
+
158
+  // if the thread has not been requested to stop
159
+  if( p->runFl )
160
+  {
161
+    // get the current time as an offset from baseTime.
162
+    clock_gettime(CLOCK_REALTIME,&t0);
163
+    t0.tv_sec -= p->baseTime.tv_sec;
164
+
165
+    // get length of time to next exec point
166
+    long dusec = _cmAfdDiffMicros(&t0, &p->nextTime);
167
+
168
+    // if the execution time has not yet arrived
169
+    if( dusec > 0 )
170
+    {
171
+      usleep(dusec);
172
+    }
173
+
174
+    // if the thread is still running
175
+    if( p->runFl )
176
+    {
177
+      // read/callback/write
178
+      _cmAudioFileDevExec(p);
179
+  
180
+      // calc the next exec time
181
+      _cmAfdIncrNextTime(p);
182
+    }
183
+  }
184
+
185
+  return p->runFl;
186
+}
187
+
188
+cmAfdRC_t cmAudioFileDevInitialize( 
189
+  cmAfdH_t*       hp, 
190
+  const cmChar_t* label,
191
+  unsigned        devIdx,
192
+  const cmChar_t* iFn,
193
+  const cmChar_t* oFn,
194
+  unsigned        oBits,
195
+  unsigned        oChCnt,
196
+  cmRpt_t*        rpt )
197
+{
198
+  cmAfdRC_t rc;
199
+  cmRC_t    afRC;
200
+
201
+  if((rc = cmAudioFileDevFinalize(hp)) != kOkAfdRC )
202
+    return rc;
203
+  
204
+  // allocate the object
205
+  cmAfd_t* p = cmMemAllocZ(cmAfd_t,1);
206
+
207
+  hp->h = p;
208
+
209
+  cmErrSetup(&p->err,rpt,"AudioFileDevice");
210
+
211
+  // create the input audio file handle
212
+  if( iFn != NULL )
213
+  {
214
+    cmAudioFileInfo_t afInfo;
215
+
216
+    // open the input file
217
+    if(cmAudioFileIsValid( p->iAfH = cmAudioFileNewOpen(iFn,&afInfo,&afRC,rpt)) == false )
218
+    {
219
+      rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"The audio input file '%s' could not be opened.", iFn);
220
+      goto errLabel;
221
+    }
222
+
223
+
224
+
225
+    p->iPkt.devIdx         = devIdx;
226
+    p->iPkt.begChIdx       = 0;
227
+    p->iPkt.chCnt          = afInfo.chCnt;   // setting iPkt.chCnt to a non-zero value marks the input file as active
228
+    p->iPkt.audioFramesCnt = 0;
229
+    p->iPkt.bitsPerSample  = afInfo.bits;
230
+    p->iPkt.flags          = kFloatApFl;
231
+    p->iPkt.audioBytesPtr  = NULL;
232
+    p->iChArray            = cmMemResizeZ( cmApSample_t*, p->iChArray, afInfo.chCnt );
233
+    p->readErrCnt          = 0;
234
+    p->eofFl               = false;
235
+  }
236
+
237
+  // create the output audio file handle
238
+  if(cmAudioFileIsValid( p->oAfH = cmAudioFileNewOpen(NULL,NULL,NULL,rpt)) == false )
239
+  {
240
+    rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"The audio output file object allocation failed.");
241
+    goto errLabel;
242
+  }
243
+
244
+  // create the driver thread
245
+  if( cmThreadCreate(&p->tH, _cmAudioDevThreadFunc, p, rpt ) != kOkThRC )
246
+  {
247
+    rc = cmErrMsg(&p->err,kThreadFailAfdRC,"The internal thread could not be created.");
248
+    goto errLabel;
249
+  }
250
+
251
+  p->runFl  = true;
252
+  p->devIdx = devIdx;
253
+  p->label  = cmMemAllocStr(label);
254
+  p->oFn    = cmMemAllocStr(oFn);
255
+  p->oBits  = oBits;
256
+  p->oChCnt = oChCnt;
257
+
258
+ errLabel:
259
+  if( rc != kOkAfdRC )
260
+    cmAudioFileDevFinalize(hp);
261
+
262
+  return rc;
263
+}
264
+
265
+cmAfdRC_t cmAudioFileDevFinalize( cmAfdH_t* hp )
266
+{
267
+  if( hp == NULL || cmAudioFileDevIsValid(*hp) == false )
268
+    return kOkAfdRC;
269
+
270
+  cmAfd_t* p = _cmAfdHandleToPtr(*hp);
271
+
272
+  p->runFl = false;
273
+
274
+  if( cmThreadIsValid(p->tH) )
275
+    cmThreadDestroy(&p->tH);
276
+
277
+  cmAudioFileDelete(&p->iAfH);
278
+  cmAudioFileDelete(&p->oAfH);
279
+  cmMemPtrFree(&p->label);
280
+  cmMemPtrFree(&p->oFn);
281
+  cmMemPtrFree(&p->iPkt.audioBytesPtr);
282
+  cmMemPtrFree(&p->oPkt.audioBytesPtr);
283
+  cmMemPtrFree(&p->iChArray);
284
+  cmMemPtrFree(&p->oChArray);
285
+  cmMemPtrFree(&p);
286
+  hp->h = NULL;
287
+
288
+  return kOkAfdRC;
289
+}
290
+
291
+bool      cmAudioFileDevIsValid( cmAfdH_t h )
292
+{ return h.h != NULL;  }
293
+
294
+
295
+cmAfdRC_t cmAudioFileDevSetup( 
296
+  cmAfdH_t                        h, 
297
+  double                          srate, 
298
+  unsigned                        framesPerCycle, 
299
+  cmApCallbackPtr_t               callbackPtr, 
300
+  void*                           cbDataPtr )
301
+{
302
+  cmAfdRC_t rc        = kOkAfdRC;
303
+  bool      restartFl = false;
304
+  unsigned  i;
305
+
306
+  if( cmAudioFileDevIsStarted(h) )
307
+  {
308
+    if((rc = cmAudioFileDevStop(h)) != kOkAfdRC )
309
+      return rc;
310
+
311
+    restartFl = true;
312
+  }
313
+
314
+  cmAfd_t* p = _cmAfdHandleToPtr(h);
315
+
316
+  /*
317
+  // close the existing input file
318
+  if(cmAudioFileClose(&p->iAfH) != kOkAfRC )
319
+    rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file close failed on input audio file.");
320
+
321
+  p->iPkt.chCnt = 0; // mark the input file as inactive
322
+  */
323
+
324
+  if( cmAudioFileIsValid( p->iAfH ) )
325
+    if( cmAudioFileSeek( p->iAfH, 0 ) != kOkAfRC )
326
+      rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file device rewind failed.");
327
+
328
+
329
+  // close the existing output file
330
+  if(cmAudioFileClose(&p->oAfH) != kOkAfRC )
331
+    rc = cmErrMsg(&p->err,kAudioFileFailAfdRC,"Audio file close failed on output audio file.");
332
+
333
+  p->oPkt.chCnt = 0; // mark the output file as inactive
334
+
335
+  // if an output audio file was given ...
336
+  if( p->oFn != NULL )
337
+  {
338
+    // ... then open it
339
+    if( cmAudioFileCreate( p->oAfH, p->oFn, srate, p->oBits, p->oChCnt ) != kOkAfRC )
340
+    {
341
+      rc = cmErrMsg(&p->err,kAudioFileFailAfdRC, "The audio output file '%s' could not be created.",p->oFn);
342
+      goto errLabel;
343
+    }
344
+
345
+    cmApSample_t* bp = (cmApSample_t*)p->oPkt.audioBytesPtr;
346
+
347
+    p->oPkt.devIdx         = p->devIdx;
348
+    p->oPkt.begChIdx       = 0;
349
+    p->oPkt.chCnt          = p->oChCnt;
350
+    p->oPkt.audioFramesCnt = framesPerCycle;
351
+    p->oPkt.bitsPerSample  = p->oBits;
352
+    p->oPkt.flags          = kFloatApFl;
353
+    p->oPkt.audioBytesPtr  = bp = cmMemResizeZ( cmApSample_t, bp, framesPerCycle*p->oChCnt );
354
+    p->oChArray            = cmMemResizeZ( cmApSample_t*, p->oChArray, p->oChCnt );
355
+    
356
+    for(i=0; i<p->oChCnt; ++i)
357
+      p->oChArray[i] = bp + (i*framesPerCycle);
358
+
359
+  }
360
+
361
+  if( cmAudioFileIsValid( p->iAfH) )
362
+  {
363
+    cmApSample_t* bp = (cmApSample_t*)p->iPkt.audioBytesPtr;
364
+
365
+    p->iPkt.audioFramesCnt = framesPerCycle;
366
+    p->iPkt.audioBytesPtr  = bp = cmMemResizeZ( cmApSample_t, bp, framesPerCycle*p->iPkt.chCnt ); ;
367
+    for(i=0; i<p->iPkt.chCnt; ++i)
368
+      p->iChArray[i] = bp + (i*framesPerCycle);
369
+  }
370
+
371
+  p->callbackPtr    = callbackPtr;
372
+  p->cbDataPtr      = cbDataPtr;
373
+  p->framesPerCycle = framesPerCycle;
374
+  p->srate          = srate;
375
+  p->cycleCnt       = 0;
376
+  p->nanosPerCycle  = floor((double)framesPerCycle / srate * cmAfd_Billion );
377
+
378
+  if( restartFl )
379
+  {
380
+    if((rc = cmAudioFileDevStart(h)) != kOkAfdRC )
381
+    {
382
+      rc = cmErrMsg(&p->err,kRestartFailAfdRC,"The audio file device could not be restarted.");
383
+    }
384
+  }
385
+
386
+ errLabel:
387
+  return rc;
388
+
389
+}
390
+
391
+const char* cmAudioFileDevLabel( cmAfdH_t h )
392
+{
393
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h); 
394
+  return p->label;
395
+}
396
+
397
+unsigned    cmAudioFileDevChannelCount( cmAfdH_t h, bool inputFl )
398
+{
399
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h); 
400
+  return inputFl ? p->iPkt.chCnt : p->oPkt.chCnt;
401
+}
402
+
403
+double      cmAudioFileDevSampleRate(   cmAfdH_t h )
404
+{
405
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h);
406
+  return p->srate;
407
+}
408
+
409
+unsigned    cmAudioFileDevFramesPerCycle( cmAfdH_t h, bool inputFl )
410
+{
411
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h);
412
+  return inputFl ? p->iPkt.audioFramesCnt : p->oPkt.audioFramesCnt;
413
+}
414
+
415
+
416
+cmAfdRC_t cmAudioFileDevRewind( cmAfdH_t h )
417
+{
418
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h);
419
+  p->rewindFl = true;
420
+  return kOkAfdRC;
421
+}
422
+
423
+cmAfdRC_t cmAudioFileDevStart( cmAfdH_t h )
424
+{
425
+  cmAfdRC_t rc = kOkAfdRC;
426
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h);
427
+
428
+  p->cycleCnt = 0;
429
+
430
+  if( cmThreadPause( p->tH, 0 ) != kOkThRC )
431
+  {
432
+    rc = cmErrMsg(&p->err,kThreadFailAfdRC,"Thread start failed.");
433
+    goto errLabel;
434
+  }
435
+
436
+  fputs("Start\n",stderr);
437
+
438
+ errLabel:
439
+  return rc;
440
+}
441
+
442
+cmAfdRC_t cmAudioFileDevStop( cmAfdH_t h )
443
+{
444
+  cmAfdRC_t rc = kOkAfdRC;
445
+  cmAfd_t*  p  = _cmAfdHandleToPtr(h);
446
+
447
+  if( cmThreadPause( p->tH, kPauseThFl | kWaitThFl ) != kOkThRC )
448
+  {
449
+    rc = cmErrMsg(&p->err,kThreadFailAfdRC,"Thread stop failed.");
450
+    goto errLabel;
451
+  }
452
+
453
+  fputs("Stop\n",stderr);
454
+
455
+ errLabel:
456
+  return rc;
457
+}
458
+
459
+bool      cmAudioFileDevIsStarted( cmAfdH_t h ) 
460
+{  
461
+  cmAfd_t* p = _cmAfdHandleToPtr(h);
462
+  return cmThreadState(p->tH) == kRunningThId; 
463
+}
464
+
465
+void cmAudioFileDevReport( cmAfdH_t h, cmRpt_t* rpt )
466
+{
467
+  cmAfd_t* p = _cmAfdHandleToPtr(h);
468
+  cmRptPrintf(rpt,"label:%s thr state:%i srate:%f\n",p->label,cmThreadState(p->tH),p->srate);
469
+  cmRptPrintf(rpt, "in  chs:%i %s\n",p->iPkt.chCnt,cmAudioFileName(p->iAfH));
470
+  cmRptPrintf(rpt, "out chs:%i %s\n",p->oPkt.chCnt,p->oFn);
471
+}
472
+
473
+// device callback function used with cmAudioFileDevTest() note that this assumes
474
+// that the packet buffer contain non-interleaved data.
475
+void _cmAfdCallback( 
476
+  cmApAudioPacket_t* inPktArray, 
477
+  unsigned           inPktCnt, 
478
+  cmApAudioPacket_t* outPktArray, 
479
+  unsigned           outPktCnt )
480
+{
481
+  cmApAudioPacket_t* ip  = inPktArray;
482
+  cmApAudioPacket_t* op  = outPktArray;
483
+  unsigned           opi = 0;
484
+  unsigned           ipi = 0;
485
+  unsigned           oci = 0;
486
+  unsigned           ici = 0;
487
+
488
+  while(1)
489
+  {
490
+    if( ici == ip->chCnt)
491
+    {
492
+      ici = 0;
493
+      if( ++ipi >= inPktCnt )
494
+        break;
495
+
496
+      ip = inPktArray + ipi;
497
+    }
498
+
499
+
500
+    if( oci == op->chCnt )
501
+    {
502
+      oci = 0;
503
+      if( ++opi >= outPktCnt )
504
+        break;
505
+
506
+      ip = outPktArray + opi;
507
+    }
508
+
509
+    assert( ip->audioFramesCnt == op->audioFramesCnt );
510
+    assert( cmIsFlag(ip->flags,kInterleavedApFl)==false && cmIsFlag(ip->flags,kInterleavedApFl)==false );
511
+
512
+    cmApSample_t* ibp = ((cmApSample_t*)ip->audioBytesPtr) + (ip->audioFramesCnt*ici);
513
+    cmApSample_t* obp = ((cmApSample_t*)op->audioBytesPtr) + (op->audioFramesCnt*oci);
514
+
515
+    memcpy(obp,ibp,ip->audioFramesCnt*sizeof(cmApSample_t));
516
+
517
+    ++ici;
518
+    ++oci;
519
+  }
520
+}
521
+
522
+void      cmAudioFileDevTest( cmRpt_t* rpt )
523
+{
524
+  cmAfdH_t        afdH           = cmAfdNullHandle;
525
+  double          srate          = 44100;
526
+  unsigned        framesPerCycle = 512;
527
+  void*           cbDataPtr      = NULL;
528
+  unsigned        devIdx         = 0;
529
+  const cmChar_t* iFn            = "/home/kevin/media/audio/McGill-1/1 Audio Track.aiff";
530
+  const cmChar_t* oFn            = "/home/kevin/temp/afd0.aif";
531
+  unsigned        oBits          = 16;
532
+  unsigned        oChCnt         = 2;
533
+
534
+  if( cmAudioFileDevInitialize(&afdH,"file",devIdx,iFn,oFn,oBits,oChCnt,rpt) != kOkAfdRC )
535
+    goto errLabel;
536
+
537
+  if( cmAudioFileDevSetup(afdH,srate,framesPerCycle,_cmAfdCallback,cbDataPtr) != kOkAfdRC )
538
+    goto errLabel;
539
+
540
+  char c;
541
+  fputs("q=quit 1=start 0=stop\n",stderr);
542
+  fflush(stderr);
543
+
544
+  while((c=getchar()) != 'q')
545
+  {
546
+    switch(c)
547
+    {
548
+      case '1': cmAudioFileDevStart(afdH); break;
549
+      case '0': cmAudioFileDevStop(afdH);  break;
550
+    }
551
+    
552
+    c = 0;
553
+    fflush(stdin);
554
+  }
555
+
556
+ errLabel:
557
+  cmAudioFileDevFinalize(&afdH);
558
+
559
+  
560
+}
561
+

+ 74
- 0
cmAudioFileDev.h View File

@@ -0,0 +1,74 @@
1
+#ifndef cmAudioFileDev_h
2
+#define cmAudioFileDev_h
3
+
4
+enum
5
+{
6
+  kOkAfdRC = cmOkRC,
7
+  kAudioFileFailAfdRC,
8
+  kThreadFailAfdRC,
9
+  kRestartFailAfdRC
10
+};
11
+
12
+typedef unsigned   cmAfdRC_t;
13
+typedef cmHandle_t cmAfdH_t;
14
+
15
+extern cmAfdH_t cmAfdNullHandle;
16
+
17
+/// Initialize an audio file device.
18
+/// Called by cmApPortInitialize().
19
+cmAfdRC_t cmAudioFileDevInitialize( 
20
+  cmAfdH_t*       hp, 
21
+  const cmChar_t* label,
22
+  unsigned        devIdx,
23
+  const cmChar_t* iFn,
24
+  const cmChar_t* oFn,
25
+  unsigned        oBits,
26
+  unsigned        oChCnt,
27
+  cmRpt_t*        rpt );
28
+
29
+/// Finalize an audio file device.
30
+/// Called by cmApPortFinalize().
31
+cmAfdRC_t cmAudioFileDevFinalize( cmAfdH_t* hp );
32
+
33
+/// Return true if 'h' represents a successfully initialized audio file device.
34
+bool      cmAudioFileDevIsValid( cmAfdH_t h );
35
+
36
+
37
+/// Setup the device. This function must be called prior to cmAudioFileDevStart().
38
+cmAfdRC_t cmAudioFileDevSetup( 
39
+  cmAfdH_t          h, 
40
+  double            srate, 
41
+  unsigned          framesPerCycle, 
42
+  cmApCallbackPtr_t callbackPtr, 
43
+  void*             cbDataPtr );
44
+
45
+/// Return a device label. 
46
+const char* cmAudioFileDevLabel( cmAfdH_t h );
47
+
48
+/// Return a channel count.
49
+unsigned    cmAudioFileDevChannelCount( cmAfdH_t h, bool inputFl );
50
+
51
+/// Return the sample rate.
52
+double      cmAudioFileDevSampleRate(   cmAfdH_t h );
53
+
54
+/// Return frames per cycle.
55
+unsigned    cmAudioFileDevFramesPerCycle( cmAfdH_t h, bool inputFl );
56
+
57
+/// Rewind the input file.
58
+cmAfdRC_t cmAudioFileDevRewind( cmAfdH_t h );
59
+
60
+/// Start the file device playing/recording.
61
+cmAfdRC_t cmAudioFileDevStart( cmAfdH_t h );
62
+
63
+/// Stop the file device playing/recording.
64
+cmAfdRC_t cmAudioFileDevStop( cmAfdH_t h );
65
+
66
+/// Return true if the file device is currently started.
67
+bool      cmAudioFileDevIsStarted( cmAfdH_t h ); 
68
+
69
+/// 
70
+void      cmAudioFileDevReport( cmAfdH_t h, cmRpt_t* rpt );
71
+
72
+void      cmAudioFileDevTest( cmRpt_t* rpt );
73
+
74
+#endif

+ 483
- 0
cmAudioFileMgr.c View File

@@ -0,0 +1,483 @@
1
+#include "cmGlobal.h"
2
+#include "cmFloatTypes.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmAudioFile.h"
9
+#include "cmVectOpsTemplateMain.h"
10
+
11
+#include "cmAudioFileMgr.h"
12
+
13
+struct cmAfm_str;
14
+
15
+typedef struct
16
+{
17
+  cmSample_t* minV;  // minV[summN]
18
+  cmSample_t* maxV;  // maxV[summN]
19
+  unsigned   summN;  // lenght of minV[] and maxV[]
20
+} cmAfmSummary_t;
21
+
22
+typedef struct cmAfmFile_str
23
+{
24
+  unsigned              id;
25
+  cmAudioFileH_t        afH;
26
+  cmAudioFileInfo_t     afInfo;
27
+  unsigned              smpPerSummPt;
28
+
29
+  cmAfmSummary_t* summArray;   // summArray[ afInfo.chCnt ]
30
+  cmSample_t*     summMem;     // memory used by summArray[] vectors
31
+
32
+  struct cmAfm_str*     p;
33
+  struct cmAfmFile_str* next;
34
+  struct cmAfmFile_str* prev;
35
+} cmAfmFile_t;
36
+
37
+typedef struct cmAfm_str
38
+{
39
+  cmErr_t      err;
40
+  cmAfmFile_t* list;
41
+} cmAfm_t;
42
+
43
+
44
+cmAfmH_t     cmAfmNullHandle       = cmSTATIC_NULL_HANDLE;
45
+cmAfmFileH_t cmAfmFileNullHandle   = cmSTATIC_NULL_HANDLE;
46
+
47
+cmAfm_t* _cmAfmHandleToPtr( cmAfmH_t h )
48
+{
49
+  cmAfm_t* p = (cmAfm_t*)h.h;
50
+  assert(p!=NULL);
51
+  return p;
52
+}
53
+
54
+cmAfmFile_t* _cmAfmFileHandleToPtr( cmAfmFileH_t fh )
55
+{
56
+  cmAfmFile_t* fp = (cmAfmFile_t*)fh.h;
57
+  assert(fp!=NULL);
58
+  return fp;
59
+}
60
+
61
+
62
+cmAfmRC_t _cmAfmFileClose( cmAfmFile_t* fp )
63
+{
64
+  cmAfmRC_t rc = kOkAfmRC;
65
+
66
+  if( cmAudioFileIsValid( fp->afH ) )
67
+    if( cmAudioFileDelete( &fp->afH) != kOkAfRC )
68
+      return cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file close failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
69
+  
70
+  if( fp->next != NULL )
71
+    fp->next->prev = fp->prev;
72
+
73
+  if( fp->prev != NULL )
74
+    fp->prev->next = fp->next;
75
+
76
+  if( fp->p->list == fp )
77
+    fp->p->list = fp->next;
78
+
79
+
80
+  cmMemFree(fp->summArray);
81
+  cmMemFree(fp->summMem);
82
+  cmMemFree(fp);
83
+
84
+  return rc;
85
+}
86
+
87
+cmAfmRC_t cmAfmFileOpen( cmAfmH_t h, cmAfmFileH_t* fhp, const cmChar_t* audioFn, unsigned id, cmAudioFileInfo_t* afInfo )
88
+{
89
+  cmAfmRC_t rc;
90
+  cmRC_t afRC;
91
+
92
+  if((rc = cmAfmFileClose(fhp)) != kOkAfmRC )
93
+    return rc;
94
+
95
+  cmAfmFile_t* fp = cmMemAllocZ(cmAfmFile_t,1);
96
+  fp->p = _cmAfmHandleToPtr(h);
97
+
98
+  // open the audio file
99
+  if( cmAudioFileIsValid(fp->afH  = cmAudioFileNewOpen(audioFn, &fp->afInfo, &afRC, fp->p->err.rpt  )) == false )
100
+  {
101
+    rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"The audio file '%s' could not be opened.",cmStringNullGuard(audioFn));
102
+    goto errLabel;
103
+  }
104
+
105
+  // prepend the new file to the mgr's file list
106
+  if( fp->p->list != NULL )
107
+    fp->p->list->prev = fp;  
108
+         
109
+  fp->next          = fp->p->list;
110
+  fp->p->list       = fp;
111
+
112
+  fp->id            = id;
113
+
114
+  fhp->h = fp;
115
+
116
+  if( afInfo != NULL )
117
+    *afInfo = fp->afInfo;
118
+  
119
+
120
+ errLabel:
121
+  if( rc != kOkAfmRC )
122
+    _cmAfmFileClose(fp);
123
+
124
+  return rc;
125
+}
126
+
127
+cmAfmRC_t cmAfmFileClose( cmAfmFileH_t* fhp )
128
+{
129
+  cmAfmRC_t rc = kOkAfmRC;
130
+  if( fhp==NULL || cmAfmFileIsValid(*fhp)==false)
131
+    return rc;
132
+
133
+  cmAfmFile_t* fp = _cmAfmFileHandleToPtr( *fhp );
134
+  if((rc = _cmAfmFileClose(fp)) != kOkAfmRC )
135
+    return rc;
136
+
137
+  fhp->h = NULL;
138
+
139
+  return rc;
140
+}
141
+
142
+bool      cmAfmFileIsValid( cmAfmFileH_t fh )
143
+{ return fh.h != NULL; }
144
+
145
+
146
+unsigned cmAfmFileId( cmAfmFileH_t fh )
147
+{
148
+  cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
149
+  return fp->id;
150
+}
151
+
152
+cmAudioFileH_t cmAfmFileHandle( cmAfmFileH_t fh )
153
+{
154
+  cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
155
+  return fp->afH;
156
+}
157
+  
158
+const cmAudioFileInfo_t* cmAfmFileInfo( cmAfmFileH_t fh )
159
+{
160
+  cmAfmFile_t* fp = _cmAfmFileHandleToPtr( fh );
161
+  return &fp->afInfo;
162
+}
163
+
164
+cmAfmRC_t cmAfmFileSummarize( cmAfmFileH_t fh, unsigned smpPerSummPt )
165
+{
166
+  cmAfmFile_t* fp    = _cmAfmFileHandleToPtr(fh);
167
+  cmAfmRC_t    rc    = kOkAfmRC;
168
+  unsigned     chCnt = fp->afInfo.chCnt;
169
+  
170
+  // summary points per channel per vector
171
+  unsigned summN = (unsigned)ceil((double)fp->afInfo.frameCnt / smpPerSummPt );
172
+
173
+  // total summary points in all channels and vectors
174
+  unsigned n  = chCnt*2*summN;
175
+
176
+  // Calc the number of summary points per audio file read
177
+  unsigned ptsPerRd = cmMax(1,cmMax(smpPerSummPt,8192) / smpPerSummPt);  
178
+
179
+  // Calc the number samples per audio file read as an integer multiple of ptsPerRd.
180
+  unsigned frmCnt   = ptsPerRd * smpPerSummPt;                       
181
+
182
+  unsigned    actualFrmCnt = 0;
183
+  cmSample_t* chBuf[ chCnt ];
184
+  cmSample_t  buf[ frmCnt * chCnt ];
185
+  unsigned    i;
186
+
187
+  // allocate the summary record array
188
+  if( fp->summArray == NULL )
189
+    fp->summArray = cmMemAllocZ( cmAfmSummary_t, chCnt );
190
+
191
+  // allocate the summary vector memory for all channels
192
+  fp->summMem      = cmMemResizeZ( cmSample_t, fp->summMem, n);  
193
+  fp->smpPerSummPt = smpPerSummPt;
194
+
195
+  // setup the summary record array and audio file read buffer
196
+  for(i=0; i<chCnt; ++i)
197
+  {
198
+    // assign memory to the summary vectors
199
+    fp->summArray[i].minV = fp->summMem + i * summN * 2;
200
+    fp->summArray[i].maxV = fp->summArray[i].minV + summN;
201
+    fp->summArray[i].summN   = summN;
202
+    
203
+    // setup the audio file reading channel buffer
204
+    chBuf[i] = buf + (i*frmCnt);
205
+  }
206
+
207
+  // read the entire file and calculate the summary vectors
208
+  i = 0;
209
+  do
210
+  {
211
+    unsigned chIdx = 0;
212
+    unsigned j,k;
213
+    
214
+    // read the next frmCnt samples from the 
215
+    if( cmAudioFileReadSample(fp->afH, frmCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
216
+    {
217
+      rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
218
+      goto errLabel;
219
+    }
220
+
221
+    // for each summary point 
222
+    for(k=0; k<actualFrmCnt && i<summN; k+=smpPerSummPt,++i)
223
+    {
224
+      // cnt of samples in this summary report
225
+      unsigned m = cmMin(smpPerSummPt,actualFrmCnt-k);
226
+
227
+      // for each channel
228
+      for(j=0; j<chCnt; ++j)
229
+      {
230
+        fp->summArray[j].minV[i] = cmVOS_Min(chBuf[j]+k,m,1);
231
+        fp->summArray[j].maxV[i] = cmVOS_Max(chBuf[j]+k,m,1);
232
+      }
233
+    }
234
+  }while( i<summN && actualFrmCnt==frmCnt );
235
+  
236
+ errLabel:
237
+  return rc;
238
+}
239
+
240
+// Downsample the summary data to produce the output.
241
+// There must be  1 or more summary points per output point.
242
+cmAfmRC_t  _cmAfmFileGetDownSummary( 
243
+  cmAfmFile_t* fp, 
244
+  unsigned     chIdx, 
245
+  unsigned     begSmpIdx, 
246
+  unsigned     smpCnt, 
247
+  cmSample_t*  minV, 
248
+  cmSample_t*  maxV, 
249
+  unsigned     outCnt )
250
+{
251
+  assert( smpCnt >= outCnt );
252
+  
253
+  double smpPerOut  = (double)smpCnt/outCnt;
254
+  double summPerOut = smpPerOut/fp->smpPerSummPt;
255
+
256
+  unsigned i;
257
+
258
+  for(i=0; i<outCnt; ++i)
259
+  {
260
+    double   fsbi = (begSmpIdx + (i*smpPerOut)) / fp->smpPerSummPt; // starting summary pt index
261
+    double   fsei = fsbi + summPerOut;                              // endiing summary pt index
262
+    unsigned si   = (unsigned)floor(fsbi);                          
263
+    unsigned sn   = (unsigned)floor(fsei - fsbi + 1);
264
+
265
+    if( si > fp->summArray[chIdx].summN )
266
+    {
267
+      minV[i] = 0;
268
+      maxV[i] = 0;
269
+    } 
270
+    else
271
+    {
272
+      if( si + sn > fp->summArray[chIdx].summN )
273
+        sn = fp->summArray[chIdx].summN - si;
274
+
275
+      if( sn == 0 )
276
+      {
277
+        minV[i] = 0;
278
+        maxV[i] = 0;
279
+      }
280
+      else
281
+      {
282
+        minV[i] = cmVOS_Min(fp->summArray[chIdx].minV+si,sn,1);
283
+        maxV[i] = cmVOS_Max(fp->summArray[chIdx].maxV+si,sn,1);
284
+      }
285
+    }
286
+  }
287
+  
288
+  return kOkAfmRC;
289
+}
290
+
291
+// Downsample the audio data to produce the output.
292
+cmAfmRC_t  _cmAfmFileGetDownAudio( 
293
+  cmAfmFile_t* fp, 
294
+  unsigned     chIdx, 
295
+  unsigned     begSmpIdx, 
296
+  unsigned     smpCnt, 
297
+  cmSample_t*  minV, 
298
+  cmSample_t*  maxV, 
299
+  unsigned     outCnt )
300
+{
301
+  assert( smpCnt >= outCnt );
302
+
303
+  cmAfmRC_t  rc           = kOkAfmRC;
304
+  unsigned   actualFrmCnt = 0;
305
+  unsigned   chCnt        = 1;
306
+  unsigned   i;
307
+  cmSample_t buf[ smpCnt ];
308
+  cmSample_t* chBuf[] = { buf };
309
+
310
+  // seek to the read location
311
+  if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC )
312
+  {
313
+    rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
314
+    goto errLabel;
315
+  }
316
+
317
+  // read 'smpCnt' samples into chBuf[][]
318
+  if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
319
+  {
320
+    rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH)));
321
+    goto errLabel;
322
+  }
323
+
324
+  
325
+  double smpPerOut  = (double)smpCnt/outCnt;
326
+
327
+  for(i=0; i<outCnt; ++i)
328
+  {
329
+    double   fsbi = i*smpPerOut;
330
+    double   fsei = fsbi + smpPerOut;
331
+    unsigned si   = (unsigned)floor(fsbi);
332
+    unsigned sn   = (unsigned)floor(fsei - fsbi + 1);
333
+
334
+    if( si > smpCnt )
335
+    {
336
+      minV[i] = 0;
337
+      maxV[i] = 0;
338
+    } 
339
+
340
+    if( si + sn > smpCnt )
341
+      sn = smpCnt - si;
342
+
343
+    minV[i] = cmVOS_Min(chBuf[chIdx]+si,sn,1);
344
+    maxV[i] = cmVOS_Max(chBuf[chIdx]+si,sn,1);
345
+  }
346
+  
347
+ errLabel:
348
+  return rc;
349
+}
350
+
351
+
352
+// If there is one or less summary points per output
353
+cmAfmRC_t  _cmAfmFileGetUpSummary( 
354
+  cmAfmFile_t* fp, 
355
+  unsigned     chIdx, 
356
+  unsigned     begSmpIdx, 
357
+  unsigned     smpCnt, 
358
+  cmSample_t*  minV, 
359
+  cmSample_t*  maxV, 
360
+  unsigned     outCnt )
361
+{
362
+  assert( outCnt >= smpCnt );
363
+  cmAfmRC_t  rc           = kOkAfmRC;
364
+  unsigned   actualFrmCnt = 0;
365
+  unsigned   chCnt        = 1;
366
+  unsigned   i;
367
+  cmSample_t buf[ smpCnt ];
368
+  cmSample_t* chBuf[] = { buf };
369
+
370
+  if( cmAudioFileSeek( fp->afH, begSmpIdx ) != kOkAfRC )
371
+  {
372
+    rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file seek failed on '%s'.",cmStringNullGuard(cmAudioFileName(fp->afH)));
373
+    goto errLabel;
374
+  }
375
+
376
+  if( cmAudioFileReadSample(fp->afH, smpCnt, chIdx, chCnt, chBuf, &actualFrmCnt ) != kOkAfRC )
377
+  {
378
+    rc = cmErrMsg(&fp->p->err,kAudioFileFailAfmRC,"Audio file read failed on '%s' durnig upsample.",cmStringNullGuard(cmAudioFileName(fp->afH)));
379
+    goto errLabel;
380
+  }
381
+
382
+  
383
+  for(i=0; i<outCnt; ++i)
384
+  {
385
+    unsigned   si = cmMin(smpCnt-1, (unsigned)floor(i * smpCnt / outCnt));
386
+    cmSample_t v  = buf[si];
387
+    minV[i]       = v;
388
+    maxV[i]       = v;
389
+  }
390
+
391
+ errLabel:
392
+  return rc;
393
+
394
+}
395
+
396
+
397
+cmAfmRC_t cmAfmFileGetSummary( cmAfmFileH_t fh, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt )
398
+{
399
+  cmAfmRC_t    rc = kOkAfmRC;
400
+  cmAfmFile_t* fp = _cmAfmFileHandleToPtr(fh);
401
+  double       maxHiResDurSecs = 20.0;
402
+
403
+  if( smpCnt <= outCnt )
404
+    rc = _cmAfmFileGetUpSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
405
+  else
406
+  {
407
+    if( smpCnt/fp->afInfo.srate < maxHiResDurSecs )
408
+      rc = _cmAfmFileGetDownAudio( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
409
+    else
410
+      rc = _cmAfmFileGetDownSummary( fp, chIdx, begSmpIdx, smpCnt, minV, maxV, outCnt );
411
+  }
412
+
413
+  return rc;
414
+}
415
+
416
+//----------------------------------------------------------------------------
417
+// Audio File Manager
418
+//----------------------------------------------------------------------------  
419
+cmAfmRC_t _cmAfmDestroy( cmAfm_t* p )
420
+{
421
+  cmAfmRC_t rc = kOkAfmRC;
422
+
423
+  while( p->list != NULL )
424
+  {
425
+    if((rc = _cmAfmFileClose(p->list)) != kOkAfmRC )
426
+      goto errLabel;
427
+  }
428
+
429
+  cmMemFree(p);
430
+
431
+ errLabel:
432
+  return rc;
433
+}
434
+
435
+cmAfmRC_t cmAfmCreate( cmCtx_t* ctx, cmAfmH_t* hp )
436
+{
437
+  cmAfmRC_t rc;
438
+  if((rc = cmAfmDestroy(hp)) != kOkAfmRC )
439
+    return rc;
440
+
441
+  cmAfm_t* p = cmMemAllocZ(cmAfm_t,1);
442
+  cmErrSetup(&p->err,&ctx->rpt,"Audio File Mgr");
443
+
444
+  hp->h = p;
445
+
446
+  return rc;
447
+}
448
+
449
+cmAfmRC_t cmAfmDestroy( cmAfmH_t* hp )
450
+{
451
+  cmAfmRC_t rc = kOkAfmRC;
452
+
453
+  if( hp==NULL || cmAfmIsValid(*hp)==false)
454
+    return rc;
455
+
456
+  cmAfm_t* p = _cmAfmHandleToPtr(*hp);
457
+
458
+  if((rc = _cmAfmDestroy(p)) != kOkAfmRC )
459
+    return rc;
460
+
461
+  hp->h = NULL;
462
+
463
+  return rc;
464
+}
465
+
466
+bool cmAfmIsValid( cmAfmH_t h )
467
+{ return h.h != NULL; }
468
+
469
+cmAfmFileH_t cmAfmIdToHandle( cmAfmH_t h, unsigned fileId )
470
+{
471
+  cmAfm_t*     p  = _cmAfmHandleToPtr(h);
472
+  cmAfmFile_t* fp = p->list;
473
+  cmAfmFileH_t fh = cmAfmFileNullHandle;
474
+
475
+  for(; fp!=NULL; fp=fp->next)
476
+    if( fp->id == fileId )
477
+    {
478
+      fh.h = fp;
479
+      break;
480
+    }
481
+
482
+  return fh;
483
+}

+ 67
- 0
cmAudioFileMgr.h View File

@@ -0,0 +1,67 @@
1
+#ifndef cmAudioFileMgr_h
2
+#define cmAudioFileMgr_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+  enum
8
+  {
9
+    kOkAfmRC = cmOkRC,
10
+    kAudioFileFailAfmRC
11
+  };
12
+
13
+  typedef cmHandle_t cmAfmH_t;
14
+  typedef cmHandle_t cmAfmFileH_t;
15
+  typedef cmRC_t     cmAfmRC_t;
16
+
17
+  extern cmAfmH_t     cmAfmNullHandle;
18
+  extern cmAfmFileH_t cmAfmFileNullHandle;
19
+
20
+
21
+  //----------------------------------------------------------------------------
22
+  // Audio Files 
23
+  //----------------------------------------------------------------------------  
24
+  cmAfmRC_t cmAfmFileOpen( cmAfmH_t h, cmAfmFileH_t* fhp, const cmChar_t* audioFn, unsigned fileId, cmAudioFileInfo_t* afInfo );
25
+  cmAfmRC_t cmAfmFileClose( cmAfmFileH_t* fhp );
26
+  bool      cmAfmFileIsValid( cmAfmFileH_t fh );
27
+  
28
+  // Return the application supplied file id associated with this file.
29
+  // This value was set by the 'fileId' argument to cmAfmFileOpen().
30
+  unsigned cmAfmFileId( cmAfmFileH_t fh );
31
+
32
+  // Return the file handle associated with this file.
33
+  cmAudioFileH_t cmAfmFileHandle( cmAfmFileH_t fh );
34
+  
35
+  // Return a pointer to the information record associated with this file.
36
+  const cmAudioFileInfo_t* cmAfmFileInfo( cmAfmFileH_t fh );
37
+
38
+  // Summarize min and max values of the downSampled audio file.
39
+  // The summary is kept in an internal cache which is used to 
40
+  // optimize the time required to complete later calls to cmAfmFileGetSummary(). 
41
+  // 'downSampleFactor' is the count of samples per summary point.
42
+  cmAfmRC_t cmAfmFileSummarize( cmAfmFileH_t fh, unsigned downSampleFactor );
43
+
44
+  // Return a summary of the samples in the range audio file range
45
+  // begSmpIdx:begSmpIdx+smpCnt-1 reduced or expanded to 'outCnt' values
46
+  // in minV[outCnt] and maxV[outCnt].
47
+  // If 'outCnt' is equal to 'smpCnt' then the actual sample values are returned. 
48
+  cmAfmRC_t cmAfmFileGetSummary( cmAfmFileH_t fh, unsigned chIdx, unsigned begSmpIdx, unsigned smpCnt, cmSample_t* minV, cmSample_t* maxV, unsigned outCnt );
49
+
50
+
51
+  //----------------------------------------------------------------------------
52
+  // Audio File Manager
53
+  //----------------------------------------------------------------------------  
54
+  cmAfmRC_t    cmAfmCreate( cmCtx_t* ctx, cmAfmH_t* hp );
55
+  cmAfmRC_t    cmAfmDestroy( cmAfmH_t* hp );
56
+  bool         cmAfmIsValid( cmAfmH_t h );
57
+  cmAfmFileH_t cmAfmIdToHandle( cmAfmH_t h, unsigned fileId );
58
+
59
+    
60
+
61
+
62
+#ifdef __cplusplus
63
+}
64
+#endif
65
+
66
+
67
+#endif

+ 355
- 0
cmAudioNrtDev.c View File

@@ -0,0 +1,355 @@
1
+#include "cmGlobal.h"
2
+#include "cmFloatTypes.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmAudioPort.h"
9
+#include "cmAudioNrtDev.h"
10
+#include "cmThread.h" 
11
+
12
+enum
13
+{
14
+  kStartedApNrtFl = 0x01,
15
+  kInStateApNrtFl = 0x02
16
+};
17
+
18
+typedef struct
19
+{
20
+  unsigned phase;
21
+  bool     implFl;
22
+  double   gain;
23
+} cmApNrtCh_t;
24
+
25
+typedef struct cmApNrtDev_str
26
+{
27
+  unsigned               flags;
28
+  unsigned               devIdx; // nrt device index
29
+  unsigned               baseApDevIdx; // global audio device index for first nrt device
30
+  cmChar_t*              label;         
31
+  unsigned               iChCnt;
32
+  unsigned               oChCnt;
33
+  double                 srate;
34
+  unsigned               fpc;   
35
+  cmThreadH_t            thH;
36
+  cmApCallbackPtr_t      cbPtr;
37
+  void*                  cbArg;
38
+  unsigned               cbPeriodMs;
39
+  struct cmApNrtDev_str* link;
40
+  unsigned               curTimeSmp;
41
+  cmApNrtCh_t*           iChArray;
42
+  cmApNrtCh_t*           oChArray;
43
+} cmApNrtDev_t;
44
+
45
+typedef struct
46
+{
47
+  cmErr_t       err;
48
+  unsigned      devCnt;
49
+  cmApNrtDev_t* devs;
50
+  unsigned      baseApDevIdx;
51
+} cmApNrt_t;
52
+
53
+cmApNrt_t* _cmNrt = NULL;
54
+
55
+cmApNrtDev_t* _cmApNrtIndexToDev( unsigned idx )
56
+{
57
+  cmApNrtDev_t* dp = _cmNrt->devs;
58
+  unsigned i;
59
+  for(i=0; dp!=NULL && i<idx; ++i)
60
+    dp = dp->link;
61
+
62
+  assert( dp != NULL );
63
+  return dp;
64
+}
65
+
66
+void cmApNrtGenImpulseCh( cmApNrtDev_t* dp, unsigned chIdx, cmSample_t* buf, unsigned chCnt, unsigned frmCnt )
67
+{
68
+  double   periodMs  = 500;    // impulse period
69
+  double   durMs     = 50;      // impulse length
70
+  double   loDb      = -90.0;
71
+  double   hiDb      = -20.0;
72
+  double   loLin     = pow(10.0,loDb/20.0);
73
+  double   hiLin     = pow(10.0,hiDb/20.0);
74
+  unsigned periodSmp = (unsigned)(periodMs * dp->srate / 1000.0);
75
+  unsigned durSmp    = (unsigned)(   durMs * dp->srate / 1000.0);
76
+  
77
+  assert( chIdx < chCnt );
78
+
79
+  cmApNrtCh_t* cp = dp->iChArray + chIdx;
80
+  cmSample_t*  sp = buf + chIdx;
81
+
82
+  unsigned i;
83
+  for(i=0; i<frmCnt; ++i, sp+=chCnt )
84
+  {
85
+    unsigned limit = periodSmp;
86
+    double   gain  = loLin;
87
+
88
+    cp->phase += 1;
89
+
90
+    if( cp->implFl )
91
+    {
92
+      gain  = cp->gain;
93
+      limit = durSmp;
94
+    }
95
+
96
+    *sp = (gain * 2.0 * rand()/RAND_MAX) - 1.0;
97
+
98
+    if( cp->phase > limit )
99
+    {
100
+      cp->phase  = 0;
101
+      cp->implFl = !cp->implFl;
102
+
103
+      if( cp->implFl )
104
+        cp->gain = loLin + (hiLin - loLin) * rand()/RAND_MAX ;
105
+      
106
+    }
107
+
108
+  }
109
+
110
+}
111
+
112
+void cmApNrtGenImpulse( cmApNrtDev_t* dp, cmSample_t* buf, unsigned chCnt, unsigned frmCnt )
113
+{
114
+  unsigned i=0;
115
+  for(; i<chCnt; ++i)
116
+    cmApNrtGenImpulseCh(dp,i,buf,chCnt,frmCnt);
117
+}
118
+
119
+void cmApNrtGenSamples( cmApNrtDev_t* dp, cmSample_t* buf, unsigned chCnt, unsigned frmCnt )
120
+{
121
+  memset(buf,0,chCnt*frmCnt*sizeof(cmSample_t));
122
+
123
+  cmApNrtGenImpulse(dp,buf,chCnt,frmCnt);
124
+
125
+  dp->curTimeSmp += frmCnt;
126
+} 
127
+
128
+// return 'false' to terminate otherwise return 'true'.
129
+bool cmApNrtThreadFunc(void* param)
130
+{
131
+  cmApNrtDev_t* dp = (cmApNrtDev_t*)param;
132
+
133
+  usleep( dp->cbPeriodMs * 1000 );
134
+
135
+
136
+  cmApAudioPacket_t pkt;
137
+  bool              inFl = cmIsFlag(dp->flags,kInStateApNrtFl);  
138
+
139
+  pkt.devIdx         = dp->devIdx + dp->baseApDevIdx;
140
+  pkt.begChIdx       = 0;
141
+  pkt.audioFramesCnt = dp->fpc;
142
+  pkt.bitsPerSample  = 32;
143
+  pkt.flags          = kInterleavedApFl | kFloatApFl;
144
+  pkt.userCbPtr      = dp->cbArg;
145
+
146
+  if( inFl )
147
+  {
148
+    unsigned   bn = dp->iChCnt * dp->fpc;
149
+    cmSample_t b[ bn ];
150
+
151
+    pkt.chCnt         = dp->iChCnt;
152
+    pkt.audioBytesPtr = b;
153
+
154
+    cmApNrtGenSamples(dp,b,dp->iChCnt,dp->fpc);
155
+  
156
+    dp->cbPtr(&pkt,1,NULL,0 );  // send the samples to the application
157
+  }
158
+  else
159
+  {
160
+    unsigned   bn = dp->oChCnt * dp->fpc;
161
+    cmSample_t b[ bn ];
162
+
163
+    pkt.chCnt         = dp->oChCnt;
164
+    pkt.audioBytesPtr = b;
165
+
166
+    dp->cbPtr(NULL,0,&pkt,1 );  // recv the samples from the application
167
+  }
168
+  
169
+  dp->flags = cmTogFlag(dp->flags,kInStateApNrtFl);
170
+
171
+  return true;
172
+}
173
+
174
+cmApRC_t cmApNrtAllocate( cmRpt_t* rpt )
175
+{
176
+  if( _cmNrt != NULL )
177
+    cmApNrtFree();
178
+
179
+  _cmNrt = cmMemAllocZ(cmApNrt_t,1);
180
+
181
+  cmErrSetup(&_cmNrt->err,rpt,"cmAudioNrtDev");
182
+  _cmNrt->devCnt       = 0;
183
+  _cmNrt->devs         = NULL;
184
+  _cmNrt->baseApDevIdx = 0;
185
+  return kOkApRC;
186
+}
187
+
188
+cmApRC_t cmApNrtFree()
189
+{
190
+  cmApRC_t rc = kOkApRC;
191
+  cmApNrtDev_t* dp = _cmNrt->devs;
192
+  while( dp != NULL )
193
+  {
194
+    cmApNrtDev_t* np = dp->link;
195
+
196
+    if( cmThreadIsValid(dp->thH) ) 
197
+      if( cmThreadDestroy(&dp->thH) != kOkThRC )
198
+        rc = cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread destroy failed.");
199
+
200
+    cmMemFree(dp->label);
201
+    cmMemFree(dp->iChArray);
202
+    cmMemFree(dp->oChArray);
203
+    cmMemFree(dp);
204
+    dp = np;
205
+  }
206
+
207
+  if( rc == kOkApRC )
208
+  {
209
+    cmMemPtrFree(&_cmNrt);
210
+  }
211
+
212
+  return rc;
213
+}
214
+
215
+cmApRC_t cmApNrtCreateDevice( 
216
+  const cmChar_t* label,
217
+  double          srate,
218
+  unsigned        iChCnt,
219
+  unsigned        oChCnt,
220
+  unsigned        cbPeriodMs )
221
+{
222
+  cmApRC_t rc = kOkApRC;
223
+
224
+  cmApNrtDev_t* dp = cmMemAllocZ(cmApNrtDev_t,1);
225
+
226
+  dp->devIdx       = _cmNrt->devCnt;
227
+  dp->baseApDevIdx = _cmNrt->baseApDevIdx;
228
+  dp->label        = cmMemAllocStr(label);
229
+  dp->iChCnt       = iChCnt;
230
+  dp->oChCnt       = oChCnt;
231
+  dp->srate        = srate;
232
+  dp->fpc          = 0;
233
+  dp->cbPeriodMs   = cbPeriodMs;
234
+  dp->cbPtr        = NULL;
235
+  dp->cbArg        = NULL;
236
+  dp->link         = NULL;
237
+  dp->iChArray    = iChCnt==0 ? NULL : cmMemAllocZ(cmApNrtCh_t,iChCnt);
238
+  dp->oChArray    = oChCnt==0 ? NULL : cmMemAllocZ(cmApNrtCh_t,oChCnt);
239
+
240
+  // attach the new recd to the end of the list
241
+  cmApNrtDev_t* np = _cmNrt->devs;
242
+  while( np != NULL && np->link != NULL )
243
+    np = np->link;
244
+
245
+  if( np == NULL )
246
+    _cmNrt->devs = dp;
247
+  else
248
+    np->link = dp;
249
+
250
+  if( cmThreadCreate( &dp->thH, cmApNrtThreadFunc, dp, _cmNrt->err.rpt ) != kOkThRC )
251
+    rc = cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread create failed.");
252
+
253
+  ++_cmNrt->devCnt;
254
+  return rc;
255
+}  
256
+
257
+cmApRC_t      cmApNrtInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
258
+{
259
+  // set the baseApDevIdx for each device 
260
+  cmApNrtDev_t* dp = _cmNrt->devs;
261
+  for(; dp!=NULL; dp=dp->link)
262
+    dp->baseApDevIdx = baseApDevIdx;
263
+
264
+  // store the baseApDevIdx for any devices that may be created after initialization
265
+  _cmNrt->baseApDevIdx = baseApDevIdx;
266
+
267
+  return kOkApRC;
268
+}
269
+
270
+cmApRC_t      cmApNrtFinalize()
271
+{
272
+  return kOkApRC;
273
+}
274
+
275
+unsigned      cmApNrtDeviceCount()
276
+{ return _cmNrt==NULL ? 0 : _cmNrt->devCnt; }
277
+
278
+const char*   cmApNrtDeviceLabel(          unsigned devIdx )
279
+{
280
+  assert( devIdx < _cmNrt->devCnt );
281
+  const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
282
+  return dp->label;
283
+}
284
+
285
+unsigned      cmApNrtDeviceChannelCount(   unsigned devIdx, bool inputFl )
286
+{
287
+  assert( devIdx < _cmNrt->devCnt );
288
+  const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
289
+  return inputFl ? dp->iChCnt : dp->oChCnt;
290
+}
291
+
292
+double        cmApNrtDeviceSampleRate(     unsigned devIdx )
293
+{
294
+  assert( devIdx < _cmNrt->devCnt );
295
+  const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
296
+  return dp->srate;
297
+}
298
+
299
+unsigned      cmApNrtDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
300
+{
301
+  assert( devIdx < _cmNrt->devCnt );
302
+  const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
303
+  return dp->fpc;
304
+}
305
+
306
+cmApRC_t      cmApNrtDeviceSetup(          
307
+  unsigned          devIdx, 
308
+  double            srate, 
309
+  unsigned          framesPerCycle, 
310
+  cmApCallbackPtr_t callbackPtr,
311
+  void*             userCbPtr )
312
+{
313
+  assert( devIdx < _cmNrt->devCnt );
314
+  cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
315
+  dp->srate        = srate;
316
+  dp->fpc          = framesPerCycle;
317
+  dp->cbPtr        = callbackPtr;
318
+  dp->cbArg        = userCbPtr;
319
+  return kOkApRC;
320
+}
321
+
322
+cmApRC_t      cmApNrtDeviceStart( unsigned devIdx )
323
+{
324
+  assert( devIdx < _cmNrt->devCnt );
325
+  cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
326
+
327
+  dp->curTimeSmp = 0;
328
+
329
+  if( cmThreadPause( dp->thH, 0 ) != kOkThRC )
330
+    return cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread start failed.");
331
+  
332
+  dp->flags = cmSetFlag(dp->flags,kStartedApNrtFl);
333
+  
334
+  return kOkApRC;
335
+}
336
+
337
+cmApRC_t      cmApNrtDeviceStop(  unsigned devIdx )
338
+{
339
+  assert( devIdx < _cmNrt->devCnt );
340
+  cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
341
+
342
+  if( cmThreadPause( dp->thH, kPauseThFl ) != kOkThRC )
343
+    return cmErrMsg(&_cmNrt->err,kThreadFailApRC,"Thread pause failed.");
344
+
345
+  dp->flags = cmClrFlag(dp->flags,kStartedApNrtFl);
346
+
347
+  return kOkApRC;
348
+}
349
+
350
+bool          cmApNrtDeviceIsStarted( unsigned devIdx )
351
+{ 
352
+  assert( devIdx < _cmNrt->devCnt );
353
+  const cmApNrtDev_t* dp = _cmApNrtIndexToDev(devIdx);
354
+  return cmIsFlag(dp->flags,kStartedApNrtFl); 
355
+}

+ 65
- 0
cmAudioNrtDev.h View File

@@ -0,0 +1,65 @@
1
+#ifndef cmAudioNrtDev_h
2
+#define cmAudioNrtDev_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  cmApRC_t cmApNrtAllocate( cmRpt_t* rpt );
9
+
10
+  cmApRC_t cmApNrtFree();
11
+
12
+  cmApRC_t cmApNrtCreateDevice( 
13
+    const cmChar_t* label,
14
+    double          srate,
15
+    unsigned        iChCnt,
16
+    unsigned        oChCnt,
17
+    unsigned        cbPeriodMs );
18
+  
19
+
20
+  /// Setup the audio port management object for this machine.
21
+  cmApRC_t      cmApNrtInitialize( cmRpt_t* rpt, unsigned baseApDevIdx );
22
+
23
+  /// Stop all audio devices and release any resources held 
24
+  /// by the audio port management object.
25
+  cmApRC_t      cmApNrtFinalize();
26
+
27
+  /// Return the count of audio devices attached to this machine.
28
+  unsigned      cmApNrtDeviceCount();
29
+
30
+  /// Get a textual description of the device at index 'devIdx'.
31
+  const char*   cmApNrtDeviceLabel(          unsigned devIdx );
32
+
33
+  /// Get the count of audio input or output channesl on device at index 'devIdx'.
34
+  unsigned      cmApNrtDeviceChannelCount(   unsigned devIdx, bool inputFl );
35
+
36
+  /// Get the current sample rate of a device.  Note that if the device has both
37
+  /// input and output capability then the sample rate is the same for both.
38
+  double        cmApNrtDeviceSampleRate(     unsigned devIdx );
39
+
40
+  /// Get the count of samples per callback for the input or output for this device.
41
+  unsigned      cmApNrtDeviceFramesPerCycle( unsigned devIdx, bool inputFl );
42
+
43
+  /// Configure a device.  
44
+  cmApRC_t      cmApNrtDeviceSetup(          
45
+    unsigned          devIdx, 
46
+    double            srate, 
47
+    unsigned          framesPerCycle, 
48
+    cmApCallbackPtr_t callbackPtr,
49
+    void*             userCbPtr );
50
+
51
+  /// Start a device. Note that the callback may be made prior to this function returning.
52
+  cmApRC_t      cmApNrtDeviceStart( unsigned devIdx );
53
+
54
+  /// Stop a device.
55
+  cmApRC_t      cmApNrtDeviceStop(  unsigned devIdx );
56
+
57
+  /// Return true if the device is currently started.
58
+  bool          cmApNrtDeviceIsStarted( unsigned devIdx );
59
+
60
+
61
+#ifdef __cplusplus
62
+}
63
+#endif
64
+
65
+#endif

+ 799
- 0
cmAudioPort.c View File

@@ -0,0 +1,799 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmAudioPort.h"
9
+#include "cmApBuf.h"  // only needed for cmApBufTest().
10
+#include "cmAudioPortFile.h"
11
+#include "cmAudioAggDev.h"
12
+#include "cmAudioNrtDev.h"
13
+
14
+#ifdef OS_LINUX
15
+#include "linux/cmAudioPortAlsa.h"
16
+#endif
17
+
18
+#ifdef OS_OSX
19
+#include "osx/cmAudioPortOsx.h"
20
+#endif
21
+
22
+
23
+typedef struct
24
+{
25
+  unsigned      begDevIdx;
26
+  unsigned      endDevIdx;
27
+
28
+  cmApRC_t      (*initialize)( cmRpt_t* rpt, unsigned baseApDevIdx );
29
+  cmApRC_t      (*finalize)();
30
+  cmApRC_t      (*deviceCount)();
31
+  const char*   (*deviceLabel)(          unsigned devIdx );
32
+  unsigned      (*deviceChannelCount)(   unsigned devIdx, bool inputFl );
33
+  double        (*deviceSampleRate)(    unsigned devIdx );
34
+  unsigned      (*deviceFramesPerCycle)( unsigned devIdx, bool inputFl );
35
+  cmApRC_t      (*deviceSetup)( unsigned devIdx, double sr, unsigned frmPerCycle, cmApCallbackPtr_t cb, void* cbData );
36
+  cmApRC_t      (*deviceStart)( unsigned devIdx );
37
+  cmApRC_t      (*deviceStop)(  unsigned devIdx );
38
+  bool          (*deviceIsStarted)( unsigned devIdx );
39
+
40
+} cmApDriver_t;
41
+
42
+typedef struct
43
+{
44
+  cmErr_t        err;
45
+  cmApDriver_t*  drvArray;
46
+  unsigned       drvCnt;
47
+  unsigned       devCnt;
48
+} cmAp_t;
49
+
50
+cmAp_t* _ap = NULL;
51
+
52
+cmApRC_t      _cmApIndexToDev( unsigned devIdx, cmApDriver_t** drvPtrPtr, unsigned* devIdxPtr )
53
+{
54
+  unsigned i;
55
+  for(i=0; i<_ap->drvCnt; ++i)
56
+    if( _ap->drvArray[i].begDevIdx != cmInvalidIdx )
57
+      if( (_ap->drvArray[i].begDevIdx <= devIdx) && (devIdx <= _ap->drvArray[i].endDevIdx) )
58
+      {
59
+        *drvPtrPtr = _ap->drvArray + i;
60
+        *devIdxPtr = devIdx - _ap->drvArray[i].begDevIdx;
61
+        return kOkApRC;
62
+      }
63
+  
64
+  return cmErrMsg(&_ap->err,kInvalidDevIdApRC,"The audio port device index %i is not valid.",devIdx);
65
+}
66
+
67
+cmApRC_t      cmApInitialize( cmRpt_t* rpt )
68
+{
69
+  cmApRC_t rc = kOkApRC;
70
+  if((rc = cmApFinalize()) != kOkApRC )
71
+    return rc;
72
+
73
+  _ap = cmMemAllocZ(cmAp_t,1);
74
+  cmErrSetup(&_ap->err,rpt,"Audio Port Driver");
75
+
76
+  _ap->drvCnt = 4;
77
+  _ap->drvArray = cmMemAllocZ(cmApDriver_t,_ap->drvCnt);
78
+  cmApDriver_t* dp = _ap->drvArray;
79
+  
80
+#ifdef OS_OSX
81
+  dp->initialize           = cmApOsxInitialize;
82
+  dp->finalize             = cmApOsxFinalize;
83
+  dp->deviceCount          = cmApOsxDeviceCount;
84
+  dp->deviceLabel          = cmApOsxDeviceLabel;
85
+  dp->deviceChannelCount   = cmApOsxDeviceChannelCount;
86
+  dp->deviceSampleRate     = cmApOsxDeviceSampleRate;
87
+  dp->deviceFramesPerCycle = cmApOsxDeviceFramesPerCycle;
88
+  dp->deviceSetup          = cmApOsxDeviceSetup;
89
+  dp->deviceStart          = cmApOsxDeviceStart;
90
+  dp->deviceStop           = cmApOsxDeviceStop;
91
+  dp->deviceIsStarted      = cmApOsxDeviceIsStarted;  
92
+#endif
93
+
94
+#ifdef OS_LINUX
95
+  dp->initialize           = cmApAlsaInitialize;
96
+  dp->finalize             = cmApAlsaFinalize;
97
+  dp->deviceCount          = cmApAlsaDeviceCount;
98
+  dp->deviceLabel          = cmApAlsaDeviceLabel;
99
+  dp->deviceChannelCount   = cmApAlsaDeviceChannelCount;
100
+  dp->deviceSampleRate     = cmApAlsaDeviceSampleRate;
101
+  dp->deviceFramesPerCycle = cmApAlsaDeviceFramesPerCycle;
102
+  dp->deviceSetup          = cmApAlsaDeviceSetup;
103
+  dp->deviceStart          = cmApAlsaDeviceStart;
104
+  dp->deviceStop           = cmApAlsaDeviceStop;
105
+  dp->deviceIsStarted      = cmApAlsaDeviceIsStarted;  
106
+#endif
107
+
108
+  dp = _ap->drvArray + 1;
109
+
110
+  dp->initialize           = cmApFileInitialize;
111
+  dp->finalize             = cmApFileFinalize;
112
+  dp->deviceCount          = cmApFileDeviceCount;
113
+  dp->deviceLabel          = cmApFileDeviceLabel;
114
+  dp->deviceChannelCount   = cmApFileDeviceChannelCount;
115
+  dp->deviceSampleRate     = cmApFileDeviceSampleRate;
116
+  dp->deviceFramesPerCycle = cmApFileDeviceFramesPerCycle;
117
+  dp->deviceSetup          = cmApFileDeviceSetup;
118
+  dp->deviceStart          = cmApFileDeviceStart;
119
+  dp->deviceStop           = cmApFileDeviceStop;
120
+  dp->deviceIsStarted      = cmApFileDeviceIsStarted;  
121
+
122
+  dp = _ap->drvArray + 2;
123
+
124
+  dp->initialize           = cmApAggInitialize;
125
+  dp->finalize             = cmApAggFinalize;
126
+  dp->deviceCount          = cmApAggDeviceCount;
127
+  dp->deviceLabel          = cmApAggDeviceLabel;
128
+  dp->deviceChannelCount   = cmApAggDeviceChannelCount;
129
+  dp->deviceSampleRate     = cmApAggDeviceSampleRate;
130
+  dp->deviceFramesPerCycle = cmApAggDeviceFramesPerCycle;
131
+  dp->deviceSetup          = cmApAggDeviceSetup;
132
+  dp->deviceStart          = cmApAggDeviceStart;
133
+  dp->deviceStop           = cmApAggDeviceStop;
134
+  dp->deviceIsStarted      = cmApAggDeviceIsStarted;  
135
+
136
+  dp = _ap->drvArray + 3;
137
+
138
+  dp->initialize           = cmApNrtInitialize;
139
+  dp->finalize             = cmApNrtFinalize;
140
+  dp->deviceCount          = cmApNrtDeviceCount;
141
+  dp->deviceLabel          = cmApNrtDeviceLabel;
142
+  dp->deviceChannelCount   = cmApNrtDeviceChannelCount;
143
+  dp->deviceSampleRate     = cmApNrtDeviceSampleRate;
144
+  dp->deviceFramesPerCycle = cmApNrtDeviceFramesPerCycle;
145
+  dp->deviceSetup          = cmApNrtDeviceSetup;
146
+  dp->deviceStart          = cmApNrtDeviceStart;
147
+  dp->deviceStop           = cmApNrtDeviceStop;
148
+  dp->deviceIsStarted      = cmApNrtDeviceIsStarted;  
149
+
150
+  _ap->devCnt = 0;
151
+
152
+  unsigned i;
153
+  for(i=0; i<_ap->drvCnt; ++i)
154
+  {
155
+    unsigned dn; 
156
+    cmApRC_t rc0;
157
+
158
+    _ap->drvArray[i].begDevIdx = cmInvalidIdx;
159
+    _ap->drvArray[i].endDevIdx = cmInvalidIdx;
160
+
161
+    if((rc0 = _ap->drvArray[i].initialize(rpt,_ap->devCnt)) != kOkApRC )
162
+    {
163
+      rc = rc0;
164
+      continue;
165
+    }
166
+
167
+    if((dn = _ap->drvArray[i].deviceCount()) > 0)
168
+    {
169
+      _ap->drvArray[i].begDevIdx = _ap->devCnt;
170
+      _ap->drvArray[i].endDevIdx = _ap->devCnt + dn - 1;
171
+      _ap->devCnt += dn;
172
+    }
173
+  }
174
+
175
+  if( rc != kOkApRC )
176
+    cmApFinalize();
177
+
178
+  return rc;
179
+}
180
+
181
+cmApRC_t      cmApFinalize()
182
+{
183
+  cmApRC_t rc=kOkApRC;
184
+  cmApRC_t rc0 = kOkApRC;
185
+  unsigned i;
186
+
187
+  if( _ap == NULL )
188
+    return kOkApRC;
189
+
190
+  for(i=0; i<_ap->drvCnt; ++i)
191
+  {
192
+    if((rc0 = _ap->drvArray[i].finalize()) != kOkApRC )
193
+      rc = rc0;
194
+  }
195
+
196
+  cmMemPtrFree(&_ap->drvArray);
197
+  cmMemPtrFree(&_ap);
198
+  return rc;
199
+}
200
+
201
+
202
+unsigned cmApDeviceCount()
203
+{ return _ap->devCnt; }
204
+
205
+const char*   cmApDeviceLabel(          unsigned devIdx )
206
+{
207
+  cmApDriver_t* dp;
208
+  unsigned      di;
209
+  cmApRC_t      rc;
210
+
211
+  if( devIdx == cmInvalidIdx )
212
+    return NULL;
213
+
214
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
215
+    return cmStringNullGuard(NULL);
216
+
217
+  return dp->deviceLabel(di);
218
+}
219
+
220
+unsigned      cmApDeviceChannelCount(   unsigned devIdx, bool inputFl )
221
+{
222
+  cmApDriver_t* dp;
223
+  unsigned      di;
224
+  cmApRC_t      rc;
225
+
226
+  if( devIdx == cmInvalidIdx )
227
+    return 0;
228
+
229
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
230
+    return rc;
231
+
232
+  return dp->deviceChannelCount(di,inputFl);
233
+}
234
+
235
+double        cmApDeviceSampleRate(     unsigned devIdx )
236
+{
237
+  cmApDriver_t* dp;
238
+  unsigned      di;
239
+  cmApRC_t      rc;
240
+
241
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
242
+    return rc;
243
+
244
+  return dp->deviceSampleRate(di);
245
+}
246
+
247
+unsigned      cmApDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
248
+{
249
+  cmApDriver_t* dp;
250
+  unsigned      di;
251
+  cmApRC_t      rc;
252
+
253
+  if( devIdx == cmInvalidIdx )
254
+    return 0;
255
+
256
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
257
+    return rc;
258
+
259
+  return dp->deviceFramesPerCycle(di,inputFl);
260
+}
261
+
262
+cmApRC_t      cmApDeviceSetup(          
263
+  unsigned          devIdx, 
264
+  double            srate, 
265
+  unsigned          framesPerCycle, 
266
+  cmApCallbackPtr_t callbackPtr,
267
+  void*             userCbPtr )
268
+{
269
+  cmApDriver_t* dp;
270
+  unsigned      di;
271
+  cmApRC_t      rc;
272
+
273
+  if( devIdx == cmInvalidIdx )
274
+    return kOkApRC;
275
+
276
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
277
+    return rc;
278
+
279
+  return dp->deviceSetup(di,srate,framesPerCycle,callbackPtr,userCbPtr);
280
+}
281
+
282
+cmApRC_t      cmApDeviceStart( unsigned devIdx )
283
+{
284
+  cmApDriver_t* dp;
285
+  unsigned      di;
286
+  cmApRC_t      rc;
287
+
288
+  if( devIdx == cmInvalidIdx )
289
+    return kOkApRC;
290
+
291
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
292
+    return rc;
293
+
294
+  return dp->deviceStart(di);
295
+}
296
+
297
+cmApRC_t      cmApDeviceStop(  unsigned devIdx )
298
+{
299
+  cmApDriver_t* dp;
300
+  unsigned      di;
301
+  cmApRC_t      rc;
302
+
303
+  if( devIdx == cmInvalidIdx )
304
+    return kOkApRC;
305
+
306
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
307
+    return rc;
308
+
309
+  return dp->deviceStop(di);
310
+}
311
+
312
+bool          cmApDeviceIsStarted( unsigned devIdx )
313
+{
314
+  cmApDriver_t* dp;
315
+  unsigned      di;
316
+  cmApRC_t      rc;
317
+
318
+  if( devIdx == cmInvalidIdx )
319
+    return false;
320
+
321
+  if((rc = _cmApIndexToDev(devIdx,&dp,&di)) != kOkApRC )
322
+    return rc;
323
+
324
+  return dp->deviceIsStarted(di);
325
+}
326
+
327
+
328
+
329
+void cmApReport( cmRpt_t* rpt )
330
+{
331
+  unsigned i,j,k;
332
+  for(j=0,k=0; j<_ap->drvCnt; ++j)
333
+  {
334
+    cmApDriver_t* drvPtr = _ap->drvArray + j;
335
+    unsigned      n      = drvPtr->deviceCount(); 
336
+
337
+    for(i=0; i<n; ++i,++k)
338
+    {
339
+      cmRptPrintf(rpt, "%i %f in:%i (%i) out:%i (%i) %s\n",
340
+        k, drvPtr->deviceSampleRate(i),
341
+        drvPtr->deviceChannelCount(i,true),  drvPtr->deviceFramesPerCycle(i,true),
342
+        drvPtr->deviceChannelCount(i,false), drvPtr->deviceFramesPerCycle(i,false),
343
+        drvPtr->deviceLabel(i));
344
+  
345
+    }
346
+  }
347
+}
348
+
349
+/// [cmAudioPortExample]
350
+
351
+// See cmApPortTest() below for the main point of entry.
352
+
353
+// Data structure used to hold the parameters for cpApPortTest()
354
+// and the user defined data record passed to the host from the
355
+// audio port callback functions.
356
+typedef struct
357
+{
358
+  unsigned      bufCnt;         // 2=double buffering 3=triple buffering
359
+  unsigned      chIdx;          // first test channel
360
+  unsigned      chCnt;          // count of channels to test
361
+  unsigned      framesPerCycle; // DSP frames per cycle
362
+  unsigned      bufFrmCnt;      // count of DSP frames used by the audio buffer  (bufCnt * framesPerCycle)
363
+  unsigned      bufSmpCnt;      // count of samples used by the audio buffer     (chCnt  * bufFrmCnt)
364
+  unsigned      inDevIdx;       // input device index
365
+  unsigned      outDevIdx;      // output device index
366
+  double        srate;          // audio sample rate
367
+  unsigned      meterMs;        // audio meter buffer length
368
+
369
+  // param's and state for cmApSynthSine()
370
+  unsigned      phase;          // sine synth phase
371
+  double        frqHz;          // sine synth frequency in Hz
372
+
373
+  // buffer and state for cmApCopyIn/Out()
374
+  cmApSample_t* buf;            // buf[bufSmpCnt] - circular interleaved audio buffer
375
+  unsigned      bufInIdx;       // next input buffer index
376
+  unsigned      bufOutIdx;      // next output buffer index
377
+  unsigned      bufFullCnt;     // count of full samples
378
+
379
+  // debugging log data arrays 
380
+  unsigned      logCnt;        // count of elements in log[] and ilong[]
381
+  char*         log;           // log[logCnt]
382
+  unsigned*     ilog;          // ilog[logCnt]
383
+  unsigned      logIdx;        // current log index
384
+
385
+  unsigned      cbCnt;         // count the callback
386
+} cmApPortTestRecd;
387
+
388
+
389
+#ifdef NOT_DEF
390
+// The application can request any block of channels from the device. The packets are provided with the starting
391
+// device channel and channel count.  This function converts device channels and channel counts to buffer
392
+// channel indexes and counts.  
393
+//
394
+//  Example:
395
+//      input                            output
396
+//       i,n                              i n
397
+//  App: 0,4   0 1 2 3                ->  2 2
398
+//  Pkt  2,8       2 3 4 5 6 7 8      ->  0 2
399
+//
400
+// The return value is the count of application requested channels located in this packet.
401
+//
402
+// input: *appChIdxPtr and appChCnt describe a block of device channels requested by the application.
403
+//        *pktChIdxPtr and pktChCnt describe a block of device channels provided to the application
404
+//
405
+// output:*appChIdxPtr and <return value> describe a block of app buffer channels which will send/recv samples.
406
+//        *pktChIdxPtr and <return value>  describe a block of pkt buffer channels which will send/recv samples
407
+//
408
+unsigned _cmApDeviceToBuffer( unsigned* appChIdxPtr, unsigned appChCnt, unsigned* pktChIdxPtr, unsigned pktChCnt )
409
+{
410
+  unsigned abi = *appChIdxPtr;
411
+  unsigned aei = abi+appChCnt-1;
412
+
413
+  unsigned pbi = *pktChIdxPtr;
414
+  unsigned pei = pbi+pktChCnt-1;
415
+
416
+  // if the ch's rqstd by the app do not overlap with this packet - return false.
417
+  if( aei < pbi || abi > pei )
418
+    return 0;
419
+
420
+  // if the ch's rqstd by the app overlap with the beginning of the pkt channel block
421
+  if( abi < pbi )
422
+  {
423
+    appChCnt     -= pbi - abi;
424
+    *appChIdxPtr  = pbi - abi;
425
+    *pktChIdxPtr  = 0;
426
+  }
427
+  else
428
+  {
429
+    // the rqstd ch's begin inside the pkt channel block
430
+    pktChCnt     -= abi - pbi;
431
+    *pktChIdxPtr  = abi - pbi;
432
+    *appChIdxPtr  = 0;
433
+  }
434
+
435
+  // if the pkt channels extend beyond the rqstd ch block
436
+  if( aei < pei )
437
+    pktChCnt -= pei - aei;
438
+  else 
439
+    appChCnt -= aei - pei; // the rqstd ch's extend beyond or coincide with the pkt block
440
+
441
+  // the returned channel count must always be the same for both the rqstd and pkt 
442
+  return cmMin(appChCnt,pktChCnt);
443
+
444
+}
445
+
446
+
447
+// synthesize a sine signal into an interleaved audio buffer
448
+unsigned _cmApSynthSine( cmApPortTestRecd* r, float* p, unsigned chIdx, unsigned chCnt, unsigned frmCnt, unsigned phs, double hz )
449
+{
450
+  long     ph = 0;
451
+  unsigned i;
452
+  unsigned bufIdx    = r->chIdx;
453
+  unsigned bufChCnt;
454
+
455
+  if( (bufChCnt =  _cmApDeviceToBuffer( &bufIdx, r->chCnt, &chIdx, chCnt )) == 0)
456
+    return phs;
457
+
458
+  
459
+  //if( r->cbCnt < 50 )
460
+  //  printf("ch:%i cnt:%i  ch:%i cnt:%i  bi:%i bcn:%i\n",r->chIdx,r->chCnt,chIdx,chCnt,bufIdx,bufChCnt);
461
+ 
462
+
463
+  for(i=bufIdx; i<bufIdx+bufChCnt; ++i)
464
+  {
465
+    unsigned j;
466
+    float*   op = p + i;
467
+
468
+    ph = phs;
469
+    for(j=0; j<frmCnt; j++, op+=chCnt, ph++)
470
+    {
471
+      *op = (float)(0.9 * sin( 2.0 * M_PI * hz * ph / r->srate ));
472
+    }
473
+  }
474
+  
475
+  return ph;
476
+}
477
+
478
+// Copy the audio samples in the interleaved audio buffer sp[srcChCnt*srcFrameCnt]
479
+// to the internal record buffer.
480
+void _cmApCopyIn( cmApPortTestRecd* r, const cmApSample_t* sp, unsigned srcChIdx, unsigned srcChCnt, unsigned srcFrameCnt  )
481
+{
482
+  unsigned i,j;
483
+
484
+  unsigned chCnt = cmMin(r->chCnt,srcChCnt);
485
+
486
+  for(i=0; i<srcFrameCnt; ++i)
487
+  {
488
+    for(j=0; j<chCnt; ++j)
489
+      r->buf[ r->bufInIdx + j ] = sp[ (i*srcChCnt) + j ];
490
+
491
+    for(; j<r->chCnt; ++j)
492
+      r->buf[ r->bufInIdx + j ] = 0;
493
+
494
+    r->bufInIdx = (r->bufInIdx+r->chCnt) % r->bufFrmCnt;
495
+  }
496
+
497
+  //r->bufFullCnt = (r->bufFullCnt + srcFrameCnt) % r->bufFrmCnt;
498
+  r->bufFullCnt += srcFrameCnt;
499
+}
500
+
501
+// Copy audio samples out of the internal record buffer into dp[dstChCnt*dstFrameCnt].
502
+void _cmApCopyOut( cmApPortTestRecd* r, cmApSample_t* dp, unsigned dstChIdx, unsigned dstChCnt, unsigned dstFrameCnt )
503
+{
504
+  // if there are not enough samples available to fill the destination buffer then zero the dst buf.
505
+  if( r->bufFullCnt < dstFrameCnt )
506
+  {
507
+    printf("Empty Output Buffer\n");
508
+    memset( dp, 0, dstFrameCnt*dstChCnt*sizeof(cmApSample_t) );
509
+  }
510
+  else
511
+  {
512
+    unsigned i,j;
513
+    unsigned chCnt = cmMin(dstChCnt, r->chCnt);
514
+
515
+    // for each output frame
516
+    for(i=0; i<dstFrameCnt; ++i)
517
+    {
518
+      // copy the first chCnt samples from the internal buf to the output buf
519
+      for(j=0; j<chCnt; ++j)
520
+        dp[ (i*dstChCnt) + j ] = r->buf[ r->bufOutIdx + j ];
521
+
522
+      // zero any output ch's for which there is no internal buf channel
523
+      for(; j<dstChCnt; ++j)
524
+        dp[ (i*dstChCnt) + j ] = 0;
525
+
526
+      // advance the internal buffer
527
+      r->bufOutIdx = (r->bufOutIdx + r->chCnt) % r->bufFrmCnt;
528
+    }
529
+
530
+    r->bufFullCnt -= dstFrameCnt;
531
+  }
532
+}
533
+
534
+// Audio port callback function called from the audio device thread.
535
+void _cmApPortCb( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
536
+{
537
+  unsigned i;
538
+
539
+  // for each incoming audio packet
540
+  for(i=0; i<inPktCnt; ++i)
541
+  {
542
+    cmApPortTestRecd* r = (cmApPortTestRecd*)inPktArray[i].userCbPtr; 
543
+
544
+    if( inPktArray[i].devIdx == r->inDevIdx )
545
+    {
546
+      // copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
547
+      _cmApCopyIn( r, (cmApSample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
548
+    }
549
+    ++r->cbCnt;
550
+
551
+    //printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
552
+  }
553
+
554
+  unsigned hold_phase = 0;
555
+
556
+  // for each outgoing audio packet
557
+  for(i=0; i<outPktCnt; ++i)
558
+  {
559
+    cmApPortTestRecd* r = (cmApPortTestRecd*)outPktArray[i].userCbPtr; 
560
+
561
+    if( outPktArray[i].devIdx == r->outDevIdx )
562
+    {
563
+      // zero the output buffer
564
+      memset(outPktArray[i].audioBytesPtr,0,outPktArray[i].chCnt * outPktArray[i].audioFramesCnt * sizeof(cmApSample_t) );
565
+      
566
+      // if the synth is enabled
567
+      if( r->synthFl )
568
+      {
569
+        unsigned tmp_phase  = _cmApSynthSine( r, outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt, r->phase, r->frqHz );  
570
+
571
+        // the phase will only change on packets that are actually used
572
+        if( tmp_phase != r->phase )
573
+          hold_phase = tmp_phase;
574
+      }
575
+      else
576
+      {
577
+        // copy the any audio in the internal record buffer to the playback device 
578
+        _cmApCopyOut( r, (cmApSample_t*)outPktArray[i].audioBytesPtr, outPktArray[i].begChIdx, outPktArray[i].chCnt, outPktArray[i].audioFramesCnt );   
579
+      }
580
+    }
581
+
582
+    r->phase = hold_phase;
583
+
584
+    //printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
585
+    // count callbacks
586
+    ++r->cbCnt;
587
+  }
588
+}
589
+#endif
590
+
591
+// print the usage message for cmAudioPortTest.c
592
+void _cmApPrintUsage( cmRpt_t* rpt )
593
+{
594
+char msg[] =
595
+  "cmApPortTest() command switches\n"
596
+  "-r <srate> -c <chcnt> -b <bufcnt> -f <frmcnt> -i <idevidx> -o <odevidx> -t -p -h \n"
597
+  "\n"
598
+  "-r <srate> = sample rate\n"
599
+  "-a <chidx> = first channel\n"
600
+  "-c <chcnt> = audio channels\n"
601
+  "-b <bufcnt> = count of buffers\n"
602
+  "-f <frmcnt> = count of samples per buffer\n"
603
+  "-i <idevidx> = input device index\n"
604
+  "-o <odevidx> = output device index\n"
605
+  "-p = print report but do not start audio devices\n"
606
+  "-h = print this usage message\n";
607
+
608
+ cmRptPrintf(rpt,msg);
609
+}
610
+
611
+// Get a command line option.
612
+int _cmApGetOpt( int argc, const char* argv[], const char* label, int defaultVal, bool boolFl )
613
+{
614
+  int i = 0;
615
+  for(; i<argc; ++i)
616
+    if( strcmp(label,argv[i]) == 0 )
617
+    {
618
+      if(boolFl)
619
+        return 1;
620
+
621
+      if( i == (argc-1) )
622
+        return defaultVal;
623
+
624
+      return atoi(argv[i+1]);
625
+    }
626
+  
627
+  return defaultVal;
628
+}
629
+
630
+unsigned _cmGlobalInDevIdx  = 0;
631
+unsigned _cmGlobalOutDevIdx = 0;
632
+
633
+void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
634
+{
635
+  cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
636
+
637
+  cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
638
+}
639
+
640
+// Audio Port testing function
641
+int cmApPortTest( bool runFl, cmRpt_t* rpt, int argc, const char* argv[] )
642
+{
643
+  cmApPortTestRecd r;
644
+  unsigned         i;
645
+  int              result = 0;
646
+
647
+  if( _cmApGetOpt(argc,argv,"-h",0,true) )
648
+    _cmApPrintUsage(rpt);
649
+
650
+
651
+  runFl            = _cmApGetOpt(argc,argv,"-p",!runFl,true)?false:true;
652
+  r.chIdx          = _cmApGetOpt(argc,argv,"-a",0,false);
653
+  r.chCnt          = _cmApGetOpt(argc,argv,"-c",2,false);
654
+  r.bufCnt         = _cmApGetOpt(argc,argv,"-b",3,false);
655
+  r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512,false);
656
+  r.bufFrmCnt      = (r.bufCnt*r.framesPerCycle);
657
+  r.bufSmpCnt      = (r.chCnt  * r.bufFrmCnt);
658
+  r.logCnt         = 100; 
659
+  r.meterMs        = 50;
660
+
661
+  cmApSample_t buf[r.bufSmpCnt];
662
+  char         log[r.logCnt];
663
+  unsigned    ilog[r.logCnt];
664
+  
665
+  r.inDevIdx   = _cmGlobalInDevIdx  = _cmApGetOpt(argc,argv,"-i",0,false);   
666
+  r.outDevIdx  = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",2,false); 
667
+  r.phase      = 0;
668
+  r.frqHz      = 2000;
669
+  r.srate      = 44100;
670
+  r.bufInIdx   = 0;
671
+  r.bufOutIdx  = 0;
672
+  r.bufFullCnt = 0;
673
+  r.logIdx     = 0;
674
+
675
+  r.buf        = buf;
676
+  r.log        = log;
677
+  r.ilog       = ilog;
678
+  r.cbCnt      = 0;
679
+
680
+  cmRptPrintf(rpt,"%s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f\n",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate);
681
+
682
+  // allocate the non-real-time port
683
+  if( cmApNrtAllocate(rpt) != kOkApRC )
684
+  {
685
+    cmRptPrintf(rpt,"Non-real-time system allocation failed.");
686
+    result = 1;
687
+    goto errLabel;
688
+  }
689
+
690
+  // initialize the audio device interface
691
+  if( cmApInitialize(rpt) != kOkApRC )
692
+  {
693
+    cmRptPrintf(rpt,"Initialize failed.\n");
694
+    result = 1;
695
+    goto errLabel;
696
+  }
697
+  
698
+  // report the current audio device configuration
699
+  for(i=0; i<cmApDeviceCount(); ++i)
700
+  {
701
+    cmRptPrintf(rpt,"%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%f %s\n",i,cmApDeviceChannelCount(i,true),cmApDeviceFramesPerCycle(i,true),cmApDeviceChannelCount(i,false),cmApDeviceFramesPerCycle(i,false),cmApDeviceSampleRate(i),cmApDeviceLabel(i));
702
+  }
703
+  // report the current audio devices using the audio port interface function
704
+  cmApReport(rpt);
705
+
706
+  if( runFl )
707
+  {
708
+    // initialize the audio bufer
709
+    cmApBufInitialize( cmApDeviceCount(), r.meterMs );
710
+
711
+    // setup the buffer for the output device
712
+    cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle );
713
+
714
+    // setup the buffer for the input device
715
+    if( r.inDevIdx != r.outDevIdx )
716
+      cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle ); 
717
+
718
+    // setup an output device
719
+    if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
720
+      cmRptPrintf(rpt,"Out device setup failed.\n");
721
+    else
722
+      // setup an input device
723
+      if( cmApDeviceSetup(r.inDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
724
+        cmRptPrintf(rpt,"In device setup failed.\n");
725
+      else
726
+        // start the input device
727
+        if( cmApDeviceStart(r.inDevIdx) != kOkApRC )
728
+          cmRptPrintf(rpt,"In device start failed.\n");
729
+        else
730
+          // start the output device
731
+          if( cmApDeviceStart(r.outDevIdx) != kOkApRC )
732
+            cmRptPrintf(rpt,"Out Device start failed.\n");
733
+
734
+    
735
+    cmRptPrintf(rpt,"q=quit O/o output tone, I/i input tone P/p pass\n");
736
+    char c;
737
+    while((c=getchar()) != 'q')
738
+    {
739
+      //cmApAlsaDeviceRtReport(rpt,r.outDevIdx);
740
+
741
+      switch(c)
742
+      {
743
+        case 'i':
744
+        case 'I':
745
+          cmApBufEnableTone(r.inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
746
+          break;
747
+
748
+        case 'o':
749
+        case 'O':
750
+          cmApBufEnableTone(r.outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
751
+          break;
752
+
753
+        case 'p':
754
+        case 'P':
755
+          cmApBufEnablePass(r.outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
756
+          break;
757
+          
758
+        case 's':
759
+          cmApBufReport(rpt);
760
+          break;
761
+      }
762
+
763
+    }
764
+
765
+    // stop the input device
766
+    if( cmApDeviceIsStarted(r.inDevIdx) )
767
+      if( cmApDeviceStop(r.inDevIdx) != kOkApRC )
768
+        cmRptPrintf(rpt,"In device stop failed.\n");
769
+
770
+    // stop the output device
771
+    if( cmApDeviceIsStarted(r.outDevIdx) )
772
+      if( cmApDeviceStop(r.outDevIdx) != kOkApRC )
773
+        cmRptPrintf(rpt,"Out device stop failed.\n");
774
+  }
775
+
776
+ errLabel:
777
+
778
+  // release any resources held by the audio port interface
779
+  if( cmApFinalize() != kOkApRC )
780
+    cmRptPrintf(rpt,"Finalize failed.\n");
781
+
782
+  cmApBufFinalize();
783
+
784
+  cmApNrtFree();
785
+
786
+  // report the count of audio buffer callbacks
787
+  cmRptPrintf(rpt,"cb count:%i\n", r.cbCnt );
788
+  //for(i=0; i<_logCnt; ++i)
789
+  //  cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]);
790
+  //cmRptPrintf(rpt,"\n");
791
+
792
+
793
+  return result;
794
+}
795
+
796
+/// [cmAudioPortExample]
797
+
798
+
799
+

+ 148
- 0
cmAudioPort.h View File

@@ -0,0 +1,148 @@
1
+/// \file cmAudioPort.h
2
+/// \brief Cross platform audio I/O interface.
3
+///
4
+/// This interface provides data declarations for platform dependent 
5
+/// audio I/O functions. The implementation for the functions are
6
+/// in platform specific modules. See cmAudioPortOsx.c and cmAudioPortAlsa.c.
7
+///
8
+/// ALSA Notes:  
9
+/// Assign capture device to line or mic input:
10
+/// amixer -c 0 cset iface=MIXER,name='Input Source',index=0 Mic
11
+/// amixer -c 0 cset iface=MIXER,name='Input Source',index=0 Line
12
+///
13
+/// -c 0                            select the first card
14
+/// -iface=MIXER                    the cset is targetting the MIXER component
15
+/// -name='Input Source',index=0    the control to set is the first 'Input Source'
16
+/// Note that the 'Capture' control sets the input gain.
17
+///
18
+/// See alsamixer for a GUI to accomplish the same thing.
19
+///
20
+///
21
+/// Usage example and testing code:
22
+/// See cmApPortTest() and cmAudioSysTest().
23
+/// \snippet cmAudioPort.c cmAudioPortExample
24
+///
25
+#ifndef cmAudioPort_h
26
+#define cmAudioPort_h
27
+
28
+#ifdef __cplusplus
29
+extern "C" {
30
+#endif
31
+
32
+  typedef unsigned cmApRC_t;      ///< Audio port interface result code.
33
+  typedef float    cmApSample_t;  ///< Audio sample type.
34
+
35
+  enum 
36
+  { 
37
+    kOkApRC =0, 
38
+    kSysErrApRC,
39
+    kInvalidDevIdApRC,
40
+    kAudioPortFileFailApRC,
41
+    kThreadFailApRC
42
+  };
43
+
44
+  // cmApAudioPacket_t flags
45
+  enum
46
+  {
47
+    kInterleavedApFl = 0x01,  ///< The audio samples are interleaved.
48
+    kFloatApFl       = 0x02   ///< The audio samples are single precision floating point values.
49
+  };
50
+
51
+  /// Audio packet record used by the cmApAudioPacket_t callback.
52
+  /// Audio ports send and receive audio using this data structure. 
53
+  typedef struct
54
+  {
55
+    unsigned devIdx;         ///< device associated with packet
56
+    unsigned begChIdx;       ///< first device channel 
57
+    unsigned chCnt;          ///< count of channels
58
+    unsigned audioFramesCnt; ///< samples per channel (see note below)
59
+    unsigned bitsPerSample;  ///< bits per sample word
60
+    unsigned flags;          ///< kInterleavedApFl | kFloatApFl
61
+    void*    audioBytesPtr;  ///< pointer to sample data
62
+    void*    userCbPtr;      ///< user defined value passed in cmApDeviceSetup()
63
+  }  cmApAudioPacket_t; 
64
+
65
+
66
+  /// Audio port callback signature. 
67
+  /// inPktArray[inPktCnt] are full packets of audio coming from the ADC to the application.
68
+  /// outPktArray[outPktCnt] are empty packets of audio which will be filled by the application 
69
+  /// and then sent to the DAC.
70
+  ///
71
+  /// The value of audioFrameCnt  gives the number of samples per channel which are available
72
+  /// in the packet data buffer 'audioBytesPtr'.  The callback function may decrease this number in
73
+  /// output packets if the number of samples available is less than the size of the buffer.
74
+  /// It is the responsibility of the calling audio port to notice this change and pass the new,
75
+  /// decreased number of samples to the hardware.
76
+  ///
77
+  /// In general it should be assmed that this call is made from a system thread which is not 
78
+  /// the same as the application thread.
79
+  /// The usual thread safety precautions should therefore be taken if this function implementation
80
+  /// interacts with data structures also handled by the application. The audio buffer class (\see cmApBuf.h) 
81
+  /// is designed to provide a safe and efficient way to communicate between
82
+  /// the audio thread and the application.
83
+  typedef void (*cmApCallbackPtr_t)( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt );
84
+
85
+  /// Setup the audio port management object for this machine.
86
+  cmApRC_t      cmApInitialize( cmRpt_t* rpt );
87
+
88
+  /// Stop all audio devices and release any resources held 
89
+  /// by the audio port management object.
90
+  cmApRC_t      cmApFinalize();
91
+
92
+  /// Return the count of audio devices attached to this machine.
93
+  unsigned      cmApDeviceCount();
94
+
95
+  /// Get a textual description of the device at index 'devIdx'.
96
+  const char*   cmApDeviceLabel(          unsigned devIdx );
97
+
98
+  /// Get the count of audio input or output channesl on device at index 'devIdx'.
99
+  unsigned      cmApDeviceChannelCount(   unsigned devIdx, bool inputFl );
100
+
101
+  /// Get the current sample rate of a device.  Note that if the device has both
102
+  /// input and output capability then the sample rate is the same for both.
103
+  double        cmApDeviceSampleRate(     unsigned devIdx );
104
+
105
+  /// Get the count of samples per callback for the input or output for this device.
106
+  unsigned      cmApDeviceFramesPerCycle( unsigned devIdx, bool inputFl );
107
+
108
+  /// Configure a device.  
109
+  /// All devices must be setup before they are started.
110
+  /// framesPerCycle is the requested number of samples per audio callback. The
111
+  /// actual number of samples made from a callback may be smaller. See the note
112
+  /// regarding this in cmApAudioPacket_t.
113
+  /// If the device cannot support the requested configuration then the function
114
+  /// will return an error code.
115
+  /// If the device is started when this function is called then it will be 
116
+  /// automatically stopped and then restarted following the reconfiguration.
117
+  /// If the reconfiguration fails then the device may not be restared.
118
+  cmApRC_t      cmApDeviceSetup(          
119
+    unsigned          devIdx, 
120
+    double            srate, 
121
+    unsigned          framesPerCycle, 
122
+    cmApCallbackPtr_t callbackPtr,
123
+    void*             userCbPtr );
124
+
125
+  /// Start a device. Note that the callback may be made prior to this function returning.
126
+  cmApRC_t      cmApDeviceStart( unsigned devIdx );
127
+
128
+  /// Stop a device.
129
+  cmApRC_t      cmApDeviceStop(  unsigned devIdx );
130
+
131
+  /// Return true if the device is currently started.
132
+  bool          cmApDeviceIsStarted( unsigned devIdx );
133
+
134
+  /// Print a report of all the current audio device configurations.
135
+  void          cmApReport( cmRpt_t* rpt );
136
+
137
+  /// Test the audio port by synthesizing a sine signal or passing audio through
138
+  /// from the input to the output.  This is also a good example of how to 
139
+  /// use all of the functions in the interface.
140
+  /// Set runFl to false to print a report without starting any audio devices.
141
+  /// See cmAudiotPortTest.c for usage example for this function.
142
+  int           cmApPortTest(bool runFl, cmRpt_t* rpt, int argc, const char* argv[] );
143
+
144
+#ifdef __cplusplus
145
+}
146
+#endif
147
+
148
+#endif

+ 280
- 0
cmAudioPortFile.c View File

@@ -0,0 +1,280 @@
1
+#include "cmGlobal.h"
2
+#include "cmRpt.h"
3
+#include "cmErr.h"
4
+#include "cmCtx.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmAudioPort.h"
8
+#include "cmAudioPortFile.h"
9
+#include "cmAudioFileDev.h"
10
+
11
+typedef struct
12
+{
13
+  cmAfdH_t devH;
14
+} cmApDev_t;
15
+
16
+typedef struct
17
+{
18
+  cmErr_t    err;
19
+  cmApDev_t* devArray;
20
+  unsigned   devCnt;
21
+} cmApf_t;
22
+
23
+cmApf_t* _cmApf = NULL;
24
+
25
+cmApRC_t      cmApFileInitialize( cmRpt_t* rpt, unsigned baseApDevIdx )
26
+{
27
+  cmApRC_t rc;
28
+  if((rc = cmApFileFinalize()) != kOkApRC )
29
+    return rc;
30
+
31
+  _cmApf = cmMemAllocZ(cmApf_t,1);
32
+
33
+  cmErrSetup(&_cmApf->err,rpt,"Audio Port File");
34
+
35
+  return rc;      
36
+}
37
+
38
+cmApRC_t      cmApFileFinalize()
39
+{
40
+  cmApRC_t rc = kOkApRC;
41
+
42
+  if( _cmApf == NULL )
43
+    return kOkApRC;
44
+
45
+  unsigned i;
46
+  for(i=0; i<_cmApf->devCnt; ++i)
47
+  {
48
+    cmApRC_t rc0;
49
+    if((rc0 = cmApFileDeviceDestroy(i)) != kOkApRC )
50
+      rc = rc0;
51
+  }
52
+
53
+  cmMemPtrFree(&_cmApf->devArray);
54
+  _cmApf->devCnt = 0;
55
+
56
+  cmMemPtrFree(&_cmApf);
57
+
58
+  return rc;
59
+}
60
+
61
+unsigned      cmApFileDeviceCreate( 
62
+  const cmChar_t* devLabel,
63
+  const cmChar_t* iFn,
64
+  const cmChar_t* oFn,
65
+  unsigned        oBits,
66
+  unsigned        oChCnt )
67
+{
68
+  unsigned i;
69
+
70
+  // find an available device slot
71
+  for(i=0; i<_cmApf->devCnt; ++i)
72
+    if( cmAudioFileDevIsValid( _cmApf->devArray[i].devH ) == false )
73
+      break;
74
+
75
+  // if no device slot is availd ...
76
+  if( i == _cmApf->devCnt )
77
+  {
78
+    // ... create a new one
79
+    _cmApf->devArray = cmMemResizePZ(cmApDev_t, _cmApf->devArray, _cmApf->devCnt+1);
80
+    ++_cmApf->devCnt;
81
+  }
82
+
83
+  // open the file device
84
+  if( cmAudioFileDevInitialize( &_cmApf->devArray[i].devH, devLabel, i, iFn, oFn, oBits, oChCnt, _cmApf->err.rpt ) != kOkAfdRC )
85
+  {
86
+    cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device initialization failed.");
87
+    i = cmInvalidIdx;
88
+  }
89
+  
90
+  return i;
91
+}
92
+
93
+cmApRC_t      cmApFileDeviceDestroy( unsigned devIdx )
94
+{
95
+  if( cmAudioFileDevFinalize( &_cmApf->devArray[devIdx].devH ) != kOkAfdRC )
96
+    return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device finalize failed.");
97
+
98
+  return kOkApRC;
99
+}
100
+
101
+unsigned      cmApFileDeviceCount()
102
+{ return _cmApf->devCnt; }
103
+
104
+const char*   cmApFileDeviceLabel( unsigned devIdx )
105
+{
106
+  assert( devIdx < cmApFileDeviceCount());
107
+  return cmAudioFileDevLabel( _cmApf->devArray[devIdx].devH ); 
108
+}
109
+
110
+unsigned      cmApFileDeviceChannelCount(   unsigned devIdx, bool inputFl )
111
+{
112
+  assert( devIdx < cmApFileDeviceCount());
113
+  return cmAudioFileDevChannelCount( _cmApf->devArray[devIdx].devH, inputFl ); 
114
+}
115
+
116
+double        cmApFileDeviceSampleRate(     unsigned devIdx )
117
+{
118
+  assert( devIdx < cmApFileDeviceCount());
119
+  return cmAudioFileDevSampleRate( _cmApf->devArray[devIdx].devH ); 
120
+}
121
+
122
+unsigned      cmApFileDeviceFramesPerCycle( unsigned devIdx, bool inputFl )
123
+{
124
+  assert( devIdx < cmApFileDeviceCount());
125
+  return cmAudioFileDevFramesPerCycle( _cmApf->devArray[devIdx].devH, inputFl ); 
126
+}
127
+
128
+cmApRC_t      cmApFileDeviceSetup(          
129
+  unsigned          devIdx, 
130
+  double            srate, 
131
+  unsigned          framesPerCycle, 
132
+  cmApCallbackPtr_t callbackPtr,
133
+  void*             userCbPtr )
134
+{
135
+  assert( devIdx < cmApFileDeviceCount());
136
+
137
+  if( cmAudioFileDevSetup( _cmApf->devArray[devIdx].devH,srate,framesPerCycle,callbackPtr,userCbPtr) != kOkAfdRC )
138
+    return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device setup failed.");
139
+
140
+  return kOkApRC;
141
+}
142
+
143
+cmApRC_t      cmApFileDeviceStart( unsigned devIdx )
144
+{
145
+  assert( devIdx < cmApFileDeviceCount());
146
+
147
+  if( cmAudioFileDevStart( _cmApf->devArray[devIdx].devH ) != kOkAfdRC )
148
+    return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device setup failed.");
149
+
150
+  return kOkApRC;
151
+}
152
+
153
+cmApRC_t      cmApFileDeviceStop(  unsigned devIdx )
154
+{
155
+  assert( devIdx < cmApFileDeviceCount());
156
+
157
+  if( cmAudioFileDevStop( _cmApf->devArray[devIdx].devH ) != kOkAfdRC )
158
+    return cmErrMsg(&_cmApf->err,kAudioPortFileFailApRC,"The audio file device setup failed.");
159
+
160
+  return kOkApRC;
161
+}
162
+
163
+bool          cmApFileDeviceIsStarted( unsigned devIdx )
164
+{
165
+  assert( devIdx < cmApFileDeviceCount());
166
+  return cmAudioFileDevIsStarted( _cmApf->devArray[devIdx].devH );
167
+}
168
+
169
+void          cmApFileReport( cmRpt_t* rpt )
170
+{
171
+  unsigned i;
172
+  for(i=0; _cmApf->devCnt; ++i)
173
+  {
174
+    cmRptPrintf(rpt,"%i: ",i);
175
+    cmAudioFileDevReport( _cmApf->devArray[i].devH, rpt );
176
+    cmRptPrintf(rpt,"\n");
177
+  }
178
+}
179
+
180
+
181
+// device callback function used with cmAudioPortFileTest() note that this assumes
182
+// that the packet buffer contain non-interleaved data.
183
+void _cmApFileTestCb(
184
+  cmApAudioPacket_t* inPktArray, 
185
+  unsigned           inPktCnt, 
186
+  cmApAudioPacket_t* outPktArray, 
187
+  unsigned           outPktCnt )
188
+{
189
+  cmApAudioPacket_t* ip  = inPktArray;
190
+  cmApAudioPacket_t* op  = outPktArray;
191
+  unsigned           opi = 0;
192
+  unsigned           ipi = 0;
193
+  unsigned           oci = 0;
194
+  unsigned           ici = 0;
195
+
196
+  while(1)
197
+  {
198
+    if( ici == ip->chCnt)
199
+    {
200
+      ici = 0;
201
+      if( ++ipi >= inPktCnt )
202
+        break;
203
+
204
+      ip = inPktArray + ipi;
205
+    }
206
+
207
+
208
+    if( oci == op->chCnt )
209
+    {
210
+      oci = 0;
211
+      if( ++opi >= outPktCnt )
212
+        break;
213
+
214
+      ip = outPktArray + opi;
215
+    }
216
+
217
+    assert( ip->audioFramesCnt == op->audioFramesCnt );
218
+    assert( cmIsFlag(ip->flags,kInterleavedApFl)==false && cmIsFlag(ip->flags,kInterleavedApFl)==false );
219
+
220
+    cmApSample_t* ibp = ((cmApSample_t*)ip->audioBytesPtr) + (ip->audioFramesCnt*ici);
221
+    cmApSample_t* obp = ((cmApSample_t*)op->audioBytesPtr) + (op->audioFramesCnt*oci);
222
+
223
+    memcpy(obp,ibp,ip->audioFramesCnt*sizeof(cmApSample_t));
224
+
225
+    ++ici;
226
+    ++oci;
227
+  }
228
+}
229
+
230
+
231
+void          cmApFileTest( cmRpt_t* rpt )
232
+{
233
+  unsigned dev0Idx;
234
+ 
235
+  const cmChar_t* promptStr      = "apf> q=quit 1=start 0=stop\n";
236
+  const cmChar_t* label0         = "file0";
237
+  const cmChar_t* i0Fn           = "/home/kevin/media/audio/McGill-1/1 Audio Track.aiff";
238
+  const cmChar_t* o0Fn           = "/home/kevin/temp/afd1.aif";
239
+  unsigned        o0Bits         = 16;
240
+  unsigned        o0ChCnt        = 2;
241
+   double         srate          = 44100;
242
+  unsigned        framesPerCycle = 512;
243
+
244
+  // initialize audio port file API
245
+  if( cmApFileInitialize(rpt,0) != kOkApRC )
246
+    return;
247
+
248
+  // create an audio port file
249
+  if((dev0Idx = cmApFileDeviceCreate(label0,i0Fn,o0Fn,o0Bits,o0ChCnt)) == cmInvalidIdx )
250
+    goto errLabel;
251
+ 
252
+  // configure an audio port file
253
+  if( cmApFileDeviceSetup( dev0Idx, srate, framesPerCycle, _cmApFileTestCb, NULL ) != kOkAfdRC )
254
+    goto errLabel;
255
+ 
256
+  char c;
257
+  fputs(promptStr,stderr);
258
+  fflush(stdin);
259
+
260
+  while((c=getchar()) != 'q')
261
+  {
262
+    switch(c)
263
+    {
264
+      case '0': cmApFileDeviceStart(dev0Idx); break;
265
+      case '1': cmApFileDeviceStop(dev0Idx);  break;
266
+    }
267
+
268
+    fputs(promptStr,stderr);
269
+    fflush(stdin);
270
+    c = 0;
271
+  }
272
+
273
+
274
+
275
+ errLabel:
276
+  //cmApFileDeviceDestroy(dev0Idx);
277
+
278
+  cmApFileFinalize();
279
+
280
+}

+ 47
- 0
cmAudioPortFile.h View File

@@ -0,0 +1,47 @@
1
+#ifndef cmAudioPortFile_h
2
+#define cmAudioPortFile_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+
9
+  cmApRC_t      cmApFileInitialize( cmRpt_t* rpt, unsigned baseApDevIdx );
10
+  cmApRC_t      cmApFileFinalize();
11
+  bool          cmApFileIsValid();
12
+
13
+  unsigned      cmApFileDeviceCreate( 
14
+    const cmChar_t* devLabel,
15
+    const cmChar_t* iFn,
16
+    const cmChar_t* oFn,
17
+    unsigned        oBits,
18
+    unsigned        oChCnt );
19
+
20
+  cmApRC_t      cmApFileDeviceDestroy( unsigned devIdx ); 
21
+  
22
+  unsigned      cmApFileDeviceCount();
23
+  const char*   cmApFileDeviceLabel(          unsigned devIdx );
24
+  unsigned      cmApFileDeviceChannelCount(   unsigned devIdx, bool inputFl );
25
+  double        cmApFileDeviceSampleRate(     unsigned devIdx );
26
+  unsigned      cmApFileDeviceFramesPerCycle( unsigned devIdx, bool inputFl );
27
+
28
+  cmApRC_t      cmApFileDeviceSetup(          
29
+    unsigned          devIdx, 
30
+    double            srate, 
31
+    unsigned          framesPerCycle, 
32
+    cmApCallbackPtr_t callbackPtr,
33
+    void*             userCbPtr );
34
+
35
+
36
+  cmApRC_t      cmApFileDeviceStart( unsigned devIdx );
37
+  cmApRC_t      cmApFileDeviceStop(  unsigned devIdx );
38
+  bool          cmApFileDeviceIsStarted( unsigned devIdx );
39
+  void          cmApFileReport( cmRpt_t* rpt );
40
+  void          cmApFileTest( cmRpt_t* rpt );
41
+
42
+#ifdef __cplusplus
43
+}
44
+#endif
45
+
46
+
47
+#endif

+ 1415
- 0
cmAudioSys.c
File diff suppressed because it is too large
View File


+ 298
- 0
cmAudioSys.h View File

@@ -0,0 +1,298 @@
1
+/// \file cmAudioSys.h
2
+/// \brief Implements a real-time audio processing engine.
3
+///
4
+/// The audio system is composed a collection of independent sub-systems.
5
+/// Each sub-system maintains a thread which runs asynchrounsly
6
+/// from the application, the MIDI devices, and the audio devices.
7
+/// To faciliate communication between these components each sub-system maintains 
8
+/// two thread-safe data buffers one for control information and a second 
9
+/// for audio data.
10
+///
11
+/// The audio devices are the primary driver for the system. 
12
+/// Callbacks from the audio devices (See #cmApCallbackPtr_t) 
13
+/// inserts incoming audio samples into the audio
14
+/// record buffers and extracts samples from the playback buffer.  
15
+/// When sufficient incoming samples and outgoing empty buffer space exists
16
+/// a sub-system thread is waken up by the callback. This triggers a DSP audio 
17
+/// processing cycle which empties/fills the audio buffers. During a DSP
18
+/// processing cycle control messages from the application and MIDI are blocked and
19
+/// buffered. Upon completetion of the DSP cycle a control message
20
+/// transfer cycles occurs - buffered incoming messages are passed to 
21
+/// the DSP system and messages originating in the DSP system are
22
+/// buffered by the audio system for later pickup by the application
23
+/// or MIDI system.
24
+/// 
25
+/// Note that control messages that arrive when the DSP cycle is not
26
+/// occurring can pass directly through to the DSP system.
27
+///
28
+/// The DSP system sends messages back to the host by calling
29
+/// cmAsDspToHostFunc_t provided by cmAudioSysCtx_t. These
30
+/// calls are always made from within an audio system call to 
31
+/// audio or control update within cmAsCallback_t. cmAsDspToHostFunc_t
32
+/// simply stores the message in a message buffer.  The host picks
33
+/// up the message at some later time when it notices that messages
34
+/// are waiting via polling cmAudioSysIsMsgWaiting().
35
+///
36
+/// Implementation: \n
37
+/// The audio sub-systems work by maintaining an internal thread
38
+/// which blocks on a mutex condition variable.
39
+/// While the thread is blocked the mutex is unlocked allowing messages
40
+/// to pass directly through to the DSP procedure via cmAsCallback().
41
+///
42
+/// Periodic calls from running audio devices update the audio buffer. 
43
+/// When the audio buffer has input samples waiting and output space
44
+/// available the condition variable is signaled, the mutex is 
45
+/// then automatically locked by the system, and the DSP execution
46
+/// procedure is called via cmAsCallback().
47
+///
48
+/// Messages arriving while the mutex is locked are queued and
49
+/// delivered to the DSP procedure at the end of the DSP execution
50
+/// procedure.
51
+///
52
+/// Usage example and testing code:
53
+/// See  cmAudioSysTest().
54
+/// \snippet cmAudioSys.c cmAudioSysTest
55
+
56
+#ifndef cmAudioSys_h
57
+#define cmAudioSys_h
58
+
59
+#ifdef __cplusplus
60
+extern "C" {
61
+#endif
62
+
63
+  /// Audio system result codes
64
+  enum
65
+  {
66
+    kOkAsRC = cmOkRC,
67
+    kThreadErrAsRC,
68
+    kMutexErrAsRC,
69
+    kTsQueueErrAsRC,
70
+    kMsgEnqueueFailAsRC,
71
+    kAudioDevSetupErrAsRC,
72
+    kAudioBufSetupErrAsRC,
73
+    kAudioDevStartFailAsRC,
74
+    kAudioDevStopFailAsRC,
75
+    kBufTooSmallAsRC,
76
+    kNoMsgWaitingAsRC,
77
+    kMidiSysFailAsRC,
78
+    kMsgSerializeFailAsRC,
79
+    kStateBufFailAsRC,
80
+    kInvalidArgAsRC,
81
+    kNotInitAsRC
82
+  };
83
+
84
+  typedef cmHandle_t cmAudioSysH_t;  ///< Audio system handle type
85
+  typedef unsigned   cmAsRC_t;       ///< Audio system result code
86
+
87
+  struct cmAudioSysCtx_str;
88
+
89
+  ///
90
+  /// DSP system callback function.
91
+  ///
92
+  /// This is the sole point of entry into the DSP system while the audio system is running.
93
+  ///
94
+  /// ctxPtr is pointer to a cmAudioSysCtx_t record.
95
+  ///
96
+  /// This function is called under two circumstances:
97
+  ///
98
+  /// 1) To notify the DSP system that the audio input/output buffers need to be serviced.
99
+  /// This is a perioidic request which the DSP system uses as its execution trigger.
100
+  /// The msgByteCnt argument is set to zero to indicate this type of call.  
101
+  ///
102
+  /// 2) To pass messages from the host application to the DSP system.
103
+  /// The DSP system is asyncronous with the host because it executes in the audio system thread
104
+  /// rather than the host thread.  The cmAudioSysDeliverMsg() function synchronizes incoming
105
+  /// messages with the internal audio system thread to prevent thread collisions.
106
+  ///
107
+  /// Notes:
108
+  /// This callback is always made with the internal audio system mutex locked.
109
+  ///
110
+  /// The signal time covered by the callback is from 
111
+  /// ctx->begSmpIdx to ctx->begSmpIdx+cfg->dspFramesPerCycle.
112
+  ///
113
+  /// The return value is currently not used.
114
+  typedef cmRC_t (*cmAsCallback_t)(void* ctxPtr, unsigned msgByteCnt, const void* msgDataPtr );
115
+
116
+  
117
+  /// Audio device sub-sytem configuration record 
118
+  typedef struct
119
+  {
120
+    cmRpt_t*       rpt;               ///< system console object
121
+    unsigned       inDevIdx;          ///< input audio device
122
+    unsigned       outDevIdx;         ///< output audio device
123
+    bool           syncInputFl;       ///< true/false sync the DSP update callbacks with audio input/output
124
+    unsigned       msgQueueByteCnt;   ///< Size of the internal msg queue used to buffer msgs arriving via cmAudioSysDeliverMsg().
125
+    unsigned       devFramesPerCycle; ///< (512) Audio device samples per channel per device update buffer.
126
+    unsigned       dspFramesPerCycle; ///< (64)  Audio samples per channel per DSP cycle.
127
+    unsigned       audioBufCnt;       ///< (3)   Audio device buffers.
128
+    double         srate;             ///< Audio sample rate.
129
+  } cmAudioSysArgs_t;
130
+
131
+  /// Audio sub-system configuration record.
132
+  /// This record is provided by the host to configure the audio system
133
+  /// via cmAudioSystemAllocate() or cmAudioSystemInitialize().
134
+  typedef struct cmAudioSysSubSys_str
135
+  {
136
+    cmAudioSysArgs_t args;              ///< Audio device configuration
137
+    cmAsCallback_t   cbFunc;            ///< DSP system entry point function.
138
+    void*            cbDataPtr;         ///< Host provided data for the DSP system callback.   
139
+  } cmAudioSysSubSys_t;
140
+
141
+
142
+  /// Signature of a callback function provided by the audio system to receive messages 
143
+  /// from the DSP system for later dispatch to the host application.
144
+  /// This declaration is used by the DSP system implementation and the audio system.
145
+  /// Note that this function is intended to convey one message broken into multiple parts.
146
+  /// See cmTsQueueEnqueueSegMsg() for the equivalent interface.
147
+  typedef cmAsRC_t (*cmAsDspToHostFunc_t)(struct cmAudioSysCtx_str* p, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt);
148
+
149
+  /// Informational record passed with each call to the DSP callback function cmAsCallback_t
150
+  typedef struct cmAudioSysCtx_str
151
+  {
152
+    void*               reserved;      ///< used internally by the system
153
+
154
+    bool                audioRateFl;
155
+
156
+    unsigned            srcNetNodeId;  ///<
157
+    unsigned            asSubIdx;      ///< index of the sub-system this DSP process is serving
158
+
159
+    cmAudioSysSubSys_t* ss;            ///< ptr to a copy of the cfg recd used to initialize the audio system
160
+    unsigned            begSmpIdx;     ///< gives signal time as a sample count
161
+
162
+    cmAsDspToHostFunc_t dspToHostFunc; ///< Callback used by the DSP process to send messages to the host
163
+                                       ///< via the audio system. Returns a cmAsRC_t result code.
164
+
165
+                                       ///< output (playback) buffers
166
+    cmSample_t**        oChArray;      ///< each ele is a ptr to buffer with cfg.dspFramesPerCycle samples
167
+    unsigned            oChCnt;        ///< count of output channels (ele's in oChArray[])
168
+
169
+                                       ///< input (recording) buffers
170
+    cmSample_t**        iChArray;      ///< each ele is a ptr to buffer with cfg.dspFramesPerCycle samples
171
+    unsigned            iChCnt;        ///< count of input channels (ele's in iChArray[])
172
+    
173
+  } cmAudioSysCtx_t;
174
+
175
+  typedef struct
176
+  {
177
+    const cmChar_t* devLabel;
178
+    const cmChar_t* inAudioFn;
179
+    const cmChar_t* outAudioFn;
180
+    unsigned        oBits;
181
+    unsigned        oChCnt;
182
+  } cmAudioSysFilePort_t;
183
+
184
+
185
+  /// Audio system configuration record used by cmAudioSysAllocate().
186
+  typedef struct cmAudioSysCfg_str
187
+  {
188
+    cmAudioSysSubSys_t*   ssArray;      ///< sub-system cfg record array
189
+    unsigned              ssCnt;        ///< count of sub-systems   
190
+    cmAudioSysFilePort_t* afpArray;     ///< audio port file cfg recd array
191
+    unsigned              afpCnt;       ///< audio port file cnt
192
+    unsigned              meterMs;      ///< Meter sample period in milliseconds
193
+    void*                 clientCbData; ///< User arg. for clientCbFunc().
194
+    cmTsQueueCb_t         clientCbFunc; ///< Called by  cmAudioSysReceiveMsg() to deliver internally generated msg's to the host. 
195
+                                        ///  Set to NULL if msg's will be directly returned by buffers passed to cmAudioSysReceiveMsg().
196
+    cmUdpNetH_t           netH;
197
+  } cmAudioSysCfg_t;
198
+
199
+  extern cmAudioSysH_t cmAudioSysNullHandle;
200
+
201
+  /// Allocate and initialize an audio system as a collection of 'cfgCnt' sub-systems.
202
+  /// Notes:
203
+  /// The audio ports system must be initalized (via cmApInitialize()) prior to calling cmAudioSysAllocate().
204
+  /// The MIDI port system must be initialized (via cmMpInitialize()) prior to calling cmAudioSysAllocate().
205
+  /// Furthermore cmApFinalize() and cmMpFinalize() cannot be called prior to cmAudioSysFree().
206
+  /// See cmAudioSystemTest() for a complete example.
207
+  cmAsRC_t  cmAudioSysAllocate( cmAudioSysH_t* hp, cmRpt_t* rpt, const cmAudioSysCfg_t* cfg  );
208
+
209
+  /// Finalize and release any resources held by the audio system.
210
+  cmAsRC_t  cmAudioSysFree( cmAudioSysH_t* hp );
211
+
212
+  /// Returns true if 'h' is a handle which was successfully allocated by cmAudioSysAllocate().
213
+  bool      cmAudioSysHandleIsValid( cmAudioSysH_t h );
214
+
215
+  /// Reinitialize a previously allocated audio system.  This function
216
+  /// begins with a call to cmAudioSysFinalize().   
217
+  /// Use cmAudioSysEnable(h,true) to begin processing audio following this call.
218
+  cmAsRC_t  cmAudioSysInitialize( cmAudioSysH_t h, const cmAudioSysCfg_t* cfg );
219
+
220
+  /// Complements cmAudioSysInitialize(). In general there is no need to call this function
221
+  /// since calls to cmAudioSysInitialize() and cmAudioSysFree() automaticatically call it.
222
+  cmAsRC_t  cmAudioSysFinalize( cmAudioSysH_t h );
223
+
224
+  /// Returns true if the audio system has been successfully initialized.
225
+  bool     cmAudioSysIsInitialized( cmAudioSysH_t );
226
+
227
+  /// Returns true if the audio system is enabled.
228
+  bool      cmAudioSysIsEnabled( cmAudioSysH_t h );
229
+
230
+  /// Enable/disable the audio system.  Enabling the starts audio stream
231
+  /// in/out of the system.
232
+  cmAsRC_t  cmAudioSysEnable( cmAudioSysH_t h, bool enableFl );
233
+
234
+  /// \name Host to DSP delivery functions
235
+  /// @{
236
+
237
+  /// Deliver a message from the host application to the DSP process. (host -> DSP);
238
+  /// The message is formed as a concatenation of the bytes in each of the segments
239
+  /// pointed to by 'msgDataPtrArrary[segCnt][msgByteCntArray[segCnt]'.
240
+  /// This is the canonical msg delivery function in so far as the other host->DSP
241
+  /// msg delivery function are written in terms of this function.
242
+  /// The first 4 bytes in the first segment must contain the index of the audio sub-system
243
+  /// which is to receive the message.
244
+  cmAsRC_t  cmAudioSysDeliverSegMsg(  cmAudioSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt, unsigned srcNetNodeId );
245
+
246
+  /// Deliver a single message from the host to the DSP system.
247
+  cmAsRC_t  cmAudioSysDeliverMsg(   cmAudioSysH_t h,  const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId );
248
+
249
+  /// Deliver a single message from the host to the DSP system.
250
+  /// Prior to delivery the 'id' is prepended to the message.
251
+  cmAsRC_t  cmAudioSysDeliverIdMsg(  cmAudioSysH_t h, unsigned asSubIdx, unsigned id, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId );
252
+  ///@}
253
+
254
+  /// \name DSP to Host message functions
255
+  /// @{
256
+
257
+  /// Is a msg from the DSP waiting to be picked up by the host?  (host <- DSP)
258
+  /// 0  = no msgs are waiting or the msg queue is locked by the DSP process.
259
+  /// >0 = the size of the buffer required to hold the next msg returned via 
260
+  /// cmAudioSysReceiveMsg().
261
+  unsigned  cmAudioSysIsMsgWaiting(  cmAudioSysH_t h );
262
+
263
+  /// Copy the next available msg sent from the DSP process to the host into the host supplied msg buffer
264
+  /// pointed to by 'msgBufPtr'.  Set 'msgDataPtr' to NULL to receive msg by callback from cmAudioSysCfg_t.clientCbFunc.
265
+  /// Returns kBufTooSmallAsRC if msgDataPtr[msgByteCnt] is too small to hold the msg.
266
+  /// Returns kNoMsgWaitingAsRC if no messages are waiting for delivery or the msg queue is locked by the DSP process.
267
+  /// Returns kOkAsRC if a msg was delivered.
268
+  /// Call cmAudioSysIsMsgWaiting() prior to calling this function to get
269
+  /// the size of the data buffer required to hold the next message.
270
+  cmAsRC_t  cmAudioSysReceiveMsg(    cmAudioSysH_t h,  void* msgDataPtr, unsigned msgByteCnt );
271
+  /// @}
272
+
273
+
274
+  /// Fill an audio system status record.
275
+  void      cmAudioSysStatus(  cmAudioSysH_t h, unsigned asSubIdx, cmAudioSysStatus_t* statusPtr );
276
+
277
+  /// Enable cmAudioSysStatus_t notifications to be sent periodically to the host.
278
+  /// Set asSubIdx to cmInvalidIdx to enable/disable all sub-systems.
279
+  /// The notifications occur approximately every cmAudioSysCfg_t.meterMs milliseconds.
280
+  void cmAudioSysStatusNotifyEnable( cmAudioSysH_t, unsigned asSubIdx, bool enableFl );
281
+
282
+  /// Return a pointer the context record associated with a sub-system
283
+  cmAudioSysCtx_t* cmAudioSysContext( cmAudioSysH_t h, unsigned asSubIdx );
284
+
285
+  /// Return the count of audio sub-systems.
286
+  /// This is the same as the count of cfg recds passed to cmAudioSystemInitialize().
287
+  unsigned cmAudioSysSubSystemCount( cmAudioSysH_t h );
288
+
289
+  /// Audio system test and example function.
290
+  void      cmAudioSysTest( cmRpt_t* rpt, int argc, const char* argv[] );
291
+
292
+
293
+
294
+#ifdef __cplusplus
295
+}
296
+#endif
297
+
298
+#endif

+ 87
- 0
cmComplexTypes.h View File

@@ -0,0 +1,87 @@
1
+#ifndef cmComplexTypes_h
2
+#define cmComplexTypes_h
3
+
4
+#include <complex.h>
5
+#include <fftw3.h>
6
+
7
+#if CM_FLOAT_SMP == 1
8
+
9
+#define cmCabsS  cabsf
10
+#define cmCatanS catanf
11
+#define cmCrealS crealf
12
+#define cmCimagS cimagf 
13
+#define cmCargS  cargf
14
+
15
+#define cmFftPlanAllocS   fftwf_plan_dft_r2c_1d
16
+#define cmFft1dPlanAllocS fftwf_plan_dft_1d
17
+#define cmIFftPlanAllocS  fftwf_plan_dft_c2r_1d
18
+#define cmFftPlanFreeS    fftwf_destroy_plan
19
+#define cmFftMallocS      fftwf_malloc
20
+#define cmFftFreeMemS     fftwf_free
21
+#define cmFftExecuteS     fftwf_execute
22
+
23
+  typedef fftwf_plan      cmFftPlanS_t;
24
+
25
+#else
26
+
27
+#define cmCabsS  cabs
28
+#define cmCatanS catan
29
+#define cmCrealS creal
30
+#define cmCimagS cimag 
31
+#define cmCargS  carg
32
+
33
+#define cmFftPlanAllocS   fftw_plan_dft_r2c_1d
34
+#define cmFft1dPlanAllocS fftw_plan_dft_1d
35
+#define cmIFftPlanAllocS  fftw_plan_dft_c2r_1d
36
+#define cmFftPlanFreeS    fftw_destroy_plan
37
+#define cmFftMallocS      fftw_malloc
38
+#define cmFftFreeMemS     fftw_free
39
+#define cmFftExecuteS     fftw_execute
40
+
41
+  typedef fftw_plan      cmFftPlanS_t;
42
+
43
+#endif
44
+
45
+//-----------------------------------------------------------------
46
+//-----------------------------------------------------------------
47
+//-----------------------------------------------------------------
48
+
49
+#if CM_FLOAT_REAL == 1
50
+
51
+#define cmCabsR  cabsf
52
+#define cmCatanR catanf
53
+#define cmCrealR crealf
54
+#define cmCimagR cimagf 
55
+#define cmCargR  cargf
56
+
57
+#define cmFftPlanAllocR   fftwf_plan_dft_r2c_1d
58
+#define cmFft1dPlanAllocR fftwf_plan_dft_1d
59
+#define cmIFftPlanAllocR  fftwf_plan_dft_c2r_1d
60
+#define cmFftPlanFreeR    fftwf_destroy_plan
61
+#define cmFftMallocR      fftwf_malloc
62
+#define cmFftFreeMemR     fftwf_free
63
+#define cmFftExecuteR     fftwf_execute
64
+
65
+  typedef fftwf_plan     cmFftPlanR_t;
66
+
67
+#else
68
+
69
+#define cmCabsR  cabs
70
+#define cmCatanR catan
71
+#define cmCrealR creal
72
+#define cmCimagR cimag 
73
+#define cmCargR  carg
74
+
75
+#define cmFftPlanAllocR   fftw_plan_dft_r2c_1d
76
+#define cmFft1dPlanAllocR fftw_plan_dft_1d
77
+#define cmIFftPlanAllocR  fftw_plan_dft_c2r_1d
78
+#define cmFftPlanFreeR    fftw_destroy_plan
79
+#define cmFftMallocR      fftw_malloc
80
+#define cmFftFreeMemR     fftw_free
81
+#define cmFftExecuteR     fftw_execute
82
+
83
+  typedef fftw_plan       cmFftPlanR_t;
84
+
85
+#endif
86
+
87
+#endif

+ 1146
- 0
cmCsv.c
File diff suppressed because it is too large
View File


+ 149
- 0
cmCsv.h View File

@@ -0,0 +1,149 @@
1
+#ifndef cmCsv_h
2
+#define cmCsv_h
3
+
4
+
5
+#ifdef __cplusplus
6
+extern "C" {
7
+#endif
8
+
9
+  enum
10
+  {
11
+    kOkCsvRC = 0,
12
+    kMemAllocErrCsvRC,
13
+    kLexErrCsvRC,
14
+    kSymTblErrCsvRC,
15
+    kSyntaxErrCsvRC,
16
+    kFileOpenErrCsvRC,
17
+    kFileCreateErrCsvRC,
18
+    kFileReadErrCsvRC,
19
+    kFileSeekErrCsvRC,
20
+    kFileCloseErrCsvRC,
21
+    kDataCvtErrCsvRC,
22
+    kCellNotFoundCsvRC,
23
+    kDuplicateLexCsvId
24
+  };
25
+
26
+  typedef unsigned   cmCsvRC_t;
27
+  typedef cmHandle_t cmCsvH_t;
28
+
29
+  enum
30
+  {
31
+    kIntCsvTFl   = 0x01,
32
+    kHexCsvTFl   = 0x02,
33
+    kRealCsvTFl  = 0x04,
34
+    kIdentCsvTFl = 0x08,
35
+    kStrCsvTFl   = 0x10,
36
+    kUdefCsvTFl  = 0x20,
37
+
38
+    kNumberTMask   = kIntCsvTFl   | kHexCsvTFl | kRealCsvTFl,
39
+    kTextTMask     = kIdentCsvTFl | kStrCsvTFl,
40
+    kTypeTMask     = kNumberTMask | kTextTMask
41
+  };
42
+
43
+  // Each non-blank CSV cell is represented by a cmCsvCell_t record.
44
+  // All the non-blank cells in a given row are organized as a linked
45
+  // list throught 'rowPtr'.
46
+  typedef struct cmCsvCell_str
47
+  {
48
+    unsigned              row;     // CSV row number
49
+    unsigned              col;     // CSV column number
50
+    struct cmCsvCell_str* rowPtr;  // links together cells in this row
51
+    
52
+    unsigned              symId;   // symbol id for this cell
53
+    unsigned              flags;   // cell flags (see kXXXCsvTFl)
54
+    unsigned              lexTId;
55
+
56
+  } cmCsvCell_t;
57
+
58
+  extern cmCsvH_t cmCsvNullHandle;
59
+
60
+  cmCsvRC_t cmCsvInitialize( cmCsvH_t *hp, cmCtx_t* ctx );
61
+  cmCsvRC_t cmCsvFinalize(   cmCsvH_t *hp );
62
+
63
+  cmCsvRC_t cmCsvInitializeFromFile( cmCsvH_t *hp, const char* fn, unsigned maxRowCnt, cmCtx_t* ctx );
64
+
65
+  bool      cmCsvIsValid( cmCsvH_t h);
66
+  cmCsvRC_t cmCsvLastRC(  cmCsvH_t h);
67
+  void      cmCsvClearRC( cmCsvH_t h);
68
+  
69
+  // Register token matchers. See cmLexRegisterToken and cmLexRegisterMatcher
70
+  // for details.
71
+  cmCsvRC_t cmCsvLexRegisterToken(   cmCsvH_t h, unsigned id, const cmChar_t* token );
72
+  cmCsvRC_t cmCsvLexRegisterMatcher( cmCsvH_t h, unsigned id, cmLexUserMatcherPtr_t funcPtr );
73
+  
74
+  // Return the next available lexer token id above the token id's used internally
75
+  // by the object. This value is fixed after cmCsvInitialize()
76
+  // and does not change for the life of the CSV object.  The application is
77
+  // therefore free to choose any lexer id values equal to or above the 
78
+  // returned value.
79
+  unsigned  cmCsvLexNextAvailId(     cmCsvH_t h );
80
+
81
+  // Set 'maxRowCnt' to 0 if there is no row limit on the file.
82
+  cmCsvRC_t cmCsvParse(      cmCsvH_t h, const char* buf, unsigned bufCharCnt, unsigned maxRowCnt );
83
+  cmCsvRC_t cmCsvParseFile(  cmCsvH_t h, const char* fn, unsigned maxRowCnt );
84
+
85
+  unsigned  cmCsvRowCount(   cmCsvH_t h );
86
+
87
+  // Return a pointer to a given cell.
88
+  cmCsvCell_t* cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col );
89
+
90
+  // Return a pointer to the first cell in a given row
91
+  cmCsvCell_t* cmCsvRowPtr(  cmCsvH_t h, unsigned row );
92
+
93
+
94
+  // Convert a cell symbold id to a value.
95
+  const char* cmCsvCellSymText(   cmCsvH_t h, unsigned symId );
96
+  cmCsvRC_t   cmCsvCellSymInt(    cmCsvH_t h, unsigned symId, int* vp );
97
+  cmCsvRC_t   cmCsvCellSymUInt(   cmCsvH_t h, unsigned symId, unsigned* vp );
98
+  cmCsvRC_t   cmCsvCellSymFloat(  cmCsvH_t h, unsigned symId, float* vp );
99
+  cmCsvRC_t   cmCsvCellSymDouble( cmCsvH_t h, unsigned symId, double* vp );
100
+
101
+  // Return the value associated with a cell.
102
+  const char* cmCsvCellText(  cmCsvH_t h, unsigned row, unsigned col ); // Returns NULL on error.
103
+  int         cmCsvCellInt(   cmCsvH_t h, unsigned row, unsigned col ); // Returns INT_MAX on error.
104
+  unsigned    cmCsvCellUInt(  cmCsvH_t h, unsigned row, unsigned col ); // Returns UINT_MAX on error.
105
+  float       cmCsvCellFloat( cmCsvH_t h, unsigned row, unsigned col ); // Returns FLT_MAX  on error.
106
+  double      cmCsvCellDouble(cmCsvH_t h, unsigned row, unsigned col ); // Returns DBL_MAX on error.
107
+
108
+  // Insert a value into the internal symbol table.
109
+  unsigned   cmCsvInsertSymText(   cmCsvH_t h, const char* text );
110
+  unsigned   cmCsvInsertSymInt(    cmCsvH_t h, int v );
111
+  unsigned   cmCsvInsertSymUInt(   cmCsvH_t h, unsigned v );
112
+  unsigned   cmCsvInsertSymFloat(  cmCsvH_t h, float v );
113
+  unsigned   cmCsvInsertSymDouble( cmCsvH_t h, double v );  
114
+
115
+  // Set the value associated with a cell.
116
+  cmCsvRC_t  cmCsvSetCellText(   cmCsvH_t h, unsigned row, unsigned col, const char* text );
117
+  cmCsvRC_t  cmCsvSetCellInt(    cmCsvH_t h, unsigned row, unsigned col, int v );
118
+  cmCsvRC_t  cmCsvSetCellUInt(   cmCsvH_t h, unsigned row, unsigned col, unsigned v );
119
+  cmCsvRC_t  cmCsvSetCellFloat(  cmCsvH_t h, unsigned row, unsigned col, float v );
120
+  cmCsvRC_t  cmCsvSetCellDouble( cmCsvH_t h, unsigned row, unsigned col, double v );
121
+
122
+  // Insert a new row and column 0 cell just above the row assigned to 'row'.
123
+  // lexTId is an arbitrary id used by the application to set the value of 
124
+  // cmCsvCell_t.lexTId in the new cell. There are no constraints on its value.
125
+  //cmCsvRC_t  cmCsvInsertRowBefore(  cmCsvH_t h, unsigned row, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId );
126
+
127
+  cmCsvRC_t  cmCsvAppendRow( cmCsvH_t h, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId );
128
+
129
+  // Insert a new cell to the right of leftCellPtr.
130
+  // lexTId is an arbitrary id used by the application to set the value of 
131
+  // cmCsvCell_t.lexTId in the new cell. There are no constraints on its value.
132
+  cmCsvRC_t  cmCsvInsertColAfter(  cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId );
133
+
134
+  cmCsvRC_t  cmCsvInsertTextColAfter(   cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, const char* val, unsigned lexTId );
135
+  cmCsvRC_t  cmCsvInsertIntColAfter(    cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, int val,         unsigned lexTId );
136
+  cmCsvRC_t  cmCsvInsertUIntColAfter(   cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val,    unsigned lexTId );
137
+  cmCsvRC_t  cmCsvInsertFloatColAfter(  cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, float val,       unsigned lexTId );
138
+  cmCsvRC_t  cmCsvInsertDoubleColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, double val,      unsigned lexTId );
139
+
140
+  // Write the CSV object out to a file.
141
+  cmCsvRC_t  cmCsvWrite( cmCsvH_t h, const char* fn );
142
+
143
+  cmCsvRC_t  cmCsvPrint( cmCsvH_t h, unsigned rowCnt );
144
+
145
+#ifdef __cplusplus
146
+}
147
+#endif
148
+
149
+#endif

+ 22
- 0
cmCtx.c View File

@@ -0,0 +1,22 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+
7
+  void cmCtxSetup( 
8
+    cmCtx_t*         ctx, 
9
+    const cmChar_t*  title,
10
+    cmRptPrintFunc_t prtFunc, 
11
+    cmRptPrintFunc_t errFunc, 
12
+    void*            cbPtr, 
13
+    unsigned         guardByteCnt,
14
+    unsigned         alignByteCnt,
15
+    unsigned         mmFlags )
16
+  {
17
+    cmRptSetup(&ctx->rpt,prtFunc,errFunc,cbPtr);
18
+    cmErrSetup(&ctx->err,&ctx->rpt,title);
19
+    ctx->guardByteCnt = guardByteCnt;
20
+    ctx->alignByteCnt = alignByteCnt;
21
+    ctx->mmFlags      = mmFlags;
22
+  }

+ 53
- 0
cmCtx.h View File

@@ -0,0 +1,53 @@
1
+//{ 
2
+//(
3
+// cmCtx_t is used to hold application supplied cmRpt_t, cmErr_t and
4
+// other global values for easy distribution throughtout a cm based application.
5
+//
6
+//  Most the cm components need at least an application supplied cmRpt_t function
7
+//  to initialize their own internal cmErr_t error class.  Likewise classes which
8
+//  use a cmLHeapH_t based internal heap manager require application wide memory 
9
+// manager configuration information.  The cmCtx_t packages this information and
10
+// allows it to be easily distributed.  The applicaton and its constituent objects
11
+// then need only maintain and pass pointers to a single cmCtx_t object to have access to 
12
+// this all the global program information.
13
+//)
14
+
15
+#ifndef cmCtx_h
16
+#define cmCtx_h
17
+
18
+#ifdef __cplusplus
19
+extern "C" {
20
+#endif
21
+
22
+  //(
23
+
24
+  // cmCtx_t data type.
25
+typedef struct
26
+{
27
+  cmRpt_t  rpt;           //< Application supplied global reporter. This reporter is also use by \ref err.
28
+  cmErr_t  err;           //< Application error reporter which can be used to report errors prior to the client object being initialized to the point where it can use it's own cmErr_t.
29
+  unsigned guardByteCnt;  //< Guard byte count in use by \ref cmMallocDebug.h .
30
+  unsigned alignByteCnt;  //< Align byte count used by the \ref cmMallocDebug.h
31
+  unsigned mmFlags;       //< Initialization flags used by \ref cmMallocDebug.h.
32
+  void*    userDefPtr;    //< Application defined pointer.
33
+} cmCtx_t;
34
+
35
+  // cmCtx_t initialization function.
36
+  void cmCtxSetup( 
37
+    cmCtx_t*         ctx,         //< The cmCtx_t to initialize.
38
+    const cmChar_t*  title,       //<  The cmCtx_t error label. See cmErrSetup().
39
+    cmRptPrintFunc_t prtFunc,     //<  The printFunc() to assign to the cmCtx_t.rpt.
40
+    cmRptPrintFunc_t errFunc,     //<  The errFunc() to assign to cmCtx_t.rpt.
41
+    void*            cbPtr,       //<  Callback data to use with prtFunc() and errFunc().
42
+    unsigned         guardByteCnt,//<  Guard byte count used to configure \ref cmMallocDebug.h
43
+    unsigned         alignByteCnt,//<  Align byte count used to configure \ref cmMallocDebug.h
44
+    unsigned         mmFlags      //<  Initialization flags used to configure \ref cmMallocDebug.h
45
+                   );
46
+  //)
47
+  //}
48
+
49
+#ifdef __cplusplus
50
+}
51
+#endif
52
+
53
+#endif

+ 54
- 0
cmDocMain.h View File

@@ -0,0 +1,54 @@
1
+/*! \mainpage cm Manual
2
+ 
3
+  To modify this page edit cmDocMain.h
4
+
5
+  \section building Building
6
+  \subsection debug_mode Debug/Release Compile Mode 
7
+  By default the project builds in debug mode.  To build in release mode define NDEBUG 
8
+  on the compiler command line.  The existence of NDEBUG is tested in cmGlobal.h  and 
9
+  the value of the preprocessor variable #cmDEBUG_FL is set to 0 if NDEBUG was defined
10
+  and 1 otherwise.  Code which depends on the debug/release mode then tests the value of
11
+  #cmDEBUG_FL. 
12
+  
13
+ 
14
+  The cm library is a set of C routines for working audio signals.
15
+ 
16
+  \section foundation Foundation
17
+  \subsection mem Memory Management
18
+  \subsection output Output and Error Reporting
19
+  \subsection files File Management
20
+  \subsection cfg Program Configuration and Data
21
+
22
+  
23
+ 
24
+  \subsection step1 Step 1: Opening the box
25
+   
26
+ */
27
+
28
+
29
+/*!
30
+
31
+
32
+\defgroup base Base
33
+@{
34
+
35
+@}
36
+
37
+\defgroup rt Real-time
38
+@{
39
+@}
40
+
41
+\defgroup audio Audio
42
+@{
43
+
44
+@}
45
+
46
+\defgroup dsp Signal Processing
47
+@{
48
+@}
49
+
50
+\defgroup gr Graphics
51
+@{
52
+@}
53
+
54
+ */

+ 137
- 0
cmErr.c View File

@@ -0,0 +1,137 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+
6
+void cmErrSetup( cmErr_t* err, cmRpt_t* rpt, const cmChar_t* label )
7
+{
8
+  err->rpt   = rpt;
9
+  err->label = label;
10
+  err->rc    = cmOkRC;
11
+}
12
+
13
+void cmErrClone( cmErr_t* dstErr, const cmErr_t* srcErr )
14
+{ memcpy(dstErr,srcErr,sizeof(*dstErr)); }
15
+
16
+void _cmErrVMsg(cmErr_t* err, bool warnFl, cmRC_t rc, const cmChar_t* fmt, va_list vl )
17
+{
18
+  if( err->rpt == NULL )
19
+    return;
20
+
21
+  const cmChar_t* hdrFmt = warnFl ? "%s warning: " : "%s error: ";
22
+  const cmChar_t* codeFmt = " (RC:%i)";
23
+
24
+  int        n0 = snprintf( NULL,0,hdrFmt,cmStringNullGuard(err->label)); 
25
+  int        n1 = vsnprintf(NULL,0,fmt,vl);
26
+  int        n2 = snprintf( NULL,0,codeFmt,rc);
27
+  int        n  = n0+n1+n2+1;
28
+  cmChar_t s[n];
29
+  n0 =  snprintf(s,n,hdrFmt,cmStringNullGuard(err->label));
30
+  n0 += vsnprintf(s+n0,n-n0,fmt,vl);
31
+  n0 += snprintf(s+n0,n-n0,codeFmt,rc);
32
+  assert(n0 <= n );
33
+  cmRptErrorf(err->rpt,"%s\n",s);
34
+}
35
+
36
+void _cmErrMsg( cmErr_t* err, bool warnFl, cmRC_t rc, const cmChar_t* fmt, ... )
37
+{
38
+  va_list vl;
39
+  va_start(vl,fmt);
40
+  _cmErrVMsg(err,warnFl,rc,fmt,vl);
41
+  va_end(vl);
42
+}
43
+
44
+cmRC_t cmErrVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl )
45
+{ 
46
+  cmErrSetRC(err,rc);
47
+  _cmErrVMsg(err,false,rc,fmt,vl);
48
+  return rc;
49
+}
50
+
51
+  
52
+cmRC_t cmErrMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... )
53
+{
54
+  va_list vl;
55
+  va_start(vl,fmt);
56
+  rc = cmErrVMsg(err,rc,fmt,vl);
57
+  va_end(vl);
58
+  return rc;
59
+}
60
+
61
+
62
+void _cmErrSysVMsg(cmErr_t* err, bool warnFl, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl )
63
+{
64
+  const char* sysFmt = "\n System Error: (code:%i) %s.";
65
+  int         n0     = snprintf(NULL,0,sysFmt,sysErrCode,strerror(sysErrCode));
66
+  int         n1     = vsnprintf(NULL,0,fmt,vl);
67
+  int         n      = n0 + n1 + 1;
68
+  cmChar_t    s[n0+n1+1];
69
+
70
+  n0  = snprintf(s,n,sysFmt,sysErrCode,strerror(sysErrCode));
71
+  n0 += vsnprintf(s+n0,n-n0,fmt,vl);
72
+  assert( n0 <= n );
73
+  _cmErrMsg(err,warnFl,rc,s);  
74
+}
75
+
76
+cmRC_t cmErrVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl )
77
+{
78
+  cmErrSetRC(err,rc);
79
+  _cmErrSysVMsg(err,false,rc,sysErrCode,fmt,vl);
80
+  return rc;   
81
+}
82
+
83
+cmRC_t cmErrSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... )
84
+{
85
+  va_list vl;
86
+  va_start(vl,fmt);
87
+  rc = cmErrVSysMsg(err,rc,sysErrCode,fmt,vl);
88
+  va_end(vl);
89
+  return rc;
90
+}
91
+
92
+cmRC_t cmErrWarnVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl )
93
+{
94
+  _cmErrVMsg(err,true,rc,fmt,vl);
95
+  err->warnRC = rc;
96
+  return rc;
97
+}
98
+
99
+cmRC_t cmErrWarnMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... )
100
+{
101
+  va_list vl;
102
+  va_start(vl,fmt);
103
+  rc = cmErrWarnVMsg(err,rc,fmt,vl);
104
+  va_end(vl);
105
+  return rc;
106
+}
107
+
108
+cmRC_t cmErrWarnVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl )
109
+{
110
+  _cmErrSysVMsg(err,true,rc,sysErrCode,fmt,vl);
111
+  err->warnRC = rc;
112
+  return rc;
113
+}
114
+
115
+
116
+cmRC_t cmErrWarnSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... )
117
+{
118
+  va_list vl;
119
+  va_start(vl,fmt);
120
+  rc =  cmErrWarnVSysMsg(err,rc,sysErrCode,fmt,vl);
121
+  va_end(vl);
122
+  return rc;
123
+}
124
+
125
+  
126
+cmRC_t cmErrLastRC( cmErr_t* err )
127
+{ return err->rc; }
128
+
129
+cmRC_t cmErrSetRC( cmErr_t* err, cmRC_t rc )
130
+{
131
+  cmRC_t retVal = err->rc;
132
+  err->rc = rc;
133
+  return retVal;
134
+}
135
+
136
+cmRC_t cmErrClearRC( cmErr_t* err )
137
+{ return cmErrSetRC(err,cmOkRC); }

+ 84
- 0
cmErr.h View File

@@ -0,0 +1,84 @@
1
+//{ 
2
+//(
3
+// This class is used to format error messages and track the last error generated.
4
+//
5
+// Most of the cmHandle_t based classes use cmErr_t to format error messages with a 
6
+// title, maintain the last result code which indicated an error, and to hold
7
+// a cmRpt_t object to manage application supplied text printing callbacks. 
8
+//
9
+//)
10
+//
11
+
12
+#ifndef cmErr_h
13
+#define cmErr_h
14
+
15
+#ifdef __cplusplus
16
+extern "C" {
17
+#endif
18
+  
19
+  //(
20
+
21
+  typedef struct
22
+  {
23
+    cmRpt_t*        rpt;       //< Pointer to a cmRpt_t object which is used to direct error messages to an application supplied console.
24
+    const cmChar_t* label;     //< This field contains a pointer to a text label used to form the error message title.
25
+    cmRC_t          rc;        //< This is the last result code passed via one of the cmErrXXXMsg() functions.
26
+    cmRC_t          warnRC;    //< Last warning RC
27
+  } cmErr_t;
28
+
29
+  // Setup a cmErr_t record.
30
+  //
31
+  // Note that rpt and staticLabelStr must point to client supplied objects
32
+  // whose lifetime is at least that of this cmErr_t object. 
33
+  void cmErrSetup( cmErr_t* err, cmRpt_t* rpt, const cmChar_t* staticLabelStr );
34
+
35
+  // Duplicate a cmErr_t record.
36
+  void cmErrClone( cmErr_t* dstErr, const cmErr_t* srcErr );
37
+  
38
+  // Error Reporting functions:
39
+  // Functions to signal an error. The rc argument is generally specific to the 
40
+  // client class using the error.  See the kXXXRC enumerations in the handle based
41
+  // classes for examples of result codes.
42
+  cmRC_t cmErrMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... );
43
+  cmRC_t cmErrVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl );
44
+
45
+  
46
+  // Report Errors which contain accompanying system error codes.
47
+  // Use these functions when a system error (e.g. Unix errno) gives additional information
48
+  // about the source of the error. 
49
+  cmRC_t cmErrSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... );
50
+  cmRC_t cmErrVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl );
51
+
52
+  // Warning Reporting functions:
53
+  // Errors generally result in a task aborting. Warnings are informative but the task is
54
+  // expected to continue.
55
+  // Functions to signal a warning. The rc argument is generally specific to the 
56
+  // client class using the error.  See the kXXXRC enumerations in the handle based
57
+  // classes for examples of result codes.
58
+  cmRC_t cmErrWarnMsg( cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, ... );
59
+  cmRC_t cmErrWarnVMsg(cmErr_t* err, cmRC_t rc, const cmChar_t* fmt, va_list vl );
60
+
61
+  
62
+  // Report warnings which contain accompanying system error codes.
63
+  // Use these functions when a system error (e.g. Unix errno) gives additional information
64
+  // about the source of the error. 
65
+  cmRC_t cmErrWarnSysMsg( cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, ... );
66
+  cmRC_t cmErrWarnVSysMsg(cmErr_t* err, cmRC_t rc, cmSysErrCode_t sysErrCode, const cmChar_t* fmt, va_list vl );
67
+
68
+  // Return the last recorded RC.
69
+  cmRC_t cmErrLastRC( cmErr_t* err );
70
+
71
+  // Return the last recorded RC and set it to a new value.
72
+  cmRC_t cmErrSetRC( cmErr_t* err, cmRC_t rc );
73
+
74
+  // Return the last recorded RC and set it to cmOkRC.
75
+  cmRC_t cmErrClearRC( cmErr_t* err );
76
+   
77
+  //)
78
+  //}
79
+
80
+#ifdef __cplusplus
81
+}
82
+#endif
83
+
84
+#endif

+ 2455
- 0
cmFeatFile.c
File diff suppressed because it is too large
View File


+ 279
- 0
cmFeatFile.h View File

@@ -0,0 +1,279 @@
1
+/// \file cmFeatFile.h
2
+/// \brief Audio file acoustic feature analyzer and accompanying file reader.
3
+///
4
+///
5
+
6
+#ifndef cmFeatFile_h
7
+#define cmFeatFile_h
8
+
9
+#ifdef __cplusplus
10
+extern "C" {
11
+#endif
12
+
13
+
14
+
15
+  /// Result codes for all functions in cmFeatFile.h
16
+  enum
17
+  {
18
+    kOkFtRC = cmOkRC,
19
+    kCfgParseFailFtRC,
20
+    kFileSysFailFtRC,
21
+    kJsonFailFtRC,
22
+    kDspProcFailFtRC,
23
+    kDirCreateFailFtRC,
24
+    kFileNotFoundFtRC,
25
+    kAudioFileOpenFailFtRC,
26
+    kFrameFileFailFtRC,
27
+    kChIdxInvalidFtRC,
28
+    kParamRangeFtRC,
29
+    kParamErrorFtRC,
30
+    kFrameWriteFailFtRC,
31
+    kEofFtRC,
32
+    kPlviewFailFtRC,
33
+    kSerialFailFtRC,
34
+    kInvalidFeatIdFtRC,
35
+    kFileFailFtRC,
36
+    kInvalidFrmIdxFtRC
37
+  };
38
+
39
+  /// Feature Id's
40
+  enum
41
+  {
42
+    kInvalidFtId,     ///< 0  
43
+    kAmplFtId,        ///< 1 Fourier transform amplitude 
44
+    kDbAmplFtId,      ///< 2 Fourier transform decibel
45
+    kPowFtId,         ///< 3 Fourier transform power
46
+    kDbPowFtId,       ///< 4 Fourier transform power decibel
47
+    kPhaseFtId,       ///< 5 Fourier transform phase (not unwrapped)
48
+    kBfccFtId,        ///< 6 Bark Frequency Cepstral Coeffcients
49
+    kMfccFtId,        ///< 7 Mel Frequency Cepstral Coefficients
50
+    kCepsFtId,        ///< 8 Cepstral Coefficients
51
+    kConstQFtId,      ///< 9 Constant-Q transform
52
+    kLogConstQFtId,   ///< 10 Log Constant-Q transform
53
+    kRmsFtId,         ///< 11 Root means square of the audio signal
54
+    kDbRmsFtId,       ///< 12 RMS in decibels                         
55
+
56
+    kD1AmplFtId,      ///< 13 1st order difference over time of the Fourier transform amplitude              
57
+    kD1DbAmplFtId,    ///< 14 1st order difference over time of the Fourier transform decibel                
58
+    kD1PowFtId,       ///< 15 1st order difference over time of the Fourier transform power                  
59
+    kD1DbPowFtId,     ///< 16 1st order difference over time of the Fourier transform power decibel          
60
+    kD1PhaseFtId,     ///< 17 1st order difference over time of the Fourier transform phase (not unwrapped)  
61
+    kD1BfccFtId,      ///< 18 1st order difference over time of the Bark Frequency Cepstral Coeffcients      
62
+    kD1MfccFtId,      ///< 19 1st order difference over time of the Mel Frequency Cepstral Coefficients      
63
+    kD1CepsFtId,      ///< 20 1st order difference over time of the Cepstral Coefficients                    
64
+    kD1ConstQFtId,    ///< 21 1st order difference over time of the Constant-Q transform                     
65
+    kD1LogConstQFtId, ///< 22 1st order difference over time of the Log Constant-Q transform                
66
+    kD1RmsFtId,       ///< 23 1st order difference over time of the Root means square of the audio signal   
67
+    kD1DbRmsFtId,     ///< 24 1st order difference over time of the RMS in decibels                         
68
+
69
+  };
70
+
71
+  /// User defined feature parameters
72
+  typedef struct
73
+  {
74
+    unsigned id;         ///< feature id
75
+    unsigned cnt;        ///< length of feature vector
76
+    bool     normFl;     ///< normalize this feature 
77
+    bool     enableFl;   ///< true if this feature is enabled
78
+  } cmFtAttr_t;
79
+
80
+
81
+  /// Skip input audio range record
82
+  typedef struct
83
+  {
84
+    unsigned smpIdx;  ///< Index of first sample to skip
85
+    unsigned smpCnt;  ///< Count of successive samples to skip.
86
+  } cmFtSkip_t;
87
+
88
+
89
+  /// Analysis parameters
90
+  typedef struct
91
+  {
92
+    const char*        audioFn;               ///< Audio file name.
93
+    const char*        featFn;                ///< Feature file name.
94
+    unsigned           chIdx;                 ///< Audio file channel index
95
+    cmReal_t           wndMs;                 ///< Length of the analysis window in milliseconds.
96
+    unsigned           hopFact;               ///< Analysis window overlap factor 1 = 1:1 2=2:1 ...
97
+    bool               normAudioFl;           ///< Normalize the audio over the length of the audio file
98
+    cmMidiByte_t       constQMinPitch;        ///< Used to determine the base const-q octave.
99
+    cmMidiByte_t       constQMaxPitch;        ///< Used to determine the maximum const-q frequency of interest.
100
+    unsigned           constQBinsPerOctave;   ///< Bands per const-q octave.
101
+    unsigned           onsetMedFiltWndSmpCnt; ///< Complex onset median filter 
102
+    cmReal_t           onsetThreshold;        ///< Complex onset threshold
103
+    cmReal_t           minDb;                 ///< Fourier Transform magnitude values below minDb are set to minDb.
104
+    cmReal_t           floorThreshDb;         ///< Frames with an RMS below this value will be skipped
105
+    cmFtSkip_t*        skipArray;             ///< skipArray[skipCnt] user defined sample skip ranges
106
+    unsigned           skipCnt;               ///< Count of records in skipArray[].
107
+    cmFtAttr_t*        attrArray;             ///< attrArray[attrCnt] user defined parameter array
108
+    unsigned           attrCnt;               ///< Count of records in attrArray[].
109
+  } cmFtParam_t;
110
+
111
+
112
+  /// Feature summary information
113
+  typedef struct 
114
+  {
115
+    unsigned    id;   ///< feature id (same as associated cmFtAttr.id)
116
+    unsigned    cnt;  ///< length of each feature vector (same as associated cmFtAttr.cnt)
117
+
118
+    /// The raw feature summary values are calculated prior to normalization.
119
+    cmReal_t* rawMinV;  ///< Vector of min value over time for each feature element.
120
+    cmReal_t* rawMaxV;  ///< Vector of max value over time for each feature element.
121
+    cmReal_t* rawAvgV;  ///< Vector of avg value over time for each feature element.
122
+    cmReal_t* rawSdvV;  ///< Vector of standard deviation values over time for each feature element.
123
+    cmReal_t  rawMin;   ///< Min value of all values for this feature. Equivalent to min(rawMinV).
124
+    cmReal_t  rawMax;   ///< Max value of all values for this feature. Equivalent to max(rawMaxV).
125
+
126
+    /// normalized feature summary values
127
+    cmReal_t* normMinV; ///< Vector of min value over time for each feature element.
128
+    cmReal_t* normMaxV; ///< Vector of max value over time for each feature element.
129
+    cmReal_t* normAvgV; ///< Vector of avg value over time for each feature element.
130
+    cmReal_t* normSdvV; ///< Vector of standard deviation values over time for each feature element.
131
+    cmReal_t  normMin;  ///< Min value of all values for this feature. Equivalent to min(normMinV).
132
+    cmReal_t  normMax;  ///< Max value of all values for this feature. Equivalent to max(rawMaxV).
133
+
134
+  } cmFtSumm_t;
135
+
136
+  /// Feature file info record
137
+  typedef struct
138
+  {
139
+    unsigned    frmCnt;         ///< count of frames in the file
140
+    cmReal_t    srate;          ///< audio sample rate
141
+    unsigned    smpCnt;         ///< audio sample count
142
+    unsigned    fftSmpCnt;      ///< FFT window length (always power of 2)
143
+    unsigned    hopSmpCnt;      ///< audio sample hop count
144
+    unsigned    binCnt;         ///< FFT bin count (always fftSmpCnt/2 + 1)
145
+    unsigned    skipFrmCnt;     ///< count of frames skipped based on user skip array 
146
+    unsigned    floorFrmCnt;    ///< count of frames skipped because below floorThreshDb
147
+    cmFtParam_t param;          ///< analysis parameter record used to form this feature file
148
+    cmFtSumm_t* summArray;      ///< summArray[ param.attrCnt ] feature summary information
149
+  } cmFtInfo_t;
150
+
151
+  /// Data structure returned by cmFtReaderAdvance(). 
152
+  typedef struct
153
+  {
154
+    unsigned smpIdx;  ///< The audio signal sample index this frames information is based on.
155
+    unsigned frmIdx;  ///< The frame index relative to other frames in this feature file.
156
+  } cmFtFrameDesc_t;
157
+
158
+  typedef cmHandle_t cmFtH_t;      ///< Analyzer handle
159
+  typedef cmHandle_t cmFtFileH_t;  ///< Feature file handle.
160
+  typedef unsigned   cmFtRC_t;     ///< Result code type used by all functions in cmFeatFile.h.
161
+
162
+  extern cmFtH_t     cmFtNullHandle;      ///< A NULL handle useful for indicating an uninitialized analyzer.
163
+  extern cmFtFileH_t cmFtFileNullHandle;  ///< A NULL handle useful for indicating an uninitialized feature file.
164
+
165
+
166
+  /// Given a feature type id return the associated label.
167
+  const char*     cmFtFeatIdToLabel( unsigned featId );
168
+
169
+  /// Given a feature type label return the associated id.
170
+  unsigned        cmFtFeatLabelToId( const char* label );
171
+
172
+  /// \name Feature Analyzer Related functions
173
+  ///@{ 
174
+
175
+  /// Initialize the feature analyzer. The memory manager and file system must
176
+  /// be initialized (cmMdInitialize(), cmFsInitialize()) prior to calling this function.
177
+  cmFtRC_t        cmFtInitialize( cmFtH_t* hp, cmCtx_t* ctx );
178
+
179
+  /// Finalize a feature analyzer.
180
+  cmFtRC_t        cmFtFinalize( cmFtH_t* h );
181
+
182
+  /// Return true if the handle represents an initialized feature analyzer.
183
+  bool            cmFtIsValid( cmFtH_t  h );
184
+
185
+  /// Parse a JSON file containing a set of analysis parameters. 
186
+  cmFtRC_t        cmFtParse( cmFtH_t  h, const char* cfgFn );
187
+
188
+  /// Run the analyzer.
189
+  cmFtRC_t        cmFtAnalyze( cmFtH_t h );
190
+
191
+  /// If cmFtAnalyze() is being run in a seperate thread this function
192
+  /// can be used to access the analyzers progress.
193
+  const char*     cmFtAnalyzeProgress( cmFtH_t h, unsigned* passPtr, cmReal_t* percentPtr );  
194
+
195
+  ///@}
196
+  
197
+  /// \name Feature File Related Functions
198
+  ///@{
199
+
200
+  /// Open a feature file.
201
+  /// Note that inforPtrPtr is optional and will be ignored if it is set to NULL.
202
+  cmFtRC_t        cmFtReaderOpen(    cmFtH_t h, cmFtFileH_t* hp, const char* featFn, const cmFtInfo_t** infoPtrPtr );
203
+
204
+  /// Close a feature file.
205
+  cmFtRC_t        cmFtReaderClose(   cmFtFileH_t* hp );
206
+
207
+  /// Return true if the handle reprents an open feature file.
208
+  bool            cmFtReaderIsValid( cmFtFileH_t h );
209
+
210
+  /// Return the count of features types this file contains.
211
+  unsigned        cmFtReaderFeatCount( cmFtFileH_t h );
212
+
213
+  /// Return the feature type id associated with the specified index.
214
+  unsigned        cmFtReaderFeatId( cmFtFileH_t h, unsigned index );
215
+
216
+  /// Reset the current file location to the first frame but do not load it.
217
+  /// The next call to cmFtReadAdvance() will load the next frame.
218
+  cmFtRC_t        cmFtReaderRewind(  cmFtFileH_t h );
219
+
220
+  /// Make frmIdx the current file location.
221
+  cmFtRC_t        cmFtReaderSeek(    cmFtFileH_t h, unsigned frmIdx );
222
+
223
+  /// Load the current frame, advance the current file position, and return
224
+  /// a pointer to a cmFtFrameDesc_t record for the loaded frame.
225
+  /// Returns kEofFtRC upon reaching end of file. 
226
+  /// The frameDescPtr is optional.
227
+  cmFtRC_t        cmFtReaderAdvance( cmFtFileH_t h, cmFtFrameDesc_t* frameDescPtr );
228
+
229
+  /// Returns a pointer to a data matrix in the feature identified by featId in the current feature frame. 
230
+  cmReal_t*       cmFtReaderData(    cmFtFileH_t h, unsigned featId, unsigned* cntPtr );
231
+
232
+  /// Copy the contents of a given set of frames into buf[frmCnt*elePerFrmCnt].
233
+  cmFtRC_t        cmFtReaderCopy(    cmFtFileH_t h, unsigned featId, unsigned frmIdx, cmReal_t* buf, unsigned frmCnt, unsigned elePerFrmCnt, unsigned* outEleCntPtr );  
234
+
235
+  /// Data structure used to specify multiple features for use by cmFtReaderMultiSetup().
236
+  typedef struct
237
+  {
238
+    unsigned featId; ///< Feature id of feature to include in the feature vector
239
+    unsigned cnt;    ///< Set to count of feat ele's for this feat. Error if greater than avail. Set to -1 to use all avail ele's.
240
+                     ///  returned with actual count used
241
+
242
+    unsigned id0;    ///< Ignored on input. Used internally by cmFtReaderXXX() 
243
+    unsigned id1;    ///< Ignored on input. Used internally by cmFtReaderXXX() 
244
+  } cmFtMulti_t;
245
+  
246
+  /// Setup an array of cmFtMulti_t records.  The cmFtMulti_t array
247
+  /// used by cmFtReaderMulitData() must be initialized by this function.
248
+  cmFtRC_t        cmFtReaderMultiSetup(  cmFtFileH_t h, cmFtMulti_t* multiArray, unsigned multiCnt, unsigned* featVectEleCntPtr ); 
249
+
250
+  /// Fill outV[outN] with a consecutive data from the features specified in the cmFtMulti_t array.
251
+  /// Use cmFtReaderMultiSetup() to configure the cmFtMulti_t array prior to calling this function.
252
+  cmFtRC_t        cmFtReaderMultiData(   cmFtFileH_t h, const cmFtMulti_t* multiArray, unsigned multiCnt, cmReal_t* outV, unsigned outN ); 
253
+
254
+  /// Report summary information for the specified feature.
255
+  cmFtRC_t        cmFtReaderReport(  cmFtFileH_t h, unsigned featId );
256
+
257
+  /// Identical to cmFtReaderReport() except the feature file is identified from a file name rather than an open cmFtFileH_t.
258
+  cmFtRC_t        cmFtReaderReportFn(  cmFtH_t h, const cmChar_t* fn, unsigned featId );
259
+
260
+  /// Report feature data for the specified set of feature frames.
261
+  cmFtRC_t        cmFtReaderReportFeature( cmFtFileH_t h, unsigned featId, unsigned frmIdx, unsigned frmCnt );
262
+
263
+  /// Write a feature into a binary file.  
264
+  /// Set 'frmCnt' to the cmInvalidCnt to include all frames past frmIdx.
265
+  /// The first three unsigned values in the output file
266
+  /// contain the row count, maximum column count, and the count of bytes in each data element (4=float,8=double). 
267
+  /// Each row of the file begins with the count of elements in the row and is followed by a data array.
268
+  cmFtRC_t        cmFtReaderToBinary( cmFtFileH_t h, unsigned featId, unsigned frmIdx, unsigned frmCnt, const cmChar_t* outFn ); 
269
+
270
+  /// Identical to cmFtReaderToBinary() except it takes a feature file name instead of a file handle.
271
+  cmFtRC_t        cmFtReaderToBinaryFn( cmFtH_t h, const cmChar_t* fn, unsigned featId, unsigned frmIdx, unsigned frmCnt, const cmChar_t* outFn ); 
272
+
273
+  ///@}
274
+  
275
+#ifdef __cplusplus
276
+}
277
+#endif
278
+
279
+#endif

+ 555
- 0
cmFile.c View File

@@ -0,0 +1,555 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmFile.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include <sys/stat.h>
9
+cmFileH_t cmFileNullHandle = { NULL };
10
+
11
+typedef struct 
12
+{
13
+  FILE*     fp;
14
+  cmErr_t   err;
15
+  cmChar_t* fnStr;
16
+} cmFile_t;
17
+
18
+cmFile_t* _cmFileHandleToPtr( cmFileH_t h )
19
+{
20
+  cmFile_t* p = (cmFile_t*)h.h;
21
+  assert(p != NULL);
22
+  return p;
23
+}
24
+
25
+cmFileRC_t _cmFileError( cmFile_t* p, cmFileRC_t rc, int errNumb, const cmChar_t* msg  )
26
+{
27
+  if(errNumb == 0)
28
+    rc = cmErrMsg(&p->err,rc,"%s on file '%s'",msg,p->fnStr);
29
+  else
30
+    rc = cmErrMsg(&p->err,rc,"%s on file '%s'\nSystem Msg:%s",msg,p->fnStr,strerror(errNumb));
31
+
32
+  return rc;
33
+}
34
+
35
+cmFileRC_t cmFileOpen(    cmFileH_t* hp, const cmChar_t* fn, enum cmFileOpenFlags_t flags, cmRpt_t* rpt )
36
+{
37
+  char mode[] = "/0/0/0";
38
+  cmFile_t* p = NULL;
39
+  cmErr_t err;
40
+  cmFileRC_t rc;
41
+
42
+  if((rc = cmFileClose(hp)) != kOkFileRC )
43
+    return rc;
44
+
45
+  cmErrSetup(&err,rpt,"File");
46
+
47
+  hp->h = NULL;
48
+
49
+  if( cmIsFlag(flags,kReadFileFl) )
50
+    mode[0]='r';
51
+  else
52
+    if( cmIsFlag(flags,kWriteFileFl) )
53
+      mode[0]='w';
54
+    else
55
+      if( cmIsFlag(flags,kAppendFileFl) )
56
+        mode[0]='a';
57
+      else
58
+        cmErrMsg(&err,kInvalidFlagFileRC,"File open flags must contain 'kReadFileFl','kWriteFileFl', or 'kAppendFileFl'.");
59
+  
60
+  if( cmIsFlag(flags,kUpdateFileFl) )
61
+    mode[1]='+';
62
+
63
+  if( fn == NULL )
64
+    return cmErrMsg(&err,kObjAllocFailFileRC,"File object allocation failed due to empty file name.");
65
+
66
+  unsigned byteCnt = sizeof(cmFile_t) + strlen(fn) + 1;
67
+
68
+  if((p = (cmFile_t*)cmMemMallocZ(byteCnt)) == NULL )
69
+    return cmErrMsg(&err,kObjAllocFailFileRC,"File object allocation failed for file '%s'.",cmStringNullGuard(fn));
70
+
71
+  cmErrClone(&p->err,&err);
72
+
73
+  p->fnStr = (cmChar_t*)(p+1);
74
+  strcpy(p->fnStr,fn);
75
+  
76
+
77
+  errno = 0;
78
+  if((p->fp = fopen(fn,mode)) == NULL )
79
+  {
80
+    cmFileRC_t rc = _cmFileError(p,kOpenFailFileRC,errno,"File open failed");
81
+    cmMemFree(p);
82
+    return rc;
83
+  }
84
+ 
85
+  hp->h    = p;
86
+
87
+  return kOkFileRC;
88
+}
89
+
90
+cmFileRC_t cmFileClose(   cmFileH_t* hp )
91
+{
92
+  if( cmFileIsValid(*hp) == false )
93
+    return kOkFileRC;
94
+
95
+  cmFile_t* p = _cmFileHandleToPtr(*hp);
96
+  
97
+  errno = 0;
98
+  if( p->fp != NULL )
99
+    if( fclose(p->fp) != 0 )
100
+      return _cmFileError(p,kCloseFailFileRC,errno,"File close failed");
101
+  
102
+  cmMemFree(p);
103
+  hp->h = NULL;
104
+
105
+  return kOkFileRC;
106
+}
107
+
108
+bool       cmFileIsValid( cmFileH_t h )
109
+{ return h.h != NULL; }
110
+
111
+cmFileRC_t cmFileRead(    cmFileH_t h, void* buf, unsigned bufByteCnt )
112
+{
113
+  cmFile_t* p = _cmFileHandleToPtr(h);
114
+  
115
+  errno = 0;
116
+  if( fread(buf,bufByteCnt,1,p->fp) != 1 )
117
+    return _cmFileError(p,kReadFailFileRC,errno,"File read failed");
118
+
119
+  return kOkFileRC;
120
+}
121
+
122
+cmFileRC_t cmFileWrite(   cmFileH_t h, const void* buf, unsigned bufByteCnt )
123
+{
124
+  cmFile_t* p = _cmFileHandleToPtr(h);
125
+  
126
+  errno = 0;
127
+  if( fwrite(buf,bufByteCnt,1,p->fp) != 1 )
128
+    return _cmFileError(p,kWriteFailFileRC,errno,"File write failed");
129
+
130
+  return kOkFileRC;
131
+}
132
+
133
+cmFileRC_t cmFileSeek(    cmFileH_t h, enum cmFileSeekFlags_t flags, int offsByteCnt )
134
+{
135
+  cmFile_t* p = _cmFileHandleToPtr(h);
136
+  unsigned fileflags = 0;
137
+
138
+  if( cmIsFlag(flags,kBeginFileFl) )
139
+    fileflags = SEEK_SET;
140
+  else
141
+    if( cmIsFlag(flags,kCurFileFl) )
142
+      fileflags = SEEK_CUR;
143
+    else
144
+      if( cmIsFlag(flags,kEndFileFl) )
145
+        fileflags = SEEK_END;
146
+      else
147
+        return cmErrMsg(&p->err,kInvalidFlagFileRC,"Invalid file seek flag on '%s'.",p->fnStr);
148
+  
149
+  errno = 0;
150
+  if( fseek(p->fp,offsByteCnt,fileflags) != 0 )
151
+    return _cmFileError(p,kSeekFailFileRC,errno,"File seek failed");
152
+
153
+  return kOkFileRC;
154
+}
155
+
156
+cmFileRC_t cmFileTell( cmFileH_t h, long* offsPtr )
157
+{
158
+  assert( offsPtr != NULL );
159
+  *offsPtr     = -1;
160
+  cmFile_t* p  = _cmFileHandleToPtr(h);
161
+  errno        = 0;
162
+  if((*offsPtr = ftell(p->fp)) == -1)
163
+    return _cmFileError(p,kTellFailFileRC,errno,"File tell failed");
164
+  return kOkFileRC;
165
+}
166
+
167
+
168
+bool       cmFileEof(     cmFileH_t h )
169
+{ return feof( _cmFileHandleToPtr(h)->fp ) != 0; }
170
+
171
+
172
+unsigned   cmFileByteCount(  cmFileH_t h )
173
+{
174
+  struct stat sr;
175
+  int         f;
176
+  cmFile_t*   p = _cmFileHandleToPtr(h);
177
+  const cmChar_t errMsg[] = "File byte count request failed.";
178
+
179
+  errno = 0;
180
+
181
+  if((f = fileno(p->fp)) == -1)
182
+  {
183
+    _cmFileError(p,kHandleInvalidFileRC,errno,errMsg);
184
+    return 0;
185
+  }
186
+  
187
+  if(fstat(f,&sr) == -1)
188
+  {
189
+    _cmFileError(p,kStatFailFileRC,errno,errMsg);
190
+    return 0;
191
+  }
192
+
193
+  return sr.st_size;
194
+}
195
+
196
+const cmChar_t* cmFileName( cmFileH_t h )
197
+{
198
+  cmFile_t* p = _cmFileHandleToPtr(h);
199
+  return p->fnStr;
200
+}
201
+
202
+cmFileRC_t cmFileFnWrite( const cmChar_t* fn, cmRpt_t* rpt, const void* buf, unsigned bufByteCnt )
203
+{
204
+  cmFileH_t h = cmFileNullHandle;
205
+  cmFileRC_t rc;
206
+
207
+  if((rc = cmFileOpen(&h,fn,kWriteFileFl,rpt)) != kOkFileRC )
208
+    goto errLabel;
209
+
210
+  rc = cmFileWrite(h,buf,bufByteCnt);
211
+
212
+ errLabel:
213
+  cmFileClose(&h);
214
+  
215
+  return rc;
216
+}
217
+
218
+cmChar_t*  _cmFileToBuf( cmFileH_t h, unsigned nn, unsigned* bufByteCntPtr )
219
+{
220
+  errno = 0;
221
+
222
+  unsigned  n   = cmFileByteCount(h);
223
+  cmChar_t* buf = NULL;
224
+  cmFile_t*   p = _cmFileHandleToPtr(h);
225
+ 
226
+
227
+  // if the file size calculation is ok
228
+  if( errno != 0 )
229
+  {
230
+    _cmFileError(p,kBufAllocFailFileRC,errno,"Invalid file buffer length.");
231
+    goto errLabel;
232
+  }
233
+  
234
+  // allocate the read target buffer
235
+  if((buf = cmMemAlloc(cmChar_t,n+nn)) == NULL)
236
+  {
237
+    _cmFileError(p,kBufAllocFailFileRC,0,"Read buffer allocation failed.");
238
+    goto errLabel;
239
+  }
240
+
241
+  // read the file
242
+  if( cmFileRead(h,buf,n) != kOkFileRC )
243
+    goto errLabel;
244
+
245
+  // zero memory after the file data
246
+  memset(buf+n,0,nn);
247
+
248
+  if( bufByteCntPtr != NULL )
249
+    *bufByteCntPtr = n;
250
+
251
+  return buf;
252
+
253
+ errLabel:
254
+  if( bufByteCntPtr != NULL )
255
+    *bufByteCntPtr = 0;
256
+
257
+  cmMemFree(buf);
258
+
259
+  return NULL;
260
+    
261
+}
262
+
263
+cmChar_t* _cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned nn, unsigned* bufByteCntPtr  )
264
+{
265
+  cmFileH_t h = cmFileNullHandle;
266
+  cmChar_t* buf = NULL;
267
+
268
+  if( cmFileOpen(&h,fn,kReadFileFl | kBinaryFileFl,rpt) != kOkFileRC )
269
+    goto errLabel;
270
+
271
+  buf = _cmFileToBuf(h,nn,bufByteCntPtr);
272
+
273
+ errLabel:
274
+  cmFileClose(&h);
275
+  
276
+  return buf;
277
+}
278
+
279
+cmChar_t*  cmFileToBuf( cmFileH_t h, unsigned* bufByteCntPtr )
280
+{ return _cmFileToBuf(h,0,bufByteCntPtr); }
281
+
282
+cmChar_t*  cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr )
283
+{ return _cmFileFnToBuf(fn,rpt,0,bufByteCntPtr); }
284
+
285
+cmChar_t*  cmFileToStr( cmFileH_t h, unsigned* bufByteCntPtr )
286
+{ return _cmFileToBuf(h,1,bufByteCntPtr); }
287
+
288
+cmChar_t*  cmFileFnToStr( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr )
289
+{ return _cmFileFnToBuf(fn,rpt,1,bufByteCntPtr); }
290
+
291
+cmFileRC_t cmFileLineCount( cmFileH_t h, unsigned* lineCntPtr )
292
+{
293
+  cmFileRC_t rc      = kOkFileRC;
294
+  cmFile_t*  p       = _cmFileHandleToPtr(h);
295
+  unsigned   lineCnt = 0;
296
+  long       offs;
297
+  int        c;
298
+
299
+
300
+  assert( lineCntPtr != NULL );
301
+  *lineCntPtr = 0;
302
+
303
+  if((rc = cmFileTell(h,&offs)) != kOkFileRC )
304
+    return rc;
305
+
306
+  errno = 0;
307
+
308
+  while(1)
309
+  {
310
+    c = fgetc(p->fp);
311
+
312
+    if( c == EOF ) 
313
+    {
314
+      if( errno )
315
+        rc =_cmFileError(p,kReadFailFileRC,errno,"File read char failed");
316
+      else
317
+        ++lineCnt; // add one in case the last line isn't terminated with a '\n'. 
318
+
319
+      break;
320
+    }
321
+
322
+    // if an end-of-line was encountered
323
+    if( c == '\n' )
324
+      ++lineCnt;
325
+
326
+  }
327
+
328
+  if((rc = cmFileSeek(h,kBeginFileFl,offs)) != kOkFileRC )
329
+    return rc;
330
+
331
+  *lineCntPtr = lineCnt;
332
+
333
+  return rc;
334
+}
335
+
336
+cmFileRC_t _cmFileGetLine( cmFile_t* p, cmChar_t* buf, unsigned* bufByteCntPtr )
337
+{
338
+  // fgets() reads up to n-1 bytes into buf[]
339
+  if( fgets(buf,*bufByteCntPtr,p->fp) == NULL )
340
+  {
341
+    // an read error or EOF condition occurred
342
+    *bufByteCntPtr = 0;
343
+
344
+    if( !feof(p->fp ) )
345
+      return _cmFileError(p,kReadFailFileRC,errno,"File read line failed");
346
+    
347
+    return kReadFailFileRC;
348
+  }
349
+
350
+  return kOkFileRC;
351
+}
352
+
353
+cmFileRC_t cmFileGetLine( cmFileH_t h, cmChar_t* buf, unsigned* bufByteCntPtr )
354
+{
355
+  assert( bufByteCntPtr != NULL );
356
+  cmFile_t* p  = _cmFileHandleToPtr(h);
357
+  unsigned  tn = 128;
358
+  cmChar_t  t[ tn ];
359
+  unsigned  on = *bufByteCntPtr;
360
+  long      offs;
361
+  cmFileRC_t rc;
362
+
363
+  // store the current file offset
364
+  if((rc = cmFileTell(h,&offs)) != kOkFileRC )
365
+    return rc;
366
+  
367
+  // if no buffer was given then use t[]
368
+  if( buf == NULL || *bufByteCntPtr == 0 )
369
+  {
370
+    *bufByteCntPtr = tn;
371
+    buf            = t;
372
+  }
373
+
374
+  // fill the buffer from the current line 
375
+  if((rc = _cmFileGetLine(p,buf,bufByteCntPtr)) != kOkFileRC )
376
+    return rc;
377
+
378
+  // get length of the string  in the buffer
379
+  // (this is one less than the count of bytes written to the buffer)
380
+  unsigned n = strlen(buf);
381
+
382
+  // if the provided buffer was large enough to read the entire string 
383
+  if( on > n+1 )
384
+  {
385
+    //*bufByteCntPtr = n+1;
386
+    return kOkFileRC;
387
+  }
388
+
389
+  //
390
+  // the provided buffer was not large enough 
391
+  //
392
+
393
+  // m tracks the length of the string
394
+  unsigned m = n;
395
+
396
+  while( n+1 == *bufByteCntPtr )
397
+  {
398
+    // fill the buffer from the current line
399
+    if((rc = _cmFileGetLine(p,buf,bufByteCntPtr)) != kOkFileRC )
400
+      return rc;
401
+
402
+    n = strlen(buf);
403
+    m += n;
404
+  }
405
+
406
+  // restore the original file offset
407
+  if((rc = cmFileSeek(h,kBeginFileFl,offs)) != kOkFileRC )
408
+    return rc;
409
+
410
+  // add 1 for /0, 1 for /n and 1 to detect buf-too-short
411
+  *bufByteCntPtr = m+3;
412
+  
413
+  return kBufTooSmallFileRC;
414
+  
415
+}
416
+
417
+cmFileRC_t cmFileGetLineAuto( cmFileH_t h, cmChar_t** bufPtrPtr, unsigned* bufByteCntPtr )
418
+{
419
+  cmFileRC_t rc         = kOkFileRC;
420
+  bool       fl         = true;
421
+  cmChar_t*  buf        = *bufPtrPtr;
422
+
423
+  *bufPtrPtr = NULL;
424
+
425
+  while(fl)
426
+  {
427
+    fl         = false;
428
+
429
+    switch( rc = cmFileGetLine(h,buf,bufByteCntPtr) )
430
+    {
431
+      case kOkFileRC:
432
+        {
433
+          *bufPtrPtr = buf;
434
+        }
435
+        break;
436
+        
437
+      case kBufTooSmallFileRC:
438
+        buf = cmMemResizeZ(cmChar_t,buf,*bufByteCntPtr);
439
+        fl  = true;
440
+        break;
441
+
442
+      default:
443
+        cmMemFree(buf);
444
+        break;
445
+    }
446
+  }
447
+
448
+  
449
+
450
+  return rc;
451
+}
452
+
453
+cmFileRC_t cmFileReadChar(   cmFileH_t h, char*           buf, unsigned cnt ) 
454
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
455
+
456
+cmFileRC_t cmFileReadUChar(  cmFileH_t h, unsigned char*  buf, unsigned cnt ) 
457
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
458
+
459
+cmFileRC_t cmFileReadShort(  cmFileH_t h, short*          buf, unsigned cnt ) 
460
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
461
+
462
+cmFileRC_t cmFileReadUShort( cmFileH_t h, unsigned short* buf, unsigned cnt ) 
463
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
464
+
465
+cmFileRC_t cmFileReadLong(   cmFileH_t h, long*           buf, unsigned cnt ) 
466
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
467
+
468
+cmFileRC_t cmFileReadULong(  cmFileH_t h, unsigned long*  buf, unsigned cnt ) 
469
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
470
+
471
+cmFileRC_t cmFileReadInt(    cmFileH_t h, int*            buf, unsigned cnt ) 
472
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
473
+
474
+cmFileRC_t cmFileReadUInt(   cmFileH_t h, unsigned int*   buf, unsigned cnt ) 
475
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
476
+
477
+cmFileRC_t cmFileReadFloat(  cmFileH_t h, float*          buf, unsigned cnt ) 
478
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
479
+
480
+cmFileRC_t cmFileReadDouble( cmFileH_t h, double*         buf, unsigned cnt ) 
481
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
482
+
483
+cmFileRC_t cmFileReadBool(   cmFileH_t h, bool*           buf, unsigned cnt ) 
484
+{ return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
485
+
486
+
487
+
488
+cmFileRC_t cmFileWriteChar(   cmFileH_t h, const char*           buf, unsigned cnt )
489
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
490
+
491
+cmFileRC_t cmFileWriteUChar(  cmFileH_t h, const unsigned char*  buf, unsigned cnt )
492
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
493
+
494
+cmFileRC_t cmFileWriteShort(  cmFileH_t h, const short*          buf, unsigned cnt )
495
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
496
+
497
+cmFileRC_t cmFileWriteUShort( cmFileH_t h, const unsigned short* buf, unsigned cnt )
498
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
499
+
500
+cmFileRC_t cmFileWriteLong(   cmFileH_t h, const long*           buf, unsigned cnt )
501
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
502
+
503
+cmFileRC_t cmFileWriteULong(  cmFileH_t h, const unsigned long*  buf, unsigned cnt )
504
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
505
+
506
+cmFileRC_t cmFileWriteInt(    cmFileH_t h, const int*            buf, unsigned cnt )
507
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
508
+
509
+cmFileRC_t cmFileWriteUInt(   cmFileH_t h, const unsigned int*   buf, unsigned cnt )
510
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
511
+
512
+cmFileRC_t cmFileWriteFloat(  cmFileH_t h, const float*          buf, unsigned cnt )
513
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
514
+
515
+cmFileRC_t cmFileWriteDouble( cmFileH_t h, const double*         buf, unsigned cnt )
516
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
517
+
518
+cmFileRC_t cmFileWriteBool(   cmFileH_t h, const bool*           buf, unsigned cnt )
519
+{ return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
520
+
521
+
522
+
523
+
524
+cmFileRC_t cmFilePrint(   cmFileH_t h, const cmChar_t* text )
525
+{
526
+  cmFile_t* p = _cmFileHandleToPtr(h);
527
+
528
+  errno = 0;
529
+  if( fputs(text,p->fp) < 0 )
530
+    return _cmFileError(p,kPrintFailFileRC,errno,"File print failed");
531
+
532
+  return kOkFileRC;
533
+}
534
+
535
+
536
+cmFileRC_t cmFileVPrintf( cmFileH_t h, const cmChar_t* fmt, va_list vl )
537
+{
538
+  cmFile_t* p = _cmFileHandleToPtr(h);
539
+  
540
+  if( vfprintf(p->fp,fmt,vl) < 0 )
541
+    return _cmFileError(p,kPrintFailFileRC,errno,"File print failed");
542
+  
543
+  return kOkFileRC;
544
+}
545
+
546
+cmFileRC_t cmFilePrintf(  cmFileH_t h, const cmChar_t* fmt, ... )
547
+{
548
+  va_list vl;
549
+  va_start(vl,fmt);
550
+  cmFileRC_t rc = cmFileVPrintf(h,fmt,vl);
551
+  va_end(vl);
552
+  return rc;
553
+}
554
+
555
+

+ 203
- 0
cmFile.h View File

@@ -0,0 +1,203 @@
1
+//{
2
+//( 
3
+// File abstraction class which slightly extends the C standard file handling routines
4
+// to cm style error handling.
5
+//)
6
+//
7
+#ifndef cmFile_h
8
+#define cmFile_h
9
+
10
+#ifdef __cplusplus
11
+extern "C" {
12
+#endif
13
+
14
+  //(
15
+  enum
16
+  {
17
+    kOkFileRC = cmOkRC,
18
+    kInvalidFlagFileRC,
19
+    kOpenFailFileRC,
20
+    kCloseFailFileRC,
21
+    kReadFailFileRC,
22
+    kWriteFailFileRC,
23
+    kSeekFailFileRC,
24
+    kTellFailFileRC,
25
+    kPrintFailFileRC,
26
+    kObjAllocFailFileRC,
27
+    kHandleInvalidFileRC,
28
+    kStatFailFileRC,
29
+    kBufAllocFailFileRC,
30
+    kBufTooSmallFileRC
31
+  };
32
+
33
+  typedef unsigned   cmFileRC_t;
34
+  typedef cmHandle_t cmFileH_t;
35
+
36
+  extern cmFileH_t cmFileNullHandle;
37
+
38
+  // Flags for use with cmFileOpen().
39
+  enum cmFileOpenFlags_t
40
+  {
41
+    kReadFileFl   = 0x01, //< Open a file for reading
42
+    kWriteFileFl  = 0x02, //< Create an empty file for writing
43
+    kAppendFileFl = 0x04, //< Open a file for writing at the end of the file.
44
+    kUpdateFileFl = 0x08, //< Open a file for reading and writing.
45
+    kBinaryFileFl = 0x10  //< Open a file for binary (not text) input/output.
46
+  };
47
+
48
+  // Open or create a file.    
49
+  // Equivalent to fopen().
50
+  // If *hp was not initalized by an earlier call to cmFileOpen() then it should 
51
+  // be set to cmFileNullHandle prior to calling this function. If *hp is a valid handle
52
+  // then it is automatically finalized by an internal call to cmFileClose() prior to
53
+  // being re-iniitalized.
54
+  cmFileRC_t cmFileOpen(    
55
+    cmFileH_t*             hp,    // Pointer to a client supplied cmFileHandle_t to recieve the handle for the new object.
56
+    const cmChar_t*        fn,    // The name of the file to open or create. 
57
+    enum cmFileOpenFlags_t flags, // See cmFileOpenFlags_t
58
+    cmRpt_t*               rpt    // The cmRpt_t to use for error reporting
59
+                            );
60
+
61
+  // Close a file opened with  Equivalent to fclose().
62
+  cmFileRC_t cmFileClose(   cmFileH_t* hp );
63
+
64
+  // Return true if the file handle is associated with an open file.
65
+  bool       cmFileIsValid( cmFileH_t h );
66
+
67
+  // Read a block bytes from a file. Equivalent to fread().
68
+  cmFileRC_t cmFileRead(    cmFileH_t h, void* buf, unsigned bufByteCnt );
69
+
70
+  // Write a block of bytes to a file. Equivalent to fwrite().
71
+  cmFileRC_t cmFileWrite(   cmFileH_t h, const void* buf, unsigned bufByteCnt );
72
+  
73
+  enum cmFileSeekFlags_t 
74
+  { 
75
+    kBeginFileFl = 0x01, 
76
+    kCurFileFl   = 0x02, 
77
+    kEndFileFl   = 0x04 
78
+  };
79
+
80
+  // Set the file position indicator. Equivalent to fseek().
81
+  cmFileRC_t cmFileSeek(    cmFileH_t h, enum cmFileSeekFlags_t flags, int offsByteCnt );
82
+
83
+  // Return the file position indicator. Equivalent to ftell().
84
+  cmFileRC_t cmFileTell(    cmFileH_t h, long* offsPtr );
85
+
86
+  // Return true if the file position indicator is at the end of the file. 
87
+  // Equivalent to feof().
88
+  bool       cmFileEof(     cmFileH_t h );
89
+
90
+  // Return the length of the file in bytes
91
+  unsigned   cmFileByteCount(  cmFileH_t h );
92
+
93
+  // Return the file name associated with a file handle.
94
+  const cmChar_t* cmFileName( cmFileH_t h );
95
+
96
+  // Write a buffer to a file.
97
+  cmFileRC_t cmFileFnWrite( const cmChar_t* fn, cmRpt_t* rpt, const void* buf, unsigned bufByteCnt );
98
+
99
+  // Allocate and fill a buffer from the file.
100
+  // Set *bufByteCntPtr to count of bytes read into the buffer.
101
+  // 'bufByteCntPtr' is optional - set it to NULL if it is not required by the caller.
102
+  // It is the callers responsibility to delete the returned buffer with a
103
+  // call to cmMemFree()
104
+  cmChar_t*  cmFileToBuf( cmFileH_t h, unsigned* bufByteCntPtr ); 
105
+  
106
+  // Same as cmFileToBuf() but accepts a file name argument.
107
+  // 'rpt' is the report object to use for error reporting.
108
+  cmChar_t*  cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr );
109
+  
110
+  // Allocate and fill a zero terminated string from a file.
111
+  // Set *bufByteCntPtr to count of bytes read into the buffer.=
112
+  // (the buffer memory size is one byte larger to account for the terminating zero) 
113
+  // 'bufByteCntPtr' is optional - set it to NULL if it is not required by the caller.
114
+  // It is the callers responsibility to delete the returned buffer with a
115
+  // call to cmMemFree()
116
+  cmChar_t*  cmFileToStr( cmFileH_t h, unsigned* bufByteCntPtr );
117
+
118
+  // Same as cmFileToBuf() but accepts a file name argument.
119
+  // 'rpt' is the report object to use for error reporting.
120
+  cmChar_t*  cmFileFnToStr( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr );
121
+
122
+  // Return the count of lines in a file.
123
+  cmFileRC_t cmFileLineCount( cmFileH_t h, unsigned* lineCntPtr );
124
+
125
+  // Read the next line into buf[bufByteCnt]. 
126
+  // Consider using cmFileGetLineAuto() as an alternative to this function 
127
+  // to avoid having to use a buffer with an explicit size.
128
+  //
129
+  // If buf is not long enough to hold the entire string then
130
+  //
131
+  // 1. The function returns kFileBufTooSmallRC
132
+  // 2. *bufByteCntPtr is set to the size of the required buffer.
133
+  // 3. The internal file position is left unchanged.
134
+  //
135
+  // If the buffer is long enough to hold the entire line then 
136
+  // *bufByteCntPtr is left unchanged.
137
+  // See  cmFileGetLineTest() in cmProcTest.c or cmFileGetLineAuto()
138
+  // in cmFile.c for examples of how to use this function to a
139
+  // achieve proper buffer sizing.
140
+  cmFileRC_t cmFileGetLine( cmFileH_t h, cmChar_t* buf, unsigned* bufByteCntPtr );
141
+
142
+  // A version of cmFileGetLine() which eliminates the need to handle buffer
143
+  // sizing. 
144
+  //
145
+  // Example usage:
146
+  // 
147
+  // cmChar_t* buf        = NULL;
148
+  // unsigned  bufByteCnt = 0;
149
+  // while(cmFileGetLineAuto(h,&buf,&bufByteCnt)==kOkFileRC)
150
+  //   proc(buf);
151
+  // cmMemPtrFree(buf);
152
+  //
153
+  // On the first call to this function *bufPtrPtr must be set to NULL and
154
+  // *bufByteCntPtr must be set to 0.
155
+  // Following the last call to this function call cmMemPtrFree(bufPtrptr)
156
+  // to be sure the line buffer is fully released. Note this step is not
157
+  // neccessary if the last call does not return kOkFileRC.
158
+  cmFileRC_t cmFileGetLineAuto( cmFileH_t h, cmChar_t** bufPtrPtr, unsigned* bufByteCntPtr );
159
+
160
+  // Binary Array Reading Functions
161
+  // Each of these functions reads a block of binary data from a file.
162
+  // The advantage to using these functions over cmFileRead() is only that they are type specific.
163
+  cmFileRC_t cmFileReadChar(   cmFileH_t h, char*           buf, unsigned cnt );
164
+  cmFileRC_t cmFileReadUChar(  cmFileH_t h, unsigned char*  buf, unsigned cnt );
165
+  cmFileRC_t cmFileReadShort(  cmFileH_t h, short*          buf, unsigned cnt );
166
+  cmFileRC_t cmFileReadUShort( cmFileH_t h, unsigned short* buf, unsigned cnt );
167
+  cmFileRC_t cmFileReadLong(   cmFileH_t h, long*           buf, unsigned cnt );
168
+  cmFileRC_t cmFileReadULong(  cmFileH_t h, unsigned long*  buf, unsigned cnt );
169
+  cmFileRC_t cmFileReadInt(    cmFileH_t h, int*            buf, unsigned cnt );
170
+  cmFileRC_t cmFileReadUInt(   cmFileH_t h, unsigned int*   buf, unsigned cnt );
171
+  cmFileRC_t cmFileReadFloat(  cmFileH_t h, float*          buf, unsigned cnt );
172
+  cmFileRC_t cmFileReadDouble( cmFileH_t h, double*         buf, unsigned cnt );
173
+  cmFileRC_t cmFileReadBool(   cmFileH_t h, bool*           buf, unsigned cnt );
174
+
175
+  // Binary Array Writing Functions
176
+  // Each of these functions writes an array to a binary file.
177
+  // The advantage to using functions rather than cmFileWrite() is only that they are type specific.
178
+  cmFileRC_t cmFileWriteChar(   cmFileH_t h, const char*           buf, unsigned cnt );
179
+  cmFileRC_t cmFileWriteUChar(  cmFileH_t h, const unsigned char*  buf, unsigned cnt );
180
+  cmFileRC_t cmFileWriteShort(  cmFileH_t h, const short*          buf, unsigned cnt );
181
+  cmFileRC_t cmFileWriteUShort( cmFileH_t h, const unsigned short* buf, unsigned cnt );
182
+  cmFileRC_t cmFileWriteLong(   cmFileH_t h, const long*           buf, unsigned cnt );
183
+  cmFileRC_t cmFileWriteULong(  cmFileH_t h, const unsigned long*  buf, unsigned cnt );
184
+  cmFileRC_t cmFileWriteInt(    cmFileH_t h, const int*            buf, unsigned cnt );
185
+  cmFileRC_t cmFileWriteUInt(   cmFileH_t h, const unsigned int*   buf, unsigned cnt );
186
+  cmFileRC_t cmFileWriteFloat(  cmFileH_t h, const float*          buf, unsigned cnt );
187
+  cmFileRC_t cmFileWriteDouble( cmFileH_t h, const double*         buf, unsigned cnt );
188
+  cmFileRC_t cmFileWriteBool(   cmFileH_t h, const bool*           buf, unsigned cnt );
189
+
190
+  // Formatted Text Output Functions:
191
+  // Print formatted text to a file.
192
+  cmFileRC_t cmFilePrint(   cmFileH_t h, const cmChar_t* text );
193
+  cmFileRC_t cmFilePrintf(  cmFileH_t h, const cmChar_t* fmt, ... );
194
+  cmFileRC_t cmFileVPrintf( cmFileH_t h, const cmChar_t* fmt, va_list vl );
195
+  //)
196
+  //}
197
+
198
+#ifdef __cplusplus
199
+}
200
+#endif
201
+
202
+
203
+#endif

+ 1285
- 0
cmFileSys.c
File diff suppressed because it is too large
View File


+ 231
- 0
cmFileSys.h View File

@@ -0,0 +1,231 @@
1
+//{
2
+//(
3
+// A collection of file system utility functions.
4
+//
5
+// Note that cmFileSysInitialize() creates an internal cmLHeapH_t based
6
+// heap manager from which it allocates memory for some returned objects.
7
+// (e.g. cmFileSysMakeFn(), cmFileSysPathParts(), cmFileSysDirEntries())
8
+// Where possible the client can explicitely free these objects via the
9
+// provided functions. (e.g. cmFileSysFreeFn(), cmFileSysFreePathParts(), cmFileSysDirFreeEntries())
10
+// However if these objects are not free they will be automatically deallocated
11
+// when the internal heap is destroyed by cmFileSysFinalize().
12
+//
13
+//)
14
+ 
15
+#ifndef cmFileSys_h
16
+#define cmFileSys_h
17
+
18
+#ifdef __cplusplus
19
+extern "C" {
20
+#endif
21
+
22
+  //(
23
+  // Result codes returned by cmFileSys.h functions
24
+  enum
25
+  {
26
+    kOkFsRC = cmOkRC,
27
+    kMemAllocErrFsRC,
28
+    kLHeapAllocErrFsRC,
29
+    kStatFailFsRC,
30
+    kAssertFailFsRC,
31
+    kOpenDirFailFsRC,
32
+    kFnTooLongFsRC,
33
+    kMkDirFailFsRC,
34
+    kSysErrFsRC,
35
+    kOsxFailFsRC,
36
+    kLinuxFailFsRC,
37
+    kInvalidDirFsRC
38
+  };
39
+
40
+
41
+  typedef cmHandle_t   cmFileSysH_t;  //< Opaque handle type used by all cmFileSys.h functions
42
+  typedef unsigned     cmFsRC_t;      //< Result code used as the return type by many cmFileSys.h functions.
43
+
44
+  extern  cmFileSysH_t cmFileSysNullHandle;  // NULL handle to be used for setting cmFileSysH_t type handles to an explicit uninitialized value.
45
+
46
+  // Initialize a file system object.  
47
+  // If *hp was not initalized by an earlier call to cmFileSysInitialize() then it should 
48
+  // be set to cmFileSysNullHandle prior to calling this function. If *hp is a valid handle
49
+  // then it is automatically finalized by an internal call to cmFileSysFinalize() prior to
50
+  // being re-iniitalized.
51
+  // The appNameStr is used to determine the location of the preference and resource directories
52
+  // on some platforms
53
+  cmFsRC_t cmFileSysInitialize( cmFileSysH_t* hp, cmCtx_t* ctx, const cmChar_t* appNameStr );
54
+
55
+  // Finalize a file system object.  
56
+  // Upon successful completion *hp is set to cmFileSysNullHandle.
57
+  cmFsRC_t cmFileSysFinalize(   cmFileSysH_t* hp );
58
+
59
+  // Returns true if the file system handle is active and initialized.
60
+  bool     cmFileSysIsValid(    cmFileSysH_t h );
61
+
62
+  const cmChar_t* cmFileSysPrefsDir( cmFileSysH_t h ); //< Return the operating system dependent preference data directory for this application.
63
+  const cmChar_t* cmFileSysRsrcDir(  cmFileSysH_t h ); //< Return the operating system dependent application resource directory for this application.
64
+  const cmChar_t* cmFileSysUserDir(  cmFileSysH_t h ); //< Return the operating system dependent user directory for this application.
65
+
66
+  // Test the type of a file system object:
67
+  //
68
+  bool     cmFileSysIsDir(  cmFileSysH_t h, const cmChar_t* dirStr ); //< Return true if 'dirStr' refers to an existing directory.
69
+  bool     cmFileSysIsFile( cmFileSysH_t h, const cmChar_t* fnStr );  //< Return true if 'fnStr' refers to an existing file.
70
+  bool     cmFileSysIsLink( cmFileSysH_t h, const cmChar_t* fnStr );  //< Return true if 'fnStr' refers to a symbolic link.
71
+
72
+  // Create File Names:
73
+  //
74
+  // Create a file name by concatenating sub-strings.
75
+  //
76
+  // Variable arg's. entries are directories inserted between 
77
+  // 'dirPrefixStr' and the file name.
78
+  // Terminate var arg's directory list with a  NULL. 
79
+  //
80
+  // The returned string is allocated in a local heap maintained by the cmFileSys object.
81
+  // The memory used by the string will exist until it is released with cmFileSysFreeFn()
82
+  // or the cmFileSys object is finalized.
83
+  const cmChar_t* cmFileSysMakeFn( cmFileSysH_t h, const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, ... );
84
+  
85
+  // Same as cmFileSysMakeFn with but with a va_list argument to accept the var. args. parameters.
86
+  const cmChar_t* cmFileSysVMakeFn( cmFileSysH_t h, const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, va_list vl );
87
+
88
+  // Release the file name created through an earlier call to cmFileSysMakeFn().
89
+  void            cmFileSysFreeFn( cmFileSysH_t h, const cmChar_t* fn );
90
+
91
+  // Create a directory - where the entire path already exists except for the 
92
+  // final directory.
93
+  cmFsRC_t    cmFileSysMkDir( cmFileSysH_t h, const cmChar_t* dir );
94
+  
95
+  // Create a complete directory path - where any of the path segments may
96
+  // not already exist.
97
+  cmFsRC_t    cmFileSysMkDirAll( cmFileSysH_t h, const cmChar_t* dir );
98
+
99
+  // Parse a path into its parts:
100
+  //  
101
+  // Return record used by cmFileSysParts()
102
+  typedef struct
103
+  {
104
+    const cmChar_t* dirStr;
105
+    const cmChar_t* fnStr;
106
+    const cmChar_t* extStr;
107
+  } cmFileSysPathPart_t;
108
+
109
+  // Given a file name decompose it into a directory string, file name string and file extension string.
110
+  // The cmFileSysPathPart_t record and the memory used by the strings that it references 
111
+  // are allocated from a local heap maintained  by the cmFileSys object. This memory will exist 
112
+  // until it is released with cmFileSysFreePathParts() or the cmFileSysH_t  handle is finalized.
113
+  cmFileSysPathPart_t* cmFileSysPathParts( cmFileSysH_t h, const cmChar_t* pathNameStr );
114
+
115
+  // Free the memory associated with a cmFileSysPathPart_t record returned from an eariler call to cmFileSysPathParts(). 
116
+  void  cmFileSysFreePathParts( cmFileSysH_t h, cmFileSysPathPart_t* p );
117
+
118
+  // Return the parts of a directory path as an array of strings.
119
+  // The last element in the array is set to NULL to mark the end of the array.
120
+  // Note that all '/' separator characters are removed from the result with 
121
+  // the exception of the first one - which denotes the root directory.
122
+  // The returned array is allocated from the file systems internal heap and will
123
+  // be automatically released when the file system is closed by cmFileSysDestroy().
124
+  // The caller may optionally release the array memory with a call to
125
+  // cmFileSysFreeDirParts().
126
+  cmChar_t** cmFileSysDirParts(     cmFileSysH_t h, const cmChar_t* dirStr );
127
+  void       cmFileSysFreeDirParts( cmFileSysH_t h, cmChar_t** dirStrArray );
128
+
129
+  // Return the count of elements in a directory parts array as returned by 
130
+  // cmFileSysDirParts().
131
+  unsigned   cmFileSysDirPartsCount(cmFileSysH_t h, cmChar_t** dirStrArray );
132
+
133
+  // Form a directory string from a NULL terminated array of strings.
134
+  // If the first element in the array is set to '/' then the
135
+  // resulting directory will be absolute rather than relative.
136
+  // The returned string is allocated from the file systems internal heap and will
137
+  // be automatically released when the file system is closed by cmFileSysDestroy().
138
+  // The caller may optionally release the array memory with a call to
139
+  // cmFileSysFreeDir().
140
+  cmChar_t*  cmFileSysFormDir( cmFileSysH_t h, cmChar_t** dirStrArray, unsigned n );
141
+  void       cmFileSysFreeDir( cmFileSysH_t h, const cmChar_t* dir );
142
+
143
+  // Walk a directory tree:
144
+  //
145
+  // Flags used by cmFileSysDirEntries 'includeFlags' parameter.
146
+  enum
147
+  {
148
+    kFileFsFl      = 0x01,  //< include all visible files
149
+    kDirFsFl       = 0x02,  //< include all visible directory 
150
+    kInvisibleFsFl = 0x04,  //< include file/dir name beginning with a '.'
151
+    kCurDirFsFl    = 0x08,  //< include '.' directory
152
+    kParentDirFsFl = 0x10,  //< include '..' directory
153
+
154
+    kAllFsFl       = 0x1f,  //< all type flags
155
+
156
+    kFullPathFsFl = 0x40, //< return the full path in the 'name' field of cmFileSysDirEntry_t;
157
+    kRecurseFsFl  = 0x80  //< recurse into directories
158
+
159
+  };
160
+
161
+  // The return type for cmFileSysDirEntries().
162
+  typedef struct
163
+  {
164
+    unsigned        flags;    //< Entry type flags from kXXXFsFl.
165
+    const cmChar_t* name;     //< Entry name or full path depending on kFullPathFsFl.
166
+  } cmFileSysDirEntry_t;
167
+
168
+  // Return the file and directory names contained in a given subdirectory.
169
+  //
170
+  // Set 'includeFlags' with the  kXXXFsFl flags of the files to include in the returned 
171
+  // directory entry array.  The value pointed to by dirEntryCntPtr will be set to the
172
+  // number of records in the returned array.
173
+  cmFileSysDirEntry_t* cmFileSysDirEntries( cmFileSysH_t h, const cmChar_t* dirStr, unsigned includeFlags, unsigned* dirEntryCntPtr );
174
+
175
+  // Release the memory assoicated with a cmFileSysDirEntry_t array returned from an earlier call to cmFileSysDirEntries().
176
+  void cmFileSysDirFreeEntries( cmFileSysH_t h, cmFileSysDirEntry_t* p );
177
+
178
+  // Return the last error code generated by the file system.
179
+  cmFsRC_t cmFileSysErrorCode( cmFileSysH_t h );
180
+
181
+  //-------------------------------------------------------------------------------------------------
182
+  
183
+  // Global file system functions:
184
+  // These functions work using a global cmFileSysH created by cmFsInitialize().
185
+  // The functions are otherwise just wrappers for the same named function above.
186
+
187
+  cmFsRC_t        cmFsInitialize( cmCtx_t* ctx, const cmChar_t* appNameStr );
188
+  cmFsRC_t        cmFsFinalize();
189
+
190
+  const cmChar_t* cmFsPrefsDir();
191
+  const cmChar_t* cmFsRsrcDir();
192
+  const cmChar_t* cmFsUserDir();
193
+
194
+  bool            cmFsIsDir(  const cmChar_t* dirStr );
195
+  bool            cmFsIsFile( const cmChar_t* fnStr ); 
196
+  bool            cmFsIsLink( const cmChar_t* fnStr ); 
197
+
198
+  const cmChar_t* cmFsVMakeFn( const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, va_list vl );
199
+  const cmChar_t* cmFsMakeFn(  const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, ... );
200
+  void            cmFsFreeFn(  const cmChar_t* fn );
201
+
202
+  cmFsRC_t        cmFsMkDir( const cmChar_t* dir );
203
+  cmFsRC_t        cmFsMkDirAll( const cmChar_t* dir );
204
+
205
+  cmFileSysPathPart_t* cmFsPathParts(     const cmChar_t* pathNameStr );
206
+  void   cmFsFreePathParts( cmFileSysPathPart_t* p );
207
+
208
+
209
+  cmChar_t** cmFsDirParts(      const cmChar_t* dirStr );
210
+  void       cmFsFreeDirParts(  cmChar_t** dirStrArray );
211
+  unsigned   cmFsDirPartsCount( cmChar_t** dirStrArray );
212
+  cmChar_t*  cmFsFormDir(       cmChar_t** dirStrArray, unsigned n );
213
+  void       cmFsFreeDir(       const cmChar_t* dir );
214
+
215
+  cmFileSysDirEntry_t* cmFsDirEntries( const cmChar_t* dirStr, unsigned includeFlags, unsigned* dirEntryCntPtr );
216
+  void        cmFsDirFreeEntries( cmFileSysDirEntry_t* p );
217
+
218
+  cmFsRC_t    cmFsErrorCode();
219
+
220
+
221
+  // Test and example function to demonstrate the use of the functions in cmFileSys.h
222
+  cmFsRC_t    cmFileSysTest( cmCtx_t* ctx );  
223
+
224
+  //)
225
+  //}
226
+
227
+#ifdef __cplusplus
228
+}
229
+#endif
230
+
231
+#endif

+ 81
- 0
cmFloatTypes.h View File

@@ -0,0 +1,81 @@
1
+/// \file cmFloatTypes.h
2
+/// \brief Declare the types cmReal_t and cmSample_t and define some useful limits.
3
+///
4
+/// For signal processing functions the cm library uses the types cmSample_t to indicate an audio
5
+/// sample value and cmReal_t to specify a general purpose floating point value. The library
6
+/// is designed in such a way that the actual type, float or double, for these two types may
7
+/// be set at compilation time. Set the preprocessor variable CM_FLOAT_SMP to 1 to indicate 
8
+/// that cmSample_t will be of type 'float' otherwise it will be of type 'double'. 
9
+/// Set the preprocessor variable CM_FLOAT_REAL to 1 to indicate 
10
+/// that cmSample_t will be of type 'float' otherwise it will be of type 'double'. 
11
+/// By default cmSample_t is float nad cmReal_t is double.
12
+///
13
+
14
+#ifndef cmFloatTypes_h
15
+#define cmFloatTypes_h
16
+
17
+
18
+#ifdef __cplusplus
19
+extern "C" {
20
+#endif
21
+
22
+  //-----------------------------------------------------------------
23
+#ifndef CM_FLOAT_SMP
24
+#define CM_FLOAT_SMP 1
25
+#endif
26
+
27
+#if CM_FLOAT_SMP == 1
28
+
29
+  typedef float          cmSample_t;  ///< cmSample_t is a float
30
+  typedef float _Complex cmComplexS_t;///< cmComplexS_t is single precision.
31
+
32
+#define cmSample_EPSILON FLT_EPSILON  ///< Minimum increment between 1.0 and the next greaterv value. (1E-5)
33
+#define cmSample_MAX     FLT_MAX      ///< Maximum representable number (1E+37).
34
+#define cmSample_MIN     FLT_MIN      ///< Minimum representable number (1E-37).
35
+
36
+#else
37
+
38
+  typedef  double          cmSample_t;   ///< cmSample_t is a double
39
+  typedef  double _Complex cmComplexS_t; ///< cmComplexS_t is doulbe precision.
40
+
41
+#define cmSample_EPSILON DBL_EPSILON    ///< Minimum increment between 1.0 and the next greaterv value. (1E-9)
42
+#define cmSample_MAX     DBL_MAX        ///< Maximum representable number (1E+37).
43
+#define cmSample_MIN     DBL_MIN        ///< Minimum representable number (1E-37).
44
+
45
+#endif
46
+
47
+//-----------------------------------------------------------------
48
+//-----------------------------------------------------------------
49
+//-----------------------------------------------------------------
50
+
51
+#ifndef CM_FLOAT_REAL
52
+#define CM_FLOAT_REAL 0
53
+#endif
54
+
55
+#if CM_FLOAT_REAL == 1
56
+
57
+  typedef float          cmReal_t;      ///< cmReal_t is a float
58
+  typedef float _Complex cmComplexR_t;  ///< cmComplexR_t is single precision.
59
+
60
+#define cmReal_EPSILON FLT_EPSILON      ///< Minimum increment between 1.0 and the next greaterv value. (1E-5)
61
+#define cmReal_MAX     FLT_MAX          ///< Maximum representable number (1E+37).
62
+#define cmReal_MIN     FLT_MIN          ///< Minimum representable number (1E-37).
63
+
64
+#else
65
+
66
+  typedef  double          cmReal_t;      ///< cmReal_t is a double.
67
+  typedef  double _Complex cmComplexR_t;  ///< cmComplexR_t is double precision.
68
+
69
+#define cmReal_EPSILON DBL_EPSILON  ///< Minimum increment between 1.0 and the next greaterv value (1E-9).
70
+#define cmReal_MAX     DBL_MAX      ///< Maximum representable number (1E+37).
71
+#define cmReal_MIN     DBL_MIN      ///< Minimum representable number (1E-37).
72
+
73
+
74
+#endif
75
+
76
+#ifdef __cplusplus
77
+}
78
+#endif
79
+
80
+
81
+#endif

+ 2210
- 0
cmFrameFile.c
File diff suppressed because it is too large
View File


+ 360
- 0
cmFrameFile.h View File

@@ -0,0 +1,360 @@
1
+#ifndef cmFrameFile_h
2
+#define cmFrameFile_h
3
+
4
+/*
5
+  file  -> cmFfFile_t frame*
6
+  frame -> cmFfFrame_t mtx*
7
+  mtx   -> cmFfMtx_t data*   
8
+ */
9
+
10
+#ifdef __cplusplus
11
+extern "C" {
12
+#endif
13
+
14
+
15
+  enum
16
+  {
17
+    kInvalidFrameTId  = 0,
18
+    kInvalidStreamId  = 0,
19
+
20
+    kTocFrameTId = -3,
21
+    kTocStreamId = -3,
22
+
23
+    kFileFfTId = 'FrmF',
24
+    
25
+  };
26
+
27
+  enum
28
+  {
29
+    kOkFfRC = 0,          // 0
30
+    kFileOpenFailFfRC,    // 1
31
+    kFileReadFailFfRC,    // 2 
32
+    kFileWriteFailFfRC,   // 3
33
+    kFileSeekFailFfRC,    // 4
34
+    kFileCloseFailFfRC,   // 5
35
+    kEofFfRC,             // 6
36
+    kInvalidHandleFfRC,   // 7
37
+    kMemAllocErrFfRC,     // 8
38
+    kNotFrameFileFfRC,    // 9
39
+    kUnknownErrFfRC,      // 10
40
+    kNoMatchingFrameFfRC, // 11
41
+    kInvalidFileModeFfRC, // 12
42
+    kJsonFailFfRC,        // 13
43
+    kInvalidFrameIdxFfRC, // 14
44
+    kDuplicateMtxIdFfRC,  // 15 
45
+    kFileTellFailFfRC,    // 16
46
+    kLHeapFailFfRC,       // 17
47
+    kTocFrameRdFailFfRC,  // 18
48
+    kTocRecdNotFoundFfRC, // 19
49
+    kBufTooSmallFfRC      // 20
50
+  };
51
+
52
+  // row data formats
53
+  enum
54
+  {
55
+    kInvalidFmtId, // 0
56
+    kUCharFmtId,   // 1
57
+    kCharFmtId,    // 2
58
+    kUShortFmtId,  // 3 
59
+    kShortFmtId,   // 4
60
+    kULongFmtId,   // 5
61
+    kLongFmtId,    // 6
62
+    kUIntFmtId,    // 7
63
+    kIntFmtId,     // 8
64
+    kLLongFmtId,   // 9
65
+    kULLongFmtId,  // 10
66
+    kOff_tFmtId,   // 11
67
+    kFloatFmtId,   // 12
68
+    kDoubleFmtId,  // 13 
69
+    kStringZFmtId, // 14 
70
+    kBlobFmtId,    // 15
71
+    kJsonFmtId     // 16
72
+  };
73
+
74
+  enum
75
+  {
76
+    kInvalidUId ,  // 0
77
+    kNoUnitsUId,   // 1
78
+    kHzUId,        // 2 
79
+    kRadsUId,      // 3   -pi to pi 
80
+    kAmplUId,      // 4   -1.0 to 1.0
81
+    kPowUId,       // 5   amp^2/2
82
+    k10DbUId,      // 6   10*log10(v)
83
+    k20DbUId,      // 7   20*log10(v)  
84
+    kCntUId,       // 8   count of elements
85
+    kIdxUId,       // 9   element index 
86
+    kBfccUId,      // 10
87
+    kMfccUId,      // 11
88
+    kCepsUId,       // 12
89
+
90
+    kD1UFl  = 0x80000000 // this is a 1st difference 
91
+  };
92
+
93
+  enum
94
+  {
95
+    kTocMId = -2,
96
+
97
+
98
+    kInvalidMId = 0,// 0
99
+    kAudioMId,      // 1    
100
+    kMagMId,        // 2
101
+    kPhsMId,        // 3
102
+    kFrqMId,        // 4  measured bin frequecies in Hz
103
+    kTrkMId,        // 5
104
+    kRmsMId,        // 6
105
+    kBinCntMId,     // 7  count of frequency domain bins in frame
106
+    kWndSmpCntMId,  // 8  actual count of samples in FFT window (this is no (binCnt-1)*2)
107
+    kAudSmpCntMId,  // 9  count of audio samples in frame
108
+    kBfccMId,       // 10 vector of BFCC's
109
+    kBfccBandCntMId,// 11 count of coeff's BFCC vector in this frame
110
+    kMfccMId,       // 12 vector of MFCC's 
111
+    kCepsMId,       // 13 vector of cepstral coefficients
112
+    kConstqMId,     // 14 vector of constant
113
+    kDataMId        // 15 blob of misc data
114
+
115
+  };
116
+
117
+  typedef cmHandle_t cmFrameFileH_t;
118
+  typedef unsigned   cmFfRC_t;
119
+
120
+  // mtx desc record
121
+  typedef struct
122
+  {
123
+    unsigned type;
124
+    unsigned fmtId;
125
+    unsigned unitsId;
126
+    unsigned rowCnt;
127
+    unsigned colCnt;
128
+  } cmFfMtx_t;
129
+
130
+  // frame desc record
131
+  typedef struct
132
+  {
133
+    unsigned type;
134
+    unsigned mtxCnt;
135
+    unsigned flags;     // used internally to track time format (seconds or samples)
136
+    unsigned streamId;
137
+
138
+    union
139
+    {
140
+      unsigned sampleIdx;
141
+      double   seconds;
142
+    } time;
143
+    
144
+  } cmFfFrame_t;
145
+
146
+
147
+  // file desc record
148
+  typedef struct
149
+  {
150
+    cmChar_t*   filenameStr;
151
+    unsigned    version;
152
+    unsigned    frameCnt;      // count of frames in all streams
153
+    double      srate;         // sample rate for all frames
154
+  } cmFfFile_t;
155
+
156
+  extern cmFrameFileH_t cmFrameFileNullHandle;
157
+
158
+  cmFfRC_t           cmFrameFileCreate(  cmFrameFileH_t* hPtr, const char* fn, double srate, cmCtx_t* ctx );
159
+
160
+  // The fileDescPtrPtr is optional. Set to NULL to ignore.
161
+  cmFfRC_t           cmFrameFileOpen(    cmFrameFileH_t* hPtr, const char* fn, cmCtx_t* ctx, const cmFfFile_t** fileDescPtrPtr );
162
+  cmFfRC_t           cmFrameFileClose(   cmFrameFileH_t* hPtr );
163
+  bool               cmFrameFileIsValid( cmFrameFileH_t h );
164
+  const cmFfFile_t*  cmFrameFileDesc(    cmFrameFileH_t h );
165
+
166
+  // Return the count of frames in the requested stream.
167
+  unsigned           cmFrameFileFrameCount( cmFrameFileH_t h, unsigned streamId );
168
+
169
+  // Create a frame in a file created via cmFrameFileCreate().
170
+  // Set 'sampleIdx' to -1 if seconds is being used instead of samples. 
171
+  cmFfRC_t cmFrameFileFrameCreate( cmFrameFileH_t h, unsigned frameType, unsigned streamId, unsigned sampleIdx, double secs  );
172
+
173
+  // (W) Complete and write a frame created via an earilier call
174
+  // to cmFrameFileFrameCreate()
175
+  cmFfRC_t cmFrameFileFrameClose(  cmFrameFileH_t h );
176
+
177
+  // (W) Fill a frame with matrix data. The frame must have been created
178
+  // via an earlier call to cmFrameFileCreate().
179
+  cmFfRC_t cmFrameFileWriteMtxUChar(   cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned char*            p, unsigned rn, unsigned cn );
180
+  cmFfRC_t cmFrameFileWriteMtxChar(    cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const char*                     p, unsigned rn, unsigned cn );
181
+  cmFfRC_t cmFrameFileWriteMtxUShort(  cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned short*           p, unsigned rn, unsigned cn );
182
+  cmFfRC_t cmFrameFileWriteMtxShort(   cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const short*                    p, unsigned rn, unsigned cn );
183
+  cmFfRC_t cmFrameFileWriteMtxULong(   cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned long*            p, unsigned rn, unsigned cn );
184
+  cmFfRC_t cmFrameFileWriteMtxLong(    cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const long*                     p, unsigned rn, unsigned cn );
185
+  cmFfRC_t cmFrameFileWriteMtxUInt(    cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned*                 p, unsigned rn, unsigned cn );
186
+  cmFfRC_t cmFrameFileWriteMtxInt(     cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const int*                      p, unsigned rn, unsigned cn );
187
+  cmFfRC_t cmFrameFileWriteMtxULLong(  cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const unsigned long long*       p, unsigned rn, unsigned cn );
188
+  cmFfRC_t cmFrameFileWriteMtxLLong(   cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const long long*                p, unsigned rn, unsigned cn );
189
+  cmFfRC_t cmFrameFileWriteMtxOff_t(   cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const off_t*                    p, unsigned rn, unsigned cn );
190
+  cmFfRC_t cmFrameFileWriteMtxFloat(   cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const float*                    p, unsigned rn, unsigned cn );
191
+  cmFfRC_t cmFrameFileWriteMtxDouble(  cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const double*                   p, unsigned rn, unsigned cn );
192
+  cmFfRC_t cmFrameFileWriteMtxBlob(    cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const void*                     p, unsigned rn, unsigned cn );  
193
+  cmFfRC_t cmFrameFileWriteMtxStringZ( cmFrameFileH_t h, unsigned mtxType, unsigned unitsId, const char* p );
194
+  cmFfRC_t cmFrameFileWriteMtxJson(    cmFrameFileH_t h, unsigned mtxType, cmJsonH_t jsH, const cmJsonNode_t* nodePtr );
195
+
196
+  // (R/W) Rewind to the first frame. Must call cmFrameFileFrameNext() 
197
+  // following successful execution of this function to maintain correct
198
+  // alignment.
199
+  cmFfRC_t          cmFrameFileRewind( cmFrameFileH_t h );
200
+
201
+  // (R/W) Seek to the frame at index 'frameIdx'.  Must call cmFrameFileFrameNext() 
202
+  // following successful execution of this function to maintain correct
203
+  // alignment.
204
+  cmFfRC_t          cmFrameFileSeek( cmFrameFileH_t h, unsigned streamId, unsigned frameIdx );
205
+
206
+  // (R/W) Seek to the next frame in stream frameStreamId with type frameTypeId.
207
+  // Set frameTypeId to kInvalidFrameTId to return any frame type.
208
+  // Set frameStreamId to kInvalidStreamId to return any stream type.
209
+  // This function must be followed by a call to cmFrameFileFrameLoad()
210
+  // or cmFrameFileSkip().
211
+  cmFfRC_t           cmFrameFileFrameNext(     cmFrameFileH_t h, unsigned frameTypeId, unsigned streamId );
212
+
213
+  // (R/W) Load the matrix data associated with the current frame. 
214
+  // This function can only be called after a successful call to 
215
+  // cmFrameFileFrameNext(). 
216
+  // The frameDescPtrPtr is optional. Set to NULL to ignore.
217
+  cmFfRC_t           cmFrameFileFrameLoad(     cmFrameFileH_t h, const cmFfFrame_t** frameDescPtrPtr );
218
+
219
+  // (R/W) Skip over the matrix data associated with the current frame. 
220
+  // This is an alternative to cmFrameFileLoad().
221
+  cmFfRC_t           cmFrameFileFrameSkip(     cmFrameFileH_t h ); 
222
+
223
+  // (R/W) Combines cmFrameFileFrameNext() and cmFrameFileFrameLoad()
224
+  // into a single call.
225
+  cmFfRC_t           cmFrameFileFrameLoadNext( cmFrameFileH_t h, unsigned frameTypeId, unsigned streamId, const cmFfFrame_t** frameDescPtrPtr );
226
+
227
+  // (R/W) Write the current frame back to disk. 
228
+  cmFfRC_t           cmFrameFileFrameUpdate( cmFrameFileH_t h );
229
+
230
+  // Return the current frame description record.
231
+  const cmFfFrame_t* cmFrameFileFrameDesc( cmFrameFileH_t h );
232
+
233
+  // Currently loaded frame index.
234
+  unsigned           cmFrameFileFrameLoadedIndex( cmFrameFileH_t h );
235
+
236
+  // Return the index of the frame with type 'mtxTypeId', units 'unitsId', and format 'fmtId'
237
+  // or cmInvalidIdx if the specified frame is not found.
238
+  // Set mtxTypeId to kInvalidMId to return any mtx type.
239
+  // Set unitsId to kInvalidUId to return a mtx with any units.
240
+  // Set fmtId to kInvalidFmtId to return a mtx with any fmt.
241
+  unsigned           cmFrameFileMtxIndex(      cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId, unsigned fmtId );
242
+
243
+  // Return a matrix description record.
244
+  const cmFfMtx_t*   cmFrameFileMtxDesc(       cmFrameFileH_t h, unsigned mtxIdx );
245
+
246
+  // Access matrix data.
247
+  //Set descPtr to NULL if the matrix desc is not needed.
248
+  unsigned char*      cmFrameFileMtxIndexUChar(   cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
249
+  char*               cmFrameFileMtxIndexChar(    cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
250
+  unsigned short*     cmFrameFileMtxIndexUShort(  cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
251
+  short*              cmFrameFileMtxIndexShort(   cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
252
+  unsigned long*      cmFrameFileMtxIndexULong(   cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
253
+  long*               cmFrameFileMtxIndexLong(    cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
254
+  unsigned*           cmFrameFileMtxIndexUInt(    cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
255
+  int*                cmFrameFileMtxIndexInt(     cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
256
+  unsigned long long* cmFrameFileMtxIndexULLong(  cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
257
+  long long*          cmFrameFileMtxIndexLLong(   cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
258
+  off_t*              cmFrameFileMtxIndexOff_t(   cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
259
+  float*              cmFrameFileMtxIndexFloat(   cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
260
+  double*             cmFrameFileMtxIndexDouble(  cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
261
+  char*               cmFrameFileMtxIndexStringZ( cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
262
+  void*               cmFrameFileMtxIndexBlob(    cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
263
+  // (The caller is responsible for invoking cmJsonFinalize() to finalize the returned json handle.)
264
+  cmJsonH_t           cmFrameFileMtxIndexJson(    cmFrameFileH_t h, unsigned mtxIdx, const cmFfMtx_t** descPtrPtr );
265
+
266
+  // Return a pointer to the data, and optionally the descPtr, for a matrix with the given 
267
+  // type,units and format in the current frame.
268
+  // The following functions are implmented in terms of
269
+  // cmFrameFileMtxIndexXXX() and cmFrameFileMtxIndex().
270
+  unsigned char*      cmFrameFileMtxUChar(   cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
271
+  char*               cmFrameFileMtxChar(    cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
272
+  unsigned short*     cmFrameFileMtxUShort(  cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
273
+  short*              cmFrameFileMtxShort(   cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
274
+  unsigned long*      cmFrameFileMtxULong(   cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
275
+  long*               cmFrameFileMtxLong(    cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
276
+  unsigned*           cmFrameFileMtxUInt(    cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
277
+  int*                cmFrameFileMtxInt(     cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
278
+  unsigned long long* cmFrameFileMtxULLong(  cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
279
+  long long*          cmFrameFileMtxLLong(   cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
280
+  off_t*              cmFrameFileMtxOff_t(   cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
281
+  float*              cmFrameFileMtxFloat(   cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
282
+  double*             cmFrameFileMtxDouble(  cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
283
+  char*               cmFrameFileMtxStringZ( cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
284
+  void*               cmFrameFileMtxBlob(    cmFrameFileH_t h, unsigned mtxTypeId, unsigned unitsId,  const cmFfMtx_t** descPtrPtr );
285
+  // (The caller is responsible for invoking cmJsonFinalize() to finalize the returned json handle.)
286
+  cmJsonH_t           cmFrameFileMtxJson(    cmFrameFileH_t h, unsigned mtxTypeId,                                   const cmFfMtx_t** descPtrPtr );
287
+
288
+
289
+  // Return the max row cnt, max col count, and total element count
290
+  // for all matrices which match the given stream/mtx/units/fmt
291
+  // combination.  Note that if the returned ele count is less than
292
+  // maxRowCnt * maxColCnt * cmFrameFileFrameCount(streamId) then
293
+  // some matched matrices contain fewer than maxRowCnt/maxColCnt
294
+  // rows/columns.
295
+  cmFfRC_t cmFrameFileMtxSize( cmFrameFileH_t h, unsigned streamId, unsigned mtxType, unsigned unitsId, unsigned fmtId, unsigned* frameCntPtr, unsigned* rowCntPtr, unsigned* colCntPtr, unsigned* eleCntPtr );
296
+
297
+  // Load a buffer with all of the data which matches a given 
298
+  // stream/mtx/unit/fmt combination in the given range of frames.
299
+  // 'frmIdx' specifies the frame index relative to the given stream id as opposed to 
300
+  // and absolute frame index.
301
+  // Set frmCnt to -1 to include all frames following 'frmIdx'.
302
+  // *outCntPtr is set to the actual number of elements copied into the buffer
303
+  // The data is packed into the return buffer by copying columwise from the source.
304
+  // matrices to the buf[].  If all of the matrices are not of a fixed known size
305
+  // it may therefore be difficult to distinguish where one frames data ends and
306
+  // the next begins.
307
+  cmFfRC_t cmFrameFileMtxLoadUChar(   cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  unsigned char*      buf, unsigned eleCnt, unsigned* outCntPtr );
308
+  cmFfRC_t cmFrameFileMtxLoadChar(    cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  char*               buf, unsigned eleCnt, unsigned* outCntPtr );
309
+  cmFfRC_t cmFrameFileMtxLoadUShort(  cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  unsigned short*     buf, unsigned eleCnt, unsigned* outCntPtr );
310
+  cmFfRC_t cmFrameFileMtxLoadShort(   cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  short*              buf, unsigned eleCnt, unsigned* outCntPtr );
311
+  cmFfRC_t cmFrameFileMtxLoadULong(   cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  unsigned long*      buf, unsigned eleCnt, unsigned* outCntPtr );
312
+  cmFfRC_t cmFrameFileMtxLoadLong(    cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  long*               buf, unsigned eleCnt, unsigned* outCntPtr );
313
+  cmFfRC_t cmFrameFileMtxLoadUInt(    cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  unsigned int*       buf, unsigned eleCnt, unsigned* outCntPtr );
314
+  cmFfRC_t cmFrameFileMtxLoadInt(     cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  int*                buf, unsigned eleCnt, unsigned* outCntPtr );
315
+  cmFfRC_t cmFrameFileMtxLoadULLong(  cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  unsigned long long* buf, unsigned eleCnt, unsigned* outCntPtr );
316
+  cmFfRC_t cmFrameFileMtxLoadLLong(   cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  long long*          buf, unsigned eleCnt, unsigned* outCntPtr );
317
+  cmFfRC_t cmFrameFileMtxLoadOff_t(   cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  off_t*              buf, unsigned eleCnt, unsigned* outCntPtr );
318
+  cmFfRC_t cmFrameFileMtxLoadFloat(   cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  float*              buf, unsigned eleCnt, unsigned* outCntPtr );
319
+  cmFfRC_t cmFrameFileMtxLoadDouble(  cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  double*             buf, unsigned eleCnt, unsigned* outCntPtr );
320
+  cmFfRC_t cmFrameFileMtxLoadStringZ( cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  char*               buf, unsigned eleCnt, unsigned* outCntPtr );
321
+  cmFfRC_t cmFrameFileMtxLoadBlob(    cmFrameFileH_t h, unsigned streamId, unsigned mtxTypeId, unsigned unitsId, unsigned frmIdx, unsigned frmCnt,  void*               buf, unsigned eleCnt, unsigned* outCntPtr );
322
+
323
+  cmFfRC_t cmFrameFileReport( cmFrameFileH_t h, bool summOnlyFl, cmRpt_t* rpt );
324
+  cmFfRC_t cmFrameFileNameReport( const char* fn, bool summOnlyFl, cmCtx_t* ctx );
325
+
326
+  cmFfRC_t cmFrameFileTest( const char* fn, cmCtx_t* ctx );
327
+
328
+#if CM_FLOAT_SMP == 1
329
+#define cmFrameFileMtxLoadSample  cmFrameFileMtxLoadFloat
330
+#define cmFrameFileWriteMtxSample cmFrameFileWriteMtxFloat
331
+#define cmFrameFileMtxIndexSample cmFrameFileMtxIndexFloat
332
+#define cmFrameFileMtxSample      cmFrameFileMtxFloat
333
+#define kSampleFmtId              kFloatFmtId
334
+#else
335
+#define cmFrameFileMtxLoadSample  cmFrameFileMtxLoadDouble
336
+#define cmFrameFileWriteMtxSample cmFrameFileWriteMtxDouble
337
+#define cmFrameFileMtxIndexSample cmFrameFileMtxIndexDouble
338
+#define cmFrameFileMtxSample      cmFrameFileMtxDouble
339
+#define kSampleFmtId              kDoubleFmtId
340
+#endif
341
+
342
+#if CM_FLOAT_REAL == 1
343
+#define cmFrameFileMtxLoadReal  cmFrameFileMtxLoadFloat
344
+#define cmFrameFileWriteMtxReal cmFrameFileWriteMtxFloat
345
+#define cmFrameFileMtxIndexReal cmFrameFileMtxIndexFloat
346
+#define cmFrameFileMtxReal      cmFrameFileMtxFloat
347
+#define kRealFmtId              kFloatFmtId
348
+#else
349
+#define cmFrameFileMtxLoadReal  cmFrameFileMtxLoadDouble
350
+#define cmFrameFileWriteMtxReal cmFrameFileWriteMtxDouble
351
+#define cmFrameFileMtxIndexReal cmFrameFileMtxIndexDouble
352
+#define cmFrameFileMtxReal      cmFrameFileMtxDouble
353
+#define kRealFmtId              kDoubleFmtId
354
+#endif
355
+
356
+#ifdef __cplusplus
357
+}
358
+#endif
359
+
360
+#endif

+ 2
- 0
cmGlobal.c View File

@@ -0,0 +1,2 @@
1
+
2
+

+ 154
- 0
cmGlobal.h View File

@@ -0,0 +1,154 @@
1
+//{
2
+//(
3
+// cmGlobal.h contains the global macros, header files, and 
4
+//  typedefs availale to all other cm modules. 
5
+//
6
+// All operating system dependencies should be resolved in this file via 
7
+// testing for OS_LINUX, OS_OSX, or OS_W32.
8
+//)
9
+
10
+#ifndef cmGlobal_h
11
+#define cmGlobal_h
12
+
13
+//(
14
+#include "config.h" // created by 'configure'
15
+#include <stdio.h>
16
+#include <stdlib.h>
17
+#include <string.h>
18
+#include <assert.h>
19
+#include <stdbool.h>
20
+#include <stdarg.h>
21
+#include <math.h>
22
+#include <ctype.h>
23
+#include <errno.h>
24
+#include <float.h>
25
+#include <limits.h>
26
+#include <signal.h>  
27
+#include <time.h>
28
+
29
+#ifdef __cplusplus
30
+extern "C" {
31
+#endif
32
+
33
+
34
+#define CM_FLOAT_SMP  1  //< make cmSample_t = float in cmFloatTypes.h
35
+#define CM_FLOAT_REAL 0  //< make cmReal_t = double in cmFloatTypes.h
36
+
37
+#ifdef NDEBUG
38
+#define cmDEBUG_FL 0  //< Define cmDEBUG_FL as 0 when building in release mode. See \ref debug_mode.
39
+#else  
40
+#define cmDEBUG_FL 1  //< Define cmDEBUG_FL as 1 when building in debug mode. See \ref debug_mode.
41
+#endif
42
+
43
+
44
+  // Perform byte swapping on 16 bit values.
45
+#define cmSwap16(x) \
46
+  (((((unsigned short)(x)) & 0x00ff) << 8) | ((((unsigned short)(x)) & 0xff00) >> 8))
47
+
48
+#ifdef OS_LINUX
49
+#include <byteswap.h>  // gcc specific
50
+#include <unistd.h>
51
+
52
+  // Perform byte swapping on 32 bit values on systems were <byteswap.h> is available.
53
+#define cmSwap32(x) (bswap_32(x))
54
+
55
+  // Perform byte swapping on 64 bit values on systems were <byteswap.h> is  available.
56
+#define cmSwap64(x) (bswap_64(x))
57
+
58
+
59
+#endif
60
+
61
+
62
+#ifdef OS_OSX
63
+#include <unistd.h>
64
+
65
+  // Perform byte swapping on 32 bit values on systems were <byteswap.h> is not available.
66
+#define cmSwap32(x)                             \
67
+  ((((unsigned)((x) & 0x000000FF)) << 24) |   \
68
+    (((unsigned)((x) & 0x0000FF00)) << 8) |   \
69
+    (((unsigned)((x) & 0x00FF0000)) >> 8) |   \
70
+    (((unsigned)((x) & 0xFF000000)) >> 24))
71
+
72
+  // Perform byte swapping on 64 bit values on systems were <byteswap.h> is not available.
73
+#define cmSwap64(x)                                 \
74
+  (((((unsigned long long)(x))<<56) & 0xFF00000000000000ULL)  |   \
75
+    ((((unsigned long long)(x))<<40) & 0x00FF000000000000ULL)  |  \
76
+    ((((unsigned long long)(x))<<24) & 0x0000FF0000000000ULL)  |  \
77
+    ((((unsigned long long)(x))<< 8) & 0x000000FF00000000ULL)  |  \
78
+    ((((unsigned long long)(x))>> 8) & 0x00000000FF000000ULL)  |  \
79
+    ((((unsigned long long)(x))>>24) & 0x0000000000FF0000ULL)  |  \
80
+    ((((unsigned long long)(x))>>40) & 0x000000000000FF00ULL)  |  \
81
+    ((((unsigned long long)(x))>>56) & 0x00000000000000FFULL))
82
+
83
+#endif
84
+
85
+#define cmAllFlags(f,m) (((f) & (m)) == (m))                   //< Test if all of a group 'm' of binary flags in 'f' are set.
86
+#define cmIsFlag(f,m)  (((f) & (m)) ? true : false)            //< Test if any one of a the bits in 'm' is also set in 'f'. 
87
+#define cmIsNotFlag(f,m) (cmIsFlag(f,m)==false)                //< Test if none of the bits in 'm' are set in 'f'.
88
+#define cmSetFlag(f,m) ((f) | (m))                             //< Return 'f' with the bits in 'm' set.
89
+#define cmClrFlag(f,m) ((f) & (~(m)))                          //< Return 'f' with the bits in 'm' cleared.
90
+#define cmTogFlag(f,m) ((f)^(m))                               //< Return 'f' with the bits in 'm' toggled.
91
+#define cmEnaFlag(f,m,b) (b) ? cmSetFlag(f,m) : cmClrFlag(f,m) //< \brief Set or clear bits in 'f' based on bits in 'm' and the state of 'b'.
92
+                                                               //<
93
+                                                               //< If 'b' == 0 then return 'f' with the bits in 'm' cleared.
94
+                                                               //< otherwise return 'f' with the bits in 'm' set.
95
+
96
+
97
+#define cmMin(v0,v1) ((v0)<(v1) ? (v0) : (v1)) //< Return the minimum arg.
98
+#define cmMax(v0,v1) ((v0)>(v1) ? (v0) : (v1)) //< Return the maximum arg.
99
+
100
+
101
+#define cmStringNullGuard(p) ((p)==NULL?"<null>":(p)) //< If 'p'==NULL return the static string "<null>" otherwise return 'p'.
102
+
103
+  // Default return code indicating successful function completion.
104
+#define cmOkRC (0)                  
105
+
106
+  // Default directory separator character for unix based systems.
107
+#define cmPathSeparatorChar ("/")
108
+
109
+#define cmInvalidIdx (0xffffffff)     //< cm wide value indicating a invalid or NULL index.
110
+#define cmInvalidId  (cmInvalidIdx)   //< cm wide value indicating an invalid or NULL numeric id.
111
+#define cmInvalidCnt (cmInvalidIdx)   //< cm wide value indicating a invalid or NULL count of items.
112
+
113
+#define cmSTATIC_NULL_HANDLE {NULL}   //< Default NULL value for cmHandle_t
114
+
115
+  // Generic data type for implementing opaque object handles.
116
+  /*
117
+  typedef struct cmHandle_str
118
+  {
119
+    void* h;
120
+  } cmHandle_t; 
121
+  */
122
+
123
+#define cmHandle_t struct { void* h; } 
124
+
125
+#define cmHandlesAreEqual(    a, b ) ((a).h == (b).h)  //< Test if two cmHandle_t values are equivalent.
126
+#define cmHandlesAreNotEqual( a, b ) (!cmHandlesAreEqual(a,b)) //< Test if two cmHandle_t value are not equivalent.
127
+
128
+  // Data type commonly used as a function return value. Functions returning cmRC_t values 
129
+  // return cmOkRC (0) to indicate successful completion or some other value to indicate 
130
+  // some kind of exceptional conidtion.  In general the condition indicates an unexpected condition
131
+  // such as resource exhaution, or a missing file. 
132
+  typedef unsigned cmRC_t; 
133
+  
134
+  // A data type which indicates a system dependent error.  This is generally an abstraction for an 'errno'
135
+  // like code.
136
+  typedef int      cmSysErrCode_t;  // same as errno
137
+
138
+  
139
+  // cmChar_t is a data type used to indicate that a char is being used to hold human readable
140
+  // text.  Eventually this type will be used to locate and handle unicode based strings.
141
+  typedef char   cmChar_t;
142
+  
143
+
144
+  typedef unsigned int   cmUInt32_t;  //< This typedef is used to indicate that the type must be an  unsigned 32 bit integer.
145
+  typedef unsigned short cmUInt16_t;  //< This typedef is used to indicate that hte type must be an  unsigned 16 bit integer.
146
+
147
+#ifdef __cplusplus
148
+}
149
+#endif
150
+
151
+//)
152
+//}
153
+
154
+#endif

+ 1121
- 0
cmGnuPlot.c
File diff suppressed because it is too large
View File


+ 102
- 0
cmGnuPlot.h View File

@@ -0,0 +1,102 @@
1
+#ifndef cmGnuPlot_h
2
+#define cmGnuPlot_h
3
+
4
+enum
5
+{
6
+  kOkPlRC,
7
+  kSignalFailedPlRC,
8
+  kPipeFailedPlRC,
9
+  kForkFailedPlRC,
10
+  kExecFailedPlRC,
11
+  kPipeCloseFailedPlRC,
12
+  kPlotNotFoundPlRC,
13
+  kNoCurPlotPlRC,
14
+  kPlotDataFileFailPlRC,
15
+  kFopenFailedPlRC,
16
+  kFcloseFailedPlRC
17
+};
18
+
19
+enum
20
+{
21
+  kInvalidPlotPtId    = 0x000,
22
+  kPlusPlotPtId       = 0x001,
23
+  kXPlotPtId          = 0x002,
24
+  kAsteriskPlotPtId   = 0x003,
25
+  kSquarePlotPtId     = 0x004,
26
+  kFillSquarePlotPtId = 0x005,
27
+  kCirclePlotPtId     = 0x006,
28
+  kFillCirclePlotPtId = 0x007,
29
+  kTriPlotPtId        = 0x008,
30
+  kFillTriPlotPtId    = 0x009,
31
+  kInvTriPlotPtId     = 0x00a,
32
+  kInvFillTriPlotPtId = 0x00b,
33
+  kDiamondPlotPtId    = 0x00c,
34
+  kFillDiamonPlotPtId = 0x00d,
35
+  kPlotPtMask         = 0x00f,
36
+};
37
+
38
+enum
39
+{
40
+  kInvalidPlotLineId   = 0x000,  // -2 after translation
41
+  kSolidPlotLineId     = 0x010,  // -1 after translation
42
+  kDashPlotLineId      = 0x020,  //  0 after translation
43
+  kPlotLineMask        = 0x0f0,
44
+  kPlotLineShift       = 4
45
+};
46
+
47
+enum 
48
+{
49
+  kImpulsePlotFl       = 0x100
50
+};
51
+
52
+/// Set terminal to NULL to use the default terminal.
53
+cmRC_t cmPlotInitialize( const char* terminalStr );
54
+
55
+// Combines initializaion and setup in a single call.
56
+cmRC_t cmPlotInitialize2( const char* terminalStr, const char* title, unsigned rowCnt, unsigned colCnt );
57
+
58
+cmRC_t cmPlotFinalize();
59
+
60
+/// Setup the plot page
61
+cmRC_t cmPlotSetup( const char* title, unsigned rowCnt, unsigned colCnt );
62
+
63
+/// Select sub-plot to apply subsequent commands to
64
+cmRC_t cmPlotSelectSubPlot( unsigned ri, unsigned ci );
65
+
66
+/// Clear the current current subplot 
67
+cmRC_t cmPlotClear();
68
+
69
+/// Set the labels on the current sub-plot
70
+cmRC_t cmPlotSetLabels( const char* title, const char* xLabelStr, const char* yLabelStr, const char* zLabelStr ); 
71
+
72
+/// Set the default ranges for the x, y and z axes. To leave the ranges at their current values set the min and max to -1.
73
+/// The range values are used to form data sets when data is not explicitely given.
74
+cmRC_t cmPlotSetRange( double xMin, double xMax, double yMin, double yMax, double zMin, double zMax ); 
75
+
76
+/// If x or y is given as NULL then the values will be taken from the range settings xMin:xMax or yMin:yMax.
77
+/// Use the gnuplot command:'test' to see the valid lineType and pointType values for a given terminal
78
+/// Color string may be any of the predefined color labels: show palette colornames or and rgb value: e.g. #FF00FF
79
+cmRC_t cmPlotLineF( const char* legendStr, const float*  x, const float*  y, const float*  z, unsigned n, const char* colorStr, unsigned styleFlags );
80
+cmRC_t cmPlotLineD( const char* legendStr, const double* x, const double* y, const double* z, unsigned n, const char* colorStr, unsigned styleFlags );
81
+
82
+cmRC_t cmPlotLineMD( const double* x, const double* y, const double* z, unsigned rn, unsigned cn, unsigned styleFlags );
83
+
84
+
85
+#if CM_FLOAT_SMP == 1
86
+#define cmPlotLineS cmPlotLineF
87
+#else
88
+#define cmPlotLineS cmPlotLineD
89
+#endif
90
+
91
+#if CM_FLOAT_REAL == 1
92
+#define cmPlotLineR cmPlotLineF
93
+#else
94
+#define cmPlotLineR cmPlotLineD
95
+#endif
96
+
97
+
98
+cmRC_t cmPlotDraw();
99
+cmRC_t cmPlotPrint( bool printDataFl );
100
+cmRC_t cmPlotDrawAndPrint( bool printDataFl );
101
+
102
+#endif

+ 2670
- 0
cmGr.c
File diff suppressed because it is too large
View File


+ 871
- 0
cmGr.h View File

@@ -0,0 +1,871 @@
1
+#ifndef cmGr_h
2
+#define cmGr_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum 
9
+  {
10
+    kAliceBlueGrId            = 0xf0f8ff,
11
+    kAntiqueWhiteGrId         = 0xfaebd7,
12
+    kAquaGrId                 = 0x00ffff,
13
+    kAquamarineGrId           = 0x7fffd4,
14
+    kAzureGrId                = 0xf0ffff,
15
+    kBeigeGrId                = 0xf5f5dc,
16
+    kBisqueGrId               = 0xffe4c4,
17
+    kBlackGrId                = 0x000000,
18
+    kBlanchedAlmondGrId       = 0xffebcd,
19
+    kBlueGrId                 = 0x0000ff,
20
+    kBlueVioletGrId           = 0x8a2be2,
21
+    kBrownGrId                = 0xa52a2a,
22
+    kBurlyWoodGrId            = 0xdeb887,
23
+    kCadetBlueGrId            = 0x5f9ea0,
24
+    kChartreuseGrId           = 0x7fff00,
25
+    kChocolateGrId            = 0xd2691e,
26
+    kCoralGrId                = 0xff7f50,
27
+    kCornflowerBlueGrId       = 0x6495ed,
28
+    kCornsilkGrId             = 0xfff8dc,
29
+    kCrimsonGrId              = 0xdc143c,
30
+    kCyanGrId                 = 0x00ffff,
31
+    kDarkBlueGrId             = 0x00008b,
32
+    kDarkCyanGrId             = 0x008b8b,
33
+    kDarkGoldenRodGrId        = 0xb8860b,
34
+    kDarkGrayGrId             = 0xa9a9a9,
35
+    kDarkGreyGrId             = 0xa9a9a9,
36
+    kDarkGreenGrId            = 0x006400,
37
+    kDarkKhakiGrId            = 0xbdb76b,
38
+    kDarkMagentaGrId          = 0x8b008b,
39
+    kDarkOliveGreenGrId       = 0x556b2f,
40
+    kDarkorangeGrId           = 0xff8c00,
41
+    kDarkOrchidGrId           = 0x9932cc,
42
+    kDarkRedGrId              = 0x8b0000,
43
+    kDarkSalmonGrId           = 0xe9967a,
44
+    kDarkSeaGreenGrId         = 0x8fbc8f,
45
+    kDarkSlateBlueGrId        = 0x483d8b,
46
+    kDarkSlateGrayGrId        = 0x2f4f4f,
47
+    kDarkSlateGreyGrId        = 0x2f4f4f,
48
+    kDarkTurquoiseGrId        = 0x00ced1,
49
+    kDarkVioletGrId           = 0x9400d3,
50
+    kDeepPinkGrId             = 0xff1493,
51
+    kDeepSkyBlueGrId          = 0x00bfff,
52
+    kDimGrayGrId              = 0x696969,
53
+    kDimGreyGrId              = 0x696969,
54
+    kDodgerBlueGrId           = 0x1e90ff,
55
+    kFireBrickGrId            = 0xb22222,
56
+    kFloralWhiteGrId          = 0xfffaf0,
57
+    kForestGreenGrId          = 0x228b22,
58
+    kFuchsiaGrId              = 0xff00ff,
59
+    kGainsboroGrId            = 0xdcdcdc,
60
+    kGhostWhiteGrId           = 0xf8f8ff,
61
+    kGoldGrId                 = 0xffd700,
62
+    kGoldenRodGrId            = 0xdaa520,
63
+    kGrayGrId                 = 0x808080,
64
+    kGreyGrId                 = 0x808080,
65
+    kGreenGrId                = 0x008000,
66
+    kGreenYellowGrId          = 0xadff2f,
67
+    kHoneyDewGrId             = 0xf0fff0,
68
+    kHotPinkGrId              = 0xff69b4,
69
+    kIndianRedGrId            = 0xcd5c5c,
70
+    kIndigoGrId               = 0x4b0082,
71
+    kIvoryGrId                = 0xfffff0,
72
+    kKhakiGrId                = 0xf0e68c,
73
+    kLavenderGrId             = 0xe6e6fa,
74
+    kLavenderBlushGrId        = 0xfff0f5,
75
+    kLawnGreenGrId            = 0x7cfc00,
76
+    kLemonChiffonGrId         = 0xfffacd,
77
+    kLightBlueGrId            = 0xadd8e6,
78
+    kLightCoralGrId           = 0xf08080,
79
+    kLightCyanGrId            = 0xe0ffff,
80
+    kLightGoldenRodYellowGrId = 0xfafad2,
81
+    kLightGrayGrId            = 0xd3d3d3,
82
+    kLightGreyGrId            = 0xd3d3d3,
83
+    kLightGreenGrId           = 0x90ee90,
84
+    kLightPinkGrId            = 0xffb6c1,
85
+    kLightSalmonGrId          = 0xffa07a,
86
+    kLightSeaGreenGrId        = 0x20b2aa,
87
+    kLightSkyBlueGrId         = 0x87cefa,
88
+    kLightSlateGrayGrId       = 0x778899,
89
+    kLightSlateGreyGrId       = 0x778899,
90
+    kLightSteelBlueGrId       = 0xb0c4de,
91
+    kLightYellowGrId          = 0xffffe0,
92
+    kLimeGrId                 = 0x00ff00,
93
+    kLimeGreenGrId            = 0x32cd32,
94
+    kLinenGrId                = 0xfaf0e6,
95
+    kMagentaGrId              = 0xff00ff,
96
+    kMaroonGrId               = 0x800000,
97
+    kMediumAquaMarineGrId     = 0x66cdaa,
98
+    kMediumBlueGrId           = 0x0000cd,
99
+    kMediumOrchidGrId         = 0xba55d3,
100
+    kMediumPurpleGrId         = 0x9370d8,
101
+    kMediumSeaGreenGrId       = 0x3cb371,
102
+    kMediumSlateBlueGrId      = 0x7b68ee,
103
+    kMediumSpringGreenGrId    = 0x00fa9a,
104
+    kMediumTurquoiseGrId      = 0x48d1cc,
105
+    kMediumVioletRedGrId      = 0xc71585,
106
+    kMidnightBlueGrId         = 0x191970,
107
+    kMintCreamGrId            = 0xf5fffa,
108
+    kMistyRoseGrId            = 0xffe4e1,
109
+    kMoccasinGrId             = 0xffe4b5,
110
+    kNavajoWhiteGrId          = 0xffdead,
111
+    kNavyGrId                 = 0x000080,
112
+    kOldLaceGrId              = 0xfdf5e6,
113
+    kOliveGrId                = 0x808000,
114
+    kOliveDrabGrId            = 0x6b8e23,
115
+    kOrangeGrId               = 0xffa500,
116
+    kOrangeRedGrId            = 0xff4500,
117
+    kOrchidGrId               = 0xda70d6,
118
+    kPaleGoldenRodGrId        = 0xeee8aa,
119
+    kPaleGreenGrId            = 0x98fb98,
120
+    kPaleTurquoiseGrId        = 0xafeeee,
121
+    kPaleVioletRedGrId        = 0xd87093,
122
+    kPapayaWhipGrId           = 0xffefd5,
123
+    kPeachPuffGrId            = 0xffdab9,
124
+    kPeruGrId                 = 0xcd853f,
125
+    kPinkGrId                 = 0xffc0cb,
126
+    kPlumGrId                 = 0xdda0dd,
127
+    kPowderBlueGrId           = 0xb0e0e6,
128
+    kPurpleGrId               = 0x800080,
129
+    kRedGrId                  = 0xff0000,
130
+    kRosyBrownGrId            = 0xbc8f8f,
131
+    kRoyalBlueGrId            = 0x4169e1,
132
+    kSaddleBrownGrId          = 0x8b4513,
133
+    kSalmonGrId               = 0xfa8072,
134
+    kSandyBrownGrId           = 0xf4a460,
135
+    kSeaGreenGrId             = 0x2e8b57,
136
+    kSeaShellGrId             = 0xfff5ee,
137
+    kSiennaGrId               = 0xa0522d,
138
+    kSilverGrId               = 0xc0c0c0,
139
+    kSkyBlueGrId              = 0x87ceeb,
140
+    kSlateBlueGrId            = 0x6a5acd,
141
+    kSlateGrayGrId            = 0x708090,
142
+    kSlateGreyGrId            = 0x708090,
143
+    kSnowGrId                 = 0xfffafa,
144
+    kSpringGreenGrId          = 0x00ff7f,
145
+    kSteelBlueGrId            = 0x4682b4,
146
+    kTanGrId                  = 0xd2b48c,
147
+    kTealGrId                 = 0x008080,
148
+    kThistleGrId              = 0xd8bfd8,
149
+    kTomatoGrId               = 0xff6347,
150
+    kTurquoiseGrId            = 0x40e0d0,
151
+    kVioletGrId               = 0xee82ee,
152
+    kWheatGrId                = 0xf5deb3,
153
+    kWhiteGrId                = 0xffffff,
154
+    kWhiteSmokeGrId           = 0xf5f5f5,
155
+    kYellowGrId               = 0xffff00,
156
+    kYellowGreenGrId          = 0x9acd32
157
+  };
158
+
159
+ 
160
+  typedef enum
161
+  {
162
+    kHomeGrId = 5,       // 5
163
+    kPageUpGrId,         // 6
164
+    kEndGrId,            // 7
165
+    kBackSpaceGrId = 8,  // 8
166
+    kTabGrId       = 9,  // 9 
167
+    kPageDownGrId,       // 10
168
+    kLeftGrId,           // 11
169
+    kUpGrId,             // 12
170
+    kEnterGrId  = 13,    // 13
171
+    kRightGrId,          // 14
172
+    kDownGrId,           // 15
173
+    kInsertGrId,         // 16
174
+    kPrintGrId,          // 17
175
+    kScrollLockGrId,     // 18
176
+    kPauseGrId,          // 19
177
+    kMenuGrId,           // 20
178
+    kLShiftGrId,         // 21
179
+    kRShiftGrId,         // 22
180
+    kLCtrlGrId,          // 23
181
+    kRCtrlGrId,          // 24
182
+    kLAltGrId,           // 25
183
+    kRAltGrId,           // 26
184
+    kEscapeGrId = 27,    // 27
185
+    kLSuperGrId,         // 28
186
+    kRSuperGrId,         // 29
187
+    kNumLockGrId,        // 30
188
+    kCapsLockGrId,       // 31
189
+    kSpaceGrId     = 32, // 32  Min. printable ASCII
190
+    kExclMarkGrId,       // 33
191
+    kDQuoteGrId,         // 34
192
+    kPoundGrId,          // 35
193
+    kDollarGrId,         // 36
194
+    kPercentGrId,        // 37
195
+    kAmpersandGrId,      // 38
196
+    kApostropheGrId,     // 39
197
+    kLParenGrId,         // 40
198
+    kRParenGrId,         // 41
199
+    kAsteriskGrId,       // 42  
200
+    kPlusGrId,           // 43
201
+    kCommaGrId,          // 44
202
+    kHyphenGrId,         // 45
203
+    kPeriodGrId,         // 46
204
+    kForwardSlashGrId,   // 47
205
+    k0GrId,              // 48
206
+    k1GrId,              // 49
207
+    k2GrId,              // 50
208
+    k3GrId,              // 51
209
+    k4GrId,              // 52
210
+    k5GrId,              // 53 
211
+    k6GrId,              // 54
212
+    k7GrId,              // 55
213
+    k8GrId,              // 56
214
+    k9GrId,              // 57
215
+    kColonGrId,          // 58 
216
+    kSemiColonGrId,      // 59
217
+    kLesserGrId,         // 60
218
+    kEqualGrId,          // 61
219
+    kGreaterGrId,        // 62
220
+    kQMarkGrId,          // 63
221
+    kAtGrId,             // 64
222
+    kA_GrId,             // 65
223
+    kB_GrId,             // 66
224
+    kC_GrId,             // 67
225
+    kD_GrId,             // 68
226
+    kE_GrId,             // 69
227
+    kF_GrId,             // 70
228
+    kG_GrId,             // 71
229
+    kH_GrId,             // 72
230
+    kI_GrId,             // 73
231
+    kJ_GrId,             // 74
232
+    kK_GrId,             // 75
233
+    kL_GrId,             // 76
234
+    kM_GrId,             // 77
235
+    kN_GrId,             // 78
236
+    kO_GrId,             // 79
237
+    kP_GrId,             // 80
238
+    kQ_GrId,             // 81
239
+    kR_GrId,             // 82
240
+    kS_GrId,             // 83
241
+    kT_GrId,             // 84
242
+    kU_GrId,             // 85
243
+    kV_GrId,             // 86
244
+    kW_GrId,             // 87
245
+    kX_GrId,             // 88
246
+    kY_GrId,             // 89
247
+    kZ_GrId,             // 90
248
+    kLBracketGrId,       // 91
249
+    kBackSlashGrId,      // 92
250
+    kRBracketGrId,       // 93
251
+    kCaretGrId,          // 94
252
+    kUnderScoreGrId,     // 95
253
+    kAccentGrId,         // 96
254
+    ka_GrId,             // 97
255
+    kb_GrId,             // 98 
256
+    kc_GrId,             // 99 
257
+    kd_GrId,             // 100
258
+    ke_GrId,             // 101
259
+    kf_GrId,             // 102
260
+    kg_GrId,             // 103
261
+    kh_GrId,             // 104
262
+    ki_GrId,             // 105
263
+    kj_GrId,             // 106
264
+    kk_GrId,             // 107
265
+    kl_GrId,             // 108
266
+    km_GrId,             // 109
267
+    kn_GrId,             // 110
268
+    ko_GrId,             // 111
269
+    kp_GrId,             // 112
270
+    kq_GrId,             // 113
271
+    kr_GrId,             // 114
272
+    ks_GrId,             // 115
273
+    kt_GrId,             // 116
274
+    ku_GrId,             // 117
275
+    kv_GrId,             // 118
276
+    kw_GrId,             // 119
277
+    kx_GrId,             // 120
278
+    ky_GrId,             // 121
279
+    kz_GrId,             // 122
280
+    kLBraceGrId,         // 123
281
+    kPipeGrId,           // 124
282
+    kRBraceGrId,         // 125
283
+    kTildeGrId,          // 126
284
+    kDeleteGrId,         // 127
285
+    kNP_MultGrId,        // 128
286
+    kNP_PlusGrId,        // 129
287
+    kNP_MinusGrId,       // 130
288
+    kNP_DecPtGrId,       // 131
289
+    kNP_DivGrId,         // 132
290
+    kNP_0GrId,           // 133
291
+    kNP_1GrId,           // 134
292
+    kNP_2GrId,           // 135
293
+    kNP_3GrId,           // 136
294
+    kNP_4GrId,           // 137
295
+    kNP_5GrId,           // 138
296
+    kNP_6GrId,           // 139
297
+    kNP_7GrId,           // 140
298
+    kNP_8GrId,           // 141
299
+    kNP_9GrId,           // 142
300
+    kNP_EqualGrId,       // 143
301
+    kNP_EnterGrId,       // 144
302
+    kFunc_1GrId,         // 145
303
+    kFunc_2GrId,         // 146
304
+    kFunc_3GrId,         // 147
305
+    kFunc_4GrId,         // 148 
306
+    kFunc_5GrId,         // 149
307
+    kFunc_6GrId,         // 150
308
+    kFunc_7GrId,         // 151
309
+    kFunc_8GrId,         // 152
310
+    kFunc_9GrId,         // 153
311
+    kFunc_10GrId,        // 154
312
+    kFunc_11GrId,        // 155
313
+    kFunc_12GrId,        // 156
314
+    kBrightUpGrId,       // 157
315
+    kBrightDnGrId,       // 158
316
+    kAudio_PrevGrId,     // 159
317
+    kAudio_PlayGrId,     // 160
318
+    kAudio_NextGrId,     // 161
319
+    kAudio_MuteGrId,     // 162
320
+    kAudio_UpGrId,       // 163
321
+    kAudio_DnGrId,       // 164
322
+    kEjectGrId,          // 165
323
+    kInvalidKeyCodeGrId
324
+  } cmGrKeyCodeId_t;
325
+
326
+  enum
327
+  {
328
+    kMinAsciiGrId = kSpaceGrId,
329
+    kMaxAsciiGrId = kDeleteGrId
330
+  };
331
+
332
+  enum
333
+  {
334
+    kOkGrRC,
335
+    kLHeapFailGrRC,
336
+    kAppErrGrRC,
337
+    kRootObjCreateFailGrRC,
338
+    kInvalidCoordsGrRC,
339
+    kExtsErrGrRC
340
+  };
341
+
342
+  enum
343
+  {
344
+    kLeftGrFl   = 0x01,
345
+    kTopGrFl    = 0x02,
346
+    kRightGrFl  = 0x04,
347
+    kBottomGrFl = 0x08,
348
+  };
349
+
350
+  typedef enum
351
+  {
352
+    kLeftGrIdx   = 0,   // min-x
353
+    kTopGrIdx    = 1,   // max-y
354
+    kRightGrIdx  = 2,   // max-x
355
+    kBottomGrIdx = 3,   // min-y
356
+    kAxisGrCnt   = 4
357
+  } cmGrAxisIdx_t;
358
+
359
+
360
+  typedef cmHandle_t cmGrH_t;
361
+  typedef cmHandle_t cmGrObjH_t;
362
+  typedef cmHandle_t cmGrDcH_t;
363
+  typedef unsigned   cmGrRC_t;
364
+
365
+  extern cmGrH_t     cmGrNullHandle;
366
+  extern cmGrObjH_t  cmGrObjNullHandle;
367
+
368
+  typedef cmReal_t cmGrV_t;
369
+
370
+  //====================================================================================================
371
+  
372
+  // Calculate the width and height between two pixels.
373
+  // This implies that the first and last pixel are inside the valid range.
374
+#define cmGrXtoW(x0,x1)        (abs((x1)-(x0))+1)
375
+#define cmGrWtoX(x0,w)         (((x0)+(w))-1)
376
+
377
+#define cmGrYtoH(y0,y1)        (abs((y1)-(y0))+1)
378
+#define cmGrHtoY(y0,h)         (((y0)+(h))-1)
379
+
380
+#define cmGrPIsXInRange(x,x0,w) ((x0)<=(x)&&(x)<=cmGrWtoX((x0),(w)))
381
+#define cmGrPIsYInRange(y,y0,h) ((y0)<=(y)&&(y)<=cmGrHtoY((y0),(h)))
382
+
383
+#define cmGrVIsXInRange(x,x0,w) ((x0)<=(x)&&(x)<=((x0)+(w)))
384
+#define cmGrVIsYInRange(y,y0,h) ((y0)<=(y)&&(y)<=((y0)+(h)))
385
+
386
+  typedef struct
387
+  {
388
+    int x;
389
+    int y;
390
+  } cmGrPPt_t;
391
+
392
+#define cmGrPPtSet( p, xx, yy ) do{ (p)->x=(xx); (p)->y=(yy); }while(0)
393
+#define cmGrPPtIsEqual(p0,p1) ((p0)->x==(p1)->x && (p0)->y==(p1)->y)
394
+#define cmGrPPtPrint(lbl,p) printf("%s x=%i y=%i\n",(lbl),(p)->x,(p)->y)
395
+
396
+  //====================================================================================================
397
+  typedef struct
398
+  {
399
+    int w;
400
+    int h;
401
+  } cmGrPSz_t;
402
+
403
+#define   cmGrPSzSet(      s, ww, hh )          do{ (s)->w=(ww); (s)->h=(hh);}while(0)
404
+#define   cmGrPSzSetD(     s, x0, y0, x1, y1 )  cmGrPSzSet(cmGrXtoW(x0,x1),cmGrYtoH(y0,y1))
405
+
406
+#define   cmGrPSzSetEmpty( s )  ((s)->w = (s)->h =  0)
407
+#define   cmGrPSzSetNull(  s )  ((s)->w = (s)->h = -1)
408
+#define   cmGrPSzIsEmpty(  s )  ((s)->w== 0 && (s)->h== 0)
409
+#define   cmGrPSzIsNull(   s )  ((s)->w==-1 && (s)->h==-1)
410
+#define   cmGrPSzIsEqual(s0,s1) ((s0)->w==(s1)->w && (s0)->h==(s1)->h)
411
+#define   cmGrPSzPrint(lbl,s) printf("%s w=%i h=%i\n",(lbl),(s)->w,(s)->h)
412
+
413
+  //====================================================================================================
414
+  typedef struct
415
+  {
416
+    cmGrPPt_t loc;
417
+    cmGrPSz_t sz;
418
+  } cmGrPExt_t;
419
+
420
+#define   cmGrPExtSet( e, x, y, w, h )    do{ cmGrPPtSet(&(e)->loc,(x),(y)); cmGrPSzSet(&(e)->sz,(w),(h)); }while(0)
421
+#define   cmGrPExtSetD(e, x0, y0, x1, y1) cmGrPExtSet(e,cmMin(x0,x1),cmMin(y0,y1),cmGrXtoW(x0,x1),cmGrYtoH(y0,y1))
422
+
423
+#define   cmGrPExtL(e) ((e)->loc.x)
424
+#define   cmGrPExtT(e) ((e)->loc.y)
425
+#define   cmGrPExtR(e) (cmGrWtoX((e)->loc.x,(e)->sz.w))
426
+#define   cmGrPExtB(e) (cmGrHtoY((e)->loc.y,(e)->sz.h))
427
+#define   cmGrPExtW(e) ((e)->sz.w)
428
+#define   cmGrPExtH(e) ((e)->sz.h)
429
+
430
+#define   cmGrPExtSetL(e,v) ((e)->loc.x = (v))
431
+#define   cmGrPExtSetT(e,v) ((e)->loc.y = (v))
432
+#define   cmGrPExtSetR(e,v) cmGrPExtSetW(e,cmGrXtoW((e)->loc.x,(v)))
433
+#define   cmGrPExtSetB(e,v) cmGrPExtSetH(e,cmGrYtoH((e)->loc.y,(v)))
434
+#define   cmGrPExtSetW(e,v) ((e)->sz.w = (v))
435
+#define   cmGrPExtSetH(e,v) ((e)->sz.h = (v))
436
+
437
+#define   cmGrPExtCtrX(e)   ((e)->loc.x + (e)->sz.w / 2)
438
+#define   cmGrPExtCtrY(e)   ((e)->loc.y + (e)->sz.h / 2)
439
+#define   cmGrPExtCtr(e,pt) do{ (pt)->x=cmGrPExtCtrX(e); (pt)->y=cmGrPExtCtrY(e); }while(0)
440
+
441
+#define   cmGrPExtSetEmpty( e ) do{ cmGrPSzSetEmpty(&(e)->sz); cmGrPPtSet(&(e)->loc,0,0); }while(0)
442
+#define   cmGrPExtSetNull(  e ) do{ cmGrPSzSetNull( &(e)->sz); cmGrPPtSet(&(e)->loc,0,0); }while(0)
443
+#define   cmGrPExtIsEmpty(  e ) cmGrPSzIsEmpty( &(e)->sz )
444
+#define   cmGrPExtIsNull(   e ) cmGrPSzIsNull(  &(e)->sz )
445
+#define   cmGrPExtIsNullOrEmpty(e) (cmGrPExtIsNull(e)||cmGrPExtIsEmpty(e))
446
+#define   cmGrPExtIsNotEmpty(e) (!cmGrPExtIsEmpty(e))
447
+#define   cmGrPExtIsNotNull(e)  (!cmGrPExtIsNull(e))
448
+#define   cmGrPExtIsNotNullOrEmpty(e) (cmGrPExtIsNotNull(e)||cmGrPExtIsNoEmpty(e))
449
+
450
+#define   cmGrPExtIsEqual( e0, e1 ) (cmGrPPtIsEqual(&(e0)->loc,&(e1)->loc) && cmGrPSzIsEqual(&(e0)->sz, &(e1)->sz))
451
+
452
+#define   cmGrPExtIsXyInside( e, xx, yy) (cmGrPIsXInRange((xx),(e)->loc.x,(e)->sz.w) && cmGrPIsYInRange((yy), (e)->loc.y, (e)->sz.h) )
453
+#define   cmGrPExtIsPtInside( e, pt )  (cmGrPExtIsXyInside((e),(pt)->x,(pt)->y))
454
+#define   cmGrPExtIsExtInside(e0, e1)  (cmGrPExtIsPtInside((e0),&((e1)->loc)) && cmGrPExtIsXyInside((e0), cmGrWtoX((e1)->loc.x,(e1)->sz.w), cmGrHtoY((e1)->loc.y,(e1)->sz.h)))
455
+
456
+#define   cmGrPExtExpand(e,l,t,r,b) do{(e)->loc.x+=(l); (e)->loc.y+=(t); (e)->sz.w+=(abs(l)+abs(r)); (e)->sz.h+=(abs(t)+abs(b));}while(0)
457
+
458
+#define   cmGrPExtRpt(e,rpt) cmRptPrintf(rpt,"x:%i y:%i w:%i h:%i",(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
459
+#define   cmGrPExtPrint(lbl,e) printf("%s %i %i %i %i\n",lbl,(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
460
+
461
+  void      cmGrPExtIntersect( cmGrPExt_t* r, const cmGrPExt_t* e0, const cmGrPExt_t* e1 );
462
+   
463
+
464
+  //====================================================================================================
465
+
466
+  typedef struct
467
+  {
468
+    cmGrV_t x;
469
+    cmGrV_t y;
470
+  } cmGrVPt_t;
471
+
472
+#define cmGrVPtSet( p, xx, yy ) do{ (p)->x=(xx); (p)->y=(yy); }while(0)
473
+#define cmGrVPtIsEqual(p0,p1) ((p0)->x==(p1)->x && (p0)->y==(p1)->y)
474
+#define cmGrVPtIsNotEqual(p0,p1) (!cmGrVPtIsEqual(p0,p1))
475
+
476
+  //====================================================================================================
477
+  typedef struct
478
+  {
479
+    cmGrV_t w;
480
+    cmGrV_t h;
481
+  } cmGrVSz_t;
482
+
483
+#define   cmGrVSzSet(      s, ww, hh )          do{ (s)->w=(ww); (s)->h=(hh);}while(0)
484
+#define   cmGrVSzSetD(     s, x0, y0, x1, y1 )  cmGrVSzSet((x1)-(x0),(y1)-(y0))
485
+
486
+#define   cmGrVSzSetEmpty( s ) ((s)->w = (s)->h =  0)
487
+#define   cmGrVSzSetNull(  s ) ((s)->w = (s)->h = -1)
488
+#define   cmGrVSzIsEmpty(  s ) ((s)->w== 0 && (s)->h== 0)
489
+#define   cmGrVSzIsNull(   s ) ((s)->w==-1 || (s)->h==-1)
490
+#define   cmGrVSzIsEqual(s0,s1) ((s0)->w==(s1)->w && (s0)->h==(s1)->h)
491
+
492
+  //====================================================================================================
493
+  typedef struct
494
+  {
495
+    cmGrVPt_t loc;  
496
+    cmGrVSz_t sz;
497
+  } cmGrVExt_t;
498
+
499
+#define   cmGrVExtIsNorm( e )             ((e)->sz.w>=0 && (e)->sz.h>=0)
500
+#define   cmGrVExtNorm(   e )             do{ if( cmGrVExtIsNotNull(e) ){ if((e)->sz.w<0){(e)->loc.x += (e)->sz.w; (e)->sz.w*=-1;} if((e)->sz.h<0){(e)->loc.y += (e)->sz.h; (e)->sz.h*=-1;}} }while(0)
501
+#define   cmGrVExtSet( e, x, y, w, h )    do{ cmGrVPtSet(&(e)->loc,(x),(y)); cmGrVSzSet(&(e)->sz,(w),(h)); cmGrVExtNorm(e); }while(0)
502
+#define   cmGrVExtSetD(e, x0, y0, x1, y1) cmGrVExtSet((e),(x0),(y0),(x1)-(x0),(y1)-(y0))
503
+
504
+  // 
505
+  //  l,t      minx,maxy   
506
+  //      r,b            maxx,miny
507
+  //
508
+#define   cmGrVExtMinX(e) ((e)->loc.x)
509
+#define   cmGrVExtMinY(e) ((e)->loc.y)
510
+#define   cmGrVExtMaxX(e) ((e)->loc.x + (e)->sz.w)
511
+#define   cmGrVExtMaxY(e) ((e)->loc.y + (e)->sz.h)
512
+#define   cmGrVExtW(e) ((e)->sz.w)
513
+#define   cmGrVExtH(e) ((e)->sz.h)
514
+
515
+#define   cmGrVExtSetMinX(e,v) ((e)->loc.x = (v))
516
+#define   cmGrVExtSetMinY(e,v) ((e)->loc.y = (v))
517
+
518
+  // Beware: setting maxx and maxy depends on the current value of minx and miny.
519
+  // If both minx and maxx are being changed then be sure to set minx first.
520
+  // If both miny and maxy are being changed then be sure to set miny first.
521
+#define   cmGrVExtSetMaxX(e,v) ((e)->sz.w  = (v) - cmGrVExtMinX(e))
522
+#define   cmGrVExtSetMaxY(e,v) ((e)->sz.h  = (v) - cmGrVExtMinY(e))
523
+#define   cmGrVExtSetW(e,v)    ((e)->sz.w  = (v))
524
+#define   cmGrVExtSetH(e,v)    ((e)->sz.h  = (v))
525
+
526
+#define   cmGrVExtSetEmpty( e )       do{ cmGrVSzSetEmpty(&(e)->sz); cmGrVPtSet(&(e)->loc,0,0); }while(0)
527
+#define   cmGrVExtSetNull(  e )       do{ cmGrVSzSetNull(&(e)->sz);  cmGrVPtSet(&(e)->loc,0,0); }while(0)
528
+#define   cmGrVExtIsEmpty(  e )       cmGrVSzIsEmpty(&(e)->sz)
529
+#define   cmGrVExtIsNull(   e )       cmGrVSzIsNull( &(e)->sz)
530
+#define   cmGrVExtIsNullOrEmpty(e)    (cmGrVExtIsNull(e)||cmGrVExtIsEmpty(e))
531
+#define   cmGrVExtIsNotEmpty(e)       (!cmGrVExtIsEmpty(e))
532
+#define   cmGrVExtIsNotNull(e)        (!cmGrVExtIsNull(e))
533
+#define   cmGrVExtIsNotNullOrEmpty(e) (cmGrVExtIsNotNull(e)||cmGrVExtIsNotEmpty(e))
534
+#define   cmGrVExtIsEqual( e0, e1 )   (cmGrVPtIsEqual(&(e0)->loc,&(e1)->loc) && cmGrVSzIsEqual(&(e0)->sz, &(e1)->sz))
535
+
536
+
537
+#define   cmGrVExtIsXyInside( e, xx, yy) (cmGrVIsXInRange((xx),(e)->loc.x,(e)->sz.w) && cmGrVIsYInRange((yy),(e)->loc.y,(e)->sz.h))
538
+#define   cmGrVExtIsPtInside( e, pt )    (cmGrVExtIsXyInside((e),(pt)->x,(pt)->y))
539
+
540
+  // e1 is inside e0
541
+#define   cmGrVExtIsExtInside(e0, e1)    (cmGrVExtIsXyInside((e0),cmGrVExtMinX(e1),cmGrVExtMinY(e1)) && cmGrVExtIsXyInside((e0), cmGrVExtMaxX(e1), cmGrVExtMaxY(e1)))
542
+
543
+#define   cmGrVExtRpt(e,rpt) cmRptPrintf(rpt,"x:%f y:%f w:%f h:%f",(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
544
+#define   cmGrVExtPrint(lbl,e) printf("%s %f %f %f %f\n",lbl,(e)->loc.x,(e)->loc.y,(e)->sz.w,(e)->sz.h)
545
+
546
+
547
+  // Shift and expand e0 to contain e1.  Return true if e0 actually changes.
548
+  bool    cmGrVExtExpandToContain( cmGrVExt_t* e0, const cmGrVExt_t* e1 );
549
+
550
+  // Force e1 to be contained by e0 by shifting e1's location. This function
551
+  // will never change the width or height of e1. Return true if e1 is changed.
552
+  bool    cmGrVExtContain( const cmGrVExt_t* e0, cmGrVExt_t* e1 );
553
+
554
+  // Return the intersection of 'e0' with 'e1' in 'r'. 
555
+  void    cmGrVExtIntersect( cmGrVExt_t* r, const cmGrVExt_t* e0, const cmGrVExt_t* e1 );
556
+
557
+  //====================================================================================================
558
+  
559
+
560
+#define cmGrRgbToColor( r, g, b )  (((r) << 16) + ((g) << 8) + (b))
561
+#define cmGrColorToR(   c )        (((c) >> 16) & 0x000000ff) 
562
+#define cmGrColorToG(   c )        (((c) >>  8) & 0x000000ff) 
563
+#define cmGrColorToB(   c )        (((c)      ) & 0x000000ff) 
564
+
565
+  typedef unsigned cmGrColor_t;
566
+  enum { kGrDefaultColorMapIdx = 0, kGrDefaultColorMapId=0 };
567
+
568
+  unsigned        cmGrColorMapCount(    cmGrH_t grH );
569
+  unsigned        cmGrColorMapId(       cmGrH_t grH, unsigned mapIdx );  
570
+  const cmChar_t* cmGrColorMapLabel(    cmGrH_t grH, unsigned id );
571
+  unsigned        cmGrColorMapRegister( cmGrH_t grH, cmChar_t* label, const cmGrColor_t* array, unsigned cnt );     
572
+  cmGrColor_t*    cmGrColorMap(         cmGrH_t grH, unsigned mapId );
573
+  unsigned        cmGrColorMapEleCount( cmGrH_t grH, unsigned mapId );
574
+
575
+  //====================================================================================================
576
+  typedef struct
577
+  {
578
+    cmCtx_t*   ctx;          // application context
579
+    cmGrH_t    grH;          // graphics system handle to which this graphic object belongs
580
+    cmGrObjH_t objH;         // this graphics object handle
581
+    void*      cbArg;        // user callback arg
582
+
583
+    cmGrPPt_t  msDnPPt;      // mouse down phys point
584
+    cmGrVPt_t  msDnVPt;      // mouse down virt point inside op->parent->wext
585
+    cmGrVSz_t  msDnVOffs;    // virtual offset from mouse down point to msDnObj->vext
586
+    cmGrObjH_t msDnObjH;     // handle of object which recv'd mouse down
587
+    cmGrVPt_t  msVPt;        // cur ms virtual point
588
+    
589
+  } cmGrObjFuncArgs_t;
590
+
591
+
592
+  typedef cmGrRC_t (*cmGrCreateObjCb_t)(   cmGrObjFuncArgs_t* args );
593
+  typedef void     (*cmGrDestroyObjCb_t)(  cmGrObjFuncArgs_t* args );
594
+  typedef bool     (*cmGrRenderObjCb_t)(   cmGrObjFuncArgs_t* args, cmGrDcH_t dcH );
595
+  typedef int      (*cmGrDistanceObjCb_t)( cmGrObjFuncArgs_t* args, int x, int y );
596
+  typedef bool     (*cmGrEventObjCb_t)(    cmGrObjFuncArgs_t* args, unsigned flags, unsigned key, int px, int py  );  
597
+  typedef void     (*cmGrVExtObjCb_t)(     cmGrObjFuncArgs_t* args, cmGrVExt_t* vext );
598
+  typedef bool     (*cmGrIsInsideObjCb_t)( cmGrObjFuncArgs_t* args, int px, int py, cmGrV_t vx, cmGrV_t vy );
599
+
600
+  typedef struct cmGrObjFunc_str
601
+  {
602
+    // User defined constructor.
603
+    cmGrCreateObjCb_t  createCbFunc;
604
+    void*              createCbArg;
605
+
606
+    // User defined destructor.
607
+    cmGrDestroyObjCb_t destroyCbFunc;
608
+    void*              destroyCbArg;
609
+
610
+    // Draw the object by calling back to the cmGrDrawXXX() functions
611
+    cmGrRenderObjCb_t  renderCbFunc;
612
+    void*              renderCbArg;
613
+
614
+    // Return the physical distance from a physical view location to the object.
615
+    // (NOT USED)
616
+    cmGrDistanceObjCb_t distanceCbFunc;
617
+    void*               distanceCbArg;
618
+
619
+    // Handle an event. gx,gy are in the same coord's as args.objH.vext (they are inside args.objH.parent.wext).
620
+    // Return true if the event objects dirty flag should be set.
621
+    cmGrEventObjCb_t    eventCbFunc;
622
+    void*               eventCbArg;
623
+
624
+    // Return the objects location and size inside op->parent->wext
625
+    cmGrVExtObjCb_t     vextCbFunc;
626
+    void*               vextCbArg;
627
+
628
+    // Return true if the point is inside this obj.  vx,vy is in the the same coord's as op->vext (i.e. vx,vy is inside op->parent->wext)
629
+    // The simple answer to this call is cmGrVExtIsXyInside( *vext, vx, vy ).
630
+    // Called to determine which object is under the mouse.
631
+    cmGrIsInsideObjCb_t isInsideCbFunc;
632
+    void*               isInsideCbArg;
633
+  } cmGrObjFunc_t;
634
+
635
+
636
+  // Create a graphic object. This function calls the user defined (*create)() function.
637
+  cmGrRC_t   cmGrObjCreate(     cmGrH_t h, cmGrObjH_t* hp, cmGrObjH_t parentH, cmGrObjFunc_t* f, unsigned id, unsigned flags, const cmGrVExt_t* wext );
638
+
639
+  // Destroy a graphic object and all of it's children. 
640
+  // This function calls the user defined (*destroy)() function.
641
+  cmGrRC_t   cmGrObjDestroy(    cmGrH_t h, cmGrObjH_t* hp );
642
+
643
+  // Return true if 'oh' is a valid handle.
644
+  cmGrRC_t   cmGrObjIsValid(    cmGrH_t h, cmGrObjH_t  oh );
645
+
646
+  // Return the user id associated with this object.
647
+  unsigned   cmGrObjId(        cmGrObjH_t oh );
648
+  void       cmGrObjSetId(     cmGrObjH_t oh, unsigned id );
649
+
650
+  // Return the handle to the parent object.
651
+  cmGrObjH_t cmGrObjParent(    cmGrObjH_t oh );
652
+
653
+  // An object world coord's are used to place child objects.
654
+  cmGrRC_t   cmGrObjSetWorldExt( cmGrH_t h, cmGrObjH_t oh, const cmGrVExt_t* vext );
655
+  void       cmGrObjWorldExt( cmGrObjH_t oh, cmGrVExt_t* vext );
656
+
657
+  cmGrRC_t   cmGrObjSetWorldLimitExt( cmGrH_t h, cmGrObjH_t oh, const cmGrVExt_t* vext, unsigned limitFlags );
658
+  void       cmGrObjWorldLimitExt(  cmGrObjH_t oh, cmGrVExt_t* vext, unsigned* limitFlags );
659
+
660
+  void                cmGrObjSetCreateCb(   cmGrObjH_t oh, cmGrCreateObjCb_t   cbFunc, void* cbArg );
661
+  void                cmGrObjSetDestroyCb(  cmGrObjH_t oh, cmGrDestroyObjCb_t  cbFunc, void* cbArg );
662
+  void                cmGrObjSetRenderCb(   cmGrObjH_t oh, cmGrRenderObjCb_t   cbFunc, void* cbArg );
663
+  void                cmGrObjSetDistanceCb( cmGrObjH_t oh, cmGrDistanceObjCb_t cbFunc, void* cbArg );
664
+  void                cmGrObjSetEventCb(    cmGrObjH_t oh, cmGrEventObjCb_t    cbFunc, void* cbArg );
665
+  void                cmGrObjSetVExtCb(     cmGrObjH_t oh, cmGrVExtObjCb_t     cbFunc, void* cbArg );
666
+  void                cmGrObjSetIsInsideCb( cmGrObjH_t oh, cmGrIsInsideObjCb_t cbFunc, void* cbArg );
667
+
668
+  cmGrCreateObjCb_t   cmGrObjCreateCbFunc(   cmGrObjH_t oh );
669
+  cmGrDestroyObjCb_t  cmGrObjDestroyCbFunc(  cmGrObjH_t oh );
670
+  cmGrRenderObjCb_t   cmGrObjRenderCbFunc(   cmGrObjH_t oh );
671
+  cmGrDistanceObjCb_t cmGrObjDistanceCbFunc( cmGrObjH_t oh );
672
+  cmGrEventObjCb_t    cmGrObjEventCbFunc(    cmGrObjH_t oh );
673
+  cmGrVExtObjCb_t     cmGrObjVExtCbFunc(     cmGrObjH_t oh );
674
+  cmGrIsInsideObjCb_t cmGrObjIsInsideCbFunc( cmGrObjH_t oh );
675
+
676
+  void*               cmGrObjCreateCbArg(   cmGrObjH_t oh );
677
+  void*               cmGrObjDestroyCbArg(  cmGrObjH_t oh );
678
+  void*               cmGrObjRenderCbArg(   cmGrObjH_t oh );
679
+  void*               cmGrObjDistanceCbArg( cmGrObjH_t oh );
680
+  void*               cmGrObjEventCbArg(    cmGrObjH_t oh );
681
+  void*               cmGrObjVExtCbArg(     cmGrObjH_t oh );
682
+  void*               cmGrObjIsInsideCbArg( cmGrObjH_t oh );
683
+  
684
+
685
+  // Same as call to user defined (*vect)(). 
686
+  void       cmGrObjLocalVExt( cmGrH_t h, cmGrObjH_t oh, cmGrVExt_t* vext );  
687
+
688
+  // Given an objects id return it's handle.
689
+  cmGrObjH_t cmGrObjIdToHandle( cmGrH_t h, unsigned id );
690
+
691
+  // Move 'aoH' such that it is drawn above 'boH' in the z-order.
692
+  // This means that 'boH' will be drawn before 'aoH'.
693
+  void       cmGrObjDrawAbove( cmGrObjH_t boH, cmGrObjH_t aoH );
694
+
695
+  void       cmGrObjReport(     cmGrH_t h, cmGrObjH_t oh, cmRpt_t* rpt ); 
696
+  void       cmGrObjReportR(    cmGrH_t h, cmGrObjH_t oh, cmRpt_t* rpt ); // print children
697
+
698
+
699
+  //====================================================================================================
700
+  // Drawing Functions - called by objects to draw themselves
701
+
702
+  int      cmGrX_VtoP(     cmGrH_t hh, cmGrObjH_t oh, cmGrV_t y );
703
+  int      cmGrY_VtoP(     cmGrH_t hh, cmGrObjH_t oh, cmGrV_t x );
704
+
705
+  void     cmGrXY_VtoP(    cmGrH_t hh, cmGrObjH_t oh, cmGrV_t x, cmGrV_t y, cmGrPPt_t* rp );
706
+  void     cmGrXYWH_VtoP(  cmGrH_t hh, cmGrObjH_t oh, cmGrV_t x, cmGrV_t y, cmGrV_t w, cmGrV_t h, cmGrPExt_t* pext ); 
707
+  void     cmGrVExt_VtoP(  cmGrH_t hh, cmGrObjH_t oh, const cmGrVExt_t* vext, cmGrPExt_t* pext );
708
+
709
+  void     cmGrXY_PtoV(    cmGrH_t hh, cmGrObjH_t oh, int x, int y, cmGrVPt_t* rp );
710
+  void     cmGrXYWH_PtoV(  cmGrH_t hh, cmGrObjH_t oh, int x, int y, int w, int h, cmGrVExt_t* vext );
711
+  void     cmGrPExt_PtoV(  cmGrH_t hh, cmGrObjH_t oh, const cmGrPExt_t* pext, cmGrVExt_t* vext );
712
+  
713
+  void     cmGrDrawVLine( cmGrH_t hh, cmGrDcH_t dcH, cmGrObjH_t oh, cmGrV_t x0, cmGrV_t y0, cmGrV_t x1, cmGrV_t y1 );
714
+  void     cmGrDrawVRect( cmGrH_t hh, cmGrDcH_t dcH, cmGrObjH_t oh, cmGrV_t x,  cmGrV_t y,  cmGrV_t w, cmGrV_t h );
715
+
716
+  //====================================================================================================
717
+
718
+  // Callback identifiers
719
+  typedef enum
720
+  {
721
+    kCreateCbGrId,
722
+    kDestroyCbGrId,
723
+    kLocalPtCbGrId,
724
+    kGlobalPtCbGrId,
725
+    kPhysExtCbGrId,
726
+    kViewExtCbGrId,
727
+    kSelectExtCbGrId,
728
+    kFocusCbGrId,
729
+    kKeyUpCbGrId,
730
+    kKeyDnCbGrId
731
+  } cmGrCbId_t;
732
+
733
+  // Callback function associated with this canvas.
734
+  typedef void (*cmGrCbFunc_t)( void* arg, cmGrH_t grH, cmGrCbId_t id, unsigned evtFlags, cmGrKeyCodeId_t keycode ); 
735
+
736
+  // Configuration Flags
737
+  enum
738
+  {
739
+    kExpandViewGrFl = 0x01,  // expand the view to show new objects
740
+    kSelectHorzGrFl = 0x02,  // select along x-axis only
741
+    kSelectVertGrFl = 0x04   // select along y-axis only
742
+  };
743
+
744
+  // 'wext' is optional. 
745
+  // 'id' is an arbitrary user definable identifier - although it is used
746
+  // as the view index by cmGrPage().
747
+  cmGrRC_t cmGrCreate( 
748
+    cmCtx_t*          ctx, 
749
+    cmGrH_t*          hp, 
750
+    unsigned          id, 
751
+    unsigned          cfgFlags, 
752
+    cmGrCbFunc_t      cbFunc,
753
+    void*             cbArg,  
754
+    const cmGrVExt_t* wext  );   // Optional internal world extents for this object
755
+
756
+  // Destroy this canvas.
757
+  cmGrRC_t cmGrDestroy( cmGrH_t* hp );
758
+
759
+  // Remove all objects from the root object and restore the canvas to it's default state.
760
+  cmGrRC_t cmGrClear( cmGrH_t h );
761
+
762
+  // Get the root object handle
763
+  cmGrObjH_t cmGrRootObjH( cmGrH_t h );
764
+
765
+  // Get and set the configuration flags (e.g. kExpandViewGrFl | kSelectHorzGrFl | kSelectVertHorzGrFl )
766
+  unsigned cmGrCfgFlags( cmGrH_t h );
767
+  void     cmGrSetCfgFlags( cmGrH_t h, unsigned cfgFlags );
768
+
769
+  // Draw the objects on the canvas.
770
+  cmGrRC_t cmGrDraw( cmGrH_t h, cmGrDcH_t dcH );
771
+
772
+  // event flags
773
+  enum
774
+  {
775
+    kMsDownGrFl = 0x0001,
776
+    kMsUpGrFl   = 0x0002,
777
+    kMsMoveGrFl = 0x0004,
778
+    kMsWheelGrFl= 0x0008,
779
+    kMsDragGrFl = 0x0010,
780
+    kMsClickGrFl= 0x0020,
781
+    kKeyDnGrFl  = 0x0040,
782
+    kKeyUpGrFl  = 0x0080,
783
+
784
+    kMsEvtMask  = 0x02f,
785
+    kEvtMask    = 0x00ff,
786
+
787
+    kMsLBtnGrFl = 0x0100,
788
+    kMsCBtnGrFl = 0x0200,
789
+    kMsRBtnGrFl = 0x0400,
790
+    
791
+    kShiftKeyGrFl = 0x0800,
792
+    kAltKeyGrFl   = 0x1000,
793
+    kCtlKeyGrFl   = 0x2000,
794
+  };
795
+
796
+  // Receive a UI event.
797
+  bool     cmGrEvent(  cmGrH_t h, unsigned flags, cmGrKeyCodeId_t key, int x, int y );
798
+
799
+  // Return true if 'h' is valid.
800
+  bool     cmGrIsValid( cmGrH_t h );
801
+
802
+  // Return the user defined 'id' set in cmGrCreate()
803
+  unsigned cmGrId( cmGrH_t h );
804
+
805
+  // Return the last mouse location in root object coordinates.
806
+  const cmGrVPt_t* cmGrGlobalPt( cmGrH_t h );
807
+
808
+  // Return the last mouse location in coordinates of the object the mouse was over.
809
+  const cmGrVPt_t* cmGrLocalPt( cmGrH_t h );
810
+  
811
+
812
+  // The new view extents must fit inside the world extents.
813
+  // Return true if the view extents actually changed.
814
+  bool     cmGrSetViewExtents( cmGrH_t hh, cmGrV_t minx, cmGrV_t miny, cmGrV_t maxx, cmGrV_t maxy );
815
+  bool     cmGrSetViewExtentsE(cmGrH_t h,  const cmGrVExt_t* ext );
816
+  void     cmGrViewExtents( cmGrH_t h, cmGrVExt_t* exts );
817
+  
818
+  // View Location
819
+  // Return true if the phys extents actually changed.
820
+  bool     cmGrSetPhysExtents( cmGrH_t hh, int x, int y, int w, int h );
821
+  bool     cmGrSetPhysExtentsE(cmGrH_t h, const cmGrPExt_t* ext );
822
+  void     cmGrPhysExtents( cmGrH_t h, cmGrPExt_t* exts );
823
+
824
+  // Return some scroll bar values for this canvas.
825
+  // tot=world pixels, vis=vis pixels, max=max scroll pos  pos=cur scroll pos
826
+  // All return values are optional.
827
+  void      cmGrScrollExtents( cmGrH_t h, cmGrPSz_t* tot, cmGrPSz_t* vis, cmGrPSz_t* max, cmGrPPt_t* pos );
828
+
829
+  // Return true if the view location actually changed.
830
+  bool      cmGrSetScrollH( cmGrH_t h, int x );
831
+  int       cmGrScrollH(    cmGrH_t h );
832
+  bool      cmGrSetScrollV( cmGrH_t h, int y );
833
+  int       cmGrScrollV(    cmGrH_t h );
834
+
835
+  // Get the current selection extents.
836
+  // If the selection extents are not valid then the function returns false
837
+  // and sets the return extents to their null state.
838
+  bool      cmGrSelectExtents( cmGrH_t h, cmGrVExt_t* vext, cmGrPExt_t* pext );
839
+
840
+  // Both pts are optional
841
+  void      cmGrSetSelectPoints(cmGrH_t h, const cmGrVPt_t* pt0, const cmGrVPt_t* pt1 );
842
+  void      cmGrSelectPoints(   cmGrH_t h, cmGrVPt_t* pt0, cmGrVPt_t* pt1 );
843
+
844
+  enum { kZoomInGrFl=0x01, kXAxisGrFl=0x02, kYAxisGrFl=0x04, kSelectGrFl=0x08, kShowAllGrFl=0x10 };
845
+
846
+  // 1) If kSelectGrFl is not set then the center 1/3 of the current view
847
+  // becomes the new view.
848
+  // 2) If kSelectGrFl is set then the selection area becomes the view.
849
+  // 3) If kSelectGrFl is set but no selection area exists then 
850
+  // option 1) is selected used and using the selection point as center.
851
+  void      cmGrZoom( cmGrH_t h, unsigned flags );
852
+
853
+  // Synchronize the 'syncGrH'  horz. and/or verical, world,view,select extents to
854
+  // this gr's  extents. Changes to this gr's extents will be automatically
855
+  // applied to 'syncGrH'.
856
+  // If 'syncGrH' was used in a previous call to this function then flags will
857
+  // modify the previously set flags value. 
858
+  // Clear the kHorzSyncFl and kVertSyncFl to disable the synchronization.
859
+  // Set flags to 0 to prevent future sync calls.
860
+  enum { kWorldSyncGrFl=0x01, kViewSyncGrFl=0x02, kSelectSyncGrFl=0x04, kHorzSyncGrFl=0x08, kVertSyncGrFl=0x10 };
861
+  void cmGrSetSync( cmGrH_t h, cmGrH_t syncGrH, unsigned flags );
862
+  
863
+
864
+  void     cmGrReport( cmGrH_t h, cmRpt_t* rpt );
865
+
866
+  
867
+#ifdef __cplusplus
868
+}
869
+#endif
870
+
871
+#endif

+ 530
- 0
cmGrDevCtx.c View File

@@ -0,0 +1,530 @@
1
+#include "cmGlobal.h"
2
+#include "cmFloatTypes.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmGr.h"
9
+#include "cmGrDevCtx.h"
10
+
11
+
12
+cmGrDcH_t cmGrDcNullHandle = cmSTATIC_NULL_HANDLE;
13
+
14
+// cmGrDcRecd is used to store the state of the
15
+// device context on the cmGrDC_t stack.
16
+typedef struct cmGrDcRecd_str
17
+{
18
+  cmGrColor_t color;
19
+  unsigned    fontId;
20
+  unsigned    fontStyle;
21
+  unsigned    fontSize;
22
+  unsigned    penWidth;
23
+  unsigned    penStyle;
24
+
25
+  struct cmGrDcRecd_str* next;
26
+  struct cmGrDcRecd_str* prev;
27
+} cmGrDcRecd_t;
28
+
29
+typedef struct cmGrDc_str
30
+{
31
+  cmErr_t       err;
32
+  cmGrDev_t*    dd;    // device driver used by this context
33
+  void*         ddArg; // user assigned device driver callback arg
34
+  cmGrDcRecd_t* list;  // First recd on the stack (not the top).
35
+  cmGrDcRecd_t* cur;   // Top recd on the stack.
36
+  cmGrPExt_t    pext;  // x,y is offset added to all drawing coordinates
37
+                       // w,h is size of drawing area
38
+} cmGrDc_t;
39
+
40
+// Note: recd's prior to p->cur are available.
41
+// Recd's after p->cur are on the stack.
42
+
43
+cmGrDc_t* _cmGrDcHandleToPtr( cmGrDcH_t h )
44
+{
45
+  cmGrDc_t* p = (cmGrDc_t*)h.h;
46
+  assert( p != NULL );
47
+  return p;
48
+}
49
+
50
+void _cmGrDcRecdPrint( const cmChar_t* label, const cmGrDcRecd_t* r )
51
+{
52
+  printf("%s r:%i g:%i b:%i fid:%i fs:0x%x fsz:%i pw:%i ps:0x%x\n",
53
+    cmStringNullGuard(label),
54
+    cmGrColorToR(r->color),cmGrColorToG(r->color),cmGrColorToB(r->color),
55
+    r->fontId,r->fontStyle,r->fontSize,r->penWidth,r->penStyle);
56
+}
57
+
58
+// Make a duplicate of the current record (if it exists) 
59
+// and insert it prior to the current record.
60
+// make the new record current.
61
+void _cmGrDcPush( cmGrDc_t* p )
62
+{
63
+  if( p->cur == NULL )
64
+  {
65
+    assert( p->list == NULL );
66
+    cmGrDcRecd_t* r = cmMemAllocZ( cmGrDcRecd_t, 1);
67
+    p->dd->get_color( p->ddArg, &r->color );
68
+    r->fontId    = p->dd->get_font_family(p->ddArg );
69
+    r->fontSize  = p->dd->get_font_size(  p->ddArg );
70
+    r->fontStyle = p->dd->get_font_style( p->ddArg );
71
+    r->penWidth  = p->dd->get_pen_width(  p->ddArg );
72
+    r->penStyle  = p->dd->get_pen_style(  p->ddArg );
73
+    p->list = r;
74
+    p->cur  = r;
75
+  }
76
+  else
77
+  {
78
+    cmGrDcRecd_t* r = p->cur->prev;
79
+
80
+    // if no prev recd exists ...
81
+    if( r == NULL )
82
+    {
83
+      // .... then allocate one
84
+      r = cmMemAllocZ( cmGrDcRecd_t, 1 );
85
+      *r           = *p->cur;
86
+      p->cur->prev = r;
87
+      r->next      = p->cur;    
88
+    }
89
+    else
90
+    {
91
+      // ... otherwise use the prev one
92
+      cmGrDcRecd_t* nrp = r->next;
93
+      cmGrDcRecd_t* prp = r->prev;
94
+      *r      = *p->cur;
95
+      r->next = nrp;
96
+      r->prev = prp;
97
+    }
98
+
99
+    // make the new recd the cur recd
100
+    p->cur = r;
101
+  
102
+    // if the new recd is the first on the list
103
+    // then update the list begin pointer
104
+    if( p->cur->prev == NULL )
105
+      p->list = p->cur;
106
+  }
107
+
108
+  //_cmGrDcRecdPrint("push:", p->cur );
109
+
110
+}
111
+
112
+cmGrDcRC_t  _cmGrDcPop(cmGrDc_t* p )
113
+{
114
+  if( p->cur==NULL || p->cur->next == NULL )
115
+    return cmErrMsg(&p->err,kStackFaultGrDcRC,"Cannot pop the last context record off the stack.");
116
+
117
+  p->cur = p->cur->next;
118
+
119
+  p->dd->set_color(       p->ddArg, p->cur->color );
120
+  p->dd->set_font_family( p->ddArg, p->cur->fontId );
121
+  p->dd->set_font_size(   p->ddArg, p->cur->fontSize );
122
+  p->dd->set_font_style(  p->ddArg, p->cur->fontStyle );
123
+  p->dd->set_pen_width(   p->ddArg, p->cur->penWidth );
124
+  p->dd->set_pen_style(   p->ddArg, p->cur->penStyle );
125
+
126
+  //_cmGrDcRecdPrint("pop:", p->cur );
127
+
128
+  return kOkGrDcRC;
129
+}
130
+
131
+cmGrDcRC_t _cmGrDcDestroy( cmGrDc_t* p )
132
+{
133
+  cmGrDcRecd_t* rp = p->list;
134
+  while( rp!=NULL )
135
+  {
136
+    cmGrDcRecd_t* tp = rp->next;
137
+    cmMemFree( rp );
138
+    rp = tp;
139
+  }
140
+
141
+  p->dd->destroy(p->ddArg);
142
+
143
+
144
+  cmMemFree(p);
145
+
146
+  return kOkGrDcRC;
147
+}
148
+
149
+cmGrDcRC_t cmGrDevCtxCreate( cmCtx_t* ctx, cmGrDcH_t* hp, cmGrDev_t* dd, void* ddArg, int x, int y, int w, int h )
150
+{
151
+  cmGrDcRC_t rc;
152
+  if((rc = cmGrDevCtxDestroy(hp)) != kOkGrDcRC )
153
+    return rc;
154
+
155
+  cmGrDc_t* p = cmMemAllocZ(cmGrDc_t,1);
156
+
157
+  cmErrSetup(&p->err,&ctx->rpt,"cmGrDevCtx");
158
+
159
+  p->dd    = dd;
160
+  p->ddArg = ddArg;
161
+ 
162
+  cmGrPExtSet(&p->pext,x,y,w,h);
163
+
164
+  if( dd->create(ddArg,w,h) == false )
165
+  {
166
+    cmErrMsg(&p->err,kDevDrvFailGrDcRC,"Device driver create failed.");
167
+    goto errLabel;
168
+  }
169
+
170
+  _cmGrDcPush(p); // create the default context
171
+
172
+  hp->h = p;
173
+
174
+  errLabel:
175
+  if(rc != kOkGrDcRC )
176
+    _cmGrDcDestroy(p);
177
+
178
+  return rc;
179
+}
180
+
181
+cmGrDcRC_t cmGrDevCtxDestroy( cmGrDcH_t* hp )
182
+{
183
+  cmGrDcRC_t rc;
184
+
185
+  if( hp==NULL || cmGrDevCtxIsValid(*hp)==false )
186
+    return kOkGrDcRC;
187
+
188
+  cmGrDc_t* p = _cmGrDcHandleToPtr(*hp);
189
+  
190
+  if((rc = _cmGrDcDestroy(p)) != kOkGrDcRC )
191
+    return rc;
192
+
193
+  hp->h = NULL;
194
+
195
+  return rc;
196
+}
197
+
198
+bool       cmGrDevCtxIsValid( cmGrDcH_t h )
199
+{ return h.h != NULL; }
200
+
201
+cmGrDcRC_t      cmGrDevCtxResize(   cmGrDcH_t h, int x, int y, int  ww, int hh )
202
+{
203
+  cmGrDcRC_t rc = kOkGrDcRC;
204
+  cmGrDc_t*  p  = _cmGrDcHandleToPtr(h);
205
+
206
+  // store the current drawing context state
207
+  _cmGrDcPush(p);
208
+  
209
+  if( p->dd->create(p->ddArg,ww,hh) == false )
210
+  {
211
+    cmErrMsg(&p->err,kDevDrvFailGrDcRC,"Device driver create failed on resize.");
212
+    goto errLabel;
213
+  }
214
+
215
+  cmGrPExtSet(&p->pext,-x,-y,ww,hh);
216
+
217
+ errLabel:
218
+  // force the current state to be reapplied to the new drawing context
219
+  _cmGrDcPop(p);
220
+
221
+  return rc;
222
+}
223
+
224
+void cmGrDevCtxSize( cmGrDcH_t h, cmGrPExt_t* pext )
225
+{
226
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
227
+  *pext = p->pext;
228
+  pext->loc.x *= -1;
229
+  pext->loc.y *= -1;
230
+}
231
+
232
+void            cmGrDevCtxBeginDraw( cmGrDcH_t h )
233
+{
234
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
235
+  p->dd->begin_draw( p->ddArg );
236
+}
237
+
238
+void            cmGrDevCtxEndDraw(   cmGrDcH_t h )
239
+{
240
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
241
+  p->dd->end_draw( p->ddArg );
242
+}
243
+
244
+void            cmGrDevCtxDraw( cmGrDcH_t h )
245
+{
246
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
247
+  p->dd->draw( p->ddArg, -p->pext.loc.x, -p->pext.loc.y );
248
+}
249
+
250
+
251
+void            cmGrDcPushCtx( cmGrDcH_t h )
252
+{
253
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
254
+  _cmGrDcPush(p);
255
+}
256
+
257
+void            cmGrDcPopCtx(  cmGrDcH_t h )
258
+{ 
259
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
260
+  _cmGrDcPop(p);
261
+}
262
+
263
+
264
+unsigned        cmGrDcColor(        cmGrDcH_t h )
265
+{ 
266
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
267
+  return p->cur->color;
268
+}
269
+
270
+void            cmGrDcSetColorRgb(  cmGrDcH_t h, unsigned char r, unsigned char g, unsigned char b )
271
+{
272
+  cmGrDcSetColor(h,cmGrRgbToColor(r,g,b));
273
+}
274
+
275
+void            cmGrDcSetColor(     cmGrDcH_t h, cmGrColor_t color )
276
+{
277
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
278
+  p->dd->set_color( p->ddArg, color );
279
+}
280
+
281
+unsigned cmGrDcFontFamily(    cmGrDcH_t h )
282
+{
283
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
284
+  return p->cur->fontId;
285
+}
286
+
287
+void            cmGrDcSetFontFamily( cmGrDcH_t h, unsigned fontId )
288
+{
289
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
290
+  p->cur->fontId = fontId;
291
+  p->dd->set_font_family( p->ddArg, fontId );
292
+}
293
+
294
+unsigned        cmGrDcFontStyle(      cmGrDcH_t h )
295
+{
296
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
297
+  return p->cur->fontStyle;
298
+}
299
+
300
+void            cmGrDcSetFontStyle(   cmGrDcH_t h, unsigned style )
301
+{
302
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
303
+  p->cur->fontStyle = style;
304
+  p->dd->set_font_style( p->ddArg, style );
305
+}
306
+
307
+unsigned        cmGrDcFontSize(      cmGrDcH_t h )
308
+{
309
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
310
+  return p->cur->fontSize;
311
+}
312
+
313
+void            cmGrDcSetFontSize(   cmGrDcH_t h, unsigned size )
314
+{
315
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
316
+  p->cur->fontSize = size;
317
+  p->dd->set_font_size( p->ddArg, size );
318
+}
319
+
320
+unsigned        cmGrDcPenWidth(      cmGrDcH_t h )
321
+{
322
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
323
+  return p->cur->penWidth;
324
+}
325
+
326
+void            cmGrDcSetPenWidth(   cmGrDcH_t h, unsigned width )
327
+{
328
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
329
+  p->cur->penWidth = width;
330
+  p->dd->set_pen_width( p->ddArg, width );
331
+}
332
+
333
+unsigned        cmGrDcPenStyle(      cmGrDcH_t h )
334
+{
335
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
336
+  return p->cur->penStyle;
337
+}
338
+
339
+void            cmGrDcSetPenStyle(   cmGrDcH_t h, unsigned style )
340
+{
341
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);
342
+  p->cur->penStyle = style;
343
+  p->dd->set_pen_style( p->ddArg, style );
344
+}
345
+
346
+void            cmGrDcDrawLine(  cmGrDcH_t h, int x0, int y0, int x1, int y1 )
347
+{ 
348
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
349
+  p->dd->draw_line( p->ddArg, x0+p->pext.loc.x, y0+p->pext.loc.y, x1+p->pext.loc.x, y1+p->pext.loc.y );
350
+}
351
+
352
+void            cmGrDcDrawRect(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh )
353
+{
354
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
355
+  p->dd->draw_rect( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
356
+}
357
+
358
+void            cmGrDcDrawRectPExt(     cmGrDcH_t h, const cmGrPExt_t* pext )
359
+{ cmGrDcDrawRect( h, cmGrPExtL(pext), cmGrPExtT(pext), cmGrPExtW(pext), cmGrPExtH(pext) ); }
360
+
361
+void            cmGrDcFillRect(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh )
362
+{
363
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
364
+  p->dd->fill_rect( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
365
+}
366
+
367
+void            cmGrDcDrawEllipse(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh )
368
+{
369
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
370
+  p->dd->draw_ellipse( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
371
+}
372
+
373
+void            cmGrDcFillEllipse(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh )
374
+{
375
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
376
+  p->dd->fill_ellipse( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
377
+}
378
+
379
+void            cmGrDcDrawDiamond(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh )
380
+{
381
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
382
+  p->dd->draw_diamond( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
383
+}
384
+
385
+void            cmGrDcFillDiamond(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh )
386
+{
387
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
388
+  p->dd->fill_diamond( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh );
389
+}
390
+
391
+void            cmGrDcDrawTriangle( cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh, unsigned dirFlag )
392
+{
393
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
394
+  p->dd->draw_triangle( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh, dirFlag );
395
+}
396
+
397
+void            cmGrDcFillTriangle( cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh, unsigned dirFlag )
398
+{
399
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
400
+  p->dd->fill_triangle( p->ddArg, x+p->pext.loc.x, y+p->pext.loc.y, ww, hh, dirFlag );
401
+}
402
+
403
+
404
+
405
+void            cmGrDcMeasure(     cmGrDcH_t h, const cmChar_t* text, cmGrPSz_t* sz )  
406
+{
407
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
408
+  if( text == NULL )
409
+    cmGrPSzSet(sz,0,0);
410
+  else
411
+  {
412
+    unsigned ww,hh;
413
+    p->dd->measure_text( p->ddArg, text, &ww, &hh );
414
+    sz->w = ww;
415
+    sz->h = hh;
416
+  }
417
+}
418
+
419
+void            cmGrDcDrawText(    cmGrDcH_t h, const cmChar_t* text, int x, int y )
420
+{
421
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
422
+  p->dd->draw_text( p->ddArg, text, x+p->pext.loc.x, y+p->pext.loc.y );
423
+}
424
+
425
+void            cmGrDcDrawTextRot( cmGrDcH_t h, const cmChar_t* text, int x, int y, int angle )
426
+{
427
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
428
+  p->dd->draw_text_rot( p->ddArg, text, x+p->pext.loc.x, y+p->pext.loc.y, angle );
429
+}
430
+
431
+
432
+void            cmGrDcReadImage(   cmGrDcH_t h, unsigned char* a, const cmGrPExt_t* pext )
433
+{
434
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
435
+  p->dd->read_image( p->ddArg, a, pext->loc.x+p->pext.loc.x, pext->loc.y+p->pext.loc.y, pext->sz.w, pext->sz.h );
436
+}
437
+
438
+void            cmGrDcDrawImage(  cmGrDcH_t h, const unsigned char* a, const cmGrPExt_t* pext )
439
+{
440
+  cmGrDc_t* p = _cmGrDcHandleToPtr(h);  
441
+  p->dd->draw_image( p->ddArg, a, pext->loc.x+p->pext.loc.x, pext->loc.y+p->pext.loc.y, pext->sz.w, pext->sz.h );
442
+}
443
+
444
+
445
+void  cmGrDcSetFont( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style )
446
+{
447
+  cmGrDcSetFontFamily(h,fontId);
448
+  cmGrDcSetFontSize(  h,size);
449
+  cmGrDcSetFontStyle( h,style);
450
+}
451
+
452
+void  cmGrDcFontSetAndMeasure(cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, cmGrPSz_t* sz )
453
+{
454
+  cmGrDcSetFont(h,fontId,size,style);
455
+  cmGrDcMeasure(h,text,sz);
456
+}
457
+
458
+void  cmGrDcDrawTextJustify(  cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, const cmGrPExt_t* pext, unsigned flags )
459
+{
460
+  int x = cmGrPExtCtrX(pext);
461
+  int y = cmGrPExtCtrY(pext);
462
+
463
+  if( cmIsFlag(flags,kNorthJsGrFl) )
464
+    y = cmGrPExtT(pext);
465
+  else
466
+    if( cmIsFlag(flags,kSouthJsGrFl) )
467
+      y = cmGrPExtB(pext);
468
+
469
+  if( cmIsFlag(flags,kEastJsGrFl) )
470
+    x = cmGrPExtR(pext);
471
+  else
472
+    if( cmIsFlag(flags,kWestJsGrFl) )
473
+      x = cmGrPExtL(pext);
474
+
475
+  cmGrDcDrawTextJustifyPt(h,fontId,size,style,text,flags,x,y);
476
+}
477
+
478
+void   cmGrDcDrawTextJustifyPt(  cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int xx, int yy )
479
+{
480
+  cmGrPSz_t sz;
481
+  cmGrDcFontSetAndMeasure(h, fontId, size, style, text, &sz );
482
+  
483
+  int x,y;
484
+  if( cmIsFlag(flags,kRightJsGrFl) )
485
+    x = xx;
486
+  else
487
+    if( cmIsFlag(flags,kLeftJsGrFl) )
488
+      x = xx - sz.w;
489
+    else
490
+      x = xx - sz.w/2;
491
+
492
+  if( cmIsFlag(flags,kBottomJsGrFl) )
493
+    y = yy;
494
+  else
495
+    if( cmIsFlag(flags,kTopJsGrFl) )
496
+      y = yy + sz.h;
497
+    else
498
+      y = yy + sz.h/2;
499
+
500
+  cmGrDcDrawText(h, text, x+.5, y+.5 );  
501
+
502
+  //cmGrPExt_t pext;
503
+  //cmGrDcDrawTextJustifyRect(h, fontId, size, style, text, flags, xx, yy, &pext );
504
+  //cmGrDcDrawRectPExt(h,&pext);
505
+}
506
+
507
+void   cmGrDcDrawTextJustifyRect( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int xx, int yy, cmGrPExt_t* pext )
508
+{
509
+  cmGrPSz_t sz;
510
+  cmGrDcFontSetAndMeasure(h, fontId, size, style, text, &sz );
511
+  
512
+  int x,y;
513
+  if( cmIsFlag(flags,kRightJsGrFl) )
514
+    x = xx;
515
+  else
516
+    if( cmIsFlag(flags,kLeftJsGrFl) )
517
+      x = xx - sz.w;
518
+    else
519
+      x = xx - sz.w/2;
520
+
521
+  if( cmIsFlag(flags,kBottomJsGrFl) )
522
+    y = yy;
523
+  else
524
+    if( cmIsFlag(flags,kTopJsGrFl) )
525
+      y = yy + sz.h;
526
+    else
527
+      y = yy + sz.h/2;
528
+
529
+  cmGrPExtSet( pext, x, y-sz.h, sz.w+1, sz.h );
530
+}

+ 196
- 0
cmGrDevCtx.h View File

@@ -0,0 +1,196 @@
1
+#ifndef cmGrDevCtx_h
2
+#define cmGrDevCtx_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkGrDcRC = cmOkRC,
11
+    kStackFaultGrDcRC,
12
+    kDevDrvFailGrDcRC
13
+  };
14
+
15
+  typedef cmRC_t     cmGrDcRC_t;
16
+
17
+  extern cmGrDcH_t cmGrDcNullHandle;
18
+
19
+  enum
20
+  {
21
+    kSolidLsGrFl = 0x01,
22
+    kDashLsGrFl  = 0x02,
23
+    kDotLsGrFl   = 0x04
24
+  };
25
+
26
+  enum
27
+  {
28
+    kHelveticaFfGrId,
29
+    kTimesFfGrId,
30
+    kCourierFfGrId,
31
+
32
+    kFontFfCnt
33
+  };
34
+
35
+  enum
36
+  {
37
+    kNormalFsGrFl = 0x00,
38
+    kBoldFsGrFl   = 0x01,
39
+    kItalicFsGrFl = 0x02
40
+  };
41
+
42
+
43
+  typedef struct cmGrDev_str
44
+  {
45
+    // return true on success
46
+    bool            (*create)( void* arg, unsigned w, unsigned h );
47
+    void            (*destroy)( void* arg );
48
+
49
+    void            (*begin_draw)( void* arg );
50
+    void            (*end_draw)(   void* arg );
51
+    void            (*draw)(       void* arg, int x, int y );
52
+
53
+
54
+    void            (*set_color)(  void* arg, const cmGrColor_t c  );
55
+    void            (*get_color)(  void* arg, cmGrColor_t* c );
56
+
57
+    // Return false if the 'font' label is not recognized.
58
+    void            (*set_font_family)(void* arg, unsigned fontId );
59
+    unsigned        (*get_font_family)(void* arg );
60
+
61
+    void            (*set_font_style)( void* arg, unsigned styleFLags );
62
+    unsigned        (*get_font_style)( void* arg );
63
+
64
+    void            (*set_font_size)(  void* arg, unsigned size );
65
+    unsigned        (*get_font_size)(  void* arg );
66
+
67
+    void            (*set_pen_style)(  void* arg, unsigned styleFlags );
68
+    unsigned        (*get_pen_style)(  void* arg );
69
+
70
+    void            (*set_pen_width)(  void* arg, unsigned w );
71
+    unsigned        (*get_pen_width)(  void* arg );
72
+
73
+    void (*draw_line)(     void* arg, int x, int y, int x1, int y1 );
74
+    void (*draw_rect)(     void* arg, int x, int y, unsigned w, unsigned h );
75
+    void (*fill_rect)(     void* arg, int x, int y, unsigned w, unsigned h );
76
+
77
+    // Draw an ellipse, diamond or triangle inside the rectangle formed by l,t,w,h.
78
+    void (*draw_ellipse)(  void* arg, int x, int y, unsigned w, unsigned h );
79
+    void (*fill_ellipse)(  void* arg, int x, int y, unsigned w, unsigned h );
80
+    void (*draw_diamond)(  void* arg, int x, int y, unsigned w, unsigned h );
81
+    void (*fill_diamond)(  void* arg, int x, int y, unsigned w, unsigned h );
82
+    void (*draw_triangle)( void* arg, int x, int y, unsigned w, unsigned h, unsigned dirFlag );
83
+    void (*fill_triangle)( void* arg, int x, int y, unsigned w, unsigned h, unsigned dirFlag );
84
+
85
+    // x,y identifies the left,lower text edge
86
+    void (*draw_text)(    void* arg, const char* text, int x, int y );
87
+    void (*draw_text_rot)(void* arg, const char* text, int x, int y, int angle );
88
+    void (*measure_text)( void* arg, const char* text, unsigned* w, unsigned* h );
89
+
90
+    // Fill p[w*h*3] with RGB data. 
91
+    void (*read_image)( void* arg,       unsigned char* p, int x, int y, unsigned w, unsigned h );
92
+    void (*draw_image)( void* arg, const unsigned char* p, int x, int y, unsigned w, unsigned h );
93
+
94
+  } cmGrDev_t;
95
+
96
+  cmGrDcRC_t      cmGrDevCtxCreate( cmCtx_t* ctx, cmGrDcH_t* hp, cmGrDev_t* dd, void* ddArg, int x, int y, int w, int h );
97
+  cmGrDcRC_t      cmGrDevCtxDestroy(  cmGrDcH_t* hp );
98
+  bool            cmGrDevCtxIsValid(  cmGrDcH_t h );
99
+
100
+  cmGrDcRC_t      cmGrDevCtxResize( cmGrDcH_t h, int x, int y, int ww, int hh );
101
+  void            cmGrDevCtxSize(   cmGrDcH_t h, cmGrPExt_t* pext );
102
+
103
+  void            cmGrDevCtxBeginDraw( cmGrDcH_t h );
104
+  void            cmGrDevCtxEndDraw(   cmGrDcH_t h );
105
+  void            cmGrDevCtxDraw(      cmGrDcH_t h );
106
+
107
+
108
+  void            cmGrDcPushCtx(      cmGrDcH_t h );
109
+  void            cmGrDcPopCtx(       cmGrDcH_t h );
110
+
111
+  unsigned        cmGrDcColor(        cmGrDcH_t h );
112
+  void            cmGrDcSetColorRgb(  cmGrDcH_t h, unsigned char r, unsigned char g, unsigned char b );
113
+  void            cmGrDcSetColor(     cmGrDcH_t h, cmGrColor_t color );
114
+
115
+  unsigned        cmGrDcFontFamily(    cmGrDcH_t h );
116
+  void            cmGrDcSetFontFamily( cmGrDcH_t h, unsigned fontId );
117
+
118
+  unsigned        cmGrDcFontStyle(      cmGrDcH_t h );
119
+  void            cmGrDcSetFontStyle(   cmGrDcH_t h, unsigned style );
120
+  
121
+  unsigned        cmGrDcFontSize(      cmGrDcH_t h );
122
+  void            cmGrDcSetFontSize(   cmGrDcH_t h, unsigned size );
123
+
124
+  unsigned        cmGrDcPenWidth(      cmGrDcH_t h );
125
+  void            cmGrDcSetPenWidth(   cmGrDcH_t h, unsigned width );
126
+
127
+  unsigned        cmGrDcPenStyle(      cmGrDcH_t h );
128
+  void            cmGrDcSetPenStyle(   cmGrDcH_t h, unsigned style );
129
+
130
+  void            cmGrDcDrawLine(     cmGrDcH_t h, int x,  int y, int x1, int y1 );
131
+  // x,y is the upper,left.
132
+  void            cmGrDcDrawRect(     cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh );
133
+  void            cmGrDcDrawRectPExt( cmGrDcH_t h, const cmGrPExt_t* pext );
134
+
135
+  void            cmGrDcFillRect(     cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh );
136
+  void            cmGrDcDrawEllipse(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh );
137
+  void            cmGrDcFillEllipse(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh );
138
+
139
+  void            cmGrDcDrawDiamond(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh );
140
+  void            cmGrDcFillDiamond(  cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh );
141
+
142
+  // Set 'dirFlag' to kTopGrFl,kBottomGrFl,kRightGrFl,kLeftGrFl to indicate 
143
+  // the direction the triangle is pointeed. 
144
+  void            cmGrDcDrawTriangle( cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh, unsigned dirFlag );
145
+  void            cmGrDcFillTriangle( cmGrDcH_t h, int x,  int y,  unsigned ww, unsigned hh, unsigned dirFlag );
146
+
147
+  void            cmGrDcMeasure(     cmGrDcH_t h, const cmChar_t* text, cmGrPSz_t* sz );  
148
+  void            cmGrDcDrawText(    cmGrDcH_t h, const cmChar_t* text, int x, int y );
149
+  void            cmGrDcDrawTextRot( cmGrDcH_t h, const cmChar_t* text, int x, int y, int angle );
150
+
151
+  void            cmGrDcReadImage(   cmGrDcH_t h,       unsigned char* p, const cmGrPExt_t* pext );
152
+  void            cmGrDcDrawImage(   cmGrDcH_t h, const unsigned char* p, const cmGrPExt_t* pext );
153
+
154
+  //
155
+  // Composite Functions
156
+  //
157
+
158
+  void            cmGrDcSetFont( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style );
159
+  void            cmGrDcFontSetAndMeasure(cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, cmGrPSz_t* sz );
160
+
161
+  enum 
162
+  { 
163
+    kLeftJsGrFl    = 0x001, 
164
+    kRightJsGrFl   = 0x002, 
165
+    kTopJsGrFl     = 0x004, 
166
+    kBottomJsGrFl  = 0x008, 
167
+    kHorzCtrJsGrFl = 0x010, 
168
+    kVertCtrJsGrFl = 0x020, 
169
+
170
+    kNorthJsGrFl   = 0x040,
171
+    kEastJsGrFl    = 0x080,
172
+    kSouthJsGrFl   = 0x100,
173
+    kWestJsGrFl    = 0x200
174
+  };
175
+
176
+  // Use compass (NSEW) flags to select the draw point. Defaults to center for both dir's.
177
+  // Use TBLF flags to select the text justification relative to the point.
178
+  // In effect the TBLF flags select the corner of the text to place at the location of
179
+  // the point selected by the NSEW flags.
180
+  // If neither NS flag is set then the vertical point is set to the vertical center.
181
+  // If neither EW flags is set then the horizontal point is set to the horzontal center.
182
+  void            cmGrDcDrawTextJustify(    cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, const cmGrPExt_t* pext, unsigned flags );
183
+
184
+  // Use LBLF to set the justification - the text corner to match to the given point.
185
+  // If neither TL  flag is given then the point is matched to the vertical center of the text.
186
+  // If neither RL  flag is given then the point is matched to the horizontal center of the text.
187
+  void            cmGrDcDrawTextJustifyPt(  cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int x, int y );
188
+
189
+  // Return the rectangle around the text but do not display the text.
190
+  void            cmGrDcDrawTextJustifyRect( cmGrDcH_t h, unsigned fontId, unsigned size, unsigned style, const cmChar_t* text, unsigned flags, int x, int y, cmGrPExt_t* pext );
191
+
192
+#ifdef __cplusplus
193
+}
194
+#endif
195
+
196
+#endif

+ 1471
- 0
cmGrPage.c
File diff suppressed because it is too large
View File


+ 164
- 0
cmGrPage.h View File

@@ -0,0 +1,164 @@
1
+#ifndef cmGrPage_h
2
+#define cmGrPage_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+  
8
+  enum
9
+  {
10
+    kHashMarkGrFl = 0x10,
11
+    kHashLabelGrFl= 0x20
12
+  };
13
+
14
+  typedef cmHandle_t cmGrPgH_t;
15
+  typedef cmHandle_t cmGrVwH_t;
16
+  typedef cmHandle_t cmGrAxH_t;
17
+
18
+  extern cmGrPgH_t   cmGrPgNullHandle;
19
+  extern cmGrVwH_t   cmGrVwNullHandle;
20
+  extern cmGrAxH_t   cmGrAxNullHandle;
21
+
22
+  // Create a cmGrPage object.
23
+  cmGrRC_t cmGrPageCreate( cmCtx_t* ctx, cmGrPgH_t* hp, cmGrCbFunc_t cbFunc, void* cbArg );
24
+
25
+  // Destroy and release the resources assoc'd with a cmGrPage object;
26
+  cmGrRC_t cmGrPageDestroy( cmGrPgH_t* hp );
27
+
28
+  // Return true if the cmGrPage object handle is valid
29
+  bool     cmGrPageIsValid( cmGrPgH_t h );
30
+
31
+  // Remove all objects from the page.
32
+  cmGrRC_t cmGrPageClear( cmGrPgH_t h );
33
+
34
+  // Intialize the count of rows and columns and setup the default layout.
35
+  cmGrRC_t cmGrPageInit( cmGrPgH_t h, const cmGrPExt_t* r, unsigned rn, unsigned cn, cmGrDcH_t dcH  );
36
+
37
+  // Update the position of the views on the page. 
38
+  cmGrRC_t cmGrPageResize( cmGrPgH_t h, const cmGrPExt_t* r, cmGrDcH_t dcH );
39
+
40
+  // Return the current page size and loc'n as set by cmGrPageInit() or cmGrPageResize().
41
+  void     cmGrPageRect(   cmGrPgH_t h, cmGrPExt_t* r );
42
+
43
+  // Return the count of plot views contained by this page. (rn*cn)
44
+  unsigned cmGrPageViewCount( cmGrPgH_t h );
45
+
46
+  // Enable or disable the focus for a given view.
47
+  // Note that the focused view is the view which is the target of controller
48
+  // buttons and scrollbars.  This does not refer to the focused object.
49
+  // Set 'enableFl' if the view is receiving the focus.
50
+  // Clear 'enableFl' if the view is losing focus.
51
+  void      cmGrPageViewFocus( cmGrPgH_t h, unsigned vwIdx, bool enableFl );
52
+
53
+  // Return the view which currently has the focus or cmGrVwNullHandle if
54
+  // no view has the focus.
55
+  cmGrVwH_t cmGrPageFocusedView( cmGrPgH_t h );
56
+
57
+  // 
58
+  void      cmGrPageLayout( cmGrPgH_t h, cmGrDcH_t dcH );
59
+
60
+  // Draw the page.
61
+  void      cmGrPageDraw( cmGrPgH_t h, cmGrDcH_t dcH );
62
+
63
+  typedef void (*cmGrLabelFunc_t)( void* arg, cmChar_t* label, unsigned labelCharCnt, cmGrV_t value );
64
+  // Returns id of the new page label function.
65
+  unsigned         cmGrPageLabelFuncRegister( cmGrPgH_t h, cmGrLabelFunc_t func, void* arg, const cmChar_t* label );      
66
+  unsigned         cmGrPageLabelFuncCount(    cmGrPgH_t h );
67
+  unsigned         cmGrPageLabelFuncIndexToId( cmGrPgH_t h, unsigned index );
68
+  unsigned         cmGrPageLabelFuncLabelToId( cmGrPgH_t h, const cmChar_t* label );
69
+  cmGrLabelFunc_t  cmGrPageLabelFunc(         cmGrPgH_t h, unsigned id );
70
+  const cmChar_t*  cmGrPageLabelFuncLabel(    cmGrPgH_t h, unsigned id );
71
+  void*            cmGrPageLabelFuncArg(      cmGrPgH_t h, unsigned id );
72
+
73
+
74
+  // Get a view handle from the view index.
75
+  cmGrVwH_t cmGrPageViewHandle( cmGrPgH_t h,  unsigned vwIdx );
76
+
77
+  // Get a view handle from to cmGrH_t.
78
+  cmGrVwH_t cmGrPageGrHandleToView( cmGrPgH_t h, cmGrH_t grH );
79
+
80
+  bool      cmGrViewIsValid( cmGrVwH_t h );
81
+
82
+  // Initialize a plot view. title,xLabel, and yLabel are optional.
83
+  cmGrRC_t cmGrViewInit( cmGrVwH_t h, cmGrH_t grH, const cmChar_t* vwTitle, const cmChar_t* xLabel, const cmChar_t* yLabel );
84
+
85
+  // Remove all objects from the view.
86
+  cmGrRC_t cmGrViewClear( cmGrVwH_t h );
87
+
88
+  // Get the plot views physical extents. This function will return the
89
+  // current view location/size only after a call to cmGrPageDraw().
90
+  // See the implementation note at the top of this file.
91
+  cmGrRC_t cmGrViewPExt( cmGrVwH_t h, cmGrPExt_t* pext );
92
+
93
+  bool      cmGrViewHasFocus( cmGrVwH_t h );
94
+  
95
+  // Get the cmGrH_t associated with a view.
96
+  cmGrH_t         cmGrViewGrHandle(      cmGrVwH_t h );
97
+
98
+  // kExpandViewGrFl | kSelectHorzGrFl | kSelectVertGrFl
99
+  void            cmGrViewSetCfg(        cmGrVwH_t h, unsigned cfgFlags );
100
+  unsigned        cmGrViewCfg(           cmGrVwH_t h );
101
+  void            cmGrViewSetTitle(      cmGrVwH_t h, const cmChar_t* title );
102
+  const cmChar_t* cmGrViewTitle(         cmGrVwH_t h );
103
+  void            cmGrViewSetFontFamily( cmGrVwH_t h, unsigned id );
104
+  unsigned        cmGrViewFontFamily(    cmGrVwH_t h );
105
+  void            cmGrViewSetFontStyle(  cmGrVwH_t h, unsigned flags );
106
+  unsigned        cmGrViewFontStyle(     cmGrVwH_t h );
107
+  void            cmGrViewSetFontSize(   cmGrVwH_t h, unsigned size );
108
+  unsigned        cmGrViewFontSize(      cmGrVwH_t h );
109
+
110
+  // Assign a translation function to be used with cmGrViewValue().
111
+  // cmLeftGrIdx or cmGrRightGrIdx is used to assign y axis translation functions.
112
+  // cmTopGrIdx or cmGrBottomGrIdx is used to assign x axis translation functions.
113
+  // 'pgLabelFuncId' must be a valid page label function id as returned from cmGrPageLabelFuncRegister().
114
+  // or cmGrPageLabelFuncIndexToId().
115
+  void            cmGrViewSetLabelFunc(  cmGrVwH_t h, cmGrAxisIdx_t axisId, unsigned pgLabelFuncId );
116
+  
117
+  typedef enum
118
+  {
119
+    kLocalX_VwId,
120
+    kLocalY_VwId,
121
+    kGlobalX_VwId,
122
+    kGlobalY_VwId,
123
+    kSelX0_VwId,
124
+    kSelY0_VwId,
125
+    kSelX1_VwId,
126
+    kSelY1_VwId,
127
+    kSelW_VwId,
128
+    kSelH_VwId
129
+  } cmGrViewValueId_t;
130
+  const cmChar_t* cmGrViewValue( cmGrVwH_t h, cmGrViewValueId_t id, cmChar_t* buf, unsigned bufCharCnt );
131
+
132
+  // Get an axis handle.
133
+  cmGrAxH_t     cmGrViewAxisHandle(    cmGrVwH_t h,  cmGrAxisIdx_t axisIdx  );
134
+  
135
+  bool            cmGrAxisIsValid(            cmGrAxH_t h );
136
+  // kHashMarkGrFl | kHashLabelGrFl
137
+  void            cmGrAxisSetCfg(             cmGrAxH_t h, unsigned cfgFlags );
138
+  unsigned        cmGrAxisCfg(                cmGrAxH_t h );
139
+  void            cmGrAxisSetTitle(           cmGrAxH_t h, const cmChar_t* title );
140
+  const cmChar_t* cmGrAxisTitle(              cmGrAxH_t h  );
141
+  void            cmGrAxisTitleSetFontFamily( cmGrAxH_t h, unsigned id );
142
+  unsigned        cmGrAxisTitleFontFamily(    cmGrAxH_t h );
143
+  void            cmGrAxisTitleSetFontStyle(  cmGrAxH_t h, unsigned flags );
144
+  unsigned        cmGrAxisTitleFontStyle(     cmGrAxH_t h );
145
+  void            cmGrAxisTitleSetFontSize(   cmGrAxH_t h, unsigned size );
146
+  unsigned        cmGrAxisTitleFontSize(      cmGrAxH_t h );
147
+
148
+  void            cmGrAxisLabelSetFontFamily( cmGrAxH_t h, unsigned id );
149
+  unsigned        cmGrAxisLabelFontFamily(    cmGrAxH_t h );
150
+  void            cmGrAxisLabelSetFontStyle(  cmGrAxH_t h, unsigned flags );
151
+  unsigned        cmGrAxisLabelFontStyle(     cmGrAxH_t h );
152
+  void            cmGrAxisLabelSetFontSize(   cmGrAxH_t h, unsigned size );
153
+  unsigned        cmGrAxisLabelFontSize(      cmGrAxH_t h );
154
+
155
+  // Assign a translation function for the value on this axis.  
156
+  // 'pgLabelFuncId' must be a valid page label function id as returned from cmGrPageLabelFuncRegister().
157
+  // or cmGrPageLabelFuncIndexToId().
158
+  void            cmGrAxisSetLabelFunc(      cmGrAxH_t h, unsigned pgLabelFuncId );
159
+
160
+#ifdef __cplusplus
161
+}
162
+#endif
163
+
164
+#endif

+ 1223
- 0
cmGrPlot.c
File diff suppressed because it is too large
View File


+ 260
- 0
cmGrPlot.h View File

@@ -0,0 +1,260 @@
1
+#ifndef cmGrTimeLine_h
2
+#define cmGrTimeLine_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkGrPlRC,
11
+    kObjNotFoundGrPlRC,
12
+    kGrFailGrPlRC,
13
+    kRsrcFailGrPlRC
14
+  };
15
+
16
+  typedef enum
17
+  {
18
+    kInvalidPlId,
19
+    kRectGrPlId,
20
+    kHLineGrPlId,
21
+    kVLineGrPlId,
22
+    kLineGrPlId,
23
+    kEllipseGrPlId,
24
+    kDiamondGrPlId,
25
+    kUTriGrPlId,
26
+    kDTriGrPlId,
27
+    kLTriGrPlId,
28
+    kRTriGrPlId,
29
+    kStarGrPlId,
30
+    kCrossGrPlId,
31
+    kPlusGrPlId,
32
+  } cmGrPlObjTypeId_t;
33
+
34
+  // object cfg flags
35
+  enum
36
+  {
37
+    kSymbolGrPlFl   = 0x0001,  // This object is a symbol
38
+    kNoSelectGrPlFl = 0x0002,  // The clicking with the mouse will not select this object
39
+    kNoDragGrPlFl   = 0x0004,  // Dragging with the mouse will not move this object
40
+    kNoFocusGrPlFl  = 0x0008,  // This object cannot receive focus.
41
+    kNoDrawGrPlFl   = 0x0010,  // Do not draw this object (is is invisible and disabled)
42
+    kNoFillGrPlFl   = 0x0020,  // Do not draw the fill area of this object
43
+    kNoBorderGrPlFl = 0x0040,  // Do not draw the border of this object
44
+    kNoLabelGrPlFl  = 0x0080,  // Do not draw the label for this object
45
+  };
46
+
47
+  // object state flags
48
+  enum
49
+  {
50
+    kVisibleGrPlFl = 0x0001,
51
+    kEnabledGrPlFl = 0x0002,  // Enabled obj's must be visible. 
52
+    kSelectGrPlFl  = 0x0004,  // 
53
+    kFocusGrPlFl   = 0x0008   // Focused obj's are also selected.
54
+  };
55
+
56
+  typedef enum
57
+  {
58
+    kFocusPlGrId, 
59
+    kSelectPlGrId, 
60
+    kEnablePlGrId, 
61
+    kDisablePlGrId, 
62
+
63
+    kMaxPlGrId
64
+  } cmGrPlStateId_t; 
65
+
66
+  enum // TODO: change these into cmGrPlotH_t user settable variables
67
+  {
68
+    kDefaultSymW = 9,
69
+    kDefaultSymH = 9
70
+  };
71
+
72
+  typedef cmHandle_t cmGrPlH_t;  
73
+  typedef cmHandle_t cmGrPlObjH_t;
74
+  typedef cmRC_t     cmGrPlRC_t;
75
+
76
+  // Plot object callback id's
77
+  typedef enum
78
+  {
79
+    kCreatedCbSelGrPlId,
80
+    kDestroyedCbSelGrPlId,
81
+    kPreEventCbSelGrPlId,
82
+    kEventCbSelGrPlId,
83
+    kStateChangeGrPlId,          // See Note with cmGrPlotCbFunc_t below. 
84
+  } cmGrPlCbSelId_t;
85
+  
86
+
87
+  typedef struct
88
+  {
89
+    cmCtx_t*        ctx;          // Global program context.
90
+    void*           cbArg;        // User pointer set in cmGrPlotObjSetCb().
91
+    cmGrPlCbSelId_t selId;        // Callback selector id. See cmGrPlCbSeId_t.
92
+    cmGrPlObjH_t    objH;         // The plot object handle.
93
+    unsigned        eventFlags;   // Event flags from canvas callback (See cmGrEvent() flags)
94
+    cmGrKeyCodeId_t eventKey;     // Event keys (See the cmGrEvent() keys)
95
+    int             eventX;       // Mouse X,Y location when event was generated (Same as cmGrEvent()) 
96
+    int             eventY;       // 
97
+    unsigned        deltaFlags;   // Event caused an object state change (See kXXXGrPlFl flags) 
98
+  } cmGrPlotCbArg_t;
99
+
100
+
101
+  // Return 'false' from kPreEventCbGrPlSelId events to prevent default processing.
102
+  // Note:
103
+  // When this callback is made with the 'kStateChangeGrPlId' the state of
104
+  // the object has not yet been changed.  This may be confusing because if 
105
+  // the state of the object is queried inside the callback it will have the 
106
+  // pre-change state - but this state will be automatically toggled when the 
107
+  // callback returns  'true'.  
108
+  typedef bool (*cmGrPlotCbFunc_t)( cmGrPlotCbArg_t* arg );
109
+
110
+  extern cmGrPlH_t    cmGrPlNullHandle;
111
+  extern cmGrPlObjH_t cmGrPlObjNullHandle;
112
+
113
+  // Notes:
114
+  // 1) Set kSymbolGrPlFl to create a symbol.
115
+  // 2) If kSymbolGrPlFl is set then w and h are taken as the physical size 
116
+  //    of the symbol. Set w and h to 0 to use the default symbols size
117
+  //    kDefaultSymW, kDefaultSymH
118
+  
119
+  cmGrPlRC_t      cmGrPlotObjCreate( 
120
+    cmGrPlH_t         plH,          // Owner Plot Object Manager. See cmGrPlotCreate().
121
+    cmGrH_t           grH,          // The canvas this object will be drawn on.
122
+    cmGrPlObjH_t*     ohp,          // Pointer to the new objects handle (optional)
123
+    unsigned          id,           // User defined identifier.
124
+    cmGrPlObjH_t      parentObjH,   // Containing parent object.
125
+    cmGrPlObjH_t      xAnchorObjH,  // x is taken as an offset from this obj's x coord (optional).
126
+    cmGrPlObjH_t      yAnchorObjH,  // y is taken as an offset from this obj's y coord (optional).
127
+    cmGrPlObjTypeId_t typeId,       // See cmGrPlObjTypeId_t
128
+    unsigned          cfgFlags,     // 
129
+    cmReal_t          x,            // Coord's within the parent's world coord system.
130
+    cmReal_t          y,            //
131
+    cmReal_t          w,            // 
132
+    cmReal_t          h,            // 
133
+    const cmChar_t*   label,        // Object text string (optional) 
134
+    const cmGrVExt_t* wext );       // This objects internal world extents (optional)
135
+
136
+  cmGrPlRC_t        cmGrPlotObjDestroy(       cmGrPlObjH_t* ohp );
137
+  bool              cmGrPlotObjIsValid(       cmGrPlObjH_t oh );
138
+
139
+  cmGrPlH_t         cmGrPlotObjMgrHandle(     cmGrPlObjH_t oh );
140
+  cmGrObjH_t        cmGrPlotObjHandle(        cmGrPlObjH_t oh );
141
+
142
+  void              cmGrPlotObjSetId(         cmGrPlObjH_t oh, unsigned id );
143
+  unsigned          cmGrPlotObjId(            cmGrPlObjH_t oh );
144
+
145
+  void              cmGrPlotObjSetUserPtr(    cmGrPlObjH_t oh, void* userPtr );
146
+  void*             cmGrPlotObjUserPtr(       cmGrPlObjH_t oh );
147
+
148
+  void              cmGrPlotObjSetLabel(      cmGrPlObjH_t oh, const cmChar_t* label );
149
+  const cmChar_t*   cmGrPlotObjLabel(         cmGrPlObjH_t oh );
150
+  // Set flags to kXXXJsGrFl values.  See cmGrDrawTextJustify for their meaning.
151
+  // 'color' is optional
152
+  void              cmGrPlotObjSetLabelAttr(  cmGrPlObjH_t oh, unsigned flags, int angle, const cmGrColor_t color );
153
+  unsigned          cmGrPlotObjLabelFlags(    cmGrPlObjH_t oh );
154
+  int               cmGrPlotObjLabelAngle(    cmGrPlObjH_t oh );
155
+  const cmGrColor_t cmGrPlotObjLabelColor(  cmGrPlObjH_t oh );
156
+  
157
+
158
+  void              cmGrPlotObjSetStateFlags( cmGrPlObjH_t oh, unsigned flags );
159
+  unsigned          cmGrPlotObjStateFlags(    cmGrPlObjH_t oh  );
160
+
161
+  void              cmGrPlotObjSetCfgFlags(   cmGrPlObjH_t oh, unsigned flags );
162
+  void              cmGrPlotObjClrCfgFlags(   cmGrPlObjH_t oh, unsigned flags );
163
+  void              cmGrPlotObjTogCfgFlags(   cmGrPlObjH_t oh, unsigned flags );
164
+  unsigned          cmGrPlotObjCfgFlags(      cmGrPlObjH_t oh );
165
+
166
+  cmGrPlRC_t        cmGrPlotObjSetPhysExt(    cmGrPlObjH_t oh, int  loffs, int  toffs, int  roffs, int  boffs );
167
+  void              cmGrPlotObjPhysExt(       cmGrPlObjH_t oh, int* loffs, int* toffs, int* roffs, int* boffs  );
168
+
169
+  void              cmGrPlotObjVExt(          cmGrPlObjH_t oh, cmGrVExt_t* vext  );
170
+
171
+  void              cmGrPlotObjSetFontFamily( cmGrPlObjH_t h, unsigned id );
172
+  unsigned          cmGrPlotObjFontFamily(    cmGrPlObjH_t h );
173
+  void              cmGrPlotObjSetFontSize(   cmGrPlObjH_t h, unsigned size );
174
+  unsigned          cmGrPlotObjFontSize(      cmGrPlObjH_t h );
175
+  void              cmGrPlotObjSetFontStyle(  cmGrPlObjH_t h, unsigned flags );
176
+  unsigned          cmGrPlotObjFontStyle(     cmGrPlObjH_t h );
177
+  void              cmGrPlotObjSetFont(       cmGrPlObjH_t h, unsigned id, unsigned size, unsigned style );
178
+
179
+  void              cmGrPlotObjSetLineColor(  cmGrPlObjH_t h, cmGrPlStateId_t id, const cmGrColor_t c );
180
+  const cmGrColor_t cmGrPlotObjLineColor(     cmGrPlObjH_t h, cmGrPlStateId_t id );
181
+  const cmGrColor_t cmGrPlotObjCurLineColor(  cmGrPlObjH_t h );
182
+
183
+  void              cmGrPlotObjSetFillColor(  cmGrPlObjH_t h, cmGrPlStateId_t id, const cmGrColor_t c );
184
+  const cmGrColor_t cmGrPlotObjFillColor(     cmGrPlObjH_t h, cmGrPlStateId_t id );
185
+  const cmGrColor_t cmGrPlotObjCurFillColor(  cmGrPlObjH_t h );
186
+
187
+  void              cmGrPlotObjSetCb(        cmGrPlObjH_t h, cmGrPlotCbFunc_t func, void* arg );
188
+  cmGrPlotCbFunc_t  cmGrPlotObjCbFunc(       cmGrPlObjH_t h );
189
+  void*             cmGrPlotObjCbArg(        cmGrPlObjH_t h );
190
+
191
+  
192
+  // Draw aH above bH in the z-order.
193
+  void            cmGrPlotObjDrawAbove(     cmGrPlObjH_t bH, cmGrPlObjH_t aH );
194
+
195
+  //----------------------------------------------------------------------------
196
+  // Plot Object Manager Functions
197
+  //----------------------------------------------------------------------------
198
+  cmGrPlRC_t   cmGrPlotCreate(  cmCtx_t* ctx, cmGrPlH_t* hp );
199
+  cmGrPlRC_t   cmGrPlotDestroy( cmGrPlH_t* hp );
200
+  bool         cmGrPlotIsValid( cmGrPlH_t h );
201
+  cmGrPlRC_t   cmGrPlotClear(   cmGrPlH_t h ); // destroy all objects
202
+  cmErr_t*     cmGrPlotErr(     cmGrPlH_t h );
203
+  cmRpt_t*     cmGrPlotRpt(     cmGrPlH_t h );
204
+  unsigned     cmGrPlotObjectCount(   cmGrPlH_t h );
205
+  cmGrPlObjH_t cmGrPlotObjectIndexToHandle( cmGrPlH_t h, unsigned index );
206
+  cmGrPlObjH_t cmGrPlotObjectIdToHandle( cmGrPlH_t h, unsigned id );
207
+
208
+  // Pass a keyboard event to the plot system.
209
+  void         cmGrPlotKeyEvent(   cmGrPlH_t h, cmGrH_t grH, unsigned eventFlags, cmGrKeyCodeId_t keycode );
210
+  
211
+
212
+
213
+#ifdef __cplusplus
214
+}
215
+#endif
216
+
217
+#endif
218
+
219
+
220
+/*
221
+Plot Object Attributes:
222
+
223
+Location: x,y
224
+Size:     w,h
225
+
226
+Shape:
227
+  rectangle:
228
+  ellipse:
229
+  line:
230
+  hline:
231
+  vline:
232
+  symbol:
233
+
234
+Parent: Defines the world coordinate system in which this object is drawn.
235
+Children are always fully contained by their parent and may not be dragged
236
+outside of their parent.
237
+
238
+Label: 
239
+Border Color: One per state (enabled,selected,focused)
240
+Fill Color:   One per state (enabled,selected,focused)
241
+State:
242
+  Visible:  Draw this object.
243
+  Enabled:  Disabled objects cannot be selected,focused, or dragged.
244
+  Selected: Multiple objects may be selected. 
245
+  Focused:  Only one object may be focused.
246
+
247
+Physical Offsets: Physical offsets which expand the size of the object.
248
+Font Family:
249
+Font Style:
250
+Font Size:
251
+Cfg Flags:
252
+  No Drag:   Do not allow dragging.
253
+  No Select: Do not allow selection.
254
+  No Focus:  Do not allow focusing.
255
+  No Draw:   Do not draw this object (automatically disabled the object)
256
+  No Fill:   Do not draw fill color.
257
+  No Border: Do not draw border.
258
+  No Label:  Do not draw label.  
259
+
260
+ */

+ 180
- 0
cmGrPlotAudio.c View File

@@ -0,0 +1,180 @@
1
+#include "cmGlobal.h"
2
+#include "cmFloatTypes.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmGr.h"
9
+#include "cmGrDevCtx.h"
10
+#include "cmGrPlot.h"
11
+
12
+#include "cmAudioFile.h"
13
+#include "cmAudioFileMgr.h"
14
+#include "cmGrPlotAudio.h"
15
+
16
+typedef struct
17
+{
18
+  cmGrPlObjH_t       oH;
19
+  cmAfmFileH_t       afH;
20
+  unsigned           chIdx;
21
+
22
+  cmGrRenderObjCb_t  renderCbFunc;
23
+  void*              renderCbArg;
24
+
25
+  cmGrDestroyObjCb_t destroyCbFunc;  
26
+  void*              destroyCbArg;
27
+
28
+  cmGrIsInsideObjCb_t isInsideCbFunc;
29
+  void*               isInsideCbArg;
30
+
31
+  void*       mem;
32
+  cmGrPExt_t  pext;
33
+  unsigned    pixN;
34
+  cmSample_t* fMinV;
35
+  cmSample_t* fMaxV;
36
+  int*        iMinV;
37
+  int*        iMaxV;
38
+
39
+} cmGrPlObjAf_t;
40
+
41
+cmGrPlRC_t _cmGrPlObjAfCalcImage( cmGrPlObjAf_t* op, cmGrH_t grH )
42
+{
43
+  cmGrPlRC_t rc     = kOkGrPlRC;
44
+  cmGrObjH_t grObjH = cmGrPlotObjHandle(op->oH);
45
+  cmGrVExt_t vwExt,objExt,drExt;
46
+
47
+  // get the intersection of the view and this audio object
48
+  cmGrViewExtents( grH, &vwExt );
49
+  cmGrPlotObjVExt( op->oH, &objExt );
50
+  cmGrVExtIntersect(&drExt,&vwExt,&objExt);
51
+
52
+  // if the audio object is visible
53
+  if( cmGrVExtIsNotNullOrEmpty(&drExt) )
54
+  {
55
+    // get the extents of the visible portion of the audio object
56
+    cmGrVExt_VtoP( grH, cmGrPlotObjHandle(op->oH), &drExt, &op->pext);
57
+
58
+    // store the count of horizontal pixels
59
+    op->pixN    = op->pext.sz.w;
60
+
61
+    // allocate a cache to hold the image data
62
+    unsigned byteCnt = op->pixN * 2 * sizeof(int) + op->pixN * 2 * sizeof(cmSample_t);
63
+    op->mem   = cmMemResize(char,op->mem,byteCnt);
64
+    op->fMinV = (cmSample_t*)op->mem;
65
+    op->fMaxV = op->fMinV + op->pixN;
66
+    op->iMinV = (int*)(op->fMaxV + op->pixN);
67
+    op->iMaxV = op->iMinV + op->pixN;
68
+    assert( op->iMaxV + op->pixN == op->mem + byteCnt );
69
+
70
+    // locate the offset into the file of the first sample to be displayed
71
+    unsigned si = 0;
72
+    if( drExt.loc.x > objExt.loc.x )
73
+      si = drExt.loc.x - objExt.loc.x;
74
+
75
+   
76
+    // get the floating point audio summary signal
77
+    if( cmAfmFileGetSummary( op->afH, op->chIdx, si, drExt.sz.w, op->fMinV, op->fMaxV, op->pixN ) != kOkAfmRC )
78
+    {
79
+      const cmChar_t* afn = cmAudioFileName( cmAfmFileHandle(op->afH));
80
+      rc = cmErrMsg( cmGrPlotErr( cmGrPlotObjMgrHandle(op->oH) ), kRsrcFailGrPlRC, "Audio file summary read failure on '%s'.",afn);
81
+      goto errLabel;
82
+    }
83
+
84
+    unsigned i;
85
+    // convert the summary to pixels values
86
+    for(i=0; i<op->pixN; ++i)
87
+    {
88
+      // Note the reversal of min and max during the conversion.
89
+      op->iMaxV[i] = cmGrY_VtoP( grH, grObjH, op->fMinV[i] );
90
+      op->iMinV[i] = cmGrY_VtoP( grH, grObjH, op->fMaxV[i] );
91
+    }
92
+  }
93
+ errLabel:
94
+  return rc;
95
+}
96
+
97
+
98
+bool _cmGrPlObjAfRender(   cmGrObjFuncArgs_t* args, cmGrDcH_t dcH )
99
+{
100
+  cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
101
+
102
+  if( _cmGrPlObjAfCalcImage(op, args->grH ) == kOkGrPlRC )
103
+  {
104
+    int i;
105
+    cmGrPExt_t pext;
106
+    cmGrPhysExtents( args->grH, &pext);
107
+
108
+    cmGrDcSetColor(dcH, cmGrPlotObjCurLineColor(op->oH));
109
+
110
+    // draw a horz line at y=0 
111
+    int y0 = cmGrY_VtoP( args->grH, cmGrPlotObjHandle(op->oH), 0.0 );
112
+    cmGrDcDrawLine(dcH, cmGrPExtL(&op->pext), y0, cmGrPExtR(&op->pext) , y0 );
113
+
114
+    // draw a vertical line for each 
115
+    for(i=0; i<op->pixN; ++i)
116
+      cmGrDcDrawLine(dcH, op->pext.loc.x+i, op->iMinV[i], op->pext.loc.x+i, op->iMaxV[i] );        
117
+
118
+
119
+    // draw a rectangle around the entire audio clip
120
+    cmGrDcDrawRect(dcH, op->pext.loc.x, cmGrPExtT(&pext),  op->pext.sz.w, cmGrPExtB(&pext) );
121
+    
122
+    // draw the file label 
123
+    cmGrDcDrawTextJustify( dcH, cmGrPlotObjFontFamily(op->oH), cmGrPlotObjFontSize(op->oH), cmGrPlotObjFontStyle(op->oH), cmGrPlotObjLabel(op->oH), &op->pext, kHorzCtrJsGrFl | kTopJsGrFl );
124
+
125
+  }
126
+  return true;
127
+}
128
+
129
+bool  _cmGrPlObjAfIsInside( cmGrObjFuncArgs_t* args, int px, int py, cmGrV_t vx, cmGrV_t vy )
130
+{
131
+  cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
132
+
133
+  if( cmGrPExtIsXyInside( &op->pext, px, py ) )
134
+  {
135
+    px -= op->pext.loc.x;
136
+    if( 0 <= px && px < op->pixN )
137
+      return op->iMinV[px] <= py && py <= op->iMaxV[px]; 
138
+      
139
+  }
140
+
141
+  return false;
142
+}
143
+
144
+void _cmGrPlObjAfDestroy( cmGrObjFuncArgs_t* args )
145
+{
146
+  cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
147
+  args->cbArg = op->destroyCbArg;
148
+  op->destroyCbFunc(args);  
149
+  cmMemFree(op->mem);
150
+  cmMemFree(op);
151
+}
152
+
153
+cmGrPlRC_t  cmGrPlotAudioFileObjCreate(
154
+  cmGrPlObjH_t oH,
155
+  cmAfmFileH_t afH,
156
+  unsigned     audioChIdx )
157
+{
158
+  cmGrPlObjAf_t* op = cmMemAllocZ(cmGrPlObjAf_t,1);
159
+  op->oH  = oH;
160
+  op->afH = afH;
161
+  op->chIdx = audioChIdx;
162
+
163
+  cmGrObjH_t grObjH = cmGrPlotObjHandle(op->oH);
164
+
165
+  op->renderCbFunc = cmGrObjRenderCbFunc(grObjH);
166
+  op->renderCbArg  = cmGrObjRenderCbArg(grObjH);
167
+  cmGrObjSetRenderCb( grObjH, _cmGrPlObjAfRender, op );
168
+
169
+  op->destroyCbFunc = cmGrObjDestroyCbFunc(grObjH);
170
+  op->destroyCbArg  = cmGrObjDestroyCbArg(grObjH);
171
+  cmGrObjSetDestroyCb( grObjH, _cmGrPlObjAfDestroy, op );
172
+
173
+  op->isInsideCbFunc = cmGrObjIsInsideCbFunc(grObjH);
174
+  op->isInsideCbArg  = cmGrObjIsInsideCbArg(grObjH);
175
+  cmGrObjSetIsInsideCb( grObjH, _cmGrPlObjAfIsInside, op );
176
+
177
+  cmGrPlotObjSetUserPtr(oH,op);
178
+
179
+  return kOkGrPlRC;
180
+}

+ 19
- 0
cmGrPlotAudio.h View File

@@ -0,0 +1,19 @@
1
+#ifndef cmGrPlotAudio_h
2
+#define cmGrPlotAudio_h
3
+
4
+
5
+#ifdef __cplusplus
6
+extern "C" {
7
+#endif
8
+
9
+
10
+  cmGrPlRC_t  cmGrPlotAudioFileObjCreate(
11
+    cmGrPlObjH_t oH,
12
+    cmAfmFileH_t afH,
13
+    unsigned     audioChIdx );
14
+
15
+#ifdef __cplusplus
16
+}
17
+#endif
18
+
19
+#endif

+ 4077
- 0
cmJson.c
File diff suppressed because it is too large
View File


+ 504
- 0
cmJson.h View File

@@ -0,0 +1,504 @@
1
+#ifndef cmJson_h
2
+#define cmJson_h
3
+
4
+
5
+#ifdef __cplusplus
6
+extern "C" {
7
+#endif
8
+  //{
9
+  //(
10
+  //
11
+  //  Limitations:
12
+  //
13
+  //  1. Accpets two digit hex sequences with 
14
+  //  the \\u escape command. JSON specifies 4 digits.
15
+  //
16
+  //  2. The scientific notation for real numbers is limited to
17
+  //  exponent prefixes: e,E,e-,E-. The prefixes e+ and E+ are
18
+  //  not recognized by cmLex.
19
+  //
20
+  //  Extensions:
21
+  //
22
+  //  1. Will accept C style identifiers where JSON demands 
23
+  //  quoted strings.
24
+  //
25
+  //  2. Will accept C style hex notation (0xddd)  for integer values.
26
+  //
27
+  //)
28
+
29
+  //(
30
+
31
+  // JSON data type flags
32
+  enum
33
+  {
34
+    kInvalidTId = 0x0000,
35
+    kObjectTId  = 0x0001,   // children are pairs
36
+    kPairTId    = 0x0002,   // children are string : value pairs
37
+    kArrayTId   = 0x0004,   // children may be of any type
38
+    kStringTId  = 0x0008,   // terminal
39
+    kNullTId    = 0x0040,   // terminal
40
+    kIntTId     = 0x0010,   // terminal
41
+    kRealTId    = 0x0020,   // terminal
42
+    kTrueTId    = 0x0080,   // terminal
43
+    kFalseTId   = 0x0100,   // terminal
44
+    
45
+    kMaskTId    = 0x01ff, 
46
+
47
+    kOptArgJsFl = 0x0800,  // only used by cmJsonVMemberValues()
48
+    kTempJsFl   = 0x1000,  // used internally
49
+
50
+    kNumericTId = kIntTId  | kRealTId | kTrueTId | kFalseTId,
51
+    kBoolTId    = kTrueTId | kFalseTId
52
+
53
+  };
54
+
55
+  enum
56
+  {
57
+    kOkJsRC,
58
+    kMemAllocErrJsRC,
59
+    kLexErrJsRC,
60
+    kSyntaxErrJsRC,
61
+    kFileOpenErrJsRC,
62
+    kFileCreateErrJsRC,
63
+    kFileReadErrJsRC,
64
+    kFileSeekErrJsRC,
65
+    kFileCloseErrJsRC,
66
+    kInvalidHexEscapeJsRC,
67
+    kSerialErrJsRC,
68
+    kNodeNotFoundJsRC,
69
+    kNodeCannotCvtJsRC,
70
+    kCannotRemoveLabelJsRC,
71
+    kInvalidNodeTypeJsRC,
72
+    kValidateFailJsRC,
73
+    kCsvErrJsRC,
74
+    kBufTooSmallJsRC
75
+  };
76
+ 
77
+  typedef unsigned cmJsRC_t;
78
+
79
+  typedef cmHandle_t cmJsonH_t;
80
+
81
+  // JSON tree node
82
+  typedef struct cmJsonNode_str
83
+  {
84
+    
85
+    unsigned               typeId;      // id of this node
86
+    struct cmJsonNode_str* siblingPtr;  // next ele in array or member list
87
+    struct cmJsonNode_str* ownerPtr;    // parent node ptr
88
+
89
+    union
90
+    {
91
+      // childPtr usage:
92
+      // object: first pair
93
+      // array:  first element
94
+      // pair:   string
95
+      struct cmJsonNode_str* childPtr;   
96
+      int                    intVal;     // valid if typeId == kIntTId
97
+      double                 realVal;    // valid if typeId == kRealTId
98
+      char*                  stringVal;  // valid if typeId == kStringTId
99
+      bool                   boolVal;    // valid if typeId == kTrueTId || kFalseTId
100
+    } u;
101
+
102
+  } cmJsonNode_t;
103
+
104
+  extern cmJsonH_t cmJsonNullHandle;
105
+
106
+  // Initialize a json parser/tree object
107
+  cmJsRC_t      cmJsonInitialize( cmJsonH_t* hp, cmCtx_t* ctx );
108
+
109
+  // Equivalent to cmJsonInitialize() followed by cmJsonParseFile()
110
+  cmJsRC_t      cmJsonInitializeFromFile( cmJsonH_t* hp, const char* fn, cmCtx_t* ctx );
111
+
112
+  // Equivalent to cmJsonInitialize() followed by cmJsonParse(h,buf,cnt,NULL).
113
+  cmJsRC_t      cmJsonInitializeFromBuf( cmJsonH_t* hp, cmCtx_t* ctx, const char* buf, unsigned bufByteCnt );
114
+
115
+  // Release all the resources held by the tree.
116
+  cmJsRC_t      cmJsonFinalize(   cmJsonH_t* hp );
117
+
118
+  // Returns true if 'h' is a valid cmJsonH_t handle.
119
+  bool          cmJsonIsValid(    cmJsonH_t h );
120
+
121
+  // Build the internal tree by parsing a text buffer. 
122
+  // altRootPtr is an optional alternate root ptr which can be used
123
+  // append to an existing tree. Set to altRootPtr to
124
+  // NULL to append the tree to the internal root.
125
+  // If altRootPtr is given it must point ot either an array or
126
+  // object node.
127
+  cmJsRC_t      cmJsonParse(      cmJsonH_t h, const char* buf, unsigned bufCharCnt, cmJsonNode_t* altRootPtr );
128
+
129
+  // Fills a text buffer from a file and calls cmJsonParse().
130
+  cmJsRC_t      cmJsonParseFile(  cmJsonH_t h, const char* fn, cmJsonNode_t* altRootPtr );
131
+
132
+  // Return the root node of the internal tree.
133
+  cmJsonNode_t* cmJsonRoot(       cmJsonH_t h );
134
+
135
+  // Return the tree to the post initialize state by clearing the  internal tree.
136
+  cmJsRC_t      cmJsonClearTree(      cmJsonH_t h );
137
+
138
+  // Node type predicates.
139
+  bool cmJsonIsObject( const cmJsonNode_t* np );
140
+  bool cmJsonIsArray(  const cmJsonNode_t* np );
141
+  bool cmJsonIsPair(   const cmJsonNode_t* np );
142
+  bool cmJsonIsString( const cmJsonNode_t* np );
143
+  bool cmJsonIsInt(    const cmJsonNode_t* np );
144
+  bool cmJsonIsReal(   const cmJsonNode_t* np );
145
+  bool cmJsonIsBool(   const cmJsonNode_t* np );
146
+
147
+
148
+  // Return the count of child nodes of 'np'. 
149
+  // Note that only object,array, and pair nodes have children.
150
+  unsigned      cmJsonChildCount( const cmJsonNode_t* np );
151
+
152
+  // Return the node at 'index' from an element array. 
153
+  // 'np must point to an array element.
154
+  cmJsonNode_t* cmJsonArrayElement( cmJsonNode_t* np, unsigned index );
155
+  const cmJsonNode_t* cmJsonArrayElementC( const cmJsonNode_t* np, unsigned index );
156
+
157
+  // Return the child value node of a pair with a label node equal to 'label'.
158
+  // Set 'root' to NULL to begin the search at the internal tree root node. 
159
+  // Set 'typeIdMask' with all type flags to match.
160
+  // If 'typeIdMask' is equal to kInvalidTId then all types will match.
161
+  cmJsonNode_t* cmJsonFindValue(    cmJsonH_t h, const char* label, const cmJsonNode_t* root, unsigned typeIdMask );
162
+
163
+  // Return the value node of a pair at the end of an object path.
164
+  // 'path' is a '/' seperated list of object names where the final
165
+  // object specifies the pair label for the value to return.
166
+  const cmJsonNode_t* cmJsonFindPathValueC( cmJsonH_t h, const char* path, const cmJsonNode_t* root, unsigned typeIdMask );
167
+  cmJsonNode_t*       cmJsonFindPathValue(  cmJsonH_t h, const char* path, const cmJsonNode_t* root, unsigned typeIdMask );
168
+
169
+  // Return the node value. If 'np' does not point to the same type as
170
+  // specified in '*retPtr' then the value is converted if possible.
171
+  // If the value cannot be converted function returns a 'kNodeCannotCvtJsRC'
172
+  // error
173
+  cmJsRC_t cmJsonUIntValue(   const cmJsonNode_t* np, unsigned*       retPtr );
174
+  cmJsRC_t cmJsonIntValue(    const cmJsonNode_t* np, int*            retPtr );
175
+  cmJsRC_t cmJsonRealValue(   const cmJsonNode_t* np, double*         retPtr );
176
+  cmJsRC_t cmJsonBoolValue(   const cmJsonNode_t* np, bool*           retPtr );
177
+  cmJsRC_t cmJsonStringValue( const cmJsonNode_t* np, const char **   retPtrPtr );  
178
+  cmJsRC_t cmJsonPairNode(    const cmJsonNode_t* vp, cmJsonNode_t ** retPtrPtr );
179
+  cmJsRC_t cmJsonArrayNode(   const cmJsonNode_t* vp, cmJsonNode_t ** retPtrPtr );
180
+  cmJsRC_t cmJsonObjectNode(  const cmJsonNode_t* vp, cmJsonNode_t ** retPtrPtr );  
181
+
182
+
183
+  // Return the label from a pair object.
184
+  const char* cmJsonPairLabel(  const cmJsonNode_t* pairPtr );
185
+  unsigned    cmJsonPairTypeId( const cmJsonNode_t* pairPtr );
186
+  cmJsonNode_t* cmJsonPairValue( cmJsonNode_t* pairPtr );
187
+
188
+  // Return values associated with the member values in the object
189
+  // pointed to by object objectNodePtr.
190
+  cmJsRC_t      cmJsonUIntMember(   const cmJsonNode_t* objectNodePtr, const char* label, unsigned* retPtr );
191
+  cmJsRC_t      cmJsonIntMember(    const cmJsonNode_t* objectNodePtr, const char* label, int* retPtr );
192
+  cmJsRC_t      cmJsonRealMember(   const cmJsonNode_t* objectNodePtr, const char* label, double* retPtr );
193
+  cmJsRC_t      cmJsonBoolMember(   const cmJsonNode_t* objectNodePtr, const char* label, bool* retPtr );
194
+  cmJsRC_t      cmJsonStringMember( const cmJsonNode_t* objectNodePtr, const char* label, const char** retPtrPtr );
195
+
196
+  // Returns array or object nodes.
197
+  cmJsRC_t      cmJsonNodeMember(   const cmJsonNode_t* objectNodePtr, const char* label, cmJsonNode_t** nodePtrPtr );
198
+
199
+  // Returns the value of the member pair named by 'label' or NULL if the 
200
+  // named pair does not exist.
201
+  cmJsonNode_t* cmJsonNodeMemberValue( const cmJsonNode_t* np, const char* label );
202
+  
203
+  // Return values for specified pairs from an object node.
204
+  //
205
+  // The var args syntax is: <label>,<typeId>,<valuePtr>.
206
+  // This functionis implemented in terms of cmJsonXXXMember().
207
+  // 
208
+  // Add kOptArgJsFl to <typeId> if the member may not exist in
209
+  // the object - otherwise the function will fail with a
210
+  // kNodeNotFoundJsRC error and errLabelPtr will have the name of the missing pair.
211
+  //
212
+  // Terminate the var args list with NULL.
213
+  //
214
+  // Object,Array, and Pair members are returned as node pointers.
215
+  // 
216
+  // Since kBoolTId does not exist use kTrueTId or kFalseTId to
217
+  // return bool values.
218
+
219
+  cmJsRC_t      cmJsonVMemberValues(const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, va_list vl );
220
+  cmJsRC_t      cmJsonMemberValues( const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, ... );
221
+
222
+  // If objectNodePtr is set to NULL then the  tree root is used as the base for the search.
223
+  // pathPrefix may be set to NULL.
224
+  cmJsRC_t cmJsonPathToValueNode( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, const cmJsonNode_t** nodePtrPtr );
225
+  cmJsRC_t cmJsonPathToBool(   cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, bool* retValPtr );
226
+  cmJsRC_t cmJsonPathToInt(    cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, int* retValPtr );
227
+  cmJsRC_t cmJsonPathToUInt(   cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, unsigned* retValPtr );
228
+  cmJsRC_t cmJsonPathToReal(   cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, double* retValPtr );
229
+  cmJsRC_t cmJsonPathToString( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, const char** retValPtr );
230
+  cmJsRC_t cmJsonPathToPair(   cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, cmJsonNode_t** retValPtr );
231
+  cmJsRC_t cmJsonPathToArray(  cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, cmJsonNode_t** retValPtr );
232
+  cmJsRC_t cmJsonPathToObject( cmJsonH_t h, const cmJsonNode_t* objectNodePtr, const char* pathPrefix, const char* path, cmJsonNode_t** retValPtr );
233
+
234
+
235
+  // Same as cmJsonMemberValues() except labels may be paths to a given variable.
236
+  // These paths are equivalent to those used in cmJsonFindPathValue()
237
+  // If objectNodePtr is NULL then the JSON tree root is used as the
238
+  // base reference object.
239
+  // If pathPrefix is non-NULL then it is appended to each of the
240
+  // individual paths.
241
+  cmJsRC_t      cmJsonVPathValues(cmJsonH_t h, const char* pathPrefix, const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, va_list vl );
242
+  cmJsRC_t      cmJsonPathValues( cmJsonH_t h, const char* pathPrefix, const cmJsonNode_t* objectNodePtr, const char** errLabelPtrPtr, ... );
243
+
244
+
245
+  // Set 'typeId' to the type of the new node.
246
+  // Use 'intVal' for the value of int nodes.
247
+  // Use 'realVal' for the value of real nodes.
248
+  // Use 'stringVal' for the label of pairs and the value of string nodes.
249
+  // 'retNodePtrPtr' is optional
250
+  cmJsRC_t      cmJsonCreate( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned typeId, const char* stringVal, int intVal, double realVal, cmJsonNode_t** newNodePtrPtr );
251
+
252
+  // Insert new nodes in the tree.  If the tree is empty then the first
253
+  // inserted node will become the root (this must be an object or array node.).
254
+  cmJsonNode_t* cmJsonCreateObject( cmJsonH_t h, cmJsonNode_t* parentPtr );
255
+  cmJsonNode_t* cmJsonCreateArray(  cmJsonH_t h, cmJsonNode_t* parentPtr );
256
+  cmJsonNode_t* cmJsonCreatePair(   cmJsonH_t h, cmJsonNode_t* parentPtr, const char* label );
257
+  cmJsRC_t      cmJsonCreateString( cmJsonH_t h, cmJsonNode_t* parentPtr, const char* stringValue );
258
+  cmJsRC_t      cmJsonCreateStringN(cmJsonH_t h, cmJsonNode_t* parentPtr, const char* stringValue, unsigned stringCharCnt );
259
+  cmJsRC_t      cmJsonCreateInt(    cmJsonH_t h, cmJsonNode_t* parentPtr, int value );
260
+  cmJsRC_t      cmJsonCreateReal(   cmJsonH_t h, cmJsonNode_t* parentPtr, double value );
261
+  cmJsRC_t      cmJsonCreateBool(   cmJsonH_t h, cmJsonNode_t* parentPtr, bool value );
262
+  cmJsRC_t      cmJsonCreateNull(   cmJsonH_t h, cmJsonNode_t* parentPtr );
263
+
264
+  cmJsRC_t      cmJsonCreateStringArray( cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const char** values );
265
+  cmJsRC_t      cmJsonCreateIntArray(    cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const int* values );
266
+  cmJsRC_t      cmJsonCreateRealArray(   cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const double* values );
267
+  cmJsRC_t      cmJsonCreateBoolArray(   cmJsonH_t h, cmJsonNode_t* parentPtr, unsigned n, const bool* values );
268
+
269
+
270
+  //--------------------------------------------------------------------------------------------------------------
271
+  //
272
+  // Tree creation helper functiosn
273
+  //
274
+
275
+  cmJsRC_t    cmJsonSetInt(    cmJsonH_t h, cmJsonNode_t*  np, int         ival );
276
+  cmJsRC_t    cmJsonSetReal(   cmJsonH_t h, cmJsonNode_t * np, double      rval );
277
+  cmJsRC_t    cmJsonSetBool(   cmJsonH_t h, cmJsonNode_t * np, bool        bval );
278
+  cmJsRC_t    cmJsonSetString( cmJsonH_t h, cmJsonNode_t*  np, const char* sval );
279
+
280
+  // Insert a pair with a value indicated by 'typeId'.
281
+  // 'stringVal','intVal' and 'realVal' are used as in cmJsonCreate().
282
+  // Return a pointer to the new pair.
283
+  cmJsonNode_t* cmJsonInsertPair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned typeId, const char* stringVal, int intVal, double realVal );
284
+
285
+  // Create a pair node and the associated label and value nodes and insert the pair in a parent object.
286
+  // The object,array and pair creation functions return pointers to the pair value node.
287
+  // These are helper functions that are implemented in terms of cmJsonCreateXXX() function.
288
+  cmJsonNode_t* cmJsonInsertPairObject( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label );
289
+  cmJsonNode_t* cmJsonInsertPairArray(  cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label );
290
+  cmJsonNode_t* cmJsonInsertPairPair(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, const char* pairLabel );
291
+  cmJsRC_t      cmJsonInsertPairInt(    cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, int intVal );
292
+  cmJsRC_t      cmJsonInsertPairReal(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, double realVal );
293
+  cmJsRC_t      cmJsonInsertPairString( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, const char* string );
294
+  cmJsRC_t      cmJsonInsertPairStringN(cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, const char* string, unsigned stringCharCnt );
295
+  cmJsRC_t      cmJsonInsertPairBool(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, bool boolVal );
296
+  cmJsRC_t      cmJsonInsertPairNull(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label );
297
+
298
+  cmJsRC_t      cmJsonInsertPairIntArray(    cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const int* values );
299
+  cmJsRC_t      cmJsonInsertPairRealArray(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const double* values );
300
+  cmJsRC_t      cmJsonInsertPairStringArray( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const char** values );
301
+  cmJsRC_t      cmJsonInsertPairBoolArray(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const bool* values );
302
+
303
+  // Returns pair pointer
304
+  cmJsonNode_t* cmJsonInsertPairIntArray2(    cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const int* values );
305
+  cmJsonNode_t* cmJsonInsertPairRealArray2(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const double* values );
306
+  cmJsonNode_t* cmJsonInsertPairStringArray2( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const char** values );
307
+  cmJsonNode_t* cmJsonInsertPairBoolArray2(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned n, const bool* values );
308
+
309
+  // Insert a pair (same as cmJsonInsertPair()) or if a pair
310
+  // with a matching label/type already exists then replace the 
311
+  // existing value with a the new value.
312
+  //
313
+  // Set matchTypeMask with the type id's of all pair value types
314
+  // which sould be considered a match.  If matchTypeMask is set to
315
+  // kInvalidTId then all value types will match. 
316
+  //
317
+  // Return a pointer to the new or existing pair.
318
+  //
319
+  // When newTypeId == kObjectTId or kArrayTId then 'nv' may optionally be set to an object or array
320
+  // to be set as the new value node for the selected pair.  If 'nv' is NULL then an empty array or
321
+  // object is created as the pair value node.  
322
+  // 
323
+  // When newTypeId == kPairTId then we are inserting/replacing a pair as the value of the selected pair.
324
+  // In this case sv must be set to the new pair label.  'nv' may be optionally
325
+  // set to a new pair value node. If 'nv' is NULL then the the new pair will have a value of type kNullTId
326
+  cmJsonNode_t* cmJsonInsertOrReplacePair( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, unsigned newTypeId, const char* sv, int iv, double dv, cmJsonNode_t* nv );
327
+
328
+  // Returns pointer to object node.
329
+  cmJsonNode_t* cmJsonInsertOrReplacePairObject( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newObjNodePtr );
330
+  // Returns pointer to array node.
331
+  cmJsonNode_t* cmJsonInsertOrReplacePairArray(  cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newArrayNodePtr );
332
+  // Returns pointer to child pair node
333
+  cmJsonNode_t* cmJsonInsertOrReplacePairPair(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* newPairLabel, cmJsonNode_t* newPairValNodePtr );
334
+  cmJsRC_t      cmJsonInsertOrReplacePairInt(    cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, int intVal );
335
+  cmJsRC_t      cmJsonInsertOrReplacePairReal(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, double realVal );
336
+  cmJsRC_t      cmJsonInsertOrReplacePairString( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* string );
337
+  cmJsRC_t      cmJsonInsertOrReplacePairBool(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, bool boolVal );
338
+  cmJsRC_t      cmJsonInsertOrReplacePairNull(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask );
339
+
340
+  // Same as the above cmJsonInsertOrReplaceXXX() functions except
341
+  // the function fails if a matching pair is not found.
342
+  //
343
+  // Replace a pair with the same name/type and return a pointer to the 
344
+  // effected pair. If a pair with the same name and type are not 
345
+  // found then no change is made and the function returns NULL. 
346
+  // For newTypeId=kObjectTId,kArrayTId,kPairTId the replaced pair node is blank,
347
+  // in other words it will have no child nodes.
348
+  cmJsonNode_t* cmJsonReplacePair(       cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, unsigned newTypeId, const char* sv, int iv, double dv, cmJsonNode_t* nv );
349
+
350
+  // Returns pointer to object node.
351
+  cmJsonNode_t* cmJsonReplacePairObject( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newObjNodePtr );
352
+  // Return  pointer to array node.
353
+  cmJsonNode_t* cmJsonReplacePairArray(  cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, cmJsonNode_t* newArrayNodePtr );
354
+  // Returns pointer to child pair node.
355
+  cmJsonNode_t* cmJsonReplacePairPair(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* newPairLabel, cmJsonNode_t* newPairValueNodePtr );
356
+  cmJsRC_t      cmJsonReplacePairInt(    cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, int intVal );
357
+  cmJsRC_t      cmJsonReplacePairReal(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, double realVal );
358
+  cmJsRC_t      cmJsonReplacePairString( cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, const char* string );
359
+  cmJsRC_t      cmJsonReplacePairBool(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask, bool boolVal );
360
+  cmJsRC_t      cmJsonReplacePairNull(   cmJsonH_t h, cmJsonNode_t* objectNodePtr, const char* label, unsigned matchTypeMask );
361
+
362
+
363
+  // Insert multiple pairs in a parent object. Terminate the pair sets with NULL.
364
+  // Note that pair,int,real,bool, and string  pairs are specified with 3 args: <label>,<typeId>,<value>
365
+  // all others are specified with 2 args: <label>,<typeId>.
366
+  // The last argument in this function must always be NULL.
367
+  cmJsRC_t      cmJsonVInsertPairs( cmJsonH_t h, cmJsonNode_t* objectNodePtr, va_list vl );
368
+  cmJsRC_t      cmJsonInsertPairs(  cmJsonH_t h, cmJsonNode_t* objectNodePtr, ... );
369
+
370
+
371
+  // Create an object node, fill it with the specified pairs, and return a pointer to
372
+  // the new object node.
373
+  // This function uses same var args syntax as cmJsonInsertPairs()
374
+  cmJsonNode_t* cmJsonVCreateFilledObject( cmJsonH_t h, cmJsonNode_t* parentPtr, va_list vl );
375
+  cmJsonNode_t* cmJsonCreateFilledObject( cmJsonH_t h, cmJsonNode_t* parentPtr, ... );
376
+  
377
+  //--------------------------------------------------------------------------------------------------------------
378
+
379
+  // Remove a node from the tree by unlinking it from its 
380
+  // parent and siblings. 
381
+  // Set the freeFl to true if the node memory should also 
382
+  // be released.
383
+  // 
384
+  // If 'freeFl' is false then np->ownerPtr and 
385
+  // np->siblingPtr remain valid when the function returns.
386
+  // Even with the valid pointer however be careful 
387
+  // not to use the node in a way  that it depends
388
+  // on existing in the tree since the parent and
389
+  // siblings will no longer know about it.
390
+  //
391
+  // If np is the root then the internal tree will be
392
+  // cleared (i.e. cmJsonRoot(h) == NULL).
393
+  //
394
+  // This function will fail if 'np' points to the label value
395
+  // of a pair node.  Pair labels cannot be removed because this
396
+  // would result in an invalid tree (all pairs must have two 
397
+  // child nodes where the first node is a string value). If 'np'
398
+  // points to a pair value node then the value node is replaced
399
+  // with a null node to maintain a valid pair structure.
400
+  cmJsRC_t cmJsonRemoveNode( cmJsonH_t h, cmJsonNode_t* np, bool freeFl );
401
+
402
+  //--------------------------------------------------------------------------------------------------------------
403
+
404
+
405
+  // Duplicate the subtree pointed to by 'np' and attach it as a child
406
+  // of the node pointed to by 'parentPtr'.  This function performs a
407
+  // deep copy of the subtree pointed to by np and returns the pointer
408
+  // to the duplicated subtree.
409
+  //
410
+  // 'parentPtr' must be a legal parent for the sub-tree or NULL to not
411
+  // attach the duplicate tree to any parent.
412
+  //
413
+  // The returned value is a pointer to the new subtree. 
414
+  //
415
+  // If an error occurs the return value is NULL and cmJsonErrorCode()
416
+  // can be used to obtain the code associated with the error.
417
+  cmJsonNode_t* cmJsonDuplicateNode( cmJsonH_t h, const cmJsonNode_t* np, cmJsonNode_t* parentPtr ); 
418
+
419
+  //--------------------------------------------------------------------------------------------------------------
420
+
421
+  // Copy any pairs not found in the destintaion object from the
422
+  // source object.
423
+  // If an error occurs during merging the destination object is
424
+  // returned unmodified.  The most likely cause of an error is a
425
+  // destination pair with the same name but different type 
426
+  // than a source pair.
427
+  cmJsRC_t  cmJsonMergeObjectNodes( cmJsonH_t h, cmJsonNode_t* destObjNodePtr, const cmJsonNode_t* srcObjNodePtr );
428
+
429
+  //--------------------------------------------------------------------------------------------------------------
430
+
431
+ 
432
+ // Validate the tree.
433
+  cmJsRC_t      cmJsonValidateTree( cmJsonH_t h );
434
+
435
+  // Validate the tree beginning with np. Note that this function does
436
+  // not print an error on failure but simply returns kValidateFailJsRC.
437
+  cmJsRC_t      cmJsonValidate( const cmJsonNode_t* np );
438
+
439
+  // Get the count of bytes required to serialize the tree rooted at 'np'.  
440
+  unsigned      cmJsonSerialByteCount( const cmJsonNode_t* np );
441
+
442
+  // Serialize the tree rooted at 'np' into the buffer buf[bufByteCnt].
443
+  cmJsRC_t      cmJsonSerialize(  const cmJsonNode_t* np, void* buf, unsigned bufByteCnt );
444
+
445
+  // Serialize the subtree indicated by 'np' or the entire tree
446
+  // if 'np' is NULL. The buffer created by this call will exist 
447
+  // for the life of 'h' or until the next call to cmJsonSerialize().
448
+  // This function is implemented in terms of cmJsonSerialByteCount()
449
+  // and cmJsonSerializeTree().
450
+  cmJsRC_t      cmJsonSerializeTree( cmJsonH_t h, const cmJsonNode_t* np, void** bufPtrPtr, unsigned* bufByteCntPtr);
451
+
452
+  // Recreate the objects previously serialzed via cmJsonSerialize().
453
+  // The tree held in the buffer will be reconstructed as a child of
454
+  // altRootPtr (if it is non-NULL) or the internal root.  
455
+  // If altRootPtr is given then it must point to an array 
456
+  // or object node.
457
+  cmJsRC_t      cmJsonDeserialize( cmJsonH_t h, const void* bufPtr, cmJsonNode_t* altRootPtr );
458
+
459
+  // Return a string/int/real/null/bool node as a string value.
460
+  cmJsRC_t      cmJsonLeafToString( const cmJsonNode_t* np, cmChar_t* buf, unsigned bufCharCnt );
461
+  
462
+  // Given a CSV file convert it to an array of JSON objects.
463
+  // The first line of the CSV file must contain a comma seperated lists of types.
464
+  // The type labels must be from the set: 'int','real','string','true','false'.
465
+  // Note that either 'true' or 'false' can be use for boolean columns.
466
+  // The seocnd line contains the field names as comma separated quoted strings.
467
+  // For example "column1","column2","column3"
468
+  // The data is presented as comma separated fields.
469
+  // If parentNodePtr is NULL then the array will be created unattached to 
470
+  // the tree.
471
+  // if arrayNodePtrPtr is non-NULL then the array node ptr will be returned.
472
+  cmJsRC_t      cmJsonFromCSV(  cmJsonH_t h, const char* iFn, cmJsonNode_t* parentPtr, cmJsonNode_t** arrayNodePtrPtr  );
473
+
474
+  // Write a CSV file from an array of objects.
475
+  // arrayNodePtr must point to an array of objects.
476
+  cmJsRC_t      cmJsonToCSV(   cmJsonH_t h, const char* oFn, const cmJsonNode_t* arrayNodePtr );
477
+
478
+  // Print the subtree using 'np as the root.
479
+  void          cmJsonPrintTree( const cmJsonNode_t* np, cmRpt_t* rpt );
480
+
481
+  // Print the subtree using 'np' as the root to a file.
482
+  // 'np' is optional and defaults to cmJsonRoot().
483
+  cmJsRC_t      cmJsonWrite( cmJsonH_t h, const cmJsonNode_t* np, const cmChar_t* fn );
484
+
485
+  // Return the code of the last error generated.  This is useful for the
486
+  // the cmJsonCreateXXX() functions which do not return error codes but
487
+  // may still fail.
488
+  cmJsRC_t      cmJsonErrorCode( cmJsonH_t h );
489
+  void          cmJsonClearErrorCode( cmJsonH_t h );
490
+
491
+  // Validate the tree and print all the nodes.
492
+  cmJsRC_t      cmJsonReport( cmJsonH_t h );
493
+
494
+  // Testing stub.
495
+  cmJsRC_t      cmJsonTest( const char* fn, cmCtx_t* ctx );
496
+
497
+  //)
498
+  //}
499
+
500
+#ifdef __cplusplus
501
+}
502
+#endif
503
+
504
+#endif

+ 235
- 0
cmKeyboard.c View File

@@ -0,0 +1,235 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmKeyboard.h"
4
+#include <termios.h>
5
+//#include <stropts.h>
6
+#include <sys/ioctl.h>
7
+//#include <linux/kd.h>
8
+//#include <linux/keyboard.h>
9
+#include <unistd.h>
10
+
11
+struct termios new_settings;
12
+struct termios stored_settings;
13
+
14
+void set_keypress(void) 
15
+{
16
+  struct termios new_settings;
17
+
18
+  tcgetattr(0,&stored_settings);
19
+  new_settings             = stored_settings;
20
+  new_settings.c_lflag    &= (~ICANON);
21
+  new_settings.c_lflag    &= (~ECHO);
22
+  new_settings.c_cc[VTIME] = 0;
23
+
24
+  //int i;
25
+  //for(i=0; i<NCCS; ++i)
26
+  //  printf("%i ",new_settings.c_cc[i]);
27
+  //printf("\n");
28
+      
29
+
30
+  tcgetattr(0,&stored_settings);
31
+
32
+  new_settings.c_cc[VMIN] = 1;
33
+  tcsetattr(0,TCSANOW,&new_settings);
34
+
35
+}
36
+
37
+void reset_keypress(void) 
38
+{
39
+  tcsetattr(0,TCSANOW,&stored_settings);
40
+}
41
+
42
+#define CM_KB_TBL_CNT (10)
43
+
44
+unsigned _cmKbTbl[][CM_KB_TBL_CNT] = 
45
+{
46
+
47
+  //                           alt ctl code
48
+  { 3, 27, 91, 68,   0,   0,   0,  0, 0, kLeftArrowKId },
49
+  { 3, 27, 91, 67,   0,   0,   0,  0, 0, kRightArrowKId },
50
+  { 3, 27, 91, 65,   0,   0,   0,  0, 0, kUpArrowKId },
51
+  { 3, 27, 91, 66,   0,   0,   0,  0, 0, kDownArrowKId },
52
+  { 3, 27, 79, 72,   0,   0,   0,  0, 0, kHomeKId },
53
+  { 3, 27, 79, 70,   0,   0,   0,  0, 0, kEndKId },
54
+  { 4, 27, 91, 53, 126,   0,   0,  0, 0, kPgUpKId },
55
+  { 4, 27, 91, 54, 126,   0,   0,  0, 0, kPgDownKId },
56
+  { 4, 27, 91, 50, 126,   0,   0,  0, 0, kInsertKId },
57
+  { 4, 27, 91, 51, 126,   0,   0,  0, 0, kDeleteKId },
58
+  { 6, 27, 91, 49,  59,  53,  68,  0, 1, kLeftArrowKId },
59
+  { 6, 27, 91, 49,  59,  53,  67,  0, 1, kRightArrowKId },
60
+  { 6, 27, 91, 49,  59,  53,  65,  0, 1, kUpArrowKId },
61
+  { 6, 27, 91, 49,  59,  53,  66,  0, 1, kDownArrowKId },
62
+  { 6, 27, 91, 53,  59,  53, 126,  0, 1, kPgUpKId }, 
63
+  { 6, 27, 91, 54,  59,  53, 126,  0, 1, kPgDownKId },
64
+  { 4, 27, 91, 51,  59,  53, 126,  0, 1, kDeleteKId }, 
65
+  { 0,  0,  0,  0,   0,   0,   0,  0, 0, kInvalidKId }
66
+};
67
+
68
+void cmKeyPress( cmKbRecd* p )
69
+{
70
+  const int bufN = 16;
71
+  char      buf[bufN];
72
+  int       n,j, k;
73
+  int       rc;
74
+  char      c;
75
+
76
+  if( p != NULL )
77
+  {
78
+    p->code  = kInvalidKId;
79
+    p->ch    = 0;
80
+    p->ctlFl = false;
81
+    p->altFl = false;
82
+  }
83
+  
84
+  set_keypress();
85
+
86
+  // block for the first character
87
+  if((rc = read(0, &c, 1 )) == 1)
88
+    buf[0]=c;
89
+
90
+  // loop in non-blocking for successive characters
91
+  new_settings.c_cc[VMIN] = 0;
92
+  tcsetattr(0,TCSANOW,&new_settings);
93
+
94
+  for(n=1; n<bufN; ++n)
95
+    if(read(0,&c,1) == 1 )
96
+      buf[n] = c;
97
+    else
98
+      break;
99
+
100
+  new_settings.c_cc[VMIN] = 1;
101
+  tcsetattr(0,TCSANOW,&new_settings);
102
+
103
+  /*
104
+  for(j=0; j<n; ++j)
105
+    printf("{%c (%i)} ",buf[j],buf[j]);
106
+  printf(" :%i\f\n",n);
107
+  fflush(stdout);
108
+  */
109
+
110
+  if( p != NULL )
111
+  {
112
+    // translate the keypress
113
+    if( n == 1)
114
+    {
115
+      p->code  = kAsciiKId;
116
+      p->ch    = buf[0];
117
+      p->ctlFl = buf[0] <= 31;    
118
+    }
119
+    else
120
+    {
121
+      for(j=0; _cmKbTbl[j][0] != 0; ++j)
122
+        if( _cmKbTbl[j][0] == n )
123
+        {
124
+          for(k=1; k<=n; ++k)
125
+            if( _cmKbTbl[j][k] != buf[k-1] )
126
+              break;
127
+
128
+          // if the key was found
129
+          if( k==n+1 )
130
+          {
131
+            p->code  = _cmKbTbl[j][ CM_KB_TBL_CNT - 1 ];
132
+            p->ctlFl = _cmKbTbl[j][ CM_KB_TBL_CNT - 2 ];
133
+            break;
134
+          }
135
+        }
136
+    }
137
+  }
138
+  reset_keypress();
139
+}
140
+
141
+void cmKbTest()
142
+{
143
+  set_keypress();
144
+  
145
+  int c = 0;
146
+  int r; 
147
+  while( c != 'q' )
148
+  {
149
+
150
+    printf("0>"); fflush(stdout);
151
+    r = read(0, &c, 1 );
152
+    printf("0: %c (%i)\r\n",(char)c,c);
153
+
154
+
155
+    new_settings.c_cc[VMIN] = 0;
156
+    tcsetattr(0,TCSANOW,&new_settings);
157
+
158
+    if( r == 1 && c == 27 )
159
+    {
160
+      r = read(0, &c, 1 );
161
+      printf("1: %c (%i)\n",(char)c,c);
162
+
163
+      if( r == 1 && c == '[' )
164
+      {
165
+        r = read(0, &c, 1 );
166
+        printf("2: %c (%i)\n",(char)c,c);
167
+
168
+      }
169
+
170
+    }
171
+
172
+    new_settings.c_cc[VMIN] = 1;
173
+    tcsetattr(0,TCSANOW,&new_settings);
174
+
175
+  }
176
+
177
+  reset_keypress();
178
+}
179
+
180
+// Note: this technique does not work because the 
181
+void testKb2()
182
+{
183
+  set_keypress();
184
+
185
+  fd_set         rfds;
186
+  struct timeval tv;
187
+  int            retval;
188
+  int            c=0;
189
+    
190
+
191
+  while( c != 'q' )
192
+  {
193
+    int i = 0;
194
+
195
+    printf(">");
196
+
197
+    do
198
+    {
199
+      
200
+      /* Watch stdin (fd 0) to see when it has input. */
201
+      FD_ZERO(&rfds);
202
+      FD_SET(0, &rfds);
203
+
204
+      // don't wait
205
+      tv.tv_sec =  0;
206
+      tv.tv_usec = 0;
207
+
208
+      retval = select(1, &rfds, NULL, NULL, i==0 ? NULL : &tv);
209
+      // Don't rely on the value of tv now - it may have been overwritten by select
210
+
211
+      // if an error occurred
212
+      if (retval == -1)
213
+        perror("select()");
214
+      else 
215
+      {
216
+        // if data is waiting
217
+        if (retval)
218
+        {
219
+          c = getchar();
220
+          printf("%i %c (%i) ",i,(char)c,c);
221
+          ++i;
222
+
223
+        }
224
+        else
225
+        {
226
+          printf("\n");
227
+          break; // no data available
228
+        }
229
+      }
230
+
231
+    } while( 1 );
232
+  }
233
+
234
+  reset_keypress();
235
+}

+ 37
- 0
cmKeyboard.h View File

@@ -0,0 +1,37 @@
1
+#ifndef cmKeyboard_h
2
+#define cmKeyboard_h
3
+
4
+
5
+enum
6
+{
7
+  kInvalidKId,
8
+  kAsciiKId,
9
+  kLeftArrowKId,
10
+  kRightArrowKId,
11
+  kUpArrowKId,
12
+  kDownArrowKId,
13
+  kHomeKId,
14
+  kEndKId,
15
+  kPgUpKId,
16
+  kPgDownKId,
17
+  kInsertKId,
18
+  kDeleteKId,
19
+  
20
+};
21
+
22
+
23
+
24
+
25
+
26
+typedef struct
27
+{
28
+  unsigned code;
29
+  char     ch;
30
+  bool     ctlFl;
31
+  bool     altFl;
32
+} cmKbRecd;
33
+
34
+// Set 'p' to NULL if the value of the key is not required.
35
+void cmKeyPress( cmKbRecd* p );
36
+
37
+#endif

+ 910
- 0
cmLex.c View File

@@ -0,0 +1,910 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmLex.h"
5
+#include "cmErr.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmFile.h"
9
+
10
+typedef struct
11
+{
12
+  unsigned        code;
13
+  const cmChar_t* msg;
14
+} cmLexErrorRecd;
15
+
16
+
17
+cmLexErrorRecd cmLexErrorArray[] = 
18
+{
19
+  { kOkLexRC,              "No error. The operation completed successfully."},
20
+  { kDuplicateTokenLexRC,  "The text or id passed as a user token is already in use by another token."},
21
+  { kMissingCmtEndLexRC,   "The end of a block comment could not be found."}, 
22
+  { kMissingEndQuoteLexRC, "The end of a quoted string could not be found."},
23
+  { kNoMatchLexRC,         "The lexer encountered a string which could not be classified."},
24
+  { kFileOpenErrLexRC,     "File open failed on cmLexSetFile()"},
25
+  { kFileSeekErrLexRC,     "File seek failed on cmLexSetFile()"},
26
+  { kFileTellErrLexRC,     "File tell failed on cmLexSetFile()"},
27
+  { kFileReadErrLexRC,     "File read failed on cmLexSetFile()"},
28
+  { kFileCloseErrLexRC,    "File close failed on cmLexSetFile()"},
29
+  { kMemAllocErrLexRC,     "An attempted memory allocation failed"},
30
+  { kEofRC,                "The end of the input text was encountered (this is a normal condition not an error)"},
31
+  { kInvalidLexRC,         "Unknown lexer error code." }
32
+};
33
+
34
+struct cmLex_str;
35
+
36
+typedef unsigned (*cmLexMatcherFuncPtr_t)( struct cmLex_str* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr );
37
+
38
+// token match function record  
39
+typedef struct
40
+{
41
+  unsigned              typeId;   // token type this matcher recognizes     
42
+  cmLexMatcherFuncPtr_t funcPtr;  // recognizer function (only used if userPtr==NULL)
43
+  cmChar_t*             tokenStr; // fixed string data used by the recognizer (only used if userPtr==NULL)
44
+  cmLexUserMatcherPtr_t userPtr;  // user defined recognizer function (only used if funcPtr==NULL)
45
+} cmLexMatcher;
46
+
47
+
48
+
49
+typedef struct cmLex_str
50
+{
51
+  cmErr_t           err;
52
+  const cmChar_t*   cp;              // character buffer
53
+  unsigned          cn;              // count of characters in buffer
54
+  unsigned          ci;              // current buffer index position
55
+  unsigned          flags;           // lexer control flags
56
+
57
+  unsigned          curTokenId;      // type id of the current token
58
+  unsigned          curTokenCharIdx; // index into cp[] of the current token
59
+  unsigned          curTokenCharCnt; // count of characters in the current token 
60
+  unsigned          curLine;         // line number of the current token
61
+  unsigned          curCol;          // column number of the current token
62
+
63
+  unsigned          nextLine;
64
+  unsigned          nextCol;
65
+
66
+  cmChar_t*         blockBegCmtStr;
67
+  cmChar_t*         blockEndCmtStr;
68
+  cmChar_t*         lineCmtStr;
69
+
70
+  cmLexMatcher*     mfp;             // base of matcher array   
71
+  unsigned          mfi;             // next available matcher array slot
72
+  unsigned          mfn;             // count of elementes in mfp[]
73
+
74
+  cmChar_t*         textBuf;         // text buf used by cmLexSetFile()
75
+
76
+} cmLex;
77
+
78
+
79
+cmLexH cmLexNullH = { NULL };
80
+
81
+bool _cmLexIsNewline( cmChar_t c )
82
+{ return c == '\n'; }
83
+
84
+bool _cmLexIsCommentTypeId( unsigned typeId )
85
+{ return typeId == kBlockCmtLexTId || typeId == kLineCmtLexTId; }
86
+
87
+cmLex* _cmLexHandleToPtr( cmLexH h )
88
+{
89
+  cmLex* p = (cmLex*)h.h;
90
+  assert(p != NULL);
91
+  return p;
92
+};
93
+
94
+cmRC_t _cmLexError( cmLex* p, unsigned rc, const char* fmt, ... )
95
+{
96
+  va_list vl;
97
+  va_start(vl,fmt);
98
+
99
+  unsigned bufCharCnt = 512;
100
+  char buf[ bufCharCnt+1 ];
101
+  snprintf(buf,bufCharCnt,"Error on line:%i ", p->curLine);
102
+
103
+  unsigned sn = strlen(buf);
104
+  vsnprintf(buf+sn,bufCharCnt-sn,fmt,vl);
105
+  buf[bufCharCnt]=0;
106
+
107
+  cmErrMsg(&p->err,rc,"%s",buf);
108
+
109
+  va_end(vl);
110
+  return rc;
111
+}
112
+
113
+unsigned _cmLexScanTo( const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
114
+{
115
+  unsigned i = 0;
116
+  unsigned n = strlen(keyStr);
117
+
118
+  if( n <= cn )
119
+    for(; i<=cn-n; ++i)
120
+      if( strncmp(cp + i, keyStr, n ) == 0 )
121
+        return i+n;
122
+
123
+  return cmInvalidIdx;
124
+    
125
+}
126
+
127
+
128
+unsigned _cmLexExactStringMatcher(   cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
129
+{
130
+  unsigned n = strlen(keyStr);
131
+  return strncmp(keyStr,cp,n) == 0  ? n : 0;
132
+}
133
+
134
+
135
+unsigned _cmLexSpaceMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
136
+{
137
+  unsigned i=0;
138
+  for(; i<cn; ++i)
139
+    if( !isspace(cp[i]) )
140
+      break;
141
+  return i;
142
+}
143
+
144
+unsigned _cmLexRealMatcher(  cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
145
+{
146
+  unsigned i  = 0;
147
+  unsigned n  = 0;     // decimal point counter
148
+  unsigned d  = 0;     // digit counter
149
+  bool     fl = false; // true if this real includes an exponent
150
+
151
+  for(; i<cn && n<=1; ++i)
152
+  {
153
+    if( i==0 && cp[i]=='-' )  // allow a leading '-'
154
+      continue;
155
+
156
+    if( isdigit(cp[i]) )      // allow digits 
157
+    {
158
+      ++d;
159
+      continue;
160
+    }
161
+
162
+    if( cp[i] == '.'  && n==0 ) // allow exactly  one decimal point
163
+      ++n;   
164
+    else
165
+      break;
166
+  }
167
+
168
+  // if there was at least one digit and the next char is an 'e'
169
+  if( d>0 && i<cn && (cp[i] == 'e' || cp[i] == 'E') )
170
+  {
171
+    d=0;
172
+    ++i;
173
+    unsigned j = i;
174
+
175
+    for(; i<cn; ++i)
176
+    {
177
+      if( i==j && cp[i]=='-' ) // allow the char following the e to be '-'
178
+        continue;
179
+
180
+      if( isdigit(cp[i]) )
181
+      {
182
+        ++d;
183
+        continue;
184
+      }
185
+
186
+      // stop at the first non-digit
187
+      break;
188
+    }
189
+
190
+    // an exp exists if digits follwed the 'e'
191
+    fl = d > 0;
192
+     
193
+  }
194
+
195
+  return i>1 && (n==1 || fl) ? i : 0;
196
+}
197
+
198
+unsigned _cmLexIntMatcher(   cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
199
+{
200
+  unsigned i = 0;
201
+  for(; i<cn; ++i)
202
+  {
203
+    if( i==0 && cp[i]=='-' )
204
+      continue;
205
+
206
+    if( !isdigit(cp[i]) )
207
+      break;
208
+  }
209
+
210
+  // BUG BUG BUG
211
+  // If an integer is specified using 'e' notiation 
212
+  // (see _cmLexRealMatcher()) and the number of exponent places
213
+  // specified following the 'e' is positive and >= number of
214
+  // digits following the decimal point (in effect zeros are
215
+  // padded on the right side) then the value is an integer.
216
+  //
217
+  // The current implementation recognizes all numeric strings 
218
+  // containing a decimal point as reals. 
219
+ 
220
+
221
+  return i;
222
+}
223
+
224
+unsigned _cmLexHexMatcher(   cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
225
+{
226
+  unsigned i = 0;
227
+
228
+  if( cn < 3 )
229
+    return 0;
230
+
231
+  if( cp[0]=='0' && cp[1]=='x')    
232
+    for(i=2; i<cn; ++i)
233
+      if( !isxdigit(cp[i]) )
234
+        break;
235
+
236
+  return i;
237
+}
238
+
239
+
240
+unsigned _cmLexIdentMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
241
+{
242
+  unsigned i = 0;
243
+  if( isalpha(cp[0]) || (cp[0]== '_'))
244
+  {
245
+    i = 1;
246
+    for(; i<cn; ++i)
247
+      if( !isalnum(cp[i]) && (cp[i] != '_') )
248
+        break;
249
+  }
250
+  return i;
251
+}
252
+
253
+
254
+unsigned _cmLexQStrMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
255
+{
256
+  cmChar_t qStr[]="\"";
257
+  unsigned n = strlen(qStr);
258
+  if( strncmp(qStr,cp,n) == 0 )
259
+  {
260
+    unsigned i;
261
+    if((i = _cmLexScanTo(cp+n, cn-n, qStr)) == cmInvalidIdx )
262
+    {
263
+      _cmLexError( p, kMissingEndQuoteLexRC, "Missing string end quote.");
264
+      return 0;
265
+    }
266
+    return n+i;
267
+  }
268
+  return 0;
269
+}
270
+
271
+
272
+unsigned _cmLexBlockCmtMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
273
+{  
274
+  unsigned n = strlen(p->blockBegCmtStr);
275
+
276
+  if( strncmp( p->blockBegCmtStr, cp, n ) == 0 )
277
+  {
278
+    unsigned i;
279
+    if((i = _cmLexScanTo(cp + n, cn-n,p->blockEndCmtStr)) == cmInvalidIdx )
280
+    {
281
+      _cmLexError(p, kMissingCmtEndLexRC, "Missing end of block comment.");
282
+      return 0;
283
+    }
284
+
285
+    return n + i;
286
+  }
287
+  return 0;
288
+}
289
+
290
+unsigned _cmLexLineCmtMatcher( cmLex* p, const cmChar_t* cp, unsigned cn, const cmChar_t* keyStr )
291
+{
292
+  unsigned n = strlen(p->lineCmtStr);
293
+  if( strncmp( p->lineCmtStr, cp, n ) == 0)
294
+  {
295
+    unsigned i;
296
+    const char newlineStr[] = "\n";
297
+    if((i = _cmLexScanTo(cp + n, cn-n, newlineStr)) == cmInvalidIdx )
298
+    {
299
+      // no EOL was found so the comment must be on the last line of the source
300
+      return cn;
301
+    }  
302
+
303
+    return n + i;
304
+  }
305
+  return 0;
306
+}
307
+
308
+cmRC_t  _cmLexInstallMatcher( cmLex* p, unsigned typeId, cmLexMatcherFuncPtr_t funcPtr, const cmChar_t* keyStr, cmLexUserMatcherPtr_t userPtr )
309
+{
310
+  assert( funcPtr==NULL || userPtr==NULL );
311
+  assert( !(funcPtr==NULL && userPtr==NULL));
312
+
313
+  // if there is no space in the user token array - then expand it
314
+  if( p->mfi == p->mfn )
315
+  {
316
+    int incr_cnt = 10;
317
+    cmLexMatcher* np = cmMemAllocZ( cmLexMatcher, p->mfn + incr_cnt );
318
+    memcpy(np,p->mfp,p->mfi*sizeof(cmLexMatcher));
319
+    cmMemPtrFree(&p->mfp);
320
+    p->mfp = np;
321
+    p->mfn += incr_cnt;
322
+  }
323
+
324
+  p->mfp[p->mfi].tokenStr = NULL;
325
+  p->mfp[p->mfi].typeId   = typeId;
326
+  p->mfp[p->mfi].funcPtr  = funcPtr;
327
+  p->mfp[p->mfi].userPtr  = userPtr;
328
+
329
+  if( keyStr != NULL )
330
+  {
331
+    // allocate space for the token string and store it
332
+    p->mfp[p->mfi].tokenStr = cmMemAlloc( cmChar_t, sizeof(cmChar_t) * (strlen(keyStr)+1) );
333
+    strcpy(p->mfp[p->mfi].tokenStr, keyStr );
334
+  }
335
+
336
+
337
+  p->mfi++;
338
+  return kOkLexRC;
339
+}
340
+cmRC_t _cmLexReset( cmLex* p )
341
+{
342
+
343
+  p->ci              = 0;
344
+
345
+  p->curTokenId      = kErrorLexTId;
346
+  p->curTokenCharIdx = cmInvalidIdx;
347
+  p->curTokenCharCnt = 0;
348
+
349
+  p->curLine         = 0;
350
+  p->curCol          = 0;
351
+  p->nextLine        = 0;
352
+  p->nextCol         = 0;
353
+
354
+  cmErrClearRC(&p->err);
355
+
356
+  return kOkLexRC;
357
+}
358
+
359
+cmRC_t _cmLexSetTextBuffer( cmLex* p, const cmChar_t* cp, unsigned cn )
360
+{
361
+  p->cp = cp;
362
+  p->cn = cn;
363
+
364
+  return _cmLexReset(p);
365
+}
366
+
367
+
368
+cmLexH cmLexInit( const cmChar_t* cp, unsigned cn, unsigned flags, cmRpt_t* rpt )
369
+{
370
+  cmLexH       h;
371
+  cmChar_t  dfltLineCmt[]     = "//";
372
+  cmChar_t  dfltBlockBegCmt[] = "/*";
373
+  cmChar_t  dfltBlockEndCmt[] = "*/";
374
+  
375
+  cmLex* p          = cmMemAllocZ( cmLex, 1 );
376
+
377
+  cmErrSetup(&p->err,rpt,"Lexer");
378
+
379
+  p->flags           = flags;
380
+  
381
+  _cmLexSetTextBuffer( p, cp, cn );
382
+
383
+  /*
384
+  p->cp              = (cn==0)    ? NULL : cp;
385
+  p->cn              = (cp==NULL) ? 0    : cn;
386
+
387
+  p->ci              = 0;
388
+
389
+
390
+  p->curTokenId      = kErrorLexTId;
391
+  p->curTokenCharIdx = cmInvalidIdx;
392
+  p->curTokenCharCnt = 0;
393
+
394
+  p->curLine         = 0;
395
+  p->curCol          = 0;
396
+  p->nextLine        = 0;
397
+  p->nextCol         = 0;
398
+  */
399
+
400
+  int init_mfn       = 10;
401
+  p->mfp             = cmMemAllocZ( cmLexMatcher, init_mfn );
402
+  p->mfn             = init_mfn;
403
+  p->mfi             = 0;
404
+
405
+
406
+  p->lineCmtStr      = cmMemAlloc( cmChar_t, strlen(dfltLineCmt)+1 );
407
+  strcpy( p->lineCmtStr, dfltLineCmt );
408
+
409
+  p->blockBegCmtStr  = cmMemAlloc( cmChar_t, strlen(dfltBlockBegCmt)+1 );
410
+  strcpy( p->blockBegCmtStr, dfltBlockBegCmt );
411
+
412
+  p->blockEndCmtStr  = cmMemAlloc( cmChar_t, strlen(dfltBlockEndCmt)+1 );
413
+  strcpy( p->blockEndCmtStr, dfltBlockEndCmt );
414
+
415
+
416
+  _cmLexInstallMatcher( p, kSpaceLexTId,    _cmLexSpaceMatcher,    NULL, NULL );
417
+  _cmLexInstallMatcher( p, kRealLexTId,     _cmLexRealMatcher,     NULL, NULL  );
418
+  _cmLexInstallMatcher( p, kIntLexTId,      _cmLexIntMatcher,      NULL, NULL  );
419
+  _cmLexInstallMatcher( p, kHexLexTId,      _cmLexHexMatcher,      NULL, NULL  );
420
+  _cmLexInstallMatcher( p, kIdentLexTId,    _cmLexIdentMatcher,    NULL, NULL  );
421
+  _cmLexInstallMatcher( p, kQStrLexTId,     _cmLexQStrMatcher,     NULL, NULL  );
422
+  _cmLexInstallMatcher( p, kBlockCmtLexTId, _cmLexBlockCmtMatcher, NULL, NULL  );
423
+  _cmLexInstallMatcher( p, kLineCmtLexTId,  _cmLexLineCmtMatcher,  NULL, NULL  );
424
+
425
+  h.h = p;
426
+
427
+  _cmLexReset(p);
428
+
429
+  return h;
430
+}
431
+
432
+cmRC_t cmLexFinal( cmLexH* hp )
433
+{
434
+  if( hp == NULL )
435
+    return cmOkRC;
436
+
437
+  cmLex* p = _cmLexHandleToPtr(*hp);
438
+
439
+  if( p != NULL )
440
+  {
441
+
442
+    if( p->mfp != NULL )
443
+    {
444
+      unsigned i = 0;
445
+
446
+      // free the user token strings
447
+      for(; i<p->mfi; ++i)
448
+        if( p->mfp[i].tokenStr != NULL )
449
+          cmMemPtrFree(&p->mfp[i].tokenStr);
450
+
451
+      // free the matcher array
452
+      cmMemPtrFree(&p->mfp);
453
+      p->mfi = 0;
454
+      p->mfn = 0;
455
+    }
456
+
457
+    cmMemPtrFree(&p->lineCmtStr);
458
+    cmMemPtrFree(&p->blockBegCmtStr);
459
+    cmMemPtrFree(&p->blockEndCmtStr);
460
+    cmMemPtrFree(&p->textBuf);
461
+
462
+    // free the lexer object
463
+    cmMemPtrFree(&p);
464
+    hp->h = NULL;
465
+  }
466
+
467
+  return kOkLexRC;
468
+}
469
+
470
+cmRC_t cmLexReset( cmLexH h )
471
+{
472
+  cmLex* p = _cmLexHandleToPtr(h);
473
+  return _cmLexReset(p);
474
+}
475
+
476
+
477
+bool               cmLexIsValid( cmLexH h )
478
+{ return h.h != NULL; }
479
+
480
+cmRC_t             cmLexSetTextBuffer( cmLexH h, const cmChar_t* cp, unsigned cn )
481
+{
482
+  cmLex* p = _cmLexHandleToPtr(h);
483
+  return _cmLexSetTextBuffer(p,cp,cn);
484
+}
485
+
486
+cmRC_t cmLexSetFile( cmLexH h, const cmChar_t* fn )
487
+{
488
+  cmRC_t    rc = kOkLexRC;
489
+  cmFileH_t fh = cmFileNullHandle;
490
+  cmLex*    p  = _cmLexHandleToPtr(h);
491
+  long      n  = 0;
492
+
493
+  assert( fn != NULL && p != NULL );
494
+
495
+  // open the file
496
+  if( cmFileOpen(&fh,fn,kReadFileFl,p->err.rpt) != kOkFileRC )
497
+    return kFileOpenErrLexRC;
498
+
499
+  // seek to the end of the file
500
+  if( cmFileSeek(fh,kEndFileFl,0) != kOkFileRC )
501
+    return kFileSeekErrLexRC;
502
+  
503
+  // get the length of the file
504
+  if( cmFileTell(fh,&n) != kOkFileRC )
505
+    return kFileTellErrLexRC;
506
+
507
+  // rewind to the beginning of the file
508
+  if( cmFileSeek(fh,kBeginFileFl,0) != kOkFileRC )
509
+    return kFileSeekErrLexRC;
510
+
511
+  // allocate the text buffer
512
+  if((p->textBuf = cmMemResizeZ( char, p->textBuf, n+1)) == NULL )
513
+  {
514
+    rc = _cmLexError(p,kMemAllocErrLexRC,"Unable to allocate the text file buffer for:'%s'.",fn);
515
+    goto errLabel;
516
+  }
517
+
518
+  // read the file into the buffer
519
+  if( cmFileRead(fh,p->textBuf,n) != kOkFileRC )
520
+    return kFileReadErrLexRC;
521
+
522
+  if((rc = _cmLexSetTextBuffer( p, p->textBuf, n )) != kOkLexRC )
523
+    goto errLabel;
524
+  
525
+ errLabel:
526
+  // close the file
527
+  if( cmFileClose(&fh) != kOkFileRC )
528
+    return kFileCloseErrLexRC;
529
+
530
+  return rc;
531
+}
532
+
533
+/*
534
+cmRC_t cmLexSetFile( cmLexH h, const cmChar_t* fn )
535
+{
536
+  cmRC_t   rc      = kOkLexRC;
537
+  FILE*    fp      = NULL;
538
+  cmLex*   p       = _cmLexHandleToPtr(h);
539
+  unsigned n       = 0;
540
+
541
+  assert( fn != NULL && p != NULL );
542
+  
543
+  // open the file
544
+  if((fp = fopen(fn,"rb")) == NULL )
545
+    return _cmLexError(p,kFileOpenErrLexRC,"Unable to open the file:'%s'.",fn);
546
+
547
+  // seek to the end
548
+  if( fseek(fp,0,SEEK_END) != 0 )
549
+  {
550
+    rc= _cmLexError(p,kFileSeekErrLexRC,"Unable to seek to the end of '%s'.",fn);
551
+    goto errLabel;
552
+  }
553
+
554
+  // get the length of the file
555
+  if( (n=ftell(fp)) == 0 )
556
+  {
557
+    rc = _cmLexError(p,kFileOpenErrLexRC,"The file '%s' appears to be empty.",fn);
558
+    goto errLabel;
559
+  }
560
+
561
+  // rewind the file
562
+  if( fseek(fp,0,SEEK_SET) != 0 )
563
+  {
564
+    rc = _cmLexError(p,kFileSeekErrLexRC,"Unable to seek to the beginning of '%s'.",fn);
565
+    goto errLabel;
566
+  }
567
+
568
+  // allocate the text buffer
569
+  if((p->textBuf = cmMemResizeZ( char, p->textBuf, n+1)) == NULL )
570
+  {
571
+    rc = _cmLexError(p,kMemAllocErrLexRC,"Unable to allocate the text file buffer for:'%s'.",fn);
572
+    goto errLabel;
573
+  }
574
+
575
+  // read the file into the text buffer
576
+  if( fread(p->textBuf,n,1,fp) != 1 )
577
+  {
578
+    rc = _cmLexError(p,kFileReadErrLexRC,"File read failed on:'%s'.",fn);
579
+    goto errLabel;
580
+  }  
581
+
582
+  if((rc = _cmLexSetTextBuffer( p, p->textBuf, n )) != kOkLexRC )
583
+    goto errLabel;
584
+
585
+ errLabel:
586
+
587
+  // close the file
588
+  if( fclose(fp) != 0 )
589
+  {
590
+    rc =  _cmLexError(p,kFileCloseErrLexRC,"File close failed on:'%s'.",fn);
591
+    goto errLabel;
592
+  }
593
+
594
+  return rc;
595
+}
596
+*/
597
+
598
+cmLexMatcher* _cmLexFindUserToken( cmLex* p, unsigned id, const cmChar_t* tokenStr )
599
+{
600
+  unsigned i = 0;
601
+  for(; i<p->mfi; ++i)
602
+  {
603
+    if( id != cmInvalidId && p->mfp[i].typeId == id  )
604
+      return p->mfp + i;
605
+
606
+    if( p->mfp[i].tokenStr != NULL && tokenStr != NULL && strcmp(p->mfp[i].tokenStr,tokenStr)==0  )
607
+      return p->mfp + i;
608
+
609
+  }
610
+
611
+  return NULL;
612
+}
613
+
614
+
615
+cmRC_t            cmLexRegisterToken( cmLexH h, unsigned id, const cmChar_t* tokenStr )
616
+{
617
+  cmLex* p = _cmLexHandleToPtr(h);
618
+
619
+  // prevent duplicate tokens
620
+  if( _cmLexFindUserToken( p, id, tokenStr ) != NULL )
621
+    return _cmLexError( p, kDuplicateTokenLexRC, "id:%i token:%s duplicates the token string or id", id, tokenStr );
622
+
623
+
624
+  return _cmLexInstallMatcher( p, id, _cmLexExactStringMatcher, tokenStr, NULL );
625
+  
626
+
627
+}
628
+
629
+cmRC_t             cmLexRegisterMatcher( cmLexH h, unsigned id, cmLexUserMatcherPtr_t userPtr )
630
+{
631
+  cmLex* p = _cmLexHandleToPtr(h);
632
+
633
+  // prevent duplicate tokens
634
+  if( _cmLexFindUserToken( p, id, NULL ) != NULL )
635
+    return _cmLexError( p, kDuplicateTokenLexRC, "A token matching function has already been installed for token id: %i", id );
636
+
637
+  return _cmLexInstallMatcher( p, id, NULL, NULL, userPtr );
638
+}
639
+
640
+
641
+unsigned           cmLexGetNextToken( cmLexH h )
642
+{
643
+  cmLex* p = _cmLexHandleToPtr(h);
644
+
645
+  if( cmErrLastRC(&p->err) != kOkLexRC )
646
+    return kErrorLexTId;
647
+
648
+  while( p->ci < p->cn )
649
+  {
650
+    unsigned i;
651
+    unsigned mi         = 0;
652
+    unsigned maxCharCnt = 0;
653
+    unsigned maxIdx     = cmInvalidIdx;
654
+
655
+    p->curTokenId      = kErrorLexTId;
656
+    p->curTokenCharIdx = cmInvalidIdx;
657
+    p->curTokenCharCnt = 0;
658
+
659
+
660
+    for(; mi<p->mfi; ++mi)
661
+    {
662
+      unsigned charCnt = 0;
663
+      if( p->mfp[mi].funcPtr != NULL )
664
+        charCnt = p->mfp[mi].funcPtr(p, p->cp + p->ci, p->cn - p->ci, p->mfp[mi].tokenStr );
665
+      else
666
+        charCnt = p->mfp[mi].userPtr( p->cp + p->ci, p->cn - p->ci);
667
+
668
+      if( cmErrLastRC(&p->err) != kOkLexRC )
669
+        return kErrorLexTId;
670
+
671
+      // if this matched token is longer then the prev. matched token or
672
+      // if the prev matched token was an identifier and this matched token is an equal length user defined token
673
+      if( (charCnt > maxCharCnt) || (charCnt>0 && charCnt==maxCharCnt && p->mfp[maxIdx].typeId==kIdentLexTId && p->mfp[mi].typeId >=kUserLexTId ) )
674
+      {
675
+        maxCharCnt = charCnt;
676
+        maxIdx     = mi;
677
+      }
678
+
679
+    }
680
+
681
+    // no token was matched
682
+    if( maxIdx == cmInvalidIdx )
683
+    {
684
+      _cmLexError( p, kNoMatchLexRC, "Unable to recognize token:'%c'.",*(p->cp+p->ci));
685
+      return kErrorLexTId;     
686
+    }
687
+
688
+    // update the current line and column position    
689
+    p->curLine = p->nextLine;
690
+    p->curCol  = p->nextCol;
691
+    
692
+
693
+    // find the next column and line position
694
+    for(i=0; i<maxCharCnt; ++i)
695
+    {
696
+      if( _cmLexIsNewline(p->cp[ p->ci + i ]) )
697
+      {
698
+        p->nextLine++;
699
+        p->nextCol = 1;
700
+      }
701
+      else
702
+        p->nextCol++;
703
+    }
704
+
705
+    bool returnFl = true;
706
+
707
+    // check the space token filter
708
+    if( (p->mfp[ maxIdx ].typeId == kSpaceLexTId) && (cmIsFlag(p->flags,kReturnSpaceLexFl)==0) )
709
+      returnFl = false;
710
+
711
+    // check the comment token filter
712
+    if( _cmLexIsCommentTypeId(p->mfp[ maxIdx ].typeId) && (cmIsFlag(p->flags,kReturnCommentsLexFl)==0) )
713
+      returnFl = false;
714
+
715
+    // update the lexer state
716
+    p->curTokenId      = p->mfp[ maxIdx ].typeId;    
717
+    p->curTokenCharIdx = p->ci;
718
+    p->curTokenCharCnt = maxCharCnt;
719
+      
720
+    // advance the text buffer
721
+    p->ci += maxCharCnt;
722
+
723
+    if( returnFl )
724
+      return p->curTokenId;
725
+  }
726
+
727
+  cmErrSetRC(&p->err,kEofRC);
728
+
729
+  return kEofLexTId;
730
+
731
+}
732
+
733
+unsigned cmLexTokenId( cmLexH h )
734
+{
735
+  cmLex* p = _cmLexHandleToPtr(h);
736
+
737
+  return p->curTokenId;
738
+}
739
+
740
+const cmChar_t* cmLexTokenText( cmLexH h )
741
+{
742
+  cmLex* p = _cmLexHandleToPtr(h);
743
+
744
+  if( p->curTokenCharIdx == cmInvalidIdx )
745
+    return NULL;
746
+
747
+  unsigned n = p->curTokenId == kQStrLexTId ? 1 : 0;
748
+
749
+  return p->cp + p->curTokenCharIdx + n;
750
+}
751
+
752
+
753
+unsigned           cmLexTokenCharCount(  cmLexH h )
754
+{
755
+  cmLex* p = _cmLexHandleToPtr(h);
756
+
757
+  if( p->curTokenCharIdx == cmInvalidIdx )
758
+    return 0;
759
+
760
+  unsigned n = p->curTokenId == kQStrLexTId ? 2 : 0;
761
+
762
+  return p->curTokenCharCnt - n;
763
+}
764
+
765
+int                cmLexTokenInt(        cmLexH h )
766
+{  return strtol( cmLexTokenText(h),NULL,0 ); }
767
+
768
+unsigned           cmLexTokenUInt(        cmLexH h )
769
+{  return strtol( cmLexTokenText(h),NULL,0 ); }
770
+
771
+float              cmLexTokenFloat(        cmLexH h )
772
+{  return strtof( cmLexTokenText(h),NULL ); }
773
+
774
+double             cmLexTokenDouble(        cmLexH h )
775
+{  return strtod( cmLexTokenText(h),NULL ); }
776
+
777
+unsigned cmLexCurrentLineNumber( cmLexH h )
778
+{ 
779
+  cmLex* p = _cmLexHandleToPtr(h);
780
+  return p->curLine + 1;
781
+}
782
+
783
+unsigned cmLexCurrentColumnNumber( cmLexH h ) 
784
+{
785
+  cmLex* p = _cmLexHandleToPtr(h);
786
+  return p->curCol + 1;
787
+}
788
+
789
+unsigned           cmLexErrorRC( cmLexH h )
790
+{
791
+  cmLex* p = _cmLexHandleToPtr(h);
792
+  return cmErrLastRC(&p->err);
793
+}
794
+
795
+const cmChar_t* cmLexIdToLabel( cmLexH h, unsigned typeId )
796
+{
797
+  cmLex* p = _cmLexHandleToPtr(h);
798
+
799
+  switch( typeId )
800
+  {
801
+    case kErrorLexTId:    return "<error>";
802
+    case kEofLexTId:      return "<EOF>";
803
+    case kSpaceLexTId:    return "<space>";
804
+    case kRealLexTId:     return "<real>";
805
+    case kIntLexTId:      return "<int>";
806
+    case kHexLexTId:      return "<hex>";
807
+    case kIdentLexTId:    return "<ident>";
808
+    case kQStrLexTId:     return "<qstr>";
809
+    case kBlockCmtLexTId: return "<bcmt>";
810
+    case kLineCmtLexTId:  return "<lcmt>";
811
+    default:
812
+      {
813
+        cmLexMatcher*  mp;
814
+        if((mp = _cmLexFindUserToken(p,typeId,NULL)) == NULL )
815
+          return "<unknown>";
816
+        return mp->tokenStr;
817
+      }
818
+  }
819
+  return "<invalid>";
820
+}
821
+
822
+const cmChar_t* cmLexRcToMsg( unsigned rc )
823
+{
824
+  unsigned i=0;
825
+  for(i=0; cmLexErrorArray[i].code != kInvalidLexRC; ++i)
826
+    if( cmLexErrorArray[i].code == rc )
827
+      break;
828
+
829
+  return cmLexErrorArray[i].msg;
830
+}
831
+
832
+
833
+//{ { label:cmLexEx }
834
+//(
835
+// cmLexTest() gives a simple cmLex example.
836
+//)
837
+
838
+//[
839
+void cmLexTest( cmRpt_t* rpt)
840
+{
841
+  cmChar_t buf[] =
842
+"123ident0\n\
843
+ 123.456\n\
844
+ident0\n\
845
+0xa12+.2\n\
846
+// comment \n\
847
+/* block \n\
848
+comment */\
849
+\"quoted string\"\
850
+ident1\
851
+// last line comment\
852
+";
853
+
854
+  // initialize a lexer with a buffer of text
855
+  cmLexH h = cmLexInit(buf,strlen(buf),
856
+    kReturnSpaceLexFl | kReturnCommentsLexFl,rpt);
857
+
858
+  // verify that the lexer initialization succeded.
859
+  if( cmLexIsValid(h) == false )
860
+  {
861
+    cmRptPrintf(rpt,"Lexer initialization failed.");
862
+    return;
863
+  }
864
+
865
+  // register some additional recoginizers
866
+  cmLexRegisterToken(h,kUserLexTId+1,"+");
867
+  cmLexRegisterToken(h,kUserLexTId+2,"-");
868
+
869
+  unsigned tid;
870
+
871
+  // ask for token id's 
872
+  while( (tid = cmLexGetNextToken(h)) != kEofLexTId )
873
+  {
874
+    // print information about each token
875
+    cmRptPrintf(rpt,"%i %i %s '%.*s' (%i) ", 
876
+      cmLexCurrentLineNumber(h), 
877
+      cmLexCurrentColumnNumber(h), 
878
+      cmLexIdToLabel(h,tid), 
879
+      cmLexTokenCharCount(h), 
880
+      cmLexTokenText(h) , 
881
+      cmLexTokenCharCount(h));
882
+
883
+    // if the token is a number ...
884
+    if( tid==kIntLexTId || tid==kRealLexTId || tid==kHexLexTId )
885
+    {
886
+      // ... then request the numbers value
887
+      int    iv = cmLexTokenInt(h);
888
+      double dv = cmLexTokenDouble(h);
889
+
890
+      cmRptPrintf(rpt,"%i %f",iv,dv);
891
+    }
892
+
893
+    cmRptPrintf(rpt,"\n");
894
+
895
+    // handle errors
896
+    if( tid == kErrorLexTId )
897
+    {
898
+      cmRptPrintf(rpt,"Error:%i\n", cmLexErrorRC(h));
899
+      break;
900
+    }
901
+
902
+  }
903
+
904
+  // finalize the lexer 
905
+  cmLexFinal(&h);
906
+
907
+}
908
+
909
+//]
910
+//}

+ 139
- 0
cmLex.h View File

@@ -0,0 +1,139 @@
1
+#ifndef cmLex_h
2
+#define cmLex_h
3
+
4
+//{
5
+//(
6
+//)
7
+
8
+//(
9
+
10
+
11
+// Predefined Lexer Id's
12
+enum
13
+{
14
+  kErrorLexTId,    // 0  the lexer was unable to identify the current token
15
+  kEofLexTId,      // 1  the lexer reached the end of input
16
+  kSpaceLexTId,    // 2  white space
17
+  kRealLexTId,     // 3  real number (contains a decimal point or is in scientific notation) 
18
+  kIntLexTId,      // 4  decimal integer
19
+  kHexLexTId,      // 5  hexidecimal integer
20
+  kIdentLexTId,    // 6  identifier
21
+  kQStrLexTId,     // 7  quoted string
22
+  kBlockCmtLexTId, // 8  block comment
23
+  kLineCmtLexTId,  // 9  line comment
24
+  kUserLexTId      // 10 user registered token (See cmLexRegisterToken().)
25
+};
26
+
27
+// Lexer control flags used with cmLexInit().
28
+enum
29
+{
30
+  kReturnSpaceLexFl    = 0x01, //< Return space tokens
31
+  kReturnCommentsLexFl = 0x02  //< Return comment tokens
32
+};
33
+
34
+// cmLex result codes.
35
+enum
36
+{
37
+  kOkLexRC = cmOkRC,       //< 0  No error. The operation completed successfully      
38
+  kDuplicateTokenLexRC,    //< 1  The text or id passed as a user token is already in use by another token
39
+  kMissingCmtEndLexRC,     //< 2  The end of a block comment could not be found. 
40
+  kMissingEndQuoteLexRC,   //< 3  The end of a quoted string could not be found.
41
+  kNoMatchLexRC,           //< 4  The lexer encountered a string which could not be classified.
42
+  kFileOpenErrLexRC,       //< 5  File open failed on cmLexSetFile()
43
+  kFileSeekErrLexRC,       //< 6  File seek failed on cmLexSetFile()
44
+  kFileTellErrLexRC,       //< 7  File tell failed on cmLexSetFile()
45
+  kFileReadErrLexRC,       //< 8  File read failed on cmLexSetFile()
46
+  kFileCloseErrLexRC,      //< 9  File close failed on cmLexSetFile()
47
+  kMemAllocErrLexRC,       //< 10  An attempted memory allocation failed
48
+  kEofRC,                  //< 11 The end of the input text was encountered (this is a normal condition not an error)
49
+  kInvalidLexRC            //< 12 Sentinal value.
50
+
51
+};
52
+
53
+typedef cmHandle_t cmLexH;
54
+
55
+extern cmLexH cmLexNullH;
56
+
57
+
58
+// Iniitalize the lexer and receive a lexer handle in return.
59
+// Set cp to NULL if the buffer will be later via cmLexSetTextBuffer();
60
+// See the kXXXLexFl enum's above for possible flag values.
61
+cmLexH             cmLexInit( const cmChar_t* cp, unsigned cn, unsigned flags, cmRpt_t* rpt );
62
+
63
+// Finalize a lexer created by an earlier call to cmLexInit()
64
+cmRC_t             cmLexFinal( cmLexH* hp );
65
+
66
+// Rewind the lexer to the begining of the buffer (the same as post initialize state)
67
+cmRC_t             cmLexReset( cmLexH h );
68
+
69
+// Verify that a lexer handle is valid
70
+bool               cmLexIsValid( cmLexH h );
71
+
72
+// Set a new text buffer and reset the lexer to the post initialize state.
73
+cmRC_t             cmLexSetTextBuffer( cmLexH h, const cmChar_t* cp, unsigned cn );
74
+cmRC_t             cmLexSetFile( cmLexH h, const cmChar_t* fn );
75
+
76
+// Register a user defined token. The id of the first user defined token should be
77
+// kUserLexTId+1.  Neither the id or token text can be used by a previously registered
78
+// or built-in token. 
79
+cmRC_t             cmLexRegisterToken( cmLexH h, unsigned id, const cmChar_t* token );
80
+
81
+// Register a user defined token recognition function.  This function should return the count
82
+// of initial, consecutive, characters in 'cp' which match its token pattern.
83
+typedef unsigned (*cmLexUserMatcherPtr_t)( const cmChar_t* cp, unsigned cn );
84
+
85
+cmRC_t             cmLexRegisterMatcher( cmLexH h, unsigned id, cmLexUserMatcherPtr_t funcPtr );
86
+
87
+// Return the type id of the current token and advances to the next token
88
+unsigned           cmLexGetNextToken( cmLexH h );
89
+
90
+// Return the type id associated with the current token. This is the same value
91
+// returned by the previous call to cmLexGetNextToken().
92
+unsigned           cmLexTokenId( cmLexH h ); 
93
+
94
+// Return a pointer to the first character of text associated with the 
95
+// current token. The returned pointer directly references the text contained
96
+// in the buffer given to the lexer in the call to cmLexInit().  The string
97
+// is therefore not zero terminated. Use cmLexTokenCharCount() to get the 
98
+// length of the token string.
99
+const cmChar_t* cmLexTokenText( cmLexH h );
100
+
101
+// Return the count of characters in the text associated with the current token.
102
+// This is the only way to get this count since the string returned by 
103
+// cmLexTokenText() is not zero terminated.
104
+unsigned           cmLexTokenCharCount(  cmLexH h );
105
+
106
+// Return the value of the current token as an integer.
107
+int                cmLexTokenInt( cmLexH h );
108
+
109
+// Return the value of the current token as an integer.
110
+unsigned           cmLexTokenUInt( cmLexH h );
111
+
112
+// Return the value of the current token as an integer.
113
+float              cmLexTokenFloat( cmLexH h );
114
+
115
+// Return the value of the current token as a double.
116
+double             cmLexTokenDouble( cmLexH h );
117
+
118
+// Return the line number associated with the current token 
119
+unsigned           cmLexCurrentLineNumber( cmLexH h );
120
+
121
+// Return the starting column of the current token
122
+unsigned           cmLexCurrentColumnNumber( cmLexH h ); 
123
+
124
+// Return the RC code associated with the last error
125
+unsigned           cmLexErrorRC( cmLexH h );
126
+
127
+// Return the label associated with a token id
128
+const cmChar_t* cmLexIdToLabel( cmLexH h, unsigned typeId );
129
+
130
+// Return the text message associated with a return code. 
131
+const cmChar_t* cmLexRcToMsg( unsigned rc );
132
+
133
+// Lexer testing stub.
134
+void cmLexTest( cmRpt_t* rpt );
135
+
136
+//)
137
+//}
138
+
139
+#endif

+ 309
- 0
cmLib.c View File

@@ -0,0 +1,309 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmMallocDebug.h"
8
+#include "cmFileSys.h"
9
+
10
+#include "cmLib.h"
11
+
12
+#ifdef OS_LINUX
13
+
14
+#include <dlfcn.h>
15
+
16
+typedef void* _libH_t;
17
+
18
+bool _cmLibIsNull( _libH_t lh )
19
+{ return lh == NULL; };
20
+
21
+const cmChar_t* _cmLibSysError()
22
+{
23
+  const char* msg = dlerror();
24
+  if( msg == NULL )
25
+    msg = "<none>";
26
+  return msg;
27
+}
28
+
29
+_libH_t  _cmLibOpen( const char* libFn )
30
+{ return dlopen(libFn,RTLD_LAZY); }
31
+
32
+bool _cmLibClose( _libH_t* lH )
33
+{
34
+  if( *lH != NULL )
35
+  {
36
+    if( dlclose(*lH) == 0 )
37
+      *lH = NULL;
38
+    else
39
+      return false;
40
+  }
41
+
42
+  return true;
43
+}
44
+
45
+void* _cmLibSym( _libH_t h, const char* symLabel )
46
+{ return dlsym(h,symLabel); }
47
+
48
+
49
+#endif
50
+
51
+
52
+typedef struct cmLibNode_str
53
+{
54
+  cmChar_t*             fn;
55
+  unsigned              id;
56
+  _libH_t               lH;
57
+  struct cmLibNode_str* link;
58
+} cmLibNode_t;
59
+
60
+typedef struct
61
+{
62
+  cmErr_t      err;
63
+  cmLibNode_t* nodes;
64
+  unsigned     id;
65
+  cmFileSysH_t fsH;
66
+} cmLib_t;
67
+
68
+cmLib_t* _cmLibHandleToPtr( cmLibH_t h )
69
+{
70
+  cmLib_t* p = (cmLib_t*)h.h;
71
+  return p;
72
+}
73
+
74
+cmLibH_t cmLibNullHandle = cmSTATIC_NULL_HANDLE;
75
+
76
+
77
+cmLibRC_t _cmLibNodeFree( cmLib_t* p, cmLibNode_t* np )
78
+{
79
+  if( !_cmLibClose( &np->lH ) )
80
+    return cmErrMsg(&p->err,kCloseFailLibRC,"Library close failed. System Message: %s", _cmLibSysError());
81
+
82
+  // free np->fn and set np->fn to NULL - so the node may be reused.
83
+  cmMemPtrFree(&np->fn);
84
+  np->id = cmInvalidId;
85
+ 
86
+  return kOkLibRC;
87
+}
88
+
89
+
90
+
91
+cmLibRC_t _cmLibFinalize( cmLib_t* p )
92
+{
93
+  cmLibNode_t* np = p->nodes;
94
+
95
+  while( np != NULL )
96
+  {
97
+    cmLibNode_t* pp = np->link;
98
+    _cmLibNodeFree(p,np);
99
+    cmMemFree(np);
100
+    np = pp;
101
+  }
102
+
103
+  if( cmFileSysIsValid(p->fsH) )
104
+    cmFileSysFinalize(&p->fsH);
105
+
106
+  cmMemFree(p);
107
+
108
+  return kOkLibRC;
109
+}
110
+
111
+cmLibRC_t cmLibInitialize(  cmCtx_t* ctx, cmLibH_t* hp, const cmChar_t* dirStr )
112
+{
113
+  cmLibRC_t rc = kOkLibRC;
114
+  cmLib_t*  p  = cmMemAllocZ(cmLib_t,1);
115
+
116
+  cmErrSetup(&p->err,&ctx->rpt,"cmLib");
117
+
118
+  cmFileSysInitialize(&p->fsH,ctx,"cmLibFs");
119
+
120
+  hp->h = p;
121
+
122
+  if( dirStr != NULL )
123
+    if((rc = cmLibScan(*hp,dirStr)) != kOkLibRC )
124
+      hp->h = NULL;
125
+
126
+  if( rc != kOkLibRC )
127
+    _cmLibFinalize(p);
128
+
129
+  return rc;
130
+}
131
+
132
+
133
+cmLibRC_t cmLibFinalize( cmLibH_t* hp )
134
+{
135
+  cmLibRC_t rc;
136
+
137
+  if( hp == NULL || hp->h == NULL )
138
+    return kOkLibRC;
139
+
140
+  cmLib_t* p = _cmLibHandleToPtr(*hp);
141
+
142
+  if((rc = _cmLibFinalize(p)) == kOkLibRC )
143
+    hp->h = NULL;
144
+
145
+  return rc;
146
+}
147
+
148
+bool      cmLibIsValid( cmLibH_t h )
149
+{ return h.h != NULL; }
150
+
151
+
152
+unsigned  cmLibOpen(  cmLibH_t h, const cmChar_t* libFn )
153
+{
154
+  cmLib_t*     p   = _cmLibHandleToPtr(h);  
155
+  _libH_t      lH  = _cmLibOpen(libFn);
156
+  cmLibNode_t* np  = p->nodes;
157
+  unsigned     idx = 0;
158
+
159
+  if( _cmLibIsNull(lH) )
160
+  {
161
+    cmErrMsg(&p->err,kOpenFailLibRC,"Library load failed. System Message: %s", _cmLibSysError() );
162
+    return cmInvalidId;
163
+  }
164
+  
165
+  while( np != NULL )
166
+  {
167
+    if( np->fn == NULL )
168
+      break;
169
+
170
+    np = np->link;
171
+    ++idx;
172
+  }
173
+
174
+  if( np == NULL )
175
+  {
176
+    np = cmMemAllocZ(cmLibNode_t,1);
177
+    np->link = p->nodes;
178
+    p->nodes = np;
179
+  }
180
+
181
+  np->fn = cmMemAllocStr(libFn);
182
+  np->lH = lH;
183
+  np->id = p->id++;
184
+
185
+  return idx;  
186
+}
187
+
188
+cmLibNode_t* _cmLibIdToNode( cmLib_t* p, unsigned libId )
189
+{
190
+  cmLibNode_t* np  = p->nodes;
191
+
192
+  while( np != NULL )
193
+  {
194
+    if( np->id == libId )
195
+      return np;
196
+
197
+    np = np->link;
198
+  }
199
+
200
+  return NULL;
201
+}
202
+
203
+cmLibRC_t cmLibClose( cmLibH_t h, unsigned libId )
204
+{
205
+  cmLib_t*     p   = _cmLibHandleToPtr(h);  
206
+  cmLibNode_t* np  = _cmLibIdToNode(p,libId);
207
+  
208
+  if( (np == NULL) || _cmLibIsNull(np->lH) )
209
+    return cmErrMsg(&p->err,kInvalidIdLibRC,"The library id %i is not valid or the library is closed.",libId);
210
+
211
+  
212
+  return kOkLibRC;
213
+}
214
+
215
+void*     cmLibSym( cmLibH_t h, unsigned libId, const cmChar_t* funcStr )
216
+{
217
+  void*        f;
218
+  cmLib_t*     p  = _cmLibHandleToPtr(h);  
219
+  cmLibNode_t* np = _cmLibIdToNode(p,libId);
220
+  
221
+  if( (np == NULL) || _cmLibIsNull(np->lH) )
222
+  {
223
+     cmErrMsg(&p->err,kInvalidIdLibRC,"The library id %i is not valid or the library is closed.",libId);
224
+     return NULL;
225
+  }
226
+
227
+  if((f = _cmLibSym(np->lH,funcStr)) == NULL)
228
+  {
229
+    cmErrMsg(&p->err,kSymFailLibRC,"The dynamic symbol '%s' was not found. System Message: %s", cmStringNullGuard(funcStr), _cmLibSysError());
230
+    return NULL;
231
+  }
232
+
233
+  return f;
234
+}
235
+
236
+
237
+cmLibRC_t cmLibScan( cmLibH_t h, const cmChar_t* dirStr )
238
+{
239
+  cmLib_t*             p           = _cmLibHandleToPtr(h);  
240
+  unsigned             dirEntryCnt = 0;
241
+  cmFileSysDirEntry_t* d           = NULL;
242
+  cmLibRC_t            rc          = kOkLibRC;
243
+  unsigned             i           = 0;
244
+
245
+  if( cmFileSysIsValid(p->fsH) == false )
246
+    return cmErrMsg(&p->err,kFileSysFailLibRC,"The file system object was not successfully initialized.");
247
+
248
+  if((d = cmFileSysDirEntries(p->fsH, dirStr, kFileFsFl, &dirEntryCnt )) != NULL )
249
+    return cmErrMsg(&p->err,kFileSysFailLibRC,"The scan of directory '%s' failed.",cmStringNullGuard(dirStr));
250
+
251
+  for(i=0; i<dirEntryCnt; ++i)
252
+    cmLibOpen(h,d[i].name);
253
+
254
+  cmFileSysDirFreeEntries(p->fsH,d);
255
+  
256
+  return rc;
257
+}
258
+
259
+unsigned  cmLibCount( cmLibH_t h )
260
+{
261
+  cmLib_t*     p   = _cmLibHandleToPtr(h);  
262
+  cmLibNode_t* np  = p->nodes;
263
+  unsigned     n   = 0;
264
+
265
+  while( np != NULL )
266
+  {
267
+    np = np->link;
268
+    ++n;
269
+  }
270
+  return n;
271
+}
272
+
273
+unsigned  cmLibIndexToId( cmLibH_t h, unsigned idx )
274
+{
275
+  cmLib_t*     p   = _cmLibHandleToPtr(h);  
276
+  cmLibNode_t* np  = p->nodes;
277
+  unsigned     i   = 0;
278
+
279
+  while( np != NULL )
280
+  {
281
+    if( i == idx )
282
+      return np->id;
283
+
284
+    np = np->link;
285
+    ++i;
286
+  }
287
+
288
+  if(np == NULL )
289
+  {
290
+     cmErrMsg(&p->err,kInvalidIdLibRC,"The library index %i is not valid.",idx);
291
+     return cmInvalidId;
292
+  }
293
+
294
+  return np->id;
295
+}
296
+
297
+const cmChar_t* cmLibName( cmLibH_t h, unsigned libId )
298
+{
299
+  cmLib_t*     p  = _cmLibHandleToPtr(h);  
300
+  cmLibNode_t* np = _cmLibIdToNode(p,libId);
301
+
302
+  if( (np == NULL) || _cmLibIsNull(np->lH) )
303
+  {
304
+     cmErrMsg(&p->err,kInvalidIdLibRC,"The library id %i is not valid or the library is closed.",libId);
305
+     return NULL;
306
+  }
307
+  
308
+  return np->fn;
309
+}

+ 63
- 0
cmLib.h View File

@@ -0,0 +1,63 @@
1
+#ifndef cmLib_h
2
+#define cmLib_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkLibRC = cmOkRC,
11
+    kOpenFailLibRC,
12
+    kCloseFailLibRC,
13
+    kSymFailLibRC,
14
+    kInvalidIdLibRC,
15
+    kFileSysFailLibRC
16
+  
17
+  };
18
+
19
+  typedef unsigned   cmLibRC_t;
20
+  typedef cmHandle_t cmLibH_t; 
21
+
22
+
23
+  extern cmLibH_t cmLibNullHandle;
24
+
25
+  // Initialize a dynamic library manager and scan a directory for dynamic libraries
26
+  // to load. 'dirStr' is optional.
27
+  cmLibRC_t cmLibInitialize( cmCtx_t* ctx, cmLibH_t* hp, const cmChar_t* dirStr );
28
+
29
+  // Release a dynamic library manager and close any open libraries it may own.
30
+  cmLibRC_t cmLibFinalize(   cmLibH_t* hp );
31
+  
32
+  // Return true if the dynamic library mgr. is initialized.
33
+  bool      cmLibIsValid( cmLibH_t h );
34
+
35
+  // Open a dynamic library.
36
+  // Return cmInvalidId on error.
37
+  unsigned  cmLibOpen(  cmLibH_t h, const cmChar_t* libFn );
38
+
39
+  // Close a dynamic library.
40
+  cmLibRC_t cmLibClose( cmLibH_t h, unsigned libId );
41
+
42
+  // Return a pointer to a symbol from a dynamic library.
43
+  void*     cmLibSym( cmLibH_t h, unsigned libId, const cmChar_t* fn );
44
+
45
+  // Scan a directory for dynamic libraries.
46
+  cmLibRC_t cmLibScan( cmLibH_t h, const cmChar_t* dirStr );
47
+
48
+  // Return the count of open libraries.
49
+  unsigned  cmLibCount( cmLibH_t h );
50
+
51
+  // Return a library id given an index
52
+  unsigned  cmLibIndexToId( cmLibH_t h, unsigned idx );
53
+
54
+  // Return the libraries file name.
55
+  const cmChar_t* cmLibName( cmLibH_t h, unsigned libId );
56
+  
57
+  
58
+
59
+#ifdef __cplusplus
60
+}
61
+#endif
62
+
63
+#endif

+ 358
- 0
cmLinkedHeap.c View File

@@ -0,0 +1,358 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmCtx.h"
6
+#include "cmMem.h"
7
+#include "cmLinkedHeap.h"
8
+#include "cmMallocDebug.h"
9
+
10
+typedef struct cmLhBlock_str
11
+{
12
+  char*                 basePtr;     // base of block
13
+  char*                 nextPtr;     // next avail location in block
14
+  char*                 endPtr;      // one past end of block
15
+  struct cmLhBlock_str* prevBlkPtr;  // backward block link
16
+  struct cmLhBlock_str* nextBlkPtr;  // forward block link
17
+  unsigned              freeCnt;     // track orphaned space that is unavailable for reuse
18
+} cmLhBlock_t;
19
+
20
+typedef struct
21
+{
22
+  unsigned      dfltBlockByteCnt; // size of each block in chain
23
+  cmLhBlock_t*  first;            // first block in chain
24
+  cmLhBlock_t*  last;             // last block in chain
25
+  cmMmH_t       mmH;
26
+} cmLHeap_t;
27
+
28
+cmLHeapH_t cmLHeapNullHandle = { NULL };
29
+
30
+cmLHeap_t* _cmLHeapHandleToPtr( cmLHeapH_t h )
31
+{
32
+  cmLHeap_t* lhp = (cmLHeap_t*)h.h;
33
+  assert( lhp != NULL);
34
+  return lhp;
35
+}
36
+
37
+cmLhBlock_t* _cmLHeapAllocBlock( cmLHeap_t* lhp, unsigned blockByteCnt )
38
+{
39
+  // note: the entire block (control record and data space) is allocated
40
+  // as one contiguous chunk of memory.
41
+
42
+  cmLhBlock_t* lbp = (cmLhBlock_t*)cmMemMallocZ( sizeof(cmLhBlock_t) + blockByteCnt );
43
+
44
+  // setup the new block
45
+  lbp->basePtr    = (char*)(lbp+1);
46
+  lbp->nextPtr    = lbp->basePtr;
47
+  lbp->endPtr     = lbp->basePtr + blockByteCnt;
48
+  lbp->prevBlkPtr = lhp->last;
49
+  lbp->nextBlkPtr = NULL;
50
+
51
+  // link the new block into the chain
52
+  if( lhp->last != NULL )
53
+    lhp->last->nextBlkPtr = lbp;
54
+  else
55
+  {
56
+    assert( lhp->first == NULL );
57
+    lhp->first = lbp;
58
+  }
59
+
60
+  lhp->last = lbp;
61
+
62
+  return lbp;
63
+}
64
+
65
+void*      _cmLHeapAlloc(  cmLHeap_t* lhp, unsigned dataByteCnt )
66
+{
67
+  void*        retPtr       = NULL;
68
+  cmLhBlock_t* lbp          = lhp->last;
69
+  unsigned     allocByteCnt = dataByteCnt + sizeof(unsigned); 
70
+
71
+  // go backwards down the chain looking for the first block with enough
72
+  // free space to hold the allocation (we go backward under the assumption
73
+  // that the last block is most likely to have available space)
74
+  while( (lbp != NULL) && ((lbp->endPtr - lbp->nextPtr) < allocByteCnt) )
75
+    lbp = lbp->prevBlkPtr; 
76
+
77
+  // no space large enough to provide the requested memory - allocate a new block
78
+  if( lbp == NULL )
79
+    lbp = _cmLHeapAllocBlock(lhp, cmMax( lhp->dfltBlockByteCnt, allocByteCnt ));
80
+
81
+  assert( lbp != NULL );
82
+
83
+  // store the sizeof the allocation at the beginning of the allocated block
84
+  *(unsigned*)lbp->nextPtr  = allocByteCnt;
85
+
86
+  // the returned block ptr begins just after the block size
87
+  retPtr        = lbp->nextPtr + sizeof(unsigned);
88
+
89
+  
90
+  lbp->nextPtr += allocByteCnt;
91
+
92
+  return retPtr;
93
+}
94
+
95
+
96
+void* _cmLHeapAllocCb(void* funcArgPtr, unsigned byteCnt)
97
+{
98
+  cmLHeap_t* p = (cmLHeap_t*)funcArgPtr;
99
+  assert( p != NULL );
100
+  return _cmLHeapAlloc(p,byteCnt);
101
+}
102
+
103
+bool     _cmLHeapFree(    cmLHeap_t* lhp, void* dataPtr )
104
+{ 
105
+  if( dataPtr == NULL )
106
+    return true;
107
+
108
+  cmLhBlock_t* lbp = lhp->first;
109
+
110
+  // locate the block containing the area to free
111
+  while( (lbp != NULL ) && (((char*)dataPtr < lbp->basePtr) || ((char*)dataPtr >= lbp->endPtr)))
112
+    lbp = lbp->nextBlkPtr;
113
+
114
+  // the pointer must be in one of the blocks
115
+  if( lbp == NULL )
116
+    return false;
117
+
118
+  unsigned* allocPtr     = ((unsigned*)dataPtr)-1;
119
+  unsigned  dataByteCnt  = *allocPtr - sizeof(unsigned);
120
+
121
+  // the data to be freed is at the end of the blocks space ...
122
+  if( dataPtr == lbp->nextPtr - dataByteCnt )
123
+    lbp->nextPtr = (char*)allocPtr; // ... then make it the space to alloc
124
+  else           
125
+    lbp->freeCnt += *allocPtr; // ... otherwise increase the free count
126
+  //(freeCnt tracks unused space that is not at the end of the block and therefore cannot be reused.)
127
+
128
+  // if all the space for this block has been freed then the
129
+  // next space to allocate must be at the base
130
+  if( lbp->freeCnt == lbp->endPtr - lbp->basePtr )
131
+    lbp->nextPtr = lbp->basePtr;
132
+
133
+  return true;
134
+  
135
+}
136
+
137
+
138
+bool  _cmLHeapFreeCb(void* funcArgPtr, void* ptr)
139
+{
140
+  cmLHeap_t*   lhp = (cmLHeap_t*)funcArgPtr;
141
+  return _cmLHeapFree(lhp,ptr);
142
+}
143
+
144
+
145
+cmLHeapH_t cmLHeapCreate( unsigned dfltBlockByteCnt, cmCtx_t* ctx )
146
+{
147
+  cmLHeapH_t h;
148
+  cmLHeap_t* lhp = cmMemAllocZ( cmLHeap_t, 1 );
149
+
150
+  // We are not going to defer freeing each allocation  because it will result in using 
151
+  // a lot of memory.  Commenting out this line however would result in
152
+  // checking all allocations for corruption during cmLHeapDestroy().
153
+  // This may be a good way to hunt down subtle memory corruption problems.
154
+  // See cmLHeapDestroy() and cmLHeapClear() for kDeferFreeMMFl related 
155
+  // items.
156
+  unsigned mmFlags = cmClrFlag(ctx->mmFlags,kDeferFreeMmFl);
157
+
158
+  if( cmMmInitialize(&lhp->mmH,_cmLHeapAllocCb,_cmLHeapFreeCb,lhp,ctx->guardByteCnt,ctx->alignByteCnt,mmFlags,&ctx->rpt) != kOkMmRC )
159
+    return cmLHeapNullHandle;
160
+      
161
+  lhp->dfltBlockByteCnt = dfltBlockByteCnt;
162
+
163
+  _cmLHeapAllocBlock( lhp, lhp->dfltBlockByteCnt );
164
+
165
+  h.h = lhp;
166
+  return h;
167
+}
168
+
169
+void cmLHeapDestroy( cmLHeapH_t* hp )
170
+{
171
+  if( hp==NULL || hp->h == NULL )
172
+    return;
173
+
174
+  cmLHeap_t* lhp = _cmLHeapHandleToPtr(*hp);
175
+
176
+  // check for corruption
177
+  if( cmIsFlag(cmMmInitializeFlags(lhp->mmH),kDeferFreeMmFl))
178
+    cmMmReport(lhp->mmH, kSuppressSummaryMmFl | kIgnoreLeaksMmFl | kIgnoreNormalMmFl );
179
+
180
+  cmMmFinalize(&lhp->mmH); 
181
+  
182
+  cmLhBlock_t* lbp = lhp->first;
183
+  while( lbp != NULL )
184
+  {
185
+    cmLhBlock_t* t = lbp;
186
+
187
+    lbp = lbp->nextBlkPtr;
188
+
189
+    cmMemFree(t);
190
+  }
191
+
192
+  cmMemPtrFree(&hp->h);
193
+  
194
+}
195
+
196
+void*      cmLHeapAllocate(cmLHeapH_t h, void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
197
+{ return cmMmAllocate(_cmLHeapHandleToPtr(h)->mmH,orgDataPtr,eleCnt,eleByteCnt,flags,fileStr,funcStr,fileLine);  }
198
+
199
+cmChar_t*  cmLHeapAllocStr(cmLHeapH_t h, void* orgDataPtr, const cmChar_t*  str, unsigned charCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
200
+{
201
+  if( str == NULL )
202
+    return NULL;
203
+
204
+  unsigned  n  = charCnt + 1;
205
+  cmChar_t* cp = cmLHeapAllocate(h, orgDataPtr, n, sizeof(cmChar_t), flags, fileStr, funcStr, fileLine );
206
+  strncpy(cp,str,n);
207
+  cp[n-1] = 0;
208
+  return cp;
209
+}
210
+
211
+void       cmLHeapFree(    cmLHeapH_t h, void* dataPtr )
212
+{ 
213
+  cmLHeap_t*   lhp = _cmLHeapHandleToPtr(h);
214
+  cmMmFree(lhp->mmH,dataPtr);
215
+}
216
+
217
+void       cmLHeapFreeDebug(    cmLHeapH_t h, void* dataPtr,  const char* fileName, const char* funcName, unsigned fileLine )
218
+{
219
+  cmLHeap_t*   lhp = _cmLHeapHandleToPtr(h);
220
+  cmMmFreeDebug(lhp->mmH,dataPtr,fileName,funcName,fileLine); 
221
+}
222
+
223
+
224
+void       cmLHeapFreePtr( cmLHeapH_t h, void** ptrPtr )
225
+{  
226
+  assert( ptrPtr != NULL );
227
+  cmLHeapFree(h,*ptrPtr);
228
+  *ptrPtr = NULL;
229
+}
230
+
231
+void       cmLHeapFreePtrDebug(    cmLHeapH_t h, void** ptrPtr,  const char* fileName, const char* funcName, unsigned fileLine )
232
+{ 
233
+  assert( ptrPtr != NULL );
234
+  cmLHeapFreeDebug(h,*ptrPtr,fileName,funcName,fileLine);
235
+  *ptrPtr = NULL;
236
+}
237
+
238
+
239
+unsigned   cmLHeapDefaultBlockByteCount( cmLHeapH_t h )
240
+{
241
+  cmLHeap_t* p = _cmLHeapHandleToPtr(h);
242
+  return p->dfltBlockByteCnt;
243
+}
244
+
245
+unsigned   cmLHeapGuardByteCount(  cmLHeapH_t h )
246
+{
247
+  cmLHeap_t* p = _cmLHeapHandleToPtr(h);
248
+  return cmMmGuardByteCount( p->mmH );
249
+}
250
+
251
+unsigned   cmLHeapAlignByteCount(  cmLHeapH_t h )
252
+{
253
+  cmLHeap_t* p = _cmLHeapHandleToPtr(h);
254
+  return cmMmAlignByteCount( p->mmH );
255
+}
256
+
257
+unsigned   cmLHeapInitializeFlags( cmLHeapH_t h )
258
+{
259
+  cmLHeap_t* p = _cmLHeapHandleToPtr(h);
260
+  return cmMmInitializeFlags( p->mmH );
261
+}
262
+
263
+bool cmLHeapIsValid( cmLHeapH_t h )
264
+{ return h.h != NULL; }
265
+
266
+void       cmLHeapClear(   cmLHeapH_t h, bool releaseFl )
267
+{
268
+  cmLHeap_t* p = _cmLHeapHandleToPtr(h);
269
+
270
+  // If we are deferring freeing memory until the heap is destroyed
271
+  // then we cannot clear the block list in this function.
272
+  // If the block list was cleared here then later calls to _cmLHeapFreeCb()
273
+  // would fail because the pointers to the deferred allocations
274
+  // would no longer exist in any of the blocks.
275
+  if(   cmIsFlag(cmMmInitializeFlags(p->mmH),kDeferFreeMmFl))
276
+    return;
277
+  
278
+  cmLhBlock_t* bp = p->first;
279
+  while( bp != NULL )
280
+  {
281
+    bp->nextPtr = bp->basePtr;
282
+    bp->freeCnt = bp->endPtr - bp->basePtr;
283
+    
284
+    cmLhBlock_t* nbp = bp->nextBlkPtr;
285
+
286
+    if( releaseFl )
287
+      cmMemFree(bp);
288
+
289
+    bp = nbp;
290
+  }
291
+
292
+  if( releaseFl )
293
+  {
294
+    p->first = NULL;
295
+    p->last  = NULL;
296
+  }
297
+}
298
+
299
+cmMmRC_t cmLHeapReportErrors( cmLHeapH_t h, unsigned mmFlags )
300
+{
301
+  cmLHeap_t*   lhp = _cmLHeapHandleToPtr(h);
302
+  return cmMmReport(lhp->mmH, mmFlags );
303
+}
304
+
305
+void cmLHeapReport( cmLHeapH_t h )
306
+{
307
+  cmLHeap_t*   lhp = _cmLHeapHandleToPtr(h);
308
+  cmLhBlock_t* lbp = lhp->first;
309
+  unsigned     i;
310
+  for(i=0; lbp != NULL; ++i )
311
+  {
312
+    printf("%u : %li available  %i free\n", i, lbp->endPtr-lbp->nextPtr, lbp->freeCnt );
313
+    lbp = lbp->nextBlkPtr;
314
+  }
315
+  printf("\n");
316
+}
317
+
318
+void cmLHeapTestPrint( void* userPtr, const char* text )
319
+{
320
+  fputs(text,stdin);
321
+}
322
+
323
+void cmLHeapTest()
324
+{
325
+  int      i            = 0;
326
+  int      n            = 5;
327
+  unsigned guardByteCnt = 8;
328
+  unsigned alignByteCnt = 16;
329
+  unsigned mmFlags      = kTrackMmFl; // | kDeferFreeMmFl;
330
+  cmCtx_t  ctx;
331
+  cmCtxSetup(&ctx,"Heap Test",cmLHeapTestPrint,cmLHeapTestPrint,NULL,guardByteCnt,alignByteCnt,mmFlags);
332
+
333
+  cmLHeapH_t h = cmLHeapCreate( 16, &ctx );
334
+  
335
+  void* pArray[ n ];
336
+
337
+  cmLHeapReport(h);
338
+
339
+  for(i=0; i<n; ++i)
340
+  {
341
+    unsigned byteCnt = (i+1) * 2;
342
+    printf("Allocating:%li\n",byteCnt+sizeof(unsigned));
343
+    pArray[i] = cmLHeapAlloc(h,byteCnt);
344
+
345
+    cmLHeapReport(h);
346
+  }
347
+
348
+  for(i=n-1; i>=0; i-=2)
349
+  {
350
+    printf("Freeing:%li\n",((i+1) * 2) + sizeof(unsigned));
351
+    cmLHeapFree(h,pArray[i]);
352
+    cmLHeapReport(h);
353
+  }
354
+
355
+  cmLHeapReportErrors(h,0);
356
+
357
+  cmLHeapDestroy(&h);
358
+}

+ 90
- 0
cmLinkedHeap.h View File

@@ -0,0 +1,90 @@
1
+#ifndef cmLinkedHeap_h
2
+#define cmLinkedHeap_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  //{
9
+  //(
10
+  typedef cmHandle_t cmLHeapH_t;
11
+  
12
+  extern cmLHeapH_t cmLHeapNullHandle;
13
+
14
+  cmLHeapH_t cmLHeapCreate( unsigned dfltBlockByteCnt, cmCtx_t* ctx );
15
+  void       cmLHeapDestroy( cmLHeapH_t* hp );
16
+
17
+  void*     cmLHeapAllocate(     cmLHeapH_t h, void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt,   unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine );
18
+  cmChar_t* cmLHeapAllocStr(     cmLHeapH_t h, void* orgDataPtr, const cmChar_t*  str,  unsigned charCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine );
19
+
20
+  // If ptr==NULL the function returns with no side effect.
21
+  void      cmLHeapFree(         cmLHeapH_t h, void* ptr );
22
+  void      cmLHeapFreeDebug(    cmLHeapH_t h, void* dataPtr,  const char* fileName, const char* funcName, unsigned fileLine );
23
+
24
+  // If *ptr==NULL the function returns with no side effect.
25
+  void      cmLHeapFreePtr(      cmLHeapH_t h, void** ptrPtr );
26
+  void      cmLHeapFreePtrDebug( cmLHeapH_t h, void** dataPtrPtr,  const char* fileName, const char* funcName, unsigned fileLine );
27
+
28
+  bool       cmLHeapIsValid( cmLHeapH_t h );
29
+
30
+  // Return the values set in call to cmLHeapCreate()
31
+  unsigned   cmLHeapDefaultBlockByteCount( cmLHeapH_t h );
32
+  unsigned   cmLHeapGuardByteCount(  cmLHeapH_t h );
33
+  unsigned   cmLHeapAlignByteCount(  cmLHeapH_t h );
34
+  unsigned   cmLHeapInitializeFlags( cmLHeapH_t h );
35
+
36
+  // If releaseFl==false then marks all internal memory blocks as empty but does not actually 
37
+  // release the associated memory, otherwise releases all memory blocks.
38
+  void       cmLHeapClear(   cmLHeapH_t h, bool releaseFl );
39
+
40
+  // mmFlags take the same values as the flags parameter to cmMmReport().
41
+  cmMmRC_t   cmLHeapReportErrors( cmLHeapH_t h, unsigned mmFlags );
42
+  void       cmLHeapReport(  cmLHeapH_t h );
43
+  void       cmLHeapTest();
44
+
45
+#if cmDEBUG_FL == 0
46
+
47
+#define cmLHeapAlloc( h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl,             NULL,NULL,0)
48
+#define cmLHeapAllocZ(h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl | kZeroMmFl, NULL,NULL,0)
49
+
50
+#define cmLhAlloc(h,t,n)      ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl,              NULL,NULL,0))
51
+#define cmLhAllocZ(h,t,n)     ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl  | kZeroMmFl, NULL,NULL,0))
52
+#define cmLhResizeN( h,t,p,n) ((t*)cmLHeapAllocate(h,p,   n,sizeof(t), kAlignMmFl,              NULL,NULL,0))
53
+#define cmLhResizeNZ(h,t,p,n) ((t*)cmLHeapAllocate(h,p,   n,sizeof(t), kAlignMmFl  | kZeroMmFl, NULL,NULL,0))
54
+
55
+#define cmLhAllocStr(   h, str )       cmLHeapAllocStr( h, NULL, str, strlen(str), kAlignMmFl, NULL,NULL,0 )
56
+#define cmLhAllocStrN(  h, str, n )    cmLHeapAllocStr( h, NULL, str, n,           kAlignMmFl, NULL,NULL,0 )
57
+#define cmLhResizeStr(  h, p, str )    cmLHeapAllocStr( h, p,    str, strlen(str), kAlignMmFl, NULL,NULL,0 )
58
+#define cmLhResizeStrN( h, p, str, n ) cmLHeapAllocStr( h, p,    str, n,           kAlignMmFl, NULL,NULL,0 )
59
+
60
+#define cmLhFree( h, p )           cmLHeapFree( h, p )
61
+#define cmLhFreePtr(h, p )         cmLHeapFreePtr( h, p ) 
62
+
63
+#else 
64
+
65
+#define cmLHeapAlloc( h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl,             __FILE__,__FUNCTION__,__LINE__)
66
+#define cmLHeapAllocZ(h, byteCnt ) cmLHeapAllocate(h,NULL,1,byteCnt, kAlignMmFl | kZeroMmFl, __FILE__,__FUNCTION__,__LINE__)
67
+
68
+#define cmLhAlloc(h,t,n)      ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl,              __FILE__,__FUNCTION__,__LINE__))
69
+#define cmLhAllocZ(h,t,n)     ((t*)cmLHeapAllocate(h,NULL,n,sizeof(t), kAlignMmFl  | kZeroMmFl, __FILE__,__FUNCTION__,__LINE__))
70
+#define cmLhResizeN( h,t,p,n) ((t*)cmLHeapAllocate(h,p,   n,sizeof(t), kAlignMmFl,              __FILE__,__FUNCTION__,__LINE__))
71
+#define cmLhResizeNZ(h,t,p,n) ((t*)cmLHeapAllocate(h,p,   n,sizeof(t), kAlignMmFl  | kZeroMmFl, __FILE__,__FUNCTION__,__LINE__))
72
+
73
+#define cmLhAllocStr(  h, str )        cmLHeapAllocStr( h, NULL, str, strlen(str),    kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
74
+#define cmLhAllocStrN( h, str, n )     cmLHeapAllocStr( h, NULL, str, n,              kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
75
+#define cmLhResizeStr( h, p, str )     cmLHeapAllocStr( h, p,    str, strlen(str),    kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
76
+#define cmLhResizeStrN( h, p, str, n ) cmLHeapAllocStr( h, p,    str, n,              kAlignMmFl, __FILE__,__FUNCTION__,__LINE__ )
77
+
78
+#define cmLhFree( h, p )           cmLHeapFreeDebug(    h, p, __FILE__,__FUNCTION__,__LINE__ )
79
+#define cmLhFreePtr(h, p )         cmLHeapFreePtrDebug( h, p, __FILE__,__FUNCTION__,__LINE__ ) 
80
+
81
+#endif
82
+
83
+  //)
84
+  //}
85
+
86
+#ifdef __cplusplus
87
+}
88
+#endif
89
+
90
+#endif

+ 33
- 0
cmMain.c View File

@@ -0,0 +1,33 @@
1
+#include "cmGlobal.h"
2
+#include "cmRpt.h"
3
+#include "cmMem.h"
4
+#include "cmMallocDebug.h"
5
+#include "cmLinkedHeap.h"
6
+
7
+void print(void* cmRptUserPtr, const char* text)
8
+{ printf(text); }
9
+
10
+
11
+int main(int argc, char* argv[] )
12
+{
13
+
14
+  // initialize the heap check library
15
+  bool       memDebugFl          = true;
16
+  unsigned   memPadByteCnt       = memDebugFl ? 8 : 0;
17
+  unsigned   memAlignByteCnt     = 16;
18
+  unsigned   memFlags            = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
19
+
20
+  cmRpt_t    rpt;
21
+  cmRptSetup(&rpt,print,print,NULL);
22
+
23
+  //cmMdTest(&rpt);
24
+  //return 0;
25
+
26
+  cmMdInitialize( memPadByteCnt, memAlignByteCnt, memFlags, &rpt );
27
+  
28
+  cmLHeapTest();
29
+
30
+  cmMdReport();
31
+  cmMdFinalize();
32
+  return 0;
33
+}

+ 136
- 0
cmMallocDebug.c View File

@@ -0,0 +1,136 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmMem.h"
5
+#include "cmMallocDebug.h"
6
+
7
+cmMmH_t _cmMdH = cmSTATIC_NULL_HANDLE;
8
+
9
+void* _cmMdAlloc(void* funcArgPtr, unsigned byteCnt)
10
+{ return malloc(byteCnt); }
11
+
12
+bool  _cmMdFree(void* funcArgPtr, void* ptr)
13
+{ free(ptr); return true; }
14
+
15
+
16
+cmMmRC_t cmMdInitialize( unsigned guardByteCnt, unsigned alignByteCnt, unsigned flags, cmRpt_t* rptPtr )
17
+{ return cmMmInitialize(&_cmMdH,_cmMdAlloc,_cmMdFree,NULL,guardByteCnt,alignByteCnt,flags,rptPtr); }
18
+
19
+cmMmRC_t cmMdFinalize()
20
+{ return cmMmFinalize(&_cmMdH); }
21
+
22
+bool cmMdIsValid()
23
+{ return _cmMdH.h != NULL; }
24
+
25
+unsigned  cmMdGuardByteCount() { return cmMmGuardByteCount( _cmMdH); }
26
+unsigned  cmMdAlignByteCount() { return cmMmAlignByteCount( _cmMdH); }
27
+unsigned  cmMdInitializeFlags(){ return cmMmInitializeFlags(_cmMdH); }
28
+
29
+
30
+void* cmMdAllocate( void *orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags)
31
+{ return cmMmAllocate(_cmMdH,orgDataPtr,eleCnt,eleByteCnt,flags,NULL,NULL,0); }
32
+
33
+void* cmMdAllocateDebug( void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* func, const char* fn, unsigned line)
34
+{ return cmMmAllocate(_cmMdH,orgDataPtr,eleCnt,eleByteCnt,flags,fn,func,line); }
35
+
36
+void     cmMdFree(     void*  p )
37
+{ cmMmFree(_cmMdH,p); }
38
+
39
+void     cmMdFreeDebug(void*  p, const char* func, const char* fn, unsigned line )
40
+{ cmMmFreeDebug(_cmMdH,p,fn,func,line); }
41
+
42
+void     cmMdFreePtr(     void* p )
43
+{ cmMmFreePtr(_cmMdH,p); }
44
+
45
+void     cmMdFreePtrDebug(void* p, const char* func, const char* fn, unsigned line )
46
+{ cmMmFreePtrDebug(_cmMdH,p,fn,func,line); }
47
+
48
+cmChar_t* cmMdAllocStr(      void* orgStrPtr, const cmChar_t* str, unsigned n, unsigned flags )
49
+{
50
+  if( str==NULL)
51
+    return NULL;
52
+
53
+  //unsigned  n  = strlen(str)+1;
54
+  n += 1;
55
+  cmChar_t* cp = cmMdAllocate(orgStrPtr,n,sizeof(cmChar_t),flags);
56
+  strncpy(cp,str,n);
57
+  return cp;
58
+}
59
+
60
+cmChar_t* cmMdAllocStrDebug( void* orgStrPtr, const cmChar_t* str, unsigned n, unsigned flags, const char* func, const char* fn, unsigned line )
61
+{
62
+  if( str==NULL)
63
+    return NULL;
64
+
65
+  n += 1;  
66
+  cmChar_t* cp = cmMdAllocateDebug((void*)orgStrPtr,n,sizeof(cmChar_t),flags,func,fn,line);
67
+  strncpy(cp,str,n);
68
+  cp[n-1] = 0;
69
+  return cp;
70
+}
71
+
72
+
73
+cmMmRC_t cmMdIsGuardCorrupt( unsigned id )
74
+{ return cmMmIsGuardCorrupt(_cmMdH,id); }
75
+
76
+cmMmRC_t cmMdCheckAllGuards( cmRpt_t* rpt )
77
+{ return cmMmCheckAllGuards(_cmMdH); }
78
+
79
+unsigned cmMdDebugId( const void* dataPtr)
80
+{ return cmMmDebugId(_cmMdH,dataPtr);  }
81
+
82
+cmMmRC_t cmMdReport( unsigned mmFlags )
83
+{ return cmMmReport(_cmMdH,mmFlags); }
84
+
85
+//! [cmMdExample]
86
+void cmMdTest( cmRpt_t* rpt )
87
+{
88
+
89
+  bool       memDebugFl          = true;
90
+  unsigned   memGuardByteCnt       = memDebugFl ? 0 : 8;
91
+  unsigned   memAlignByteCnt     = 16;
92
+  unsigned   memFlags            = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl | kFillFreedMmFl : 0;
93
+
94
+  // initialize the library
95
+  cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, rpt );
96
+  
97
+  // Allocate a block of 16 bytes of aligned and zeroed memory.
98
+  void*     d0p = cmMdAllocateDebug(NULL, 1, 16, kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ );
99
+
100
+  // Allocate a block of 20 bytes of non-aligned, zeroed memory.
101
+  unsigned* d1p = cmMdAllocateDebug(NULL, 1, 20, /*kAlignMmFl |*/ kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ );
102
+  unsigned i;
103
+  
104
+  // Intentionally overwrite the guard bytes by writing 
105
+  // 24 bytes where only 20 where allocated
106
+  for(i=0; i<3; ++i)
107
+    d1p[i] = i;
108
+
109
+  // Print a report showing the state of each memory block.
110
+  // This should show that d1p[] has been corrupted and
111
+  // memory leaks because active blocks exist.
112
+  cmMdReport( 0 );
113
+
114
+  // Expand d1p[] preserving the existing 20 bytes.
115
+  d1p = cmMdAllocateDebug(d1p, 1, 24, kPreserveMmFl | /*kAlignMmFl |*/ kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ );
116
+
117
+  // Print the contents of the expanded d1p[]
118
+  for(i=0; i<3; ++i)
119
+    printf("%i ",d1p[i]);
120
+  printf("\n");
121
+
122
+  // Free d0p[] and d1p[];
123
+  cmMdFreeDebug(d0p, __FUNCTION__, __FILE__, __LINE__);
124
+  cmMdFreeDebug(d1p, __FUNCTION__, __FILE__, __LINE__);
125
+
126
+  // Perform a write after free on d0p[].
127
+  *(unsigned*)d0p = 1;
128
+
129
+  // Print another report showing to test write-after-free detection. 
130
+  cmMdReport( 0 );
131
+
132
+  // Close the library.
133
+  cmMdFinalize();
134
+  
135
+}
136
+//! [cmMdExample]

+ 168
- 0
cmMallocDebug.h View File

@@ -0,0 +1,168 @@
1
+//{ { label:cmMd }
2
+//(
3
+// Implements an extended memory allocation and tracking manager.
4
+//
5
+// cmMallocDebug is a wrapper to cmMem.h for calls to malloc() and free().
6
+// Most of the cmMdXXX() calls are directly associated with same named 
7
+// functions in cmMem.h. In effect this library is an implementation of the
8
+// cmMm object where malloc() and free() are the callback functions 
9
+// provided to cmMmInitialize().
10
+// 
11
+//)
12
+
13
+#ifndef cmMallocDebug_h
14
+#define cmMallocDebug_h
15
+
16
+#ifdef __cplusplus
17
+extern "C" {
18
+#endif
19
+  //(
20
+  // Initialize the malloc debug manager. guardByteCnt, alignByteeCnt, flags, and rpt
21
+  // are used to initialize an internal cmMm object.  See cmMm for their semantics.
22
+  cmMmRC_t  cmMdInitialize( unsigned guardByteCnt, unsigned alignByteCnt, unsigned flags, cmRpt_t* rptPtr );
23
+
24
+  // Finalize the malloc debug manager which was previously iniitalized via 
25
+  // a call to cmMdInitialize(). 
26
+  cmMmRC_t  cmMdFinalize();
27
+
28
+  // Returns true if the malloc debug manager has been initialized.
29
+  bool      cmMdIsValid();
30
+
31
+  unsigned  cmMdGuardByteCount();  //< Guard byte count set in cmMdInitialize().
32
+  unsigned  cmMdAlignByteCount();  //< Byte alignment set in cmMdInitialize().
33
+  unsigned  cmMdInitializeFlags(); //< cmMdInitialize() configuration flags.
34
+
35
+  // Allocate a block of memory in release compile mode. This function is generally called
36
+  // via one of the cmMemXXX() macros.
37
+  void*    cmMdAllocate(      void *orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags);
38
+
39
+  // Allocate a block of memory in debug compile mode. This function is generally called 
40
+  // via one of the cmMmXXX() macros.
41
+  void*    cmMdAllocateDebug( void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* func, const char* fn, unsigned line);
42
+
43
+  // Free a block of memory allocated through cmMdAllocate().
44
+  void     cmMdFree(     void*  p );
45
+
46
+  // Free a block of memory allocated through cmMdAllocateDebug().
47
+  void     cmMdFreeDebug(void*  p, const char* func, const char* fn, unsigned line );
48
+
49
+  // Free a block of memory allocated through cmMdAllocateI() and set the 
50
+  // pointer variable to NULL.  This function combines the act of releasing memory
51
+  // and then setting the memory variable to NULL in order to indicate that the 
52
+  // variable no longer points to a valid data area.
53
+  //
54
+  // The following two function calls cmMdFree/Debug(p) internally and then
55
+  // set *p=NULL. In almost all cases this is what we want to do when freeing
56
+  // allocated memory since it will prevent the pointer from being accidently
57
+  // reused.
58
+  //
59
+  // cmMdFreePtr( void* p ) would ideally be written as 
60
+  // cmMdFreePtr( void** p ) because it is really taking a pointer to a pointer.
61
+  // void** however produces compiler warning because while any pointer will be
62
+  // automatically cast to void* the same is not true of void** (because void**
63
+  // is naturally more restrictive - asking for a pointer to the specific type void).
64
+  void     cmMdFreePtr(     void* p );  
65
+  void     cmMdFreePtrDebug(void* p, const char* func, const char* fn, unsigned line );
66
+
67
+  // void  cmMdFreePtr( void** p )
68
+  // void  cmMdFreePtrDebug( void** p, ... )
69
+
70
+  // Allocate a block of memory and then copy a string into it. This function is generally 
71
+  // called by the release compile mode version of the cmMemXXXStr() macros.
72
+  cmChar_t* cmMdAllocStr(      void* orgStrPtr, const cmChar_t* str, unsigned strCharCnt, unsigned flags );
73
+
74
+  // Allocate a block of memory and then copy a string into it. This function is generally
75
+  // called by the debug compile mode version of the cmMemXXXStr() macros.
76
+  cmChar_t* cmMdAllocStrDebug( void* orgStrPtr, const cmChar_t* str, unsigned strCharCnt, unsigned flags, const char* func, const char* fn, unsigned line );
77
+
78
+  // Check if the guard bytes associated with a specified memory block are corrupt.
79
+  // This call is implemented as a direct call the cmMmIsGuardCorrupt().
80
+  cmMmRC_t cmMdIsGuardCorrupt( unsigned id );
81
+
82
+  // Check if any of the currently allocated blocks contain corrupt guard bytes.
83
+  // This call is implemented as a direct call the cmMmCheckAllGuards().
84
+  cmMmRC_t cmMdCheckAllGuards();
85
+
86
+  // Return the unique id associated with a address returned from cmMdAllocateXXX().
87
+  unsigned cmMdDebugId( const void* dataPtr);
88
+
89
+  // Report the status of the memory manager and all blocks. 
90
+  // This call is implemented as a direct call to cmMmReport().
91
+  cmMmRC_t cmMdReport( unsigned mmFlags );
92
+
93
+  // An example and test function for the cmMallocDebug manager.
94
+  void     cmMdTest( cmRpt_t* rpt );
95
+
96
+#if cmDEBUG_FL == 0
97
+
98
+  // Memory Allocation and Release Macros:
99
+  // Release compile mode memory macros generally used in place of direct calls 
100
+  // to cmMdAllocate() or cmMdFree().
101
+  //
102
+
103
+#define cmMemAllocate( type, p, eleCnt, fl ) ((type*)cmMdAllocate( p, eleCnt, sizeof(type), fl )) 
104
+#define cmMemMalloc(   byteCnt )                 cmMdAllocate( NULL, byteCnt,           1, kAlignMmFl) 
105
+#define cmMemMallocZ(  byteCnt )                 cmMdAllocate( NULL, byteCnt,           1, kAlignMmFl | kZeroMmFl) 
106
+#define cmMemAlloc(    type, eleCnt )    ((type*)cmMdAllocate( NULL, eleCnt, sizeof(type), kAlignMmFl))
107
+#define cmMemAllocZ(   type, eleCnt )    ((type*)cmMdAllocate( NULL, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl))
108
+#define cmMemAllocStr(  str )                    cmMdAllocStr( NULL, str,    strlen(str),  kAlignMmFl )
109
+#define cmMemAllocStrN( str, charCnt )           cmMdAllocStr( NULL, str,    charCnt,      kAlignMmFl )
110
+#define cmMemResizeStr( p, str )                 cmMdAllocStr( p,    str,    strlen(str),  kAlignMmFl )
111
+#define cmMemResizeStrN(p, str, charCnt )       cmMdAllocStr( p,    str,    charCnt,      kAlignMmFl )
112
+#define cmMemResizeN(   n, p, eleCnt )          (cmMdAllocate( p,    eleCnt, n,            kAlignMmFl))
113
+#define cmMemResizeNZ(  n, p, eleCnt )          (cmMdAllocate( p,    eleCnt, n,            kAlignMmFl | kZeroMmFl ))
114
+#define cmMemResize(   type, p, eleCnt ) ((type*)cmMdAllocate( p,    eleCnt, sizeof(type), kAlignMmFl))
115
+#define cmMemResizeZ(  type, p, eleCnt ) ((type*)cmMdAllocate( p,    eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl))
116
+#define cmMemResizeP(  type, p, eleCnt ) ((type*)cmMdAllocate( p,    eleCnt, sizeof(type), kAlignMmFl | kPreserveMmFl))
117
+#define cmMemResizePZ( type, p, eleCnt ) ((type*)cmMdAllocate( p,    eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl | kPreserveMmFl))
118
+#define cmMemFree(     ptr )                     cmMdFree( ptr )
119
+#define cmMemPtrFree(  ptrPtr )                  cmMdFreePtr(ptrPtr);
120
+
121
+
122
+#define cmIsPadCorrupt( id ) (kOkMmRC)
123
+
124
+
125
+#else 
126
+  
127
+  // Memory Allocation and Release Macros:
128
+  // These are debug compile mode memory allocation macros generally used in place of 
129
+  // direct calls to cmMdAllocateDebug() or cmMdFree().
130
+  //
131
+  //
132
+#define cmMemAllocate( type, p, eleCnt, pre, fl ) ((type*)cmMdAllocateDebug( p, eleCnt, sizeof(type), fl, __FUNCTION__, __FILE__, __LINE__ ))
133
+#define cmMemMalloc(   byteCnt )                 cmMdAllocateDebug( NULL, 1,      byteCnt,      kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ )
134
+#define cmMemMallocZ(  byteCnt )                 cmMdAllocateDebug( NULL, 1,      byteCnt,      kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ )
135
+#define cmMemAlloc(    type, eleCnt )    ((type*)cmMdAllocateDebug( NULL, eleCnt, sizeof(type), kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ ))
136
+#define cmMemAllocZ(   type, eleCnt )    ((type*)cmMdAllocateDebug( NULL, eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
137
+#define cmMemAllocStr( str )                    (cmMdAllocStrDebug( NULL, str,    strlen(str),  kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ ))
138
+#define cmMemAllocStrN(str, charCnt )           (cmMdAllocStrDebug( NULL, str,    charCnt,      kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ ))
139
+#define cmMemResizeStr(p, str )                 (cmMdAllocStrDebug( p,    str,    strlen(str),  kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ ))
140
+#define cmMemResizeStrN(p, str, charCnt )        (cmMdAllocStrDebug( p,    str,    charCnt,      kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ ))
141
+#define cmMemResizeN(  n,    p, eleCnt )        (cmMdAllocateDebug( p,    eleCnt, n,            kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
142
+#define cmMemResizeNZ( n,    p, eleCnt )        (cmMdAllocateDebug( p,    eleCnt, n,                         kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))      
143
+#define cmMemResize(   type, p, eleCnt ) ((type*)cmMdAllocateDebug( p,    eleCnt, sizeof(type), kAlignMmFl,             __FUNCTION__, __FILE__, __LINE__ )) 
144
+#define cmMemResizeZ(  type, p, eleCnt ) ((type*)cmMdAllocateDebug( p,    eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl, __FUNCTION__, __FILE__, __LINE__ ))
145
+#define cmMemResizeP(  type, p, eleCnt ) ((type*)cmMdAllocateDebug( p,    eleCnt, sizeof(type), kAlignMmFl | kPreserveMmFl,             __FUNCTION__, __FILE__, __LINE__ )) 
146
+#define cmMemResizePZ( type, p, eleCnt ) ((type*)cmMdAllocateDebug( p,    eleCnt, sizeof(type), kAlignMmFl | kZeroMmFl | kPreserveMmFl, __FUNCTION__, __FILE__, __LINE__ ))
147
+#define cmMemFree(     ptr )                     cmMdFreeDebug( ptr,                                                                  __FUNCTION__, __FILE__, __LINE__ )
148
+#define cmMemPtrFree(  ptrPtr )                  cmMdFreePtrDebug( (void**)ptrPtr,                                                    __FUNCTION__, __FILE__, __LINE__ )
149
+
150
+#define cmIsPadCorrupt( id )                     cmMdIsPadCorrupt(id)
151
+
152
+  // Reports corrupt blocks and returns false if any corrupt blocks are found
153
+#define cmCheckAllPads( file )                   cmMdCheckAllPads(file)
154
+
155
+#endif
156
+
157
+
158
+  //)
159
+
160
+
161
+#ifdef __cplusplus
162
+}
163
+#endif
164
+
165
+
166
+//}
167
+
168
+#endif

+ 369
- 0
cmMath.c View File

@@ -0,0 +1,369 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmMath.h"
5
+#include <sys/types.h> // u_char
6
+
7
+// TODO: rewrite to avoid copying
8
+// this code comes via csound source ...
9
+double 		cmX80ToDouble( unsigned char rate[10] )
10
+{
11
+  	char sign;
12
+    short exp = 0;
13
+    unsigned long mant1 = 0;
14
+    unsigned long mant0 = 0;
15
+    double val;
16
+    unsigned char* p = (unsigned char*)rate;
17
+
18
+    exp = *p++;
19
+    exp <<= 8;
20
+    exp |= *p++;
21
+    sign = (exp & 0x8000) ? 1 : 0;
22
+    exp &= 0x7FFF;
23
+    
24
+    mant1 = *p++;
25
+    mant1 <<= 8;
26
+    mant1 |= *p++;
27
+    mant1 <<= 8;
28
+    mant1 |= *p++;
29
+    mant1 <<= 8;
30
+    mant1 |= *p++;
31
+
32
+    mant0 = *p++;
33
+    mant0 <<= 8;
34
+    mant0 |= *p++;
35
+    mant0 <<= 8;
36
+    mant0 |= *p++;
37
+    mant0 <<= 8;
38
+    mant0 |= *p++;
39
+
40
+    /* special test for all bits zero meaning zero 
41
+       - else pow(2,-16383) bombs */
42
+    if (mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0)
43
+      return 0.0;
44
+    else {
45
+      val  = ((double)mant0) * pow(2.0,-63.0);
46
+      val += ((double)mant1) * pow(2.0,-31.0);
47
+      val *= pow(2.0,((double) exp) - 16383.0);
48
+      return sign ? -val : val;
49
+    }
50
+}
51
+
52
+// TODO: rewrite to avoid copying
53
+/*
54
+ * Convert double to IEEE 80 bit floating point
55
+ * Should be portable to all C compilers.
56
+ * 19aug91 aldel/dpwe  covered for MSB bug in Ultrix 'cc'
57
+ */
58
+
59
+void cmDoubleToX80(double val, unsigned char rate[10])
60
+{
61
+    char sign = 0;
62
+    short exp = 0;
63
+    unsigned long mant1 = 0;
64
+    unsigned long mant0 = 0;
65
+    unsigned char* p = (unsigned char*)rate;
66
+
67
+    if (val < 0.0)	{  sign = 1;  val = -val; }
68
+	
69
+    if (val != 0.0)	/* val identically zero -> all elements zero */
70
+      {
71
+        exp = (short)(log(val)/log(2.0) + 16383.0);
72
+        val *= pow(2.0, 31.0+16383.0-(double)exp);
73
+        mant1 =((unsigned)val);
74
+        val -= ((double)mant1);
75
+        val *= pow(2.0, 32.0);
76
+        mant0 =((double)val);
77
+      }
78
+    
79
+    *p++ = ((sign<<7)|(exp>>8));
80
+    *p++ = (u_char)(0xFF & exp);
81
+    *p++ = (u_char)(0xFF & (mant1>>24));
82
+    *p++ = (u_char)(0xFF & (mant1>>16));
83
+    *p++ = (u_char)(0xFF & (mant1>> 8));
84
+    *p++ = (u_char)(0xFF & (mant1));
85
+    *p++ = (u_char)(0xFF & (mant0>>24));
86
+    *p++ = (u_char)(0xFF & (mant0>>16));
87
+    *p++ = (u_char)(0xFF & (mant0>> 8));
88
+    *p++ = (u_char)(0xFF & (mant0));
89
+
90
+}
91
+
92
+bool		cmIsPowerOfTwo( unsigned x )
93
+{
94
+  return !( (x < 2) || (x & (x-1)) );
95
+}
96
+
97
+unsigned 	cmNextPowerOfTwo(	unsigned val )
98
+{
99
+  unsigned i;
100
+	unsigned mask 	= 1;
101
+	unsigned msb 	= 0;
102
+	unsigned cnt	= 0;
103
+	
104
+	// if val is a power of two return it
105
+	if( cmIsPowerOfTwo(val) )
106
+		return val;
107
+
108
+	// next pow of zero is 2
109
+	if( val == 0 )
110
+		return 2;
111
+	
112
+	// if the next power of two can't be represented in 32 bits
113
+	if( val > 0x80000000)
114
+  {
115
+    assert(0);
116
+		return 0;
117
+	}
118
+
119
+	// find most sig. bit that is set - the number with only the next msb set is next pow 2 
120
+	for(i=0; i<31; i++,mask<<=1)
121
+		if( mask & val )
122
+		{
123
+			msb = i;
124
+			cnt++;
125
+		}
126
+		
127
+		
128
+	return 1 << (msb + 1);	
129
+}
130
+
131
+unsigned cmNearPowerOfTwo( unsigned i )
132
+{
133
+  unsigned vh = cmNextPowerOfTwo(i);
134
+
135
+  if( vh == 2 )
136
+    return vh;
137
+
138
+  unsigned vl = vh / 2;
139
+
140
+  if( vh - i < i - vl )
141
+    return vh;
142
+  return vl;
143
+}
144
+
145
+bool     cmIsOddU(    unsigned v ) { return v % 2 == 1; }
146
+bool     cmIsEvenU(   unsigned v ) { return !cmIsOddU(v); }
147
+unsigned cmNextOddU(  unsigned v ) { return cmIsOddU(v)  ? v : v+1; }
148
+unsigned cmPrevOddU(  unsigned v ) { return cmIsOddU(v)  ? v : v-1; }
149
+unsigned cmNextEvenU( unsigned v ) { return cmIsEvenU(v) ? v : v+1; }
150
+unsigned cmPrevEvenU( unsigned v ) { return cmIsEvenU(v) ? v : v-1; }
151
+
152
+// modified bessel function of first kind, order 0
153
+// ref: orfandis appendix B io.m
154
+double	cmBessel0( double x )
155
+{
156
+	double eps = pow(10.0,-9.0);
157
+	double n = 1.0;
158
+	double S = 1.0;
159
+	double D = 1.0;
160
+
161
+	while(D > eps*S)
162
+	{
163
+		double T = x /(2.0*n);
164
+		n = n+1;
165
+		D = D * pow(T,2.0);
166
+		S = S + D;
167
+	}
168
+
169
+	return S;
170
+
171
+}
172
+
173
+//=================================================================
174
+// The following elliptic-related function approximations come from
175
+// Parks & Burrus, Digital Filter Design, Appendix program 9, pp. 317-326
176
+// which in turn draws directly on other sources
177
+
178
+// calculate complete elliptic integral (quarter period) K
179
+// given *complimentary* modulus kc
180
+cmReal_t cmEllipK( cmReal_t kc )
181
+{
182
+  cmReal_t a = 1, b = kc, c = 1, tmp;
183
+
184
+  while( c > cmReal_EPSILON )
185
+  {
186
+    c = 0.5*(a-b);
187
+    tmp = 0.5*(a+b);
188
+    b = sqrt(a*b);
189
+    a = tmp;
190
+  }
191
+
192
+  return M_PI/(2*a);
193
+}
194
+
195
+// calculate elliptic modulus k
196
+// given ratio of complete elliptic integrals r = K/K'
197
+// (solves the "degree equation" for fixed N = K*K1'/K'K1)
198
+cmReal_t cmEllipDeg( cmReal_t r )
199
+{
200
+  cmReal_t q,a,b,c,d;
201
+  a = b = c = 1;
202
+  d = q = exp(-M_PI*r);
203
+
204
+  while( c > cmReal_EPSILON )
205
+  {
206
+    a = a + 2*c*d;
207
+    c = c*d*d;
208
+    b = b + c;
209
+    d = d*q;
210
+  }
211
+
212
+  return 4*sqrt(q)*pow(b/a,2);
213
+}
214
+
215
+// calculate arc elliptic tangent u (elliptic integral of the 1st kind)
216
+// given argument x = sc(u,k) and *complimentary* modulus kc
217
+cmReal_t cmEllipArcSc( cmReal_t x, cmReal_t kc )
218
+{
219
+  cmReal_t a = 1, b = kc, y = 1/x, tmp;
220
+  unsigned L = 0;
221
+
222
+  while( true )
223
+  {
224
+    tmp = a*b;
225
+    a += b;
226
+    b = 2*sqrt(tmp);
227
+    y -= tmp/y;
228
+    if( y == 0 )
229
+      y = sqrt(tmp) * 1E-10;
230
+    if( fabs(a-b)/a < cmReal_EPSILON )
231
+      break;
232
+    L *= 2;
233
+    if( y < 0 )
234
+      L++;
235
+  }
236
+
237
+  if( y < 0 )
238
+    L++;
239
+
240
+  return (atan(a/y) + M_PI*L)/a;
241
+}
242
+
243
+// calculate Jacobi elliptic functions sn, cn, and dn
244
+// given argument u and *complimentary* modulus kc
245
+cmRC_t cmEllipJ( cmReal_t u, cmReal_t kc, cmReal_t* sn, cmReal_t* cn, cmReal_t* dn )
246
+{
247
+  assert( sn != NULL || cn != NULL || dn != NULL );
248
+
249
+  if( u == 0 )
250
+  {
251
+    if( sn != NULL ) *sn = 0;
252
+    if( cn != NULL ) *cn = 1;
253
+    if( dn != NULL ) *dn = 1;
254
+    return cmOkRC;
255
+  }
256
+
257
+  int i;
258
+  cmReal_t a,b,c,d,e,tmp,_sn,_cn,_dn;
259
+  cmReal_t aa[16], bb[16];
260
+
261
+  a = 1;
262
+  b = kc;
263
+
264
+  for( i = 0; i < 16; i++ )
265
+  {
266
+    aa[i] = a;
267
+    bb[i] = b;
268
+    tmp = (a+b)/2;
269
+    b = sqrt(a*b);
270
+    a = tmp;
271
+    if( (a-b)/a < cmReal_EPSILON )
272
+      break;
273
+  }
274
+
275
+  c = a/tan(u*a);
276
+  d = 1;
277
+
278
+  for( ; i >= 0; i-- )
279
+  {
280
+    e = c*c/a;
281
+    c = c*d;
282
+    a = aa[i];
283
+    d = (e + bb[i]) / (e+a);
284
+  }
285
+
286
+  _sn = 1/sqrt(1+c*c);
287
+  _cn = _sn*c;
288
+  _dn = d;
289
+
290
+  if( sn != NULL ) *sn = _sn;
291
+  if( cn != NULL ) *cn = _cn;
292
+  if( dn != NULL ) *dn = _dn;
293
+
294
+  return cmOkRC;
295
+}
296
+
297
+//=================================================================
298
+// bilinear transform
299
+// z = (2*sr + s)/(2*sr - s)
300
+cmRC_t cmBlt( unsigned n, cmReal_t sr, cmReal_t* rp, cmReal_t* ip )
301
+{
302
+  unsigned i;
303
+  cmReal_t a = 2*sr,
304
+           tr, ti, td;
305
+
306
+  for( i = 0; i < n; i++ )
307
+  {
308
+    tr = rp[i];
309
+    ti = ip[i];
310
+    td = pow(a-tr, 2) + ti*ti;
311
+    rp[i] = (a*a - tr*tr - ti*ti)/td;
312
+    ip[i] = 2*a*ti/td;
313
+    if( tr < -1E15 )
314
+      rp[i] = 0;
315
+    if( fabs(ti) > 1E15 )
316
+      ip[i] = 0;
317
+  }
318
+
319
+  return cmOkRC;
320
+}
321
+
322
+unsigned cmHzToMidi( double hz )
323
+{
324
+
325
+  float midi = 12.0 * log2(hz/13.75) + 9;
326
+
327
+  if( midi < 0 )
328
+    midi = 0;
329
+  if( midi > 127 )
330
+    midi = 127;
331
+
332
+  return (unsigned)lround(midi);
333
+}
334
+
335
+float    cmMidiToHz( unsigned midi )
336
+{
337
+  double m = midi <= 127 ? midi : 127;
338
+  
339
+  return (float)( 13.75 * pow(2.0,(m - 9.0)/12.0)); 
340
+}
341
+
342
+
343
+//=================================================================
344
+// Floating point byte swapping
345
+unsigned           cmFfSwapFloatToUInt( float v )
346
+{
347
+  assert( sizeof(float) == sizeof(unsigned));
348
+  return cmSwap32(*(unsigned*)&v);
349
+}
350
+
351
+float              cmFfSwapUIntToFloat( unsigned v )
352
+{
353
+  assert( sizeof(float) == sizeof(unsigned));
354
+  v = cmSwap32(v);
355
+  return *((float*)&v);
356
+}
357
+
358
+unsigned long long cmFfSwapDoubleToULLong( double v )
359
+{
360
+  assert( sizeof(double) == sizeof(unsigned long long));
361
+  return cmSwap64(*(unsigned long long*)&v);
362
+}
363
+
364
+double             cmFfSwapULLongToDouble( unsigned long long v )
365
+{
366
+  assert( sizeof(double) == sizeof(unsigned long long));
367
+  v = cmSwap64(v);
368
+  return *((double*)&v);
369
+}

+ 67
- 0
cmMath.h View File

@@ -0,0 +1,67 @@
1
+#ifndef cmMath_h
2
+#define cmMath_h
3
+
4
+double   cmX80ToDouble( unsigned char s[10] );
5
+void     cmDoubleToX80( double v, unsigned char s[10] );
6
+
7
+bool     cmIsPowerOfTwo(   unsigned i );
8
+unsigned cmNextPowerOfTwo( unsigned i );
9
+unsigned cmNearPowerOfTwo( unsigned i );
10
+
11
+bool     cmIsOddU(    unsigned v );
12
+bool     cmIsEvenU(   unsigned v );
13
+unsigned cmNextOddU(  unsigned v );
14
+unsigned cmPrevOddU(  unsigned v );
15
+unsigned cmNextEvenU( unsigned v );
16
+unsigned cmPrevEvenU( unsigned v );
17
+
18
+
19
+// modified bessel function of first kind, order 0
20
+// ref: orfandis appendix B io.m
21
+double   cmBessel0( double x );
22
+
23
+
24
+//=================================================================
25
+// The following elliptic-related function approximations come from
26
+// Parks & Burrus, Digital Filter Design, Appendix program 9, pp. 317-326
27
+// which in turn draws directly on other sources
28
+
29
+// calculate complete elliptic integral (quarter period) K
30
+// given *complimentary* modulus kc
31
+cmReal_t cmEllipK( cmReal_t kc );
32
+
33
+// calculate elliptic modulus k
34
+// given ratio of complete elliptic integrals r = K/K'
35
+// (solves the "degree equation" for fixed N = K*K1'/K'K1)
36
+cmReal_t cmEllipDeg( cmReal_t r );
37
+
38
+// calculate arc elliptic tangent u (elliptic integral of the 1st kind)
39
+// given argument x = sc(u,k) and *complimentary* modulus kc
40
+cmReal_t cmEllipArcSc( cmReal_t x, cmReal_t kc );
41
+
42
+// calculate Jacobi elliptic functions sn, cn, and dn
43
+// given argument u and *complimentary* modulus kc
44
+cmRC_t   cmEllipJ( cmReal_t u, cmReal_t kc, cmReal_t* sn, cmReal_t* cn, cmReal_t* dn );
45
+
46
+
47
+//=================================================================
48
+// bilinear transform
49
+// z = (2*sr + s)/(2*sr - s)
50
+cmRC_t cmBlt( unsigned n, cmReal_t sr, cmReal_t* rp, cmReal_t* ip );
51
+
52
+
53
+//=================================================================
54
+// Pitch conversion
55
+unsigned cmHzToMidi( double hz );
56
+float    cmMidiToHz( unsigned midi );
57
+
58
+//=================================================================
59
+// Floating point byte swapping
60
+unsigned           cmFfSwapFloatToUInt( float v );
61
+float              cmFfSwapUIntToFloat( unsigned v );
62
+unsigned long long cmFfSwapDoubleToULLong( double v );
63
+double             cmFfSwapULLongToDouble( unsigned long long v );
64
+
65
+
66
+
67
+#endif

+ 699
- 0
cmMem.c View File

@@ -0,0 +1,699 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmMem.h"
6
+#include "cmFloatTypes.h"
7
+#include "cmMath.h"
8
+
9
+// Block layout
10
+//
11
+// |a       |b   |c   |d       |e        |f
12
+// |xx...xxx|yyyy|zzzz|gggggggg|ddd...ddd|gggggggg|
13
+//
14
+//
15
+
16
+// xxxx = alignment (prefix) bytes - size is given by yyyy (may contain up to alignByteCnt-1 bytes).
17
+// yyyy = count of bytes preceding yyyy
18
+// zzzz = count of data bytes
19
+// gggg = guard area (size is fixed by guardByteCnt arg. to cmMemInitialize())
20
+// dddd = data area
21
+//
22
+// d = e - guardByteCnt
23
+// c = d - sizeof(unsigned)
24
+// b = c - sizeof(unsigned)
25
+// a = b - yyyy
26
+//
27
+// Notes:
28
+// 1) The smallest allocation is dataByteCnt +  (2*sizeof(unsigned)
29
+// 2) If allocByteCnt > 0 then the smallest allocation is allocByteCnt + (2*sizeof(unsigned)) + dataByteCnt.
30
+//
31
+
32
+#define  _cmMmDataToByteCntPtr(    dp, gbc )      (dp==NULL?NULL:(((char*)(dp))-(gbc)-sizeof(unsigned)))
33
+#define  _cmMmDataToByteCnt(       dp, gbc )      (dp==NULL?0   :(*(unsigned*)_cmMmDataToByteCntPtr(dp,gbc)))
34
+#define  _cmMmDataToPrefixCntPtr(  dp, gbc )      (dp==NULL?NULL:( _cmMmDataToByteCntPtr(dp,gbc) - sizeof(unsigned)))
35
+#define  _cmMmDataToPrefixCnt(     dp, gbc )      (dp==NULL?0   :(*(unsigned*)_cmMmDataToPrefixCntPtr(dp,gbc)))
36
+#define  _cmMmDataToBasePtr(       dp, gbc )      (dp==NULL?NULL:(_cmMmDataToPrefixCntPtr(dp,gbc) - _cmMmDataToPrefixCnt(dp,gbc)))
37
+#define  _cmMmDataToTotalByteCnt(  dp, gbc )      (dp==NULL?0   :(sizeof(unsigned) + (2*gbc) + _cmMmDataToByteCnt(dp,gbc) + (_cmMmDataToPrefixCnt(dp,gbc) + sizeof(unsigned))))
38
+#define  _cmMmDataToPreGuardPtr(   dp, gbc )      (dp==NULL?NULL:(((char*)(dp))-(gbc)))
39
+#define  _cmMmDataToPostGuardPtr(  dp, gbc )      (dp==NULL?NULL:(((char*)(dp))+_cmMmDataToByteCnt(dp,gbc)))
40
+
41
+
42
+enum
43
+{
44
+  kFreedMmFl    = 0x01,
45
+  kDblFreeMmFl  = 0x02
46
+};
47
+
48
+typedef struct cmMmRecd_str
49
+{
50
+  unsigned             uniqueId;     // 
51
+  void*                dataPtr;      // dataPtr may be NULL if the assoc'd alloc request was for 0 bytes.
52
+  unsigned             dataByteCnt;  // data area bytes on original allocation
53
+  unsigned             fileLine;
54
+  const char*          fileNameStr;
55
+  const char*          funcNameStr;
56
+  unsigned             flags;
57
+  struct cmMmRecd_str* linkPtr;
58
+} cmMmRecd_t;
59
+
60
+typedef struct
61
+{
62
+  cmRpt_t*        rpt;
63
+  cmErr_t         err;
64
+  cmMmRecd_t*     listPtr;
65
+  unsigned        nextId;
66
+  unsigned        alignByteCnt;
67
+  unsigned        guardByteCnt;
68
+  cmAllocMmFunc_t allocFunc;
69
+  cmFreeMmFunc_t  freeFunc; 
70
+  void*           funcArgPtr;
71
+  char            uninitChar;
72
+  char            freeChar;
73
+  char            guardChar; 
74
+  unsigned        flags;
75
+} cmMm_t;
76
+
77
+cmMmH_t cmMmNullHandle = { NULL };
78
+
79
+cmMm_t* _cmMmHandleToPtr( cmMmH_t h )
80
+{
81
+  assert(h.h != NULL );
82
+  return (cmMm_t*)h.h;
83
+}
84
+
85
+cmMmRC_t _cmMmCheckGuards( cmMm_t* p, cmMmRecd_t* rp )
86
+{
87
+  // it is possible that dataPtr is NULL if zero bytes was requested at allocation
88
+  if( rp->dataPtr == NULL )
89
+    return kOkMmRC;
90
+
91
+  // check the pre data area guard space
92
+  char* pp = _cmMmDataToPreGuardPtr( rp->dataPtr, p->guardByteCnt );
93
+  char* ep = pp + p->guardByteCnt;
94
+
95
+  for(;pp<ep; ++pp)
96
+    if( *pp != p->guardChar )
97
+      return kGuardCorruptMmRC;
98
+
99
+
100
+  // check the post data area guard space 
101
+  pp = _cmMmDataToPostGuardPtr( rp->dataPtr, p->guardByteCnt );
102
+  ep = pp + p->guardByteCnt;
103
+
104
+  for(;pp<ep; ++pp)
105
+    if( *pp != p->guardChar )
106
+      return kGuardCorruptMmRC;
107
+
108
+  // if this block was freed and the kFillFreedMmFl was set then check for write-after-free
109
+  if( cmIsFlag(rp->flags,kFreedMmFl) && cmIsFlag(p->flags,kFillFreedMmFl) )
110
+  {
111
+    pp = rp->dataPtr;
112
+    ep = pp + _cmMmDataToByteCnt(pp,p->guardByteCnt);
113
+
114
+    for(; pp<ep; ++pp)
115
+      if( *pp != p->freeChar )
116
+        return kWriteAfterFreeMmRC;    
117
+  }
118
+  
119
+  return kOkMmRC;
120
+}
121
+
122
+cmMmRecd_t* _cmMmFindRecd( cmMm_t* p, const void* dataPtr )
123
+{
124
+  cmMmRecd_t* rp = p->listPtr;
125
+
126
+  while( rp != NULL )
127
+  {
128
+    if( rp->dataPtr == dataPtr )
129
+      break;
130
+
131
+    rp = rp->linkPtr;
132
+  }
133
+
134
+  return rp;
135
+}
136
+
137
+cmMmRC_t _cmMmFree( cmMm_t* p, void* dataPtr, cmMmRecd_t* rp )
138
+{
139
+  cmMmRC_t rc = kOkMmRC;
140
+
141
+  if( p->freeFunc(p->funcArgPtr, _cmMmDataToBasePtr(dataPtr,p->guardByteCnt)) == false )
142
+  {
143
+    if( rp == NULL )
144
+      rc = cmErrMsg(&p->err,kFreeFailMmRC,"Memory free failed on data area at %p.",dataPtr);
145
+    else
146
+      rc = cmErrMsg(&p->err,kFreeFailMmRC,"Memory free failed on data area at %p. Allocated with id:%i at %s line:%i %s.",dataPtr, rp->uniqueId,rp->funcNameStr,rp->fileLine,rp->fileNameStr); 
147
+  }
148
+
149
+  return rc;
150
+}
151
+
152
+cmMmRC_t _cmMmFreeP( cmMm_t* p, void* dataPtr )
153
+{
154
+  if( dataPtr == NULL )
155
+    return kOkMmRC;
156
+
157
+  cmMmRecd_t* rp = p->listPtr;
158
+  cmMmRC_t    rc = kOkMmRC;
159
+
160
+  // if we are tracking alloc's and free's
161
+  if( cmIsFlag(p->flags,kTrackMmFl) )
162
+  {
163
+    // locate the tracking recd
164
+    rp = _cmMmFindRecd(p,dataPtr);
165
+
166
+    // if no tracking recd was found
167
+    if( rp == NULL )
168
+      return cmErrMsg(&p->err,kMissingRecdMmRC,"Unable to locate a tracking record associated with released data area pointer:%p.",dataPtr);
169
+
170
+    // if this block was already freed then this is a double free
171
+    if( cmIsFlag(rp->flags,kFreedMmFl) )
172
+      rp->flags = cmSetFlag(rp->flags,kDblFreeMmFl);
173
+    else
174
+      // otherwise fill the freed block with the freeChar (if requested)
175
+      if( cmIsFlag(p->flags,kFillFreedMmFl) )
176
+        memset(dataPtr, p->freeChar, _cmMmDataToByteCnt(dataPtr,p->guardByteCnt));
177
+
178
+    // mark the block as having been freed
179
+    rp->flags = cmSetFlag(rp->flags,kFreedMmFl);
180
+  }
181
+
182
+  // if we are not deferring free to the finalize stage ... then free the memory here
183
+  if( cmIsFlag(p->flags,kDeferFreeMmFl) == false)
184
+    rc = _cmMmFree(p,dataPtr,rp);
185
+  
186
+  return rc;
187
+}
188
+
189
+void* _cmMmAllocate(cmMm_t* p, void* orgDataPtr, unsigned newByteCnt, unsigned flags )
190
+{
191
+  unsigned abc        = cmIsFlag(flags,kAlignMmFl) ? p->alignByteCnt : 0;
192
+  unsigned gbc        = p->guardByteCnt;
193
+  char*    bp         = NULL;
194
+  char*    dp         = NULL;
195
+  char*    idp        = NULL;
196
+  unsigned orgByteCnt = 0;
197
+  unsigned prefixByteCnt = 0;
198
+
199
+  // Determine the total allocation block size including the auxillary data.
200
+  //
201
+  //                      alignment bytes     +  data_byte_cnt   + guard bytes + actual data
202
+  unsigned ttlByteCnt =  abc+sizeof(unsigned) + sizeof(unsigned) + (2*gbc)     + newByteCnt;
203
+
204
+  // if this is a reallocation
205
+  if( orgDataPtr != NULL )
206
+  {
207
+    // asking to reallocate a block with zero bytes free's the original block and returns NULL
208
+    // (this is in keeping with realloc() behaviour)
209
+    if( newByteCnt == 0 )
210
+    {
211
+      _cmMmFreeP(p,orgDataPtr);
212
+      return NULL;
213
+    }
214
+
215
+    // get the size of the currently allocated block
216
+    orgByteCnt = _cmMmDataToByteCnt(orgDataPtr,gbc);
217
+
218
+    // if the requested data area is <= the alloc'd data area
219
+    if( newByteCnt <= orgByteCnt)
220
+    {
221
+      // if preservation was requested simply return with the original data area ptr
222
+      if( cmIsFlag(flags,kPreserveMmFl) )
223
+        return orgDataPtr;
224
+
225
+      // otherwise initialze the data area
226
+      dp = orgDataPtr;
227
+      goto initLabel;
228
+    }
229
+
230
+    // expansion was requested 
231
+
232
+  }  
233
+
234
+  // if an empty data area was requested
235
+  if( newByteCnt == 0 )
236
+    return NULL;
237
+
238
+  //
239
+  // A new data block must be allocated
240
+  //
241
+
242
+  // allocate the memory block via a callback
243
+  if((bp = p->allocFunc(p->funcArgPtr,ttlByteCnt)) == NULL )
244
+  {
245
+    cmErrMsg(&p->err,kAllocFailMmRC,"Attempt to allocate %i bytes failed.",ttlByteCnt);
246
+    goto errLabel;
247
+  }
248
+
249
+  // make the data area offset: data_byte_cnt + guard bytes
250
+  dp = bp + (2*sizeof(unsigned)) + gbc;
251
+
252
+  if( abc )
253
+  {
254
+    unsigned long alignMask = abc - 1;
255
+
256
+    // alignment requires an additional offset
257
+    dp += abc;
258
+
259
+    // align the data area to the specified boundary
260
+    char* ndp = ((char*)((unsigned long)dp & (~alignMask)));
261
+
262
+    prefixByteCnt = abc - (dp - ndp);
263
+
264
+    dp = ndp;
265
+  }
266
+
267
+  // set the prefix byteCnt
268
+  *(unsigned*)_cmMmDataToPrefixCntPtr(dp,gbc) = prefixByteCnt;
269
+
270
+  // set the data area byte cnt
271
+  *(unsigned*)_cmMmDataToByteCntPtr(dp,gbc) = newByteCnt;
272
+
273
+  // uncomment this line to print memory layout information for each block
274
+  //printf("prefix:%i prefix*:%p bytes:%i bytes*:%p base:%p==%p data:%p\n",_cmMmDataToPrefixCnt(dp,gbc),_cmMmDataToPrefixCntPtr(dp,gbc),_cmMmDataToByteCnt(dp,gbc),_cmMmDataToByteCntPtr(dp,gbc),_cmMmDataToBasePtr(dp,gbc),bp,dp);
275
+    
276
+  // set the guard bytes
277
+  if( gbc )
278
+  {
279
+    void* gp = _cmMmDataToPreGuardPtr(  dp,gbc);
280
+    if( gp != NULL )
281
+      memset(gp, p->guardChar, gbc);
282
+
283
+    gp = _cmMmDataToPostGuardPtr( dp,gbc);
284
+    if( gp != NULL )
285
+      memset(gp, p->guardChar, gbc);
286
+  }
287
+    
288
+
289
+ initLabel:
290
+
291
+  //
292
+  // initialize the new data area
293
+  //
294
+  
295
+  idp = dp;
296
+
297
+  // if this is a reallocation with expansion ...
298
+  if( orgDataPtr != NULL && newByteCnt > orgByteCnt )
299
+  {
300
+    // ... and preservation was requested
301
+    if( cmIsFlag(flags,kPreserveMmFl) )
302
+    {
303
+      // copy original data into the new block
304
+      memcpy(idp,orgDataPtr,orgByteCnt);
305
+
306
+      idp        += orgByteCnt;
307
+      newByteCnt -= orgByteCnt;
308
+    }
309
+
310
+    _cmMmFreeP(p,orgDataPtr); // free the original data block
311
+  }
312
+
313
+  // if zeroing was requested
314
+  if( cmIsFlag(flags,kZeroMmFl))
315
+    memset(idp,0,newByteCnt);
316
+  else
317
+    // if uninitialized data should be set to a known character
318
+    if( cmIsFlag(p->flags,kFillUninitMmFl) )
319
+      memset(idp,p->uninitChar,newByteCnt);
320
+
321
+    
322
+  return dp;
323
+
324
+ errLabel:
325
+  return NULL;
326
+}
327
+
328
+
329
+cmMmRC_t cmMmInitialize( 
330
+  cmMmH_t*        hp, 
331
+  cmAllocMmFunc_t allocFunc, 
332
+  cmFreeMmFunc_t  freeFunc, 
333
+  void*           funcArgPtr,
334
+  unsigned        guardByteCnt, 
335
+  unsigned        alignByteCnt, 
336
+  unsigned        flags,
337
+  cmRpt_t*        rpt )
338
+{
339
+  cmMm_t* p;
340
+  cmErr_t err;
341
+  cmErrSetup(&err,rpt,"Memory Manager");
342
+
343
+  // validate alignByteCnt
344
+  if(alignByteCnt>0 && cmIsPowerOfTwo(alignByteCnt)==false)
345
+    return cmErrMsg(&err,kParamErrMmRC,"The 'alignByteCnt' parameter must be a power of two.");
346
+    
347
+  // allocate the main object
348
+  if((p= calloc(1,sizeof(cmMm_t))) == NULL )
349
+    return cmErrMsg(&err,kObjAllocFailMmRC,"Object allocation failed.");
350
+
351
+  // setting kDeferFreeMmFl only makes sense when kTrackMmFl is also set
352
+  if(cmIsFlag(flags,kTrackMmFl)==false && cmIsFlag(flags,kDeferFreeMmFl)==true)
353
+  {
354
+    cmErrMsg(&err,kParamErrMmRC,"The flag 'kDeferFreeMmFl' may only be set if the 'kTrackMmFl' is also set. 'kDeferFreeMmFl' is begin disabled.");
355
+    flags = cmClrFlag(flags,kDeferFreeMmFl);
356
+  }
357
+
358
+  cmErrClone(&p->err,&err);
359
+  p->rpt          = rpt;
360
+  p->alignByteCnt = alignByteCnt;
361
+  p->guardByteCnt = guardByteCnt;
362
+  p->allocFunc    = allocFunc;
363
+  p->freeFunc     = freeFunc;
364
+  p->funcArgPtr   = funcArgPtr;
365
+  p->uninitChar   = 0x55;  // non-zeroed data areas are automatically filled w/ this value on allocation
366
+  p->freeChar     = 0x33;  // data areas are overwritten with this value on free
367
+  p->guardChar    = 0xAA;  // guard areas are set with this value
368
+
369
+  p->flags        = flags;
370
+
371
+  hp->h = p;
372
+
373
+  return kOkMmRC;
374
+}
375
+
376
+cmMmRC_t cmMmFinalize( cmMmH_t* hp )
377
+{
378
+  cmMm_t* p;
379
+  cmMmRC_t rc = kOkMmRC;
380
+
381
+  if( hp == NULL || cmMmIsValid(*hp)==false )
382
+    return kOkMmRC;
383
+
384
+  p = _cmMmHandleToPtr(*hp);
385
+
386
+  cmMmRecd_t* rp = p->listPtr;
387
+  while( rp != NULL )
388
+  {
389
+    cmMmRecd_t* tp = rp->linkPtr;
390
+    cmMmRC_t    rrc;
391
+
392
+    if( cmIsFlag(p->flags,kDeferFreeMmFl) )
393
+      if((rrc = _cmMmFree(p,rp->dataPtr,rp)) != kOkMmRC )
394
+        rc = rrc;
395
+
396
+    free(rp);
397
+    rp = tp;
398
+  }
399
+
400
+  free(p);
401
+  hp->h = NULL;
402
+
403
+  return rc;
404
+}
405
+
406
+unsigned cmMmGuardByteCount(  cmMmH_t h )
407
+{
408
+  cmMm_t* p = _cmMmHandleToPtr(h);
409
+  return p->guardByteCnt;
410
+}
411
+
412
+unsigned cmMmAlignByteCount(  cmMmH_t h )
413
+{
414
+  cmMm_t* p = _cmMmHandleToPtr(h);
415
+  return p->alignByteCnt;
416
+}
417
+
418
+unsigned cmMmInitializeFlags( cmMmH_t h )
419
+{
420
+  cmMm_t* p = _cmMmHandleToPtr(h);
421
+  return p->flags;
422
+}
423
+
424
+bool     cmMmIsValid( cmMmH_t h )
425
+{ return h.h != NULL; }
426
+
427
+void* cmMmAllocate(     
428
+  cmMmH_t     h, 
429
+  void*       orgDataPtr, 
430
+  unsigned    newEleCnt,
431
+  unsigned    newEleByteCnt, 
432
+  enum cmMmAllocFlags_t    flags, 
433
+  const char* fileName, 
434
+  const char* funcName, 
435
+  unsigned    fileLine )
436
+{
437
+   cmMm_t*  p          = _cmMmHandleToPtr(h); 
438
+   unsigned newByteCnt = newEleCnt * newEleByteCnt;
439
+   void*    ndp        = _cmMmAllocate(p,orgDataPtr,newByteCnt,flags);
440
+   /*
441
+   if( p->nextId == 1575 )
442
+   {
443
+     cmErrMsg(&p->err,kOkMmRC,"Breakpoint for memory allocation id:%i.",p->nextId);
444
+   }
445
+   */
446
+   // if we are tracking changes
447
+   if( cmIsFlag(p->flags,kTrackMmFl) )
448
+   {
449
+     //
450
+     // NOTE: it is possible that ndp is NULL if newByteCnt == 0.
451
+     // 
452
+
453
+     cmMmRecd_t* rp = NULL;
454
+
455
+     // if a new memory block was allocated
456
+     if( orgDataPtr == NULL || orgDataPtr != ndp )
457
+     {
458
+       // allocate a new tracking recd
459
+       if( (rp =  calloc(1,sizeof(cmMmRecd_t))) == NULL )
460
+       {
461
+         cmErrMsg(&p->err,kTrkAllocFailMmRC,"Unable to allocate a tracking record for %s line:%i %s.",funcName,fileLine,fileName);
462
+         return ndp;
463
+       }
464
+
465
+       // initialize the new tracking recd
466
+       rp->uniqueId     = p->nextId;
467
+       rp->dataPtr      = ndp;
468
+       rp->dataByteCnt  = newByteCnt;
469
+       rp->fileLine     = fileLine;
470
+       rp->fileNameStr  = fileName;
471
+       rp->funcNameStr  = funcName;
472
+       rp->flags        = 0;
473
+       rp->linkPtr      = p->listPtr;
474
+
475
+       //printf("%i %i %s %i %s\n",rp->uniqueId,newByteCnt,funcName,fileLine,fileName);
476
+
477
+       p->listPtr = rp;
478
+
479
+       assert( _cmMmCheckGuards(p,rp) == kOkMmRC );
480
+
481
+       ++p->nextId;
482
+     }
483
+     else // a reallocation occurred.
484
+       if( orgDataPtr == ndp )
485
+       {
486
+         if((rp = _cmMmFindRecd(p,orgDataPtr)) == NULL )
487
+           cmErrMsg(&p->err,kMissingRecdMmRC,"Unable to locate a tracking record associated with reallocation data area pointer:%p.",orgDataPtr);           
488
+       }
489
+   }
490
+
491
+   return ndp;
492
+}
493
+
494
+cmMmRC_t cmMmFree( cmMmH_t h, void* dataPtr )
495
+{
496
+  cmMm_t* p = _cmMmHandleToPtr(h);
497
+
498
+  return _cmMmFreeP(p,dataPtr);
499
+}
500
+
501
+cmMmRC_t cmMmFreeDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
502
+{
503
+  cmMmRC_t rc;
504
+  if((rc = cmMmFree(h,dataPtr)) != kOkMmRC )
505
+    cmErrMsg(&_cmMmHandleToPtr(h)->err,rc,"Memory free failed at %s() line:%i %s",funcName,fileLine,fileName);
506
+  return rc;
507
+}
508
+
509
+cmMmRC_t cmMmFreePtr( cmMmH_t h, void** dataPtrPtr )
510
+{
511
+  assert(dataPtrPtr != NULL );
512
+
513
+  cmMmRC_t rc = cmMmFree(h,*dataPtrPtr);
514
+
515
+  *dataPtrPtr = NULL;
516
+
517
+  return rc;
518
+}
519
+
520
+cmMmRC_t cmMmFreePtrDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
521
+{
522
+  cmMmRC_t rc;
523
+  if((rc = cmMmFreePtr(h,dataPtr)) != kOkMmRC )
524
+    cmErrMsg(&_cmMmHandleToPtr(h)->err,rc,"Memory free failed at %s() line:%i %s",funcName,fileLine,fileName);
525
+  return rc;
526
+}
527
+
528
+unsigned cmMmByteCount( cmMmH_t h, const void* dataPtr )
529
+{ 
530
+  cmMm_t* p = _cmMmHandleToPtr(h);
531
+  return _cmMmDataToByteCnt(dataPtr,p->guardByteCnt);
532
+}
533
+
534
+unsigned cmMmDebugId( cmMmH_t h, const void* dataPtr)
535
+{
536
+  cmMm_t*     p  = _cmMmHandleToPtr(h);
537
+  const cmMmRecd_t* rp = NULL;
538
+
539
+  // if we are tracking alloc's and free's
540
+  if( cmIsFlag(p->flags,kTrackMmFl) )
541
+  {
542
+    // locate the tracking recd
543
+    rp = _cmMmFindRecd(p,dataPtr);
544
+  }
545
+
546
+  return rp==NULL ? cmInvalidId : rp->uniqueId;
547
+}
548
+
549
+cmMmRC_t _cmMmError( cmMm_t* p, cmMmRC_t rc, const cmMmRecd_t* rp, const char* msg )
550
+{
551
+ return cmErrMsg(&p->err,rc,"%s detected on block id:%i from %s() line:%i %s.",msg,rp->uniqueId,rp->funcNameStr,rp->fileLine,rp->fileNameStr);
552
+}
553
+
554
+cmMmRC_t _cmMmRecdPrint( cmMm_t* p, cmMmRecd_t* rp, cmMmRC_t rc )
555
+{
556
+  void*    dp  = rp->dataPtr;
557
+  unsigned gbc = p->guardByteCnt;
558
+  char*    lbl = NULL;
559
+
560
+  switch( rc )
561
+  {
562
+    case kOkMmRC:               lbl = "Ok              "; break;
563
+    case kLeakDetectedMmRC:     lbl = "Memory Leak     "; break;
564
+    case kWriteAfterFreeMmRC:   lbl = "Write After Free"; break;
565
+    case kDblFreeDetectedMmRC:  lbl = "Double Free     "; break;
566
+    case kGuardCorruptMmRC:     lbl = "Guard Corrupt   "; break;
567
+    default:
568
+      lbl = "Unknown Status  ";
569
+  }
570
+
571
+  cmRptPrintf(p->err.rpt,"%s id:%5i data:%p : data:%5i prefix:%5i total:%5i base:%p : %5i %s %s\n",
572
+    lbl,rp->uniqueId,rp->dataPtr,
573
+    _cmMmDataToByteCnt(dp,gbc),
574
+    _cmMmDataToPrefixCnt(dp,gbc), 
575
+    _cmMmDataToTotalByteCnt(dp,gbc), 
576
+    _cmMmDataToBasePtr(dp,gbc),  
577
+    rp->fileLine,rp->funcNameStr,rp->fileNameStr );
578
+
579
+  return rc;
580
+}
581
+
582
+cmMmRC_t cmMmReport( cmMmH_t h, unsigned flags )
583
+{
584
+  cmMm_t*     p             = _cmMmHandleToPtr(h);
585
+  unsigned    allocByteCnt  = 0;
586
+  unsigned    ttlByteCnt    = 0;
587
+  unsigned    dataFrByteCnt = 0;
588
+  unsigned    ttlFrByteCnt  = 0;
589
+  unsigned    dataLkByteCnt = 0;
590
+  unsigned    ttlLkByteCnt  = 0;
591
+  unsigned    allocBlkCnt   = 0;
592
+  unsigned    freeBlkCnt    = 0;
593
+  unsigned    leakBlkCnt    = 0;
594
+  cmMmRecd_t* rp            = p->listPtr;
595
+  cmMmRC_t    ret_rc        = kOkMmRC;
596
+
597
+  for(; rp != NULL; rp = rp->linkPtr,++allocBlkCnt )
598
+  {
599
+     cmMmRC_t rc               = kOkMmRC;
600
+     unsigned blkDataByteCnt   = _cmMmDataToByteCnt(rp->dataPtr,p->guardByteCnt);
601
+     unsigned blkTtlByteCnt    = _cmMmDataToTotalByteCnt(rp->dataPtr,p->guardByteCnt);
602
+
603
+    allocByteCnt += blkDataByteCnt;
604
+    ttlByteCnt   += blkTtlByteCnt;
605
+    
606
+    // if this block was freed or never alloc'd
607
+    if( cmIsFlag(rp->flags,kFreedMmFl) || rp->dataPtr == NULL )
608
+    {
609
+      ++freeBlkCnt;
610
+      dataFrByteCnt += blkDataByteCnt;
611
+      ttlFrByteCnt  += blkTtlByteCnt;
612
+    }
613
+    else // if this block leaked
614
+    {
615
+      ++leakBlkCnt;
616
+      dataLkByteCnt += blkDataByteCnt;
617
+      ttlLkByteCnt  += blkTtlByteCnt;
618
+      if( cmIsFlag(flags,kIgnoreLeaksMmFl) == false )
619
+        rc   = _cmMmRecdPrint(p,rp,kLeakDetectedMmRC);
620
+    }
621
+    
622
+    // if this block was double freed
623
+    if( cmIsFlag(rp->flags,kDblFreeMmFl) )
624
+      rc = _cmMmRecdPrint(p,rp,kDblFreeDetectedMmRC);
625
+
626
+    // check for guard corruption and write-after-free 
627
+    cmMmRC_t t_rc = _cmMmCheckGuards(p,rp);
628
+
629
+    switch( t_rc )
630
+    {
631
+      case kOkMmRC:
632
+        break;
633
+
634
+      case kGuardCorruptMmRC:
635
+        rc = _cmMmRecdPrint(p,rp,t_rc);
636
+        break;
637
+
638
+      case kWriteAfterFreeMmRC:
639
+        rc = _cmMmRecdPrint(p,rp,t_rc);
640
+        break;
641
+
642
+      default:
643
+        assert(0);
644
+        break;
645
+    }
646
+
647
+    if( rc != kOkMmRC )
648
+      ret_rc = rc;
649
+    else
650
+    {
651
+      if( cmIsFlag(flags,kIgnoreNormalMmFl)==false )
652
+        _cmMmRecdPrint(p,rp,rc);
653
+    }
654
+
655
+  }
656
+  
657
+  if( p->listPtr != NULL && cmIsFlag(flags,kSuppressSummaryMmFl)==false )
658
+  {
659
+    cmRptPrintf(p->err.rpt,"Blocks Allocated:%i Freed:%i Leaked:%i\n",allocBlkCnt,freeBlkCnt,leakBlkCnt);
660
+    cmRptPrintf(p->err.rpt,"Bytes  Allocated: data=%i total=%i Freed: data=%i total=%i Leaked: data=%i total=%i\n",allocByteCnt,ttlByteCnt,dataFrByteCnt,ttlFrByteCnt,dataLkByteCnt,ttlLkByteCnt);
661
+  }
662
+
663
+  return ret_rc;
664
+}
665
+
666
+cmMmRC_t cmMmIsGuardCorrupt( cmMmH_t h, unsigned id )
667
+{
668
+  cmMm_t*     p  = _cmMmHandleToPtr(h);
669
+  cmMmRecd_t* rp = p->listPtr;
670
+
671
+  while(rp != NULL )
672
+  {
673
+    if( rp->uniqueId == id )
674
+      break;
675
+    rp = rp->linkPtr;
676
+  }
677
+  
678
+  if( rp == NULL )
679
+    return cmErrMsg(&p->err,kMissingRecdMmRC,"Unable to locate the tracking record associated with id %i.",id);
680
+
681
+  return _cmMmCheckGuards(p,rp);
682
+}
683
+
684
+cmMmRC_t cmMmCheckAllGuards( cmMmH_t h )
685
+{
686
+  cmMm_t*     p  = _cmMmHandleToPtr(h);
687
+  cmMmRecd_t* rp = p->listPtr;
688
+  cmMmRC_t    rc = kOkMmRC;
689
+
690
+  while(rp != NULL )
691
+  {
692
+    if((rc = _cmMmCheckGuards(p,rp)) != kOkMmRC )
693
+      rc = cmErrMsg(&p->err,rc,"A corrupt guard or 'write after free' was detected in the data area allocated with id:%i at %s (line:%i) %s.",rp->uniqueId,rp->funcNameStr,rp->fileLine,rp->fileNameStr);
694
+
695
+    rp = rp->linkPtr;
696
+  }
697
+
698
+  return rc;
699
+}

+ 231
- 0
cmMem.h View File

@@ -0,0 +1,231 @@
1
+//{
2
+//(
3
+// The cmMem class implements a memory allocation manager interface.
4
+//
5
+//
6
+// Using cmMem allows memory leaks and some instances of memory corruption 
7
+// to be be detected. It can also perform memory block alignment.
8
+// 
9
+// The cmMm class acts as an interface for implementing functions designed to replace
10
+// malloc() and free().  cmMm does not actually allocate memory itself but rather
11
+// tracks and conditions block of memory provided by other sources.  In this sense 
12
+// it acts as a backend for a memory allocation manager.  
13
+// cmMallocDebug.h gives an example of using cmMm to interface to malloc() and free().
14
+// cmLinkedHeap.h gives an example of using cmMm to link to an alternate heap manager.
15
+// See cmMdTest() and cmLHeapTest() for usage examples of cmMm.
16
+//
17
+// cmMm works as follows:
18
+//
19
+// 1. A client memory manager creates and configures a cmMm object via cmMmInitialize().
20
+// As part of the configuration the client gives callback functions which implement
21
+// actual memory allocation and release.  In practice this means the callback probably
22
+// call malloc() or free(). 
23
+// 2. At some point later when the client needs to allocate a block of memory it calls
24
+// cmMmAllocate() with the size of the requested block.  cmMm translates this request
25
+// into a call to the client provided memory allocation callback to get a block of raw
26
+// memory which is slightly larger than the request block.
27
+// 3. Given the raw memory block cmMm conditions it in the following ways and returns
28
+// it to the client.
29
+// * The base of the blocks data area is shifted such that it is has an arbitrary 
30
+// address aligned according to the value set by the alignByteCnt parameter to cmMmInitialize().
31
+// Address aligment is sometimes required by routines which make use of the the SIMD
32
+// unit on some CPUs.  
33
+// * 'Guard' bytes are prepended and appended to the blocks data area. 
34
+// These bytes are set to the known fixed value (0xaa).  At some point later cmMm can 
35
+// then test for accidental writes just before or just after the legal data area by 
36
+// checking the value of these guard bytes. 
37
+// * The number of bytes allocated is written just prior to the leading guard bytes.
38
+// This allows the memory manager to track the
39
+// size of the memory and thereby makes reallocations() to smaller or equal data areas 
40
+// very fast.  This also allows the size of the data area to be known just by having a 
41
+// pointer to the data area (see cmMmByteCount()). This basic information is not availabe
42
+// via malloc().
43
+// * A record is added to an internal database to track the allocation code location 
44
+// (file name, file line, function name) and the allocation status (active or released).
45
+// * The client may request that a new block of memory be automatically filled with zeros.
46
+// If automatic zeroing is not requested then the block is filled with 0x55 to indicate that
47
+// it is not initialized.  This can be useful when attempting to recognize uninitialized
48
+// memory during debugging.
49
+//
50
+// When a client requests that a block of memory is released cmMm does the following:
51
+// 
52
+// 1. If deferred release is enabled (kDeferFreeFl) then the block is filled with 0x33
53
+// but the callback to freeFunc() is not actually made. This allows cmMm to track attempted
54
+// writes to freed memory areas.  When deferred release is enabled the freeFunc() is not called
55
+// on any blocks until cmMmFinalize().  If the program continually allocates memory over the 
56
+// life of the program this may mean that the program will eventually exhaust physical memory.
57
+// 2. If tracking is enabled (kTrackMmFl) then the block pointer is looked up in the internal database.
58
+// If the pointer is not found then a kMissingRecdRC is returned indicating an attempt to release
59
+// a non-allocated block.  
60
+// 3. If tracking is enabled (kTrackMmFl) then the block is marked as released in the 
61
+// internal tracking database. At the end of the program all blocks should be marked for release
62
+// otherwise they are considered leaks.  
63
+//
64
+//
65
+// At any time during the life of the cmMm object the client can request a report of the 
66
+// allocated blocks cmMmReport(). This report examines each allocated block for corrupt guard bytes,
67
+// double frees (attempts to release an allocated block that was already released), and
68
+// leaked blocks (active blocks).
69
+//
70
+//)
71
+
72
+
73
+#ifndef cmMem_h
74
+#define cmMem_h
75
+
76
+#ifdef __cplusplus
77
+extern "C" {
78
+#endif
79
+  
80
+  //(
81
+
82
+  typedef cmHandle_t cmMmH_t;  //< cmMm handle type. 
83
+  typedef cmRC_t     cmMmRC_t; //< cmMm result code types.
84
+
85
+  // cmMm result codes 
86
+  enum
87
+  {
88
+    kOkMmRC = cmOkRC,
89
+    kObjAllocFailMmRC,
90
+    kTrkAllocFailMmRC,
91
+    kAllocFailMmRC,
92
+    kFreeFailMmRC,
93
+    kMissingRecdMmRC,
94
+    kGuardCorruptMmRC,
95
+    kWriteAfterFreeMmRC,
96
+    kLeakDetectedMmRC,
97
+    kDblFreeDetectedMmRC,
98
+    kParamErrMmRC
99
+  };
100
+
101
+  // All cmMmH_t variables should be initialized with this value prior to calling cmMmInitialize().
102
+  extern cmMmH_t cmMmNullHandle;
103
+
104
+  // Function signature for data allocation routine client provided to cmMmInitialize().
105
+  // Return NULL if byteCnt == 0.
106
+  typedef void* (*cmAllocMmFunc_t)(void* funcArgPtr, unsigned byteCnt);
107
+
108
+  // Function signature for data release routine client provided to cmMmInitialize().
109
+  // Return true on success and false on failure.  Return true if ptr==NULL.
110
+  typedef bool  (*cmFreeMmFunc_t)( void* funcArgPtr, void* ptr);
111
+
112
+  // Flags for use with cmMmInitialize()
113
+  enum
114
+  {
115
+    kTrackMmFl      = 0x01,   //< Track alloc's and free's for use by cmMmReport().
116
+    kDeferFreeMmFl  = 0x02,   //< Defer memory release until cmMmFinalize() (ignored unless kTrackMmFl is set.)  Allows checks for 'write after release'.
117
+    kFillUninitMmFl = 0x04,   //< Fill uninitialized (non-zeroed) memory with a 0x55 upon allocation
118
+    kFillFreedMmFl  = 0x08    //< Fill freed memory with 0x33. This allow checks for wite-after-free.
119
+  };
120
+
121
+  // Create a new cmMm object.
122
+  // If *hp was not initalized by an earlier call to cmMmInitialize() then it should 
123
+  // be set to cmMmNullHandle prior to calling this function. If *hp is a valid handle
124
+  // then it is automatically finalized by an internal call to cmMmFinalize() prior to
125
+  // being re-iniitalized.
126
+  cmMmRC_t cmMmInitialize( 
127
+    cmMmH_t*        hp,           //< Pointer to a client provided cmMmH_t handle to recieve the handle of the new object.
128
+    cmAllocMmFunc_t allocFunc,    //< The memory allocation function equivalent to malloc().
129
+    cmFreeMmFunc_t  freeFunc,     //< The memory release function equivalent to free().
130
+    void*           funcArgPtr,   //< An application supplied data value sent with call backs to allocFunc() and freeFunc().
131
+    unsigned        guardByteCnt, //< Count of guardBytes to precede and follow each allocated block.
132
+    unsigned        alignByteCnt, //< Address alignment to provide for each allocated block.
133
+    unsigned        flags,        //< Configuration flags (See cmXXXMmFl).
134
+    cmRpt_t*        rptPtr        //< Pointer to an error reporting object.
135
+  );
136
+
137
+  // Release a cmMm object created by an earlier call to cmMmInitialize(). Upon successful completion *hp is set to cmMmNullHandle.
138
+  cmMmRC_t cmMmFinalize( cmMmH_t* hp );
139
+
140
+  unsigned cmMmGuardByteCount(  cmMmH_t h ); //< Return the count of guard bytes this cmMm object is applying.
141
+  unsigned cmMmAlignByteCount(  cmMmH_t h ); //< Return the byte alignment this cmMm object is applying.
142
+  unsigned cmMmInitializeFlags( cmMmH_t h ); //< Return the configuration flags this cmMm object was initialized with.
143
+
144
+  // Return true if 'h' is a valid handle for an existing cmMm object.
145
+  bool     cmMmIsValid(  cmMmH_t h );
146
+
147
+  // flags for use with cmMmAllocate()
148
+  enum cmMmAllocFlags_t
149
+  {
150
+    kZeroMmFl     = 0x01, //< Initialize new memory area to zero.
151
+    kAlignMmFl    = 0x02, //< Align the returned memory according to the alignByteCnt set in cmMmInitialize().
152
+    kPreserveMmFl = 0x04  //< Preserve existing memory contents during reallocation (orgDataPtr!=NULL). 
153
+  }; 
154
+
155
+  // Allocate a block of memory.
156
+  // Calling this function results in a call to the function named in allocFunc() in cmMmInitialize(). 
157
+  void* cmMmAllocate(     
158
+    cmMmH_t     h,                  //< Handle for this cmMm object returned from an earlier successful call to cmMmInitialize().
159
+    void*       orgDataPtr,         //< If this is a re-allocation then this pointer should point to the original allocation otherwise it should be NULL.
160
+    unsigned    newEleCnt,          //< Count of elmements in this allocation.
161
+    unsigned    newEleByteCnt,      //< Bytes per element in this allocation. The total memory request is newEleCnt*newEleByteCnt.
162
+    enum cmMmAllocFlags_t    flags, //< See cmMmAllocFlags_t.
163
+    const char* fileName,           //< Name of the C file from which the allocation request is being made. 
164
+    const char* funcName,           //< Name of the C function from which the allocation request is being made.
165
+    unsigned    fileLine            //< Line in the C file on which the allocation request is being made.
166
+                          );
167
+
168
+  // Free memory pointed to by dataPtr.  
169
+  // If dataPtr==NULL then the functon does nothing and returns.
170
+  // Calling this function results in a call to the function named in freeFunc() in cmMmInitialize().
171
+  // This is the release mode memory free routine. See cmMmFreeDebug() for the debug mode memory release routine. 
172
+  //  See \ref debug_mode for more about debug vs. release mode.
173
+  cmMmRC_t cmMmFree( cmMmH_t h, void* dataPtr );
174
+
175
+  // Debug mode version of cmMmFree(). See cmMmFree() for the release mode memory free routine. 
176
+  // See debug_mode for more about debug vs. release mode.  
177
+  // This routine is functionally identical to the cmMmFree() but takes the calling
178
+  // location information for use in tracking the block of memory.
179
+  cmMmRC_t cmMmFreeDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine );
180
+
181
+  // This function is identical to cmMmFree() but takes the address of the pointer
182
+  // to the block of memory to free. Upon successful completion *dataPtrPtr is
183
+  // set to NULL. In general this should be the preferred version of the free routine
184
+  // because it helps to eliminate problems of reusing deallocated memory blocks.
185
+  // Note that although dataPtrPtr must  point to a valid address  *dataPtrPtr may be NULL.
186
+  // This routine is generally only used in the release compile mode.
187
+  //  See cmMmFreePtrDebug() for the debug mode version.  See \ref debug_mode for more
188
+  //  about compile vs. release mode.
189
+  cmMmRC_t cmMmFreePtr(  cmMmH_t h, void** dataPtrPtr );
190
+
191
+  // Debug compile mode version of cmMmFreePtr(). 
192
+  // This function is functionally identical to cmMmFreePtr() but accepts information
193
+  // on the location of the call to aid in debuging.
194
+  cmMmRC_t cmMmFreePtrDebug( cmMmH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine );
195
+
196
+  // Return the size of a memory block returned from cmMmAllocate().
197
+  unsigned cmMmByteCount( cmMmH_t h, const void* dataPtr );
198
+
199
+  // Return the unique id associated with an address returned from cmMmAllocate().
200
+  unsigned cmMmDebugId( cmMmH_t h, const void* dataPtr);
201
+
202
+  // Flags for use with cmMmReport().
203
+  enum 
204
+  {
205
+    kSuppressSummaryMmFl = 0x01, //< Do not print a memory use summary report.
206
+    kIgnoreNormalMmFl    = 0x02, //< Do not print information for non-leaked,non-corrupt memory blocks. 
207
+    kIgnoreLeaksMmFl     = 0x04  //< Do not print information for leaked blocks. 
208
+  
209
+  };
210
+
211
+  // Report on the memory tracking data. 
212
+  // Returns kMmOkRC if no errors were found otherwise returns the error of the
213
+  // last anomoly reported. 
214
+  cmMmRC_t cmMmReport(   cmMmH_t h, unsigned flags );
215
+
216
+  // Analyze the memory assoc'd with a specific tracking record for corruption.
217
+  // Returns: kOkMmRC,kGuardCorruptMmRC,kWriteAfterFreeMmRc, or kMissingRecdMmRC.
218
+  // This function is only useful if kTrackMmFl was set in cmMmInitialize().
219
+  // Write-after-free errors are only detectable if kDeferFreeMmFl was set in cmMmInitialize().
220
+  cmMmRC_t cmMmIsGuardCorrupt( cmMmH_t h, unsigned id );
221
+
222
+  // Check all tracking records by calling cmMmmIsGuardCorrupt() on each record.
223
+  cmMmRC_t cmMmCheckAllGuards( cmMmH_t h );
224
+
225
+  //)
226
+  //}
227
+#ifdef __cplusplus
228
+}
229
+#endif
230
+
231
+#endif

+ 227
- 0
cmMidi.c View File

@@ -0,0 +1,227 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmMidi.h"
4
+
5
+
6
+enum
7
+{
8
+  mdStatusDescLabelCharCnt = 5
9
+};
10
+
11
+typedef struct
12
+{
13
+  cmMidiByte_t   status;
14
+  cmMidiByte_t  byteCnt;
15
+  char     label[ mdStatusDescLabelCharCnt+1 ];
16
+} cmMidiStatusDesc;
17
+
18
+cmMidiStatusDesc _cmMidiStatusDescArray[] =
19
+{
20
+  // channel messages
21
+	{ kNoteOffMdId,	2, "nof" },
22
+	{	kNoteOnMdId,	2, "non" },
23
+	{	kPolyPresMdId,2, "ppr" },
24
+	{	kCtlMdId,		  2, "ctl" },
25
+	{	kPgmMdId,		  1, "pgm" },
26
+	{	kChPresMdId,	1, "cpr" },
27
+	{	kPbendMdId,		2, "pb"  },
28
+
29
+
30
+	{	kSysExMdId,		kInvalidMidiByte,"sex" },
31
+
32
+  // system common
33
+  { kSysComMtcMdId,    1, "mtc" },
34
+  { kSysComSppMdId,    2, "spp" },
35
+  { kSysComSelMdId,    1, "sel" },
36
+  { kSysComUndef0MdId, 0, "cu0" },
37
+  { kSysComUndef1MdId, 0, "cu1" },
38
+  { kSysComTuneMdId,   0, "tun" },
39
+  { kSysComEoxMdId,    0, "eox" },
40
+
41
+  // system real-time
42
+  { kSysRtClockMdId, 0, "clk" },
43
+  { kSysRtUndef0MdId,0, "ud0" },
44
+  { kSysRtStartMdId, 0, "beg" },
45
+  { kSysRtContMdId,  0, "cnt" },
46
+  { kSysRtStopMdId,  0, "end" },
47
+  { kSysRtUndef1MdId,0, "ud1" },
48
+  { kSysRtSenseMdId, 0, "sns" },
49
+  { kSysRtResetMdId, 0, "rst" },
50
+
51
+ 	{ kInvalidStatusMdId,  kInvalidMidiByte, "ERR" }
52
+};
53
+
54
+cmMidiStatusDesc _cmMidiMetaStatusDescArray[] =
55
+{
56
+  { kSeqNumbMdId,    2, "seqn"  },
57
+  { kTextMdId,      -1, "text"  },
58
+  { kCopyMdId,      -1, "copy"  },
59
+  { kTrkNameMdId,   -1, "name"  },
60
+  { kInstrNameMdId, -1, "instr" },
61
+  { kLyricsMdId,    -1, "lyric" },
62
+  { kMarkerMdId,    -1, "mark"  },
63
+  { kCuePointMdId,  -1, "cue"   },
64
+  { kMidiChMdId,     1, "chan"  },
65
+  { kEndOfTrkMdId,   0, "eot"   },
66
+  { kTempoMdId,      3, "tempo" },
67
+  { kSmpteMdId,      5, "smpte" },
68
+  { kTimeSigMdId,    4, "tsig"  },
69
+  { kKeySigMdId,     2, "ksig"  },
70
+  { kSeqSpecMdId,   -1, "seqs"  },
71
+  { kInvalidMetaMdId, kInvalidMidiByte, "ERROR"}
72
+};
73
+
74
+//====================================================================================================
75
+
76
+const char* cmMidiStatusToLabel( cmMidiByte_t status )
77
+{
78
+  unsigned i;
79
+
80
+  if( !cmMidiIsStatus(status) )
81
+    return NULL;
82
+
83
+  // remove the channel value from ch msg status bytes
84
+  if( cmMidiIsChStatus(status) )
85
+    status &= 0xf0;
86
+
87
+  for(i=0; _cmMidiStatusDescArray[i].status != kInvalidStatusMdId; ++i)
88
+    if( _cmMidiStatusDescArray[i].status == status )
89
+      return _cmMidiStatusDescArray[i].label;
90
+
91
+  return _cmMidiStatusDescArray[i].label; 
92
+}
93
+
94
+const char*   cmMidiMetaStatusToLabel( cmMidiByte_t metaStatus )
95
+{
96
+  int i;
97
+  for(i=0; _cmMidiMetaStatusDescArray[i].status != kInvalidMetaMdId; ++i)
98
+    if( _cmMidiMetaStatusDescArray[i].status == metaStatus )
99
+      break;
100
+
101
+  return _cmMidiMetaStatusDescArray[i].label; 
102
+}
103
+
104
+cmMidiByte_t cmMidiStatusToByteCount( cmMidiByte_t status )
105
+{
106
+  unsigned i;
107
+
108
+  if( !cmMidiIsStatus(status) )
109
+    return kInvalidMidiByte;
110
+
111
+  // remove the channel value from ch msg status bytes
112
+  if( cmMidiIsChStatus(status) )
113
+    status &= 0xf0;
114
+
115
+  for(i=0; _cmMidiStatusDescArray[i].status != kInvalidStatusMdId; ++i)
116
+    if( _cmMidiStatusDescArray[i].status == status )
117
+      return _cmMidiStatusDescArray[i].byteCnt;
118
+
119
+  assert(0);
120
+
121
+  return 0; 
122
+}
123
+
124
+
125
+//====================================================================================================
126
+const char*     cmMidiToSciPitch( cmMidiByte_t pitch, char* label, unsigned labelCharCnt )
127
+{
128
+  static char buf[ kMidiSciPitchCharCnt ];
129
+
130
+  if( label == NULL || labelCharCnt == 0 )
131
+  {
132
+    label = buf;
133
+    labelCharCnt = kMidiSciPitchCharCnt;
134
+  }
135
+
136
+  assert( labelCharCnt >= kMidiSciPitchCharCnt );
137
+
138
+  if( /*pitch < 0 ||*/ pitch > 127 )
139
+  {
140
+    label[0] = 0;
141
+    return label;
142
+  }
143
+
144
+  assert( labelCharCnt >= 5 && /*pitch >= 0 &&*/ pitch <= 127 );
145
+
146
+  char     noteV[]      =  { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
147
+  char     shrpV[]      =  { ' ', '#', ' ', '#', ' ', ' ', '#', ' ', '#', ' ', '#', ' ' };
148
+  int      octave       =  (pitch / 12)-1;
149
+  unsigned noteIdx      =  pitch % 12;
150
+  char     noteCh       =  noteV[ noteIdx ];
151
+  char     sharpCh      =  shrpV[ noteIdx ];
152
+  unsigned idx          =  1;
153
+
154
+  label[labelCharCnt-1] = 0;
155
+  label[0]              = noteCh;
156
+
157
+  if( sharpCh != ' ' )
158
+  {
159
+    label[1] = sharpCh;
160
+    idx      = 2;
161
+  }
162
+
163
+  assert( -1 <= octave && octave <= 9);
164
+
165
+  snprintf(label+idx,kMidiSciPitchCharCnt-idx-1,"%i",octave);
166
+
167
+  return label;
168
+}
169
+
170
+
171
+cmMidiByte_t    cmSciPitchToMidi( const char* sciPitchStr )
172
+{
173
+  const char* cp      = sciPitchStr;
174
+  bool        sharpFl = false;
175
+  bool        flatFl  = false;
176
+  int         octave;
177
+  int         idx     = -1;
178
+
179
+  if( sciPitchStr==NULL || strlen(sciPitchStr) > 5 )
180
+    return kInvalidMidiPitch;
181
+
182
+  switch(tolower(*cp))
183
+  {
184
+    case 'a': idx = 9;  break;
185
+    case 'b': idx = 11; break;
186
+    case 'c': idx = 0;  break;
187
+    case 'd': idx = 2;  break;
188
+    case 'e': idx = 4;  break;
189
+    case 'f': idx = 5;  break;
190
+    case 'g': idx = 7;  break;
191
+    default:
192
+      return kInvalidMidiPitch;
193
+  }
194
+
195
+  ++cp;
196
+
197
+  if( !(*cp) )
198
+    return kInvalidMidiPitch;
199
+
200
+  
201
+  if((sharpFl = *cp=='#') == true )
202
+    ++idx;
203
+  else
204
+    if((flatFl  = *cp=='b') == true )
205
+      --idx;
206
+
207
+  if( sharpFl || flatFl )
208
+  {
209
+    ++cp;
210
+
211
+    if( !(*cp) )
212
+      return kInvalidMidiPitch;
213
+  }
214
+  
215
+  if( isdigit(*cp) == false && *cp!='-' )
216
+    return kInvalidMidiPitch;
217
+
218
+  octave = atoi(cp);
219
+
220
+  unsigned rv =  (octave*12) + idx + 12;
221
+  
222
+  if( 0 <= rv && rv <= 127 )
223
+    return rv;
224
+
225
+  return kInvalidMidiPitch;
226
+  
227
+}

+ 140
- 0
cmMidi.h View File

@@ -0,0 +1,140 @@
1
+#ifndef cmMidi_h
2
+#define cmMidi_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+enum
9
+{
10
+  kMidiChCnt           = 16,
11
+  kInvalidMidiByte     = 128,
12
+  kMidiNoteCnt         = kInvalidMidiByte,
13
+  kMidiCtlCnt          = kInvalidMidiByte,
14
+  kMidiPgmCnt          = kInvalidMidiByte,
15
+  kInvalidMidiPitch    = kInvalidMidiByte,
16
+  kInvalidMidiVelocity = kInvalidMidiByte,
17
+  kInvalidMidiCtl      = kInvalidMidiByte,
18
+  kInvalidMidiPgm      = kInvalidMidiByte,
19
+  kMidiSciPitchCharCnt = 5  // A#-1
20
+};
21
+
22
+
23
+// MIDI status bytes
24
+enum
25
+{
26
+  kInvalidStatusMdId = 0x00,
27
+  kNoteOffMdId       = 0x80,
28
+  kNoteOnMdId        = 0x90,
29
+  kPolyPresMdId      = 0xa0,
30
+  kCtlMdId           = 0xb0,
31
+  kPgmMdId           = 0xc0,
32
+  kChPresMdId        = 0xd0,
33
+  kPbendMdId         = 0xe0,
34
+  kSysExMdId         = 0xf0,
35
+
36
+  kSysComMtcMdId     = 0xf1,
37
+  kSysComSppMdId     = 0xf2,
38
+  kSysComSelMdId     = 0xf3,
39
+  kSysComUndef0MdId  = 0xf4,
40
+  kSysComUndef1MdId  = 0xf5,
41
+  kSysComTuneMdId    = 0xf6,
42
+  kSysComEoxMdId     = 0xf7,
43
+
44
+  kSysRtClockMdId  = 0xf8,
45
+  kSysRtUndef0MdId = 0xf9,
46
+  kSysRtStartMdId  = 0xfa,
47
+  kSysRtContMdId   = 0xfb,
48
+  kSysRtStopMdId   = 0xfc,
49
+  kSysRtUndef1MdId = 0xfd,
50
+  kSysRtSenseMdId  = 0xfe,
51
+  kSysRtResetMdId  = 0xff,
52
+  kMetaStId        = 0xff,
53
+
54
+  kSeqNumbMdId     = 0x00,
55
+  kTextMdId        = 0x01,
56
+  kCopyMdId        = 0x02,
57
+  kTrkNameMdId     = 0x03,
58
+  kInstrNameMdId   = 0x04,
59
+  kLyricsMdId      = 0x05,
60
+  kMarkerMdId      = 0x06,
61
+  kCuePointMdId    = 0x07,
62
+  kMidiChMdId      = 0x20,
63
+  kEndOfTrkMdId    = 0x2f,
64
+  kTempoMdId       = 0x51,
65
+  kSmpteMdId       = 0x54,
66
+  kTimeSigMdId     = 0x58,
67
+  kKeySigMdId      = 0x59,
68
+  kSeqSpecMdId     = 0x7f,
69
+  kInvalidMetaMdId = 0x80,
70
+
71
+  kSustainCtlMdId = 64
72
+  
73
+};
74
+
75
+
76
+typedef unsigned char cmMidiByte_t;
77
+
78
+//===============================================================================================
79
+// Utility Functions
80
+//
81
+
82
+#define cmMidiIsStatus( s )   (kNoteOffMdId <= (s) /*&& ((unsigned)(s)) <= kSysRtResetMdId*/ )
83
+#define cmMidiIsChStatus( s ) (kNoteOffMdId <= (s) && (s) <  kSysExMdId)
84
+
85
+
86
+const char*   cmMidiStatusToLabel(     cmMidiByte_t status );
87
+const char*   cmMidiMetaStatusToLabel( cmMidiByte_t metaStatus );
88
+
89
+// Returns kInvalidMidiByte if status is not a valid status byte
90
+cmMidiByte_t    cmMidiStatusToByteCount( cmMidiByte_t status );
91
+
92
+
93
+//===============================================================================================
94
+// MIDI Communication data types
95
+//
96
+
97
+typedef struct 
98
+{
99
+  unsigned     deltaUs; // time since last MIDI msg in microseconds
100
+  cmMidiByte_t status;  // midi status byte
101
+  cmMidiByte_t d0;      // midi data byte 0
102
+  cmMidiByte_t d1;      // midi data byte 1
103
+  cmMidiByte_t pad;
104
+} cmMidiMsg;
105
+
106
+typedef struct
107
+{
108
+  void*         cbDataPtr; // application supplied reference value from mdParserCreate()
109
+  unsigned      devIdx;    // the device the msg originated from
110
+  unsigned      portIdx;   // the port index on the source device
111
+  cmMidiMsg*    msgArray;  // pointer to an array of 'msgCnt' mdMsg records or NULL if sysExMsg is non-NULL
112
+  cmMidiByte_t* sysExMsg;  // pointer to a sys-ex msg or NULL if msgArray is non-NULL (see note below)
113
+  unsigned      msgCnt;    // count of mdMsg records or sys-ex bytes
114
+} cmMidiPacket_t;
115
+
116
+// Notes: If the sys-ex message can be contained in a single msg then
117
+// then the first msg byte is kSysExMdId and the last is kSysComEoxMdId.
118
+// If the sys-ex message is broken into multiple pieces then only the
119
+// first will begin with kSysExMdId and the last will end with kSysComEoxMdId.
120
+
121
+
122
+// If label is NULL or labelCharCnt==0 then a pointer to an internal static
123
+// buffer is returned. If label[] is given the it
124
+// should have at least 5 (kMidiPitchCharCnt) char's (including the terminating zero).
125
+// If 'pitch' is outside of the range 0-127 then a blank string is returned.
126
+const char*     cmMidiToSciPitch( cmMidiByte_t pitch, char* label, unsigned labelCharCnt );
127
+
128
+
129
+// Scientific pitch string: [A-Ga-g][#b][#] where  # may be -1 to 9.
130
+// Return kInvalidMidiPitch if sciPtichStr does not contain a valid 
131
+// scientific pitch string. This function will convert C-1 to G9 to 
132
+// valid MIDI pitch values 0 to 127.  Scientific pitch strings outside
133
+// of this range will be returned as kInvalidMidiPitch.   
134
+cmMidiByte_t    cmSciPitchToMidi( const char* sciPitchStr );
135
+
136
+#ifdef __cplusplus
137
+}
138
+#endif
139
+
140
+#endif

+ 1036
- 0
cmMidiFile.c
File diff suppressed because it is too large
View File


+ 163
- 0
cmMidiFile.h View File

@@ -0,0 +1,163 @@
1
+#ifndef cmMidiFile_h
2
+#define cmMidiFile_h
3
+
4
+// MIDI file timing:
5
+// Messages in the MIDI file are time tagged with a delta offset in 'ticks'
6
+// from the previous message in the same track.
7
+// 
8
+// A 'tick' can be converted to microsends as follows:
9
+//
10
+// microsecond per tick = micros per quarter note / ticks per quarter note
11
+// 
12
+// MpT = MpQN / TpQN
13
+// 
14
+// TpQN is given as a constant in the MIDI file header.
15
+// MpQN is given as the value of the MIDI file tempo message.
16
+//
17
+// See cmMidiFileSeekUSecs() for an example of converting ticks to milliseconds.
18
+
19
+
20
+typedef cmHandle_t cmMidiFileH_t;
21
+typedef unsigned   cmMfRC_t;
22
+
23
+typedef struct
24
+{
25
+  cmMidiByte_t hr;
26
+  cmMidiByte_t min;
27
+  cmMidiByte_t sec;
28
+  cmMidiByte_t frm;
29
+  cmMidiByte_t sfr;
30
+} cmMidiSmpte_t;
31
+
32
+typedef struct
33
+{
34
+  cmMidiByte_t num;
35
+  cmMidiByte_t den;
36
+  cmMidiByte_t metro;
37
+  cmMidiByte_t th2s;
38
+} cmMidiTimeSig_t;
39
+
40
+typedef struct
41
+{
42
+  cmMidiByte_t key;
43
+  cmMidiByte_t scale;
44
+} cmMidiKeySig_t;
45
+
46
+typedef struct
47
+{
48
+  cmMidiByte_t ch;
49
+  cmMidiByte_t d0;
50
+  cmMidiByte_t d1;
51
+  unsigned     durTicks; // note duration calc'd by 
52
+} cmMidiChMsg_t;
53
+
54
+
55
+typedef struct cmMidiTrackMsg_str
56
+{
57
+  unsigned                   dtick;   // delta ticks
58
+  cmMidiByte_t               status;  // ch msg's have the channel value removed (it is stored in u.chMsgPtr->ch)
59
+  cmMidiByte_t               metaId;  //
60
+  unsigned short             trkIdx;  //  
61
+  unsigned                   byteCnt; // length of data pointed to by u.voidPtr (or any other pointer in the union)
62
+  struct cmMidiTrackMsg_str* link;    // link to next record in this track
63
+
64
+  union
65
+  {
66
+    cmMidiByte_t           bVal;
67
+    unsigned               iVal;
68
+    unsigned short         sVal;
69
+    const char*            text;
70
+    const void*            voidPtr;
71
+    const cmMidiSmpte_t*   smptePtr;
72
+    const cmMidiTimeSig_t* timeSigPtr;
73
+    const cmMidiKeySig_t*  keySigPtr;
74
+    const cmMidiChMsg_t*   chMsgPtr;
75
+    const cmMidiByte_t*    sysExPtr;
76
+  } u;
77
+} cmMidiTrackMsg_t;
78
+
79
+enum
80
+{
81
+  kOkMfRC = cmOkRC,  // 0
82
+  kSysFopenFailMfRC,  // 1
83
+  kSysFreadFailMfRC,  // 2
84
+  kSysFseekFailMfRC,  // 3
85
+  kSysFtellFailMfRC,  // 4
86
+  kSysFcloseFailMfRC, // 5
87
+  kNotAMidiFileMfRC,  // 6
88
+  kMemAllocFailMfRC,  // 7
89
+  kFileCorruptMfRC,   // 8
90
+  kMissingEoxMfRC,    // 9 
91
+  kUnknownMetaIdMfRC, // 10
92
+  kInvalidHandleMfRC, // 11
93
+  kMissingNoteOffMfRC, // 12
94
+  kInvalidStatusMfRC  // 13
95
+};
96
+
97
+extern cmMidiFileH_t cmMidiFileNullHandle;
98
+
99
+cmMfRC_t              cmMidiFileOpen( const char* fn, cmMidiFileH_t* hPtr, cmCtx_t* ctx );
100
+cmMfRC_t              cmMidiFileClose( cmMidiFileH_t* hp );
101
+
102
+// Returns track count or kInvalidCnt if 'h' is invalid.
103
+unsigned              cmMidiFileTrackCount( cmMidiFileH_t h );
104
+
105
+// Return midi file format id (0,1,2) or kInvalidId if 'h' is invalid.
106
+unsigned              cmMidiFileType( cmMidiFileH_t h );
107
+
108
+// Returns ticks per quarter note or kInvalidMidiByte if 'h' is invalid or 0 if file uses SMPTE ticks per frame time base.
109
+unsigned              cmMidiFileTicksPerQN( cmMidiFileH_t h );
110
+
111
+// The file name used in an earlier call to midiFileOpen() or NULL if this 
112
+// midi file did not originate from an actual file.
113
+const char*           cmMidiFileName( cmMidiFileH_t h );
114
+
115
+// Returns SMPTE ticks per frame or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
116
+cmMidiByte_t          cmMidiFileTicksPerSmpteFrame( cmMidiFileH_t h );
117
+
118
+// Returns SMPTE format or kInvalidMidiByte if 'h' is invalid or 0 if file uses ticks per quarter note time base.
119
+cmMidiByte_t          cmMidiFileSmpteFormatId( cmMidiFileH_t h );
120
+
121
+// Returns count of records in track 'trackIdx' or kInvalidCnt if 'h' is invalid.
122
+unsigned              cmMidiFileTrackMsgCount( cmMidiFileH_t h, unsigned trackIdx );
123
+
124
+// Returns base of record chain from track 'trackIdx' or NULL if 'h' is invalid.
125
+const cmMidiTrackMsg_t* cmMidiFileTrackMsg( cmMidiFileH_t h, unsigned trackIdx );
126
+
127
+// Returns the total count of records in the midi file and the number in the array returned by cmMidiFileMsgArray().
128
+// Return kInvalidCnt if 'h' is invalid.
129
+unsigned              cmMidiFileMsgCount( cmMidiFileH_t h );
130
+
131
+// Returns a pointer to the base of an array of pointers to each record in the file sorted in ascending time order.
132
+// Returns NULL if 'h' is invalid.
133
+const cmMidiTrackMsg_t** cmMidiFileMsgArray( cmMidiFileH_t h );
134
+
135
+// Return a pointer to the first msg at or after 'usecsOffs' or kInvalidIdx if no
136
+// msg exists after 'usecsOffs'.  Note that 'usecOffs' is an offset from the beginning
137
+// of the file.
138
+// On return *'msgUsecsPtr' is set to the actual time of the msg. 
139
+// (which will be equal to or greater than 'usecsOffs').
140
+unsigned              cmMidiFileSeekUsecs( cmMidiFileH_t h, unsigned usecsOffs, unsigned* msgUsecsPtr, unsigned* newMicrosPerTickPtr );
141
+
142
+double                cmMidiFileDurSecs( cmMidiFileH_t h );
143
+
144
+// Convert the track message 'dtick' field to delta-microseconds.
145
+void                  cmMidiFileTickToMicros( cmMidiFileH_t h );
146
+
147
+// Calculate Note Duration 
148
+void                  cmMidiFileCalcNoteDurations( cmMidiFileH_t h );
149
+
150
+// Set the delay prior to the first non-zero msg.
151
+void                  cmMidiFileSetDelay( cmMidiFileH_t h, unsigned ticks );
152
+
153
+// This function packs a track msg into a single  consecutive 
154
+// block of memory buf[ bufByteCnt ]. Call cmMidiFilePackTracMsgBufByteCount()
155
+// to get the required buffer length for any given cmMidiTrackMsg_t instance.
156
+cmMidiTrackMsg_t*     cmMidiFilePackTrackMsg( const cmMidiTrackMsg_t* m, void* buf, unsigned bufByteCnt );
157
+unsigned              cmMidiFilePackTrackMsgBufByteCount( const cmMidiTrackMsg_t* m );
158
+
159
+void                  cmMidiFilePrint( cmMidiFileH_t h, unsigned trkIdx, cmRpt_t* rpt );
160
+bool                  cmMidiFileIsNull( cmMidiFileH_t h );
161
+void                  cmMidiFileTest( const char* fn, cmCtx_t* ctx );
162
+ 
163
+#endif

+ 567
- 0
cmMidiFilePlay.c View File

@@ -0,0 +1,567 @@
1
+#include <sys/time.h>  // gettimeofday()
2
+#include <unistd.h>    // usleep()
3
+//#include <time.h>      // clock_gettime()
4
+#include "cmPrefix.h"
5
+#include "cmGlobal.h"
6
+#include "cmRpt.h"
7
+#include "cmErr.h"
8
+#include "cmCtx.h"
9
+#include "cmMem.h"
10
+#include "cmMallocDebug.h"
11
+#include "cmFile.h"
12
+#include "cmMidi.h"
13
+#include "cmMidiPort.h"
14
+#include "cmMidiFile.h"
15
+#include "cmMidiFilePlay.h"
16
+
17
+#ifdef OS_OSX
18
+#include "osx/clock_gettime_stub.h"
19
+#endif
20
+
21
+#ifdef OS_LINUX
22
+#include <time.h> // clock_gettime()
23
+#endif
24
+
25
+typedef struct
26
+{
27
+  cmErr_t                  err;
28
+  cmCtx_t                  ctx;
29
+  cmMfpCallback_t          cbFunc;          
30
+  void*                    userCbPtr;
31
+  void*                    printDataPtr;
32
+  unsigned                 memBlockByteCnt;
33
+  cmMidiFileH_t            mfH;           // midi file handle
34
+  bool                     closeFileFl;   // true mfH should be closed when this midi file player is closed
35
+  unsigned                 ticksPerQN;    // global for file
36
+  unsigned                 microsPerTick; // set via tempo
37
+  unsigned                 etime;         // usecs elapsed since transmitting prev msg
38
+  unsigned                 mtime;         // usecs to wait before transmitting next msg 
39
+  unsigned                 msgN;          // count of pointers in msgV[]
40
+  unsigned                 msgIdx;        // index into msgV[] of next msg to transmit
41
+  const cmMidiTrackMsg_t** msgV;          // array of msg pointers
42
+
43
+} cmMfp_t;
44
+
45
+cmMfpH_t cmMfpNullHandle = cmSTATIC_NULL_HANDLE;
46
+
47
+#define _cmMfpError( mfp, rc ) _cmMfpOnError(mfp, rc, __LINE__,__FILE__,__FUNCTION__ )
48
+
49
+// note: mfp may be NULL
50
+cmMfpRC_t _cmMfpOnError( cmMfp_t* mfp, cmMfpRC_t rc, int line, const char* fn, const char* func )
51
+{
52
+  return cmErrMsg(&mfp->err,rc,"rc:%i %i %s %s\n",rc,line,func,fn);
53
+}
54
+
55
+cmMfp_t* _cmMfpHandleToPtr( cmMfpH_t h )
56
+{
57
+  cmMfp_t* p = (cmMfp_t*)h.h;
58
+  assert(p != NULL);
59
+  return p;
60
+}
61
+
62
+void _cmMfpUpdateMicrosPerTick( cmMfp_t* mfp, unsigned microsPerQN )
63
+{ 
64
+  mfp->microsPerTick =  microsPerQN / mfp->ticksPerQN; 
65
+  printf("microsPerTick: %i bpm:%i ticksPerQN:%i\n", mfp->microsPerTick,microsPerQN,mfp->ticksPerQN);
66
+}
67
+
68
+cmMfpRC_t cmMfpCreate( cmMfpH_t* hp, cmMfpCallback_t cbFunc, void* userCbPtr, cmCtx_t* ctx )
69
+{
70
+  cmMfp_t* p = cmMemAllocZ( cmMfp_t, 1 );
71
+
72
+  cmErrSetup(&p->err,&ctx->rpt,"MIDI File Player");
73
+  p->ctx             = *ctx;
74
+  p->cbFunc          = cbFunc;
75
+  p->userCbPtr       = userCbPtr;
76
+  p->mfH.h           = NULL;
77
+  p->closeFileFl     = false;
78
+  p->ticksPerQN      = 0;
79
+  p->microsPerTick   = 0;
80
+  p->etime           = 0;
81
+  p->msgN            = 0;
82
+  p->msgV            = NULL;
83
+  p->msgIdx          = 0;
84
+  hp->h              = p;
85
+  return kOkMfpRC;
86
+}
87
+
88
+cmMfpRC_t cmMfpDestroy( cmMfpH_t* hp )
89
+{
90
+  if( hp == NULL )
91
+    return kOkMfpRC;
92
+
93
+  if( cmMfpIsValid(*hp) )
94
+  {
95
+    cmMfp_t* p = _cmMfpHandleToPtr(*hp);
96
+    
97
+    if( cmMidiFileIsNull(p->mfH)==false && p->closeFileFl==true )
98
+      cmMidiFileClose(&p->mfH);
99
+
100
+    cmMemFree(p);
101
+    hp->h = NULL;
102
+  }
103
+
104
+  return kOkMfpRC;
105
+}
106
+
107
+bool cmMfpIsValid(    cmMfpH_t h )
108
+{ return h.h != NULL; }
109
+
110
+cmMfpRC_t cmMfpLoadFile(   cmMfpH_t h, const char* fn )
111
+{
112
+  cmMfpRC_t     rc   = kOkMfpRC;
113
+  cmMfp_t*      p    = _cmMfpHandleToPtr(h);
114
+  cmMidiFileH_t mfH  = cmMidiFileNullHandle;
115
+
116
+  if((rc = cmMidiFileOpen( fn, &mfH, &p->ctx )) != kOkMfRC )
117
+    return _cmMfpError(p,kFileOpenFailMfpRC);
118
+
119
+  if((rc= cmMfpLoadHandle( h, mfH )) == kOkMfpRC )
120
+    p->closeFileFl                  = true;
121
+
122
+  return rc;
123
+}
124
+
125
+cmMfpRC_t cmMfpLoadHandle( cmMfpH_t h, cmMidiFileH_t mfH )
126
+{
127
+  cmMfp_t* p = _cmMfpHandleToPtr(h);
128
+
129
+  // if a file has already been assigned to this player
130
+  if( (cmMidiFileIsNull(p->mfH) == false) && p->closeFileFl)
131
+  {
132
+    // close the existing file
133
+    cmMidiFileClose(&p->mfH);    
134
+  }
135
+
136
+  // get the count of msg's in the new midi file
137
+  if((p->msgN = cmMidiFileMsgCount(mfH)) == cmInvalidCnt )
138
+    return _cmMfpError(p,kInvalidFileMfpRC);
139
+
140
+  // get a pointer to the first mesage
141
+  if((p->msgV = cmMidiFileMsgArray(mfH)) == NULL )
142
+    return _cmMfpError(p,kInvalidFileMfpRC);
143
+
144
+  // get the count of ticks per qn
145
+  if((p->ticksPerQN = cmMidiFileTicksPerQN( mfH )) == 0 )
146
+    return _cmMfpError(p,kSmpteTickNotImplMfpRC);
147
+  
148
+  // set the initial tempo to 120
149
+  _cmMfpUpdateMicrosPerTick(p,60000000/120);
150
+
151
+  p->msgIdx     = 0;
152
+  p->mfH        = mfH;
153
+  p->etime      = 0;
154
+  p->mtime      = 0;
155
+  p->closeFileFl= false;
156
+
157
+  //if( p->msgIdx > 0 )
158
+  //  p->mtime = p->msgV[0]->tick * p->microsPerTick;
159
+
160
+  return kOkMfpRC;
161
+}
162
+
163
+cmMfpRC_t cmMfpSeek( cmMfpH_t h, unsigned offsUsecs )
164
+{
165
+  cmMfp_t*   p = _cmMfpHandleToPtr(h);
166
+  unsigned msgOffsUsecs = 0;
167
+  unsigned msgIdx;
168
+  unsigned newMicrosPerTick;
169
+
170
+  // if the requested offset is past the end of the file then return EOF
171
+  if((msgIdx = cmMidiFileSeekUsecs( p->mfH, offsUsecs, &msgOffsUsecs, &newMicrosPerTick )) == cmInvalidIdx )
172
+  {
173
+    p->msgIdx = p->msgN;
174
+    return _cmMfpError(p,kEndOfFileMfpRC);
175
+  }
176
+
177
+  if( msgIdx < p->msgIdx )
178
+    p->msgIdx = 0;
179
+
180
+  p->mtime =  msgOffsUsecs;
181
+  p->etime =  0;
182
+  p->microsPerTick = newMicrosPerTick;
183
+  p->msgIdx = msgIdx;
184
+
185
+  assert(p->mtime >= 0);
186
+
187
+  return kOkMfpRC;
188
+}
189
+
190
+//    p  0     1      n  2     
191
+//    v  v     v      v  v     
192
+// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
193
+// 012345678901234567890123456780
194
+// 0         1         2
195
+// 
196
+// p =  3 = prev msg sent
197
+// n = 19 = next msg to send
198
+// 0 =  6 = call to cmMfpClock()
199
+// 1 = 12 = call to cmMfpClock()
200
+// 2 = 22 = call to cmMfpClock()
201
+// 
202
+//    dusecs   etime   mtime 
203
+// 0   n/a       3      13        
204
+// 1    6        9       7
205
+// 2   10       19      -3
206
+//      
207
+
208
+cmMfpRC_t cmMfpClock(  cmMfpH_t h, unsigned dusecs )
209
+{
210
+  cmMfp_t* p = _cmMfpHandleToPtr(h);
211
+
212
+  if( p->msgIdx >= p->msgN )
213
+    return kEndOfFileMfpRC;
214
+
215
+  // get a pointer to the next msg to send
216
+  const cmMidiTrackMsg_t* mp    = p->msgV[p->msgIdx];
217
+
218
+  // p->etime is the interval of time between when the last msg was
219
+  // sent and the end of the time window for this mfpClock() cycle
220
+  p->etime += dusecs;
221
+
222
+  //printf("init e:%i d:%i\n",p->etime, p->mtime);
223
+
224
+  // if the elapsed time (etime) since the last msg is greater or equal
225
+  // to the delta time to the next msg (mtime)
226
+  while( p->etime >= p->mtime )
227
+  {      
228
+    //printf("e:%i d:%i\n",p->etime, p->mtime);
229
+
230
+    if( mp->status == kMetaStId && mp->metaId == kTempoMdId )
231
+      _cmMfpUpdateMicrosPerTick(p,mp->u.iVal );
232
+    
233
+    
234
+    p->cbFunc( p->userCbPtr, p->mtime, mp );
235
+      
236
+    ++(p->msgIdx);
237
+
238
+    if( p->msgIdx >= p->msgN )
239
+      break;
240
+
241
+    // get the next msg to send
242
+    mp        = p->msgV[p->msgIdx];
243
+
244
+    // we probably went past the actual mtime - so update etime
245
+    // with the delta usecs from the msg just sent and the current time
246
+    p->etime -= p->mtime;
247
+
248
+    // calc the delta usecs from the message just sent to the next msg to send
249
+    //p->mtime  = (mp->tick - p->msgV[p->msgIdx-1]->tick) * p->microsPerTick;
250
+    p->mtime  = mp->dtick * p->microsPerTick;
251
+
252
+  }
253
+
254
+  return p->msgIdx >= p->msgN ? kEndOfFileMfpRC : kOkMfpRC;
255
+
256
+}
257
+
258
+
259
+void mfpPrint( void* userDataPtr, const char* fmt, va_list vl )
260
+{
261
+  vprintf(fmt,vl);
262
+}
263
+
264
+// this assumes that the seconds have been normalized to a recent start time
265
+// so as to avoid overflow
266
+unsigned _cmMfpElapsedMicroSecs( const struct timespec* t0, const struct timespec* t1 )
267
+{
268
+  // convert seconds to usecs
269
+  long u0 = t0->tv_sec * 1000000;
270
+  long u1 = t1->tv_sec * 1000000;
271
+
272
+  // convert nanoseconds to usec
273
+  u0 += t0->tv_nsec / 1000;
274
+  u1 += t1->tv_nsec / 1000;
275
+
276
+  // take diff between t1 and t0
277
+  return u1 - u0;
278
+}
279
+
280
+void _cmMfpTestTimer()
281
+{
282
+  useconds_t  suspendUsecs    = 15 * 1000;
283
+  struct timespec t0,t1,t2;
284
+  unsigned        accum = 0;
285
+  unsigned        i;
286
+  unsigned        n = 4000;
287
+  
288
+  // t0 will be the base time which all other times will be 
289
+  // set relative to.
290
+  clock_gettime(CLOCK_REALTIME,&t0);
291
+  t2 = t0;
292
+  t2.tv_sec = 0;
293
+
294
+  for(i=0; i<n; ++i)
295
+  {
296
+    usleep(suspendUsecs);
297
+
298
+    
299
+    clock_gettime(CLOCK_REALTIME,&t1);
300
+    t1.tv_sec -= t0.tv_sec;
301
+
302
+    unsigned d0usec = _cmMfpElapsedMicroSecs(&t0,&t1);
303
+    unsigned d1usec = _cmMfpElapsedMicroSecs(&t2,&t1);
304
+
305
+    accum += d1usec;
306
+
307
+    if( i == n-1 )
308
+      printf("%i %i %i\n",d0usec,d1usec,accum);
309
+    
310
+    t2 = t1;
311
+  }
312
+
313
+}
314
+
315
+// midi file player callback test function
316
+void _cmMfpCallbackTest( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
317
+{
318
+  if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
319
+    cmMpDeviceSend( 0, 0, msgPtr->status+msgPtr->u.chMsgPtr->ch, msgPtr->u.chMsgPtr->d0,msgPtr->u.chMsgPtr->d1);
320
+
321
+  //printf("%i 0x%x 0x%x %i\n",msgPtr->tick,msgPtr->status,msgPtr->metaId,msgPtr->trkIdx);
322
+}
323
+
324
+// midi port callback test function
325
+void _cmMpCallbackTest( const cmMidiPacket_t* pktArray, unsigned pktCnt )
326
+{}
327
+
328
+cmMfpRC_t cmMfpTest( const char* fn, cmCtx_t* ctx )
329
+{
330
+  cmMfpH_t        mfpH               = cmMfpNullHandle;
331
+  cmMfpRC_t       rc;
332
+  useconds_t      suspendUsecs       = 15 * 1000;
333
+  struct timespec t0,t1,base;
334
+  //unsigned        i;
335
+  //unsigned        n                = 4000;
336
+  unsigned        mdParserBufByteCnt = 1024;
337
+
338
+  printf("Initializing MIDI Devices...\n");
339
+  cmMpInitialize( _cmMpCallbackTest, NULL, mdParserBufByteCnt,"app", &ctx->rpt );
340
+
341
+  //mdReport();
342
+
343
+  printf("Creating Player...\n");
344
+  if((rc = cmMfpCreate( &mfpH, _cmMfpCallbackTest, NULL, ctx )) != kOkMfpRC )
345
+    return rc;
346
+
347
+  printf("Loading MIDI file...\n");
348
+  if((rc = cmMfpLoadFile( mfpH, fn )) != kOkMfpRC )
349
+    goto errLabel;
350
+
351
+  if((rc = cmMfpSeek( mfpH, 60 * 1000000 )) != kOkMfpRC )
352
+    goto errLabel;
353
+
354
+  clock_gettime(CLOCK_REALTIME,&base);
355
+  t0 = base;
356
+  t0.tv_sec = 0;
357
+
358
+  //for(i=0; i<n; ++i)
359
+  while(rc != kEndOfFileMfpRC)
360
+  {
361
+    usleep(suspendUsecs);
362
+    
363
+    clock_gettime(CLOCK_REALTIME,&t1);    
364
+    t1.tv_sec -= base.tv_sec;
365
+
366
+    unsigned dusecs = _cmMfpElapsedMicroSecs(&t0,&t1);
367
+   
368
+    rc = cmMfpClock( mfpH, dusecs );
369
+    //printf("%i %i\n",dusecs,rc);
370
+    t0 = t1;
371
+  }
372
+ 
373
+ errLabel:
374
+  cmMfpDestroy(&mfpH);
375
+
376
+  cmMpFinalize();
377
+
378
+  return rc;
379
+}
380
+
381
+
382
+//------------------------------------------------------------------------------------------------------------
383
+#include "cmFloatTypes.h"
384
+#include "cmComplexTypes.h"
385
+#include "cmLinkedHeap.h"
386
+#include "cmSymTbl.h"
387
+#include "cmAudioFile.h"
388
+#include "cmProcObj.h"
389
+#include "cmProcTemplateMain.h"
390
+#include "cmVectOps.h"
391
+
392
+#include "cmProc.h"
393
+#include "cmProc2.h"
394
+
395
+
396
+enum
397
+{
398
+  kOkMfptRC = cmOkRC,
399
+  kMfpFailMfptRC,
400
+  kAudioFileFailMfptRC,
401
+  kProcObjFailMfptRC
402
+};
403
+
404
+typedef struct
405
+{
406
+  cmErr_t*     err;
407
+  cmMidiSynth* msp; 
408
+} _cmMfpTest2CbData_t;
409
+
410
+
411
+// Called by the MIDI file player to send a msg to the MIDI synth. 
412
+void _cmMfpCb( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr )
413
+{
414
+  if( kNoteOffMdId <= msgPtr->status && msgPtr->status <= kPbendMdId )
415
+  {
416
+    cmMidiPacket_t pkt;
417
+    cmMidiMsg      msg;
418
+    _cmMfpTest2CbData_t* d = (_cmMfpTest2CbData_t*)userCbPtr;
419
+
420
+    msg.deltaUs = dmicros;
421
+    msg.status  = msgPtr->status + msgPtr->u.chMsgPtr->ch;
422
+    msg.d0      = msgPtr->u.chMsgPtr->d0;
423
+    msg.d1      = msgPtr->u.chMsgPtr->d1;
424
+
425
+    pkt.cbDataPtr = NULL;
426
+    pkt.devIdx    = cmInvalidIdx;
427
+    pkt.portIdx   = cmInvalidIdx;
428
+    pkt.msgArray  = &msg;
429
+    pkt.sysExMsg  = NULL;
430
+    pkt.msgCnt    = 1;
431
+
432
+    if( cmMidiSynthOnMidi( d->msp, &pkt, 1 ) != cmOkRC )
433
+      cmErrMsg(d->err,kProcObjFailMfptRC,"Synth. MIDI receive failed.");
434
+  }
435
+}
436
+
437
+// Called by the MIDI synth to send a msg to the voice bank.
438
+int _cmMidiSynthCb( struct cmMidiVoice_str* voicePtr, unsigned sel, cmSample_t* outChArray[], unsigned outChCnt )
439
+{
440
+  return cmWtVoiceBankExec( ((cmWtVoiceBank*)voicePtr->pgm.cbDataPtr), voicePtr, sel, outChArray, outChCnt );
441
+}
442
+
443
+
444
+// BUG BUG BUG: THIS FUNCTION IS NOT TESTED!!!!!
445
+cmRC_t cmMfpTest2( const char* midiFn, const char* audioFn, cmCtx_t* ctx )
446
+{
447
+  cmRC_t              rc         = kOkMfptRC;
448
+  cmMfpH_t            mfpH       = cmMfpNullHandle;
449
+  _cmMfpTest2CbData_t cbData;
450
+  cmErr_t             err;
451
+  cmAudioFileH_t      afH        = cmNullAudioFileH;
452
+  cmRC_t              afRC       = kOkAfRC;
453
+  double              afSrate    = 44100;
454
+  unsigned            afBits     = 16;
455
+  unsigned            afChCnt    = 1;
456
+  cmCtx*              cctx;
457
+  cmMidiSynth*        msp;
458
+  cmWtVoiceBank*      vbp;
459
+  unsigned            msPgmCnt   = 127;
460
+  cmMidiSynthPgm      msPgmArray[ msPgmCnt ];
461
+  unsigned            msVoiceCnt = 36;
462
+  unsigned            procSmpCnt = 64;
463
+  unsigned            i;
464
+
465
+  cmErrSetup(&err,&ctx->rpt,"MFP Test 2");
466
+
467
+  // create the MIDI file player
468
+  if( cmMfpCreate(&mfpH, _cmMfpCb, &cbData, ctx ) != kOkMfpRC )
469
+    return cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player create failed.");
470
+
471
+  // create an output audio file
472
+  if( cmAudioFileIsValid( afH = cmAudioFileNewCreate(audioFn, afSrate, afBits, afChCnt, &afRC, &ctx->rpt))==false)
473
+  {
474
+    rc = cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file create failed.");
475
+    goto errLabel;
476
+  }
477
+
478
+  // load the midi file into the player
479
+  if( cmMfpLoadFile( mfpH, midiFn ) != kOkMfpRC )
480
+  {
481
+    rc = cmErrMsg(&err,kMfpFailMfptRC,"MIDI file load failed.");
482
+    goto errLabel;
483
+  }
484
+
485
+  // create the proc obj context
486
+  if((cctx = cmCtxAlloc(NULL, &ctx->rpt, cmLHeapNullHandle, cmSymTblNullHandle )) == NULL)
487
+  {
488
+    rc = cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx allocate failed.");
489
+    goto errLabel;
490
+  }
491
+
492
+  // create the voice bank
493
+  if((vbp = cmWtVoiceBankAlloc(cctx, NULL, afSrate, procSmpCnt, msVoiceCnt, afChCnt )) == NULL)
494
+  {
495
+    rc = cmErrMsg(&err,kProcObjFailMfptRC,"WT voice bank allocate failed.");
496
+    goto errLabel;
497
+  }
498
+
499
+  // a MIDI synth
500
+  if((msp =  cmMidiSynthAlloc(cctx, NULL, msPgmArray, msPgmCnt, msVoiceCnt, procSmpCnt, afChCnt, afSrate  )) == NULL )
501
+  {
502
+    rc = cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth allocate failed.");
503
+    goto errLabel;
504
+  }
505
+
506
+  cbData.msp = msp;
507
+  cbData.err = &err;
508
+
509
+  // load all of the the MIDI pgm recds with the same settings
510
+  for(i=0; i<msPgmCnt; ++i)
511
+  {
512
+    msPgmArray[i].pgm       = i;
513
+    msPgmArray[i].cbPtr     = _cmMidiSynthCb; // Call this function to update voices using this pgm
514
+    msPgmArray[i].cbDataPtr = vbp;            // Voice bank containing the voice states.
515
+  }
516
+
517
+
518
+  unsigned dusecs = floor((double)procSmpCnt * 1000000. / afSrate);
519
+
520
+  while(rc != kEndOfFileMfpRC)
521
+  {
522
+    // update the MFP's current time and call _cmMfpCb() for MIDI msgs whose time has elapsed
523
+    rc = cmMfpClock( mfpH, dusecs );
524
+
525
+    // check for MFP errors
526
+    if(rc!=kOkMfpRC && rc!=kEndOfFileMfpRC)
527
+    {
528
+      cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player exec failed.");
529
+      goto errLabel;
530
+    }
531
+    
532
+    // generate audio based on the current state of the synth voices
533
+    if( cmMidiSynthExec(msp, NULL, 0 ) != cmOkRC )
534
+    {
535
+      cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth exec. failed.");
536
+      goto errLabel;
537
+    }
538
+
539
+    // write the last frame of synth. generated audio to the output file
540
+    if( cmAudioFileWriteSample(afH, procSmpCnt, msp->outChCnt, msp->outChArray ) != kOkAfRC )
541
+    {
542
+      cmErrMsg(&err,kProcObjFailMfptRC,"Audio file write failed.");
543
+      goto errLabel;
544
+    }
545
+
546
+  }
547
+  
548
+
549
+ errLabel:
550
+  if( cmMidiSynthFree(&msp) != cmOkRC )
551
+    cmErrMsg(&err,kProcObjFailMfptRC,"MIDI synth. free failed.");
552
+
553
+  if( cmWtVoiceBankFree(&vbp) != cmOkRC )
554
+    cmErrMsg(&err,kProcObjFailMfptRC,"WT voice free failed.");
555
+
556
+  if( cmCtxFree(&cctx) != cmOkRC )
557
+    cmErrMsg(&err,kProcObjFailMfptRC,"cmCtx free failed.");
558
+
559
+  if( cmAudioFileDelete(&afH) )
560
+    cmErrMsg(&err,kAudioFileFailMfptRC,"The audio file close failed.");
561
+
562
+  if( cmMfpDestroy(&mfpH) != kOkMfpRC )
563
+    cmErrMsg(&err,kMfpFailMfptRC,"MIDI file player destroy failed.");
564
+
565
+
566
+  return rc;
567
+}

+ 59
- 0
cmMidiFilePlay.h View File

@@ -0,0 +1,59 @@
1
+#ifndef midiFilePlay_h
2
+#define midiFilePlay_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+typedef cmHandle_t cmMfpH_t;
9
+typedef cmRC_t     cmMfpRC_t;
10
+
11
+typedef void (*cmMfpCallback_t)( void* userCbPtr, unsigned dmicros, const cmMidiTrackMsg_t* msgPtr );
12
+
13
+enum
14
+{
15
+  kOkMfpRC = cmOkRC,            // 0
16
+  kInvalidHandleMfpRC,          // 1
17
+  kFileOpenFailMfpRC,           // 2 
18
+  kInvalidFileMfpRC,            // 3
19
+  kMemAllocFailMfpRC,           // 4
20
+  kSmpteTickNotImpleMfpRC,      // 5
21
+  kEndOfFileMfpRC,              // 6
22
+  kSmpteTickNotImplMfpRC        // 7
23
+  
24
+};
25
+
26
+extern cmMfpH_t cmMfpNullHandle;
27
+
28
+cmMfpRC_t cmMfpCreate(     cmMfpH_t* hp, cmMfpCallback_t cbFunc, void* userCbPtr, cmCtx_t* ctx );
29
+cmMfpRC_t cmMfpDestroy(    cmMfpH_t* hp );
30
+bool      cmMfpIsValid(    cmMfpH_t h );
31
+
32
+// Load a MIDI file into the player. This MIDI file will be automatically
33
+// closed when a new file is loaded at a later time or the MIDI file player handle is destroyed.
34
+cmMfpRC_t cmMfpLoadFile(   cmMfpH_t h, const char* fn );
35
+
36
+// Load a MIDI file into the player using a file owned by the host.
37
+// This file will NOT be closed when a new file is loaded at a later time
38
+// or the MIDI file player handle is destroyed.
39
+cmMfpRC_t cmMfpLoadHandle( cmMfpH_t h, cmMidiFileH_t mfH );
40
+
41
+// Reset the play position of the player to an offset in microseconds from 
42
+// the beginning of the file.  If there are no message at or after 'offsMicrosecs'
43
+// then the function will return kEndOfFileMfpRC.
44
+cmMfpRC_t cmMfpSeek(       cmMfpH_t h, unsigned offsMicrosecs );
45
+
46
+// This is the driving clock call for the player. 'deltaMicroSecs' is the
47
+// elapsed time in microseconds since the last call to this function.
48
+// Call to 'cbFunc', as set in by cmMfpCreate() occur from this function.
49
+cmMfpRC_t cmMfpClock(      cmMfpH_t h, unsigned deltaMicroSecs );
50
+
51
+cmMfpRC_t cmMfpTest( const char* fn, cmCtx_t* ctx );
52
+
53
+cmRC_t cmMfpTest2( const char* midiFn, const char* audioFn, cmCtx_t* ctx );
54
+
55
+#ifdef __cplusplus
56
+}
57
+#endif
58
+
59
+#endif

+ 458
- 0
cmMidiPort.c View File

@@ -0,0 +1,458 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmRpt.h"
4
+#include "cmErr.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmMidi.h"
8
+#include "cmMidiPort.h"
9
+
10
+
11
+
12
+//===================================================================================================
13
+//
14
+//
15
+
16
+enum 
17
+{ 
18
+  kBufByteCnt = 1024, 
19
+
20
+  kExpectStatusStId=0,       // 0
21
+  kExpectDataStId,           // 1
22
+  kExpectStatusOrDataStId,   // 2
23
+  kExpectEOXStId             // 3
24
+};
25
+
26
+typedef struct cmMpParserCb_str
27
+{
28
+  cmMpCallback_t           cbFunc;
29
+  void*                    cbDataPtr;
30
+  struct cmMpParserCb_str* linkPtr;
31
+} cmMpParserCb_t;
32
+
33
+typedef struct
34
+{
35
+  cmErr_t        err;
36
+
37
+  cmMpParserCb_t* cbChain;
38
+  
39
+  cmMidiPacket_t  pkt;
40
+
41
+  unsigned      state;          // parser state id
42
+  unsigned      errCnt;         // accumlated error count
43
+  cmMidiByte_t  status;         // running status
44
+  cmMidiByte_t  data0;          // data byte 0
45
+  unsigned      dataCnt;        // data byte cnt for current status 
46
+  unsigned      dataIdx;        // index (0 or 1) of next data byte
47
+  cmMidiByte_t* buf;            // output buffer
48
+  unsigned      bufByteCnt;     // output buffer byte cnt
49
+  unsigned      bufIdx;         // next output buffer index
50
+  unsigned      msgCnt;         // count of channel messages in the buffer
51
+} cmMpParser_t;
52
+
53
+cmMpParser_t* _cmMpParserFromHandle( cmMpParserH_t h )
54
+{
55
+  cmMpParser_t* p = (cmMpParser_t*)h.h;
56
+  assert(p!=NULL);
57
+  return p;
58
+}
59
+
60
+void _cmMpParserReport( cmMpParser_t* p )
61
+{
62
+  cmRptPrintf(p->err.rpt,"state:%i st:0x%x d0:%i dcnt:%i didx:%i buf[%i]->%i msg:%i err:%i\n",p->state,p->status,p->data0,p->dataCnt,p->dataIdx,p->bufByteCnt,p->bufIdx,p->msgCnt,p->errCnt);
63
+}
64
+
65
+void _cmMpParserDestroy( cmMpParser_t* p )
66
+{
67
+  cmMemPtrFree(&p->buf);
68
+
69
+  cmMpParserCb_t* c = p->cbChain;
70
+  while(c != NULL)
71
+  {
72
+    cmMpParserCb_t* nc = c->linkPtr;
73
+    cmMemFree(c);
74
+    c = nc;
75
+  }
76
+
77
+  cmMemPtrFree(&p);
78
+
79
+}
80
+ 
81
+cmMpParserH_t cmMpParserCreate( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned bufByteCnt, cmRpt_t* rpt )
82
+{
83
+  cmMpRC_t      rc = kOkMpRC;
84
+  cmMpParserH_t h;
85
+  cmMpParser_t* p = cmMemAllocZ( cmMpParser_t, 1 );
86
+
87
+  cmErrSetup(&p->err,rpt,"MIDI Parser");
88
+
89
+  p->pkt.devIdx        = devIdx;
90
+  p->pkt.portIdx       = portIdx;
91
+
92
+  //p->cbChain           = cmMemAllocZ( cmMpParserCb_t, 1 );
93
+  //p->cbChain->cbFunc    = cbFunc;
94
+  //p->cbChain->cbDataPtr = cbDataPtr;
95
+  //p->cbChain->linkPtr   = NULL;
96
+  p->cbChain           = NULL;
97
+  p->buf               = cmMemAllocZ( cmMidiByte_t, bufByteCnt );
98
+  p->bufByteCnt        = bufByteCnt;
99
+  p->bufIdx            = 0;
100
+  p->msgCnt            = 0;
101
+  p->state             = kExpectStatusStId;
102
+  p->dataIdx           = cmInvalidIdx;
103
+  p->dataCnt           = cmInvalidCnt;
104
+  p->status            = kInvalidStatusMdId;
105
+
106
+  h.h = p;
107
+
108
+  if( cbFunc != NULL )
109
+    rc = cmMpParserInstallCallback(h, cbFunc, cbDataPtr );
110
+    
111
+
112
+  if( rc != kOkMpRC )
113
+  {
114
+    h.h = NULL;
115
+    _cmMpParserDestroy(p);
116
+  }
117
+  
118
+
119
+  return h;
120
+} 
121
+
122
+void        cmMpParserDestroy(    cmMpParserH_t* hp )
123
+{
124
+  if( hp==NULL || hp->h == NULL )
125
+    return;
126
+
127
+  cmMpParser_t* p = _cmMpParserFromHandle(*hp);
128
+
129
+  _cmMpParserDestroy(p);
130
+  
131
+  hp->h = NULL;
132
+
133
+}
134
+
135
+unsigned    cmMpParserErrorCount( cmMpParserH_t h )
136
+{
137
+  cmMpParser_t* p = _cmMpParserFromHandle(h);
138
+  if( p == NULL )
139
+    return 0;
140
+
141
+  return p->errCnt;
142
+}
143
+
144
+void _cmMpParserCb( cmMpParser_t* p, cmMidiPacket_t* pkt, unsigned pktCnt )
145
+{
146
+  cmMpParserCb_t* c = p->cbChain;
147
+  for(; c!=NULL; c=c->linkPtr)
148
+  {
149
+    pkt->cbDataPtr = c->cbDataPtr;
150
+    c->cbFunc( pkt, pktCnt );
151
+  }
152
+}
153
+
154
+void _cmMpTransmitChMsgs( cmMpParser_t* p )
155
+{
156
+  if( p->msgCnt > 0 )
157
+  {
158
+    p->pkt.msgArray = (cmMidiMsg*)p->buf;
159
+    p->pkt.msgCnt   = p->msgCnt;
160
+    p->pkt.sysExMsg = NULL;
161
+
162
+    //p->cbFunc( &p->pkt, 1 );  
163
+    _cmMpParserCb(p,&p->pkt,1);
164
+
165
+    p->bufIdx = 0;
166
+    p->msgCnt = 0;
167
+  }
168
+}
169
+
170
+void _cmMpTransmitSysEx( cmMpParser_t* p )
171
+{
172
+  p->pkt.msgArray = NULL;
173
+  p->pkt.sysExMsg = p->buf;
174
+  p->pkt.msgCnt   = p->bufIdx;
175
+  //p->cbFunc( &p->pkt, 1 );
176
+  _cmMpParserCb(p,&p->pkt,1);
177
+  p->bufIdx = 0;
178
+
179
+}
180
+
181
+void _cmMpParserStoreChMsg( cmMpParser_t* p, unsigned deltaMicroSecs,  cmMidiByte_t d )
182
+{
183
+  // if there is not enough room left in the buffer then transmit the current messages
184
+  if( p->bufByteCnt - p->bufIdx < sizeof(cmMidiMsg) )
185
+    _cmMpTransmitChMsgs(p);
186
+
187
+
188
+  assert( p->bufByteCnt - p->bufIdx >= sizeof(cmMidiMsg) );
189
+
190
+  // get a pointer to the next msg in the buffer
191
+  cmMidiMsg* msgPtr = (cmMidiMsg*)(p->buf + p->bufIdx);
192
+
193
+  // fill the buffer msg
194
+  msgPtr->deltaUs = deltaMicroSecs;
195
+  msgPtr->status  = p->status;
196
+
197
+  switch( p->dataCnt )
198
+  {
199
+    case 0: 
200
+      break;
201
+    case 1:
202
+      msgPtr->d0 = d;
203
+      msgPtr->d1 = 0;
204
+      break;
205
+
206
+    case 2:
207
+      msgPtr->d0 = p->data0;
208
+      msgPtr->d1 = d;
209
+      break;
210
+
211
+    default:
212
+      assert(0);
213
+  }
214
+
215
+  // update the msg count and next buffer 
216
+  ++p->msgCnt;
217
+
218
+  p->bufIdx += sizeof(cmMidiMsg);
219
+
220
+}
221
+
222
+void cmMpParseMidiData( cmMpParserH_t h, unsigned deltaMicroSecs, const cmMidiByte_t* iBuf, unsigned iByteCnt )
223
+{
224
+  
225
+  cmMpParser_t* p = _cmMpParserFromHandle(h);
226
+
227
+  if( p == NULL )
228
+    return;
229
+  
230
+  const cmMidiByte_t* ip = iBuf;
231
+  const cmMidiByte_t* ep  = iBuf + iByteCnt;
232
+
233
+  for(; ip < ep; ++ip )
234
+  {
235
+    // if this byte is a status byte
236
+    if( cmMidiIsStatus(*ip) )
237
+    {
238
+      if( p->state != kExpectStatusStId && p->state != kExpectStatusOrDataStId )
239
+        ++p->errCnt;
240
+
241
+      p->status  = *ip;
242
+      p->dataCnt = cmMidiStatusToByteCount(*ip);
243
+
244
+      switch( p->status )
245
+      {
246
+        case kSysExMdId: // if this is the start of a sys-ex msg ...
247
+          // ... clear the buffer to prepare from sys-ex data
248
+          _cmMpTransmitChMsgs(p); 
249
+
250
+          p->state   = kExpectEOXStId;
251
+          p->dataCnt = cmInvalidCnt;
252
+          p->dataIdx = cmInvalidIdx;
253
+          p->buf[ p->bufIdx++ ] =  kSysExMdId;
254
+          break;
255
+
256
+        case kSysComEoxMdId: // if this is the end of a sys-ex msg
257
+          assert( p->bufIdx < p->bufByteCnt );
258
+          p->buf[p->bufIdx++] = *ip; 
259
+          _cmMpTransmitSysEx(p);
260
+          p->state = kExpectStatusStId;
261
+          break;
262
+
263
+        default: // ... otherwise it is a 1,2, or 3 byte msg status
264
+          if( p->dataCnt > 0 )
265
+          {
266
+            p->state   = kExpectDataStId;
267
+            p->dataIdx = 0;
268
+          }
269
+          else
270
+          {
271
+            // this is a status only msg - store it
272
+            _cmMpParserStoreChMsg(p,deltaMicroSecs,*ip);
273
+
274
+            p->state   = kExpectStatusStId;
275
+            p->dataIdx = cmInvalidIdx;
276
+            p->dataCnt = cmInvalidCnt;
277
+          }
278
+
279
+      }
280
+
281
+      continue;
282
+    }
283
+
284
+    // at this point the current byte (*ip) is a data byte
285
+
286
+    switch(p->state)
287
+    {
288
+
289
+      case kExpectStatusOrDataStId:
290
+        assert( p->dataIdx == 0 );
291
+          
292
+      case kExpectDataStId:
293
+        switch( p->dataIdx )
294
+        {
295
+          case 0: // expecting data byte 0 ...
296
+            
297
+            switch( p->dataCnt )
298
+            {
299
+              case 1: // ... of a 1 byte msg - the msg is complete
300
+                _cmMpParserStoreChMsg(p,deltaMicroSecs,*ip);
301
+                p->state = kExpectStatusOrDataStId; 
302
+                break;
303
+
304
+              case 2: // ... of a 2 byte msg - prepare to recv the second data byte
305
+                p->state   = kExpectDataStId;
306
+                p->dataIdx = 1;
307
+                p->data0   = *ip;
308
+                break;
309
+
310
+              default:
311
+                assert(0);
312
+            }
313
+            break;
314
+
315
+          case 1: // expecting data byte 1 of a two byte msg
316
+            assert( p->dataCnt == 2 );
317
+            assert( p->state == kExpectDataStId );
318
+
319
+            _cmMpParserStoreChMsg(p,deltaMicroSecs,*ip);
320
+            p->state   = kExpectStatusOrDataStId;
321
+            p->dataIdx = 0;
322
+            break;
323
+
324
+          default:
325
+            assert(0);            
326
+            
327
+        }
328
+        break;
329
+
330
+      case kExpectEOXStId:
331
+          assert( p->bufIdx < p->bufByteCnt );
332
+
333
+          p->buf[p->bufIdx++] = *ip;
334
+
335
+          // if the buffer is full - then transmit it
336
+          if( p->bufIdx == p->bufByteCnt )
337
+            _cmMpTransmitSysEx(p);
338
+
339
+        break;
340
+
341
+    }
342
+
343
+  } // ip loop
344
+
345
+  _cmMpTransmitChMsgs(p);
346
+ 
347
+}
348
+
349
+cmMpRC_t      cmMpParserInstallCallback( cmMpParserH_t h, cmMpCallback_t  cbFunc, void* cbDataPtr )
350
+{
351
+  cmMpParser_t*   p        = _cmMpParserFromHandle(h);
352
+  cmMpParserCb_t* newCbPtr = cmMemAllocZ( cmMpParserCb_t, 1 );
353
+  cmMpParserCb_t* c        = p->cbChain;
354
+  
355
+  newCbPtr->cbFunc    = cbFunc;
356
+  newCbPtr->cbDataPtr = cbDataPtr;
357
+  newCbPtr->linkPtr   = NULL;
358
+
359
+  if( p->cbChain == NULL )
360
+    p->cbChain = newCbPtr;
361
+  else  
362
+  {
363
+    while( c->linkPtr != NULL )
364
+      c = c->linkPtr;
365
+
366
+    c->linkPtr = newCbPtr;
367
+  }
368
+  
369
+  return kOkMpRC;
370
+}
371
+
372
+cmMpRC_t      cmMpParserRemoveCallback(  cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
373
+{
374
+  cmMpParser_t*   p = _cmMpParserFromHandle(h);
375
+  cmMpParserCb_t* c1 = p->cbChain;  // target link
376
+  cmMpParserCb_t* c0 = NULL;        // link pointing to target
377
+
378
+  // search for the cbFunc to remove
379
+  for(; c1!=NULL; c1=c1->linkPtr)
380
+  {
381
+    if( c1->cbFunc == cbFunc && c1->cbDataPtr == cbDataPtr)
382
+      break;
383
+
384
+    c0 = c1;
385
+  }
386
+
387
+  // if the cbFunc was not found
388
+  if( c1 == NULL )
389
+    return cmErrMsg(&p->err,kCbNotFoundMpRC,"Unable to locate the callback function %p for removal.",cbFunc);
390
+
391
+  // the cbFunc to remove was found
392
+
393
+  // if it was the first cb in the chain
394
+  if( c0 == NULL )
395
+    p->cbChain = c1->linkPtr;
396
+  else
397
+    c0->linkPtr = c1->linkPtr;
398
+
399
+  cmMemFree(c1);
400
+  
401
+  return kOkMpRC;
402
+}
403
+
404
+bool cmMpParserHasCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr )
405
+{
406
+  cmMpParser_t*   p = _cmMpParserFromHandle(h);
407
+  cmMpParserCb_t* c = p->cbChain;  // target link
408
+
409
+  // search for the cbFunc to remove
410
+  for(; c!=NULL; c=c->linkPtr)
411
+    if( c->cbFunc == cbFunc && c->cbDataPtr == cbDataPtr )
412
+      return true;
413
+
414
+  return false;
415
+}
416
+
417
+//====================================================================================================
418
+//
419
+//
420
+
421
+void cmMpTestPrint( void* userDataPtr, const char* fmt, va_list vl )
422
+{
423
+  vprintf(fmt,vl);
424
+}
425
+
426
+void cmMpTestCb( const cmMidiPacket_t* pktArray, unsigned pktCnt )
427
+{
428
+  unsigned i,j;
429
+  for(i=0; i<pktCnt; ++i)
430
+  {
431
+    const cmMidiPacket_t* p = pktArray + i;
432
+
433
+    for(j=0; j<p->msgCnt; ++j)
434
+      if( p->msgArray != NULL )
435
+        printf("%8i 0x%x %i %i\n", p->msgArray[j].deltaUs/1000, p->msgArray[j].status,p->msgArray[j].d0, p->msgArray[j].d1);
436
+      else
437
+        printf("0x%x ",p->sysExMsg[j]);
438
+
439
+  }
440
+}
441
+
442
+void cmMpTest( cmRpt_t* rpt )
443
+{
444
+  char ch;
445
+  unsigned parserBufByteCnt = 1024;
446
+  cmMpInitialize(cmMpTestCb,NULL,parserBufByteCnt,"app",rpt);
447
+  cmMpReport(rpt);
448
+
449
+  
450
+  cmRptPrintf(rpt,"<return> to continue\n");
451
+
452
+  while((ch = getchar()) != 'q')
453
+  {
454
+    cmMpDeviceSend(0,0,0x90,60,60);
455
+  }
456
+
457
+  cmMpFinalize();
458
+}

+ 89
- 0
cmMidiPort.h View File

@@ -0,0 +1,89 @@
1
+#ifndef cmMidiPort_h
2
+#define cmMidiPort_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  typedef unsigned cmMpRC_t;
9
+
10
+  // Flags used to identify input and output ports on MIDI devices
11
+  enum 
12
+  { 
13
+    kInMpFl  = 0x01, 
14
+    kOutMpFl = 0x02 
15
+  };
16
+
17
+
18
+  enum
19
+  {
20
+    kOkMpRC = cmOkRC,
21
+    kCfStringErrMpRC,
22
+    kSysErrMpRC,
23
+    kInvalidArgMpRC,
24
+    kMemAllocFailMpRC,
25
+    kNotImplMpRC,
26
+    kCbNotFoundMpRC
27
+
28
+  };
29
+
30
+  typedef void (*cmMpCallback_t)( const cmMidiPacket_t* pktArray, unsigned pktCnt );
31
+
32
+
33
+  //===============================================================================================
34
+  // MIDI Parser
35
+  //
36
+
37
+  typedef cmHandle_t cmMpParserH_t;
38
+
39
+  // 'cbFunc' and 'cbDataPtr' are optional.  If 'cbFunc' is not supplied in the call to
40
+  // cmMpParserCreate() it may be supplied later by cmMpParserInstallCallback().
41
+  // 'bufByteCnt' defines is the largest complete system-exclusive message the parser will 
42
+  // by able to transmit. System-exclusive messages larger than this will be broken into 
43
+  // multiple sequential callbacks. 
44
+  cmMpParserH_t cmMpParserCreate( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr, unsigned bufByteCnt, cmRpt_t* rpt );
45
+  void          cmMpParserDestroy(    cmMpParserH_t* hp );
46
+  unsigned      cmMpParserErrorCount( cmMpParserH_t h );
47
+  void          cmMpParseMidiData(    cmMpParserH_t h, unsigned deltaMicroSecs, const cmMidiByte_t* buf, unsigned bufByteCnt );
48
+
49
+  // Install/Remove additional callbacks.
50
+  cmMpRC_t      cmMpParserInstallCallback( cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr );
51
+  cmMpRC_t      cmMpParserRemoveCallback(  cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr );
52
+
53
+  // Returns true if the parser uses the given callback.
54
+  bool          cmMpParserHasCallback(     cmMpParserH_t h, cmMpCallback_t cbFunc, void* cbDataPtr );
55
+
56
+
57
+  //===============================================================================================
58
+  // MIDI Device Interface
59
+  //
60
+
61
+  // 'cbFunc' and 'cbDataPtr' are optional (they may be set to NULL).  In this case
62
+  // 'cbFunc' and 'cbDataPtr' may be set in a later call to cmMpInstallCallback().
63
+  cmMpRC_t    cmMpInitialize( cmMpCallback_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr, cmRpt_t* rpt );
64
+  cmMpRC_t    cmMpFinalize();
65
+  bool        cmMpIsInitialized();
66
+
67
+  unsigned    cmMpDeviceCount();
68
+  const char* cmMpDeviceName(       unsigned devIdx );
69
+  unsigned    cmMpDevicePortCount(  unsigned devIdx, unsigned flags );
70
+  const char* cmMpDevicePortName(   unsigned devIdx, unsigned flags, unsigned portIdx );
71
+  cmMpRC_t    cmMpDeviceSend(       unsigned devIdx, unsigned portIdx, cmMidiByte_t st, cmMidiByte_t d0, cmMidiByte_t d1 );
72
+  cmMpRC_t    cmMpDeviceSendData(   unsigned devIdx, unsigned portIdx, const cmMidiByte_t* dataPtr, unsigned byteCnt );
73
+
74
+  // Set devIdx to -1 to assign the callback to all devices.
75
+  // Set portIdx to -1 to assign the callback to all ports on the specified devices.
76
+  // 
77
+  cmMpRC_t    cmMpInstallCallback( unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr );
78
+  cmMpRC_t    cmMpRemoveCallback(  unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr );
79
+  bool        cmMpUsesCallback(    unsigned devIdx, unsigned portIdx, cmMpCallback_t cbFunc, void* cbDataPtr );
80
+
81
+  void        cmMpReport( cmRpt_t* rpt );
82
+
83
+  void cmMpTest( cmRpt_t* rpt );
84
+
85
+#ifdef __cplusplus
86
+}
87
+#endif
88
+
89
+#endif

+ 122
- 0
cmMsgProtocol.c View File

@@ -0,0 +1,122 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmRpt.h"
5
+#include "cmErr.h"
6
+#include "cmCtx.h"
7
+#include "cmJson.h"
8
+#include "dsp/cmDspValue.h"
9
+#include "cmMsgProtocol.h"
10
+
11
+// buffer layout is:
12
+// [ cmDspUiHdr_t <data> ]
13
+// The format of the <data> is determiend by hdr.value.
14
+// Since hdr.value is the last field in the cmDspUiHdr_t record
15
+// the data follows this value.  
16
+cmMsgRC_t cmMsgSend( 
17
+  cmErr_t* err, 
18
+  unsigned asSubIdx,
19
+  unsigned msgTypeId,
20
+  unsigned selId,
21
+  unsigned flags,
22
+  unsigned instId,
23
+  unsigned instVarId,
24
+  const cmDspValue_t* valPtr,
25
+  cmMsgSendFuncPtr_t sendFunc,
26
+  void* cbDataPtr )
27
+{
28
+  unsigned bufByteCnt  = sizeof(cmDspUiHdr_t);
29
+  unsigned dataByteCnt = 0;
30
+
31
+  if( valPtr != NULL )
32
+   dataByteCnt = cmDsvSerialDataByteCount(valPtr);
33
+
34
+  bufByteCnt += dataByteCnt;
35
+
36
+  char          buf[ bufByteCnt ];
37
+  cmDspUiHdr_t* hdr = (cmDspUiHdr_t*)buf;
38
+
39
+  hdr->asSubIdx  = asSubIdx;
40
+  hdr->uiId      = msgTypeId;  // see kXXXSelAsId identifiers in cmAudioSys.h
41
+  hdr->selId     = selId;      // if msgTypeId==kUiSelAsId then see kXXXDuId in cmDspUi.h
42
+  hdr->flags     = flags;
43
+  hdr->instId    = instId;
44
+  hdr->instVarId = instVarId;
45
+
46
+  if( valPtr == NULL )
47
+    cmDsvSetNull(&hdr->value);
48
+  else
49
+  {
50
+    // this function relies on the 'hdr.value' field being the last field in the 'hdr'.
51
+    if( cmDsvSerialize( valPtr, &hdr->value, sizeof(cmDspValue_t) + dataByteCnt) != kOkDsvRC )
52
+      return cmErrMsg(err,kSerializeFailMsgRC,"An attempt to serialize a UI msg failed.");
53
+  }
54
+
55
+  const void* vp = buf;
56
+  if( sendFunc(cbDataPtr,bufByteCnt,vp) != cmOkRC )
57
+    return cmErrMsg(err,kSendFailMsgRC,"An attempt to transmit a msg to the host failed.");
58
+  
59
+
60
+  return kOkMsgRC;
61
+  
62
+}
63
+
64
+// Return the unsigned value at the specified byte offset into the msg buffer.
65
+cmMsgRC_t  _cmMsgPeekAtUInt( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned offset, unsigned* retValPtr )
66
+{
67
+  unsigned i,j,k;
68
+  
69
+  for(k=0,i=0; i<segCnt; ++i)
70
+    for(j=0; j<msgByteCntArray[i]; ++j,++k)
71
+      if( k == offset )
72
+        break;
73
+  
74
+  if( i == segCnt )
75
+    return kDecodeFailMsgRC;
76
+
77
+  *retValPtr = *((unsigned*)((char*)(msgArray[i]) + j));
78
+  return kOkMsgRC;    
79
+}
80
+
81
+cmMsgRC_t cmMsgPeekAsSubIndex( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
82
+{ 
83
+  cmDspUiHdr_t h;
84
+  unsigned offset = ((char*)(&h.asSubIdx)) - ((char*)&h);
85
+  return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
86
+}
87
+
88
+cmMsgRC_t cmMsgPeekMsgTypeId(  const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
89
+{ 
90
+  cmDspUiHdr_t h;
91
+  unsigned offset = ((char*)(&h.uiId)) - ((char*)&h);
92
+  return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
93
+}
94
+
95
+cmMsgRC_t cmMsgPeekSelId(      const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
96
+{ 
97
+  cmDspUiHdr_t h;
98
+  unsigned offset = ((char*)(&h.selId)) - ((char*)&h);
99
+  return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
100
+}
101
+
102
+cmMsgRC_t cmMsgPeekFlags(      const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
103
+{ 
104
+  cmDspUiHdr_t h;
105
+  unsigned offset = ((char*)(&h.flags)) - ((char*)&h);
106
+  return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
107
+}
108
+
109
+cmMsgRC_t cmMsgPeekInstId(     const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
110
+{ 
111
+  cmDspUiHdr_t h;
112
+  unsigned offset = ((char*)(&h.instId)) - ((char*)&h);
113
+  return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
114
+}
115
+
116
+cmMsgRC_t cmMsgPeekInstVarId(  const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr )
117
+{ 
118
+  cmDspUiHdr_t h;
119
+  unsigned offset = ((char*)(&h.instVarId)) - ((char*)&h);
120
+  return _cmMsgPeekAtUInt(msgArray,msgByteCntArray,segCnt,offset,retValPtr);
121
+}
122
+

+ 233
- 0
cmMsgProtocol.h View File

@@ -0,0 +1,233 @@
1
+#ifndef cmMsgProtocol_h
2
+#define cmMsgProtocol_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  #define cmAudDspSys_FILENAME "aud_dsp.js"
9
+
10
+
11
+  /// Reserved DSP message selector id's (second field of all host<->audio system messages)
12
+  enum
13
+  {
14
+    kMidiMsgArraySelAsId = 1000,
15
+    kMidiSysExSelAsId,
16
+    kUiSelAsId,      // indicates a cmDspUiHdr_t msg
17
+    kUiMstrSelAsId,  // indicates a cmDspUiHdr_t msg containing master control information for the audio system
18
+    kSsInitSelAsId,  // indicates the msg is of type cmAudioSysSsInitMsg_t
19
+    kStatusSelAsId,  // indicates the msg is of type cmAudioSysStatus_t
20
+    kNetSyncSelAsId,   // sent with a cmDspNetMsg_t object  
21
+  };
22
+
23
+
24
+
25
+  // UI seletor id's used in the cmDspUiHdr_t selId field
26
+  enum 
27
+  {
28
+    kPrintDuiId,   // ui<--eng print the value to the console
29
+    kSliderDuiId,  // ui<--eng create a slider control
30
+    kButtonDuiId,  // ui<--eng create button control
31
+    kCheckDuiId,   // ui<--eng create a check box control
32
+    kLabelDuiId,   // ui<--end create a label control
33
+    kNumberDuiId,  // ui<--eng create a number box
34
+    kTextDuiId,    // ui<--eng create a text entry control
35
+    kFnameDuiId,   // ui<--eng create a file/directory picker control
36
+    kMsgListDuiId, // ui<--eng create a msg list control
37
+    kMeterDuiId,   // ui<--eng create a meter display
38
+    kValueDuiId,   // ui<->eng a control changed values  
39
+    kColumnDuiId,  // ui<--eng start a new column
40
+    kHBorderDuiId, // ui<--eng insert a vertical border 
41
+    kPageDuiId,    // ui<--eng insert a new control page
42
+    
43
+    kAudioSysCfgDuiId,  // ui<--audio system cfg label      
44
+    kSubSysCntDuiId,    // ui<--eng audio sub-system count
45
+    kDeviceDuiId,       // ui<--eng device label
46
+    kProgramDuiId,      // ui<--eng program label
47
+
48
+    // The following selId's are used by cmAudDsp to indicate various commands.
49
+    kSetAudioCfgDuiId,     // 1) select an audio system setup
50
+    kSetAudioDevDuiId,     // 2) (optional) set an audio device on an audio sub-system
51
+    kSetSampleRateDuiId,   // 3) (optional) set the sample rate of an audio sub-system
52
+    kSetPgmDuiId,          // 4) select a program
53
+    kEnableDuiId,          // 5) enable/disable the audio system (also resets the DSP system)
54
+    kSyncDuiId,            // 6) sent by cmAudDsp to client to indicate sync success or failure.
55
+    kSetNotifyEnableDuiId, // enable/disable periodic status notification from the audio system.
56
+    kClientMsgPollDuiId,   // Periodic check for and deliver messages waiting in the audio system for delivery to the client. 
57
+    kSendMsgDuiId,         // forward msg to the audio system
58
+    kDevReportDuiId,       // print a device report
59
+
60
+    kRightAlignDuiId = 0,  // label alignment id used by kLabelDuiId 
61
+    kLeftAlignDuiId,  
62
+    kCenterAlignDuiId
63
+  };
64
+
65
+  enum
66
+  {
67
+    kDuplexDuiFl = 0x01
68
+  };
69
+
70
+  // Header record for all messages between the host and the DSP controllers.
71
+  typedef struct
72
+  {
73
+    unsigned     asSubIdx;  // the audio sub-system this UI belongs to
74
+    unsigned     uiId;      // msg type kXXXAsId 
75
+    unsigned     selId;     // action to perform see above
76
+    unsigned     flags;     //
77
+    unsigned     instId;    // DSP instance id
78
+    unsigned     instVarId; // DSP instance var id
79
+    
80
+    // The cmDspValue_t field must come last in the structure in
81
+    // order for the cmDsvSerialize() to work in cmDspUI.c:_cmDspUiMsg().
82
+    cmDspValue_t value;     // Data value associated with this msg.                             
83
+  } cmDspUiHdr_t;
84
+
85
+  // All of the UI messages that create a UI control contain an array of integers
86
+  // as in the 'value' field. The array contains the id's associated with
87
+  // the different programmable paramters which are part of the control.
88
+  // For example a slider control has minimum,maximum, step size, and value 
89
+  // parameters. The location in the array is hard coded according to the
90
+  // parameters meaning but the actual value of the id is left up to the 
91
+  // engine. This allows the engine to use whatever values work best for
92
+  // it on a per instance basis. 
93
+
94
+
95
+
96
+  /// The cmDspUiHdr_t.instId of UI control messages associated with master
97
+  /// control encode the device,channel,in/out, and control type. These macros
98
+  /// should be used for encoding and decoding.
99
+#define cmAudioSysFormUiInstId(dev,ch,ifl,ctl) (((dev)<<16) + ((ch)<<4) + ((ifl)<<3) + (ctl))
100
+#define cmAudioSysUiInstIdToDevIndex(instId)  ( (instId) >> 16)
101
+#define cmAudioSysUiInstIdToChIndex(instId)   (((instId) &  0x0000ffff) >> 4)
102
+#define cmAudioSysUiInstIdToInFlag(instId)    ( (instId) &  0x08)
103
+#define cmAudioSysUiInstIdToCtlId(instId)     ( (instId) &  0x07)
104
+
105
+  /// Control id's used to identify the control type of master contols.
106
+  enum
107
+  {
108
+    kSliderUiAsId = 0,
109
+    kMeterUiAsId  = 1,
110
+    kMuteUiAsId   = 2,
111
+    kToneUiAsId   = 3,
112
+    kPassUiAsId   = 4
113
+  };
114
+
115
+
116
+  /// This message is transmitted to the host application just prior to returning
117
+  /// from cmAudioSysInitialize().
118
+  /// When transmitted to the host this record acts as a message header.
119
+  /// This header is followed by two zero terminated char arrays containing the device
120
+  /// labels associated with the input and output devices.
121
+  /// Message Layout: [ cmAudioSysInitMsg_t "In Device Label" "Out Device Label"]
122
+  typedef struct
123
+  {
124
+    unsigned asSubIdx;  ///< asSubIdx of this sub-system
125
+    unsigned selId;     ///< always kSsInitAsId
126
+    unsigned asSubCnt;  ///< count of sub-systems
127
+    unsigned inDevIdx;  ///< input device index
128
+    unsigned outDevIdx; ///< output device index
129
+    unsigned inChCnt;   ///< input device channel count
130
+    unsigned outChCnt;  ///< outut device channel count
131
+  } cmAudioSysSsInitMsg_t;
132
+
133
+  /// Audio sub-system status record - this message can be transmitted to the host at
134
+  /// periodic intervals.  See cmAudioSysStatusNotifyEnable().
135
+  /// When transmitted to the host this record acts as the message header.
136
+  /// This header is followed by two arrays of doubles containing the input and output meter values
137
+  /// associated with the input and output audio devices.
138
+  /// Message Layout: [ asSubIdx kStatusSelId cmAudioSysStatus_t iMeterArray[iMeterCnt] oMeterArray[oMeterCnt] ]
139
+  typedef struct
140
+  {
141
+    unsigned asSubIdx;     ///< originating audio sub-system
142
+
143
+    unsigned updateCnt;    ///< count of callbacks from the audio devices.
144
+    unsigned wakeupCnt;    ///< count of times the audio system thread has woken up after the cond. var has been signaled by the audio update thread.
145
+    unsigned msgCbCnt;     ///< count of msgs delivered via cmAsCallback() .
146
+    unsigned audioCbCnt;   ///< count of times the DSP execution was requested via cmAsCallback().    
147
+
148
+    unsigned iDevIdx;      ///< Input device index
149
+    unsigned oDevIdx;      ///< Output device index
150
+
151
+    unsigned overflowCnt;  ///< count of times the audio input buffers overflowed
152
+    unsigned underflowCnt; ///< count of times the audio output buffers underflowed
153
+    unsigned iMeterCnt;    ///< count of input meter channels
154
+    unsigned oMeterCnt;    ///< count of output meter channels
155
+    
156
+  } cmAudioSysStatus_t;
157
+
158
+  // cmDspNetMsg_t sub-selector id's
159
+  enum {
160
+    kNetHelloSelAsId,     // node->node awake msg 
161
+    kNetDstIdReqSelAsId,  // src->dst request a dst id
162
+    kNetDstIdReqDoneAsId, // src->dst all requests have been sent
163
+    kNetDstIdSelAsId,     // dst->src provide dst id
164
+    kNetDoneSelAsId,      // node->node sync done
165
+    kNetErrSelAsId,       // node->node sync error
166
+    kNetEvtSelAsId        // src->dst send cmDspEvnt_t
167
+  };
168
+
169
+  // Message Layout [ cmDspNetMsg_t dstInstLabel[] dstVarLabel[] ]
170
+  typedef struct
171
+  {
172
+    unsigned asSubIdx;
173
+    unsigned selId;       // kNetSyncSelAsId
174
+    unsigned subSelId;    // see above kNetXXXSelAsId
175
+    unsigned srcId; 
176
+    unsigned dstId;
177
+    cmDspValue_t value;
178
+    // char dstInstLabel[] - with kNetSyncSelAsId only
179
+    // char dstVarLabel[]  - with kNetSyncSelAsId only
180
+  } cmDspNetMsg_t;
181
+
182
+  /*
183
+  typedef struct
184
+  {
185
+    unsigned asSubIdx;
186
+    unsigned selId;     // kNetEvtSelAsId
187
+    unsigned dstId;
188
+
189
+    // The cmDspValue_t field must come last in the structure in
190
+    // order for the cmDsvSerialize() to work.
191
+    cmDspValue_t value;     // Data value associated with this msg.                             
192
+    
193
+  } cmDspNetEvt_t;
194
+  */
195
+
196
+  enum
197
+  {
198
+    kOkMsgRC = cmOkRC,
199
+    kSerializeFailMsgRC,
200
+    kSendFailMsgRC,
201
+    kDecodeFailMsgRC
202
+  };
203
+
204
+  typedef cmRC_t cmMsgRC_t;
205
+
206
+
207
+  typedef cmMsgRC_t (*cmMsgSendFuncPtr_t)(void* cbDataPtr, unsigned msgByteCnt, const void* msg );
208
+
209
+  cmMsgRC_t cmMsgSend( 
210
+  cmErr_t*            err, 
211
+  unsigned            asSubIdx,
212
+  unsigned            msgTypeId,
213
+  unsigned            selId,
214
+  unsigned            flags,
215
+  unsigned            instId,
216
+  unsigned            instVarId,
217
+  const cmDspValue_t* valPtr,
218
+  cmMsgSendFuncPtr_t  sendFunc,
219
+  void*               cbDataPtr );
220
+
221
+  cmMsgRC_t cmMsgPeekAsSubIndex( const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
222
+  cmMsgRC_t cmMsgPeekMsgTypeId(  const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
223
+  cmMsgRC_t cmMsgPeekSelId(      const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
224
+  cmMsgRC_t cmMsgPeekFlags(      const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
225
+  cmMsgRC_t cmMsgPeekInstId(     const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
226
+  cmMsgRC_t cmMsgPeekInstVarId(  const void* msgArray[], unsigned msgByteCntArray[], unsigned segCnt, unsigned* retValPtr );
227
+
228
+#ifdef __cplusplus
229
+}
230
+#endif
231
+
232
+
233
+#endif

+ 83
- 0
cmOp.c View File

@@ -0,0 +1,83 @@
1
+#include "cmPrefix.h"
2
+#include "cmGlobal.h"
3
+#include "cmFloatTypes.h"
4
+#include "cmOp.h"
5
+
6
+void     vs_Zero( cmSample_t v[], unsigned vn)
7
+{ memset(v,0,sizeof(v[0])*vn); }
8
+
9
+
10
+cmReal_t vs_Sine( cmSample_t v[], unsigned vn, cmReal_t hzRad, cmReal_t initPhs )
11
+{
12
+  const cmSample_t* ep  = v + vn;
13
+  double            phs = initPhs;
14
+
15
+  while(v<ep)
16
+  {
17
+    *v++ = (cmSample_t)sin( phs );
18
+    phs += hzRad;
19
+  }
20
+
21
+  return (cmReal_t)phs;
22
+}
23
+
24
+void vs_Rand( cmSample_t v[], unsigned vn, cmSample_t min, cmSample_t max )
25
+{
26
+  const cmSample_t* ep = v + vn;
27
+  while(v<ep)
28
+    *v++ = ((cmSample_t)rand()/RAND_MAX) * (max-min) + min;
29
+
30
+}
31
+
32
+void     vs_MultVVS(   cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult )
33
+{
34
+  const cmSample_t* ep = d + n;
35
+  while(d<ep)
36
+    *d++ = *s++ * mult;
37
+}
38
+
39
+void     vs_SumMultVVS(   cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult )
40
+{
41
+  const cmSample_t* ep = d + n;
42
+  while(d<ep)
43
+    *d++ += *s++ * mult;
44
+}
45
+
46
+
47
+void     vs_Copy(   cmSample_t d[], const cmSample_t s[], unsigned n )
48
+{
49
+  memcpy(d,s,n*sizeof(d[0]));
50
+}
51
+
52
+cmSample_t     vs_SquaredSum(   const cmSample_t s[], unsigned n )
53
+{
54
+  cmSample_t        sum = 0;
55
+  const cmSample_t* ep  = s + n;
56
+
57
+  for(;s<ep;++s)
58
+    sum += *s * *s;
59
+  return sum;
60
+}
61
+
62
+
63
+/*
64
+unsigned vs_Interp2( cmSample_t v[], unsigned vn, const cmSample_t[] xx, const cmSample_t y[], unsigned yn )
65
+{
66
+
67
+  unsigned i = 0;
68
+  for(; i<vn; ++i)
69
+  {
70
+    double   x  = fmod(*xx++,yn);
71
+    unsigned x0 = floor(x);
72
+    unsigned x1 = x0 + 1;
73
+    double   d  = x - x0;
74
+
75
+    if( x1>=yn || x0>=yn)
76
+      break;
77
+
78
+    *v++ = y[x0] + (y[x1] - y[x0]) * d;
79
+  }
80
+
81
+  return i;
82
+}
83
+*/

+ 30
- 0
cmOp.h View File

@@ -0,0 +1,30 @@
1
+#ifndef cmOp_h
2
+#define cmOp_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  void     vs_Zero(   cmSample_t v[], unsigned vn);
9
+  cmReal_t vs_Sine(   cmSample_t v[], unsigned vn, cmReal_t hzRad, cmReal_t initPhs );
10
+  void     vs_Rand(   cmSample_t v[], unsigned vn, cmSample_t min, cmSample_t max );
11
+
12
+  void     vs_Copy(   cmSample_t d[], const cmSample_t s[], unsigned n );
13
+
14
+  cmSample_t vs_SquaredSum( const cmSample_t s[], unsigned n );
15
+
16
+  // d[] = s[] * mult;
17
+  void     vs_MultVVS(   cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult );
18
+
19
+  //d[] += s[] * mult
20
+  void     vs_SumMultVVS(cmSample_t d[], const cmSample_t s[], unsigned n, cmReal_t mult ); 
21
+
22
+  // Interpolate the values of y[yn] at the points defined by x[vn] and store the result in v[vn].
23
+  // User linear interpolation.
24
+  //void     vs_Interp2( cmSample_t v[], unsigned vn, const cmSample_t[] x, const cmSample_t y[], unsigned yn );
25
+
26
+#ifdef __cplusplus
27
+}
28
+#endif
29
+
30
+#endif

+ 1149
- 0
cmPgmOpts.c
File diff suppressed because it is too large
View File


+ 191
- 0
cmPgmOpts.h View File

@@ -0,0 +1,191 @@
1
+#ifndef cmPgmOpts_h
2
+#define cmPgmOpts_h
3
+
4
+//{
5
+//(
6
+// Command line program option syntax:
7
+//
8
+//
9
+// '-'<charId>*  <value>+  - a dash followed by one or more one character id's optionally followed by a parameter value.
10
+// '--'<wordId> <value>+   - a double dash followed by a word id optionally followed by a parameter value.
11
+// 
12
+//  A char id is a single character.
13
+//  A word id is a string of characters with no intervening white space.
14
+//
15
+// Notes:
16
+// 1) There is no way to give multiple <values> without an intervening character or word id.
17
+//    A <value>  must therefore always be immediately preceded by an id.
18
+// 2) There must never be a space between the dash(es) and the characters forming the identifier.
19
+// 3) There must always be a space between the identifier and any subsequent <value>.
20
+// 4) See src/mas/src/main.c for a complete example.
21
+//
22
+// Terms:
23
+// Parameter - Description of the allowable types and constraints for a program option.
24
+// Argument  - An instance of a parameter or the values associated with a parameter.
25
+// 
26
+//)
27
+
28
+#ifdef __cplusplus
29
+extern "C" {
30
+#endif
31
+
32
+  //(
33
+
34
+  // cmPgmOpts Result Codes
35
+  enum
36
+  {
37
+    kOkPoRC = cmOkRC,
38
+    kSyntaxErrPoRC,
39
+    kInvalidIdxPoRC,
40
+    kParseFailPoRC,
41
+    kNoReqArgPoRC,
42
+    kDuplicateIdPoRC,
43
+    kArgCntErrPoRC,
44
+    kParmNotFoundPoRC,
45
+    kInstNotFoundPoRC,
46
+    kTypeErrPoRC
47
+  };
48
+
49
+  // cmPgmOpts parameter id's
50
+  enum
51
+  {
52
+    kPrintHelpPoId,
53
+    kVersionPoId,
54
+    kPrintParmsPoId,
55
+    kNoExecPoId,
56
+    kBasePoId
57
+  };
58
+
59
+  typedef cmRC_t     cmPoRC_t;
60
+  typedef cmHandle_t cmPgmOptH_t;
61
+  
62
+  extern cmPgmOptH_t cmPgmOptNullHandle;
63
+
64
+  // Initialize a program options parser.
65
+  cmPoRC_t cmPgmOptInitialize(cmCtx_t* c, cmPgmOptH_t* hp, const cmChar_t* helpBegStr, const cmChar_t* helpEndStr );
66
+
67
+  // Finalize a program options parser.
68
+  cmPoRC_t cmPgmOptFinalize( cmPgmOptH_t* hp );
69
+
70
+  // Return true if the program options parser was successfully initialized.
71
+  bool     cmPgmOptIsValid( cmPgmOptH_t h );
72
+
73
+  // Flag used by the 'flags' arg. to cmPgmOptInstall().
74
+  enum { 
75
+    kNoPoFlags = 0x000, 
76
+    kReqPoFl   = 0x001,  // this is a required parameter
77
+    kBoolPoFl  = 0x002,  // this parameter takes a value
78
+    kCharPoFl  = 0x004,  // parm. value is a character
79
+    kIntPoFl   = 0x008,  // parm. value is a decimal int
80
+    kUIntPoFl  = 0x010,  // parm. value is a decimal unsigned int
81
+    kHexPoFl   = 0x020,  // parm. value is a hex. unsigned int 
82
+    kDblPoFl   = 0x040,  // parm. value is a decimal double
83
+    kStrPoFl   = 0x080,  // parm. value is a string (default)
84
+    kEnumPoFl  = 0x100,  // parm. valus is a enum type (automatically set by a non-zero enumId)
85
+
86
+    kTypeMaskPoFl = 0x1f6
87
+  };
88
+
89
+
90
+  // Define a parameter.
91
+  //
92
+  // unsigned        numId,   - numeric id used to identify this parameter 
93
+  // const cmChar_t  charId,  - a character used to identify this parameter
94
+  // const cmChar_t* wordId,  - a label used to identify this parameter
95
+  // unsigned        flags,   - kNoPoFlags | kReqPoFl (the type flags are automatically assigned)
96
+  // unsigned        enumId,  - non-zero value used to group enumerated parameter values  (ignored for non-enum types)
97
+  // unsigned        cnt,     - count of times this parameter may repeated or 0 for an unlimited repetitions
98
+  // const cmChar_t* helpStr  - a textual description of this parameter
99
+  //
100
+  // Notes
101
+  // 1) 'numId','charId', and 'wordId' must all be unique among all parameter definitions.
102
+  // 2) If no  parameter value type flag is given then the type is assumed to be of type bool.
103
+  // 3) For all parameter value types except the string type arguments are automatically parsed to the
104
+  //    defined type. To avoid automatic parsing simply define the type as a string (using cmPgmOptInstallStr()).
105
+  // 4) All expected parameters must be defined prior to calling cmPgmOptParse().
106
+  // 5) One call to cmPgmOPtInstallEnum() is made for each possible enumeration value - where the 'enumId' gives the value.
107
+  //    A given set of associated enum values is grouped by giving a common 'numId'.
108
+  //    Example:
109
+  //     cmPgmOptInstallEnum(h,colorId,...,redId,...); 
110
+  //     cmPgmOptInstallEnum(h,colorId,...,greenId,...); 
111
+  //     cmPgmOptInstallEnum(h,colorId,...,blueId,...);   
112
+  //
113
+  // 6) The following id's are used for built-in actions and are therefore restricted from use by the client:
114
+  //    a. -h --help    Print the program usage information.
115
+  //    b. -v --version Print the program version informatoin.
116
+  //    c. -p --parms   Print the program parameter values.
117
+  //
118
+  cmPoRC_t cmPgmOptInstallChar(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, cmChar_t        dfltVal, cmChar_t*        retValPtr, unsigned cnt, const cmChar_t* helpStr );
119
+  cmPoRC_t cmPgmOptInstallBool(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, bool            dfltVal, bool*            retValPtr, unsigned cnt, const cmChar_t* helpStr );
120
+  cmPoRC_t cmPgmOptInstallInt( cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, int             dfltVal, int*             retValPtr, unsigned cnt, const cmChar_t* helpStr );
121
+  cmPoRC_t cmPgmOptInstallUInt(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, unsigned        dfltVal, unsigned*        retValPtr, unsigned cnt, const cmChar_t* helpStr );
122
+  cmPoRC_t cmPgmOptInstallDbl( cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, double          dfltVal, double*          retValPtr, unsigned cnt, const cmChar_t* helpStr );
123
+  cmPoRC_t cmPgmOptInstallStr( cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, const cmChar_t* dfltVal, const cmChar_t** retValPtr, unsigned cnt, const cmChar_t* helpStr );
124
+  cmPoRC_t cmPgmOptInstallEnum(cmPgmOptH_t h, unsigned numId, cmChar_t charId, const cmChar_t* worldId, unsigned flags, unsigned enumId, unsigned dfltVal, unsigned* retValPtr, unsigned cnt, const cmChar_t* helpStr  );
125
+
126
+  // Parse a set of command line arguments.
127
+  cmPoRC_t cmPgmOptParse( cmPgmOptH_t h, unsigned argCnt, char* argArray[] );
128
+  
129
+  // Get the total count of arguments passed to cmPgmOptParse().
130
+  unsigned cmPgmOptArgCount( cmPgmOptH_t h);
131
+  
132
+  // Get the numeric id associated with each argument.
133
+  unsigned cmPgmOptNumId( cmPgmOptH_t h, unsigned argIdx );
134
+
135
+  // Manually convert each argument string into the specified type.
136
+  // These functions are useful if all of the parameters were defined using cmPgmOptInstallStr().
137
+  // Use cmPgmOptRC() to check for errors.
138
+  char        cmPgmOptParseArgChar(cmPgmOptH_t h, unsigned argIdx );
139
+  bool        cmPgmOptParseArgBool(cmPgmOptH_t h, unsigned argIdx );
140
+  int         cmPgmOptParseArgInt( cmPgmOptH_t h, unsigned argIdx );
141
+  unsigned    cmPgmOptParseArgUInt(cmPgmOptH_t h, unsigned argIdx );
142
+  double      cmPgmOptParseArgDbl( cmPgmOptH_t h, unsigned argIdx );
143
+  const char* cmPgmOptParseArgStr( cmPgmOptH_t h, unsigned argIdx );
144
+
145
+  // Get the count of arg's for a given parameter.
146
+  unsigned    cmPgmOptParmArgCount( cmPgmOptH_t h, unsigned numId );
147
+
148
+  // Get the value associated with each parsed argument.
149
+  // If no argument was given for the requested parameter 
150
+  // (cmPgmOptParmArgCount(numId)==0) and 'instIdx' == 0 then the default value is returned. 
151
+  // Use cmPgOptRC() to check for errors.
152
+  //
153
+  // The parameter identified by numId must has been defined by an earlier call to
154
+  // cmPgmOptInstallChar() or this function
155
+  char        cmPgmOptArgChar(   cmPgmOptH_t h, unsigned numId, unsigned instIdx );
156
+
157
+  // No matter the type of the parameter it will be converted to a bool.
158
+  bool        cmPgmOptArgBool(   cmPgmOptH_t h, unsigned numId, unsigned instIdx );
159
+
160
+  // All types, except strings, are converted to type int. Doubles are rounded.
161
+  int         cmPgmOptArgInt(    cmPgmOptH_t h, unsigned numId, unsigned instIdx );
162
+
163
+  // All types, except strings, are converted to type unsigned. Doubles are rounded.
164
+  unsigned    cmPgmOptArgUInt(   cmPgmOptH_t h, unsigned numId, unsigned instIdx );
165
+
166
+  // All types except strings, are converted to double.
167
+  double      cmPgmOptArgDbl(    cmPgmOptH_t h, unsigned numId, unsigned instIdx );
168
+
169
+  // If the parameter is not defined as a string then the arg. string value us returned.
170
+  const char* cmPgmOptArgStr(    cmPgmOptH_t h, unsigned numId, unsigned instIdx );
171
+  
172
+  
173
+  // Get and set the current result code.
174
+  cmPoRC_t    cmPgmOptRC( cmPgmOptH_t h, cmPoRC_t rc );
175
+
176
+  // Returns 'true' only if non- built-in options were selected
177
+  bool cmPgmOptHandleBuiltInActions( cmPgmOptH_t h, cmRpt_t* rpt );
178
+
179
+  void cmPgmOptPrintHelp(    cmPgmOptH_t h, cmRpt_t* rpt );
180
+  void cmPgmOptPrintVersion( cmPgmOptH_t h, cmRpt_t* rpt );
181
+  void cmPgmOptPrintParms(   cmPgmOptH_t h, cmRpt_t* rpt );
182
+
183
+  //)
184
+  //}
185
+
186
+#ifdef __cplusplus
187
+}
188
+#endif
189
+
190
+
191
+#endif

+ 0
- 0
cmPrefix.h View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save