Kaynağa Gözat

cmSyncRecd.h/c : Initial implementation of MIDI / Audio synchronized recorder.

Recording has been tested but reading the recorded files has not. Initial commit.
master
kevin 10 yıl önce
ebeveyn
işleme
0ae13b9fc7
2 değiştirilmiş dosya ile 469 ekleme ve 0 silme
  1. 437
    0
      cmSyncRecd.c
  2. 32
    0
      cmSyncRecd.h

+ 437
- 0
cmSyncRecd.c Dosyayı Görüntüle

@@ -0,0 +1,437 @@
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 "cmTime.h"
9
+#include "cmFile.h"
10
+#include "cmAudioFile.h"
11
+#include "cmSyncRecd.h"
12
+
13
+
14
+typedef enum
15
+{
16
+kInvalidSrId,
17
+  kMidiSrId,
18
+  kAudioSrId
19
+  } cmSrTypeId_t;
20
+
21
+typedef struct cmSrMidi_str
22
+{
23
+  cmTimeSpec_t timestamp;
24
+  unsigned     status;
25
+  unsigned     d0;
26
+  unsigned     d1;
27
+} cmSrMidi_t;
28
+
29
+typedef struct cmSrAudio_str
30
+{
31
+  cmTimeSpec_t timestamp;
32
+  unsigned     smpIdx;
33
+} cmSrAudio_t;
34
+
35
+typedef struct cmSrRecd_str
36
+{
37
+  cmSrTypeId_t tid;
38
+  union
39
+  {
40
+    cmSrMidi_t  m;
41
+    cmSrAudio_t a;
42
+  } u;
43
+
44
+} cmSrRecd_t;
45
+
46
+
47
+enum
48
+{
49
+  kReadSrFl = 0x01,   // This is a read (not a write) file
50
+
51
+  kFileUUSrId = 0xf00d
52
+};
53
+
54
+typedef struct cmSr_str
55
+{
56
+  cmErr_t        err;
57
+  cmFileH_t      fH;
58
+  cmAudioFileH_t afH;
59
+  unsigned       flags;  // See kXXXSrFl 
60
+  cmSrRecd_t*    cache;  // cache[cn]
61
+  cmSrAudio_t*   map;    // 
62
+  unsigned       cn;     // count of records in cache[].
63
+  unsigned       ci;     // next cache recd index
64
+  unsigned       fn;     // count of recds written to file
65
+  long           offs;
66
+} cmSr_t;
67
+
68
+
69
+
70
+
71
+cmSr_t* _cmSrHtoP( cmSyncRecdH_t h )
72
+{
73
+  cmSr_t* p = (cmSr_t*)h.h;
74
+  assert(p!=NULL);
75
+  return p;
76
+}
77
+
78
+cmSrRC_t _cmSrWriteCache( cmSr_t* p )
79
+{
80
+  if( cmFileWrite(p->fH,p->cache,p->ci * sizeof(cmSrRecd_t)) != kOkFileRC )
81
+    return cmErrMsg(&p->err,kFileFailSrRC,"File write failed.");
82
+
83
+  p->fn += p->ci;
84
+  p->ci = 0;
85
+  
86
+  return kOkSrRC;
87
+}
88
+
89
+
90
+cmSrRC_t _cmSrFinal( cmSr_t* p )
91
+{
92
+  cmSrRC_t rc = kOkSrRC;
93
+  
94
+  // write any remaining cache records
95
+  if( cmIsFlag(p->flags,kReadSrFl) == false )
96
+  {
97
+    if((rc = _cmSrWriteCache(p)) == kOkSrRC )
98
+    {
99
+      if(cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
100
+        rc = cmErrMsg(&p->err,kFileFailSrRC, "File seek fail on file offset positioning.");
101
+      else
102
+        if(cmFileWriteUInt(p->fH,&p->fn,1) != kOkFileRC )
103
+          rc = cmErrMsg(&p->err,kFileFailSrRC, "File write failed on record count.");
104
+    }
105
+  }
106
+
107
+  // release the audio file object
108
+  if( cmAudioFileIsValid(p->afH) )
109
+    if( cmAudioFileDelete(&p->afH) != kOkAfRC )
110
+      return cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file object delete failed.");
111
+
112
+  // release the sync-recd file object
113
+  if( cmFileIsValid(p->fH) )
114
+    if( cmFileClose(&p->fH) != kOkFileRC )
115
+      return cmErrMsg(&p->err,kFileFailSrRC,"File close failed.");
116
+
117
+  cmMemFree(p->cache);
118
+  cmMemFree(p->map);
119
+  cmMemFree(p);
120
+
121
+  return rc;
122
+}
123
+
124
+cmSr_t*  _cmSrAlloc( cmCtx_t* ctx, unsigned flags )
125
+{
126
+  cmSr_t* p = cmMemAllocZ(cmSr_t,1);
127
+
128
+  cmErrSetup(&p->err,&ctx->rpt,"SyncRecd");
129
+
130
+  p->cn    = 2048;
131
+  p->cache = cmMemAllocZ(cmSrRecd_t,p->cn);
132
+
133
+  return p;
134
+}
135
+
136
+cmSrRC_t cmSyncRecdCreate( cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn, const cmChar_t* audioFn, double srate, unsigned chCnt, unsigned bits )
137
+{
138
+  cmSrRC_t rc   = kOkSrRC;
139
+  cmRC_t   afRC = kOkAfRC;
140
+
141
+  assert( audioFn != NULL );
142
+
143
+  if((rc = cmSyncRecdFinal(hp)) != kOkSrRC )
144
+    return rc;
145
+
146
+  cmSr_t* p = _cmSrAlloc(ctx,0);
147
+
148
+  if( cmFileOpen(&p->fH,srFn,kWriteFileFl,&ctx->rpt) != kOkFileRC )
149
+  {
150
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to create the sync-recd file '%s'.",cmStringNullGuard(srFn));
151
+    goto errLabel;
152
+  }
153
+
154
+  if( cmAudioFileIsValid(p->afH = cmAudioFileNewCreate(audioFn,srate,bits,chCnt,&afRC,&ctx->rpt))==false)
155
+  {
156
+    rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to create the sync-recd audio file '%s'.",cmStringNullGuard(audioFn));
157
+    goto errLabel;
158
+  }
159
+
160
+  unsigned fileUUId = kFileUUSrId;
161
+  unsigned audioFnCnt = strlen(audioFn)+1;
162
+
163
+  if( cmFileWriteUInt(p->fH,&fileUUId,1) != kOkFileRC )
164
+  {
165
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on UUID.");
166
+    goto errLabel;
167
+  }
168
+
169
+  if( cmFileWriteUInt(p->fH,&audioFnCnt,1) != kOkFileRC )
170
+  {
171
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on audio file length write count.");
172
+    goto errLabel;
173
+  }
174
+
175
+  if( cmFileWriteChar(p->fH,audioFn,audioFnCnt) != kOkFileRC )
176
+  {
177
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on audio file string.");
178
+    goto errLabel;
179
+  }
180
+
181
+  if( cmFileTell(p->fH,&p->offs) != kOkFileRC )
182
+  {
183
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to determine file offset.");
184
+    goto errLabel;
185
+  }
186
+
187
+  if( cmFileWriteUInt(p->fH,&p->fn,1) != kOkFileRC )
188
+  {
189
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File write failed on initial record count.");
190
+    goto errLabel;
191
+  }
192
+
193
+  hp->h = p;
194
+
195
+ errLabel:
196
+  if( rc != kOkSrRC )
197
+    _cmSrFinal(p);
198
+
199
+  return rc;
200
+}
201
+
202
+
203
+cmSrRC_t cmSyncRecdOpen(   cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn )
204
+{
205
+  cmSrRC_t  rc         = kOkSrRC;
206
+  cmRC_t    afRC       = kOkAfRC;
207
+  unsigned  fileUUId   = cmInvalidId;
208
+  unsigned  audioFnCnt = 0;
209
+  cmChar_t* audioFn    = NULL;
210
+  cmAudioFileInfo_t afInfo;
211
+  unsigned i;
212
+  unsigned acnt = 0;
213
+  unsigned mcnt = 0;
214
+  unsigned* tiV  = NULL;
215
+
216
+  if((rc = cmSyncRecdFinal(hp)) != kOkSrRC )
217
+    return rc;
218
+
219
+  cmSr_t* p = _cmSrAlloc(ctx,kReadSrFl);
220
+
221
+  if( cmFileOpen(&p->fH,srFn,kReadFileFl,&ctx->rpt) != kOkFileRC )
222
+  {
223
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to open the sync-recd file '%s'.",cmStringNullGuard(srFn));
224
+    goto errLabel;
225
+  }
226
+
227
+  if( cmFileReadUInt(p->fH,&fileUUId,1) != kOkFileRC )
228
+  {
229
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on UUId.");
230
+    goto errLabel;
231
+  }
232
+
233
+  if( cmFileReadUInt(p->fH,&audioFnCnt,1) != kOkFileRC )
234
+  {
235
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on audio file name count.");
236
+    goto errLabel;
237
+  }
238
+
239
+  audioFn = cmMemAllocZ(cmChar_t,audioFnCnt);
240
+
241
+  if( cmFileReadChar(p->fH,audioFn,audioFnCnt) != kOkFileRC )
242
+  {
243
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on audio file string.");
244
+    goto errLabel;
245
+  }
246
+
247
+  if( cmFileReadUInt(p->fH,&p->fn,1) != kOkFileRC )
248
+  {
249
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"File read failed on record count.");
250
+    goto errLabel;
251
+  }
252
+
253
+  // store the file offset to the first recd
254
+  if( cmFileTell(p->fH,&p->offs) != kOkFileRC )
255
+  {
256
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to determine the current file offset.");
257
+    goto errLabel;
258
+  }
259
+
260
+
261
+  // read each file - and count the types
262
+  for(i=0; i<p->fn; ++i)
263
+  {
264
+    cmSrRecd_t r;
265
+    if( cmFileRead(p->fH,&r,sizeof(r)) != kOkFileRC )
266
+    {
267
+      rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
268
+      goto errLabel;
269
+    }
270
+
271
+    switch(r.tid)
272
+    {
273
+      case kMidiSrId:
274
+        mcnt += 1;
275
+        break;
276
+
277
+      case kAudioSrId:
278
+        acnt += 1;
279
+        break;
280
+
281
+      default:
282
+        { assert(0); }
283
+    }
284
+  }
285
+
286
+  // rewind to the begining of the records
287
+  if( cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
288
+  {
289
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to seek to first recd offset.");
290
+    goto errLabel;
291
+  }
292
+
293
+  // allocate space to hold the MIDI records
294
+  p->cn    = mcnt;
295
+  p->ci    = 0;
296
+  p->cache = cmMemResizeZ(cmSrRecd_t,p->cache,p->cn);
297
+  p->map   = cmMemAllocZ(cmSrAudio_t,p->cn);
298
+  tiV      = cmMemAllocZ(unsigned,p->cn);
299
+  
300
+  for(i=0; p->ci<p->cn && i<p->fn; ++i)
301
+  {
302
+    if( cmFileRead(p->fH,p->cache + p->ci,sizeof(cmSrRecd_t)) != kOkFileRC )
303
+    {
304
+      rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
305
+      goto errLabel;
306
+    }
307
+
308
+    if( p->cache[p->ci].tid == kMidiSrId )
309
+      p->ci += 1;
310
+  }
311
+
312
+  // assert that all the MIDI records were read
313
+  assert( p->ci == p->cn);
314
+
315
+  // rewind to the first recd
316
+  if( cmFileSeek(p->fH,kBeginFileFl,p->offs) != kOkFileRC )
317
+  {
318
+    rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to seek to first recd offset.");
319
+    goto errLabel;
320
+  }
321
+
322
+
323
+  // for each recd in the file
324
+  for(i=0; i<p->fn; ++i)
325
+  {
326
+    cmSrRecd_t r;
327
+    if( cmFileRead(p->fH,&r,sizeof(r)) != kOkFileRC )
328
+    {
329
+      rc = cmErrMsg(&p->err,kFileFailSrRC,"Unable to read the record at index %i.");
330
+      goto errLabel;
331
+    }
332
+
333
+    // if this is an audio record
334
+    if( r.tid == kAudioSrId )
335
+    {
336
+      unsigned j;
337
+      
338
+      // for each midi record
339
+      for(j=0; j<p->cn; ++j)
340
+      {
341
+        // measure the time interval between this midi and this audio recd
342
+        unsigned time_interval_micros = cmTimeAbsElapsedMicros(&r.u.a.timestamp,&p->cache[j].u.m.timestamp);
343
+
344
+        // if the audio recd is closer to this midi recd than prior audio records ...
345
+        if( tiV[j] < time_interval_micros || i==0 )
346
+        {
347
+          // ... then store the audio time stamp in the map
348
+          tiV[j]            = time_interval_micros;
349
+          p->map->timestamp = r.u.a.timestamp;
350
+          p->map->smpIdx    = r.u.a.smpIdx;
351
+        }        
352
+      }
353
+    }
354
+  }
355
+
356
+  // open the audio file
357
+  if( cmAudioFileIsValid(p->afH = cmAudioFileNewOpen(audioFn,&afInfo,&afRC,&ctx->rpt ))==false)
358
+  {
359
+    rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Unable to open the sync-recd audio file '%s'.",cmStringNullGuard(audioFn));
360
+    goto errLabel;
361
+  }
362
+ 
363
+  p->flags = cmSetFlag(p->flags,kReadSrFl);
364
+
365
+  hp->h    = p;
366
+
367
+ errLabel:
368
+
369
+  cmMemFree(tiV);
370
+  cmMemFree(audioFn);
371
+
372
+  if( rc != kOkSrRC )
373
+    _cmSrFinal(p);
374
+
375
+  return rc;
376
+}
377
+
378
+cmSrRC_t cmSyncRecdFinal(  cmSyncRecdH_t* hp )
379
+{
380
+  cmSrRC_t rc = kOkSrRC;
381
+
382
+  if( hp==NULL || cmSyncRecdIsValid(*hp)==false)
383
+    return rc;
384
+
385
+  cmSr_t* p = _cmSrHtoP(*hp);
386
+
387
+  if((rc = _cmSrFinal(p)) != kOkSrRC )
388
+    return rc;
389
+
390
+  hp->h = NULL;
391
+
392
+  return rc;
393
+}
394
+
395
+bool     cmSyncRecdIsValid( cmSyncRecdH_t h )
396
+{ return h.h != NULL; }
397
+
398
+cmSrRC_t cmSyncRecdMidiWrite(  cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned status, unsigned d0, unsigned d1 )
399
+{
400
+  cmSrRC_t    rc    = kOkSrRC;
401
+  cmSr_t*     p     = _cmSrHtoP(h);
402
+  cmSrRecd_t* rp    = p->cache + p->ci;
403
+  rp->tid           = kMidiSrId;
404
+  rp->u.m.timestamp = *timestamp;
405
+  rp->u.m.status    = status;
406
+  rp->u.m.d0        = d0;
407
+  rp->u.m.d1        = d1;
408
+
409
+  p->ci += 1;
410
+
411
+  if( p->ci == p->cn )
412
+    rc = _cmSrWriteCache(p);
413
+  
414
+  return rc;
415
+}
416
+
417
+cmSrRC_t cmSyncRecdAudioWrite( cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned smpIdx, const cmSample_t* ch[], unsigned chCnt, unsigned frmCnt )
418
+{
419
+  cmSrRC_t    rc    = kOkSrRC;
420
+  cmSr_t*     p     = _cmSrHtoP(h);
421
+  cmSrRecd_t* rp    = p->cache + p->ci;
422
+  rp->tid           = kAudioSrId;
423
+  rp->u.a.timestamp = *timestamp;
424
+  rp->u.a.smpIdx    =  smpIdx;
425
+
426
+  p->ci += 1;
427
+
428
+  if( p->ci == p->cn )
429
+    if((rc = _cmSrWriteCache(p)) != kOkSrRC )
430
+      goto errLabel;
431
+
432
+  if( cmAudioFileWriteSample(p->afH,frmCnt,chCnt,(cmSample_t**)ch) != kOkAfRC )
433
+    rc = cmErrMsg(&p->err,kAudioFileFailSrRC,"Audio file write failed.");
434
+
435
+ errLabel:
436
+  return rc;
437
+}

+ 32
- 0
cmSyncRecd.h Dosyayı Görüntüle

@@ -0,0 +1,32 @@
1
+#ifndef cmSyncRecd_h
2
+#define cmSyncRecd_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkSrRC,
11
+    kFileFailSrRC,
12
+    kAudioFileFailSrRC
13
+  };
14
+
15
+  typedef cmHandle_t cmSyncRecdH_t;
16
+  typedef cmRC_t     cmSrRC_t;
17
+  extern cmSyncRecdH_t cmSyncRecdNullHandle;
18
+  
19
+  cmSrRC_t cmSyncRecdCreate(  cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn, const cmChar_t* audioFn, double srate, unsigned chCnt, unsigned bits );
20
+  cmSrRC_t cmSyncRecdOpen(    cmCtx_t* ctx, cmSyncRecdH_t* hp, const cmChar_t* srFn );
21
+  cmSrRC_t cmSyncRecdFinal(   cmSyncRecdH_t* hp );
22
+  bool     cmSyncRecdIsValid( cmSyncRecdH_t h );
23
+
24
+  cmSrRC_t cmSyncRecdMidiWrite(  cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned status, unsigned d0, unsigned d1 );
25
+  cmSrRC_t cmSyncRecdAudioWrite( cmSyncRecdH_t h, const cmTimeSpec_t* timestamp, unsigned smpIdx, const cmSample_t* ch[], unsigned chCnt, unsigned frmCnt );
26
+  
27
+
28
+#ifdef __cplusplus
29
+}
30
+#endif
31
+
32
+#endif

Loading…
İptal
Kaydet