Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions documentation/source/testsAndInProgress/midi.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
"id": "f380fcf0-4e6a-4d90-a9c6-70b4fa398eea",
"metadata": {},
"source": [
"MIDI will only play from **Trusted** notebooks."
"MIDI will only reliably play from **Trusted** notebooks."
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 4,
"id": "72e204b7-409b-48ef-b404-a310d281b54c",
"metadata": {},
"outputs": [],
Expand All @@ -28,7 +28,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 5,
"id": "c9b77464-389f-4dba-9ac8-aef5bd85e2e1",
"metadata": {},
"outputs": [],
Expand All @@ -38,23 +38,23 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 6,
"id": "e0040c55-5bab-42f8-ad1c-284ea8170647",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" <div id=\"midiPlayerDiv98\">\n",
" <div id=\"midiPlayerDiv197\">\n",
" <p class=\"m21-trust-warning\">\n",
" This MIDI player only runs in <em>trusted</em> notebooks.\n",
" In JupyterLab: File &rarr; Trust Notebook, then re-run this cell.\n",
" </p>\n",
" </div>\n",
" <script>\n",
" (function() {\n",
" const outputId = \"midiPlayerDiv98\";\n",
" const outputId = \"midiPlayerDiv197\";\n",
" const b64 = \"TVRoZAAAAAYAAQACJ2BNVHJrAAAAFAD/UQMHoSAA/1gEBAIYCM5g/y8ATVRyawAAADIA/wMAAOAAQM5gkDxazmCAPAAAkD5azmCAPgAAkEBazmCAQAAAkEFazmCAQQDOYP8vAA==\";\n",
" const jsUrl = \"https://cdn.jsdelivr.net/npm/music21j@0.23.1/releases/music21.debug.min.js\";\n",
" const cssUrl = \"https://cdn.jsdelivr.net/npm/music21j@0.23.1/releases/music21j.min.css\";\n",
Expand Down Expand Up @@ -145,7 +145,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.3"
"version": "3.14.2"
}
},
"nbformat": 4,
Expand Down
4 changes: 2 additions & 2 deletions music21/alpha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
The alpha module/directory contains features that are useful for some things, but
not a core part of the system. Some of these modules may require external
libraries that are obscure and not part of the recommended "additional music21
features" set. (For instance, alpha.chant requires Gregorio and LaTeX -- a daunting
requirement)
features" set. (For instance, at one point alpha.chant required
Gregorio and LaTeX -- a daunting requirement)

This directory is a compromise between removing the features altogether and
putting them in the main documentation. Some of these features may "graduate"
Expand Down
19 changes: 16 additions & 3 deletions music21/common/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
'unique',
]

from functools import cache

if t.TYPE_CHECKING:
_T = t.TypeVar('_T')

Expand Down Expand Up @@ -209,22 +211,33 @@ def pitchList(pitchL):
return '[' + ', '.join([x.nameWithOctave for x in pitchL]) + ']'


@cache
def runningInNotebook() -> bool:
'''
return bool if we are running under Jupyter Notebook (not IPython terminal)
or Google Colabatory (colab).
or Google Colabatory (colab) or Marimo.

Methods based on:

https://stackoverflow.com/questions/15411967/how-can-i-check-if-code-is-executed-in-the-ipython-notebook

(No tests provided here, since results will differ depending on environment)
'''
if sys.stderr.__class__.__name__ == 'OutStream':
if runningInJupyterOrColab():
return True
elif runningInMarimo():
return True
else:
return False

def runningInJupyterOrColab() -> bool:
return sys.stderr.__class__.__name__ == 'OutStream'

def runningInMarimo() -> bool:
if 'marimo' not in sys.modules: # only True if it's been loaded already
return False
# so this is very fast, even if False.
return __import__('marimo').running_in_notebook()


# ----------------------------
# match collections, defaultdict()
Expand Down
7 changes: 4 additions & 3 deletions music21/converter/subConverters.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def show(
app=None,
subformats=(),
**keywords
) -> None:
) -> t.Any|None:
'''
Write the data, then show the generated data, using `.launch()` or printing
to a console.
Expand Down Expand Up @@ -355,7 +355,7 @@ class ConverterIPython(SubConverter):
registerOutputSubformatExtensions = {'lilypond': 'ly'}

def show(self, obj, fmt, app=None, subformats=(),
**keywords): # pragma: no cover
**keywords) -> t.Any: # pragma: no cover
'''
show a specialized for Jupyter Notebook using the appropriate subformat.

Expand Down Expand Up @@ -387,10 +387,11 @@ def show(self, obj, fmt, app=None, subformats=(),
subformats=helperSubformats,
**keywords,
)
return None
elif helperFormat == 'midi':
if t.TYPE_CHECKING:
assert isinstance(helperSubConverter, ConverterMidi)
ip21_converters.displayMusic21jMIDI(
return ip21_converters.displayMusic21jMIDI(
obj,
subConverter=helperSubConverter,
fmt=helperFormat,
Expand Down
32 changes: 25 additions & 7 deletions music21/ipython21/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import typing as t

from music21 import common
from music21.common.misc import runningInJupyterOrColab, runningInMarimo
from music21.converter import museScore
from music21 import defaults
from music21 import environment
Expand Down Expand Up @@ -128,10 +129,7 @@ def displayMusic21jMIDI(
fmt: str = 'midi',
subformats: Sequence[str] = (),
**keywords,
):
# noinspection PyPackageRequirements
from IPython.display import display, HTML # type: ignore

) -> t.Any:
fp = subConverter.write(
obj,
fmt,
Expand All @@ -141,14 +139,34 @@ def displayMusic21jMIDI(
)

with open(fp, 'rb') as f:
binaryMidiData = f.read()
binaryMidiData: bytes = f.read()

htmlOutput = htmlOutputForMidi(binaryMidiData)
displayMusic21jMIDIMarimo(htmlOutput)

if runningInJupyterOrColab():
displayMusic21jMIDIJupyter(htmlOutput)
elif runningInMarimo():
return displayMusic21jMIDIMarimo(htmlOutput)
else:
raise ValueError('Cannot run displayMusic21jMIDI if not running in a supported Notebook')

def displayMusic21jMIDIJupyter(htmlOutput: str) -> None:
from IPython.display import display, HTML
display(HTML(htmlOutput))

def displayMusic21jMIDIMarimo(htmlOutput: str) -> t.Any:
# noinspection PyPackageRequirements
from marimo import iframe # type: ignore # pylint: disable=import-error
return iframe(htmlOutput, width='100%', height='80px')

def htmlOutputForMidi(binaryMidiData: bytes) -> str:
binaryBase64 = base64.b64encode(binaryMidiData)
s = common.SingletonCounter()
outputId = 'midiPlayerDiv' + str(s())
utf_binary: str = binaryBase64.decode('utf-8')
# noinspection PyTypeChecker
display(HTML('''
return '''
<div id="''' + outputId + '''">
<p class="m21-trust-warning">
This MIDI player only runs in <em>trusted</em> notebooks.
Expand Down Expand Up @@ -209,7 +227,7 @@ def displayMusic21jMIDI(
}
script.addEventListener("load", function() { play(window.music21); });
})();
</script>'''))
</script>'''


# def vfshow(self, s):
Expand Down
Loading