Source code for music21.common.formats

# ------------------------------------------------------------------------------
# Name:         common/formats.py
# Purpose:      Utilities for formats
#
# Authors:      Michael Scott Asato Cuthbert
#               Christopher Ariza
#
# Copyright:    Copyright © 2009-2015 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# ------------------------------------------------------------------------------
'''
Utilities for working with file formats.

almost everything here is deprecated.
'''
from __future__ import annotations

__all__ = [
    'findSubConverterForFormat',
    'findFormat',
    'findInputExtension',
    'findFormatFile',
    'findFormatExtFile',
    'findFormatExtURL',
    'VALID_SHOW_FORMATS',
    'VALID_WRITE_FORMATS',
    'VALID_AUTO_DOWNLOAD',
]

from functools import cache
import pathlib
import typing as t


if t.TYPE_CHECKING:
    from music21.converter.subConverters import SubConverter


# used for checking preferences, and for setting environment variables
# TODO: only check top-level.  Let subConverters check sub formats.
VALID_SHOW_FORMATS = ['musicxml', 'lilypond', 'text', 'textline', 'midi',
                      'png', 'pdf', 'svg',
                      'lily.pdf', 'lily.png', 'lily.svg', 'braille',
                      'ipython', 'ipython.png', 'ipython.midi',
                      'musicxml.png', 'musicxml.pdf']
VALID_WRITE_FORMATS = ['musicxml', 'lilypond', 'text', 'textline', 'midi',
                       'png', 'pdf', 'svg',
                       'lily.pdf', 'lily.png', 'lily.svg', 'braille',
                       'ipython', 'ipython.png', 'ipython.midi',
                       'musicxml.png', 'musicxml.pdf',
                       'rntxt']
VALID_AUTO_DOWNLOAD = ['ask', 'deny', 'allow']

# ------------------------------------------------------------------------------


[docs] def findSubConverterForFormat(fmt: str) -> type[SubConverter]|None: ''' return a converter.subConverter.SubConverter subclass for a given format -- this is a music21 format name, not a file extension. Or returns None >>> common.findSubConverterForFormat('musicxml') <class 'music21.converter.subConverters.ConverterMusicXML'> >>> common.findSubConverterForFormat('text') <class 'music21.converter.subConverters.ConverterText'> >>> common.findSubConverterForFormat('romantext') <class 'music21.converter.subConverters.ConverterRomanText'> Some subConverters have format aliases >>> common.findSubConverterForFormat('t') <class 'music21.converter.subConverters.ConverterText'> ''' fmt = fmt.lower().strip() from music21 import converter scl = converter.Converter().subConvertersList() for sc in scl: formats = sc.registerFormats if fmt in formats: return sc return None
# @deprecated('May 2014', '[soonest possible]', 'Moved to converter')
[docs] def findFormat(fmt): ''' Given a format defined either by a format name, abbreviation, or an extension, return the regularized format name as well as the output extensions. DEPRECATED May 2014 -- moving to converter All but the first element of the tuple are deprecated for use, since the extension can vary by subConverter (e.g., lily.png) >>> common.findFormat('.mxl') ('musicxml', '.musicxml') >>> common.findFormat('musicxml') ('musicxml', '.musicxml') >>> common.findFormat('lily') ('lilypond', '.ly') >>> common.findFormat('lily.png') ('lilypond', '.ly') >>> common.findFormat('humdrum') ('humdrum', '.krn') >>> common.findFormat('txt') ('text', '.txt') >>> common.findFormat('textline') ('textline', '.txt') >>> common.findFormat('midi') ('midi', '.mid') >>> common.findFormat('abc') ('abc', '.abc') >>> common.findFormat('scl') ('scala', '.scl') >>> common.findFormat('braille') ('braille', '.txt') >>> common.findFormat('capx') ('capella', '.capx') Works the same whether you have a leading dot or not: >>> common.findFormat('md') ('musedata', '.md') >>> common.findFormat('.md') ('musedata', '.md') If you give something we can't deal with, returns a Tuple of None, None: >>> common.findFormat('wpd') (None, None) ''' # These don't work but should eventually: # # # >>> common.findFormat('png') # # ('musicxml.png', '.png') # # # >>> common.findFormat('ipython') # # ('ipython', '.png') # # >>> common.findFormat('ipython.png') # # ('ipython', '.png') # # # >>> common.findFormat('musicxml.png') # # ('musicxml.png', '.png') from music21 import converter c = converter.Converter() fileFormat = c.regularizeFormat(fmt) if fileFormat is None: return (None, None) scf = c.getSubConverterFormats() sc = scf[fileFormat] if sc.registerOutputExtensions: firstOutput = '.' + sc.registerOutputExtensions[0] elif sc.registerInputExtensions: firstOutput = '.' + sc.registerInputExtensions[0] else: firstOutput = None return fileFormat, firstOutput
# @deprecated('May 2014', '[soonest possible]', 'Moved to converter') @cache def findInputExtension(fmt: str) -> tuple[str, ...]: ''' Will be fully deprecated when there's an exact equivalent in converter. Given an input format or music21 format, find and return all possible input extensions. >>> a = common.findInputExtension('musicxml') >>> a ('.xml', '.mxl', '.musicxml') >>> a = common.findInputExtension('humdrum') >>> a ('.krn',) >>> common.findInputExtension('musedata') ('.md', '.musedata', '.zip') Leading dots don't matter: >>> common.findInputExtension('.mxl') ('.xml', '.mxl', '.musicxml') Blah is not a format >>> common.findInputExtension('blah') () ''' from music21 import converter fmt = fmt.lower().strip() if fmt.startswith('.'): fmt = fmt[1:] # strip . sc = findSubConverterForFormat(fmt) if sc is None: # file extension post: list[str] = [] for sc in converter.Converter().subConvertersList(): if fmt not in sc.registerInputExtensions: continue for ext in sc.registerInputExtensions: if not ext.startswith('.'): ext = '.' + ext post.append(ext) if post: return tuple(post) return tuple(post) # empty else: # music21 format post = [] for ext in sc.registerInputExtensions: if not ext.startswith('.'): ext = '.' + ext post.append(ext) return tuple(post) # @deprecated('May 2014', '[soonest possible]', 'Moved to converter')
[docs] def findFormatFile(fp): r''' Given a file path (relative or absolute) return the format DEPRECATED May 2014 -- moving to converter >>> common.findFormatFile('test.xml') 'musicxml' >>> common.findFormatFile('long/file/path/test-2009.03.02.xml') 'musicxml' >>> common.findFormatFile('long/file/path.intermediate.png/test-2009.03.xml') 'musicxml' On a windows networked filesystem >>> common.findFormatFile(r'\\long\file\path\test.krn') 'humdrum' ''' if not isinstance(fp, pathlib.Path): fp = pathlib.Path(fp) fmt, unused_ext = findFormat(fp.suffix) return fmt # may be None if no match
# @deprecated('May 2014', '[soonest possible]', 'Moved to converter')
[docs] def findFormatExtFile(fp): r''' Given a file path (relative or absolute) find format and extension used (not the output extension) DEPRECATED May 2014 -- moving to converter >>> common.findFormatExtFile('test.mxl') ('musicxml', '.mxl') >>> common.findFormatExtFile('long/file/path/test-2009.03.02.xml') ('musicxml', '.xml') >>> common.findFormatExtFile('long/file/path.intermediate.png/test-2009.03.xml') ('musicxml', '.xml') >>> common.findFormatExtFile('test') (None, None) Windows drive >>> common.findFormatExtFile('d:/long/file/path/test.xml') ('musicxml', '.xml') On a windows networked filesystem >>> common.findFormatExtFile(r'\\long\file\path\test.krn') ('humdrum', '.krn') ''' if not isinstance(fp, pathlib.Path): fp = pathlib.Path(fp) fileFormat, unused_extOut = findFormat(fp.suffix) if fileFormat is None: return (None, None) else: return (fileFormat, fp.suffix) # may be None if no match
# @deprecated('May 2014', '[soonest possible]', 'Moved to converter')
[docs] def findFormatExtURL(url): ''' Given a URL, attempt to find the extension. This may scrub arguments in a URL, or simply look at the last characters. DEPRECATED May 2014 -- moving to converter >>> urlA = 'http://somesite.com/?l=cc/schubert/piano/d0576&file=d0576-06.krn&f=xml' >>> urlB = 'http://somesite.com/cgi-bin/ksdata?l=cc/schubert/d0576&file=d0576-06.krn&f=kern' >>> urlC = 'http://somesite.com/cgi-bin/ksdata?l=cc/bach/cello&file=bwv1007-01.krn&f=xml' >>> urlF = 'https://junk' >>> urlM = 'http://somesite.com/files/mid001.mid' >>> common.findFormatExtURL(urlA) ('musicxml', '.xml') >>> common.findFormatExtURL(urlB) ('humdrum', '.krn') >>> common.findFormatExtURL(urlC) ('musicxml', '.xml') >>> common.findFormatExtURL(urlF) (None, None) >>> common.findFormatExtURL(urlM) ('midi', '.mid') ''' from music21 import converter ext = None # first, look for cgi arguments if '=xml' in url: ext = '.xml' elif '=kern' in url: ext = '.krn' # specific tag used on musedata.org elif 'format=stage2' in url or 'format=stage1' in url: ext = '.md' else: # check for file that ends in all known input extensions for sc in converter.Converter().subConvertersList(): inputTypes = sc.registerInputExtensions for extSample in inputTypes: if url.endswith('.' + extSample): ext = '.' + extSample break # presently, not keeping the extension returned from this function # reason: mxl is converted to xml; need to handle mxl files first if ext is not None: fileFormat, unused_junk = findFormat(ext) return fileFormat, ext else: return None, None
if __name__ == '__main__': import music21 music21.mainTest()