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
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Whoops! The last F of measure 2 is actually too long, and the next measure's first F begins half a beat later. Let's run the powerful command :meth:`~music21.stream.Stream.makeNotation` first before showing:"
"Whoops! The last F of measure 2 is actually too long, and the next measure's first F begins half a beat later. Let's run the powerful command :meth:`~music21.stream.base.Stream.makeNotation` first before showing:"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion documentation/source/usersGuide/usersGuide_31_clefs.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2226,7 +2226,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This is accomplished because before showing the Stream, `music21` runs the powerful method :meth:`~music21.stream.Stream.makeNotation` on the stream. This calls a function in :ref:`moduleStreamMakeNotation` module called :func:`~music21.stream.makeNotation.makeBeams` that does the real work. That function checks the stream to see if any beams exist on it:"
"This is accomplished because before showing the Stream, `music21` runs the powerful method :meth:`~music21.stream.base.Stream.makeNotation` on the stream. This calls a function in :ref:`moduleStreamMakeNotation` module called :func:`~music21.stream.makeNotation.makeBeams` that does the real work. That function checks the stream to see if any beams exist on it:"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion music21/audioSearch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ def notesAndDurationsToStream(

returns a :class:`~music21.stream.Score` object, containing
a metadata object and a single :class:`~music21.stream.Part` object, which in turn
contains the notes, etc. Does not run :meth:`~music21.stream.Stream.makeNotation`
contains the notes, etc. Does not run :meth:`~music21.stream.base.Stream.makeNotation`
on the Score.


Expand Down
11 changes: 11 additions & 0 deletions music21/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,11 @@ def write(self, fmt=None, fp=None, **keywords): # pragma: no cover
be used. For most people that is musicxml.

Returns the full path to the file.

Some formats, including .musicxml, create a copy of the stream, pack it into a well-formed
score if necessary, and run :meth:`~music21.stream.Score.makeNotation`. To
avoid this when writing .musicxml, use `makeNotation=False`, an advanced option
that prioritizes speed but may not guarantee satisfactory notation.
'''
if fmt is None: # get setting in environment
fmt = environLocal['writeFormat']
Expand Down Expand Up @@ -2605,6 +2610,7 @@ def show(self, fmt=None, app=None, **keywords): # pragma: no cover
fmt argument or, if not provided, the format set in the user's Environment

Valid formats include (but are not limited to)::

musicxml
text
midi
Expand All @@ -2618,6 +2624,11 @@ def show(self, fmt=None, app=None, **keywords): # pragma: no cover

N.B. score.write('lily') returns a bare lilypond file,
score.show('lily') runs it through lilypond and displays it as a png.

Some formats, including .musicxml, create a copy of the stream, pack it into a well-formed
score if necessary, and run :meth:`~music21.stream.Score.makeNotation`. To
avoid this when showing .musicxml, use `makeNotation=False`, an advanced option
that prioritizes speed but may not guarantee satisfactory notation.
'''
# note that all formats here must be defined in
# common.VALID_SHOW_FORMATS
Expand Down
4 changes: 2 additions & 2 deletions music21/braille/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
Keywords:


* **inPlace** (False): If False, then :meth:`~music21.stream.Stream.makeNotation` is called
* **inPlace** (False): If False, then :meth:`~music21.stream.base.Stream.makeNotation` is called
on all :class:`~music21.stream.Measure`, :class:`~music21.stream.Part`, and
:class:`~music21.stream.PartStaff` instances. Copies of those objects are then
used to transcribe the music. If True, the transcription is done "as is."
This is useful for strict transcription because
sometimes :meth:`~music21.stream.Stream.makeNotation`
sometimes :meth:`~music21.stream.base.Stream.makeNotation`
introduces some unwanted artifacts in the music. However, the music needs
to be organized into measures for transcription to work.
* **debug** (False): If True, a braille-english representation of the music is returned. Useful
Expand Down
61 changes: 57 additions & 4 deletions music21/converter/subConverters.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,6 @@ def parseFile(self, fp, number=None):
'''
Open Noteworthy data (as nwctxt) from a file path.

>>> import os #_DOCS_HIDE
>>> nwcTranslatePath = common.getSourceFilePath() / 'noteworthy' #_DOCS_HIDE
>>> filePath = nwcTranslatePath / 'Part_OWeisheit.nwctxt' #_DOCS_HIDE
>>> #_DOCS_SHOW paertPath = converter.parse('d:/desktop/arvo_part_o_weisheit.nwctxt')
Expand Down Expand Up @@ -1002,10 +1001,24 @@ def writeDataStream(self, fp, dataBytes: bytes) -> pathlib.Path: # pragma: no c

return fp

def write(self, obj, fmt, fp=None, subformats=None,
compress=False, **keywords): # pragma: no cover
def write(self,
obj,
fmt,
*,
fp=None,
subformats=None,
makeNotation=True,
compress=False,
**keywords): # pragma: no cover
'''
Write to a .musicxml file.

Set `makeNotation=False` to prevent fixing up the notation, and where possible,
to prevent making additional deepcopies. (This option cannot be used if `obj` is not a
:class:`~music21.stream.Score`.) `makeNotation=True` generally solves common notation
issues, whereas `makeNotation=False` is intended for advanced users facing
special cases where speed is a priority or making notation reverses user choices.

Set `compress=True` to immediately compress the output to a .mxl file.
'''
from music21.musicxml import archiveTools, m21ToXml
Expand All @@ -1019,8 +1032,10 @@ def write(self, obj, fmt, fp=None, subformats=None,
defaults.title = ''
defaults.author = ''

dataBytes: bytes = b''
generalExporter = m21ToXml.GeneralObjectExporter(obj)
dataBytes: bytes = generalExporter.parse()
generalExporter.makeNotation = makeNotation
dataBytes = generalExporter.parse()

writeDataStreamFp = fp
if fp is not None and subformats: # could be empty list
Expand Down Expand Up @@ -1494,6 +1509,44 @@ def testWriteMXL(self):
self.assertTrue(str(mxlPath).endswith('.mxl'))
os.remove(mxlPath)

def testWriteMusicXMLMakeNotation(self):
from music21 import converter
from music21 import note
from music21.musicxml.xmlObjects import MusicXMLExportException

m1 = stream.Measure(note.Note(quarterLength=5.0))
m2 = stream.Measure()
p = stream.Part([m1, m2])
s = stream.Score(p)

self.assertEqual(len(m1.notes), 1)
self.assertEqual(len(m2.notes), 0)

out1 = s.write(makeNotation=True)
# 4/4 will be assumed; quarter note will be moved to measure 2
roundtrip_back = converter.parse(out1)
self.assertEqual(
len(roundtrip_back.parts.first().getElementsByClass(stream.Measure)[0].notes), 1)
self.assertEqual(
len(roundtrip_back.parts.first().getElementsByClass(stream.Measure)[1].notes), 1)

out2 = s.write(makeNotation=False)
roundtrip_back = converter.parse(out2)
# 4/4 will not be assumed; quarter note will still be split out from 5.0QL
# but it will remain in measure 1
# and there will be no rests in measure 2
self.assertEqual(
len(roundtrip_back.parts.first().getElementsByClass(stream.Measure)[0].notes), 2)
self.assertEqual(
len(roundtrip_back.parts.first().getElementsByClass(stream.Measure)[1].notes), 0)

# makeNotation = False cannot be used on non-scores
with self.assertRaises(MusicXMLExportException):
p.write(makeNotation=False)

for out in (out1, out2):
os.remove(out)


class TestExternal(unittest.TestCase): # pragma: no cover

Expand Down
Loading