{"id":38656095,"url":"https://github.com/urswilke/pyramidi","last_synced_at":"2026-01-17T09:28:01.339Z","repository":{"id":46095718,"uuid":"302114983","full_name":"urswilke/pyramidi","owner":"urswilke","description":"Generate \u0026 manipulate midi data in R data frames","archived":false,"fork":false,"pushed_at":"2024-03-05T08:07:55.000Z","size":15532,"stargazers_count":9,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-03-05T11:26:22.497Z","etag":null,"topics":["midi"],"latest_commit_sha":null,"homepage":"https://urswilke.github.io/pyramidi/","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/urswilke.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-10-07T17:44:27.000Z","updated_at":"2024-03-05T11:26:22.498Z","dependencies_parsed_at":"2024-03-03T11:26:11.813Z","dependency_job_id":"817ea1d4-1850-466f-b8a5-2b26029a749a","html_url":"https://github.com/urswilke/pyramidi","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/urswilke/pyramidi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urswilke%2Fpyramidi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urswilke%2Fpyramidi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urswilke%2Fpyramidi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urswilke%2Fpyramidi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/urswilke","download_url":"https://codeload.github.com/urswilke/pyramidi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/urswilke%2Fpyramidi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28505556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T06:57:29.758Z","status":"ssl_error","status_checked_at":"2026-01-17T06:56:03.931Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["midi"],"created_at":"2026-01-17T09:28:01.243Z","updated_at":"2026-01-17T09:28:01.329Z","avatar_url":"https://github.com/urswilke.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput: github_document\nbibliography: grateful-refs.bib\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n# pyramidi \u003ca href='https://github.com/urswilke/pyramidi/'\u003e\u003cimg src='man/figures/hex_logo_pyramidi.png' align=\"right\" height=\"139\" /\u003e\u003c/a\u003e\n\n\u003c!-- badges: start --\u003e\n[![Codecov test coverage](https://codecov.io/gh/urswilke/pyramidi/branch/master/graph/badge.svg)](https://app.codecov.io/gh/urswilke/pyramidi?branch=master)\n[![R-CMD-check](https://github.com/urswilke/pyramidi/workflows/R-CMD-check/badge.svg)](https://github.com/urswilke/pyramidi/actions)\n[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)\n\u003c!-- badges: end --\u003e\n\n# Introduction\n\npyramidi is a very experimental package to generate / manipulate midi data from R.\nBe aware that a lot of the code I've written some years ago hurts my eyes when I look at it now :)\nMidi data is read into dataframes, using the python package [miditapyr](https://pypi.org/project/miditapyr/) \nunder the hood (which itself uses the excellent [mido](https://github.com/mido/mido)). \nThe notes' midi information (one line per `note_on`/`note_off` midi event) \nis translated into a wide format (one line per note). \nThis format facilitates some manipulations of the notes' data and also plotting them in piano roll plots. \nFinally, the modified dataframes can be written back to midi files (again using miditapyr).\n\nThus, you can manipulate all the intermediate dataframes and create midi files from R. \nHowever, you need to make sure yourself that the midi files you write can be understood by your softsynth.\nThe data is not yet validated by pyramidi, \nbut mido (also used to write midi files) already catches some of the possible inconsistencies. \n\nIf you're new to midi, [mido's documentation](https://mido.readthedocs.io/en/latest/) might be a good start.\n\n\n## New since version 0.2\n\nThe midi data can now be \n\n* played live in the R console OR generate a sound file and a html audio player when knitting rmarkdown documents thanks to the excellent R packages [fluidsynth](https://github.com/ropensci/fluidsynth) (see the documentation of the [`play()`](https://urswilke.github.io/pyramidi/reference/MidiFramer.html#method-play-) method in the `MidiFramer` class and its helper function `player()` which use\n* `fluidsynth::midi_convert()` to synthesize midi to wav files (needs [fluidsynth](https://www.fluidsynth.org/) installed, but if I understand correctly R will do that for you)\n\n\n\n## Installation\n\nYou can install pyramidi from R-universe with:\n\n``` r\ninstall.packages('pyramidi', repos = c('https://urswilke.r-universe.dev', 'https://cloud.r-project.org'))\n```\n\nThe python package [miditapyr](https://pypi.org/project/miditapyr/) also needs \nto be installed in your python environment used by \n[reticulate](https://github.com/rstudio/reticulate).\n\n```sh\npip install miditapyr\n```\n\n\n\nBut if everything works as I believe it should, miditapyr is automatically installed\nif you install pyramidi, as soon as you access the module for the first time. \n\nOtherwise, you can also install it in your reticulate python environment\nwith the included helper function:\n\n```{r, eval=FALSE}\npyramidi::install_miditapyr()\n```\n\n*I'm not sure if that works on windows too. Perhaps there you have to manually configure your reticulate environment.*\n\n## Usage \n\n### Generate a `MidiFramer` object\n\nWe can create a `MidiFramer` object by passing the file path to the constructor method \n([`new()`](https://urswilke.github.io/pyramidi/reference/MidiFramer.html#method-new)). \n\n```{r, message=FALSE}\nlibrary(pyramidi)\nlibrary(dplyr)\nmidi_file_string \u003c- system.file(\"extdata\", \"test_midi_file.mid\", package = \"pyramidi\")\nmfr \u003c- MidiFramer$new(midi_file_string)\n```\n\n\n\nThe object contains the midi data in various dataframe formats and an interface\nto the miditapyr\n[miditapyr.MidiFrames](https://miditapyr.readthedocs.io/en/latest/notebooks/midi_frame_usage.html)\nobject `mfr$mf`. You can write the midi file resulting of the `MidiFramer` object to disk:\n\n```{r, eval=FALSE}\nmfr$mf$write_file(\"/path/to/your/midifile.mid\")\n```\n\n\n### Modifying midi data\n\nIn the `MidiFramer` object, we can modify `mfr$df_notes_wide`, the notes in note-wise wide format\n(`note_on` \u0026 `note_off` events in the same line). Thus we don't need to worry which \nmidi events belong together\n\nLet's look at a small example. We'll define a function to replace every \nnote with a random midi note between 60 \u0026 71::\n\n```{r}\nmod \u003c- function(dfn, seed) {\n  n_notes \u003c- sum(!is.na(dfn$note))\n  dfn %\u003e% mutate(note = ifelse(\n    !is.na(note),\n    sample(60:71, n_notes, TRUE),\n    note\n  ))\n}\n```\n\n\nWhen we call the `update_notes_wide()` method, the midi data in `mfr` is updated:\n\n```{r, message=FALSE}\nmfr$update_notes_wide(mod)\n```\n\n\n### Writing modified midi files\n\nThus, we can now save the modifications to a midi file:\n\n```{r, eval=FALSE}\nmfr$mf$write_file(\"mod_test_midi_file.mid\")\n```\n\n### Synthesizing and playing audio\n\nSee the `vignette(\"pyramidi\", package = \"pyramidi\")` to see how you can\nsynthesize the midi data to wav, convert to mp3 if you want, and then \nembed a player in your rmarkdown html document with\n```{r, eval=FALSE}\nmfr$play(\"mod_test_midi_file.mp3\")\n```\n\n\n```{r echo=FALSE}\nhtmltools::tags$audio(\n    controls = \"\",\n    htmltools::tags$source(\n      src = \"https://urswilke.github.io/pyramidi/articles/mod_test_midi_file.mp3\",\n      type = \"audio/mp3\"\n    )\n  )\n```\n\n\n*The player only appears in the [docs](https://urswilke.github.io/pyramidi/index.html#synthesizing-and-playing-audio).\n\nEven if that sound is very weird, I was very happy not having to listen to the package midi file over and over again. :)\n\n\n## Documentation\n\nYou can find the complete online documentation of the package [here](https://urswilke.github.io/pyramidi/).\n\n* See the `vignette(\"pyramidi\")` for a brief usage introduction how to manipulate midi data.\n* The `vignette(\"compose\")` shows a more extended example how to compose music and generate midi files from scratch.\n* `vignette(\"package_workflow\")` shows in detail the structure of the `MidiFramer` class and the functions of the pyramidi package.\n* `vignette(\"functions_usage\")` illustrates the low-level functions of the pyramidi package, that `MidiFramer` objects use under the hood.\n\n## pyramidi out in the wild\n\nTo see examples where pyramidi is used for midi mangling in R dataframes etc. (amongst plenty other of his awesome writings about music),\nplease check out Matt Crump's [blog](https://homophony.quest/notes.html#category=midi)\nand his package [midiblender](https://www.crumplab.com/midiblender/articles/Getting_started.html)!\n\n## Related R packages\n\n* The [tabr](https://github.com/leonawicz/tabr) package is a massive music notation, transcription and analysis program allowing to create musical scores (using Lilypond). It allows to read midi files (wrapping tuneR; see below) and also to export them (also using Lilypond).\n* The [gm](https://github.com/flujoo/gm) package also allows to create and show musical scores using musescore. It also allows to export the music to audio (also using musescore) and to embed the players in html documents.\n* The [noon package](https://github.com/ColinFay/noon) wraps node.js libraries and can be used to read live midi input port data. I wrote a small [blog post](https://urssblogg.netlify.app/post/2020-10-24-live-recording-of-a-midi-controller-via-mido-inport/) how reading a midi port can also be done in R with [mido](https://mido.readthedocs.io/en/latest/ports.html). Interestingly, the node.js libraries and mido rely on a the same C++ library [RtMidi](http://www.music.mcgill.ca/~gary/rtmidi/index.html). \n* The [tuneR](https://cran.r-project.org/package=tuneR) package can also read in midi data. See the `vignette(\"tuner\")`, for an example how you can transform the tuner format into the pyramidi format. \n\n\n## R packages used\n\nThis package stands on the shoulders of giants. A big thank you to the authors of the following libraries!\n\n```{r, echo=FALSE, warning=FALSE}\npkgs \u003c- grateful::cite_packages(\n  output = \"table\",\n  out.dir = \".\", \n  omit = c(\"pyramidi\"),\n)\nknitr::kable(pkgs)\n```\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Furswilke%2Fpyramidi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Furswilke%2Fpyramidi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Furswilke%2Fpyramidi/lists"}