rodio/source/
mod.rs

1//! Sources of sound and various filters.
2
3use core::fmt;
4use core::time::Duration;
5
6use cpal::FromSample;
7
8use crate::Sample;
9
10pub use self::agc::AutomaticGainControl;
11pub use self::amplify::Amplify;
12pub use self::blt::BltFilter;
13pub use self::buffered::Buffered;
14pub use self::channel_volume::ChannelVolume;
15pub use self::chirp::{chirp, Chirp};
16pub use self::crossfade::Crossfade;
17pub use self::delay::Delay;
18pub use self::done::Done;
19pub use self::empty::Empty;
20pub use self::empty_callback::EmptyCallback;
21pub use self::fadein::FadeIn;
22pub use self::fadeout::FadeOut;
23pub use self::from_factory::{from_factory, FromFactoryIter};
24pub use self::from_iter::{from_iter, FromIter};
25pub use self::linear_ramp::LinearGainRamp;
26pub use self::mix::Mix;
27pub use self::pausable::Pausable;
28pub use self::periodic::PeriodicAccess;
29pub use self::position::TrackPosition;
30pub use self::repeat::Repeat;
31pub use self::samples_converter::SamplesConverter;
32pub use self::signal_generator::{Function, SignalGenerator};
33pub use self::sine::SineWave;
34pub use self::skip::SkipDuration;
35pub use self::skippable::Skippable;
36pub use self::spatial::Spatial;
37pub use self::speed::Speed;
38pub use self::stoppable::Stoppable;
39pub use self::take::TakeDuration;
40pub use self::uniform::UniformSourceIterator;
41pub use self::zero::Zero;
42
43mod agc;
44mod amplify;
45mod blt;
46mod buffered;
47mod channel_volume;
48mod chirp;
49mod crossfade;
50mod delay;
51mod done;
52mod empty;
53mod empty_callback;
54mod fadein;
55mod fadeout;
56mod from_factory;
57mod from_iter;
58mod linear_ramp;
59mod mix;
60mod pausable;
61mod periodic;
62mod position;
63mod repeat;
64mod samples_converter;
65mod signal_generator;
66mod sine;
67mod skip;
68mod skippable;
69mod spatial;
70mod speed;
71mod stoppable;
72mod take;
73mod uniform;
74mod zero;
75
76#[cfg(feature = "noise")]
77mod noise;
78#[cfg(feature = "noise")]
79pub use self::noise::{pink, white, PinkNoise, WhiteNoise};
80
81/// A source of samples.
82///
83/// # A quick lesson about sounds
84///
85/// ## Sampling
86///
87/// A sound is a vibration that propagates through air and reaches your ears. This vibration can
88/// be represented as an analog signal.
89///
90/// In order to store this signal in the computer's memory or on the disk, we perform what is
91/// called *sampling*. The consists in choosing an interval of time (for example 20µs) and reading
92/// the amplitude of the signal at each interval (for example, if the interval is 20µs we read the
93/// amplitude every 20µs). By doing so we obtain a list of numerical values, each value being
94/// called a *sample*.
95///
96/// Therefore a sound can be represented in memory by a frequency and a list of samples. The
97/// frequency is expressed in hertz and corresponds to the number of samples that have been
98/// read per second. For example if we read one sample every 20µs, the frequency would be
99/// 50000 Hz. In reality, common values for the frequency are 44100, 48000 and 96000.
100///
101/// ## Channels
102///
103/// But a frequency and a list of values only represent one signal. When you listen to a sound,
104/// your left and right ears don't receive exactly the same signal. In order to handle this,
105/// we usually record not one but two different signals: one for the left ear and one for the right
106/// ear. We say that such a sound has two *channels*.
107///
108/// Sometimes sounds even have five or six channels, each corresponding to a location around the
109/// head of the listener.
110///
111/// The standard in audio manipulation is to *interleave* the multiple channels. In other words,
112/// in a sound with two channels the list of samples contains the first sample of the first
113/// channel, then the first sample of the second channel, then the second sample of the first
114/// channel, then the second sample of the second channel, and so on. The same applies if you have
115/// more than two channels. The rodio library only supports this schema.
116///
117/// Therefore in order to represent a sound in memory in fact we need three characteristics: the
118/// frequency, the number of channels, and the list of samples.
119///
120/// ## The `Source` trait
121///
122/// A Rust object that represents a sound should implement the `Source` trait.
123///
124/// The three characteristics that describe a sound are provided through this trait:
125///
126/// - The number of channels can be retrieved with `channels`.
127/// - The frequency can be retrieved with `sample_rate`.
128/// - The list of values can be retrieved by iterating on the source. The `Source` trait requires
129///   that the `Iterator` trait be implemented as well. When a `Source` returns None the
130///   sound has ended.
131///
132/// # Frames
133///
134/// The samples rate and number of channels of some sound sources can change by itself from time
135/// to time.
136///
137/// > **Note**: As a basic example, if you play two audio files one after the other and treat the
138/// > whole as a single source, then the channels and samples rate of that source may change at the
139/// > transition between the two files.
140///
141/// However, for optimization purposes rodio supposes that the number of channels and the frequency
142/// stay the same for long periods of time and avoids calling `channels()` and
143/// `sample_rate` too frequently.
144///
145/// In order to properly handle this situation, the `current_frame_len()` method should return
146/// the number of samples that remain in the iterator before the samples rate and number of
147/// channels can potentially change.
148///
149pub trait Source: Iterator
150where
151    Self::Item: Sample,
152{
153    /// Returns the number of samples before the current frame ends. `None` means "infinite" or
154    /// "until the sound ends".
155    /// Should never return 0 unless there's no more data.
156    ///
157    /// After the engine has finished reading the specified number of samples, it will check
158    /// whether the value of `channels()` and/or `sample_rate()` have changed.
159    fn current_frame_len(&self) -> Option<usize>;
160
161    /// Returns the number of channels. Channels are always interleaved.
162    fn channels(&self) -> u16;
163
164    /// Returns the rate at which the source should be played. In number of samples per second.
165    fn sample_rate(&self) -> u32;
166
167    /// Returns the total duration of this source, if known.
168    ///
169    /// `None` indicates at the same time "infinite" or "unknown".
170    fn total_duration(&self) -> Option<Duration>;
171
172    /// Stores the source in a buffer in addition to returning it. This iterator can be cloned.
173
174    #[inline]
175    fn buffered(self) -> Buffered<Self>
176    where
177        Self: Sized,
178    {
179        buffered::buffered(self)
180    }
181
182    /// Mixes this source with another one.
183    #[inline]
184    fn mix<S>(self, other: S) -> Mix<Self, S>
185    where
186        Self: Sized,
187        Self::Item: FromSample<S::Item>,
188        S: Source,
189        S::Item: Sample,
190    {
191        mix::mix(self, other)
192    }
193
194    /// Repeats this source forever.
195    ///
196    /// Note that this works by storing the data in a buffer, so the amount of memory used is
197    /// proportional to the size of the sound.
198    #[inline]
199    fn repeat_infinite(self) -> Repeat<Self>
200    where
201        Self: Sized,
202    {
203        repeat::repeat(self)
204    }
205
206    /// Takes a certain duration of this source and then stops.
207    #[inline]
208    fn take_duration(self, duration: Duration) -> TakeDuration<Self>
209    where
210        Self: Sized,
211    {
212        take::take_duration(self, duration)
213    }
214
215    /// Delays the sound by a certain duration.
216    ///
217    /// The rate and channels of the silence will use the same format as the first frame of the
218    /// source.
219    #[inline]
220    fn delay(self, duration: Duration) -> Delay<Self>
221    where
222        Self: Sized,
223    {
224        delay::delay(self, duration)
225    }
226
227    /// Immediately skips a certain duration of this source.
228    ///
229    /// If the specified duration is longer than the source itself, `skip_duration` will skip to the end of the source.
230    #[inline]
231    fn skip_duration(self, duration: Duration) -> SkipDuration<Self>
232    where
233        Self: Sized,
234    {
235        skip::skip_duration(self, duration)
236    }
237
238    /// Amplifies the sound by the given value.
239    #[inline]
240    fn amplify(self, value: f32) -> Amplify<Self>
241    where
242        Self: Sized,
243    {
244        amplify::amplify(self, value)
245    }
246
247    /// Applies automatic gain control to the sound.
248    ///
249    /// Automatic Gain Control (AGC) adjusts the amplitude of the audio signal
250    /// to maintain a consistent output level.
251    ///
252    /// # Parameters
253    ///
254    /// `target_level`:
255    ///   **TL;DR**: Desired output level. 1.0 = original level, > 1.0 amplifies, < 1.0 reduces.
256    ///
257    ///   The desired output level, where 1.0 represents the original sound level.
258    ///   Values above 1.0 will amplify the sound, while values below 1.0 will lower it.
259    ///   For example, a target_level of 1.4 means that at normal sound levels, the AGC
260    ///   will aim to increase the gain by a factor of 1.4, resulting in a minimum 40% amplification.
261    ///   A recommended level is `1.0`, which maintains the original sound level.
262    ///
263    /// `attack_time`:
264    ///   **TL;DR**: Response time for volume increases. Shorter = faster but may cause abrupt changes. **Recommended: `4.0` seconds**.
265    ///
266    ///   The time (in seconds) for the AGC to respond to input level increases.
267    ///   Shorter times mean faster response but may cause abrupt changes. Longer times result
268    ///   in smoother transitions but slower reactions to sudden volume changes. Too short can
269    ///   lead to overreaction to peaks, causing unnecessary adjustments. Too long can make the
270    ///   AGC miss important volume changes or react too slowly to sudden loud passages. Very
271    ///   high values might result in excessively loud output or sluggish response, as the AGC's
272    ///   adjustment speed is limited by the attack time. Balance is key for optimal performance.
273    ///   A recommended attack_time of `4.0` seconds provides a sweet spot for most applications.
274    ///
275    /// `release_time`:
276    ///   **TL;DR**: Response time for volume decreases. Shorter = faster gain reduction. **Recommended: `0.005` seconds**.
277    ///
278    ///   The time (in seconds) for the AGC to respond to input level decreases.
279    ///   This parameter controls how quickly the gain is reduced when the signal level drops.
280    ///   Shorter release times result in faster gain reduction, which can be useful for quick
281    ///   adaptation to quieter passages but may lead to pumping effects. Longer release times
282    ///   provide smoother transitions but may be slower to respond to sudden decreases in volume.
283    ///   However, if the release_time is too high, the AGC may not be able to lower the gain
284    ///   quickly enough, potentially leading to clipping and distorted sound before it can adjust.
285    ///   Finding the right balance is crucial for maintaining natural-sounding dynamics and
286    ///   preventing distortion. A recommended release_time of `0.005` seconds often works well for
287    ///   general use, providing a good balance between responsiveness and smooth transitions.
288    ///
289    /// `absolute_max_gain`:
290    ///   **TL;DR**: Maximum allowed gain. Prevents over-amplification. **Recommended: `5.0`**.
291    ///
292    ///   The maximum gain that can be applied to the signal.
293    ///   This parameter acts as a safeguard against excessive amplification of quiet signals
294    ///   or background noise. It establishes an upper boundary for the AGC's signal boost,
295    ///   effectively preventing distortion or overamplification of low-level sounds.
296    ///   This is crucial for maintaining audio quality and preventing unexpected volume spikes.
297    ///   A recommended value for `absolute_max_gain` is `5`, which provides a good balance between
298    ///   amplification capability and protection against distortion in most scenarios.
299    ///
300    /// Use `get_agc_control` to obtain a handle for real-time enabling/disabling of the AGC.
301    ///
302    /// # Example (Quick start)
303    ///
304    /// ```rust
305    /// // Apply Automatic Gain Control to the source (AGC is on by default)
306    /// let agc_source = source.automatic_gain_control(1.0, 4.0, 0.005, 5.0);
307    ///
308    /// // Get a handle to control the AGC's enabled state (optional)
309    /// let agc_control = agc_source.get_agc_control();
310    ///
311    /// // You can toggle AGC on/off at any time (optional)
312    /// agc_control.store(false, std::sync::atomic::Ordering::Relaxed);
313    ///
314    /// // Add the AGC-controlled source to the sink
315    /// sink.append(agc_source);
316    ///
317    /// // Note: Using agc_control is optional. If you don't need to toggle AGC,
318    /// // you can simply use the agc_source directly without getting agc_control.
319    /// ```
320    #[inline]
321    fn automatic_gain_control(
322        self,
323        target_level: f32,
324        attack_time: f32,
325        release_time: f32,
326        absolute_max_gain: f32,
327    ) -> AutomaticGainControl<Self>
328    where
329        Self: Sized,
330    {
331        // Added Limits to prevent the AGC from blowing up. ;)
332        const MIN_ATTACK_TIME: f32 = 10.0;
333        const MIN_RELEASE_TIME: f32 = 10.0;
334        let attack_time = attack_time.min(MIN_ATTACK_TIME);
335        let release_time = release_time.min(MIN_RELEASE_TIME);
336
337        agc::automatic_gain_control(
338            self,
339            target_level,
340            attack_time,
341            release_time,
342            absolute_max_gain,
343        )
344    }
345
346    /// Mixes this sound fading out with another sound fading in for the given duration.
347    ///
348    /// Only the crossfaded portion (beginning of self, beginning of other) is returned.
349    #[inline]
350    fn take_crossfade_with<S: Source>(self, other: S, duration: Duration) -> Crossfade<Self, S>
351    where
352        Self: Sized,
353        Self::Item: FromSample<S::Item>,
354        <S as Iterator>::Item: Sample,
355    {
356        crossfade::crossfade(self, other, duration)
357    }
358
359    /// Fades in the sound.
360    #[inline]
361    fn fade_in(self, duration: Duration) -> FadeIn<Self>
362    where
363        Self: Sized,
364    {
365        fadein::fadein(self, duration)
366    }
367
368    /// Fades out the sound.
369    #[inline]
370    fn fade_out(self, duration: Duration) -> FadeOut<Self>
371    where
372        Self: Sized,
373    {
374        fadeout::fadeout(self, duration)
375    }
376
377    /// Applies a linear gain ramp to the sound.
378    ///
379    /// If `clamp_end` is `true`, all samples subsequent to the end of the ramp
380    /// will be scaled by the `end_value`. If `clamp_end` is `false`, all
381    /// subsequent samples will not have any scaling applied.
382    #[inline]
383    fn linear_gain_ramp(
384        self,
385        duration: Duration,
386        start_value: f32,
387        end_value: f32,
388        clamp_end: bool,
389    ) -> LinearGainRamp<Self>
390    where
391        Self: Sized,
392    {
393        linear_ramp::linear_gain_ramp(self, duration, start_value, end_value, clamp_end)
394    }
395
396    /// Calls the `access` closure on `Self` the first time the source is iterated and every
397    /// time `period` elapses.
398    ///
399    /// Later changes in either `sample_rate()` or `channels_count()` won't be reflected in
400    /// the rate of access.
401    ///
402    /// The rate is based on playback speed, so both the following will call `access` when the
403    /// same samples are reached:
404    /// `periodic_access(Duration::from_secs(1), ...).speed(2.0)`
405    /// `speed(2.0).periodic_access(Duration::from_secs(2), ...)`
406    #[inline]
407    fn periodic_access<F>(self, period: Duration, access: F) -> PeriodicAccess<Self, F>
408    where
409        Self: Sized,
410        F: FnMut(&mut Self),
411    {
412        periodic::periodic(self, period, access)
413    }
414
415    /// Changes the play speed of the sound. Does not adjust the samples, only the playback speed.
416    ///
417    /// # Note:
418    /// 1. **Increasing the speed will increase the pitch by the same factor**
419    /// - If you set the speed to 0.5 this will halve the frequency of the sound
420    ///   lowering its pitch.
421    /// - If you set the speed to 2 the frequency will double raising the
422    ///   pitch of the sound.
423    /// 2. **Change in the speed affect the total duration inversely**
424    /// - If you set the speed to 0.5, the total duration will be twice as long.
425    /// - If you set the speed to 2 the total duration will be halve of what it
426    ///   was.
427    ///
428    /// See [`Speed`] for details
429    #[inline]
430    fn speed(self, ratio: f32) -> Speed<Self>
431    where
432        Self: Sized,
433    {
434        speed::speed(self, ratio)
435    }
436
437    /// Adds a basic reverb effect.
438    ///
439    /// This function requires the source to implement `Clone`. This can be done by using
440    /// `buffered()`.
441    ///
442    /// # Example
443    ///
444    /// ```ignore
445    /// use std::time::Duration;
446    ///
447    /// let source = source.buffered().reverb(Duration::from_millis(100), 0.7);
448    /// ```
449    #[inline]
450    fn reverb(self, duration: Duration, amplitude: f32) -> Mix<Self, Delay<Amplify<Self>>>
451    where
452        Self: Sized + Clone,
453    {
454        let echo = self.clone().amplify(amplitude).delay(duration);
455        self.mix(echo)
456    }
457
458    /// Converts the samples of this source to another type.
459    #[inline]
460    fn convert_samples<D>(self) -> SamplesConverter<Self, D>
461    where
462        Self: Sized,
463        D: Sample,
464    {
465        SamplesConverter::new(self)
466    }
467
468    /// Makes the sound pausable.
469    // TODO: add example
470    #[inline]
471    fn pausable(self, initially_paused: bool) -> Pausable<Self>
472    where
473        Self: Sized,
474    {
475        pausable::pausable(self, initially_paused)
476    }
477
478    /// Makes the sound stoppable.
479    // TODO: add example
480    #[inline]
481    fn stoppable(self) -> Stoppable<Self>
482    where
483        Self: Sized,
484    {
485        stoppable::stoppable(self)
486    }
487
488    /// Adds a method [`Skippable::skip`] for skipping this source. Skipping
489    /// makes Source::next() return None. Which in turn makes the Sink skip to
490    /// the next source.
491    fn skippable(self) -> Skippable<Self>
492    where
493        Self: Sized,
494    {
495        skippable::skippable(self)
496    }
497
498    /// Start tracking the elapsed duration since the start of the underlying
499    /// source.
500    ///
501    /// If a speedup and or delay is applied after this that will not be reflected
502    /// in the position returned by [`get_pos`](TrackPosition::get_pos).
503    ///
504    /// This can get confusing when using [`get_pos()`](TrackPosition::get_pos)
505    /// together with [`Source::try_seek()`] as the latter does take all
506    /// speedup's and delay's into account. Its recommended therefore to apply
507    /// track_position after speedup's and delay's.
508    fn track_position(self) -> TrackPosition<Self>
509    where
510        Self: Sized,
511    {
512        position::track_position(self)
513    }
514
515    /// Applies a low-pass filter to the source.
516    /// **Warning**: Probably buggy.
517    #[inline]
518    fn low_pass(self, freq: u32) -> BltFilter<Self>
519    where
520        Self: Sized,
521        Self: Source<Item = f32>,
522    {
523        blt::low_pass(self, freq)
524    }
525
526    /// Applies a high-pass filter to the source.
527    #[inline]
528    fn high_pass(self, freq: u32) -> BltFilter<Self>
529    where
530        Self: Sized,
531        Self: Source<Item = f32>,
532    {
533        blt::high_pass(self, freq)
534    }
535
536    /// Applies a low-pass filter to the source while allowing the q (bandwidth) to be changed.
537    #[inline]
538    fn low_pass_with_q(self, freq: u32, q: f32) -> BltFilter<Self>
539    where
540        Self: Sized,
541        Self: Source<Item = f32>,
542    {
543        blt::low_pass_with_q(self, freq, q)
544    }
545
546    /// Applies a high-pass filter to the source while allowing the q (bandwidth) to be changed.
547    #[inline]
548    fn high_pass_with_q(self, freq: u32, q: f32) -> BltFilter<Self>
549    where
550        Self: Sized,
551        Self: Source<Item = f32>,
552    {
553        blt::high_pass_with_q(self, freq, q)
554    }
555
556    // There is no `can_seek()` method as it is impossible to use correctly. Between
557    // checking if a source supports seeking and actually seeking the sink can
558    // switch to a new source.
559
560    /// Attempts to seek to a given position in the current source.
561    ///
562    /// As long as the duration of the source is known seek is guaranteed to saturate
563    /// at the end of the source. For example given a source that reports a total duration
564    /// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
565    /// 42 seconds.
566    ///
567    /// # Errors
568    /// This function will return [`SeekError::NotSupported`] if one of the underlying
569    /// sources does not support seeking.
570    ///
571    /// It will return an error if an implementation ran
572    /// into one during the seek.
573    ///
574    /// Seeking beyond the end of a source might return an error if the total duration of
575    /// the source is not known.
576    #[allow(unused_variables)]
577    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
578        Err(SeekError::NotSupported {
579            underlying_source: std::any::type_name::<Self>(),
580        })
581    }
582}
583
584// We might add decoders requiring new error types, without non_exhaustive
585// this would break users builds
586/// Occurs when try_seek fails because the underlying decoder has an error or
587/// does not support seeking.
588#[non_exhaustive]
589#[derive(Debug)]
590pub enum SeekError {
591    /// One of the underlying sources does not support seeking
592    NotSupported {
593        /// The source that did not support seek
594        underlying_source: &'static str,
595    },
596    #[cfg(feature = "symphonia")]
597    /// The symphonia decoder ran into an issue
598    SymphoniaDecoder(crate::decoder::symphonia::SeekError),
599    #[cfg(feature = "wav")]
600    /// The hound (wav) decoder ran into an issue
601    HoundDecoder(std::io::Error),
602    // Prefer adding an enum variant to using this. Its meant for end users their
603    // own try_seek implementations
604    /// Any other error probably in a custom Source
605    Other(Box<dyn std::error::Error + Send>),
606}
607impl fmt::Display for SeekError {
608    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
609        match self {
610            SeekError::NotSupported { underlying_source } => {
611                write!(
612                    f,
613                    "Seeking is not supported by source: {}",
614                    underlying_source
615                )
616            }
617            #[cfg(feature = "symphonia")]
618            SeekError::SymphoniaDecoder(err) => write!(f, "Error seeking: {}", err),
619            #[cfg(feature = "wav")]
620            SeekError::HoundDecoder(err) => write!(f, "Error seeking in wav source: {}", err),
621            SeekError::Other(_) => write!(f, "An error occurred"),
622        }
623    }
624}
625impl std::error::Error for SeekError {
626    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
627        match self {
628            SeekError::NotSupported { .. } => None,
629            #[cfg(feature = "symphonia")]
630            SeekError::SymphoniaDecoder(err) => Some(err),
631            #[cfg(feature = "wav")]
632            SeekError::HoundDecoder(err) => Some(err),
633            SeekError::Other(err) => Some(err.as_ref()),
634        }
635    }
636}
637
638#[cfg(feature = "symphonia")]
639impl From<crate::decoder::symphonia::SeekError> for SeekError {
640    fn from(source: crate::decoder::symphonia::SeekError) -> Self {
641        SeekError::SymphoniaDecoder(source)
642    }
643}
644
645impl SeekError {
646    /// Will the source remain playing at its position before the seek or is it
647    /// broken?
648    pub fn source_intact(&self) -> bool {
649        match self {
650            SeekError::NotSupported { .. } => true,
651            #[cfg(feature = "symphonia")]
652            SeekError::SymphoniaDecoder(_) => false,
653            #[cfg(feature = "wav")]
654            SeekError::HoundDecoder(_) => false,
655            SeekError::Other(_) => false,
656        }
657    }
658}
659
660macro_rules! source_pointer_impl {
661    ($($sig:tt)+) => {
662        impl $($sig)+ {
663            #[inline]
664            fn current_frame_len(&self) -> Option<usize> {
665                (**self).current_frame_len()
666            }
667
668            #[inline]
669            fn channels(&self) -> u16 {
670                (**self).channels()
671            }
672
673            #[inline]
674            fn sample_rate(&self) -> u32 {
675                (**self).sample_rate()
676            }
677
678            #[inline]
679            fn total_duration(&self) -> Option<Duration> {
680                (**self).total_duration()
681            }
682
683            #[inline]
684            fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
685                (**self).try_seek(pos)
686            }
687        }
688    };
689}
690
691source_pointer_impl!(<S> Source for Box<dyn Source<Item = S>> where S: Sample,);
692
693source_pointer_impl!(<S> Source for Box<dyn Source<Item = S> + Send> where S: Sample,);
694
695source_pointer_impl!(<S> Source for Box<dyn Source<Item = S> + Send + Sync> where S: Sample,);
696
697source_pointer_impl!(<'a, S, C> Source for &'a mut C where S: Sample, C: Source<Item = S>,);