Initial commit
This commit is contained in:
commit
63991ae469
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# directories to ignore
|
||||
libcm
|
||||
|
||||
.deps
|
||||
autom4te.cache
|
||||
build-aux
|
||||
build/linux/debug/src/
|
||||
build/linux/debug/bin
|
||||
build/linux/debug/lib
|
||||
build/linux/debug/include
|
||||
build/linux/release/src/
|
||||
build/linux/release/bin
|
||||
build/linux/release/lib
|
||||
build/linux/release/include
|
||||
|
||||
#Files to ignore
|
||||
*~
|
||||
*.[oa]
|
||||
|
||||
Makefile
|
||||
aclocal.m4
|
||||
config.h.in
|
||||
config.h
|
||||
configure
|
||||
hold.makefile
|
||||
Makefile.in
|
||||
config.log
|
||||
config.status
|
||||
libtool
|
||||
stamp-h1
|
||||
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
80
Makefile.am
Normal file
80
Makefile.am
Normal file
@ -0,0 +1,80 @@
|
||||
AM_CXXFLAGS =
|
||||
AM_LDFLAGS =
|
||||
AM_CPPFLAGS =
|
||||
AM_CFLAGS =
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4 # use custom macro's in ./m4
|
||||
|
||||
# if we are building and linking to a nested copy of libcm
|
||||
if BUILD_LIBCM
|
||||
SUBDIRS = src/libcm # causes recursive make into given sub-directories
|
||||
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
|
||||
AM_LDFLAGS += -Lsrc/libcm/src
|
||||
endif
|
||||
|
||||
# To Profile w/ gprof:
|
||||
# 1) Modify configure: ./configure --disable-shared CFLAGS="-pg"
|
||||
# 2) Run the program. ./foo
|
||||
# 3) Run gprof /libtool --mode=execute gprof ./foo
|
||||
|
||||
|
||||
# C compiler flags
|
||||
# _GNU_SOURCE - turns on GNU specific extensions and gives correct prototype for double log2(double)
|
||||
# -Wall turn on all warnings
|
||||
# -Wno-multichar - turns off multi-character constant warnings from cmAudioFile.c
|
||||
|
||||
|
||||
|
||||
AM_CPPFLAGS += -D _GNU_SOURCE -I..
|
||||
AM_CFLAGS += -Wno-multichar
|
||||
|
||||
|
||||
# debug/release switches
|
||||
if DEBUG
|
||||
AM_CFLAGS += -g
|
||||
AM_CXXFLAGS += -g
|
||||
else
|
||||
AM_CFLAGS += -O3
|
||||
AM_CXXFLAGS += -O3
|
||||
endif
|
||||
|
||||
MYLIBS = -lpthread -lfftw3f -lfftw3 -lcm
|
||||
|
||||
# Linux specific
|
||||
if OS_LINUX
|
||||
MYLIBS += -lsatlas -lasound
|
||||
if OS_64
|
||||
AM_CFLAGS += -m64
|
||||
AM_LDFLAGS += -L/usr/lib64/atlas -L/usr/lib64
|
||||
MYLIBS += -lrt -lm
|
||||
endif
|
||||
endif
|
||||
|
||||
if OS_OSX
|
||||
AM_CPPFLAGS += -I/opt/local/include # Search macports directory for fftw headers
|
||||
AM_LDFLAGS += -L/opt/local/lib # and libraries.
|
||||
AM_LDFLAGS += -framework Cocoa -framework CoreAudio -framework CoreMIDI -framework Carbon -framework Accelerate
|
||||
endif
|
||||
|
||||
src_cmtools_cmtools_SOURCES = src/cmtools/cmtools.c
|
||||
src_cmtools_cmtools_LDADD = $(MYLIBS)
|
||||
bin_PROGRAMS = src/cmtools/cmtools
|
||||
|
||||
src_cmtools_mas_SOURCES = src/cmtools/mas.c
|
||||
src_cmtools_mas_LDADD = $(MYLIBS)
|
||||
bin_PROGRAMS += src/cmtools/mas
|
||||
|
||||
src_cmtools_audiodev_SOURCES = src/cmtools/audiodev.c
|
||||
src_cmtools_audiodev_LDADD = $(MYLIBS)
|
||||
bin_PROGRAMS += src/cmtools/audiodev
|
||||
|
||||
# See: https://www.gnu.org/savannah-checkouts/gnu/automake/manual/html_node/Clean.html#Clean
|
||||
# 'make distclean' sets the source tree back to it's pre-configure state
|
||||
# 'distclean-local' is used by automake 'distclean' to perform customized local actions
|
||||
# ${exec_prefix} is the install prefix given to 'configure' by the user.
|
||||
# ${srcdir} is the directory of this Makefile and is set by autoconf.
|
||||
distclean-local:
|
||||
rm -rf ${exec_prefix}/src
|
||||
rm -rf ${exec_prefix}/bin
|
||||
rm -rf ${exec_prefix}/include
|
||||
rm -rf ${exec_prefix}/lib
|
452
README.md
Normal file
452
README.md
Normal file
@ -0,0 +1,452 @@
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
fftw fftw-devel atlas atlas-devel alsa-lib alsa-lib-devel fltk fltk-devel
|
||||
|
||||
|
||||
|
||||
In the 'template generation' mode the program generates a text file that contains information
|
||||
of interest from the MusicXML file. We refer to the text file as the as the 'decoration' file
|
||||
because it allows the score to be further decorated by adding additional information to the score.
|
||||
|
||||
In the 'escore generation' mode the program outputs a data file in CSV format which contains
|
||||
the score in a format which is conveniently readable by a score matching program. The program
|
||||
also generates a MIDI file which allows the clarified score to be rendered with a sampler.
|
||||
An additional SVG (scalable vector graphics) file is generated which shows the score in
|
||||
piano roll form along with any problems that the program may have had during the conversion process.
|
||||
|
||||
|
||||
|
||||
```
|
||||
cmtool --score_gen -x <xml_file> -d <dec_fn> {-c <csvOutFn>} {-m <midiOutFn>} {-s <svgOutFn>} {-r report} {-b begMeasNumb} {t begTempoBPM}
|
||||
|
||||
|
||||
Enumerated group: Action selector
|
||||
|
||||
-S --score_gen
|
||||
Run the score generation tool.
|
||||
|
||||
-T --timeline_gen
|
||||
Run the time line generation tool.
|
||||
|
||||
-M --meas
|
||||
Generate perfomance measurements.
|
||||
|
||||
-x --xml_fn <filename>
|
||||
Name of the input MusicXML file.
|
||||
|
||||
-d --dec_fn <filename>
|
||||
Name of a score decoration file.
|
||||
|
||||
-c --csv_fn <filename>
|
||||
Name of a CSV score file.
|
||||
|
||||
-p --pgm_rsrc_fn <filename>
|
||||
Name of program resource file.
|
||||
|
||||
-m --midi_out_fn <filename>
|
||||
Name of a MIDI file to generate as output.
|
||||
|
||||
-i --midi_in_fn <filename>
|
||||
Name of a MIDI file to generate as output.
|
||||
|
||||
-s --svg_fn <filename>
|
||||
Name of a HTML/SVG file to generate as output.
|
||||
|
||||
-t --timeline_fn <filename>
|
||||
Name of a timeline to generate as output.
|
||||
|
||||
-r --report_fn <filename>
|
||||
Name of a status file to generate as output.
|
||||
|
||||
-f --debug_fl (required)
|
||||
Print a report of the score following processing.
|
||||
|
||||
-b --beg_meas <int>
|
||||
The first measure the to be written to the output CSV, MIDI and SVG files.
|
||||
|
||||
-e --beg_bpm <int>
|
||||
Set to 0 to use the tempo from the score otherwise set to use the tempo at begMeasNumb.
|
||||
|
||||
-n --svg_stand_alone_fl
|
||||
Write the SVG output with an HTML wrapper.
|
||||
|
||||
-z --svg_pan_zoom_fl
|
||||
Include the SVG pan-zoom control (--svg_stand_alone must also be enabled)
|
||||
|
||||
-h --help
|
||||
Print this usage information.
|
||||
|
||||
-v --version
|
||||
Print version information.
|
||||
|
||||
```
|
||||
|
||||
|
||||
Score Following and Timeline Marker Generator
|
||||
==============================================
|
||||
|
||||
Perform score following based tasks.
|
||||
Generate the time line marker information used by the performance program resource file.
|
||||
|
||||
```
|
||||
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>
|
||||
```
|
||||
|
||||
If `<midi_out_fn>` is given then the a copy of `<midi_in_fn>` will be created with
|
||||
note velocities and sostenuto pedal events from the score.
|
||||
|
||||
|
||||
|
||||
|
||||
Score match report output snippet:
|
||||
|
||||
```
|
||||
Score Score Score MIDI MIDI MIDI
|
||||
Bar UUID Pitch UUID Ptch Vel.
|
||||
- ----- ----- ----- ----- ---- ----
|
||||
m 1 3 B3 19 B3 127
|
||||
m 1 4 D#4 20 D#4 127
|
||||
m 1 5 G#5 21 G#5 127
|
||||
m 1 14 E4 25 E4 29
|
||||
m 1 15 G#3 27 G#3 36
|
||||
m 1 16 E4 29 E4 43
|
||||
m 1 17 G#3 31 G#3 50
|
||||
```
|
||||
|
||||
TODO: Show errors
|
||||
TODO: Show SVG output
|
||||
|
||||
|
||||
Performance Measurement Generators
|
||||
==================================
|
||||
|
||||
Given a performance program resource file generate performance measurements where the internal MIDI file
|
||||
is used as a substitute for a real player.
|
||||
|
||||
|
||||
```
|
||||
cmtool --meas_gen -p <pgm_rsrc_fn> -r <report_fn>
|
||||
```
|
||||
|
||||
|
||||
Example `<pgm_rsrc_fn>`:
|
||||
|
||||
```
|
||||
{
|
||||
timeLineFn: "kc/data/round2.js"
|
||||
tlPrefixPath: "projects/imag_themes/scores/gen"
|
||||
scoreFn: "score.csv"
|
||||
|
||||
// pppp ppp pp p mp mf f ff fff
|
||||
dynRef: [ 14, 28, 42, 56, 71, 85, 99, 113,128 ]
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Example call with output file snippet:
|
||||
|
||||
```
|
||||
cmtool --meas_gen -g ~/src/cmtools/examples/perf_meas/pgm_rsrc_round2.js -r ~/src/cmtools/examples/perf_meas/perf_meas_out.js
|
||||
|
||||
{
|
||||
meas :
|
||||
[
|
||||
[ "sec" "typeLabel" "val" "cost" "loc" "evt" "seq" "mark" "typeId" ]
|
||||
[ "6002" "tempo" 34.730932 0.000000 59 76 0 "1" 3 ]
|
||||
[ "6002" "dyn" 0.952381 0.000000 59 76 0 "1" 2 ]
|
||||
[ "6002" "even" 0.600000 0.000000 59 76 0 "1" 1 ]
|
||||
[ "6002" "dyn" 1.000000 0.142857 59 76 0 "2" 2 ]
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Column Descriptions:
|
||||
|
||||
Column | Description
|
||||
----------|-----------------------------------------------------
|
||||
sec | Score section to which this measurement will be applied.
|
||||
typeLabel | Measurement type
|
||||
value | Measurement value
|
||||
cost | Score follower error value (0= perfect match)
|
||||
loc | Score location index where this measurement will be applied
|
||||
evt | Score event index where this measurement will be applied
|
||||
seq | Sequence id.
|
||||
mark | Time line marker label from which the measurements were made
|
||||
typeId | Measurement type id (numeric id associated with typeLabel)
|
||||
|
||||
Note that the event indexes and score locations are best
|
||||
seen in the score report (as generated by `cmtool --score_report`)
|
||||
NOT by directly referencing the score CSV file.
|
||||
|
||||
|
||||
Score Report
|
||||
============
|
||||
|
||||
Generate a human readable score report from a score CSV file.
|
||||
|
||||
```
|
||||
cmtool --score_report -c <csvScoreFn> -r <scoreRptFn>"
|
||||
|
||||
```
|
||||
|
||||
Example report file snippet:
|
||||
|
||||
```
|
||||
evnt CSV bar
|
||||
index line loctn bar idx type pitch ETD Dynamic
|
||||
----- ----- ----- --- --- ----- ----- --- -------
|
||||
0 2 1 bar
|
||||
1 3 0 1 0 non B3 section:6001
|
||||
2 4 0 1 1 non D#4
|
||||
3 5 0 1 2 non G#5
|
||||
4 6 1 1 3 ped dn
|
||||
5 7 2 1 3 ped dn
|
||||
6 8 3 1 3 non E4
|
||||
7 9 4 1 4 non G#3 td ppp
|
||||
8 10 5 1 5 non E4 td pp
|
||||
9 11 6 1 6 non G#3 td pp
|
||||
10 12 7 1 7 non C#2 td p
|
||||
11 13 8 1 8 non C4 td mp
|
||||
12 14 9 1 9 non G#3 td mp
|
||||
13 15 10 1 10 non C#2 td mf
|
||||
14 16 11 1 11 non A#2 td f
|
||||
15 17 12 1 12 non C4 td f
|
||||
16 18 13 1 13 non A#2 td f
|
||||
17 19 14 1 14 non C#1
|
||||
|
||||
|
||||
```
|
||||
|
||||
MIDI File Reports
|
||||
=================
|
||||
|
||||
Generate a MIDI file report and optional SVG piano roll image."
|
||||
|
||||
cmtool --midi_report -i <midiInFn> -r <midiRptFn> {-s <svgOutFn> {--svg_stand_alone_fl} {--svg_pan_zoom_fl} }
|
||||
|
||||
|
||||
Timeline Report
|
||||
=================
|
||||
|
||||
Generate human readable report from a time line setup file.
|
||||
|
||||
cmtool --timeline_report -t <timelineInFn> -l <tlPrefix> -r <rptOutFn>
|
||||
|
||||
tlPrefix is the folder where data files for this timeline are stored.
|
||||
|
||||
|
||||
Score Follow Report
|
||||
===================
|
||||
|
||||
```
|
||||
cmtool --score_follow -c round2.csv -i new_round2.mid -r report.txt -s report_svg.html
|
||||
```
|
||||
|
||||
SVG Description
|
||||
---------------
|
||||
|
||||
- Red borders around score events that were not matched.
|
||||
- Red borders around MIDI events that did not match.
|
||||
- Line is drawn to MIDI events that matched to multiple score events. The lines
|
||||
are drawn to all score events after the first match.
|
||||
|
||||
|
||||
Audio Device Test
|
||||
=================
|
||||
|
||||
Real-time audio port test.
|
||||
|
||||
This test also excercises the real-time audio buffer which implements most of the global audio control functions.
|
||||
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
-s --srate <real> Audio system sample rate.
|
||||
-z --hz <real> Tone frequency in Hertz.
|
||||
-x --ch_index <uint> Index of first channel index.
|
||||
-c --ch_cnt <uint> Count of audio channels.
|
||||
-b --buf_cnt <uint> Count of audio buffers. (e.g. 2=double buffering, 3=triple buffering)
|
||||
-f --frames_per_buf <uint> Count of audio channels.
|
||||
-i --in_dev_index <uint> Input device index as taken from the audio device report.
|
||||
-o --out_dev_index <uint> Output device index as taken from the audio device report.
|
||||
-r --report_flag Print a report of the score following processing.
|
||||
-h --help Print this usage information.
|
||||
-v --version Print version information.
|
||||
-p --parms Print the arguments.
|
||||
```
|
||||
|
||||
MIDI Audio Sync (MAS)
|
||||
=====================
|
||||
|
||||
1) Synchronize Audio to MIDI based on onset patterns:
|
||||
|
||||
a. Convert MIDI to audio impulse files:
|
||||
|
||||
mas -m -i <midi_dir | midi_fn > -o <out_dir> -s <srate>
|
||||
|
||||
Notes:
|
||||
|
||||
* If <midi_dir> is given then use all files in the directory as input otherwise convert a single file.
|
||||
|
||||
* 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.
|
||||
|
||||
b. Convert the onsets in audio file(s) to audio impulse
|
||||
file(s).
|
||||
|
||||
mas -a -i <audio_dir | audio_fn > -o <out_dir>
|
||||
-w <wndMs> -f <hopFactor> -u <chIdx> -r <wnd_frm_cnt>
|
||||
-x <preWndMult> -t <threshold> -z <maxFrqHz> -e <filtCoeff>
|
||||
|
||||
1) If <audio_dir> is given then use all files
|
||||
in the directory as input otherwise convert a
|
||||
single file.
|
||||
2) The onset detector uses a spectral flux based
|
||||
algorithm.
|
||||
See cmOnset.h/.c for an explanation of the
|
||||
onset detection parameters.
|
||||
|
||||
|
||||
c) Convolve impulse files created in a) and b) with a
|
||||
Hann window to widen the impulse width.
|
||||
|
||||
mas -c -i <audio_dir | audio_fn > -o <out_dir> -w <wndMs>
|
||||
|
||||
1) If <audio_dir> is given then use all files
|
||||
in the directory as input otherwise convert a
|
||||
single file.
|
||||
2) <wndMs> gives the width of the Hann window.
|
||||
|
||||
d) Synchronize MIDI and Audio based convolved impulse
|
||||
files based on their onset patterns.
|
||||
|
||||
mas -y -i <sync_cfg_fn.js> -o <sync_out_fn.js>
|
||||
|
||||
1) The <sync_cfg_fn.js> file has the following format:
|
||||
{
|
||||
ref_dir : "/home/kevin/temp/mas/midi_conv" // location of ref files
|
||||
key_dir : "/home/kevin/temp/mas/onset_conv" // location of key files
|
||||
hop_ms : 25 // sliding window increment
|
||||
|
||||
sync_array :
|
||||
[
|
||||
// ref_fn wnd_beg_secs wnd_dur_secs key_fn key_beg_secs, key_end_secs
|
||||
[ "1.aif", 678, 113, "Piano 3_01.aif", 239.0, 417.0],
|
||||
[ "3.aif", 524, 61, "Piano 3_06.aif", 556.0, 619.0],
|
||||
]
|
||||
}
|
||||
|
||||
Notes:
|
||||
a. The 'window' is the section of the reference file which is compared
|
||||
to the key file search area <key_beg_secs> to <key_end_secs> by sliding it
|
||||
in increments of 'hop_ms' samples.
|
||||
|
||||
b. Set 'key_end_secs' to 0 to search to the end of the file.
|
||||
|
||||
c. When one key file matches to multiple reference files the
|
||||
key files sync recd should be listed consecutively. This way
|
||||
the earlier searches can stop when they reach the beginning
|
||||
of the next sync records search region. See sync_files().
|
||||
|
||||
Note that by setting <key_beg_secs> to a non-zero value
|
||||
as occurs in the multi-key-file case has a subtle effect of
|
||||
changing the master-slave relationship between the reference
|
||||
an key file.
|
||||
|
||||
In general the reference file is the master and the key file
|
||||
is the slave. When a non-zero <key_beg_secs> is given however
|
||||
this relationship reverses. See masCreateTimeLine() for
|
||||
how this is used to assign file group id's during the
|
||||
time line creation.
|
||||
|
||||
3) The <sync_out_fn.js> has the following form.
|
||||
```
|
||||
{
|
||||
"sync" :
|
||||
{
|
||||
"refDir" : "/home/kevin/temp/mas/midi_conv"
|
||||
"keyDir" : "/home/kevin/temp/mas/onset_conv"
|
||||
"hopMs" : 25.000000
|
||||
|
||||
"array" :
|
||||
[
|
||||
|
||||
//
|
||||
// sync results for "1.aif" to "Piano 3_01.aif"
|
||||
//
|
||||
|
||||
{
|
||||
// The following block of fields were copied from <sync_cfg_fn.js>.
|
||||
"refFn" : "1.aif"
|
||||
"refWndBegSecs" : 678.000000
|
||||
"refWndSecs" : 113.000000
|
||||
"keyFn" : "Piano 3_01.aif"
|
||||
"keyBegSecs" : 239.000000
|
||||
"keyEndSecs" : 417.000000
|
||||
|
||||
// Sync. location of the 'window' in the key file.
|
||||
// Sample index into the key file which matches to the first sample
|
||||
// in the reference window.
|
||||
"keySyncIdx" : 25768800 // Offset into the key file of the best match.
|
||||
|
||||
"syncDist" : 4184.826108 // Match distance score for the sync location.
|
||||
"refSmpCnt" : 200112000 // Count of samples in the reference file.
|
||||
"keySmpCnt" : 161884800 // Count of samples in the key file.
|
||||
"srate" : 96000.000000 // Sample rate of the reference and key file.
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
2) Create a time line from the results of a synchronization. A time line is a data structure
|
||||
(See cmTimeLine.h/.c) which maintains a time based ordering of Audio files, MIDI files,
|
||||
and arbitrary markers.
|
||||
|
||||
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>
|
||||
|
||||
| <sync_out_fn.js> The output file produced as a result of a previous MIDI <-> Audio synchronization.
|
||||
|
|
||||
| <ref_dir> Location of the reference files (MIDI) used for the synchronization.
|
||||
| <ref_ext> File extension used by the reference files.
|
||||
| <key_dir> Locate of the key files (Audio) used for the synchronization.
|
||||
| <key_ext> File extension used by the key files.
|
||||
|
||||
a. The time line 'trackId' assigned to each time line object is based on the files
|
||||
'groupId'. A common group id is given to sets of files which are
|
||||
locked in time relative to one another. For example
|
||||
if file B and C are synced to master file A and
|
||||
file D is synced to file E which is synced to master
|
||||
file F. Then files A,B,C will be given one group
|
||||
id and files D,E and F will be given another group id.
|
||||
(See masCreateTimeLine()).
|
||||
|
||||
b. The time line object 'offset' values gives the offset in samples where the object
|
||||
begins relative to other objects in the group. Note that the master object in the
|
||||
group may not begin at offset 0 if there are slave objects which start before it.
|
||||
|
||||
|
||||
|
||||
|
||||
TODO:
|
||||
=====
|
||||
|
||||
* replace round2.csv time with the times in the full fragment MIDI file
|
||||
|
||||
* update timeline lite to allow for a synchronized audio file
|
||||
|
||||
* change the build setup to default to getting libcm from the system
|
||||
and allow an option to build it from src/libcm
|
||||
|
||||
* for all svg output create a standalone flag that wraps the output in HTML
|
||||
and another option to load pan-zoom.
|
||||
|
||||
|
||||
* MIDI report output example and description
|
||||
|
||||
* Timeline report output example and description
|
||||
|
||||
|
37
build/clean.sh
Executable file
37
build/clean.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Run 'make distclean' to clean many of the temporary make files.
|
||||
# then use this script run from cm/build to clean the remaining files
|
||||
#
|
||||
|
||||
|
||||
|
||||
function clean_dir {
|
||||
|
||||
make -C $1 uninstall
|
||||
make -C $1 distclean
|
||||
|
||||
rm -f $1/bin/kc.app/Contents/MacOS/kc
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
clean_dir linux/debug
|
||||
clean_dir linux/release
|
||||
clean_dir osx/debug
|
||||
clean_dir osx/release
|
||||
|
||||
rm -rf osx/debug/a.out.dSYM
|
||||
|
||||
# delete everything created by 'autoreconf'.
|
||||
rm -rf ../build-aux
|
||||
rm -rf ../autom4te.cache
|
||||
rm -f ../config.h.in ../config.h.in~ ../configure ../libtool.m4
|
||||
rm -f ../Makefile.in ../aclocal.m4
|
||||
rm -f ../m4/libtool.m4 ../m4/ltoptions.m4 ../m4/ltsugar.m4 ../m4/ltversion.m4 ../m4/lt~obsolete.m4
|
||||
|
||||
|
||||
|
||||
|
25
build/linux/debug/build.sh
Executable file
25
build/linux/debug/build.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
curdir=`pwd`
|
||||
|
||||
cd ../../..
|
||||
autoreconf --force --install
|
||||
|
||||
cd ${curdir}
|
||||
|
||||
# To Profile w/ gprof:
|
||||
# 1) Modify configure: ./configure --disable-shared CFLAGS="-pg"
|
||||
# 2) Run the program. ./foo
|
||||
# 3) Run gprof /libtool --mode=execute gprof ./foo
|
||||
|
||||
../../../configure --prefix=${curdir} \
|
||||
--enable-debug --enable-build_libcm \
|
||||
CFLAGS="-g -Wall" \
|
||||
CXXFLAGS="-g -Wall" \
|
||||
LIBS=
|
||||
|
||||
# CPPFLAGS="-I/home/kevin/src/libcm/build/linux/debug/include " \
|
||||
# LDFLAGS="-L/home/kevin/src/libcm/build/linux/debug/lib" \
|
||||
|
||||
#make
|
||||
#make install
|
24
build/linux/release/build.sh
Executable file
24
build/linux/release/build.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
curdir=`pwd`
|
||||
|
||||
cd ../../..
|
||||
autoreconf --force --install
|
||||
|
||||
cd ${curdir}
|
||||
|
||||
# To Profile w/ gprof:
|
||||
# 1) Modify configure: ./configure --disable-shared CFLAGS="-pg"
|
||||
# 2) Run the program. ./foo
|
||||
# 3) Run gprof /libtool --mode=execute gprof ./foo
|
||||
|
||||
../../../configure --prefix=${curdir} \
|
||||
CFLAGS="-Wall" \
|
||||
CXXFLAGS="-Wall" \
|
||||
CPPFLAGS= \
|
||||
LDFLAGS= \
|
||||
LIBS=
|
||||
|
||||
|
||||
#make
|
||||
#make install
|
20
build/osx/debug/build.sh
Executable file
20
build/osx/debug/build.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
curdir=`pwd`
|
||||
|
||||
cd ../../..
|
||||
autoreconf --force --install
|
||||
|
||||
cd ${curdir}
|
||||
|
||||
../../../configure --prefix=${curdir} \
|
||||
--enable-debug \
|
||||
CFLAGS="-g -Wall" \
|
||||
CXXFLAGS="-g -Wall" \
|
||||
CPPFLAGS= \
|
||||
LDFLAGS= \
|
||||
LIBS=
|
||||
|
||||
|
||||
#make
|
||||
#make install
|
19
build/osx/release/build.sh
Executable file
19
build/osx/release/build.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
curdir=`pwd`
|
||||
|
||||
cd ../../..
|
||||
autoreconf --force --install
|
||||
|
||||
cd ${curdir}
|
||||
|
||||
../../../configure --prefix=${curdir} \
|
||||
CFLAGS="-Wall" \
|
||||
CXXFLAGS="-Wall" \
|
||||
CPPFLAGS= \
|
||||
LDFLAGS= \
|
||||
LIBS=
|
||||
|
||||
|
||||
#make
|
||||
#make install
|
152
configure.ac
Normal file
152
configure.ac
Normal file
@ -0,0 +1,152 @@
|
||||
#
|
||||
# Use "autoreconf --force --install" to update depedent files after changing
|
||||
# this configure.ac or any of the Makefile.am files.
|
||||
#
|
||||
|
||||
AC_INIT([cmtools],[1.0],[kl@currawongproject.org])
|
||||
AC_CONFIG_AUX_DIR([build-aux]) # put aux files in build-aux
|
||||
AM_INIT_AUTOMAKE([1.9 -Wall foreign subdir-objects]) # subdir-objects needed for non-recursive make
|
||||
AC_CONFIG_SRCDIR([src/cmtools/cmtools.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_PROG_AR
|
||||
|
||||
LT_INIT
|
||||
|
||||
# Check for programs
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
# AC_PROG_RANLIB # required for static librarires
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
# Checks for libraries.
|
||||
# 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.])])
|
||||
#TODO: add more library checks
|
||||
|
||||
# Checks for header files.
|
||||
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])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_HEADER_STDBOOL
|
||||
AC_C_INLINE
|
||||
AC_TYPE_OFF_T
|
||||
AC_TYPE_SSIZE_T
|
||||
AC_TYPE_UINT64_T
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_ERROR_AT_LINE
|
||||
AC_FUNC_FORK
|
||||
AC_FUNC_FSEEKO
|
||||
AC_FUNC_MALLOC
|
||||
AC_FUNC_REALLOC
|
||||
AC_FUNC_STRTOD
|
||||
AC_CHECK_FUNCS([clock_gettime floor memmove memset mkdir pow rint select socket sqrt strcasecmp strchr strcspn strerror strspn strstr strtol])
|
||||
|
||||
|
||||
# The following is a custom macro in ./m4/os_type.m4
|
||||
# be sure to also set "ACLOCAL_AMFLAGS = -I m4" in ./Makefile.am
|
||||
# Defines the config.h variable OS_LINUX or OS_OSX
|
||||
AX_FUNC_OS_TYPE
|
||||
|
||||
AX_FUNC_OS_64
|
||||
|
||||
# ac_cv_os_type is set by AX_FUNC_OS_TYPE
|
||||
AM_CONDITIONAL([OS_LINUX],[test x"${ax_cv_os_type}" = xLinux])
|
||||
AM_CONDITIONAL([OS_OSX],[test x"${ax_cv_os_type}" = xDarwin])
|
||||
echo "OS='${ax_cv_os_type}'"
|
||||
|
||||
AM_CONDITIONAL([OS_64],[test x"${ax_cv_os_64}" == xx86_64])
|
||||
echo "ptr width='${ax_cv_os_64}'"
|
||||
|
||||
# check if a request has been made to build libcm
|
||||
AC_ARG_ENABLE([build_libcm],
|
||||
[ --enable-build_libcm libcm is included in the local source tree],
|
||||
[case "${enableval}" in
|
||||
yes) build_libcm=true ;;
|
||||
no) build_libcm=false ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-build_libcm]) ;;
|
||||
esac],[build_libcm=false])
|
||||
|
||||
echo "build_libcm=${build_libcm}"
|
||||
|
||||
# check if a nested copy of libcm exists in /src/libcm
|
||||
AC_CHECK_FILE([${srcdir}/src/libcm/src/cmGlobal.h],[local_libcm=true],[local_libcm=false])
|
||||
echo "local_libcm=${local_libcm}"
|
||||
|
||||
# set BUILD_LIBCM if a libcm build request was set and a nested copy of libcm exists
|
||||
AM_CONDITIONAL([BUILD_LIBCM], [test x$build_libcm = xtrue -a x$local_libcm = xtrue ])
|
||||
|
||||
AC_ARG_ENABLE([debug],
|
||||
[ --enable-debug Turn on debugging],
|
||||
[case "${enableval}" in
|
||||
yes) debug=true ;;
|
||||
no) debug=false ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
|
||||
esac],[debug=false])
|
||||
|
||||
echo "debug=${debug}"
|
||||
|
||||
AM_CONDITIONAL([DEBUG], [test x$debug = xtrue])
|
||||
|
||||
if test x$debug = xfalse; then
|
||||
AC_DEFINE([NDEBUG], 1,[Debugging off.])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([vectop],
|
||||
[ --enable-vectop Turn on use of Lapack and Atlas vector/matrix operations. ],
|
||||
[case "${enableval}" in
|
||||
yes) vectop=true ;;
|
||||
no) vectop=false ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-vectop]) ;;
|
||||
esac],[vectop=true])
|
||||
|
||||
echo "vectop=${vectop}"
|
||||
|
||||
# if --enable-vectop then #define CM_VECTOP = 1 in config.h otherwise CM_VECTOP is undefined.
|
||||
if test x"$vectop" = xtrue; then
|
||||
AC_DEFINE([CM_VECTOP], 1,[Use Lapack and Atlas.])
|
||||
fi
|
||||
|
||||
|
||||
AC_ARG_ENABLE([memalign],
|
||||
[ --enable-memalign Turn on memory alignment on dynamic memory allocations. ],
|
||||
[case "${enableval}" in
|
||||
yes) memalign=true ;;
|
||||
no) memalign=false ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-memalign]) ;;
|
||||
esac],[memalign=true])
|
||||
|
||||
echo "memalign=${memalign}"
|
||||
|
||||
# if --enable-vectop then #define CM_MEMALIGN = 1 in config.h otherwise CM_MEMALIGN is undefined.
|
||||
if test x"$memalign" = xtrue; then
|
||||
AC_DEFINE([CM_MEMALIGN], 1,[Turn on dynamic memory alignment.])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([sonicart],
|
||||
[ --enable-sonicart Enable use of Sonic Arts proprietary code. ],
|
||||
[case "${enableval}" in
|
||||
yes) sonicart=true ;;
|
||||
no) sonicart=false ;;
|
||||
*) AC_MSG_ERROR([bad value ${enableval} for --enable-sonicart]) ;;
|
||||
esac],[sonicart=false])
|
||||
|
||||
echo "sonicart=${sonicart}"
|
||||
|
||||
# if --enable-sonicart then #define CM_SONICART = 1 in config.h otherwise CM_SONICART is undefined.
|
||||
if test x"$sonicart" = xtrue; then
|
||||
AC_DEFINE([CM_SONICART], 1,[Include Sonic Arts proprietry code.])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([INC_SONICART], [test x$sonicart = xtrue])
|
||||
|
||||
AC_CONFIG_FILES([ Makefile ])
|
||||
|
||||
# if local nested libcm then do recursive configure into subdirs
|
||||
if test x$build_libcm = xtrue -a x$local_libcm = xtrue; then
|
||||
AC_CONFIG_SUBDIRS([src/libcm])
|
||||
fi
|
||||
|
||||
AC_OUTPUT
|
BIN
doc/score_follow_0.png
Normal file
BIN
doc/score_follow_0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
254
doc/xscore_gen.md
Normal file
254
doc/xscore_gen.md
Normal file
@ -0,0 +1,254 @@
|
||||
xscore_gen
|
||||
==========
|
||||
|
||||
*xscore_gen* parses MusicXML score files and generates a text file
|
||||
which allows the score to be clarified and additional information to
|
||||
be added. This is the first step in creating a 'machine readable
|
||||
score' based on the 'human readable score'.
|
||||
|
||||
This step is necessary because we need a way to efficiently
|
||||
append additional information to the score which cannot be entered
|
||||
directly by the score editing program (e.g. Sibelius, Dorico, Finale).
|
||||
In practice we use Sibelius 6 as our primary score editor.
|
||||
|
||||
Likewise there are certain limitations to the generated MusicXML
|
||||
which need to be worked around. The primary problem being that
|
||||
dynamic markings are not tied to specific notes. This is important
|
||||
for purposes of score analysis as well as audio rendering.
|
||||
|
||||
The overall approach to adding this addtional information
|
||||
is as follows:
|
||||
|
||||
1. Add as much auxilliary information as possible from within Sibelius.
|
||||
This entails using colored note heads, and carefully placed
|
||||
text strings.
|
||||
|
||||
2. Generate the MusicXML file using the [Dolet 6 Sibelius
|
||||
plug-in](https://www.musicxml.com/). The resulting MusicXML file is
|
||||
run through *xscore_gen* and parsed to find any invalid structures
|
||||
such as damper up events not preceeded by damper down events, or tied
|
||||
notes with no ending note. These problems are cleared by careful
|
||||
re-editing of the score within Sibelius until all the problematic
|
||||
structures are fixed.
|
||||
|
||||
3. As a side effect of step 2 a template 'decoration' file is generated.
|
||||
This text file has all the relavant 'machine score' information
|
||||
from the XML score as a time tagged list. In this step *decoration* information is manually
|
||||
added by entering codes at the end of each line. The codes
|
||||
are cryptic but they are also succinct and allow for relatively
|
||||
painless editing.
|
||||
|
||||
4. Once the addition information is entered *xscore_gen* is
|
||||
run again to generate three output files:
|
||||
|
||||
- machine score as a CSV file
|
||||
- MIDI file suitable for audio rendering
|
||||
- SVG piano roll file
|
||||
|
||||
As with step 2 this step may need to be iterated several times
|
||||
to clear syntactic errors in the decoration data.
|
||||
|
||||
5. Generate the time line marker information to be used with the performance program:
|
||||
|
||||
Generate the time line marker information, into `temp/time_line_temp.txt` like this:
|
||||
|
||||
`cmtest -F`
|
||||
|
||||
This calls `cmMidiScoreFollowMain()` in app\cmMidiScoreFollow.c.
|
||||
|
||||
Then paste `temp\time_line_temp.txt` into kc/src/kc/data/round1.js.
|
||||
|
||||
|
||||
|
||||
Preparing the score
|
||||
-------------------
|
||||
|
||||
Note color is used to assign notes to groups.
|
||||
These groups may later be used to indicate
|
||||
certain processes which will be performed
|
||||
on these notes during performance.
|
||||
|
||||
There are currently three defined groups
|
||||
'even','dynamics' and 'tempo'.
|
||||
|
||||
|
||||
### Score Coloring Chart:
|
||||
|
||||
Description Number Color
|
||||
------------------- -------- -------------------------
|
||||
Even #0000FF blue
|
||||
Tempo #00FF00 green
|
||||
Dyn #FF0000 red
|
||||
Tempo + Even #00FFFF green + blue (turquoise)
|
||||
Dyn + Even #FF00FF red + blue
|
||||
Dyn + Tempo #FF7F00 red + green (brown)
|
||||
Tempo + Even + Dyn #996633 purple
|
||||
|
||||
Decrement color by one (i.e. 0xFE) to indicate the last note in a group
|
||||
of measured notes. Note that a decremented color stops all active measures
|
||||
not just the measurement associated with the decremented color.
|
||||
|
||||
|
||||
Preparing the Music XML File
|
||||
----------------------------
|
||||
|
||||
*xscore_gen* is know to work with the MusicXML files produced by
|
||||
the [Dolet 6 Sibelius plug-in]<https://www.musicxml.com/>
|
||||
|
||||
After generating the file it is necessary to do some
|
||||
minor pre-processing before submitting it to *xscore_gen*
|
||||
|
||||
iconv -f UTF-16 -t UTF-8 -o score-utf16.xml score-utf8.xml
|
||||
|
||||
|
||||
Create the decoration file
|
||||
--------------------------
|
||||
|
||||
```
|
||||
cmtools --score_gen -x myscore.xml -d mydec.txt
|
||||
```
|
||||
|
||||
Here's a snippet of a typical 'decoration' file.
|
||||
|
||||
```
|
||||
Part:P1
|
||||
1 : div:768 beat:4 beat-type:4 (3072)
|
||||
idx voc loc tick durtn rval flags
|
||||
--- --- ----- ------- ----- ---- --- ---------------
|
||||
0 0 2 0 0 0.0 |--------------
|
||||
1 0 0 0 54 4.0 --------------- 54 bpm
|
||||
2 1 0 0 3072 1.0 -R-------------
|
||||
3 5 0 0 2304 2.0 -R-.-----------
|
||||
4 0 0 996 0 0.0 --------V------
|
||||
5 0 0 1920 0 0.0 --------^------
|
||||
6 5 0 2304 341 8.0 -R-------------
|
||||
7 0 0 2643 0 0.0 --------V------
|
||||
8 5 0 2645 85 32.0 -R-------------
|
||||
9 5 3 2730 85 32.0 F 5 --------------*
|
||||
10 5 4 2815 85 32.0 Ab2 --------------*
|
||||
11 5 5 2900 85 32.0 C 3 --------------*
|
||||
12 5 6 2985 87 32.0 F 6 --------------*
|
||||
13 1 0 3072 768 4.0 -R-------------
|
||||
14 5 0 3072 768 4.0 -R------------- 3840
|
||||
```
|
||||
|
||||
### Decoration file format
|
||||
|
||||
Column | Description
|
||||
-------|-----------------------------
|
||||
idx | event index
|
||||
voc | voice index
|
||||
tick | MIDI tick
|
||||
durtn | duration in MIDI ticks
|
||||
rval | rythmic value
|
||||
pitch | scientific pitch
|
||||
flags | event attributes
|
||||
|
||||
### Event attribute flags:
|
||||
|
||||
Event attribute symbols used in the decoration file:
|
||||
|
||||
Desc | Flag |
|
||||
----------|------|-----------------------------------------
|
||||
Bar | | | Beginning of a measure
|
||||
Rest | R | Rest event
|
||||
Grace | G | Grace note event
|
||||
Dot | . | note is dotted
|
||||
Chord | C | note is part of a chord
|
||||
Even | e | note is part of an 'even' group
|
||||
Dyn | d | note is part of a 'dynamics' group
|
||||
Tempo | t | note is part of a 'tempo' group
|
||||
DampDn | V | damper down event
|
||||
DampUp | ^ | damper up event
|
||||
DampUpDn | X | damper up/down event
|
||||
SostDn | { | sostenuto down event
|
||||
Section | S | section boundary
|
||||
SostUp | } | sostenuto up event
|
||||
Heel | H | heel event
|
||||
Tie Begin | T | begin of a tied note
|
||||
Tie End | _ | end of a tied note
|
||||
Onset | * | note onset
|
||||
|
||||
|
||||
|
||||
Decoration Sytax:
|
||||
------------------
|
||||
|
||||
!<dyn_mark> Assign dynamics
|
||||
!(<dyn_mark>) - less uncertain dynamic mark
|
||||
!<upper-case-dyn-mark> - begin of dynamic fork (See note below regarding dynamic forks)
|
||||
!!<upper-case-dyn-mark> - end of dynamic fork
|
||||
~<mark> Insert or remove event (See pedal marks below.)
|
||||
@<new_tick_value> Move event to a new time position
|
||||
%<grace_note_flag> Flag note as a grace note
|
||||
%%<grace_note_flag> -last note in grace note sequence
|
||||
$<sci_pitch> Assign a note a new pitch
|
||||
|
||||
<grace_note_flag>
|
||||
b (begin grace)
|
||||
a (add grace and end grace)
|
||||
s (subtract grace and end grace)
|
||||
g (grace note)
|
||||
A (after first)
|
||||
N (soon after first)
|
||||
|
||||
Note: The first non-grace note in a grace note sequence is marked with a %b.
|
||||
The last non-grace note in a grace note sequence is marked with a %s or %a.
|
||||
|
||||
Where: %s = steal time from the note marked with %b.
|
||||
%a = insert time prior to the note marked with %a.
|
||||
|
||||
The last (by row number) note (grace or non-grace) in the the sequence
|
||||
is marked with %%# where # is replaced with a,b,s,or g.
|
||||
|
||||
It is only necessary to mark the tick number of grace notes in order
|
||||
to give the time sequence of the notes. A single grace note therefore does
|
||||
not require an explict tick mark notation (i.e. @####)
|
||||
|
||||
|
||||
Insert/delete Event Marks: <mark>
|
||||
-----------------------------------
|
||||
d (sostenuto down - just after note onset)
|
||||
u ( " up - just before this event)
|
||||
x ( " up just before this event and down just after it)
|
||||
D (damper pedal down - after this event)
|
||||
U (damper pedal up - before this event)
|
||||
_ (set tie end flag)
|
||||
& (skip this event)
|
||||
|
||||
Dynamic Marks: <dyn-mark>
|
||||
--------------------------
|
||||
s (silent note)
|
||||
pppp-
|
||||
pppp
|
||||
pppp+
|
||||
ppp-
|
||||
ppp
|
||||
ppp+
|
||||
pp-
|
||||
pp
|
||||
pp+
|
||||
p-
|
||||
p
|
||||
p+
|
||||
mp-
|
||||
mp
|
||||
mp+
|
||||
mf-
|
||||
mf
|
||||
mf+
|
||||
f-
|
||||
f
|
||||
f+
|
||||
ff
|
||||
ff+
|
||||
fff
|
||||
|
||||
Note: Dynamic Forks:
|
||||
--------------------
|
||||
Use upper case dynamic letters to indicate forks in the dynamics
|
||||
which should be filled automatically. Note that all notes
|
||||
in the voice assigned to the first note in the fork will be
|
||||
included in the dynamic change. To exclude a note from the
|
||||
fork assign it a lower case mark.
|
||||
|
8
m4/os_64.m4
Normal file
8
m4/os_64.m4
Normal file
@ -0,0 +1,8 @@
|
||||
AC_DEFUN([AX_FUNC_OS_64],
|
||||
[AC_CACHE_CHECK([operating system address width],
|
||||
[ax_cv_os_64],
|
||||
[ax_cv_os_64=`uname -m`])
|
||||
if test x"$ax_cv_os_64" = xx86_64; then
|
||||
AC_DEFINE([OS_64], 1,[Operating system is 64 bits.])
|
||||
fi
|
||||
]) # AX_FUNC_OS_TYPE
|
11
m4/os_type.m4
Normal file
11
m4/os_type.m4
Normal file
@ -0,0 +1,11 @@
|
||||
AC_DEFUN([AX_FUNC_OS_TYPE],
|
||||
[AC_CACHE_CHECK([operating system type],
|
||||
[ax_cv_os_type],
|
||||
[ax_cv_os_type=`uname`])
|
||||
if test x"$ax_cv_os_type" = xLinux; then
|
||||
AC_DEFINE([OS_LINUX], 1,[Operating system is Linux.])
|
||||
fi
|
||||
if test x"$ax_cv_os_type" = xDarwin; then
|
||||
AC_DEFINE([OS_OSX], 1,[Operating system is Darwin.])
|
||||
fi]) # AX_FUNC_OS_TYPE
|
||||
|
397
src/cmtools/audiodev.c
Normal file
397
src/cmtools/audiodev.c
Normal file
@ -0,0 +1,397 @@
|
||||
#include "cmPrefix.h"
|
||||
#include "cmGlobal.h"
|
||||
#include "cmRpt.h"
|
||||
#include "cmErr.h"
|
||||
#include "cmCtx.h"
|
||||
#include "cmMem.h"
|
||||
#include "cmMallocDebug.h"
|
||||
#include "cmLinkedHeap.h"
|
||||
#include "cmFileSys.h"
|
||||
#include "cmText.h"
|
||||
|
||||
#include "cmPgmOpts.h"
|
||||
|
||||
#include "cmTime.h"
|
||||
#include "cmAudioPort.h"
|
||||
#include "cmApBuf.h" // only needed for cmApBufTest().
|
||||
#include "cmAudioPortFile.h"
|
||||
#include "cmAudioAggDev.h"
|
||||
#include "cmAudioNrtDev.h"
|
||||
|
||||
#include "cmFloatTypes.h"
|
||||
#include "cmAudioFile.h"
|
||||
#include "cmFile.h"
|
||||
|
||||
enum
|
||||
{
|
||||
kOkAdRC = cmOkRC,
|
||||
kAudioPortFailAdRC,
|
||||
kAudioPortFileFailAdRC,
|
||||
kAudioPortNrtFailAdRC,
|
||||
kAudioBufFailAdRC
|
||||
};
|
||||
|
||||
const cmChar_t* poBegHelpStr =
|
||||
"audiodev Test the real-time audio ports"
|
||||
"\n"
|
||||
"audiodev -i<in_dev_index> -o <out_dev_index -s <sample_rate>\n"
|
||||
"\n"
|
||||
"All arguments are optional. The default input and output device index is 0.\n"
|
||||
"\n";
|
||||
|
||||
const cmChar_t* poEndHelpStr = "";
|
||||
|
||||
/// [cmAudioPortExample]
|
||||
|
||||
// See cmApPortTest() below for the main point of entry.
|
||||
|
||||
// Data structure used to hold the parameters for cpApPortTest()
|
||||
// and the user defined data record passed to the host from the
|
||||
// audio port callback functions.
|
||||
typedef struct
|
||||
{
|
||||
unsigned bufCnt; // 2=double buffering 3=triple buffering
|
||||
unsigned chIdx; // first test channel
|
||||
unsigned chCnt; // count of channels to test
|
||||
unsigned framesPerCycle; // DSP frames per cycle
|
||||
unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
|
||||
unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
|
||||
unsigned inDevIdx; // input device index
|
||||
unsigned outDevIdx; // output device index
|
||||
double srate; // audio sample rate
|
||||
unsigned meterMs; // audio meter buffer length
|
||||
|
||||
// param's and state for cmApSynthSine()
|
||||
unsigned phase; // sine synth phase
|
||||
double frqHz; // sine synth frequency in Hz
|
||||
|
||||
// buffer and state for cmApCopyIn/Out()
|
||||
cmApSample_t* buf; // buf[bufSmpCnt] - circular interleaved audio buffer
|
||||
unsigned bufInIdx; // next input buffer index
|
||||
unsigned bufOutIdx; // next output buffer index
|
||||
unsigned bufFullCnt; // count of full samples
|
||||
|
||||
// debugging log data arrays
|
||||
unsigned logCnt; // count of elements in log[] and ilong[]
|
||||
char* log; // log[logCnt]
|
||||
unsigned* ilog; // ilog[logCnt]
|
||||
unsigned logIdx; // current log index
|
||||
|
||||
unsigned cbCnt; // count the callback
|
||||
} cmApPortTestRecd;
|
||||
|
||||
unsigned _cmGlobalInDevIdx = 0;
|
||||
unsigned _cmGlobalOutDevIdx = 0;
|
||||
unsigned _cmGlobalCbCnt = 0;
|
||||
|
||||
#define aSrate 48000
|
||||
#define aFrmN aSrate*10
|
||||
#define aChN 2
|
||||
#define abufN aFrmN*aChN
|
||||
|
||||
cmApSample_t abuf[ abufN ];
|
||||
unsigned abufi = 0;
|
||||
|
||||
void _abuf_copy_in( cmApAudioPacket_t* pktArray, unsigned pktN )
|
||||
{
|
||||
unsigned i,j,k;
|
||||
for(i=0; i<pktN; ++i)
|
||||
{
|
||||
cmApSample_t* sp = (cmApSample_t*)(pktArray[i].audioBytesPtr);
|
||||
unsigned frmN = pktArray[i].audioFramesCnt;
|
||||
unsigned chN = cmMin(pktArray[i].chCnt,aChN);
|
||||
|
||||
for(j=0; abufi<aFrmN && j<frmN; ++j, ++abufi)
|
||||
for(k=0; k<chN; ++k)
|
||||
abuf[ (k*aFrmN) + abufi ] = *sp++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void _abuf_write_audio_file(cmCtx_t* ctx )
|
||||
{
|
||||
cmApSample_t* sigVV[ ] = { abuf, abuf + aFrmN };
|
||||
cmAudioFileWriteFileFloat( "/home/kevin/temp/temp.wav", aSrate, 16, aFrmN, 2, sigVV, &ctx->rpt );
|
||||
}
|
||||
|
||||
void _abuf_write_csv_file(cmCtx_t* ctx )
|
||||
{
|
||||
cmFileH_t fH = cmFileNullHandle;
|
||||
unsigned i = 0,j;
|
||||
cmFileOpen( &fH, "/home/kevin/temp/temp.csv", kWriteFileFl, &ctx->rpt );
|
||||
|
||||
for(i=0; i<aFrmN; ++i)
|
||||
{
|
||||
for(j=0; j<aChN; ++j)
|
||||
{
|
||||
char comma = j==aChN-1 ? ' ':',';
|
||||
|
||||
cmFilePrintf(fH, "%f%c",abuf[ aFrmN*j + i ], comma );
|
||||
}
|
||||
|
||||
cmFilePrintf( fH, "\n");
|
||||
}
|
||||
|
||||
cmFileClose(&fH);
|
||||
}
|
||||
|
||||
void _cmApPortCb2( cmApAudioPacket_t* inPktArray, unsigned inPktCnt, cmApAudioPacket_t* outPktArray, unsigned outPktCnt )
|
||||
{
|
||||
cmApBufInputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
|
||||
|
||||
cmApBufUpdate( inPktArray, inPktCnt, outPktArray, outPktCnt );
|
||||
|
||||
if( outPktArray != NULL )
|
||||
_abuf_copy_in(outPktArray,outPktCnt);
|
||||
|
||||
|
||||
_cmGlobalCbCnt += 1;
|
||||
}
|
||||
|
||||
cmRC_t audio_port_test( cmCtx_t* ctx, cmApPortTestRecd* r, bool runFl )
|
||||
{
|
||||
cmRC_t rc = kOkAdRC;
|
||||
unsigned i = 0;
|
||||
int srateMult = 0;
|
||||
cmRpt_t* rpt = &ctx->rpt;
|
||||
|
||||
cmApSample_t buf[r->bufSmpCnt];
|
||||
char log[r->logCnt];
|
||||
unsigned ilog[r->logCnt];
|
||||
|
||||
r->buf = buf;
|
||||
r->log = log;
|
||||
r->ilog = ilog;
|
||||
r->cbCnt = 0;
|
||||
|
||||
_cmGlobalInDevIdx = r->inDevIdx;
|
||||
_cmGlobalOutDevIdx= r->outDevIdx;
|
||||
|
||||
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);
|
||||
|
||||
if( cmApFileAllocate(rpt) != kOkApRC )
|
||||
{
|
||||
rc = cmErrMsg(&ctx->err, kAudioPortFileFailAdRC,"Audio port file allocation failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// allocate the non-real-time port
|
||||
if( cmApNrtAllocate(rpt) != kOkApRC )
|
||||
{
|
||||
rc = cmErrMsg(&ctx->err, kAudioPortNrtFailAdRC,"Non-real-time system allocation failed.");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// initialize the audio device interface
|
||||
if( cmApInitialize(rpt) != kOkApRC )
|
||||
{
|
||||
rc = cmErrMsg(&ctx->err, kAudioPortFailAdRC,"Port initialize failed.\n");
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// report the current audio device configuration
|
||||
for(i=0; i<cmApDeviceCount(); ++i)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
// report the current audio devices using the audio port interface function
|
||||
cmApReport(rpt);
|
||||
|
||||
if( runFl )
|
||||
{
|
||||
// initialize the audio buffer
|
||||
cmApBufInitialize( cmApDeviceCount(), r->meterMs );
|
||||
|
||||
// setup the buffer for the output device
|
||||
cmApBufSetup( r->outDevIdx, r->srate, r->framesPerCycle, r->bufCnt, cmApDeviceChannelCount(r->outDevIdx,true), r->framesPerCycle, cmApDeviceChannelCount(r->outDevIdx,false), r->framesPerCycle, srateMult );
|
||||
|
||||
// setup the buffer for the input device
|
||||
if( r->inDevIdx != r->outDevIdx )
|
||||
cmApBufSetup( r->inDevIdx, r->srate, r->framesPerCycle, r->bufCnt, cmApDeviceChannelCount(r->inDevIdx,true), r->framesPerCycle, cmApDeviceChannelCount(r->inDevIdx,false), r->framesPerCycle, srateMult );
|
||||
|
||||
|
||||
// setup an output device
|
||||
if(cmApDeviceSetup(r->outDevIdx,r->srate,r->framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
|
||||
rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"Out audio device setup failed.\n");
|
||||
else
|
||||
// setup an input device
|
||||
if( cmApDeviceSetup(r->inDevIdx,r->srate,r->framesPerCycle,_cmApPortCb2,&r) != kOkApRC )
|
||||
rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"In audio device setup failed.\n");
|
||||
else
|
||||
// start the input device
|
||||
if( cmApDeviceStart(r->inDevIdx) != kOkApRC )
|
||||
rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"In audio device start failed.\n");
|
||||
else
|
||||
// start the output device
|
||||
if( cmApDeviceStart(r->outDevIdx) != kOkApRC )
|
||||
rc = cmErrMsg(&ctx->err, kAudioPortFailAdRC,"Out audio device start failed.\n");
|
||||
else
|
||||
cmRptPrintf(rpt,"Started...");
|
||||
|
||||
cmApBufEnableChannel(r->inDevIdx, -1, kEnableApFl | kInApFl );
|
||||
cmApBufEnableMeter( r->inDevIdx, -1, kEnableApFl | kInApFl );
|
||||
|
||||
cmApBufEnableChannel(r->outDevIdx, -1, kEnableApFl | kOutApFl );
|
||||
cmApBufEnableMeter( r->outDevIdx, -1, kEnableApFl | kOutApFl );
|
||||
|
||||
cmRptPrintf(rpt,"q=quit O/o=output tone, I/i=input tone P/p=pass s=buf report\n");
|
||||
char c;
|
||||
while((c=getchar()) != 'q')
|
||||
{
|
||||
//cmApAlsaDeviceRtReport(rpt,r->outDevIdx);
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case 'i':
|
||||
case 'I':
|
||||
cmApBufEnableTone(r->inDevIdx,-1,kInApFl | (c=='I'?kEnableApFl:0));
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
case 'O':
|
||||
cmApBufEnableTone(r->outDevIdx,-1,kOutApFl | (c=='O'?kEnableApFl:0));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
case 'P':
|
||||
cmApBufEnablePass(r->outDevIdx,-1,kOutApFl | (c=='P'?kEnableApFl:0));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
cmApBufReport(rpt);
|
||||
cmRptPrintf(rpt,"CB:%i\n",_cmGlobalCbCnt);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stop the input device
|
||||
if( cmApDeviceIsStarted(r->inDevIdx) )
|
||||
if( cmApDeviceStop(r->inDevIdx) != kOkApRC )
|
||||
cmRptPrintf(rpt,"In device stop failed.\n");
|
||||
|
||||
// stop the output device
|
||||
if( cmApDeviceIsStarted(r->outDevIdx) )
|
||||
if( cmApDeviceStop(r->outDevIdx) != kOkApRC )
|
||||
cmRptPrintf(rpt,"Out device stop failed.\n");
|
||||
}
|
||||
|
||||
errLabel:
|
||||
|
||||
// release any resources held by the audio port interface
|
||||
if( cmApFinalize() != kOkApRC )
|
||||
rc = cmErrMsg(&ctx->err,kAudioPortFailAdRC,"Finalize failed.\n");
|
||||
|
||||
cmApBufFinalize();
|
||||
|
||||
cmApNrtFree();
|
||||
cmApFileFree();
|
||||
|
||||
// report the count of audio buffer callbacks
|
||||
cmRptPrintf(rpt,"cb count:%i\n", r->cbCnt );
|
||||
//for(i=0; i<_logCnt; ++i)
|
||||
// cmRptPrintf(rpt,"%c(%i)",_log[i],_ilog[i]);
|
||||
//cmRptPrintf(rpt,"\n");
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void print( void* arg, const char* text )
|
||||
{
|
||||
printf("%s",text);
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
enum
|
||||
{
|
||||
kSratePoId = kBasePoId,
|
||||
kHzPoId,
|
||||
kChIdxPoId,
|
||||
kChCntPoId,
|
||||
kBufCntPoId,
|
||||
kFrmCntPoId,
|
||||
kFrmsPerBufPoId,
|
||||
kInDevIdxPoId,
|
||||
kOutDevIdxPoId,
|
||||
kReportFlagPoId
|
||||
};
|
||||
|
||||
cmRC_t rc = cmOkRC;
|
||||
bool memDebugFl = cmDEBUG_FL;
|
||||
unsigned memGuardByteCnt = memDebugFl ? 8 : 0;
|
||||
unsigned memAlignByteCnt = 16;
|
||||
unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
|
||||
cmPgmOptH_t poH = cmPgmOptNullHandle;
|
||||
const cmChar_t* appTitle = "audiodev";
|
||||
unsigned reportFl = 0;
|
||||
cmCtx_t ctx;
|
||||
cmApPortTestRecd r;
|
||||
memset(&r,0,sizeof(r));
|
||||
r.meterMs = 50;
|
||||
r.logCnt = 100;
|
||||
|
||||
memset(abuf,0,sizeof(cmApSample_t)*abufN);
|
||||
|
||||
cmCtxSetup(&ctx,appTitle,print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
|
||||
|
||||
cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt );
|
||||
|
||||
cmFsInitialize( &ctx, appTitle );
|
||||
|
||||
cmTsInitialize(&ctx );
|
||||
|
||||
cmPgmOptInitialize(&ctx, &poH, poBegHelpStr, poEndHelpStr );
|
||||
|
||||
cmPgmOptInstallDbl( poH, kSratePoId, 's', "srate", 0, 48000, &r.srate, 1,
|
||||
"Audio system sample rate." );
|
||||
|
||||
cmPgmOptInstallDbl( poH, kHzPoId, 'z', "hz", 0, 1000, &r.frqHz, 1,
|
||||
"Tone frequency in Hertz." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kChIdxPoId, 'x', "ch_index", 0, 0, &r.chIdx, 1,
|
||||
"Index of first channel index." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kChCntPoId, 'c', "ch_cnt", 0, 2, &r.chCnt, 1,
|
||||
"Count of audio channels." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kBufCntPoId, 'b', "buf_cnt", 0, 3, &r.bufCnt, 1,
|
||||
"Count of audio buffers. (e.g. 2=double buffering, 3=triple buffering)" );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kFrmsPerBufPoId, 'f', "frames_per_buf",0, 512, &r.framesPerCycle, 1,
|
||||
"Count of audio channels." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kInDevIdxPoId, 'i', "in_dev_index",0, 0, &r.inDevIdx, 1,
|
||||
"Input device index as taken from the audio device report." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kOutDevIdxPoId, 'o', "out_dev_index",0, 0, &r.outDevIdx, 1,
|
||||
"Output device index as taken from the audio device report." );
|
||||
|
||||
cmPgmOptInstallFlag( poH, kReportFlagPoId, 'r', "report_flag", 0, 1, &reportFl, 1,
|
||||
"Print an audio device report." );
|
||||
|
||||
// parse the command line arguments
|
||||
if( cmPgmOptParse(poH, argc, argv ) == kOkPoRC )
|
||||
{
|
||||
// handle the built-in arg's (e.g. -v,-p,-h)
|
||||
// (returns false if only built-in options were selected)
|
||||
if( cmPgmOptHandleBuiltInActions(poH, &ctx.rpt ) == false )
|
||||
goto errLabel;
|
||||
|
||||
rc = audio_port_test( &ctx, &r, !reportFl );
|
||||
}
|
||||
|
||||
errLabel:
|
||||
_abuf_write_audio_file(&ctx);
|
||||
cmPgmOptFinalize(&poH);
|
||||
cmTsFinalize();
|
||||
cmFsFinalize();
|
||||
cmMdReport( kIgnoreNormalMmFl );
|
||||
cmMdFinalize();
|
||||
|
||||
return rc;
|
||||
}
|
431
src/cmtools/cmtools.c
Normal file
431
src/cmtools/cmtools.c
Normal file
@ -0,0 +1,431 @@
|
||||
#include "cmPrefix.h"
|
||||
#include "cmGlobal.h"
|
||||
#include "cmRpt.h"
|
||||
#include "cmErr.h"
|
||||
#include "cmCtx.h"
|
||||
#include "cmMem.h"
|
||||
#include "cmMallocDebug.h"
|
||||
#include "cmLinkedHeap.h"
|
||||
#include "cmFileSys.h"
|
||||
#include "cmText.h"
|
||||
|
||||
#include "cmPgmOpts.h"
|
||||
|
||||
#include "cmXScore.h"
|
||||
#include "cmMidiScoreFollow.h"
|
||||
#include "cmScoreProc.h"
|
||||
|
||||
|
||||
#include "cmSymTbl.h"
|
||||
#include "cmTime.h"
|
||||
#include "cmMidi.h"
|
||||
#include "cmScore.h"
|
||||
|
||||
#include "cmMidiFile.h"
|
||||
|
||||
#include "cmFloatTypes.h"
|
||||
#include "cmAudioFile.h"
|
||||
#include "cmTimeLine.h"
|
||||
|
||||
enum
|
||||
{
|
||||
kOkCtRC = cmOkRC,
|
||||
kNoActionIdSelectedCtRC,
|
||||
kMissingRequiredFileNameCtRC,
|
||||
kScoreGenFailedCtRC,
|
||||
kScoreFollowFailedCtRC,
|
||||
kMidiFileRptFailedCtRC,
|
||||
kTimeLineRptFailedCtRC,
|
||||
kAudioFileRptFailedCtRC
|
||||
};
|
||||
|
||||
|
||||
const cmChar_t poEndHelpStr[] = "";
|
||||
const cmChar_t poBegHelpStr[] =
|
||||
"xscore_proc Music XML to electronic score generator\n"
|
||||
"\n"
|
||||
"USAGE:\n"
|
||||
"\n"
|
||||
"Parse an XML score file and decoration file to produce a score file in CSV format.\n"
|
||||
"\n"
|
||||
"cmtool --score_gen -x <xml_file> -d <dec_fn> {-c <csvScoreOutFn} {-m <midiOutFn>} {-s <svgOutFn>} {-r report} {-b begMeasNumb} {t begTempoBPM}\n"
|
||||
"\n"
|
||||
"Notes:\n"
|
||||
"1. If <dec_fn> does not exist then a decoration template file will be generated based on the MusicXML file. \n"
|
||||
"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"
|
||||
"3. See README.md for a detailed description of the how to edit the decoration file.\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"Use the score follower to generate a timeline configuration file.\n"
|
||||
"\n"
|
||||
"cmtool --timeline_gen -c <csvScoreFn> -i <midiInFn> -r <matchRptFn> -s <matchSvgFn> {-m <midiOutFn>} {-t timelineOutFn} \n"
|
||||
"\n"
|
||||
"Measure some perforamance attributes:\n"
|
||||
"\n"
|
||||
"cmtool --meas_gen -g <pgmRsrcFn> -r <measRptFn>\n"
|
||||
"\n"
|
||||
"Generate a score file report\n"
|
||||
"\n"
|
||||
"cmtool --score_report -c <csvScoreFn> -r <scoreRptFn>\n"
|
||||
"\n"
|
||||
"Generate a MIDI file report and optional SVG piano roll image\n"
|
||||
"\n"
|
||||
"cmtool --midi_report -i <midiInFn> -r <midiRptFn> {-s <svgOutFn>}\n"
|
||||
"\n"
|
||||
"Generate a timeline report\n"
|
||||
"\n"
|
||||
"cmtool --timeline_report -t <timelineFn> -r <timelineRptFn>\n"
|
||||
"\n"
|
||||
"Generate an audio file report\n"
|
||||
"\n"
|
||||
"cmtool --audiofile_report -a <audioFn> -r <rptFn>\n"
|
||||
"\n";
|
||||
|
||||
|
||||
void print( void* arg, const char* text )
|
||||
{
|
||||
printf("%s",text);
|
||||
}
|
||||
|
||||
bool verify_file_exists( cmCtx_t* ctx, const cmChar_t* fn, const cmChar_t* msg )
|
||||
{
|
||||
if( fn == NULL || cmFsIsFile(fn)==false )
|
||||
return cmErrMsg(&ctx->err,kMissingRequiredFileNameCtRC,"The required file <%s> does not exist.",msg);
|
||||
|
||||
return kOkCtRC;
|
||||
}
|
||||
|
||||
bool verify_non_null_filename( cmCtx_t* ctx, const cmChar_t* fn, const cmChar_t* msg )
|
||||
{
|
||||
if( fn == NULL )
|
||||
return cmErrMsg(&ctx->err,kMissingRequiredFileNameCtRC,"The required file name <%s> is blank.",msg);
|
||||
|
||||
return kOkCtRC;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
cmRC_t rc;
|
||||
if((rc = verify_file_exists(ctx,xmlFn,"XML file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
if( cmXScoreTest( ctx, xmlFn, decFn, csvOutFn, midiOutFn, svgOutFn, reportFl, begMeasNumb, begTempoBPM, svgStandAloneFl, svgPanZoomFl ) != kOkXsRC )
|
||||
return cmErrMsg(&ctx->err,kScoreGenFailedCtRC,"score_gen failed.");
|
||||
|
||||
return kOkCtRC;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
cmRC_t rc;
|
||||
|
||||
if((rc = verify_file_exists(ctx,csvScoreFn,"Score CSV file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
if((rc = verify_file_exists(ctx,midiInFn,"MIDI input file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
//if((rc = verify_file_exists(ctx,matchRptOutFn,"Match report file")) != kOkCtRC )
|
||||
// return rc;
|
||||
|
||||
//if((rc = verify_file_exists(ctx,matchSvgOutFn,"Match HTML/SVG file")) != kOkCtRC )
|
||||
// return rc;
|
||||
|
||||
if(cmMidiScoreFollowMain(ctx, csvScoreFn, midiInFn, matchRptOutFn, matchSvgOutFn, midiOutFn, timelineFn) != kOkMsfRC )
|
||||
return cmErrMsg(&ctx->err,kScoreFollowFailedCtRC,"score_follow failed.");
|
||||
|
||||
return kOkCtRC;
|
||||
}
|
||||
|
||||
cmRC_t meas_gen( cmCtx_t* ctx, const cmChar_t* pgmRsrcFn, const cmChar_t* outFn )
|
||||
{
|
||||
cmRC_t rc;
|
||||
|
||||
if((rc = verify_file_exists(ctx,pgmRsrcFn,"Program resource file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
if((rc = verify_non_null_filename( ctx,outFn,"Measurements output file.")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
return cmScoreProc(ctx, "meas", pgmRsrcFn, outFn );
|
||||
}
|
||||
|
||||
cmRC_t score_report( cmCtx_t* ctx, const cmChar_t* csvScoreFn, const cmChar_t* rptFn )
|
||||
{
|
||||
cmRC_t rc;
|
||||
|
||||
if((rc = verify_file_exists(ctx,csvScoreFn,"Score CSV file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
cmScoreReport(ctx,csvScoreFn,rptFn);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cmRC_t midi_file_report( cmCtx_t* ctx, const cmChar_t* midiFn, const cmChar_t* rptFn, const cmChar_t* svgFn, bool standAloneFl, bool panZoomFl )
|
||||
{
|
||||
cmRC_t rc ;
|
||||
|
||||
if((rc = verify_file_exists(ctx,midiFn,"MIDI file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
if((rc = cmMidiFileReport(ctx, midiFn, rptFn )) != kOkMfRC )
|
||||
return cmErrMsg(&ctx->err,kMidiFileRptFailedCtRC,"MIDI file report generation failed.");
|
||||
|
||||
if( svgFn != NULL )
|
||||
if((rc = cmMidiFileGenSvgFile(ctx, midiFn, svgFn, "midi_file_svg.css", standAloneFl, panZoomFl )) != kOkMfRC )
|
||||
return cmErrMsg(&ctx->err,kMidiFileRptFailedCtRC,"MIDI file SVG output generation failed.");
|
||||
|
||||
return kOkCtRC;
|
||||
}
|
||||
|
||||
cmRC_t timeline_report( cmCtx_t* ctx, const cmChar_t* timelineFn, const cmChar_t* tlPrefixPath, const cmChar_t* rptFn )
|
||||
{
|
||||
cmRC_t rc ;
|
||||
|
||||
if((rc = verify_file_exists(ctx,timelineFn,"Timeline file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
if((rc = cmTimeLineReport( ctx, timelineFn, tlPrefixPath, rptFn )) != kOkTlRC )
|
||||
return cmErrMsg(&ctx->err,kTimeLineRptFailedCtRC,"The timeline file report failed.");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
cmRC_t audio_file_report( cmCtx_t* ctx, const cmChar_t* audioFn, const cmChar_t* rptFn )
|
||||
{
|
||||
cmRC_t rc;
|
||||
|
||||
if((rc = verify_file_exists(ctx,audioFn,"Audio file")) != kOkCtRC )
|
||||
return rc;
|
||||
|
||||
if((rc = cmAudioFileReportInfo( ctx, audioFn, rptFn )) != kOkTlRC )
|
||||
return cmErrMsg(&ctx->err,kAudioFileRptFailedCtRC,"The audio file report failed.");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
cmRC_t midi_trim(cmCtx_t* ctx, const cmChar_t* midiInFn, unsigned begMidiUId, unsigned endMidiUId, const cmChar_t* midiOutFn)
|
||||
{
|
||||
cmRC_t rc;
|
||||
|
||||
if((rc = verify_file_exists(ctx,midiInFn,"MIDI file")) != kOkCtRC )
|
||||
return rc;
|
||||
// kNoteTerminateFl | kPedalTerminateFl
|
||||
return cmMidiFileTrimFn(ctx, midiInFn, begMidiUId, endMidiUId, 0, midiOutFn );
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
cmRC_t rc = cmOkRC;
|
||||
enum
|
||||
{
|
||||
kInvalidPoId = kBasePoId,
|
||||
kActionPoId,
|
||||
kXmlFileNamePoId,
|
||||
kDecorateFileNamePoId,
|
||||
kCsvOutFileNamePoId,
|
||||
kPgmRsrcFileNamePoId,
|
||||
kMidiOutFileNamePoId,
|
||||
kMidiInFileNamePoId,
|
||||
kSvgOutFileNamePoId,
|
||||
kStatusOutFileNamePoId,
|
||||
kTimelineFileNamePoId,
|
||||
kTimelinePrefixPoId,
|
||||
kAudioFileNamePoId,
|
||||
kReportFlagPoId,
|
||||
kSvgStandAloneFlPoId,
|
||||
kSvgPanZoomFlPoId,
|
||||
kBegMeasPoId,
|
||||
kBegBpmPoId,
|
||||
kBegMidiUidPoId,
|
||||
kEndMidiUidPoId
|
||||
};
|
||||
|
||||
enum {
|
||||
kNoSelId,
|
||||
kScoreGenSelId,
|
||||
kScoreFollowSelId,
|
||||
kMeasGenSelId,
|
||||
kScoreReportSelId,
|
||||
kMidiReportSelId,
|
||||
kTimelineReportSelId,
|
||||
kAudioReportSelId,
|
||||
kMidiTrimSelId
|
||||
};
|
||||
|
||||
|
||||
// initialize the heap check library
|
||||
bool memDebugFl = 0; //cmDEBUG_FL;
|
||||
unsigned memGuardByteCnt = memDebugFl ? 8 : 0;
|
||||
unsigned memAlignByteCnt = 16;
|
||||
unsigned memFlags = memDebugFl ? kTrackMmFl | kDeferFreeMmFl | kFillUninitMmFl : 0;
|
||||
cmPgmOptH_t poH = cmPgmOptNullHandle;
|
||||
const cmChar_t* appTitle = "cmtools";
|
||||
cmCtx_t ctx;
|
||||
const cmChar_t* xmlFn = NULL;
|
||||
const cmChar_t* decFn = NULL;
|
||||
const cmChar_t* pgmRsrcFn = NULL;
|
||||
const cmChar_t* csvScoreFn = NULL;
|
||||
const cmChar_t* midiOutFn = NULL;
|
||||
const cmChar_t* midiInFn = NULL;
|
||||
const cmChar_t* audioFn = NULL;
|
||||
const cmChar_t* svgOutFn = NULL;
|
||||
const cmChar_t* timelineFn = NULL;
|
||||
const cmChar_t* timelinePrefix = NULL;
|
||||
const cmChar_t* rptFn = NULL;
|
||||
unsigned reportFl = 0;
|
||||
unsigned svgStandAloneFl = 1;
|
||||
unsigned svgPanZoomFl = 1;
|
||||
int begMeasNumb = 0;
|
||||
int begTempoBPM = 60;
|
||||
unsigned begMidiUId = cmInvalidId;
|
||||
unsigned endMidiUId = cmInvalidId;
|
||||
unsigned actionSelId = kNoSelId;
|
||||
|
||||
cmCtxSetup(&ctx,appTitle,print,print,NULL,memGuardByteCnt,memAlignByteCnt,memFlags);
|
||||
|
||||
cmMdInitialize( memGuardByteCnt, memAlignByteCnt, memFlags, &ctx.rpt );
|
||||
|
||||
cmFsInitialize( &ctx, appTitle);
|
||||
|
||||
cmTsInitialize(&ctx );
|
||||
|
||||
cmPgmOptInitialize(&ctx, &poH, poBegHelpStr, poEndHelpStr );
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'S', "score_gen", 0, kScoreGenSelId, kNoSelId, &actionSelId, 1,
|
||||
"Run the score generation tool.","Action selector");
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'F', "score_follow", 0, kScoreFollowSelId, kNoSelId, &actionSelId, 1,
|
||||
"Run the time line marker generation tool.",NULL);
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'M', "meas_gen", 0, kMeasGenSelId, kNoSelId, &actionSelId, 1,
|
||||
"Generate perfomance measurements.",NULL);
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'R', "score_report", 0, kScoreReportSelId, kNoSelId, &actionSelId, 1,
|
||||
"Generate a score file report.",NULL);
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'I', "midi_report", 0, kMidiReportSelId, kNoSelId, &actionSelId, 1,
|
||||
"Generate a MIDI file report and optional SVG piano roll output.",NULL);
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'E', "timeline_report", 0, kTimelineReportSelId, kNoSelId, &actionSelId, 1,
|
||||
"Generate a timeline report.",NULL);
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'A', "audio_report", 0, kAudioReportSelId, kNoSelId, &actionSelId, 1,
|
||||
"Generate an audio file report.",NULL);
|
||||
|
||||
cmPgmOptInstallEnum( poH, kActionPoId, 'T', "midi_trim", 0, kMidiTrimSelId, kNoSelId, &actionSelId, 1,
|
||||
"Trim a MIDI file to create a shortened version.",NULL);
|
||||
|
||||
cmPgmOptInstallStr( poH, kXmlFileNamePoId, 'x', "muisic_xml_fn",0, NULL, &xmlFn, 1,
|
||||
"Name of the input MusicXML file.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kDecorateFileNamePoId, 'd', "dec_fn", 0, NULL, &decFn, 1,
|
||||
"Name of a score decoration file.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kCsvOutFileNamePoId, 'c', "score_csv_fn",0, NULL, &csvScoreFn, 1,
|
||||
"Name of a CSV score file.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kPgmRsrcFileNamePoId, 'g', "pgm_rsrc_fn", 0, NULL, &pgmRsrcFn, 1,
|
||||
"Name of program resource file.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kMidiOutFileNamePoId, 'm', "midi_out_fn", 0, NULL, &midiOutFn, 1,
|
||||
"Name of a MIDI file to generate as output.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kMidiInFileNamePoId, 'i', "midi_in_fn", 0, NULL, &midiInFn, 1,
|
||||
"Name of a MIDI file to generate as output.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kSvgOutFileNamePoId, 's', "svg_fn", 0, NULL, &svgOutFn, 1,
|
||||
"Name of a HTML/SVG file to generate as output.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kTimelineFileNamePoId, 't', "timeline_fn", 0, NULL, &timelineFn, 1,
|
||||
"Name of a timeline to generate as output.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kTimelinePrefixPoId, 'l', "tl_prefix", 0, NULL, &timelinePrefix,1,
|
||||
"Timeline data path prefix.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kAudioFileNamePoId, 'a', "audio_fn", 0, NULL, &audioFn, 1,
|
||||
"Audio file name.");
|
||||
|
||||
cmPgmOptInstallStr( poH, kStatusOutFileNamePoId,'r', "report_fn", 0, NULL, &rptFn, 1,
|
||||
"Name of a status file to generate as output.");
|
||||
|
||||
cmPgmOptInstallFlag( poH, kReportFlagPoId, 'f', "debug_fl", 0, 1, &reportFl, 1,
|
||||
"Print a report of the score following processing." );
|
||||
|
||||
cmPgmOptInstallInt( poH, kBegMeasPoId, 'b', "beg_meas", 0, 1, &begMeasNumb, 1,
|
||||
"The first measure the to be written to the output CSV, MIDI and SVG files." );
|
||||
|
||||
cmPgmOptInstallInt( poH, kBegBpmPoId, 'e', "beg_bpm", 0, 0, &begTempoBPM, 1,
|
||||
"Set to 0 to use the tempo from the score otherwise set to use the tempo at begMeasNumb." );
|
||||
|
||||
cmPgmOptInstallFlag( poH, kSvgStandAloneFlPoId, 'n', "svg_stand_alone_fl",0, 1, &svgStandAloneFl, 1,
|
||||
"Write the SVG file as a stand alone HTML file. Enabled by default." );
|
||||
|
||||
cmPgmOptInstallFlag( poH, kSvgPanZoomFlPoId, 'z', "svg_pan_zoom_fl", 0, 1, &svgPanZoomFl, 1,
|
||||
"Include the pan-zoom control. Enabled by default." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kBegMidiUidPoId, 'w', "beg_midi_uid", 0, 1, &begMidiUId, 1,
|
||||
"Begin MIDI msg. uuid." );
|
||||
|
||||
cmPgmOptInstallUInt( poH, kEndMidiUidPoId, 'y', "end_midi_uid", 0, 1, &endMidiUId, 1,
|
||||
"End MIDI msg. uuid." );
|
||||
|
||||
// parse the command line arguments
|
||||
if( cmPgmOptParse(poH, argc, argv ) == kOkPoRC )
|
||||
{
|
||||
// handle the built-in arg's (e.g. -v,-p,-h)
|
||||
// (returns false if only built-in options were selected)
|
||||
if( cmPgmOptHandleBuiltInActions(poH, &ctx.rpt ) == false )
|
||||
goto errLabel;
|
||||
|
||||
switch( actionSelId )
|
||||
{
|
||||
case kScoreGenSelId:
|
||||
rc = score_gen( &ctx, xmlFn, decFn, csvScoreFn, midiOutFn, svgOutFn, reportFl, begMeasNumb, begTempoBPM, svgStandAloneFl, svgPanZoomFl );
|
||||
break;
|
||||
|
||||
case kScoreFollowSelId:
|
||||
rc = score_follow( &ctx, csvScoreFn, midiInFn, rptFn, svgOutFn, midiOutFn, timelineFn );
|
||||
break;
|
||||
|
||||
case kMeasGenSelId:
|
||||
rc = meas_gen(&ctx, pgmRsrcFn, rptFn);
|
||||
break;
|
||||
|
||||
case kScoreReportSelId:
|
||||
rc = score_report(&ctx, csvScoreFn, rptFn );
|
||||
break;
|
||||
|
||||
case kMidiReportSelId:
|
||||
rc = midi_file_report(&ctx, midiInFn, rptFn, svgOutFn, svgStandAloneFl, svgPanZoomFl );
|
||||
break;
|
||||
|
||||
case kTimelineReportSelId:
|
||||
rc = timeline_report(&ctx, timelineFn, timelinePrefix, rptFn );
|
||||
break;
|
||||
|
||||
case kAudioReportSelId:
|
||||
rc = audio_file_report(&ctx, audioFn, rptFn );
|
||||
break;
|
||||
|
||||
case kMidiTrimSelId:
|
||||
rc = midi_trim(&ctx, midiInFn, begMidiUId, endMidiUId, midiOutFn);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = cmErrMsg(&ctx.err, kNoActionIdSelectedCtRC,"No action selector was selected.");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
errLabel:
|
||||
cmPgmOptFinalize(&poH);
|
||||
cmTsFinalize();
|
||||
cmFsFinalize();
|
||||
cmMdReport( kIgnoreNormalMmFl );
|
||||
cmMdFinalize();
|
||||
return rc;
|
||||
}
|
2232
src/cmtools/mas.c
Normal file
2232
src/cmtools/mas.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user