In this vignette, we will illustrate how to extract kinematic (gestural) landmarks from displacement signals.
it01_dlc_path <- system.file("extdata/it01-dlc.tsv", package = "rticulate")
it01_dlc <- read_aaa(it01_dlc_path) |>
# Let's rename some columns.
rename(
date_time = `Date Time of recording`,
time_rec = `Time of sample in recording`,
time_annot = `Time of sample in annot`
)
#> Rows: 521 Columns: 36
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: "\t"
#> chr (4): Client family name, Date Time of recording, Prompt, Annotation Title
#> dbl (32): Time of sample in recording, Time of sample in annot, X0 DLC_Tongu...
#>
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Let’s plot the smoothed and resampled displacement signal on the Y axis of knot 8 (Blade 2).
it01_up <- it01_dlc |>
filter(spline == "DLC_Tongue") |>
mutate(
Y_sm = filter_signal(Y, order = 2, window_length = 7, apply = 2),
.by = c(displ_id)
) |>
reframe(
resample_signal(Y_sm, time_annot, by = 3), .by = c(displ_id, knot)
) |>
mutate(
Y_sm_up = filter_signal(signal_int, window_length = 11, apply = 2),
.by = c(displ_id, knot)
)
it01_up |>
filter(knot == 8) |>
ggplot(aes(time_int, Y_sm_up)) +
geom_point(alpha = 0.2) +
facet_wrap(vars(displ_id))
To obtain gestural landmarks, it is necessary to calculate the
velocity (i.e. the first derivative) of the displacement signal. This
package has a get_velocity()
function which calculates the
velocity of a signal.
Note that calculating the derivative of a signal returns a vector
that is one item shorter than the original signal vector we get the
derivative of. get_velocity()
adds a NA
at the
beginning of the output vector to correct for that.
The following code chunk illustrates the use of
get_velocity()
on a single signal
(displ_id = 14
) and plots the results.
it01_up <- it01_up |>
mutate(
Y_sm_up_vel = get_velocity(Y_sm_up)
)
it01_up |>
filter(displ_id == 14) |>
ggplot(aes(time_int, Y_sm_up_vel)) +
geom_point() +
geom_line(aes(y = abs(Y_sm_up_vel)), colour = "red") +
annotate("rect", xmin = 0.3, xmax = 0.55, ymin = -Inf, ymax = Inf, alpha = 0.3)
The black dots represent the calculated velocity of the signal while
the red line is the absolute velocity (i.e. the speed). Absolute
velocity is used by the get_landmarks()
function to obtain
the landmarks (you will see how to use this function in the next
section).
The grey area indicates the approximate location of the tongue blade raising and lowering in the sequence /ata/.
The following schematics illustrates the relation between the displacement signal, the velocity and absolute velocity signals, and the gestural landmarks.
The following code illustrates the use of
get_landmarks()
with the same single displacement signal
from above (displ_id == 14
).
For the get_landmarks()
function to function correctly,
the user has to select a time window within which the maximum
displacement (i.e. the minimum velocity) will be searched. The window
should not contain any other velocity minima. For consonants, a good
reference is the start and end of the consonant closure as obtained from
acoustics. To make things easier, here I am selecting a single window
for all displacement signals, which happens to work fine.
I also recommend to run the function on the signal within a reduced time window which includes the opening-closing gesture only. This will help reducing the erroneous detection of other peaks/minima in the signal.
# Filter signal to include a reduced time window
it01_up_win <- it01_up |>
filter(displ_id == 14, time_int > 0.3, time_int < 0.55)
# Get landmarks
it01_up_win_land <- get_landmarks(it01_up_win$Y_sm_up_vel, it01_up_win$time_int, 0.4, 0.5)
it01_up_win_land_long <- it01_up_win_land |>
pivot_longer(everything(), names_to = "land")
it01_up_win_land_long
#> # A tibble: 14 × 2
#> land value
#> <chr> <dbl>
#> 1 min_1_vel 0.0199
#> 2 peak_1_vel 0.562
#> 3 max_disp_vel 0
#> 4 peak_2_vel 0.494
#> 5 min_3_vel 0
#> 6 min_1_time 0.317
#> 7 peak_1_time 0.390
#> 8 max_disp_time 0.448
#> 9 peak_2_time 0.488
#> 10 min_3_time 0.526
#> 11 GEST_ons 0.336
#> 12 PLAT_ons 0.441
#> 13 PLAT_off 0.454
#> 14 GEST_off 0.522
The following plots show the gestural landmarks overlaid on the absolute velocity (top) and on the displacement (bottom).
it01_up_win |>
ggplot(aes(time_int, abs(get_velocity(Y_sm_up)))) +
geom_point(colour = "red") +
geom_vline(data = it01_up_win_land_long |> filter(str_detect(land, "_ons|_off")), aes(xintercept = value)) +
labs(y = "Absolute velocity")
it01_up_win |>
ggplot(aes(time_int, Y_sm_up)) +
geom_point() +
geom_vline(data = it01_up_win_land_long |> filter(str_detect(land, "_ons|_off")), aes(xintercept = value)) +
labs(y = "Y displacement")
Let’s now get gestural landmarks for all the knot 8 data.
it01_up_lands <- it01_up |>
filter(knot == 8, time_int > 0.3, time_int < 0.55) |>
reframe(
get_landmarks(Y_sm_up_vel, time_int, 0.35, 0.45),
.by = displ_id
)
it01_up_lands
#> # A tibble: 12 × 15
#> displ_id min_1_vel peak_1_vel max_disp_vel peak_2_vel min_3_vel min_1_time
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 13 0 0.526 0.00979 0.391 0 0.323
#> 2 28 0.241 0.687 0 0.306 0 0.306
#> 3 43 0 0.549 0 0.397 0 0.311
#> 4 58 0.0676 0.598 0 0.506 0 0.305
#> 5 73 0.113 0.706 0 0.446 0 0.303
#> 6 88 0.212 0.632 0 0.374 0 0.305
#> 7 103 0.166 0.648 0 0.419 0 0.303
#> 8 118 0.513 0.665 0 0.478 0 0.301
#> 9 133 0.393 0.606 0 0.446 0 0.304
#> 10 148 0.533 0.607 0 0.316 0 0.302
#> 11 163 0.389 0.574 0 0.444 0 0.303
#> 12 178 0.410 0.590 0 0.375 0 0.303
#> # ℹ 8 more variables: peak_1_time <dbl>, max_disp_time <dbl>,
#> # peak_2_time <dbl>, min_3_time <dbl>, GEST_ons <dbl>, PLAT_ons <dbl>,
#> # PLAT_off <dbl>, GEST_off <dbl>
And finally, let’s plot the gestural landmarks!
it01_up_lands_long <- it01_up_lands |>
pivot_longer(-displ_id, names_to = "land")
it01_up |>
filter(knot == 8) |>
ggplot(aes(time_int, Y_sm_up)) +
geom_point(alpha = 0.2) +
geom_vline(data = it01_up_lands_long |> filter(str_detect(land, "_ons|_off")), aes(xintercept = value)) +
facet_wrap(vars(displ_id))
#> Warning: Removed 1 row containing missing values or values outside the scale range
#> (`geom_vline()`).