def m21JupyterHook(enable=True) -> None: """ Set an ipython-hook to display music21 objects inline on the ipython notebook """ if not misc.inside_jupyter(): logger.debug("m21JupyterHook: not inside ipython/jupyter, skipping") return from IPython.core.getipython import get_ipython from IPython.core import display from IPython.display import Image, display ip = get_ipython() formatter = ip.display_formatter.formatters['image/png'] if enable: def showm21(stream: m21.stream.Stream): fmt = config['m21.displayhook.format'] filename = str(stream.write(fmt)) return display(Image(filename=filename)) # return display.Image(filename=filename)._repr_png_() dpi = formatter.for_type(m21.Music21Object, showm21) return dpi else: logger.debug("disabling display hook") formatter.for_type(m21.Music21Object, None)
def setJupyterHookForClass(cls, func, fmt='image/png'): """ Register func as a displayhook for class `cls` """ if not misc.inside_jupyter(): logger.debug("_setJupyterHookForClass: not inside IPython/jupyter, skipping") return import IPython ip = IPython.get_ipython() formatter = ip.display_formatter.formatters[fmt] return formatter.for_type(cls, func)
def jupyterShowImage(path: str): """ Show an image inside (inline) of a jupyter notebook Args: path: the path to the image file """ if not misc.inside_jupyter(): logger.error("jupyter is not available") return img = jupyterMakeImage(path) return jupyterDisplay(img)
def pngShow(pngpath:str, forceExternal=False, app:str='') -> None: """ Show a png either inside jupyter or with an external app Args: pngpath: the path to a png file forceExternal: if True, it will show in an external app even inside jupyter. Otherwise it will show inside an external app if running a normal session and show an embedded image if running inside a notebook """ if misc.inside_jupyter() and not forceExternal: jupyterShowImage(pngpath) else: environment.viewPng(pngpath, app=app)
def jupyterMakeImage(path: str) -> JupyterImage: """ Makes a jupyter Image, which can be displayed inline inside a notebook Args: path: the path to the image file Returns: an IPython.core.display.Image """ if not misc.inside_jupyter(): raise RuntimeError("Not inside a Jupyter session") scalefactor = config.get('show.scalefactor', 1.0) if scalefactor != 1.0: imgwidth, imgheight = imgSize(path) width = imgwidth*scalefactor else: width = None return JupyterImage(filename=path, embed=True, width=width)
:return: the string to be shown alongside the notated pitch """ # cents can be also negative (see self.cents) pivot = int(round(100 / divsPerSemitone)) dist = min(centsdev%pivot, -centsdev%pivot) if dist <= 2: return "" if centsdev < 0: # NB: this is not a normal - sign! We do this to avoid it being confused # with a syllable separator during rendering (this is currently the case # in musescore return f"–{-centsdev}" return str(int(centsdev)) if misc.inside_jupyter(): from IPython.core.display import (display as jupyterDisplay, Image as JupyterImage) def setJupyterHookForClass(cls, func, fmt='image/png'): """ Register func as a displayhook for class `cls` """ if not misc.inside_jupyter(): logger.debug("_setJupyterHookForClass: not inside IPython/jupyter, skipping") return import IPython ip = IPython.get_ipython() formatter = ip.display_formatter.formatters[fmt] return formatter.for_type(cls, func)
import sys import shutil from typing import List, Optional as Opt from emlib import misc import logging import music21 as m21 import appdirs insideJupyter = misc.inside_jupyter() logger = logging.getLogger("maelzel") def hasBinary(binary: str) -> bool: if shutil.which(binary): return True return False def defaultImageViewer() -> Opt[str]: """ Returns a command string or None if no default was found. For that case, use emlib.misc.open_with_standard_app """ if sys.platform == 'linux': if hasBinary('feh'): return 'feh --image-bg white' elif hasBinary('imv'): return 'imv -b "#ffffff"' return None
def _get_jupyter_display(): if not misc.inside_jupyter(): return None from IPython.display import display return display
def difftone_sources(result: pitch_t, maxdist=0.5, gap=13.0, minnote=None, maxnote="C8", intervals=None, resolution=1, display=False) -> RecordList: """ find two notes which produce a difference tone near the given note result: the resulting difference tone, as Note, notename or midinote maxdist: the maximum distance between the expected result and the generated tone, in semitones (0.5 == 50 cents) gap: the distance between the resulting difference tone and the lowest of the two originating notes intervals: accepted intervals between the notes. If None is given, a set of intervals based on resolution is used resolution: the resolution of the pitch grid, in semitones. A resolution of 1 will search for the given difftone along all semitones between minnote and maxnote Returns a List of Difftones """ midiresult = asmidi(result) maxnote = asmidi(maxnote) minnote = minnote or midiresult + gap if minnote is None: minnote = midiresult + gap else: minnote = max(minnote, midiresult + gap) assert maxnote > minnote intervals = intervals or list(frange(resolution, 7, resolution)) pairs = _difftone_find_source(result, maxdist, minnote=minnote, maxnote=maxnote, intervals=intervals, resolution=resolution) difftones = [ Difftone(note0=Note(midi0), note1=Note(midi1), desired=Note(midiresult)) for midi0, midi1 in pairs ] reclist = RecordList(difftones, fields=Difftone._fields) if display: # ipython? chordseq = EventSeq( [Chord(diff.note0, diff.note1, diff.diff) for diff in difftones]) if misc.inside_jupyter(): chordseq.show(split=midiresult + 5) disp = _get_jupyter_display() disp(reclist) else: chordseq.show() print(reclist) return reclist