summaryrefslogtreecommitdiff
path: root/src/Sound/RubberBand/Option.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Sound/RubberBand/Option.hs')
-rw-r--r--src/Sound/RubberBand/Option.hs459
1 files changed, 459 insertions, 0 deletions
diff --git a/src/Sound/RubberBand/Option.hs b/src/Sound/RubberBand/Option.hs
new file mode 100644
index 0000000..2bef7dc
--- /dev/null
+++ b/src/Sound/RubberBand/Option.hs
@@ -0,0 +1,459 @@
+module Sound.RubberBand.Option
+( Options(..)
+
+, Process(..)
+, Stretch(..)
+, Transients(..)
+, Detector(..)
+, Phase(..)
+, Threading(..)
+, Window(..)
+, Smoothing(..)
+, Formant(..)
+, Pitch(..)
+, Channels(..)
+
+, Option(..)
+
+, setOption
+, getOption
+
+, toOptions
+, fromOptions
+
+, defaultOptions
+, percussiveOptions
+) where
+
+import Data.Bits ((.|.), (.&.), complement)
+import Data.Maybe (fromMaybe)
+
+{- |
+Processing options for the timestretcher. The preferred
+options should normally be set when calling 'Sound.RubberBand.Nice.new'.
+-}
+data Options = Options
+ { oProcess :: Process
+ , oStretch :: Stretch
+ , oTransients :: Transients
+ , oDetector :: Detector
+ , oPhase :: Phase
+ , oThreading :: Threading
+ , oWindow :: Window
+ , oSmoothing :: Smoothing
+ , oFormant :: Formant
+ , oPitch :: Pitch
+ , oChannels :: Channels
+ } deriving (Eq, Ord, Show, Read)
+
+{- |
+'Process' flags determine how the timestretcher
+will be invoked. These options may not be changed after
+construction.
+
+The 'Process' setting is likely to depend on your architecture:
+non-real-time operation on seekable files: 'Offline'; real-time
+or streaming operation: 'RealTime'.
+-}
+data Process
+ = Offline
+ {- ^
+ Run the stretcher in offline mode. In this mode the input data needs to
+ be provided twice, once to 'Sound.RubberBand.Nice.study', which calculates
+ a stretch profile for the audio, and once to
+ 'Sound.RubberBand.Nice.process', which stretches it.
+ -}
+ | RealTime
+ {- ^
+ Run the stretcher in real-time mode. In this mode only
+ 'Sound.RubberBand.Nice.process' should be called, and the
+ stretcher adjusts dynamically in response to the input audio.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Stretch' flags control the profile used for
+variable timestretching. Rubber Band always adjusts the
+stretch profile to minimise stretching of busy broadband
+transient sounds, but the degree to which it does so is
+adjustable. These options may not be changed after
+construction.
+-}
+data Stretch
+ = Elastic
+ {- ^
+ Only meaningful in offline
+ mode, and the default in that mode. The audio will be
+ stretched at a variable rate, aimed at preserving the quality
+ of transient sounds as much as possible. The timings of low
+ activity regions between transients may be less exact than
+ when the precise flag is set.
+ -}
+ | Precise
+ {- ^
+ Although still using a variable
+ stretch rate, the audio will be stretched so as to maintain
+ as close as possible to a linear stretch ratio throughout.
+ Timing may be better than when using 'Elastic', at
+ slight cost to the sound quality of transients. This setting
+ is always used when running in real-time mode.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Transients' flags control the component
+frequency phase-reset mechanism that may be used at transient
+points to provide clarity and realism to percussion and other
+significant transient sounds. These options may be changed
+after construction when running in real-time mode, but not when
+running in offline mode.
+-}
+data Transients
+ = Crisp
+ {- ^
+ Reset component phases at the
+ peak of each transient (the start of a significant note or
+ percussive event). This, the default setting, usually
+ results in a clear-sounding output; but it is not always
+ consistent, and may cause interruptions in stable sounds
+ present at the same time as transient events. The
+ 'Detector' flags can be used to tune this to some
+ extent.
+ -}
+ | Mixed
+ {- ^
+ Reset component phases at the
+ peak of each transient, outside a frequency range typical of
+ musical fundamental frequencies. The results may be more
+ regular for mixed stable and percussive notes than
+ 'Crisp', but with a "phasier" sound. The
+ balance may sound very good for certain types of music and
+ fairly bad for others.
+ -}
+ | Smooth
+ {- ^
+ Do not reset component phases
+ at any point. The results will be smoother and more regular
+ but may be less clear than with either of the other
+ 'Transients' flags.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Detector' flags control the type of
+transient detector used. These options may be changed
+after construction when running in real-time mode, but not when
+running in offline mode.
+-}
+data Detector
+ = Compound
+ {- ^
+ Use a general-purpose
+ transient detector which is likely to be good for most
+ situations. This is the default.
+ -}
+ | Percussive
+ {- ^
+ Detect percussive
+ transients. Note that this was the default and only option
+ in Rubber Band versions prior to 1.5.
+ -}
+ | Soft
+ {- ^
+ Use an onset detector with less
+ of a bias toward percussive transients. This may give better
+ results with certain material (e.g. relatively monophonic
+ piano music).
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Phase' flags control the adjustment of
+component frequency phases from one analysis window to the next
+during non-transient segments. These options may be changed at
+any time.
+-}
+data Phase
+ = Laminar
+ {- ^
+ Adjust phases when stretching in
+ such a way as to try to retain the continuity of phase
+ relationships between adjacent frequency bins whose phases
+ are behaving in similar ways. This, the default setting,
+ should give good results in most situations.
+ -}
+ | Independent
+ {- ^
+ Adjust the phase in each
+ frequency bin independently from its neighbours. This
+ usually results in a slightly softer, phasier sound.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Threading' flags control the threading
+model of the stretcher. These options may not be changed after
+construction.
+-}
+data Threading
+ = Auto
+ {- ^
+ Permit the stretcher to
+ determine its own threading model. Usually this means using
+ one processing thread per audio channel in offline mode if
+ the stretcher is able to determine that more than one CPU is
+ available, and one thread only in realtime mode. This is the
+ defafult.
+ -}
+ | Never
+ {- ^
+ Never use more than one thread.
+ -}
+ | Always
+ {- ^
+ Use multiple threads in any
+ situation where 'Auto' would do so, except omit
+ the check for multiple CPUs and instead assume it to be true.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Window' flags control the window size for
+FFT processing. The window size actually used will depend on
+many factors, but it can be influenced. These options may not
+be changed after construction.
+-}
+data Window
+ = Standard
+ {- ^
+ Use the default window size.
+ The actual size will vary depending on other parameters.
+ This option is expected to produce better results than the
+ other window options in most situations.
+ -}
+ | Short
+ {- ^
+ Use a shorter window. This may
+ result in crisper sound for audio that depends strongly on
+ its timing qualities.
+ -}
+ | Long
+ {- ^
+ Use a longer window. This is
+ likely to result in a smoother sound at the expense of
+ clarity and timing.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Smoothing' flags control the use of
+window-presum FFT and time-domain smoothing. These options may
+not be changed after construction.
+-}
+data Smoothing
+ = SmoothingOff
+ {- ^
+ Do not use time-domain smoothing.
+ This is the default.
+ -}
+ | SmoothingOn
+ {- ^
+ Use time-domain smoothing. This
+ will result in a softer sound with some audible artifacts
+ around sharp transients, but it may be appropriate for longer
+ stretches of some instruments and can mix well with a 'Window' setting of
+ 'Short'.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Formant' flags control the handling of
+formant shape (spectral envelope) when pitch-shifting. These
+options may be changed at any time.
+-}
+data Formant
+ = Shifted
+ {- ^
+ Apply no special formant
+ processing. The spectral envelope will be pitch shifted as
+ normal. This is the default.
+ -}
+ | Preserved
+ {- ^
+ Preserve the spectral
+ envelope of the unshifted signal. This permits shifting the
+ note frequency without so substantially affecting the
+ perceived pitch profile of the voice or instrument.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Pitch' flags control the method used for
+pitch shifting. These options may be changed at any time.
+They are only effective in realtime mode; in offline mode, the
+pitch-shift method is fixed.
+-}
+data Pitch
+ = HighSpeed
+ {- ^
+ Use a method with a CPU cost
+ that is relatively moderate and predictable. This may
+ sound less clear than 'HighQuality', especially
+ for large pitch shifts. This is the default.
+ -}
+ | HighQuality
+ {- ^
+ Use the highest quality
+ method for pitch shifting. This method has a CPU cost
+ approximately proportional to the required frequency shift.
+ -}
+ | HighConsistency
+ {- ^
+ Use the method that gives
+ greatest consistency when used to create small variations in
+ pitch around the 1.0-ratio level. Unlike the previous two
+ options, this avoids discontinuities when moving across the
+ 1.0 pitch scale in real-time; it also consumes more CPU than
+ the others in the case where the pitch scale is exactly 1.0.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+{- |
+'Channels' flags control the method used for
+processing two-channel audio. These options may not be changed
+after construction.
+-}
+data Channels
+ = Apart
+ {- ^
+ Each channel is processed
+ individually, though timing is synchronised and phases are
+ synchronised at transients (depending on the 'Transients'
+ setting). This gives the highest quality for the individual
+ channels but a relative lack of stereo focus and unrealistic
+ increase in "width". This is the default.
+ -}
+ | Together
+ {- ^
+ The first two channels (where
+ two or more are present) are considered to be a stereo pair
+ and are processed in mid-side format; mid and side are
+ processed individually, with timing synchronised and phases
+ synchronised at transients (depending on the 'Transients'
+ setting). This usually leads to better focus in the centre
+ but a loss of stereo space and width. Any channels beyond
+ the first two are processed individually.
+ -}
+ deriving (Eq, Ord, Show, Read, Enum, Bounded)
+
+-- | A more limited enumeration class, without the extra functionality of
+-- Haskell's 'Enum'.
+class (Enum o, Bounded o) => Option o where
+ -- | Note that 'optionEnum' returns different values from 'toEnum'.
+ optionEnum :: o -> Int
+
+setOption :: (Option o) => o -> Int -> Int
+setOption o i = let
+ allOptions = [minBound..maxBound] `asTypeOf` [o]
+ mask = foldr (.|.) 0 $ map optionEnum allOptions
+ in (i .&. complement mask) .|. optionEnum o
+ -- Each option type uses some bits of an 'Int' for its value.
+ -- Conveniently only the low 29 bits are used, fitting nicely
+ -- into the Haskell Report's 30-bit guaranteed 'Int' size.
+
+getOption :: (Option o) => Int -> o
+getOption i = let
+ allOptions = [minBound..maxBound] `asTypeOf` [o]
+ lookupList = zip (map optionEnum allOptions) allOptions
+ mask = foldr (.|.) 0 $ map fst lookupList
+ masked = i .&. mask
+ o = fromMaybe
+ (error $ "getOption: no result for masked value " ++ show masked)
+ (lookup masked lookupList)
+ in o
+
+toOptions :: Int -> Options
+toOptions i = Options
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+ (getOption i)
+
+fromOptions :: Options -> Int
+fromOptions (Options a b c d e f g h i j k) = foldr (.|.) 0
+ [ optionEnum a
+ , optionEnum b
+ , optionEnum c
+ , optionEnum d
+ , optionEnum e
+ , optionEnum f
+ , optionEnum g
+ , optionEnum h
+ , optionEnum i
+ , optionEnum j
+ , optionEnum k
+ ]
+
+instance Option Process where
+ optionEnum Offline = 0x00000000
+ optionEnum RealTime = 0x00000001
+
+instance Option Stretch where
+ optionEnum Elastic = 0x00000000
+ optionEnum Precise = 0x00000010
+
+instance Option Transients where
+ optionEnum Crisp = 0x00000000
+ optionEnum Mixed = 0x00000100
+ optionEnum Smooth = 0x00000200
+
+instance Option Detector where
+ optionEnum Compound = 0x00000000
+ optionEnum Percussive = 0x00000400
+ optionEnum Soft = 0x00000800
+
+instance Option Phase where
+ optionEnum Laminar = 0x00000000
+ optionEnum Independent = 0x00002000
+
+instance Option Threading where
+ optionEnum Auto = 0x00000000
+ optionEnum Never = 0x00010000
+ optionEnum Always = 0x00020000
+
+instance Option Window where
+ optionEnum Standard = 0x00000000
+ optionEnum Short = 0x00100000
+ optionEnum Long = 0x00200000
+
+instance Option Smoothing where
+ optionEnum SmoothingOff = 0x00000000
+ optionEnum SmoothingOn = 0x00800000
+
+instance Option Formant where
+ optionEnum Shifted = 0x00000000
+ optionEnum Preserved = 0x01000000
+
+instance Option Pitch where
+ optionEnum HighSpeed = 0x00000000
+ optionEnum HighQuality = 0x02000000
+ optionEnum HighConsistency = 0x04000000
+
+instance Option Channels where
+ optionEnum Apart = 0x00000000
+ optionEnum Together = 0x10000000
+
+-- | Intended to give good results in most situations.
+defaultOptions :: Options
+defaultOptions = toOptions 0x00000000
+
+percussiveOptions :: Options
+percussiveOptions = toOptions 0x00102000
+ \ No newline at end of file