Set-up

This week, I have expanded spotify.R and moved all of the functions, including new functions, there.

library(tidyverse)
library(spotifyr)
source('spotify.R')

New functions

Last week, we used several custom functions to work with the Spotify API:

This week, we have a few new custom functions.

Common combinations

Domain Normalisation Distance Summary Statistic
Non-negative (e.g., chroma) Manhattan Manhattan mean
Aitchison Aitchison centre
Euclidean cosine root mean square
angular root mean square
Chebyshev [none] max
Full-range (e.g., timbre) [none] Euclidean mean
Euclidean cosine root mean square
angular root mean square

Bloed, Zweet en Tranen

The following examples from Andre Hazes’s ‘Bloed, Zweet en Tranen’ highlight how to use these functions. Can you choose better combinations of normalisations, distances, and summary statistics?

bzt <- 
    get_tidy_audio_analysis('54RPlXW7LlMNJMrkIJuXGI') %>% 
    compmus_align(bars, segments) %>% 
    select(bars) %>% unnest(bars) %>% 
    mutate(
        pitches = 
            map(segments, 
                compmus_summarise, pitches, 
                method = 'rms', norm = 'euclidean')) %>% 
    mutate(
        timbre = 
            map(segments, 
                compmus_summarise, timbre, 
                method = 'mean'))

Cepstrogram

We can use compmus_gather_timbre much like compmus_gather_chroma last week to yield a cepstrogram. Try different levels of structure – Spotify’s estimates of beats, bars, or sections – to see which level is the most meaningful.

bzt %>% 
    compmus_gather_timbre %>% 
    ggplot(
        aes(
            x = start + duration / 2, 
            width = duration, 
            y = basis, 
            fill = value)) + 
    geom_tile() +
    labs(x = 'Time (s)', y = NULL, fill = 'Magnitude') +
    scale_fill_viridis_c(option = 'E') +
    theme_classic()

Self-Similarity Matrices

The function compmus_self_similarity is a wrapper around compmus_long_distance from last week, for the case where the distances are computed form the same track. Try different distance functions to see what is most useful for this track – and compare your results with a self-similarity matrix based on chroma.

bzt %>% 
    compmus_self_similarity(timbre, 'cosine') %>% 
    ggplot(
        aes(
            x = xstart + xduration / 2, 
            width = xduration,
            y = ystart + yduration / 2,
            height = yduration,
            fill = d)) + 
    geom_tile() +
    coord_fixed() +
    scale_fill_viridis_c(option = 'E', guide = 'none') +
    theme_classic() +
    labs(x = '', y = '')
LS0tDQp0aXRsZTogIldlZWsgOSDCtyBTdHJ1Y3R1cmUgQW5hbHlzaXMgwrcgU2VsZi1TaW1pbGFyaXR5IE1hdHJpY2VzIg0KYXV0aG9yOiAiSm9obiBBc2hsZXkgQnVyZ295bmUiDQpkYXRlOiAiMjcgRmVicnVhcnkgMjAxOSINCm91dHB1dDoNCiAgICBodG1sX25vdGVib29rOg0KICAgICAgICB0aGVtZTogZmxhdGx5DQotLS0NCg0KIyMgU2V0LXVwDQoNClRoaXMgd2VlaywgSSBoYXZlIGV4cGFuZGVkIGBzcG90aWZ5LlJgIGFuZCBtb3ZlZCBhbGwgb2YgdGhlIGZ1bmN0aW9ucywgaW5jbHVkaW5nIG5ldyBmdW5jdGlvbnMsIHRoZXJlLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzcG90aWZ5cikNCnNvdXJjZSgnc3BvdGlmeS5SJykNCmBgYA0KDQojIyBOZXcgZnVuY3Rpb25zDQoNCkxhc3Qgd2Vlaywgd2UgdXNlZCBzZXZlcmFsIGN1c3RvbSBmdW5jdGlvbnMgdG8gd29yayB3aXRoIHRoZSBTcG90aWZ5IEFQSToNCg0KICAtIGBnZXRfdGlkeV9hdWRpb19hbmFseXNpc2AgdG8gbG9hZCBhdWRpbyBhbmFseXNlcyBmcm9tIFNwb3RpZnksIG9uZSB0cmFjayBhdCBhIHRpbWUNCiAgLSBgY29tcG11c19ub3JtYWxpc2VgIHRvIG5vcm1hbGlzZSBhdWRpbyBmZWF0dXJlcyB1c2luZyBjb21tb24gdGVjaG5pcXVlcywgaW5jbHVkaW5nOg0KICAgICAgLSBgbWFuaGF0dGFuYA0KICAgICAgLSBgZXVjbGlkZWFuYA0KICAgICAgLSBgY2hlYnlzaGV2YA0KICAtIGBjb21wbXVzX2xvbmdfZGlzdGFuY2VgIHRvIGNvbXBhcmUgdG8gc2VyaWVzIG9mIGF1ZGlvIGZlYXR1cmVzIGFnYWluc3QgZWFjaCBvdGhlciB1c2luZyBjb21tb24gZGlzdGFuY2UgbWV0cmljcywgaW5jbHVkaW5nOg0KICAgICAgLSBgbWFuaGF0dGFuYA0KICAgICAgLSBgYWl0Y2hpc29uYA0KICAgICAgLSBgZXVjbGlkZWFuYA0KICAgICAgLSBgY29zaW5lYA0KICAgICAgLSBgYW5ndWxhcmANCiAgDQpUaGlzIHdlZWssIHdlIGhhdmUgYSBmZXcgbmV3IGN1c3RvbSBmdW5jdGlvbnMuIA0KDQogIC0gYGNvbXBtdXNfYWxpZ25gIGFsaWducyB0d28gbGV2ZWxzIG9mIHN0cnVjdHVyZSB3aXRoIGVhY2ggb3RoZXIsIGUuZy4sIFNwb3RpZnkgc2VnbWVudHMgd2l0aCBiZWF0cyBvciBiYXJzLg0KICAtIGBjb21wbXVzX3N1bW1hcmlzZWAgaGVscHMgdG8gc3VtbWFyaXNlIGZlYXR1cmVzIHdpdGhpbiBoaWdoZXIgbGV2ZWxzIG9mIHN0cnVjdHVyZSwgaW5jbHVkaW5nOg0KICAgICAgLSBgbWVhbmANCiAgICAgIC0gYGFjZW50cmVgIFtBaXRjaGlzb24gY2VudHJlXQ0KICAgICAgLSBgcm1zYCBbcm9vdCBtZWFuIHNxdWFyZV0NCiAgICAgIC0gYG1heGANCg0KIyMgQ29tbW9uIGNvbWJpbmF0aW9ucw0KDQp8IERvbWFpbiAgICAgICAgICAgICAgICAgICAgICB8IE5vcm1hbGlzYXRpb24gfCBEaXN0YW5jZSAgfCBTdW1tYXJ5IFN0YXRpc3RpYyB8DQp8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IE5vbi1uZWdhdGl2ZSAoZS5nLiwgY2hyb21hKSB8IE1hbmhhdHRhbiAgICAgfCBNYW5oYXR0YW4gfCBtZWFuICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBBaXRjaGlzb24gfCBBaXRjaGlzb24gY2VudHJlICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEV1Y2xpZGVhbiAgICAgfCBjb3NpbmUgICAgfCByb290IG1lYW4gc3F1YXJlICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBhbmd1bGFyICAgfCByb290IG1lYW4gc3F1YXJlICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IENoZWJ5c2hldiAgICAgfCBbbm9uZV0gICAgfCBtYXggICAgICAgICAgICAgICB8DQp8IEZ1bGwtcmFuZ2UgKGUuZy4sIHRpbWJyZSkgICB8IFtub25lXSAgICAgICAgfCBFdWNsaWRlYW4gfCBtZWFuICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEV1Y2xpZGVhbiAgICAgfCBjb3NpbmUgICAgfCByb290IG1lYW4gc3F1YXJlICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgfCBhbmd1bGFyICAgfCByb290IG1lYW4gc3F1YXJlICB8DQoNCg0KIyMgQmxvZWQsIFp3ZWV0IGVuIFRyYW5lbg0KDQpUaGUgZm9sbG93aW5nIGV4YW1wbGVzIGZyb20gQW5kcmUgSGF6ZXMncyDigJhCbG9lZCwgWndlZXQgZW4gVHJhbmVu4oCZIGhpZ2hsaWdodCBob3cgdG8gdXNlIHRoZXNlIGZ1bmN0aW9ucy4gQ2FuIHlvdSBjaG9vc2UgYmV0dGVyIGNvbWJpbmF0aW9ucyBvZiBub3JtYWxpc2F0aW9ucywgZGlzdGFuY2VzLCBhbmQgc3VtbWFyeSBzdGF0aXN0aWNzPw0KDQpgYGB7cn0NCmJ6dCA8LSANCiAgICBnZXRfdGlkeV9hdWRpb19hbmFseXNpcygnNTRSUGxYVzdMbE1OSk1ya0lKdVhHSScpICU+JSANCiAgICBjb21wbXVzX2FsaWduKGJhcnMsIHNlZ21lbnRzKSAlPiUgDQogICAgc2VsZWN0KGJhcnMpICU+JSB1bm5lc3QoYmFycykgJT4lIA0KICAgIG11dGF0ZSgNCiAgICAgICAgcGl0Y2hlcyA9IA0KICAgICAgICAgICAgbWFwKHNlZ21lbnRzLCANCiAgICAgICAgICAgICAgICBjb21wbXVzX3N1bW1hcmlzZSwgcGl0Y2hlcywgDQogICAgICAgICAgICAgICAgbWV0aG9kID0gJ3JtcycsIG5vcm0gPSAnZXVjbGlkZWFuJykpICU+JSANCiAgICBtdXRhdGUoDQogICAgICAgIHRpbWJyZSA9IA0KICAgICAgICAgICAgbWFwKHNlZ21lbnRzLCANCiAgICAgICAgICAgICAgICBjb21wbXVzX3N1bW1hcmlzZSwgdGltYnJlLCANCiAgICAgICAgICAgICAgICBtZXRob2QgPSAnbWVhbicpKQ0KYGBgDQoNCiMjIyBDZXBzdHJvZ3JhbQ0KDQpXZSBjYW4gdXNlIGBjb21wbXVzX2dhdGhlcl90aW1icmVgIG11Y2ggbGlrZSBgY29tcG11c19nYXRoZXJfY2hyb21hYCBsYXN0IHdlZWsgdG8geWllbGQgYSBjZXBzdHJvZ3JhbS4gVHJ5IGRpZmZlcmVudCBsZXZlbHMgb2Ygc3RydWN0dXJlIC0tIFNwb3RpZnkncyBlc3RpbWF0ZXMgb2YgYmVhdHMsIGJhcnMsIG9yIHNlY3Rpb25zIC0tIHRvIHNlZSB3aGljaCBsZXZlbCBpcyB0aGUgbW9zdCBtZWFuaW5nZnVsLg0KDQpgYGB7cn0NCmJ6dCAlPiUgDQogICAgY29tcG11c19nYXRoZXJfdGltYnJlICU+JSANCiAgICBnZ3Bsb3QoDQogICAgICAgIGFlcygNCiAgICAgICAgICAgIHggPSBzdGFydCArIGR1cmF0aW9uIC8gMiwgDQogICAgICAgICAgICB3aWR0aCA9IGR1cmF0aW9uLCANCiAgICAgICAgICAgIHkgPSBiYXNpcywgDQogICAgICAgICAgICBmaWxsID0gdmFsdWUpKSArIA0KICAgIGdlb21fdGlsZSgpICsNCiAgICBsYWJzKHggPSAnVGltZSAocyknLCB5ID0gTlVMTCwgZmlsbCA9ICdNYWduaXR1ZGUnKSArDQogICAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gJ0UnKSArDQogICAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KIyMjIFNlbGYtU2ltaWxhcml0eSBNYXRyaWNlcw0KDQpUaGUgZnVuY3Rpb24gYGNvbXBtdXNfc2VsZl9zaW1pbGFyaXR5YCBpcyBhIHdyYXBwZXIgYXJvdW5kIGBjb21wbXVzX2xvbmdfZGlzdGFuY2VgIGZyb20gbGFzdCB3ZWVrLCBmb3IgdGhlIGNhc2Ugd2hlcmUgdGhlIGRpc3RhbmNlcyBhcmUgY29tcHV0ZWQgZm9ybSB0aGUgc2FtZSB0cmFjay4gVHJ5IGRpZmZlcmVudCBkaXN0YW5jZSBmdW5jdGlvbnMgdG8gc2VlIHdoYXQgaXMgbW9zdCB1c2VmdWwgZm9yIHRoaXMgdHJhY2sgLS0gYW5kIGNvbXBhcmUgeW91ciByZXN1bHRzIHdpdGggYSBzZWxmLXNpbWlsYXJpdHkgbWF0cml4IGJhc2VkIG9uIGNocm9tYS4NCg0KYGBge3J9DQpienQgJT4lIA0KICAgIGNvbXBtdXNfc2VsZl9zaW1pbGFyaXR5KHRpbWJyZSwgJ2Nvc2luZScpICU+JSANCiAgICBnZ3Bsb3QoDQogICAgICAgIGFlcygNCiAgICAgICAgICAgIHggPSB4c3RhcnQgKyB4ZHVyYXRpb24gLyAyLCANCiAgICAgICAgICAgIHdpZHRoID0geGR1cmF0aW9uLA0KICAgICAgICAgICAgeSA9IHlzdGFydCArIHlkdXJhdGlvbiAvIDIsDQogICAgICAgICAgICBoZWlnaHQgPSB5ZHVyYXRpb24sDQogICAgICAgICAgICBmaWxsID0gZCkpICsgDQogICAgZ2VvbV90aWxlKCkgKw0KICAgIGNvb3JkX2ZpeGVkKCkgKw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICdFJywgZ3VpZGUgPSAnbm9uZScpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIGxhYnMoeCA9ICcnLCB5ID0gJycpDQpgYGANCg0K