diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2277dad..8ec5c3a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -2,6 +2,8 @@ name: test on: push: + branches-ignore: + - main paths-ignore: - "doc/**" - ".vscode/**" @@ -33,7 +35,7 @@ jobs: # Install the required dependencies - name: Install test dependencies - run: sudo apt-get install clang cmake speech-dispatcher libspeechd-dev pkg-config libssl-dev alsa librust-alsa-sys-dev + run: sudo apt install clang cmake speech-dispatcher libspeechd-dev pkg-config libssl-dev alsa librust-alsa-sys-dev -y # Cargo fmt - uses: actions-rs/cargo@v1 @@ -66,12 +68,14 @@ jobs: # Install the required dependencies on Ubuntu - name: Install test dependencies - run: sudo apt-get install clang cmake speech-dispatcher libspeechd-dev pkg-config libssl-dev alsa librust-alsa-sys-dev + run: sudo apt install clang cmake speech-dispatcher libspeechd-dev pkg-config libssl-dev alsa librust-alsa-sys-dev -y if: ${{ contains(matrix.os, 'ubuntu') }} - # Test for Ubuntu 20.04 + # Test for Ubuntu 20.04. Copy a valid speechd.conf file and then test. - name: Test - run: cargo test --all --features speech_dispatcher_0_9 + run: | + sudo cp test_files/ubuntu20.04/speechd.conf /etc/speech-dispatcher/speechd.conf + cargo test --all --features speech_dispatcher_0_9 if: matrix.os == 'ubuntu-20.04' # Test for Ubuntu 22.04 diff --git a/Cargo.lock b/Cargo.lock index 46bab6c..18aa822 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "audio" -version = "0.2.4" +version = "0.2.5" dependencies = [ "chrono", "common", @@ -141,9 +141,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.2" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" @@ -247,7 +247,7 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cacophony" -version = "0.2.4" +version = "0.2.5" dependencies = [ "audio", "clap", @@ -265,11 +265,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -405,7 +406,7 @@ dependencies = [ [[package]] name = "common" -version = "0.2.4" +version = "0.2.5" dependencies = [ "clap", "directories", @@ -837,9 +838,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", @@ -895,7 +896,7 @@ dependencies = [ "atomic-polyfill", "hash32", "rustc_version", - "spin 0.9.8", + "spin", "stable_deref_trait", ] @@ -1017,13 +1018,14 @@ dependencies = [ [[package]] name = "input" -version = "0.2.4" +version = "0.2.5" dependencies = [ "clap", "common", "hashbrown 0.13.2", "macroquad", "midir", + "midly", "parking_lot 0.12.1", "rust-ini", "serde", @@ -1043,7 +1045,7 @@ dependencies = [ [[package]] name = "io" -version = "0.2.4" +version = "0.2.5" dependencies = [ "audio", "common", @@ -1155,9 +1157,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" @@ -1878,7 +1880,7 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "render" -version = "0.2.4" +version = "0.2.5" dependencies = [ "audio", "colorgrad", @@ -1902,17 +1904,17 @@ checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" [[package]] name = "ring" -version = "0.16.20" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if 1.0.0", + "getrandom", "libc", - "once_cell", - "spin 0.5.2", + "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1956,23 +1958,32 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -2003,16 +2014,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "semver" version = "1.0.20" @@ -2120,12 +2121,6 @@ dependencies = [ "bindgen 0.65.1", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -2172,6 +2167,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -2209,7 +2210,7 @@ dependencies = [ [[package]] name = "text" -version = "0.2.4" +version = "0.2.5" dependencies = [ "common", "csv", @@ -2336,20 +2337,21 @@ dependencies = [ [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.8.0" +version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ "base64", "log", "once_cell", "rustls", + "rustls-pki-types", "rustls-webpki", "url", "webpki-roots", @@ -2497,9 +2499,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "which" @@ -2638,6 +2643,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -2824,3 +2838,9 @@ checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index bdd5264..e49543f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = ["audio", "common", "input", "io", "render", "text"] [workspace.package] -version = "0.2.4" +version = "0.2.5" authors = ["Esther Alter "] description = "A minimalist and ergonomic MIDI sequencer" documentation = "https://github.com/subalterngames/cacophony" @@ -48,7 +48,7 @@ default-features = false features = [] [workspace.dependencies.ureq] -version = "2.8.0" +version = "2.9.7" default-features = false features = ["tls"] @@ -84,7 +84,7 @@ speech_dispatcher_0_9 = ["text/speech_dispatcher_0_9"] [package] name = "cacophony" -version = "0.2.4" +version = "0.2.5" authors = ["Esther Alter "] description = "A minimalist and ergonomic MIDI sequencer" documentation = "https://github.com/subalterngames/cacophony" @@ -120,7 +120,7 @@ path = "text" name = "Cacophony" identifier = "com.subalterngames.cacophony" icon = ["icon/32.png", "icon/64.png", "icon/128.png", "icon/256.png"] -version = "0.2.4" +version = "0.2.5" resources = ["data/*"] copyright = "Copyright (c) Subaltern Games LLC 2023. All rights reserved." short_description = "A minimalist and ergonomic MIDI sequencer." diff --git a/changelog.md b/changelog.md index 2221a45..869006c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # 0.2.x +## 0.2.5 + +- Fixed: Crash if a Channel Pressure or Program Change message is sent from a MIDI controller. +- (Backend) Added some more tests +- (Backend) Bumped ureq version (fixes a security warning) +- (Backend) Added tests for TTS + ## 0.2.4 - Fixed: If note is played via a qwerty key press, and then an octave is changed via a qwerty key press, there won't be a note-off event. diff --git a/common/src/open_file/child_paths.rs b/common/src/open_file/child_paths.rs index a3c8039..b12fd00 100644 --- a/common/src/open_file/child_paths.rs +++ b/common/src/open_file/child_paths.rs @@ -152,7 +152,7 @@ mod tests { Some(ch) => ch != '.', None => false, }); - assert_eq!(child_paths.children.len(), 14); + assert!(child_paths.children.len() > 0); assert!(child_paths.selected.is_some()); assert_eq!(child_paths.selected.unwrap(), 0); // Go "up" a directory. diff --git a/input/Cargo.toml b/input/Cargo.toml index d260b06..d3bd874 100644 --- a/input/Cargo.toml +++ b/input/Cargo.toml @@ -18,5 +18,8 @@ midir = { workspace = true } parking_lot = { workspace = true } clap = { workspace = true } +[dev-dependencies] +midly = { workspace = true } + [dependencies.common] path = "../common" \ No newline at end of file diff --git a/input/src/midi_conn.rs b/input/src/midi_conn.rs index 3a60cd5..72695b6 100644 --- a/input/src/midi_conn.rs +++ b/input/src/midi_conn.rs @@ -68,9 +68,72 @@ impl MidiConn { /// The MIDI callback function. Send the message out of the thread. fn midi_callback(_: u64, message: &[u8], sender: &mut MidiBuffer) { - let mut m = [0u8; 3]; - m.copy_from_slice(&message[0..3]); + const LEN: usize = 3; + + // There are a few 2-byte MIDI messages that need to be ignored. + if message.len() != LEN { + return; + } + let mut m = [0u8; LEN]; + m.copy_from_slice(message); let mut buffer = sender.lock(); buffer.push(m); } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use super::MidiConn; + + use midly::{live::LiveEvent, MidiMessage}; + use parking_lot::Mutex; + + #[test] + fn midi_test() { + // These messages should be ready. + for midi_message in [ + MidiMessage::NoteOn { + key: 60.into(), + vel: 120.into(), + }, + MidiMessage::NoteOff { + key: 60.into(), + vel: 120.into(), + }, + ] + .iter() + .zip([144, 128]) + { + let message = LiveEvent::Midi { + channel: 0.into(), + message: midi_message.0.clone(), + }; + let mut buffer_conn = Arc::new(Mutex::new(Vec::new())); + let mut buffer_message = Vec::new(); + message.write(&mut buffer_message).unwrap(); + MidiConn::midi_callback(0, &buffer_message, &mut buffer_conn); + // The message was ready. + let b = buffer_conn.lock(); + assert_eq!(b.len(), 1); + assert_eq!(b[0], [midi_message.1, 60, 120]); + } + // These messages should be ignored. + for ignore_message in [ + MidiMessage::ChannelAftertouch { vel: 5.into() }, + MidiMessage::ProgramChange { program: 0.into() }, + ] { + let message = LiveEvent::Midi { + channel: 0.into(), + message: ignore_message, + }; + let mut buffer_conn = Arc::new(Mutex::new(Vec::new())); + let mut buffer_message = Vec::new(); + message.write(&mut buffer_message).unwrap(); + MidiConn::midi_callback(0, &buffer_message, &mut buffer_conn); + // The message was ignored. + assert_eq!(buffer_conn.lock().len(), 0); + } + } +} diff --git a/render/src/piano_roll_panel/viewable_notes.rs b/render/src/piano_roll_panel/viewable_notes.rs index 21d40bc..248eb6d 100644 --- a/render/src/piano_roll_panel/viewable_notes.rs +++ b/render/src/piano_roll_panel/viewable_notes.rs @@ -83,10 +83,10 @@ impl<'a> ViewableNotes<'a> { }; // Get the selected notes. - let selected = match state.select_mode.get_notes(&state.music) { - Some(selected) => selected, - None => vec![], - }; + let selected = state + .select_mode + .get_notes(&state.music) + .unwrap_or_default(); let mut notes = vec![]; for note in track.notes.iter() { // Is the note in view? diff --git a/test_files/ubuntu20.04/speechd.conf b/test_files/ubuntu20.04/speechd.conf new file mode 100644 index 0000000..665c0cb --- /dev/null +++ b/test_files/ubuntu20.04/speechd.conf @@ -0,0 +1,317 @@ +# Global configuration for Speech Dispatcher +# ========================================== + +# -----SYSTEM OPTIONS----- + +# CommunicationMethod specifies the method to be used by Speech Dispatcher to communicate with +# its clients. Two basic methods are "unix_socket" and "inet_socket". +# +# unix_socket -- communication over Unix sockets represented by a file in the +# filesystem (see SocketPath below). This method works only locally, but is +# prefered for standard session setup, where every user runs his own instance of Speech +# Dispatcher to get voice feedback on his own computer. +# +# inet_socket -- alternatively, you can start Speech Dispatcher on +# a TCP port and connect to it via hostname/port. This allows for a more +# flexible setup, where you can use Speech Dispatcher over network +# from different machines. See also the Port and LocalhostAccessOnly +# configuration variables. +# +# CommunicationMethod "unix_socket" + +# SocketPath is either "default" or a full path to the filesystem +# where the driving Unix socket file should be created in case the +# CommunicationMethod is set to "unix_socket". The default is +# $XDG_RUNTIME_DIR/speech-dispatcher/speechd.sock where $XDG_RUNTIME_DIR +# is the directory specified by the XDG Base Directory Specification. +# Do not change this unless you have a reason and know what you are doing. + +# SocketPath "default" + +# The Port on which Speech Dispatcher should be available to clients if the "inet_socket" +# communication method is used. + +# Port 6560 + +# By default, if "inet_socket" communication method is used, the specified port is opened only +# for connections coming from localhost. If LocalhostAccessOnly is set to 0 it disables this +# access control. It means that the port will be accessible from all computers on the +# network. If you turn off this option, please make sure you set up some system rules on what +# computers are and are not allowed to access the Speech Dispatcher port. + +# LocalhostAccessOnly 1 + +# By default, Speech Dispatcher is configured to shut itself down after a period of +# time if no clients are connected. The timeout value is in seconds, and is started when +# the last client disconnects. A value of 0 disables the timeout. + +# Timeout 5 + +# -----LOGGING CONFIGURATION----- + +# The LogLevel is a number between 0 and 5 specifying the +# verbosity of information to the logfile or screen +# 0 means nothing, 5 means everything (not recommended). + +LogLevel 3 + +# The LogDir specifies where the Speech Dispatcher logs reside +# Specify "stdout" for standard console output +# or a custom log directory path. 'default' means +# the logs are written to the default destination (e.g. a preconfigured +# system directory or the home directory if .speech-dispatcher is present) +# DO NOT COMMENT OUT THIS OPTION, leave as "default" for standard logging + +LogDir "default" +#LogDir "/var/log/speech-dispatcher/" +#LogDir "stdout" + +# The CustomLogFile allows logging all messages # regardless of +# priority, to the given destination. +#CustomLogFile "protocol" "/var/log/speech-dispatcher/speech-dispatcher-protocol.log" + +# ----- VOICE PARAMETERS ----- + +# The DefaultRate controls how fast the synthesizer is going to speak. +# The value must be between -100 (slowest) and +100 (fastest), default +# is 0. + +# DefaultRate 0 + +# The DefaultPitch controls the pitch of the synthesized voice. The +# value must be between -100 (lowest) and +100 (highest), default is +# 0. + +# DefaultPitch 0 + +# The DefaultPitchRange controls the pitch range of the synthesized voice. The +# value must be between -100 (lowest) and +100 (highest), default is +# 0. + +# DefaultPitchRange 0 + +# The DefaultVolume controls the default volume of the voice. It is +# a value between -100 (softly) and +100 (loudly). Currently, +100 +# maps to the default volume of the synthesizer. + +DefaultVolume 100 + +# The DefaultVoiceType controls which voice type should be used by +# default. Voice types are symbolic names which map to particular +# voices provided by the synthesizer according to the output module +# configuration. Please see the synthesizer-specific configuration +# in etc/speech-dispatcher/modules/ to see which voices are assigned to +# different symbolic names. The following symbolic names are +# currently supported: MALE1, MALE2, MALE3, FEMALE1, FEMALE2, FEMALE3, +# CHILD_MALE, CHILD_FEMALE + +# DefaultVoiceType "MALE1" + +# The Default language with which to speak + +# DefaultLanguage "en" + + +# ----- MESSAGE DISPATCHING CONTROL ----- + +# The DefaultClientName specifies the name of a client who didn't +# introduce himself at the beginning of an SSIP session. + +# DefaultClientName "unknown:unknown:unknown" + +# The Default Priority. Use with caution, normally this shouldn't be +# changed globally (at this place) + +# DefaultPriority "text" + +# The DefaultPauseContext specifies by how many index marks a speech +# cursor should return when resuming after a pause. This is roughly +# equivalent to the number of sentences before the place of the +# execution of pause that will be repeated. + +# DefaultPauseContext 0 + +# -----SPELLING/PUNCTUATION/CAPITAL LETTERS CONFIGURATION----- + +# The DefaultPunctuationMode sets the way dots, comas, exclamation +# marks, question marks etc. are interpreted. none: they are ignored +# some: some of them are sent to synthesis (see +# DefaultPunctuationSome) all: all punctuation marks are sent to +# synthesis + +# DefaultPunctuationMode "none" + +# Whether to use server-side symbols pre-processing by default. +# This controls whether the server should pre-process the messages to insert +# the appropriate words or if the output module is responsible for speaking +# symbols and punctuation. + +# DefaultSymbolsPreprocessing 0 + +# The DefaultCapLetRecognition: if set to "spell", capital letters +# should be spelled (e.g. "capital b"), if set to "icon", +# capital letters are indicated by inserting a special sound +# before them but they should be read normally, it set to "none" +# capital letters are not recognized (by default) + +# DefaultCapLetRecognition "none" + +# The DefaultSpelling: if set to On, all messages will be spelt +# unless set otherwise (this is usually not something you want to do.) + +# DefaultSpelling Off + +# ----- AUDIO CONFIGURATION ----------- + +# -- AUDIO OUTPUT -- + +# Chooses between the possible sound output systems: +# "pulse" - PulseAudio +# "alsa" - Advanced Linux Sound System +# "oss" - Open Sound System +# "nas" - Network Audio System +# "libao" - A cross platform audio library +# Pulse audio is the default and recommended sound server. OSS and ALSA +# are only provided for compatibility with architectures that do not +# include Pulse Audio. NAS provides network transparency, but is not +# very well tested. libao is a cross platform library with plugins for +# different sound systems and provides alternative output for Pulse Audio +# and ALSA as well as for other backends. + +# AudioOutputMethod "pulse" + +# -- Pulse Audio parameters -- + +# Pulse audio server name or "default" for the default pulse server + +#AudioPulseServer "default" + +#AudioPulseMinLength 1764 + +# -- ALSA parameters -- + +# Audio device for ALSA output + +#AudioALSADevice "default" + +# -- OSS parameters -- + +# Audio device for OSS output + +#AudioOSSDevice "/dev/dsp" + +# -- NAS parameters -- + +# Route to the Network Audio System server when NAS +# is chosen for the audio output. Note that NAS +# server doesn't need to run on your machine, +# you can use it also over network (for instance +# when working on remote machines). + +#AudioNASServer "tcp/localhost:5450" + + + +# -----OUTPUT MODULES CONFIGURATION----- + +# Each AddModule line loads an output module. +# Syntax: AddModule "name" "binary" "configuration" "logfile" +# - name is the name under which you can access this module +# - binary is the path to the binary executable of this module, +# either relative (to lib/speech-dispatcher-modules/) or absolute +# - configuration is the path to the config file of this module, +# either relative (to etc/speech-dispatcher/modules/) or absolute + +#AddModule "espeak" "sd_espeak" "espeak.conf" +#AddModule "espeak-ng" "sd_espeak-ng" "espeak-ng.conf" +#AddModule "festival" "sd_festival" "festival.conf" +#AddModule "flite" "sd_flite" "flite.conf" +#AddModule "ivona" "sd_ivona" "ivona.conf" +#AddModule "pico" "sd_pico" "pico.conf" +#AddModule "espeak-generic" "sd_generic" "espeak-generic.conf" +#AddModule "espeak-ng-mbrola-generic" "sd_generic" "espeak-ng-mbrola-generic.conf" +#AddModule "espeak-mbrola-generic" "sd_generic" "espeak-mbrola-generic.conf" +#AddModule "swift-generic" "sd_generic" "swift-generic.conf" +#AddModule "epos-generic" "sd_generic" "epos-generic.conf" +#AddModule "dtk-generic" "sd_generic" "dtk-generic.conf" +#AddModule "pico-generic" "sd_generic" "pico-generic.conf" +#AddModule "ibmtts" "sd_ibmtts" "ibmtts.conf" +#AddModule "cicero" "sd_cicero" "cicero.conf" +#AddModule "kali" "sd_kali" "kali.conf" +#AddModule "mary-generic" "sd_generic" "mary-generic.conf" +#AddModule "baratinoo" "sd_baratinoo" "baratinoo.conf" +#AddModule "rhvoice" "sd_rhvoice" "rhvoice.conf" +#AddModule "voxin" "sd_voxin" "voxin.conf" + +# DO NOT REMOVE the following line unless you have +# a specific reason -- this is the fallback output module +# that is only used when no other modules are in use +#AddModule "dummy" "sd_dummy" "" + +# The output module testing doesn't actually connect to anything. It +# outputs the requested commands to standard output and reads +# responses from stdandard input. This way, Speech Dispatcher's +# communication with output modules can be tested easily. + +AddModule "testing" + +# The DefaultModule selects which output module is the default. You +# must use one of the names of the modules loaded with AddModule. + +DefaultModule testing + +# The LanguageDefaultModule selects which output modules are prefered +# for specified languages. + +#LanguageDefaultModule "en" "espeak" +#LanguageDefaultModule "cs" "festival" +#LanguageDefaultModule "es" "festival" + +# -----CLIENT SPECIFIC CONFIGURATION----- + +# Here you can include the files with client-specific configuration +# for different types of clients. They must contain one or more sections with +# this structure: +# BeginClient "emacs:*" +# DefaultPunctuationMode "some" +# ...and/or some other settings +# EndClient +# The parameter of BeginClient tells Speech Dispatcher which clients +# it should apply the settings to (it does glob-style matching, you can use +# * to match any number of characters and ? to match one character) + +# There are some sample client settings + +Include "clients/*.conf" + +# The DisableAutoSpawn option will disable the autospawn mechanism. +# Thus the server will not start automatically on requests from the clients +# DisableAutoSpawn + + +# Copyright (C) 2001-2009 Brailcom, o.p.s +# Copyright (C) 2009 Rui Batista +# Copyright (C) 2010 Andrei Kholodnyi +# Copyright (C) 2010 William Hubbs +# Copyright (C) 2010 Trevor Saunders +# Copyright (C) 2012 William Jon McCann +# Copyright (C) 2014 Rob Whyte +# Copyright (C) 2014-2016 Luke Yelavich +# Copyright (C) 2014 Hussain Jasim +# Copyright (C) 2017 Colomban Wendling +# Copyright (C) 2018 Raphaƫl POITEVIN +# Copyright (C) 2018 Florian Steinhardt +# Copyright (C) 2018 Samuel Thibault +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more details (file +# COPYING in the root directory). +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . \ No newline at end of file diff --git a/text/src/tts.rs b/text/src/tts.rs index 3f97dc9..52e55e1 100644 --- a/text/src/tts.rs +++ b/text/src/tts.rs @@ -97,7 +97,10 @@ impl TTS { let _ = tts.set_rate(parse(section, rate_key)); (Some(tts), callbacks) } - Err(_) => (None, false), + Err(error) => { + println!("{}", error); + (None, false) + } }; Self { show_subtitles, @@ -255,3 +258,28 @@ fn on_utterance_end(_: UtteranceId) { let mut u = UTTERANCE_ID.lock(); *u = None; } + +#[cfg(test)] +mod tests { + use crate::Enqueable; + use common::get_test_config; + + use super::TTS; + + #[test] + fn test_tts() { + const TTS_STRING: &str = "Hello world!"; + + let config = get_test_config(); + let mut tts = TTS::new(&config); + assert!(tts.tts.is_some()); + tts.enqueue(TTS_STRING); + assert_eq!(tts.speech.len(), 1); + assert!(tts.show_subtitles); + assert_eq!(tts.get_subtitles().unwrap(), TTS_STRING); + tts.update(); + assert!(tts.is_speaking()); + tts.stop(); + tts.update(); + } +}