1#!/usr/local/bin/bash -e
2
3#####################################################################
4#                                                                   #
5#               Compiles Faust programs to Max externals            #
6#               using simple precision samples                      #
7#               (c) Grame, 2012-2019                                #
8#                                                                   #
9#####################################################################
10
11. faustpath
12. faustoptflags
13. usage.sh
14
15CXXFLAGS+=" $MYGCCFLAGSGENERIC"  # So that additional CXXFLAGS can be used
16
17OSCDEFS=""
18OSCLIB=""
19NVOICES=-1
20EFFECT=""
21SOUNDFILE="0"
22SOUNDFILEDEFS=""
23SOUNDFILELIBS=""
24US="0"
25DS="0"
26FILTER="0"
27
28# path to max SDK
29# MAXSDK is declared in faustpath
30MAXINC=$MAXSDK/max-includes
31MSPINC=$MAXSDK/msp-includes
32
33createInfoPList() {
34(
35    echo '<?xml version="1.0" encoding="UTF-8"?>'
36    echo '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
37    echo '<plist version="1.0">'
38    echo '<dict>'
39    echo '  <key>CFBundleDevelopmentRegion</key>'
40    echo '  <string>English</string>'
41    echo '  <key>CFBundleExecutable</key>'
42    echo "	<string>$1</string>"
43    echo '  <key>CFBundleIconFile</key>'
44    echo '  <string></string>'
45    echo '  <key>CFBundleIdentifier</key>'
46    echo "  <string>com.grame.$1</string>"
47    echo '  <key>CFBundleInfoDictionaryVersion</key>'
48    echo '  <string>1.0.0</string>'
49    echo '  <key>CFBundlePackageType</key>'
50    echo '  <string>iLaX</string>'
51    echo '  <key>CFBundleSignature</key>'
52    echo '  <string>max2</string>'
53    echo '  <key>CFBundleVersion</key>'
54    echo '  <string>1.0.0</string>'
55    echo '  <key>CFBundleShortVersionString</key>'
56    echo '  <string>1.0.0</string>'
57    echo '  <key>CFBundleLongVersionString</key>'
58    echo "  <string>$1 1.0.0, Copyright 2012-2018 Grame</string>"
59    echo '  <key>CSResourcesFileMapped</key>'
60    echo '  <true/>'
61    echo '</dict>'
62    echo '</plist>'
63    ) > "$2"
64}
65
66#-------------------------------------------------------------------
67# Analyze command arguments :
68# faust options                 -> OPTIONS
69# if -omp : -openmp or -fopenmp -> OPENMP
70# existing *.dsp files          -> FILES
71#
72
73# dispatch command arguments
74while [ $1 ]
75do
76    p=$1
77
78    if [ $p = "-help" ] || [ $p = "-h" ]; then
79        usage faust2msp "[options] [Faust options] <file.dsp>"
80        require Max-MSP SDK
81        echo "Compiles Faust programs to Max6 externals using simple precision samples"
82        option
83        options -osc -midi -soundfile
84        option "-nvoices <num>"
85        option "-us <factor>"
86        option "-ds <factor>"
87        option "-filter <filter>"
88        option "-effect <effect.dsp>"
89        option -native "to compile for the native CPU (otherwise the 'generic' mode is used by default)"
90        option "Faust options"
91        exit
92    fi
93
94	if [ $p = "-nvoices" ]; then
95        shift
96        NVOICES=$1
97        if [ $NVOICES -ge 0 ]; then
98            CXXFLAGS="$CXXFLAGS -DNVOICES=$NVOICES"
99        fi
100    elif [ $p = "-effect" ]; then
101        POLYDEFS="-DPOLY2"
102        shift
103    EFFECT=$1
104    elif [ $p = "-midi" ]; then
105        MIDIDEFS="-DMIDICTRL"
106    elif [ $p = "-soundfile" ]; then
107        SOUNDFILE="1"
108        SOUNDFILEDEFS="-DSOUNDFILE"
109        SOUNDFILELIBS=`pkg-config --cflags --static --libs sndfile`
110    elif [ $p = "-osc" ]; then
111        OSCDEFS="-DOSCCTRL"
112        OSCLIBS="-lOSCFaust"
113    elif [ $p = "-native" ]; then
114        CXXFLAGS=$MYGCCFLAGS
115    elif [ $p = "-us" ]; then
116        shift
117        US=$1
118    elif [ $p = "-ds" ]; then
119        shift
120        DS=$1
121    elif [ $p = "-filter" ]; then
122        shift
123        FILTER=$1
124    elif [ ${p:0:1} = "-" ]; then
125	    OPTIONS="$OPTIONS $p"
126	elif [[ -f "$p" ]] && [ ${p: -4} == ".dsp" ]; then
127	    FILES="$FILES $p"
128	else
129	    OPTIONS="$OPTIONS $p"
130	fi
131
132shift
133
134done
135
136#customize CXXFLAGS
137
138CXXFLAGS+=" -fbracket-depth=512"
139
140#-------------------------------------------------------------------
141# Check darwin specifics
142#
143if [[ $(uname) == Darwin ]]; then
144    EXT="~.mxo"
145fi
146
147#-------------------------------------------------------------------
148# compile the *.dsp files
149#
150PATH=$PATH:/usr/local/bin
151
152for p in $FILES; do
153
154    CC=g++
155
156    CUR=$(pwd)
157    f=$(basename "$p")
158	SRCDIR=$(dirname "$p")
159
160    # creates a temporary dir
161    TDR=$(mktemp -d faust.XXXXXX)
162	TMP="$TDR/${f%.dsp}"
163    mkdir "$TMP"
164
165    # compile faust to c++
166    if [ "$EFFECT" = "" ]; then
167        faust -i -cn ${f%.dsp} -uim -json -a $FAUSTARCH/max-msp/max-msp.cpp $OPTIONS "$SRCDIR/$f" -o "$TMP/${f%.dsp}.cpp" || exit
168    else
169        faust -i -cn ${f%.dsp} -uim -json -a $FAUSTARCH/max-msp/max-msp.cpp $OPTIONS "$SRCDIR/$f" -o "$TMP/${f%.dsp}.cpp" || exit
170        faust -i -cn effect -a $FAUSTARCH/minimal-effect.cpp "$SRCDIR/$EFFECT" -o "$TMP/effect.h" || exit
171    fi
172
173    # compile c++ to binary
174    (
175        cd "$TMP"
176        install -d "${f%.dsp}$EXT/Contents/MacOS"
177        $CXX $CXXFLAGS -mmacosx-version-min=10.9 -Wfatal-errors -framework Carbon $POLYDEFS $MIDIDEFS $SOUNDFILEDEFS $OSCDEFS -DDOWN_SAMPLING=$DS -DUP_SAMPLING=$US -DFILTER_TYPE=$FILTER -I ../../ -I"$MAXINC" -I"$MSPINC" -F"$MAXINC" -F"$MSPINC" -framework MaxAPI -framework MaxAudioAPI $SOUNDFILELIBS $OSCLIBS -arch i386 -Wl,-Y,1455 -bundle "${f%.dsp}.cpp" -o "${f%.dsp}$EXT/Contents/MacOS/${f%.dsp}~"
178	) >/dev/null  || exit
179
180    rm -rf "$SRCDIR/${f%.dsp}$EXT"
181
182    # Keep .dsp and .cpp files in the plug-in
183    cp "$TMP/${f%.dsp}.cpp" "$TMP/${f%.dsp}$EXT"
184    cp "$SRCDIR/$f" "$TMP/${f%.dsp}$EXT"
185    # Create Info.plist
186    createInfoPList "${f%.dsp}~" "$TMP/${f%.dsp}$EXT/Contents/Info.plist"
187
188    if [ "$SOUNDFILE" = "1" ]; then
189        # get all soundfiles from the JSON file
190        cat $p.json | awk '
191                        BEGIN { FS=":"; SOFI=0; }
192                            /"soundfile"/ { SOFI=1; }
193                            /"url"/ {
194                            if (SOFI) {
195                                match($2, /"[^"]*/);
196                                split(substr($2, RSTART+2, RLENGTH-3), res, ";");
197                                for (x in res) print substr(res[x], 2, length(res[x])-2);
198                                SOFI=0;
199                            }
200                        }
201        ' > $p-tmp.txt
202        # copy found soundfiles in the final binary
203        for snd in $(cat $p-tmp.txt); do
204            if [ -f $snd ]; then
205                if [ ${snd:0:1} = "/" ]; then
206                    echo "Warning: soundfile with absolute path is not copied !"
207                else
208                    #create destination path and possibly create directory
209                    sfpath="$TMP/${f%.dsp}$EXT/$(dirname $snd)/"
210                    if ! [ -d $sfpath ]; then
211                        echo "Create $sfpath"
212                        mkdir $sfpath
213                    fi
214                    echo "Copy $snd in ${f%.dsp}$EXT"
215                    cp $snd $sfpath
216                fi
217            else
218                echo "Error: file $snd not found !"
219            fi
220        done
221        rm $p-tmp.txt
222    fi
223
224    cp -r "$TMP/${f%.dsp}$EXT" "$SRCDIR/${f%.dsp}$EXT"
225    rm -rf "$TDR"
226    rm $p.json
227
228    # collect binary file name for FaustWorks
229    BINARIES="$BINARIES$SRCDIR/${f%.dsp}$EXT;"
230
231done
232
233BINARIES="$BINARIES ui.js"
234
235echo $BINARIES
236
237
238