getSmoothContour: Smooth contour from anchors

View source: R/smoothContours.R

getSmoothContourR Documentation

Smooth contour from anchors

Description

Returns a smooth contour based on an arbitrary number of anchors - points through which we wish to draw a curve, and which may or may not be equidistant along the x (time) dimension. getSmoothContour is used extensively by soundgen for generating intonation contour, mouth opening, etc. Pitch contours are treated as a special case: values are log-transformed prior to smoothing, so that with 2 anchors we get a linear transition on a log scale (as if we were operating with musical notes rather than frequencies in Hz).

Usage

getSmoothContour(
  anchors = data.frame(time = c(0, 1), value = c(0, 1)),
  len = NULL,
  thisIsPitch = FALSE,
  normalizeTime = TRUE,
  interpol = "splineFC",
  discontThres = 0.05,
  jumpThres = 0.01,
  valueFloor = NULL,
  valueCeiling = NULL,
  plot = FALSE,
  xlim = NULL,
  ylim = NULL,
  xlab = "Time, ms",
  ylab = ifelse(thisIsPitch, "Frequency, Hz", "Amplitude"),
  main = ifelse(thisIsPitch, "Pitch contour", ""),
  samplingRate = 16000,
  voiced = NULL,
  contourLabel = NULL,
  NA_to_zero = TRUE,
  ...
)

Arguments

anchors

a numeric vector of values or a list/dataframe with one column (value) or two columns (time and value). anchors$time can be in ms or in arbitrary units, eg 0 to 1. anchors$value can be on any scale.

len

the required length of the output contour. If NULL, it will be calculated based on the maximum time value (in ms) and samplingRate. If len == 1, returns the mean of anchors

thisIsPitch

(boolean) is this a pitch contour? If TRUE, log-transforms before smoothing and plots in both Hz and musical notation

normalizeTime

if TRUE, normalizes anchors$time values to range from 0 to 1

interpol

the method of interpolation (only used for upsampling, not downsampling); see interpolate for details

discontThres

if two anchors are closer in time than discontThres (on a 0-1 scale, ie specified as proportion of total length), the contour is broken into segments with a linear transition between these segments; set to 0 to avoid ever splitting into segments

jumpThres

if anchors are closer than jumpThres, a new section starts with no transition at all (e.g. for adding pitch jumps); normally, jumpThres <= discontThres

valueFloor, valueCeiling

lower/upper bounds for the contour

plot

(boolean) produce a plot?

xlim, ylim, xlab, ylab, main

plotting options

samplingRate

sampling rate used to convert time values to points (Hz)

voiced, contourLabel

graphical parameters for plotting breathing contours (see examples below)

NA_to_zero

if TRUE, all NAs are replaced with zero; if FALSE, NAs propagate and can be upsampled

...

other plotting options passed to plot()

Value

A numeric vector of length len.

Examples

# long format: anchors are a dataframe
a = soundgen:::getSmoothContour(anchors = data.frame(
  time = c(50, 137, 300), value = c(0.03, 0.78, 0.5)),
  normalizeTime = FALSE,
  voiced = 200, valueFloor = 0, plot = TRUE, main = '',
  samplingRate = 16000) # breathing

# short format: anchors are a vector (equal time steps assumed)
a = soundgen:::getSmoothContour(anchors = c(350, 800, 600),
  len = 5500, thisIsPitch = TRUE, plot = TRUE,
  samplingRate = 3500) # pitch

# a single anchor gives constant value
a = soundgen:::getSmoothContour(anchors = 800,
  len = 500, thisIsPitch = TRUE, plot = TRUE, samplingRate = 500)

# two pitch anchors give loglinear F0 change
a = soundgen:::getSmoothContour(anchors = c(220, 440),
  len = 500, thisIsPitch = TRUE, plot = TRUE, samplingRate = 500)

## Two closely spaced anchors produce a pitch jump
# one curve through all anchors
a1 = soundgen:::getSmoothContour(anchors = list(time = c(0, .15, .2, .7, 1),
    value = c(360, 116, 550, 700, 610)), len = 500, thisIsPitch = TRUE,
    plot = TRUE, samplingRate = 500)
# two segments with a linear transition
a2 = soundgen:::getSmoothContour(anchors = list(time = c(0, .15, .17, .7, 1),
    value = c(360, 116, 550, 700, 610)), len = 500, thisIsPitch = TRUE,
    plot = TRUE, samplingRate = 500)
# two segments with an abrupt jump
a3 = soundgen:::getSmoothContour(anchors = list(time = c(0, .15, .155, .7, 1),
    value = c(360, 116, 550, 700, 610)), len = 500, thisIsPitch = TRUE,
    plot = TRUE, samplingRate = 500)
# compare:
plot(a2)
plot(a3)  # NB: the segment before the jump is upsampled to compensate

## Control the amount of smoothing
# linear - no smoothing
soundgen:::getSmoothContour(c(1, 3, 9, 10, 9, 9, 2), len = 100, plot = TRUE,
  interpol = 'linear')
# spline (smooth, but may overshoot slightly)
soundgen:::getSmoothContour(c(1, 3, 9, 10, 9, 9, 2), len = 100, plot = TRUE,
  interpol = 'splineFC')
# cosine - goes strictly through anchors, no overshoot, but possible ripples
soundgen:::getSmoothContour(c(1, 3, 9, 10, 9, 9, 2), len = 100, plot = TRUE,
  interpol = 'cosine')
# see ?interpolate for more options

## Upsample preserving leading and trailing NAs
anchors = data.frame(time =  c(1,  4,  5,  7,  10, 20, 23, 25, 30),
                     value = c(NA, NA, 10, 15, 12, NA, 17, 15, NA))
plot(anchors, type = 'b')
anchors_ups = soundgen:::getSmoothContour(
  anchors, len = 200,
  interpol = 'linear',
  NA_to_zero = FALSE,   # preserve NAs
  discontThres = 0)     # don't break into sub-contours
plot(anchors_ups, type = 'b')

soundgen documentation built on Sept. 20, 2026, 5:07 p.m.