Browse Source

Initial commit

master
kevin 1 year ago
commit
63991ae469

+ 36
- 0
.gitignore View File

@@ -0,0 +1,36 @@
1
+# directories to ignore
2
+libcm
3
+
4
+.deps
5
+autom4te.cache
6
+build-aux
7
+build/linux/debug/src/
8
+build/linux/debug/bin
9
+build/linux/debug/lib
10
+build/linux/debug/include
11
+build/linux/release/src/
12
+build/linux/release/bin
13
+build/linux/release/lib
14
+build/linux/release/include
15
+
16
+#Files to ignore
17
+*~
18
+*.[oa]
19
+
20
+Makefile
21
+aclocal.m4
22
+config.h.in
23
+config.h
24
+configure
25
+hold.makefile
26
+Makefile.in
27
+config.log
28
+config.status
29
+libtool
30
+stamp-h1
31
+
32
+m4/libtool.m4
33
+m4/ltoptions.m4
34
+m4/ltsugar.m4
35
+m4/ltversion.m4
36
+m4/lt~obsolete.m4

+ 80
- 0
Makefile.am View File

@@ -0,0 +1,80 @@
1
+AM_CXXFLAGS =
2
+AM_LDFLAGS  =  
3
+AM_CPPFLAGS =
4
+AM_CFLAGS   =
5
+
6
+ACLOCAL_AMFLAGS = -I m4 # use custom macro's in ./m4 
7
+
8
+# if we are building and linking to a nested copy of libcm
9
+if BUILD_LIBCM
10
+  SUBDIRS      = src/libcm  # causes recursive make into given sub-directories
11
+  AM_CPPFLAGS += -I$(srcdir)/src/libcm/src  -I$(srcdir)/src/libcm/src/dsp  -I$(srcdir)/src/libcm/src/vop  -I$(srcdir)/src/libcm/src/app
12
+  AM_LDFLAGS  += -Lsrc/libcm/src
13
+endif
14
+
15
+# To Profile w/ gprof:
16
+# 1) Modify configure: ./configure --disable-shared CFLAGS="-pg"
17
+# 2) Run the program. ./foo
18
+# 3) Run gprof /libtool --mode=execute gprof ./foo
19
+
20
+
21
+# C compiler flags
22
+# _GNU_SOURCE - turns on GNU specific extensions and gives correct prototype for double log2(double) 
23
+# -Wall turn on all warnings
24
+# -Wno-multichar - turns off multi-character constant warnings from cmAudioFile.c
25
+
26
+
27
+
28
+AM_CPPFLAGS += -D _GNU_SOURCE  -I..
29
+AM_CFLAGS   += -Wno-multichar 
30
+
31
+
32
+# debug/release switches
33
+if DEBUG
34
+  AM_CFLAGS   += -g 
35
+  AM_CXXFLAGS += -g
36
+else
37
+  AM_CFLAGS   += -O3
38
+  AM_CXXFLAGS += -O3
39
+endif
40
+
41
+MYLIBS      = -lpthread  -lfftw3f -lfftw3 -lcm
42
+
43
+# Linux specific
44
+if OS_LINUX
45
+ MYLIBS    +=  -lsatlas  -lasound
46
+if OS_64
47
+  AM_CFLAGS  += -m64
48
+  AM_LDFLAGS += -L/usr/lib64/atlas -L/usr/lib64
49
+  MYLIBS     += -lrt -lm 
50
+endif	
51
+endif
52
+
53
+if OS_OSX
54
+ AM_CPPFLAGS +=  -I/opt/local/include	# Search macports directory for fftw headers
55
+ AM_LDFLAGS  +=  -L/opt/local/lib       # and libraries.
56
+ AM_LDFLAGS  +=  -framework Cocoa -framework CoreAudio -framework CoreMIDI -framework Carbon -framework Accelerate
57
+endif
58
+
59
+src_cmtools_cmtools_SOURCES  = src/cmtools/cmtools.c 
60
+src_cmtools_cmtools_LDADD    = $(MYLIBS)
61
+bin_PROGRAMS                 = src/cmtools/cmtools
62
+
63
+src_cmtools_mas_SOURCES  = src/cmtools/mas.c 
64
+src_cmtools_mas_LDADD    = $(MYLIBS)
65
+bin_PROGRAMS            += src/cmtools/mas 
66
+
67
+src_cmtools_audiodev_SOURCES  = src/cmtools/audiodev.c 
68
+src_cmtools_audiodev_LDADD    = $(MYLIBS)
69
+bin_PROGRAMS            += src/cmtools/audiodev
70
+
71
+# See: https://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Clean.html#Clean
72
+# 'make distclean' sets the source tree back to it's pre-configure state
73
+# 'distclean-local' is used by automake 'distclean' to perform customized local actions
74
+# ${exec_prefix} is the install prefix given to 'configure' by the user.
75
+# ${srcdir} is the directory of this Makefile and is set by autoconf.
76
+distclean-local:
77
+	rm -rf ${exec_prefix}/src        
78
+	rm -rf ${exec_prefix}/bin        
79
+	rm -rf ${exec_prefix}/include
80
+	rm -rf ${exec_prefix}/lib        

+ 452
- 0
README.md View File

@@ -0,0 +1,452 @@
1
+Prerequisites
2
+=============
3
+
4
+fftw fftw-devel atlas atlas-devel alsa-lib alsa-lib-devel fltk fltk-devel
5
+
6
+
7
+
8
+In the 'template generation' mode the program generates a text file that contains information
9
+of interest from the MusicXML file. We refer to the text file as the as the 'decoration' file
10
+because it allows the score to be further decorated by adding additional information to the score.
11
+
12
+In the 'escore generation' mode the program outputs a data file in CSV format which contains
13
+the score in a format which is conveniently readable by a score matching program. The program
14
+also generates a MIDI file which allows the clarified score to be rendered with a sampler.
15
+An additional SVG (scalable vector graphics) file is generated which shows the score in
16
+piano roll form along with any problems that the program may have had during the conversion process.
17
+
18
+
19
+
20
+```
21
+cmtool --score_gen -x <xml_file> -d <dec_fn> {-c <csvOutFn>} {-m <midiOutFn>} {-s <svgOutFn>} {-r report} {-b begMeasNumb} {t begTempoBPM}
22
+
23
+
24
+Enumerated group: Action selector 
25
+
26
+    -S --score_gen  
27
+    Run the score generation tool.
28
+
29
+    -T --timeline_gen  
30
+    Run the time line generation tool.
31
+
32
+    -M --meas  
33
+    Generate perfomance measurements.
34
+
35
+-x --xml_fn <filename> 
36
+    Name of the input MusicXML file.
37
+
38
+-d --dec_fn <filename> 
39
+    Name of a score decoration file.
40
+
41
+-c --csv_fn <filename> 
42
+    Name of a CSV score file.
43
+
44
+-p --pgm_rsrc_fn <filename> 
45
+    Name of program resource file.
46
+
47
+-m --midi_out_fn <filename> 
48
+    Name of a MIDI file to generate as output.
49
+
50
+-i --midi_in_fn <filename> 
51
+    Name of a MIDI file to generate as output.
52
+
53
+-s --svg_fn <filename> 
54
+    Name of a HTML/SVG file to generate as output.
55
+
56
+-t --timeline_fn <filename> 
57
+    Name of a timeline to generate as output.
58
+
59
+-r --report_fn <filename> 
60
+    Name of a status file to generate as output.
61
+
62
+-f --debug_fl  (required)
63
+    Print a report of the score following processing.
64
+
65
+-b --beg_meas <int> 
66
+    The first measure the to be written to the output CSV, MIDI and SVG files.
67
+
68
+-e --beg_bpm <int> 
69
+    Set to 0 to use the tempo from the score otherwise set to use the tempo at begMeasNumb.
70
+	
71
+-n --svg_stand_alone_fl 
72
+    Write the SVG output with an HTML wrapper.
73
+
74
+-z --svg_pan_zoom_fl
75
+    Include the SVG pan-zoom control (--svg_stand_alone must also be enabled)
76
+
77
+-h --help  
78
+    Print this usage information.
79
+
80
+-v --version  
81
+    Print version information.
82
+
83
+```
84
+
85
+
86
+Score Following and Timeline Marker Generator
87
+==============================================
88
+
89
+Perform score following based tasks.
90
+Generate the time line marker information used by the performance program resource file.
91
+
92
+```
93
+cmtool --score_follow -c <csv_score_fn> -i <midi_in_fn> -r <report_fn> -s <svg_out_fn> -m <midi_out_fn> -t <timeline_fn>
94
+```
95
+
96
+If `<midi_out_fn>` is given then the a copy of `<midi_in_fn>` will be created with 
97
+note velocities and sostenuto pedal events from the score.
98
+
99
+
100
+
101
+
102
+Score match report output snippet:
103
+
104
+```
105
+  Score Score Score MIDI  MIDI MIDI
106
+  Bar   UUID  Pitch UUID  Ptch Vel.
107
+- ----- ----- ----- ----- ---- ----
108
+m     1     3    B3    19   B3 127
109
+m     1     4   D#4    20  D#4 127
110
+m     1     5   G#5    21  G#5 127
111
+m     1    14    E4    25   E4  29
112
+m     1    15   G#3    27  G#3  36
113
+m     1    16    E4    29   E4  43
114
+m     1    17   G#3    31  G#3  50
115
+```
116
+
117
+TODO: Show errors
118
+TODO: Show SVG output
119
+
120
+
121
+Performance Measurement Generators
122
+==================================
123
+
124
+Given a performance program resource file generate performance measurements where the internal MIDI file
125
+is used as a substitute for a real player.
126
+
127
+
128
+```
129
+cmtool --meas_gen -p <pgm_rsrc_fn> -r <report_fn>
130
+```
131
+
132
+
133
+Example `<pgm_rsrc_fn>`:
134
+
135
+``` 
136
+{
137
+  timeLineFn:   "kc/data/round2.js"
138
+  tlPrefixPath: "projects/imag_themes/scores/gen"
139
+  scoreFn:      "score.csv"
140
+
141
+  //            pppp  ppp pp  p   mp  mf  f   ff  fff 
142
+     dynRef: [   14,  28, 42, 56, 71, 85, 99, 113,128 ]
143
+
144
+}
145
+```
146
+
147
+Example call with output file snippet:
148
+
149
+```
150
+cmtool --meas_gen -g ~/src/cmtools/examples/perf_meas/pgm_rsrc_round2.js -r ~/src/cmtools/examples/perf_meas/perf_meas_out.js
151
+
152
+{
153
+  meas : 
154
+  [
155
+    [  "sec"  "typeLabel"  "val"     "cost" "loc" "evt" "seq" "mark" "typeId" ]
156
+    [  "6002"  "tempo"    34.730932 0.000000  59   76     0    "1"      3 ]
157
+    [  "6002"  "dyn"       0.952381 0.000000  59   76     0    "1"      2 ]
158
+    [  "6002"  "even"      0.600000 0.000000  59   76     0    "1"      1 ]
159
+    [  "6002"  "dyn"       1.000000 0.142857  59   76     0    "2"      2 ]
160
+  ]
161
+}
162
+
163
+ ```
164
+
165
+Column Descriptions:
166
+
167
+Column    |   Description
168
+----------|-----------------------------------------------------
169
+sec       | Score section to which this measurement will be applied.
170
+typeLabel | Measurement type
171
+value     | Measurement value
172
+cost      | Score follower error value (0= perfect match)
173
+loc       | Score location index where this measurement will be applied
174
+evt       | Score event index where this measurement will be applied
175
+seq       | Sequence id.
176
+mark      | Time line marker label from which the measurements were made
177
+typeId    | Measurement type id (numeric id associated with typeLabel)
178
+
179
+Note that the event indexes and score locations are best
180
+seen in the score report (as generated by `cmtool --score_report`)
181
+NOT by directly referencing the score CSV file.
182
+
183
+
184
+Score Report
185
+============
186
+
187
+Generate a human readable score report from a score CSV file.
188
+
189
+```
190
+cmtool --score_report -c <csvScoreFn> -r <scoreRptFn>"
191
+
192
+```
193
+
194
+Example report file snippet:
195
+
196
+```
197
+evnt  CSV             bar
198
+index line  loctn bar idx type  pitch ETD Dynamic
199
+----- ----- ----- --- --- ----- ----- --- -------
200
+    0     2   1 bar 
201
+    1     3     0   1   0 non    B3               section:6001 
202
+    2     4     0   1   1 non   D#4               
203
+    3     5     0   1   2 non   G#5               
204
+    4     6     1   1   3 ped    dn               
205
+    5     7     2   1   3 ped    dn               
206
+    6     8     3   1   3 non    E4               
207
+    7     9     4   1   4 non   G#3    td ppp     
208
+    8    10     5   1   5 non    E4    td pp      
209
+    9    11     6   1   6 non   G#3    td pp      
210
+   10    12     7   1   7 non   C#2    td p       
211
+   11    13     8   1   8 non    C4    td mp      
212
+   12    14     9   1   9 non   G#3    td mp      
213
+   13    15    10   1  10 non   C#2    td mf      
214
+   14    16    11   1  11 non   A#2    td f       
215
+   15    17    12   1  12 non    C4    td f       
216
+   16    18    13   1  13 non   A#2    td f       
217
+   17    19    14   1  14 non   C#1               
218
+
219
+
220
+```
221
+
222
+MIDI File Reports
223
+=================
224
+
225
+Generate a MIDI file report and optional SVG piano roll image."
226
+
227
+    cmtool --midi_report -i <midiInFn> -r <midiRptFn> {-s <svgOutFn> {--svg_stand_alone_fl} {--svg_pan_zoom_fl}  }
228
+
229
+
230
+Timeline Report
231
+=================
232
+
233
+Generate human readable report from a time line setup file.
234
+
235
+    cmtool --timeline_report -t <timelineInFn> -l <tlPrefix> -r <rptOutFn>
236
+
237
+tlPrefix is the folder where data files for this timeline are stored.	
238
+
239
+
240
+Score Follow Report
241
+===================
242
+
243
+```
244
+cmtool  --score_follow -c round2.csv -i new_round2.mid -r report.txt -s report_svg.html
245
+ ```
246
+ 
247
+SVG Description
248
+---------------
249
+
250
+- Red borders around score events that were not matched.
251
+- Red borders around MIDI events that did not match.
252
+- Line is drawn to MIDI events that matched to multiple score events. The lines
253
+are drawn to all score events after the first match.
254
+ 
255
+
256
+Audio Device Test
257
+=================
258
+
259
+Real-time audio port test.
260
+
261
+This test also excercises the real-time audio buffer which implements most of the global audio control functions.
262
+
263
+```
264
+
265
+
266
+```
267
+
268
+```
269
+-s --srate <real>     Audio system sample rate.
270
+-z --hz <real>     Tone frequency in Hertz.
271
+-x --ch_index <uint>     Index of first channel index.
272
+-c --ch_cnt <uint>     Count of audio channels.
273
+-b --buf_cnt <uint>     Count of audio buffers. (e.g. 2=double buffering, 3=triple buffering)
274
+-f --frames_per_buf <uint>     Count of audio channels.
275
+-i --in_dev_index <uint>     Input device index as taken from the audio device report.
276
+-o --out_dev_index <uint>     Output device index as taken from the audio device report.
277
+-r --report_flag     Print a report of the score following processing.
278
+-h --help      Print this usage information.
279
+-v --version      Print version information.
280
+-p --parms      Print the arguments.
281
+```
282
+
283
+MIDI Audio Sync (MAS)
284
+=====================
285
+
286
+1) Synchronize Audio to MIDI based on onset patterns:
287
+
288
+    a. Convert MIDI to audio impulse files:
289
+
290
+        mas -m -i <midi_dir | midi_fn >  -o <out_dir> -s <srate>
291
+
292
+        Notes:
293
+
294
+        * If <midi_dir> is given then use all files in the directory as input otherwise convert a single file.
295
+   
296
+        * The files written to <out_dir> are audio files with impulses written at the location of note on msg's. The amplitude of the the impulse is velocity/127.
297
+
298
+   b. Convert the onsets in audio file(s) to audio impulse
299
+      file(s).
300
+
301
+      mas -a -i <audio_dir | audio_fn > -o <out_dir>   
302
+          -w <wndMs> -f <hopFactor> -u <chIdx> -r <wnd_frm_cnt> 
303
+          -x <preWndMult> -t <threshold> -z <maxFrqHz> -e <filtCoeff>
304
+
305
+      1) If <audio_dir> is given then use all files
306
+         in the directory as input otherwise convert a 
307
+         single file.
308
+      2) The onset detector uses a spectral flux based
309
+         algorithm.
310
+         See cmOnset.h/.c for an explanation of the 
311
+         onset detection parameters.
312
+         
313
+
314
+   c) Convolve impulse files created in a) and b) with a 
315
+      Hann window to widen the impulse width.
316
+
317
+      mas -c -i <audio_dir | audio_fn > -o <out_dir> -w <wndMs>
318
+
319
+      1) If <audio_dir> is given then use all files
320
+         in the directory as input otherwise convert a 
321
+         single file.
322
+      2) <wndMs> gives the width of the Hann window.
323
+      
324
+   d) Synchronize MIDI and Audio based convolved impulse
325
+      files based on their onset patterns.
326
+
327
+      mas -y -i <sync_cfg_fn.js> -o <sync_out_fn.js>
328
+
329
+      1) The <sync_cfg_fn.js> file has the following format:
330
+        {
331
+          ref_dir : "/home/kevin/temp/mas/midi_conv"    // location of ref files
332
+          key_dir : "/home/kevin/temp/mas/onset_conv"   // location of key files
333
+          hop_ms  : 25                                  // sliding window increment
334
+
335
+          sync_array :
336
+          [
337
+            //   ref_fn  wnd_beg_secs wnd_dur_secs  key_fn        key_beg_secs, key_end_secs
338
+            [    "1.aif",    678,         113,    "Piano 3_01.aif",  239.0,     417.0], 
339
+            [    "3.aif",    524,          61,    "Piano 3_06.aif",  556.0,     619.0],
340
+          ]
341
+        }
342
+
343
+         Notes:
344
+         a. The 'window' is the section of the reference file which is compared
345
+            to the key file search area <key_beg_secs> to <key_end_secs> by sliding it 
346
+            in increments of 'hop_ms' samples.
347
+
348
+         b. Set 'key_end_secs' to 0 to search to the end of the file.
349
+
350
+         c. When one key file matches to multiple reference files the
351
+            key files sync recd should be listed consecutively.  This way
352
+            the earlier searches can stop when they reach the beginning 
353
+            of the next sync records search region.  See sync_files().
354
+
355
+            Note that by setting  <key_beg_secs> to a non-zero value
356
+            as occurs in the multi-key-file case has a subtle effect of
357
+            changing the master-slave relationship between the reference
358
+            an key file.  
359
+
360
+            In general the reference file is the master and the key file
361
+            is the slave.  When a non-zero <key_beg_secs> is given however
362
+            this relationship reverses.  See masCreateTimeLine() for 
363
+            how this is used to assign file group id's during the
364
+            time line creation.
365
+
366
+      3) The <sync_out_fn.js> has the following form.
367
+```
368
+         {
369
+           "sync" : 
370
+           {
371
+             "refDir" : "/home/kevin/temp/mas/midi_conv"     
372
+             "keyDir" : "/home/kevin/temp/mas/onset_conv"     
373
+             "hopMs" : 25.000000     
374
+
375
+             "array" : 
376
+             [
377
+              
378
+               //
379
+               // sync results for "1.aif" to "Piano 3_01.aif"
380
+               //
381
+
382
+               {
383
+                 // The following block of fields were copied from  <sync_cfg_fn.js>. 
384
+                 "refFn"         : "1.aif"         
385
+                 "refWndBegSecs" : 678.000000         
386
+                 "refWndSecs"    : 113.000000         
387
+                 "keyFn"         : "Piano 3_01.aif"         
388
+                 "keyBegSecs"    : 239.000000         
389
+                 "keyEndSecs"    : 417.000000 
390
+        
391
+                 // Sync. location of the 'window' in the key file.
392
+                 // Sample index into the key file which matches to the first sample 
393
+                 // in the reference window.
394
+                 "keySyncIdx" : 25768800     // Offset into the key file of the best match.
395
+    
396
+                 "syncDist"  : 4184.826108   // Match distance score for the sync location.          
397
+                 "refSmpCnt" : 200112000     // Count of samples in the reference file.       
398
+                 "keySmpCnt" : 161884800     // Count of samples in the key file.        
399
+                 "srate"     : 96000.000000  // Sample rate of the reference and key file.
400
+               },
401
+             ]    
402
+           }  
403
+         }
404
+```
405
+2) Create a time line from the results of a synchronization.  A time line is a data structure
406
+   (See cmTimeLine.h/.c) which maintains a time based ordering of Audio files, MIDI files,
407
+   and arbitrary markers.
408
+
409
+    mas -g -i <sync_out_fn.js>  -o <time_line_out_fn.js> -R <ref_dir> -K <key_dir> -M <ref_ext> -A <key_ext>
410
+
411
+|  <sync_out_fn.js> The output file produced as a result of a previous MIDI <-> Audio synchronization.
412
+|  
413
+|  <ref_dir>        Location of the reference files (MIDI) used for the synchronization. 
414
+|  <ref_ext>        File extension used by the reference files.
415
+|  <key_dir>        Locate of the key files (Audio) used for the synchronization.
416
+|  <key_ext>        File extension used by the key files.
417
+
418
+  a. The time line 'trackId' assigned to each time line object is based on the files
419
+     'groupId'. A common group id is given to sets of files which are
420
+     locked in time relative to one another.  For example
421
+     if file B and C are synced to master file A and
422
+     file D is synced to file E which is synced to master
423
+     file F.  Then files A,B,C will be given one group
424
+     id and files D,E and F will be given another group id.
425
+     (See masCreateTimeLine()).
426
+
427
+  b. The time line object 'offset' values gives the offset in samples where the object
428
+     begins relative to other objects in the group.  Note that the master object in the
429
+     group may not begin at offset 0 if there are slave objects which start before it.
430
+
431
+
432
+     
433
+
434
+TODO:
435
+=====
436
+
437
+* replace round2.csv time with the times in the full fragment MIDI file
438
+
439
+* update timeline lite to allow for a synchronized audio file
440
+
441
+* change the build setup to default to getting libcm from the system
442
+and allow an option to build it from src/libcm
443
+
444
+* for all svg output create a standalone flag that wraps the output in HTML
445
+and another option to load pan-zoom.
446
+
447
+
448
+* MIDI report output example and description
449
+
450
+* Timeline report output example and description
451
+
452
+

+ 37
- 0
build/clean.sh View File

@@ -0,0 +1,37 @@
1
+#!/bin/bash
2
+#
3
+# Run 'make distclean' to clean many of the temporary make files.
4
+# then use this script run from cm/build to clean the remaining files
5
+#
6
+
7
+
8
+
9
+function clean_dir {
10
+
11
+    make -C $1 uninstall
12
+    make -C $1 distclean
13
+    
14
+    rm -f  $1/bin/kc.app/Contents/MacOS/kc
15
+    
16
+    
17
+}
18
+
19
+
20
+
21
+clean_dir linux/debug
22
+clean_dir linux/release
23
+clean_dir osx/debug
24
+clean_dir osx/release
25
+
26
+rm -rf osx/debug/a.out.dSYM
27
+
28
+# delete everything created by 'autoreconf'.
29
+rm -rf ../build-aux
30
+rm -rf ../autom4te.cache
31
+rm -f  ../config.h.in ../config.h.in~ ../configure ../libtool.m4
32
+rm -f  ../Makefile.in ../aclocal.m4
33
+rm -f  ../m4/libtool.m4 ../m4/ltoptions.m4 ../m4/ltsugar.m4 ../m4/ltversion.m4 ../m4/lt~obsolete.m4
34
+
35
+
36
+
37
+

+ 25
- 0
build/linux/debug/build.sh View File

@@ -0,0 +1,25 @@
1
+#!/bin/sh
2
+
3
+curdir=`pwd`
4
+
5
+cd ../../..
6
+autoreconf --force --install
7
+
8
+cd ${curdir}
9
+
10
+# To Profile w/ gprof:
11
+# 1) Modify configure: ./configure --disable-shared CFLAGS="-pg"
12
+# 2) Run the program. ./foo
13
+# 3) Run gprof /libtool --mode=execute gprof ./foo
14
+
15
+../../../configure --prefix=${curdir} \
16
+		   --enable-debug --enable-build_libcm \
17
+		   CFLAGS="-g -Wall" \
18
+		   CXXFLAGS="-g -Wall" \
19
+		   LIBS=
20
+
21
+#		   CPPFLAGS="-I/home/kevin/src/libcm/build/linux/debug/include " \
22
+#		   LDFLAGS="-L/home/kevin/src/libcm/build/linux/debug/lib" \
23
+
24
+#make
25
+#make install

+ 24
- 0
build/linux/release/build.sh View File

@@ -0,0 +1,24 @@
1
+#!/bin/sh
2
+
3
+curdir=`pwd`
4
+
5
+cd ../../..
6
+autoreconf --force --install
7
+
8
+cd ${curdir}
9
+
10
+# To Profile w/ gprof:
11
+# 1) Modify configure: ./configure --disable-shared CFLAGS="-pg"
12
+# 2) Run the program. ./foo
13
+# 3) Run gprof /libtool --mode=execute gprof ./foo
14
+
15
+../../../configure --prefix=${curdir} \
16
+CFLAGS="-Wall" \
17
+CXXFLAGS="-Wall" \
18
+CPPFLAGS= \
19
+LDFLAGS= \
20
+LIBS=
21
+
22
+
23
+#make
24
+#make install

+ 20
- 0
build/osx/debug/build.sh View File

@@ -0,0 +1,20 @@
1
+#!/bin/sh
2
+
3
+curdir=`pwd`
4
+
5
+cd ../../..
6
+autoreconf --force --install
7
+
8
+cd ${curdir}
9
+
10
+../../../configure --prefix=${curdir} \
11
+--enable-debug \
12
+CFLAGS="-g -Wall" \
13
+CXXFLAGS="-g -Wall" \
14
+CPPFLAGS= \
15
+LDFLAGS= \
16
+LIBS=
17
+
18
+
19
+#make
20
+#make install

+ 19
- 0
build/osx/release/build.sh View File

@@ -0,0 +1,19 @@
1
+#!/bin/sh
2
+
3
+curdir=`pwd`
4
+
5
+cd ../../..
6
+autoreconf --force --install
7
+
8
+cd ${curdir}
9
+
10
+../../../configure --prefix=${curdir} \
11
+CFLAGS="-Wall" \
12
+CXXFLAGS="-Wall" \
13
+CPPFLAGS= \
14
+LDFLAGS= \
15
+LIBS=
16
+
17
+
18
+#make
19
+#make install

+ 152
- 0
configure.ac View File

@@ -0,0 +1,152 @@
1
+#
2
+# Use "autoreconf --force --install" to update depedent files after changing 
3
+# this configure.ac or any of the Makefile.am files.
4
+#
5
+
6
+AC_INIT([cmtools],[1.0],[kl@currawongproject.org])
7
+AC_CONFIG_AUX_DIR([build-aux])                    # put aux files in build-aux
8
+AM_INIT_AUTOMAKE([1.9 -Wall foreign subdir-objects]) # subdir-objects needed for non-recursive make
9
+AC_CONFIG_SRCDIR([src/cmtools/cmtools.c])
10
+AC_CONFIG_HEADERS([config.h])
11
+AC_CONFIG_MACRO_DIR([m4])
12
+
13
+AM_PROG_AR
14
+
15
+LT_INIT
16
+
17
+# Check for programs
18
+AC_PROG_CC
19
+AC_PROG_CXX
20
+# AC_PROG_RANLIB  # required for static librarires
21
+
22
+AM_PROG_CC_C_O
23
+
24
+# Checks for libraries.
25
+# AC_CHECK_LIB([cairo],[cairo_debug_reset_static_data],[AC_MSG_RESULT([The 'cairo' library was found.])],[AC_MSG_ERROR([The 'cairo' library was not found.])])
26
+#TODO: add more library checks 
27
+
28
+# Checks for header files.
29
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h float.h limits.h mach/mach.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h termios.h unistd.h])
30
+
31
+# Checks for typedefs, structures, and compiler characteristics.
32
+AC_HEADER_STDBOOL
33
+AC_C_INLINE
34
+AC_TYPE_OFF_T
35
+AC_TYPE_SSIZE_T
36
+AC_TYPE_UINT64_T
37
+
38
+# Checks for library functions.
39
+AC_FUNC_ERROR_AT_LINE
40
+AC_FUNC_FORK
41
+AC_FUNC_FSEEKO
42
+AC_FUNC_MALLOC
43
+AC_FUNC_REALLOC
44
+AC_FUNC_STRTOD
45
+AC_CHECK_FUNCS([clock_gettime floor memmove memset mkdir pow rint select socket sqrt strcasecmp strchr strcspn strerror strspn strstr strtol])
46
+
47
+
48
+# The following is a custom macro in ./m4/os_type.m4
49
+# be sure to also set "ACLOCAL_AMFLAGS = -I m4" in ./Makefile.am
50
+# Defines the config.h variable OS_LINUX or OS_OSX
51
+AX_FUNC_OS_TYPE
52
+
53
+AX_FUNC_OS_64
54
+
55
+# ac_cv_os_type is set by AX_FUNC_OS_TYPE
56
+AM_CONDITIONAL([OS_LINUX],[test x"${ax_cv_os_type}" = xLinux])
57
+AM_CONDITIONAL([OS_OSX],[test x"${ax_cv_os_type}" = xDarwin])
58
+echo "OS='${ax_cv_os_type}'"
59
+
60
+AM_CONDITIONAL([OS_64],[test x"${ax_cv_os_64}" == xx86_64])
61
+echo "ptr width='${ax_cv_os_64}'"
62
+
63
+# check if a request has been made to build libcm
64
+AC_ARG_ENABLE([build_libcm],
65
+     [  --enable-build_libcm    libcm is included in the local source tree],
66
+     [case "${enableval}" in
67
+       yes) build_libcm=true ;;
68
+       no)  build_libcm=false ;;
69
+       *) AC_MSG_ERROR([bad value ${enableval} for --enable-build_libcm]) ;;
70
+     esac],[build_libcm=false])
71
+     
72
+echo "build_libcm=${build_libcm}"
73
+
74
+# check if a nested copy of libcm exists in /src/libcm
75
+AC_CHECK_FILE([${srcdir}/src/libcm/src/cmGlobal.h],[local_libcm=true],[local_libcm=false])
76
+echo "local_libcm=${local_libcm}"
77
+
78
+# set BUILD_LIBCM if a libcm build request was set and a nested copy of libcm exists
79
+AM_CONDITIONAL([BUILD_LIBCM], [test x$build_libcm = xtrue -a x$local_libcm = xtrue ])
80
+
81
+AC_ARG_ENABLE([debug],
82
+     [  --enable-debug    Turn on debugging],
83
+     [case "${enableval}" in
84
+       yes) debug=true ;;
85
+       no)  debug=false ;;
86
+       *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
87
+     esac],[debug=false])
88
+     
89
+echo "debug=${debug}"
90
+     
91
+AM_CONDITIONAL([DEBUG], [test x$debug = xtrue])
92
+
93
+if test x$debug = xfalse; then
94
+AC_DEFINE([NDEBUG], 1,[Debugging off.])
95
+fi
96
+
97
+AC_ARG_ENABLE([vectop],
98
+     [  --enable-vectop    Turn on use of Lapack and Atlas vector/matrix operations. ],
99
+     [case "${enableval}" in
100
+       yes) vectop=true ;;
101
+       no)  vectop=false ;;
102
+       *) AC_MSG_ERROR([bad value ${enableval} for --enable-vectop]) ;;
103
+     esac],[vectop=true])
104
+     
105
+echo "vectop=${vectop}"
106
+     
107
+# if --enable-vectop then #define CM_VECTOP = 1 in config.h otherwise CM_VECTOP is undefined.
108
+if test x"$vectop" = xtrue; then
109
+AC_DEFINE([CM_VECTOP], 1,[Use Lapack and Atlas.])
110
+fi
111
+
112
+
113
+AC_ARG_ENABLE([memalign],
114
+     [  --enable-memalign    Turn on memory alignment on dynamic memory allocations. ],
115
+     [case "${enableval}" in
116
+       yes) memalign=true ;;
117
+       no)  memalign=false ;;
118
+       *) AC_MSG_ERROR([bad value ${enableval} for --enable-memalign]) ;;
119
+     esac],[memalign=true])
120
+     
121
+echo "memalign=${memalign}"
122
+
123
+# if --enable-vectop then #define CM_MEMALIGN = 1 in config.h otherwise CM_MEMALIGN is undefined.     
124
+if test x"$memalign" = xtrue; then
125
+AC_DEFINE([CM_MEMALIGN], 1,[Turn on dynamic memory alignment.])
126
+fi
127
+
128
+AC_ARG_ENABLE([sonicart],
129
+     [  --enable-sonicart    Enable use of Sonic Arts proprietary code. ],
130
+     [case "${enableval}" in
131
+       yes) sonicart=true ;;
132
+       no)  sonicart=false ;;
133
+       *) AC_MSG_ERROR([bad value ${enableval} for --enable-sonicart]) ;;
134
+     esac],[sonicart=false])
135
+     
136
+echo "sonicart=${sonicart}"
137
+     
138
+# if --enable-sonicart then #define CM_SONICART = 1 in config.h otherwise CM_SONICART is undefined.
139
+if test x"$sonicart" = xtrue; then
140
+AC_DEFINE([CM_SONICART], 1,[Include Sonic Arts proprietry code.])
141
+fi
142
+
143
+AM_CONDITIONAL([INC_SONICART], [test x$sonicart = xtrue])
144
+
145
+AC_CONFIG_FILES([ Makefile ])
146
+
147
+# if local nested libcm then do recursive configure into subdirs
148
+if test x$build_libcm = xtrue -a x$local_libcm = xtrue; then
149
+AC_CONFIG_SUBDIRS([src/libcm])
150
+fi
151
+
152
+AC_OUTPUT

BIN
doc/score_follow_0.png View File


+ 254
- 0
doc/xscore_gen.md View File

@@ -0,0 +1,254 @@
1
+xscore_gen
2
+==========
3
+
4
+*xscore_gen* parses MusicXML score files and generates a text file
5
+which allows the score to be clarified and additional information to
6
+be added.  This is the first step in creating a 'machine readable
7
+score' based on the 'human readable score'.
8
+
9
+This step is necessary because we need a way to efficiently 
10
+append additional information to the score which cannot be entered
11
+directly by the score editing program (e.g. Sibelius, Dorico, Finale).
12
+In practice we use Sibelius 6 as our primary score editor.
13
+
14
+Likewise there are certain limitations to the generated MusicXML
15
+which need to be worked around. The primary problem being that
16
+dynamic markings are not tied to specific notes. This is important
17
+for purposes of score analysis as well as audio rendering. 
18
+
19
+The overall approach to adding this addtional information
20
+is as follows:
21
+
22
+1. Add as much auxilliary information as possible from within Sibelius.
23
+This entails using colored note heads, and carefully placed
24
+text strings.
25
+
26
+2. Generate the MusicXML file using the [Dolet 6 Sibelius
27
+plug-in](https://www.musicxml.com/).  The resulting MusicXML file is
28
+run through *xscore_gen* and parsed to find any invalid structures
29
+such as damper up events not preceeded by damper down events, or tied
30
+notes with no ending note. These problems are cleared by careful
31
+re-editing of the score within Sibelius until all the problematic
32
+structures are fixed.
33
+
34
+3. As a side effect of step 2 a template 'decoration' file is generated.
35
+This text file has all the relavant 'machine score' information 
36
+from the XML score as a time tagged list. In this step *decoration* information is manually
37
+added by entering codes at the end of each line.  The codes
38
+are cryptic but they are also succinct and allow for relatively
39
+painless editing.
40
+
41
+4. Once the addition information is entered *xscore_gen* is
42
+run again to generate three output files: 
43
+
44
+- machine score as a CSV file
45
+- MIDI file suitable for audio rendering
46
+- SVG piano roll file
47
+
48
+As with step 2 this step may need to be iterated several times
49
+to clear syntactic errors in the decoration data.
50
+
51
+5. Generate the time line marker information to be used with the performance program:
52
+
53
+Generate the time line marker information, into `temp/time_line_temp.txt` like this:
54
+
55
+`cmtest -F`
56
+
57
+This calls `cmMidiScoreFollowMain()` in app\cmMidiScoreFollow.c.
58
+
59
+Then paste `temp\time_line_temp.txt` into kc/src/kc/data/round1.js.
60
+
61
+
62
+
63
+Preparing the score
64
+-------------------
65
+
66
+Note color is used to assign notes to groups. 
67
+These groups may later be used to indicate
68
+certain processes which will be performed
69
+on these notes during performance.
70
+
71
+There are currently three defined groups
72
+'even','dynamics' and 'tempo'.
73
+
74
+
75
+### Score Coloring Chart:
76
+
77
+Description         Number   Color
78
+------------------- -------- -------------------------
79
+Even                #0000FF  blue 
80
+Tempo               #00FF00  green
81
+Dyn                 #FF0000  red
82
+Tempo + Even        #00FFFF  green + blue (turquoise)
83
+Dyn   + Even        #FF00FF  red   + blue
84
+Dyn   + Tempo       #FF7F00  red   + green (brown)
85
+Tempo + Even + Dyn  #996633  purple
86
+
87
+Decrement color by one (i.e. 0xFE) to indicate the last note in a group
88
+of measured notes.  Note that a decremented color stops all active measures
89
+not just the measurement associated with the decremented color.  
90
+
91
+
92
+Preparing the Music XML File
93
+----------------------------
94
+
95
+*xscore_gen* is know to work with the MusicXML files produced by
96
+the [Dolet 6 Sibelius plug-in]<https://www.musicxml.com/>
97
+
98
+After generating the file it is necessary to do some
99
+minor pre-processing before submitting it to *xscore_gen*
100
+
101
+iconv -f UTF-16 -t UTF-8 -o score-utf16.xml score-utf8.xml
102
+
103
+
104
+Create the decoration file
105
+--------------------------
106
+
107
+```
108
+cmtools --score_gen -x myscore.xml -d mydec.txt
109
+```
110
+
111
+Here's a snippet of a typical 'decoration' file.
112
+
113
+```
114
+Part:P1
115
+  1 : div:768 beat:4 beat-type:4 (3072)
116
+      idx voc  loc    tick  durtn rval        flags
117
+      --- --- ----- ------- ----- ---- --- ---------------
118
+        0   0     2       0     0  0.0     |--------------
119
+        1   0     0       0    54  4.0     --------------- 54 bpm
120
+        2   1     0       0  3072  1.0     -R-------------
121
+        3   5     0       0  2304  2.0     -R-.-----------
122
+        4   0     0     996     0  0.0     --------V------
123
+        5   0     0    1920     0  0.0     --------^------
124
+        6   5     0    2304   341  8.0     -R-------------
125
+        7   0     0    2643     0  0.0     --------V------
126
+        8   5     0    2645    85 32.0     -R-------------
127
+        9   5     3    2730    85 32.0 F 5 --------------*
128
+       10   5     4    2815    85 32.0 Ab2 --------------*
129
+       11   5     5    2900    85 32.0 C 3 --------------*
130
+       12   5     6    2985    87 32.0 F 6 --------------*
131
+       13   1     0    3072   768  4.0     -R-------------
132
+       14   5     0    3072   768  4.0     -R------------- 3840
133
+```
134
+
135
+### Decoration file format
136
+
137
+Column | Description
138
+-------|-----------------------------
139
+idx    | event index
140
+voc    | voice index
141
+tick   | MIDI tick 
142
+durtn  | duration in MIDI ticks
143
+rval   | rythmic value
144
+pitch  | scientific pitch
145
+flags  | event attributes 
146
+
147
+### Event attribute flags:
148
+
149
+Event attribute symbols used in the decoration file:
150
+
151
+Desc      | Flag | 
152
+----------|------|-----------------------------------------
153
+Bar       |  |   | Beginning of a measure
154
+Rest      |  R   | Rest event
155
+Grace     |  G   | Grace note event
156
+Dot       |  .   | note is dotted
157
+Chord     |  C   | note is part of a chord
158
+Even      |  e   | note is part of an 'even' group
159
+Dyn       |  d   | note is part of a 'dynamics' group
160
+Tempo     |  t   | note is part of a 'tempo' group
161
+DampDn    |  V   | damper down event
162
+DampUp    |  ^   | damper up event
163
+DampUpDn  |  X   | damper up/down event
164
+SostDn    |  {   | sostenuto down event
165
+Section   |  S   | section boundary
166
+SostUp    |  }   | sostenuto up event
167
+Heel      |  H   | heel event 
168
+Tie Begin |  T   | begin of a tied note
169
+Tie End   |  _   | end of a tied note
170
+Onset     |  *   | note onset  
171
+
172
+
173
+
174
+Decoration Sytax:
175
+------------------
176
+
177
+!<dyn_mark>             Assign dynamics
178
+!(<dyn_mark>)             - less uncertain dynamic mark
179
+!<upper-case-dyn-mark>    - begin of dynamic fork (See note below regarding dynamic forks)
180
+!!<upper-case-dyn-mark>   - end of dynamic fork                      
181
+~<mark>                 Insert or remove event (See pedal marks below.)
182
+@<new_tick_value>       Move event to a new time position
183
+%<grace_note_flag>      Flag note as a grace note
184
+%%<grace_note_flag>         -last note in grace note sequence
185
+$<sci_pitch>            Assign a note a new pitch
186
+
187
+<grace_note_flag>
188
+  b (begin grace)
189
+  a (add grace and end grace)
190
+  s (subtract grace and end grace)
191
+  g (grace note) 
192
+  A (after first)
193
+  N (soon after first)
194
+
195
+Note: The first non-grace note in a grace note sequence is marked with a %b.
196
+The last non-grace note in a grace note sequence is marked with a %s or %a.
197
+
198
+Where: %s = steal time from the note marked with %b.
199
+       %a = insert time prior to the note marked with %a.
200
+       
201
+ The last (by row number) note (grace or non-grace) in the the sequence
202
+is marked with %%# where # is replaced with a,b,s,or g.
203
+
204
+It is only necessary to mark the tick number of grace notes in order
205
+to give the time sequence of the notes. A single grace note therefore does
206
+not require an explict tick mark notation (i.e. @####)
207
+
208
+
209
+Insert/delete  Event Marks: <mark>
210
+-----------------------------------
211
+ d (sostenuto down - just after note onset)
212
+ u (    "      up  - just before this event)
213
+ x (    "      up    just before this event and down just after it)
214
+ D (damper pedal down - after this event)
215
+ U (damper pedal up - before this event)
216
+ _ (set tie end flag)
217
+ & (skip this event)
218
+
219
+Dynamic Marks: <dyn-mark>
220
+--------------------------
221
+  s       (silent note)
222
+  pppp-
223
+  pppp
224
+  pppp+
225
+  ppp-
226
+  ppp
227
+  ppp+
228
+  pp-
229
+  pp
230
+  pp+
231
+  p-
232
+  p
233
+  p+
234
+  mp-
235
+  mp
236
+  mp+
237
+  mf-
238
+  mf
239
+  mf+
240
+  f-
241
+  f
242
+  f+
243
+  ff
244
+  ff+
245
+  fff
246
+
247
+Note: Dynamic Forks:
248
+--------------------
249
+Use upper case dynamic letters to indicate forks in the dynamics
250
+which should be filled automatically.  Note that all notes
251
+in the voice assigned to the first note in the fork will be
252
+included in the dynamic change. To exclude a note from the
253
+fork assign it a lower case mark.
254
+

+ 8
- 0
m4/os_64.m4 View File

@@ -0,0 +1,8 @@
1
+AC_DEFUN([AX_FUNC_OS_64],
2
+[AC_CACHE_CHECK([operating system address width],
3
+[ax_cv_os_64],
4
+[ax_cv_os_64=`uname -m`])
5
+if test x"$ax_cv_os_64" = xx86_64; then
6
+AC_DEFINE([OS_64], 1,[Operating system is 64 bits.])
7
+fi
8
+]) # AX_FUNC_OS_TYPE

+ 11
- 0
m4/os_type.m4 View File

@@ -0,0 +1,11 @@
1
+AC_DEFUN([AX_FUNC_OS_TYPE],
2
+[AC_CACHE_CHECK([operating system type],
3
+[ax_cv_os_type],
4
+[ax_cv_os_type=`uname`])
5
+if test x"$ax_cv_os_type" = xLinux; then
6
+AC_DEFINE([OS_LINUX], 1,[Operating system is Linux.])
7
+fi
8
+if test x"$ax_cv_os_type" = xDarwin; then
9
+AC_DEFINE([OS_OSX], 1,[Operating system is Darwin.])
10
+fi]) # AX_FUNC_OS_TYPE
11
+

+ 397
- 0
src/cmtools/audiodev.c View File

@@ -0,0 +1,397 @@
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 "cmLinkedHeap.h"
9
+#include "cmFileSys.h"
10
+#include "cmText.h"
11
+
12
+#include "cmPgmOpts.h"
13
+
14
+#include "cmTime.h"
15
+#include "cmAudioPort.h"
16
+#include "cmApBuf.h"  // only needed for cmApBufTest().
17
+#include "cmAudioPortFile.h"
18
+#include "cmAudioAggDev.h"
19
+#include "cmAudioNrtDev.h"
20
+
21
+#include "cmFloatTypes.h"
22
+#include "cmAudioFile.h"
23
+#include "cmFile.h"
24
+
25
+enum
26
+{
27
+ kOkAdRC = cmOkRC,
28
+ kAudioPortFailAdRC,
29
+ kAudioPortFileFailAdRC,
30
+ kAudioPortNrtFailAdRC,
31
+ kAudioBufFailAdRC
32
+};
33
+
34
+const cmChar_t* poBegHelpStr =
35
+  "audiodev Test the real-time audio ports"
36
+  "\n"
37
+  "audiodev -i<in_dev_index> -o <out_dev_index -s <sample_rate>\n"
38
+  "\n"
39
+  "All arguments are optional. The default input and output device index is 0.\n"
40
+  "\n";
41
+  
42
+const cmChar_t* poEndHelpStr = "";
43
+  
44
+/// [cmAudioPortExample]
45
+
46
+// See cmApPortTest() below for the main point of entry.
47
+
48
+// Data structure used to hold the parameters for cpApPortTest()
49
+// and the user defined data record passed to the host from the
50
+// audio port callback functions.
51
+typedef struct
52
+{
53
+  unsigned      bufCnt;         // 2=double buffering 3=triple buffering
54
+  unsigned      chIdx;          // first test channel
55
+  unsigned      chCnt;          // count of channels to test
56
+  unsigned      framesPerCycle; // DSP frames per cycle
57
+  unsigned      bufFrmCnt;      // count of DSP frames used by the audio buffer  (bufCnt * framesPerCycle)
58
+  unsigned      bufSmpCnt;      // count of samples used by the audio buffer     (chCnt  * bufFrmCnt)
59
+  unsigned      inDevIdx;       // input device index
60
+  unsigned      outDevIdx;      // output device index
61
+  double        srate;          // audio sample rate
62
+  unsigned      meterMs;        // audio meter buffer length
63
+
64
+  // param's and state for cmApSynthSine()
65
+  unsigned      phase;          // sine synth phase
66
+  double        frqHz;          // sine synth frequency in Hz
67
+
68
+  // buffer and state for cmApCopyIn/Out()
69
+  cmApSample_t* buf;            // buf[bufSmpCnt] - circular interleaved audio buffer
70
+  unsigned      bufInIdx;       // next input buffer index
71
+  unsigned      bufOutIdx;      // next output buffer index
72
+  unsigned      bufFullCnt;     // count of full samples
73
+
74
+  // debugging log data arrays 
75
+  unsigned      logCnt;        // count of elements in log[] and ilong[]
76
+  char*         log;           // log[logCnt]
77
+  unsigned*     ilog;          // ilog[logCnt]
78
+  unsigned      logIdx;        // current log index
79
+
80
+  unsigned      cbCnt;         // count the callback
81
+} cmApPortTestRecd;
82
+
83
+unsigned _cmGlobalInDevIdx  = 0;
84
+unsigned _cmGlobalOutDevIdx = 0;
85
+unsigned _cmGlobalCbCnt     = 0;
86
+
87
+#define aSrate  48000
88
+#define aFrmN  aSrate*10
89
+#define aChN   2
90
+#define abufN  aFrmN*aChN
91
+
92
+cmApSample_t abuf[ abufN ];
93
+unsigned     abufi  = 0;
94
+
95
+void _abuf_copy_in( cmApAudioPacket_t* pktArray, unsigned pktN )
96
+{
97
+  unsigned i,j,k;
98
+  for(i=0; i<pktN; ++i)
99
+  {
100
+    cmApSample_t* sp   = (cmApSample_t*)(pktArray[i].audioBytesPtr);
101
+    unsigned      frmN = pktArray[i].audioFramesCnt;
102
+    unsigned      chN  = cmMin(pktArray[i].chCnt,aChN);
103
+    
104
+    for(j=0; abufi<aFrmN && j<frmN; ++j, ++abufi)
105
+      for(k=0; k<chN; ++k)
106
+        abuf[ (k*aFrmN) + abufi ] = *sp++;
107
+
108
+  }
109
+
110
+  
111
+}
112
+  
113
+void _abuf_write_audio_file(cmCtx_t* ctx )
114
+{
115
+  cmApSample_t* sigVV[ ] = { abuf, abuf + aFrmN };
116
+  cmAudioFileWriteFileFloat(   "/home/kevin/temp/temp.wav", aSrate, 16, aFrmN, 2, sigVV, &ctx->rpt );
117
+}
118
+
119
+void _abuf_write_csv_file(cmCtx_t* ctx )
120
+{
121
+  cmFileH_t fH = cmFileNullHandle;
122
+  unsigned i = 0,j;
123
+  cmFileOpen( &fH, "/home/kevin/temp/temp.csv", kWriteFileFl, &ctx->rpt );
124
+
125
+  for(i=0; i<aFrmN; ++i)
126
+  {
127
+    for(j=0; j<aChN; ++j)
128
+    {
129
+      char comma = j==aChN-1 ? ' ':',';
130
+      
131
+      cmFilePrintf(fH, "%f%c",abuf[ aFrmN*j + i ], comma );
132
+    }
133
+       
134
+    cmFilePrintf(  fH, "\n");
135
+  }
136
+  
137
+  cmFileClose(&fH);
138
+}
139
+
140
+void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
141
+{
142
+  cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
143
+
144
+  cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
145
+
146
+  if( outPktArray != NULL )
147
+    _abuf_copy_in(outPktArray,outPktCnt);
148
+
149
+
150
+  _cmGlobalCbCnt += 1;
151
+}
152
+
153
+cmRC_t audio_port_test( cmCtx_t* ctx, cmApPortTestRecd* r, bool runFl )
154
+{
155
+  cmRC_t        rc = kOkAdRC;
156
+  unsigned       i = 0;
157
+  int    srateMult = 0;  
158
+  cmRpt_t*     rpt = &ctx->rpt;
159
+  
160
+  cmApSample_t buf[r->bufSmpCnt];
161
+  char         log[r->logCnt];
162
+  unsigned    ilog[r->logCnt];
163
+  
164
+  r->buf        = buf;
165
+  r->log        = log;
166
+  r->ilog       = ilog;
167
+  r->cbCnt      = 0;
168
+
169
+  _cmGlobalInDevIdx = r->inDevIdx;
170
+  _cmGlobalOutDevIdx= r->outDevIdx;
171
+
172
+  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);
173
+
174
+  if( cmApFileAllocate(rpt) != kOkApRC )
175
+  {
176
+    rc = cmErrMsg(&ctx->err, kAudioPortFileFailAdRC,"Audio port file allocation failed.");
177
+    goto errLabel;
178
+  }
179
+
180
+  // allocate the non-real-time port
181
+  if( cmApNrtAllocate(rpt) != kOkApRC )
182
+  {
183
+    rc = cmErrMsg(&ctx->err, kAudioPortNrtFailAdRC,"Non-real-time system allocation failed.");
184
+    goto errLabel;
185
+  }
186
+
187
+  // initialize the audio device interface
188
+  if( cmApInitialize(rpt) != kOkApRC )
189
+  {
190
+    rc = cmErrMsg(&ctx->err, kAudioPortFailAdRC,"Port initialize failed.\n");
191
+    goto errLabel;
192
+  }
193
+  
194
+  // report the current audio device configuration
195
+  for(i=0; i<cmApDeviceCount(); ++i)
196
+  {
197
+    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));
198
+  }
199
+  
200
+  // report the current audio devices using the audio port interface function
201
+  cmApReport(rpt);
202
+
203
+  if( runFl )
204
+  {
205
+    // initialize the audio buffer
206
+    cmApBufInitialize( cmApDeviceCount(), r->meterMs );
207
+
208
+    // setup the buffer for the output device
209
+    cmApBufSetup( r->outDevIdx, r->srate, r->framesPerCycle, r->bufCnt, cmApDeviceChannelCount(r->outDevIdx,true), r->framesPerCycle, cmApDeviceChannelCount(r->outDevIdx,false), r->framesPerCycle, srateMult );
210
+    
211
+    // setup the buffer for the input device
212
+    if( r->inDevIdx != r->outDevIdx )
213
+      cmApBufSetup( r->inDevIdx, r->srate, r->framesPerCycle, r->bufCnt, cmApDeviceChannelCount(r->inDevIdx,true), r->framesPerCycle, cmApDeviceChannelCount(r->inDevIdx,false), r->framesPerCycle, srateMult ); 
214
+
215
+
216
+    // setup an output device
217
+    if(cmApDeviceSetup(r->outDevIdx,r->srate,r->framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
218
+      rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"Out audio device setup failed.\n");
219
+    else
220
+      // setup an input device
221
+      if( cmApDeviceSetup(r->inDevIdx,r->srate,r->framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
222
+        rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"In audio device setup failed.\n");
223
+      else
224
+        // start the input device
225
+        if( cmApDeviceStart(r->inDevIdx) != kOkApRC )
226
+          rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"In audio device start failed.\n");
227
+        else
228
+          // start the output device
229
+          if( cmApDeviceStart(r->outDevIdx) != kOkApRC )
230
+            rc = cmErrMsg(&ctx->err, kAudioPortFailAdRC,"Out audio device start failed.\n");
231
+          else
232
+            cmRptPrintf(rpt,"Started...");
233
+
234
+    cmApBufEnableChannel(r->inDevIdx, -1, kEnableApFl | kInApFl );
235
+    cmApBufEnableMeter(  r->inDevIdx, -1, kEnableApFl | kInApFl );
236
+
237
+    cmApBufEnableChannel(r->outDevIdx, -1, kEnableApFl | kOutApFl );
238
+    cmApBufEnableMeter(  r->outDevIdx, -1, kEnableApFl | kOutApFl );
239
+    
240
+    cmRptPrintf(rpt,"q=quit O/o=output tone, I/i=input tone P/p=pass s=buf report\n");
241
+    char c;
242
+    while((c=getchar()) != 'q')
243
+    {
244
+      //cmApAlsaDeviceRtReport(rpt,r->outDevIdx);
245
+
246
+      switch(c)
247
+      {
248
+        case 'i':
249
+        case 'I':
250
+          cmApBufEnableTone(r->inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
251
+          break;
252
+
253
+        case 'o':
254
+        case 'O':
255
+          cmApBufEnableTone(r->outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
256
+          break;
257
+
258
+        case 'p':
259
+        case 'P':
260
+          cmApBufEnablePass(r->outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
261
+          break;
262
+          
263
+        case 's':
264
+          cmApBufReport(rpt);
265
+          cmRptPrintf(rpt,"CB:%i\n",_cmGlobalCbCnt);
266
+          break;
267
+      }
268
+
269
+    }
270
+
271
+    // stop the input device
272
+    if( cmApDeviceIsStarted(r->inDevIdx) )
273
+      if( cmApDeviceStop(r->inDevIdx) != kOkApRC )
274
+        cmRptPrintf(rpt,"In device stop failed.\n");
275
+
276
+    // stop the output device
277
+    if( cmApDeviceIsStarted(r->outDevIdx) )
278
+      if( cmApDeviceStop(r->outDevIdx) != kOkApRC )
279
+        cmRptPrintf(rpt,"Out device stop failed.\n");
280
+  }
281
+
282
+ errLabel:
283
+
284
+  // release any resources held by the audio port interface
285
+  if( cmApFinalize() != kOkApRC )
286
+    rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"Finalize failed.\n");
287
+
288
+  cmApBufFinalize();
289
+
290
+  cmApNrtFree();
291
+  cmApFileFree();
292
+
293
+  // report the count of audio buffer callbacks
294
+  cmRptPrintf(rpt,"cb count:%i\n", r->cbCnt );
295
+  //for(i=0; i<_logCnt; ++i)
296
+  //  cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]);
297
+  //cmRptPrintf(rpt,"\n");
298
+
299
+
300
+  return rc;  
301
+}
302
+
303
+void print( void* arg, const char* text )
304
+{
305
+  printf("%s",text);
306
+}
307
+
308
+int main( int argc, char* argv[] )
309
+{
310
+  enum
311
+  {
312
+   kSratePoId = kBasePoId,
313
+   kHzPoId,
314
+   kChIdxPoId,
315
+   kChCntPoId,
316
+   kBufCntPoId,
317
+   kFrmCntPoId,
318
+   kFrmsPerBufPoId,
319
+   kInDevIdxPoId,
320
+   kOutDevIdxPoId,
321
+   kReportFlagPoId
322
+  };
323
+  
324
+  cmRC_t          rc              = cmOkRC;
325
+  bool            memDebugFl      = cmDEBUG_FL;
326
+  unsigned        memGuardByteCnt = memDebugFl ? 8 : 0;
327
+  unsigned        memAlignByteCnt = 16;
328
+  unsigned        memFlags        = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;  
329
+  cmPgmOptH_t     poH             = cmPgmOptNullHandle;
330
+  const cmChar_t* appTitle        = "audiodev";
331
+  unsigned        reportFl        = 0;
332
+  cmCtx_t         ctx;
333
+  cmApPortTestRecd r; 
334
+  memset(&r,0,sizeof(r));
335
+  r.meterMs = 50;
336
+  r.logCnt  = 100;
337
+
338
+  memset(abuf,0,sizeof(cmApSample_t)*abufN);
339
+  
340
+  cmCtxSetup(&ctx,appTitle,print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
341
+
342
+  cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt );
343
+
344
+  cmFsInitialize( &ctx, appTitle );
345
+
346
+  cmTsInitialize(&ctx );
347
+
348
+  cmPgmOptInitialize(&ctx, &poH, poBegHelpStr, poEndHelpStr );
349
+
350
+  cmPgmOptInstallDbl( poH, kSratePoId,           's', "srate",      0,       48000,          &r.srate,     1,
351
+    "Audio system sample rate." );
352
+
353
+  cmPgmOptInstallDbl( poH, kHzPoId,              'z', "hz",         0,        1000,          &r.frqHz,     1,
354
+    "Tone frequency in Hertz." );
355
+   
356
+  cmPgmOptInstallUInt( poH, kChIdxPoId,          'x', "ch_index",   0,           0,          &r.chIdx,     1,
357
+    "Index of first channel index." );
358
+
359
+  cmPgmOptInstallUInt( poH, kChCntPoId,          'c', "ch_cnt",     0,           2,          &r.chCnt,     1,
360
+    "Count of audio channels." );
361
+  
362
+  cmPgmOptInstallUInt( poH, kBufCntPoId,         'b', "buf_cnt",    0,           3,          &r.bufCnt,    1,
363
+    "Count of audio buffers. (e.g. 2=double buffering, 3=triple buffering)" );
364
+
365
+  cmPgmOptInstallUInt( poH, kFrmsPerBufPoId,     'f', "frames_per_buf",0,      512,   &r.framesPerCycle,   1,
366
+    "Count of audio channels." );
367
+  
368
+  cmPgmOptInstallUInt( poH, kInDevIdxPoId,       'i', "in_dev_index",0,          0,          &r.inDevIdx,  1,
369
+    "Input device index as taken from the audio device report." );
370
+
371
+  cmPgmOptInstallUInt( poH, kOutDevIdxPoId,      'o', "out_dev_index",0,         0,          &r.outDevIdx, 1,
372
+    "Output device index as taken from the audio device report." );
373
+  
374
+  cmPgmOptInstallFlag( poH, kReportFlagPoId,     'r', "report_flag",  0,         1,          &reportFl,    1,
375
+    "Print an audio device report." );
376
+  
377
+    // parse the command line arguments
378
+  if( cmPgmOptParse(poH, argc, argv ) == kOkPoRC )
379
+  {
380
+    // handle the built-in arg's (e.g. -v,-p,-h)
381
+    // (returns false if only built-in options were selected)
382
+    if( cmPgmOptHandleBuiltInActions(poH, &ctx.rpt ) == false )
383
+      goto errLabel;
384
+
385
+    rc = audio_port_test( &ctx, &r, !reportFl );
386
+  }
387
+
388
+ errLabel:
389
+  _abuf_write_audio_file(&ctx);
390
+  cmPgmOptFinalize(&poH);
391
+  cmTsFinalize();
392
+  cmFsFinalize();
393
+  cmMdReport( kIgnoreNormalMmFl );
394
+  cmMdFinalize();
395
+  
396
+  return rc;
397
+}

+ 431
- 0
src/cmtools/cmtools.c View File

@@ -0,0 +1,431 @@
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 "cmLinkedHeap.h"
9
+#include "cmFileSys.h"
10
+#include "cmText.h"
11
+
12
+#include "cmPgmOpts.h"
13
+
14
+#include "cmXScore.h"
15
+#include "cmMidiScoreFollow.h"
16
+#include "cmScoreProc.h"
17
+
18
+
19
+#include "cmSymTbl.h"
20
+#include "cmTime.h"
21
+#include "cmMidi.h"
22
+#include "cmScore.h"
23
+
24
+#include "cmMidiFile.h"
25
+
26
+#include "cmFloatTypes.h"
27
+#include "cmAudioFile.h"
28
+#include "cmTimeLine.h"
29
+
30
+enum
31
+{
32
+ kOkCtRC = cmOkRC,
33
+ kNoActionIdSelectedCtRC,
34
+ kMissingRequiredFileNameCtRC,
35
+ kScoreGenFailedCtRC,
36
+ kScoreFollowFailedCtRC,
37
+ kMidiFileRptFailedCtRC,
38
+ kTimeLineRptFailedCtRC,
39
+ kAudioFileRptFailedCtRC
40
+};
41
+
42
+
43
+const cmChar_t poEndHelpStr[] = "";
44
+const cmChar_t poBegHelpStr[] =
45
+  "xscore_proc Music XML to electronic score generator\n"
46
+  "\n"
47
+  "USAGE:\n"
48
+  "\n"
49
+  "Parse an XML score file and decoration file to produce a score file in CSV format.\n"
50
+  "\n"
51
+  "cmtool --score_gen -x <xml_file> -d <dec_fn> {-c <csvScoreOutFn} {-m <midiOutFn>} {-s <svgOutFn>} {-r report} {-b begMeasNumb} {t begTempoBPM}\n"
52
+  "\n"
53
+  "Notes:\n"
54
+  "1.  If <dec_fn> does not exist then a decoration template file will be generated based on the MusicXML file. \n"
55
+  "2.  Along with the CSV score file MIDI and HTML/SVG files will also be produced based on the contents of the MusicXML and decoration file.\n"
56
+  "3. See README.md for a detailed description of the how to edit the decoration file.\n"
57
+  "\n"
58
+  "\n"
59
+  "Use the score follower to generate a timeline configuration file.\n"
60
+  "\n"
61
+  "cmtool --timeline_gen -c <csvScoreFn> -i <midiInFn> -r <matchRptFn> -s <matchSvgFn> {-m <midiOutFn>} {-t timelineOutFn} \n"
62
+  "\n"
63
+  "Measure some perforamance attributes:\n"
64
+  "\n"
65
+  "cmtool --meas_gen -g <pgmRsrcFn> -r <measRptFn>\n"
66
+  "\n"
67
+  "Generate a score file report\n"
68
+  "\n"
69
+  "cmtool --score_report -c <csvScoreFn> -r <scoreRptFn>\n"
70
+  "\n"
71
+  "Generate a MIDI file report and optional SVG piano roll image\n"
72
+  "\n"
73
+  "cmtool --midi_report -i <midiInFn> -r <midiRptFn> {-s <svgOutFn>}\n"
74
+  "\n"
75
+  "Generate a timeline report\n"
76
+  "\n"
77
+  "cmtool --timeline_report -t <timelineFn> -r <timelineRptFn>\n"
78
+  "\n"
79
+  "Generate an audio file report\n"
80
+  "\n"
81
+  "cmtool --audiofile_report -a <audioFn> -r <rptFn>\n"
82
+  "\n";
83
+
84
+
85
+void print( void* arg, const char* text )
86
+{
87
+  printf("%s",text);
88
+}
89
+
90
+bool verify_file_exists( cmCtx_t* ctx, const cmChar_t* fn, const cmChar_t* msg )
91
+{
92
+  if( fn == NULL || cmFsIsFile(fn)==false )
93
+    return cmErrMsg(&ctx->err,kMissingRequiredFileNameCtRC,"The required file <%s> does not exist.",msg);
94
+
95
+  return kOkCtRC;
96
+}
97
+
98
+bool verify_non_null_filename( cmCtx_t* ctx, const cmChar_t* fn, const cmChar_t* msg )
99
+{
100
+  if( fn == NULL )
101
+    return cmErrMsg(&ctx->err,kMissingRequiredFileNameCtRC,"The required file name <%s> is blank.",msg);
102
+
103
+  return kOkCtRC;
104
+}
105
+
106
+cmRC_t score_gen( cmCtx_t* ctx, const cmChar_t* xmlFn, const cmChar_t* decFn, const cmChar_t* csvOutFn, const cmChar_t* midiOutFn, const cmChar_t* svgOutFn, unsigned reportFl, int begMeasNumb, int begTempoBPM, bool svgStandAloneFl, bool svgPanZoomFl )
107
+{
108
+  cmRC_t rc;
109
+  if((rc = verify_file_exists(ctx,xmlFn,"XML file")) != kOkCtRC )
110
+    return rc;
111
+  
112
+  if( cmXScoreTest( ctx, xmlFn, decFn, csvOutFn, midiOutFn, svgOutFn, reportFl, begMeasNumb, begTempoBPM, svgStandAloneFl, svgPanZoomFl ) != kOkXsRC )
113
+    return cmErrMsg(&ctx->err,kScoreGenFailedCtRC,"score_gen failed.");
114
+    
115
+  return kOkCtRC;
116
+}
117
+
118
+cmRC_t score_follow( cmCtx_t* ctx, const cmChar_t* csvScoreFn, const cmChar_t* midiInFn, const cmChar_t* matchRptOutFn, const cmChar_t* matchSvgOutFn,  const cmChar_t* midiOutFn, const cmChar_t* timelineFn )
119
+{
120
+  cmRC_t rc;
121
+  
122
+  if((rc = verify_file_exists(ctx,csvScoreFn,"Score CSV file")) != kOkCtRC )
123
+    return rc;
124
+
125
+  if((rc = verify_file_exists(ctx,midiInFn,"MIDI input file")) != kOkCtRC )
126
+    return rc;
127
+
128
+  //if((rc = verify_file_exists(ctx,matchRptOutFn,"Match report file")) != kOkCtRC )
129
+  //  return rc;
130
+
131
+  //if((rc = verify_file_exists(ctx,matchSvgOutFn,"Match HTML/SVG file")) != kOkCtRC )
132
+  //  return rc;
133
+  
134
+  if(cmMidiScoreFollowMain(ctx, csvScoreFn, midiInFn, matchRptOutFn, matchSvgOutFn, midiOutFn, timelineFn) != kOkMsfRC )
135
+    return cmErrMsg(&ctx->err,kScoreFollowFailedCtRC,"score_follow failed.");
136
+    
137
+  return kOkCtRC;         
138
+}
139
+
140
+cmRC_t meas_gen( cmCtx_t* ctx, const cmChar_t* pgmRsrcFn, const cmChar_t* outFn )
141
+{
142
+  cmRC_t rc;
143
+  
144
+  if((rc = verify_file_exists(ctx,pgmRsrcFn,"Program resource file")) != kOkCtRC )
145
+    return rc;
146
+
147
+  if((rc = verify_non_null_filename( ctx,outFn,"Measurements output file.")) != kOkCtRC )
148
+    return rc;
149
+  
150
+  return cmScoreProc(ctx, "meas", pgmRsrcFn, outFn );
151
+}
152
+
153
+cmRC_t score_report( cmCtx_t* ctx, const cmChar_t* csvScoreFn, const cmChar_t* rptFn )
154
+{
155
+  cmRC_t rc;
156
+  
157
+  if((rc = verify_file_exists(ctx,csvScoreFn,"Score CSV file")) != kOkCtRC )
158
+    return rc;
159
+
160
+  cmScoreReport(ctx,csvScoreFn,rptFn);
161
+  
162
+  return rc;
163
+}
164
+
165
+
166
+cmRC_t midi_file_report( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* rptFn, const cmChar_t* svgFn, bool standAloneFl, bool panZoomFl )
167
+{
168
+  cmRC_t rc ;
169
+
170
+  if((rc = verify_file_exists(ctx,midiFn,"MIDI file")) != kOkCtRC )
171
+    return rc;
172
+  
173
+  if((rc = cmMidiFileReport(ctx, midiFn, rptFn )) != kOkMfRC )
174
+    return cmErrMsg(&ctx->err,kMidiFileRptFailedCtRC,"MIDI file report generation failed.");  
175
+
176
+  if( svgFn != NULL )
177
+    if((rc = cmMidiFileGenSvgFile(ctx, midiFn, svgFn, "midi_file_svg.css", standAloneFl, panZoomFl )) != kOkMfRC )
178
+      return cmErrMsg(&ctx->err,kMidiFileRptFailedCtRC,"MIDI file SVG output generation failed.");
179
+  
180
+  return kOkCtRC;
181
+}
182
+
183
+cmRC_t timeline_report( cmCtx_t* ctx, const cmChar_t* timelineFn, const cmChar_t* tlPrefixPath, const cmChar_t* rptFn )
184
+{
185
+  cmRC_t rc ;
186
+
187
+  if((rc = verify_file_exists(ctx,timelineFn,"Timeline file")) != kOkCtRC )
188
+    return rc;
189
+
190
+  if((rc = cmTimeLineReport( ctx, timelineFn, tlPrefixPath, rptFn  )) != kOkTlRC )
191
+    return cmErrMsg(&ctx->err,kTimeLineRptFailedCtRC,"The timeline file report failed.");
192
+
193
+  return rc;
194
+}
195
+
196
+cmRC_t audio_file_report( cmCtx_t* ctx, const cmChar_t* audioFn, const cmChar_t* rptFn )
197
+{
198
+  cmRC_t rc;
199
+
200
+  if((rc = verify_file_exists(ctx,audioFn,"Audio file")) != kOkCtRC )
201
+    return rc;
202
+
203
+  if((rc = cmAudioFileReportInfo( ctx, audioFn, rptFn  )) != kOkTlRC )
204
+    return cmErrMsg(&ctx->err,kAudioFileRptFailedCtRC,"The audio file report failed.");
205
+
206
+  return rc;
207
+}
208
+
209
+cmRC_t  midi_trim(cmCtx_t* ctx, const cmChar_t* midiInFn, unsigned begMidiUId, unsigned endMidiUId, const cmChar_t* midiOutFn)
210
+{
211
+  cmRC_t rc;
212
+  
213
+  if((rc = verify_file_exists(ctx,midiInFn,"MIDI file")) != kOkCtRC )
214
+    return rc;
215
+  // kNoteTerminateFl | kPedalTerminateFl
216
+  return cmMidiFileTrimFn(ctx,  midiInFn, begMidiUId, endMidiUId, 0, midiOutFn );
217
+}
218
+
219
+
220
+int main( int argc, char* argv[] )
221
+{
222
+  cmRC_t rc = cmOkRC;
223
+  enum
224
+  {
225
+   kInvalidPoId = kBasePoId,
226
+   kActionPoId,
227
+   kXmlFileNamePoId,
228
+   kDecorateFileNamePoId,
229
+   kCsvOutFileNamePoId,
230
+   kPgmRsrcFileNamePoId,
231
+   kMidiOutFileNamePoId,
232
+   kMidiInFileNamePoId,
233
+   kSvgOutFileNamePoId,
234
+   kStatusOutFileNamePoId,
235
+   kTimelineFileNamePoId,
236
+   kTimelinePrefixPoId,
237
+   kAudioFileNamePoId,
238
+   kReportFlagPoId,
239
+   kSvgStandAloneFlPoId,
240
+   kSvgPanZoomFlPoId,
241
+   kBegMeasPoId,
242
+   kBegBpmPoId,
243
+   kBegMidiUidPoId,
244
+   kEndMidiUidPoId
245
+  };
246
+
247
+  enum {
248
+        kNoSelId,
249
+        kScoreGenSelId,
250
+        kScoreFollowSelId,
251
+        kMeasGenSelId,
252
+        kScoreReportSelId,
253
+        kMidiReportSelId,
254
+        kTimelineReportSelId,
255
+        kAudioReportSelId,
256
+        kMidiTrimSelId
257
+  };
258
+    
259
+  
260
+  // initialize the heap check library
261
+  bool            memDebugFl      = 0; //cmDEBUG_FL;
262
+  unsigned        memGuardByteCnt = memDebugFl ? 8 : 0;
263
+  unsigned        memAlignByteCnt = 16;
264
+  unsigned        memFlags        = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;  
265
+  cmPgmOptH_t     poH             = cmPgmOptNullHandle;
266
+  const cmChar_t* appTitle        = "cmtools";
267
+  cmCtx_t         ctx;
268
+  const cmChar_t* xmlFn           = NULL;
269
+  const cmChar_t* decFn           = NULL;
270
+  const cmChar_t* pgmRsrcFn       = NULL;
271
+  const cmChar_t* csvScoreFn      = NULL;
272
+  const cmChar_t* midiOutFn       = NULL;
273
+  const cmChar_t* midiInFn        = NULL;
274
+  const cmChar_t* audioFn         = NULL;
275
+  const cmChar_t* svgOutFn        = NULL;
276
+  const cmChar_t* timelineFn      = NULL;
277
+  const cmChar_t* timelinePrefix  = NULL;
278
+  const cmChar_t* rptFn           = NULL;
279
+  unsigned        reportFl        = 0;
280
+  unsigned        svgStandAloneFl = 1;
281
+  unsigned        svgPanZoomFl    = 1;
282
+  int             begMeasNumb     = 0;
283
+  int             begTempoBPM     = 60;
284
+  unsigned        begMidiUId      = cmInvalidId;
285
+  unsigned        endMidiUId      = cmInvalidId;
286
+  unsigned        actionSelId     = kNoSelId;
287
+    
288
+  cmCtxSetup(&ctx,appTitle,print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
289
+
290
+  cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt );
291
+
292
+  cmFsInitialize( &ctx, appTitle);
293
+
294
+  cmTsInitialize(&ctx );
295
+
296
+  cmPgmOptInitialize(&ctx, &poH, poBegHelpStr, poEndHelpStr );
297
+
298
+  cmPgmOptInstallEnum( poH, kActionPoId, 'S', "score_gen",    0, kScoreGenSelId,    kNoSelId,  &actionSelId, 1,
299
+    "Run the score generation tool.","Action selector");
300
+
301
+  cmPgmOptInstallEnum( poH, kActionPoId, 'F', "score_follow", 0, kScoreFollowSelId, kNoSelId,  &actionSelId, 1,
302
+    "Run the time line marker generation tool.",NULL);
303
+
304
+  cmPgmOptInstallEnum( poH, kActionPoId, 'M', "meas_gen",     0, kMeasGenSelId,     kNoSelId,  &actionSelId, 1,
305
+    "Generate perfomance measurements.",NULL);
306
+
307
+  cmPgmOptInstallEnum( poH, kActionPoId, 'R', "score_report", 0, kScoreReportSelId, kNoSelId,  &actionSelId, 1,
308
+    "Generate a score file report.",NULL);
309
+
310
+  cmPgmOptInstallEnum( poH, kActionPoId, 'I', "midi_report", 0, kMidiReportSelId, kNoSelId,  &actionSelId, 1,
311
+    "Generate a MIDI file report and optional SVG piano roll output.",NULL);
312
+
313
+  cmPgmOptInstallEnum( poH, kActionPoId, 'E', "timeline_report", 0, kTimelineReportSelId, kNoSelId,  &actionSelId, 1,
314
+    "Generate a timeline report.",NULL);
315
+
316
+  cmPgmOptInstallEnum( poH, kActionPoId, 'A', "audio_report",    0, kAudioReportSelId, kNoSelId,  &actionSelId, 1,
317
+    "Generate an audio file report.",NULL);
318
+
319
+  cmPgmOptInstallEnum( poH, kActionPoId, 'T', "midi_trim",       0, kMidiTrimSelId, kNoSelId,  &actionSelId, 1,
320
+    "Trim a MIDI file to create a shortened version.",NULL);
321
+  
322
+  cmPgmOptInstallStr( poH, kXmlFileNamePoId,      'x', "muisic_xml_fn",0,    NULL,         &xmlFn,        1, 
323
+    "Name of the input MusicXML file.");
324
+
325
+  cmPgmOptInstallStr( poH, kDecorateFileNamePoId, 'd', "dec_fn",       0,    NULL,         &decFn,        1, 
326
+    "Name of a score decoration file.");
327
+
328
+  cmPgmOptInstallStr( poH, kCsvOutFileNamePoId,   'c', "score_csv_fn",0,    NULL,         &csvScoreFn,    1, 
329
+    "Name of a CSV score file.");
330
+
331
+  cmPgmOptInstallStr( poH, kPgmRsrcFileNamePoId,  'g', "pgm_rsrc_fn", 0,     NULL,         &pgmRsrcFn,     1, 
332
+    "Name of program resource file.");
333
+  
334
+  cmPgmOptInstallStr( poH, kMidiOutFileNamePoId,  'm', "midi_out_fn",  0,    NULL,         &midiOutFn,     1, 
335
+    "Name of a MIDI file to generate as output.");
336
+  
337
+  cmPgmOptInstallStr( poH, kMidiInFileNamePoId,   'i', "midi_in_fn",   0,    NULL,         &midiInFn,      1, 
338
+    "Name of a MIDI file to generate as output.");
339
+  
340
+  cmPgmOptInstallStr( poH, kSvgOutFileNamePoId,   's', "svg_fn",       0,    NULL,         &svgOutFn,      1, 
341
+    "Name of a HTML/SVG file to generate as output.");
342
+
343
+  cmPgmOptInstallStr( poH, kTimelineFileNamePoId, 't', "timeline_fn",  0,    NULL,         &timelineFn,    1,
344
+    "Name of a timeline to generate as output.");
345
+  
346
+  cmPgmOptInstallStr( poH, kTimelinePrefixPoId,   'l', "tl_prefix",    0,    NULL,         &timelinePrefix,1,
347
+    "Timeline data path prefix.");
348
+
349
+  cmPgmOptInstallStr( poH, kAudioFileNamePoId,    'a', "audio_fn",    0,    NULL,          &audioFn,       1,
350
+    "Audio file name.");
351
+  
352
+  cmPgmOptInstallStr( poH, kStatusOutFileNamePoId,'r', "report_fn",    0,    NULL,         &rptFn,         1, 
353
+    "Name of a status file to generate as output.");
354
+  
355
+  cmPgmOptInstallFlag( poH, kReportFlagPoId,      'f', "debug_fl",     0,       1,         &reportFl,      1,
356
+    "Print a report of the score following processing." );
357
+
358
+  cmPgmOptInstallInt( poH, kBegMeasPoId,          'b', "beg_meas",     0,       1,         &begMeasNumb,   1,
359
+    "The first measure the to be written to the output CSV, MIDI and SVG files." );
360
+
361
+  cmPgmOptInstallInt( poH, kBegBpmPoId,           'e', "beg_bpm",      0,       0,          &begTempoBPM,  1,
362
+    "Set to 0 to use the tempo from the score otherwise set to use the tempo at begMeasNumb." );
363
+
364
+  cmPgmOptInstallFlag( poH, kSvgStandAloneFlPoId,  'n', "svg_stand_alone_fl",0, 1,          &svgStandAloneFl, 1,
365
+    "Write the SVG file as a stand alone HTML file. Enabled by default." );
366
+
367
+  cmPgmOptInstallFlag( poH, kSvgPanZoomFlPoId,     'z', "svg_pan_zoom_fl", 0,   1,          &svgPanZoomFl, 1,
368
+    "Include the pan-zoom control. Enabled by default." );
369
+
370
+  cmPgmOptInstallUInt( poH, kBegMidiUidPoId,        'w', "beg_midi_uid",    0,   1,          &begMidiUId,   1,
371
+    "Begin MIDI msg. uuid." );
372
+
373
+  cmPgmOptInstallUInt( poH, kEndMidiUidPoId,        'y', "end_midi_uid",    0,   1,          &endMidiUId,   1,
374
+    "End MIDI msg. uuid." );
375
+  
376
+  // parse the command line arguments
377
+  if( cmPgmOptParse(poH, argc, argv ) == kOkPoRC )
378
+  {
379
+    // handle the built-in arg's (e.g. -v,-p,-h)
380
+    // (returns false if only built-in options were selected)
381
+    if( cmPgmOptHandleBuiltInActions(poH, &ctx.rpt ) == false )
382
+      goto errLabel;
383
+
384
+    switch( actionSelId )
385
+    {
386
+      case kScoreGenSelId:
387
+        rc = score_gen( &ctx, xmlFn, decFn, csvScoreFn, midiOutFn, svgOutFn, reportFl, begMeasNumb, begTempoBPM, svgStandAloneFl, svgPanZoomFl );
388
+        break;
389
+
390
+      case kScoreFollowSelId:
391
+        rc = score_follow( &ctx, csvScoreFn, midiInFn, rptFn, svgOutFn,  midiOutFn, timelineFn );
392
+        break;
393
+
394
+      case kMeasGenSelId:
395
+        rc = meas_gen(&ctx, pgmRsrcFn, rptFn);
396
+        break;
397
+
398
+      case kScoreReportSelId:
399
+        rc = score_report(&ctx, csvScoreFn, rptFn );
400
+        break;
401
+
402
+      case kMidiReportSelId:
403
+        rc = midi_file_report(&ctx, midiInFn, rptFn, svgOutFn, svgStandAloneFl, svgPanZoomFl );
404
+        break;
405
+
406
+      case kTimelineReportSelId:
407
+        rc = timeline_report(&ctx, timelineFn, timelinePrefix, rptFn );
408
+        break;
409
+
410
+      case kAudioReportSelId:
411
+        rc = audio_file_report(&ctx, audioFn, rptFn );
412
+        break;
413
+
414
+      case kMidiTrimSelId:
415
+        rc = midi_trim(&ctx, midiInFn, begMidiUId, endMidiUId, midiOutFn);
416
+        break;
417
+        
418
+      default:
419
+        rc = cmErrMsg(&ctx.err, kNoActionIdSelectedCtRC,"No action selector was selected.");
420
+
421
+    }
422
+  }
423
+  
424
+ errLabel:
425
+  cmPgmOptFinalize(&poH);
426
+  cmTsFinalize();
427
+  cmFsFinalize();
428
+  cmMdReport( kIgnoreNormalMmFl );
429
+  cmMdFinalize();
430
+  return rc;
431
+}

+ 2232
- 0
src/cmtools/mas.c
File diff suppressed because it is too large
View File


Loading…
Cancel
Save