Skip to content

Instantly share code, notes, and snippets.

@happyalu
Created March 20, 2021 18:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save happyalu/f1b5ecd4736e06f9fcdef710f292b6d3 to your computer and use it in GitHub Desktop.
Save happyalu/f1b5ecd4736e06f9fcdef710f292b6d3 to your computer and use it in GitHub Desktop.
build and crosscompile flite TTS with the zig build system
// MIT LICENSE
//
// Copyright 2021 Alok Parlikar <alok@parlikar.com>
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// =============================================================================
// This file provides a way to build flite using the zig build system. To use
// this, zig (www.ziglang.org) must be installed and available in $PATH. zig
// version 0.8.0-dev.1544+867ae506e or newer is required.
//
// 1. git clone github.com/festvox/flite
// 2. save this file as flite/build.zig
// 3. cd flite
// 4. zig build
//
// to cross-compile, run:
// 5. zig build -Dtarget=<TARGET>
//
// example targets:
// x86_64-linux-gnu
// x86_64-macos
// x86_64-windows-gnu
// aarch64-macos
// aarch64-linux-gnu
//
// =============================================================================
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("flite", null);
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
exe.addIncludeDir("include");
exe.want_lto = false;
exe.addCSourceFiles(&.{
"main/flite_main.c",
"flite_lang_list.c",
"flite_voice_list.c",
}, &.{});
const flite = b.addStaticLibrary("flite", null);
flite.setTarget(target);
flite.setBuildMode(mode);
flite.linkLibC();
flite.addIncludeDir("include");
flite.addCSourceFiles(&.{
"src/audio/au_command.c",
//"src/audio/au_oss.c",
//"src/audio/au_palmos.c",
"src/audio/au_none.c",
"src/audio/au_streaming.c",
//"src/audio/au_sun.c",
//"src/audio/au_win.c",
//"src/audio/au_wince.c",
"src/audio/auclient.c",
"src/audio/audio.c",
"src/audio/auserver.c",
"src/cg/cst_cg.c",
"src/cg/cst_cg_dump_voice.c",
"src/cg/cst_cg_load_voice.c",
"src/cg/cst_cg_map.c",
"src/cg/cst_mlpg.c",
"src/cg/cst_mlsa.c",
"src/cg/cst_spamf0.c",
"src/cg/cst_vc.c",
"src/hrg/cst_ffeature.c",
"src/hrg/cst_item.c",
"src/hrg/cst_rel_io.c",
"src/hrg/cst_relation.c",
"src/hrg/cst_utterance.c",
"src/lexicon/cst_lexicon.c",
"src/lexicon/cst_lts.c",
"src/lexicon/cst_lts_rewrites.c",
"src/regex/cst_regex.c",
"src/regex/regexp.c",
"src/regex/regsub.c",
"src/speech/cst_lpcres.c",
"src/speech/cst_track.c",
"src/speech/cst_track_io.c",
"src/speech/cst_wave.c",
"src/speech/cst_wave_io.c",
"src/speech/cst_wave_utils.c",
"src/speech/g721.c",
"src/speech/g723_24.c",
"src/speech/g723_40.c",
"src/speech/g72x.c",
"src/speech/rateconv.c",
"src/stats/cst_cart.c",
"src/stats/cst_ss.c",
"src/stats/cst_viterbi.c",
"src/synth/cst_ffeatures.c",
"src/synth/cst_phoneset.c",
"src/synth/cst_ssml.c",
"src/synth/cst_synth.c",
"src/synth/cst_utt_utils.c",
"src/synth/cst_voice.c",
"src/synth/flite.c",
"src/utils/cst_alloc.c",
"src/utils/cst_args.c",
"src/utils/cst_endian.c",
"src/utils/cst_error.c",
"src/utils/cst_features.c",
//"src/utils/cst_file_palmos.c",
"src/utils/cst_file_stdio.c",
//"src/utils/cst_file_wince.c",
"src/utils/cst_mmap_none.c",
"src/utils/cst_socket.c",
"src/utils/cst_string.c",
"src/utils/cst_tokenstream.c",
"src/utils/cst_url.c",
"src/utils/cst_val.c",
"src/utils/cst_val_const.c",
"src/utils/cst_val_user.c",
"src/utils/cst_wchar.c",
"src/wavesynth/cst_clunits.c",
"src/wavesynth/cst_diphone.c",
"src/wavesynth/cst_reflpc.c",
"src/wavesynth/cst_sigpr.c",
"src/wavesynth/cst_sts.c",
"src/wavesynth/cst_units.c",
}, &.{
"-DCST_NO_SOCKETS",
});
const audio_driver = getAudioDriver(b);
if (audio_driver == AudioDriver.none) {
flite.addCSourceFiles(&.{}, &.{
"-DCST_AUDIO_NONE",
});
} else if (audio_driver == AudioDriver.alsa) {
flite.addCSourceFiles(&.{
"src/audio/au_alsa.c",
}, &.{
"-DCST_AUDIO_ALSA",
});
flite.linkSystemLibrary("asound");
} else if (audio_driver == AudioDriver.pulseaudio) {
flite.addCSourceFiles(&.{
"src/audio/au_pulseaudio.c",
}, &.{
"-DCST_AUDIO_PULSEAUDIO",
});
flite.linkSystemLibrary("pulse-simple");
flite.linkSystemLibrary("pulse");
}
if (flite.target.getOs().tag == .windows) {
flite.addCSourceFiles(&.{
"src/utils/cst_mmap_win32.c",
}, &.{});
} else {
flite.addCSourceFiles(&.{
"src/utils/cst_mmap_posix.c",
}, &.{});
}
exe.linkLibrary(flite);
// languages
const usenglish = b.addStaticLibrary("usenglish", null);
usenglish.setTarget(target);
usenglish.setBuildMode(mode);
usenglish.linkLibC();
usenglish.addIncludeDir("include");
usenglish.addCSourceFiles(&.{
"lang/usenglish/us_aswd.c",
"lang/usenglish/us_dur_stats.c",
"lang/usenglish/us_durz_cart.c",
"lang/usenglish/us_expand.c",
"lang/usenglish/us_f0_model.c",
"lang/usenglish/us_f0lr.c",
"lang/usenglish/us_ffeatures.c",
"lang/usenglish/us_gpos.c",
"lang/usenglish/us_int_accent_cart.c",
"lang/usenglish/us_int_tone_cart.c",
"lang/usenglish/us_nums_cart.c",
"lang/usenglish/us_phoneset.c",
"lang/usenglish/us_phrasing_cart.c",
"lang/usenglish/us_pos_cart.c",
"lang/usenglish/us_text.c",
"lang/usenglish/usenglish.c",
}, &.{});
exe.linkLibrary(usenglish);
const indic = b.addStaticLibrary("indic", null);
indic.setTarget(target);
indic.setBuildMode(mode);
indic.linkLibC();
indic.addIncludeDir("include");
indic.addCSourceFiles(&.{
"lang/cmu_indic_lang/cmu_indic_phrasing_cart.c",
"lang/cmu_indic_lang/cmu_indic_phoneset.c",
"lang/cmu_indic_lang/cmu_indic_lang.c",
}, &.{});
exe.linkLibrary(indic);
const grapheme = b.addStaticLibrary("grapheme", null);
grapheme.setTarget(target);
grapheme.setBuildMode(mode);
grapheme.linkLibC();
grapheme.addIncludeDir("include");
grapheme.addCSourceFiles(&.{
"lang/cmu_grapheme_lang/cmu_grapheme_phrasing_cart.c",
"lang/cmu_grapheme_lang/cmu_grapheme_phoneset.c",
"lang/cmu_grapheme_lang/cmu_grapheme_lang.c",
}, &.{});
exe.linkLibrary(grapheme);
// lexicons
const cmulex = b.addStaticLibrary("cmulex", null);
cmulex.setTarget(target);
cmulex.setBuildMode(mode);
cmulex.linkLibC();
cmulex.addIncludeDir("include");
cmulex.addCSourceFiles(&.{
"lang/cmulex/cmu_lex.c",
"lang/cmulex/cmu_lex_data.c",
"lang/cmulex/cmu_lex_entries.c",
"lang/cmulex/cmu_lts_model.c",
"lang/cmulex/cmu_lts_rules.c",
"lang/cmulex/cmu_postlex.c",
}, &.{});
exe.linkLibrary(cmulex);
const indiclex = b.addStaticLibrary("indiclex", null);
indiclex.setTarget(target);
indiclex.setBuildMode(mode);
indiclex.linkLibC();
indiclex.addIncludeDir("include");
indiclex.addCSourceFiles(&.{
"lang/cmu_indic_lex/cmu_indic_lex.c",
}, &.{});
exe.linkLibrary(indiclex);
const graphemelex = b.addStaticLibrary("graphemelex", null);
graphemelex.setTarget(target);
graphemelex.setBuildMode(mode);
graphemelex.linkLibC();
graphemelex.addIncludeDir("include");
graphemelex.addCSourceFiles(&.{
"lang/cmu_grapheme_lex/grapheme_unitran_tables.c",
"lang/cmu_grapheme_lex/cmu_grapheme_lex.c",
}, &.{});
exe.linkLibrary(graphemelex);
// voices
const kal = b.addStaticLibrary("kal", null);
kal.setTarget(target);
kal.setBuildMode(mode);
kal.linkLibC();
kal.addIncludeDir("include");
kal.addIncludeDir("lang/usenglish");
kal.addIncludeDir("lang/cmulex");
kal.linkLibrary(usenglish);
kal.linkLibrary(cmulex);
kal.addCSourceFiles(&.{
"lang/cmu_us_kal/cmu_us_kal_res.c",
"lang/cmu_us_kal/cmu_us_kal_diphone.c",
"lang/cmu_us_kal/cmu_us_kal_residx.c",
"lang/cmu_us_kal/cmu_us_kal.c",
"lang/cmu_us_kal/cmu_us_kal_ressize.c",
"lang/cmu_us_kal/cmu_us_kal_lpc.c",
}, &.{});
exe.linkLibrary(kal);
const cgvoices = [_][]const u8{ "rms", "slt", "awb" };
for (cgvoices) |voice| {
const libv = b.addStaticLibrary(voice, null);
libv.setTarget(target);
libv.setBuildMode(mode);
libv.linkLibC();
libv.addIncludeDir("include");
libv.addIncludeDir("lang/usenglish");
libv.addIncludeDir("lang/cmulex");
libv.linkLibrary(usenglish);
libv.linkLibrary(cmulex);
const cgfiles = [_][]const u8{
"_spamf0_phrase.c",
"_cg_f0_trees.c",
"_cg_phonestate.c",
"_cg.c",
"_spamf0_accent_params.c",
"_cg_single_mcep_trees.c",
".c",
"_spamf0_accent.c",
"_cg_durmodel.c",
"_cg_single_params.c",
};
var buf: [100]u8 = undefined;
for (cgfiles) |f| {
const fpath = std.fmt.bufPrint(buf[0..], "lang/cmu_us_{s}/cmu_us_{s}{s}", .{ voice, voice, f }) catch unreachable;
libv.addCSourceFiles(&.{fpath}, &.{});
}
exe.linkLibrary(libv);
}
}
pub const AudioDriver = enum {
none,
alsa,
pulseaudio,
};
pub fn getAudioDriver(b: *std.build.Builder) AudioDriver {
const audio_alsa = b.option(bool, "audio-alsa", "use alsa for direct playback") orelse false;
const audio_pa = b.option(bool, "audio-pulseaudio", "use pulseaudio for direct playback") orelse false;
const driver = if (!audio_alsa and !audio_pa)
AudioDriver.none
else if (audio_alsa and !audio_pa)
AudioDriver.alsa
else if (audio_pa and !audio_alsa)
AudioDriver.pulseaudio
else x: {
b.invalid_user_input = true;
break :x AudioDriver.none;
};
return driver;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment