Skip to content

Commit

Permalink
More miscellany (#993) (JTW)
Browse files Browse the repository at this point in the history
* Use pathlib in Environment.__setitem__

* Module names

* Fix compileall command

* Typo in ch. 53

* Update test coverage

* Fix bug with intermittent metadata writing
Regression in 0c47a64.

* Enable providing path to `saveFile` arg in `runTranscribe()`
Paths are truthy, so we executed `if saveFile` and ignored the input.

* Better failure in audioSearch module if no pyaudio
ImportError is better than NoneType has no attribute...

* Remove unused EnumerationException

* Prevent crash in exporting MIDI from having MetronomeMark without a number.

* Reduce indentation in midiTrackToStream()

* Don't export invalid StringIndications
Number must be > 0

* Remove check for hasattr 'software'

* Remove moot TODO
Has been working since at least 5.1.0

* Add VS Code config files to .gitignore

* Avoid some calls to hyphenToCamelCase()

* Restore `- minPitch` for future safety

* Clarify return of None in new doctest

* README typo

* Correct name is `rebuildMetadataCache()`

* Revert "Reduce indentation in midiTrackToStream()"

This reverts commit 6ff0377.

* Revert "Use pathlib in Environment.__setitem__"

This reverts commit 3ae8289.

* Spelling
  • Loading branch information
jacobtylerwalls committed May 5, 2021
1 parent 557680a commit 7fec3f5
Show file tree
Hide file tree
Showing 19 changed files with 56 additions and 44 deletions.
5 changes: 2 additions & 3 deletions .github/scripts/install_ubuntu_deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -e # error if anything returns non-zero exit code
mkdir ~/Desktop

sudo apt-get install -y libpng-dev
# sudo apt-get install -y python-qt4
# sudo apt-get install -y python3-pyqt5
wget -q https://lilypond.org/download/binaries/linux-64/lilypond-2.22.0-1.linux-64.sh
sh lilypond-2.22.0-1.linux-64.sh --batch
export PATH=/home/runner/bin:$PATH
Expand All @@ -15,5 +15,4 @@ pip3 install scipy
pip3 install python-Levenshtein
pip3 install setuptools
pip3 install coverage
cd music21
python -m compileall music21
python3 -m compileall music21
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ nosetests.xml
.pydevproject
/.settings

# VS Code
/.vscode

# remove all from autogenerated except conf.py
documentation/autogenerated/**/*
documentation/autogenerated/**/*.png
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ http://web.mit.edu/music21 or http://music21.readthedocs.org/en/latest/index.htm
And to install, see:
http://web.mit.edu/music21/doc/usersGuide/usersGuide_01_installing.html

Music21 runs on Python 3.6+. User version 4 on Python 2 or Py3.4, version 5
Music21 runs on Python 3.6+. Use version 4 on Python 2 or Py3.4, version 5
on Py3.5.

Released under the BSD (3-clause) license. Music21 may also be used
Expand Down
2 changes: 1 addition & 1 deletion documentation/source/developerReference/testing.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@
"\n",
" https://coveralls.io/github/cuthbertLab/music21\n",
"\n",
"While 100% code coverage is impossible, increasing the coverage percentage with each new contribution is essential. We're at 91% code coverage and get the green badge of pride on our README.md file, and we like that!\n",
"While 100% code coverage is impossible, increasing the coverage percentage with each new contribution is essential. We're at 92% code coverage and get the green badge of pride on our README.md file, and we like that!\n",
"\n",
"If something should be impossible to trigger, but the code is being maintained for some reason, you can add \"`# pragma: no cover`\" to the line or to the fork, but this is generally a no-no."
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Again, unfortuantely, some MusicXML readers do not respect this value.\n",
"Again, unfortunately, some MusicXML readers do not respect this value.\n",
"\n",
"MusicXML distinguishes articulations from technical indicates, but in `music21` these are all in the `articulations` module and stored in the `.articulations` list. They do, however, have `TechnicalIndication` in their classes: "
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Consult the API for class:`~music21.metadata.bundles.MetadataBundle` for a more\n",
"Consult the API for :class:`~music21.metadata.bundles.MetadataBundle` for a more\n",
"in depth look at how this works."
]
},
Expand Down Expand Up @@ -693,7 +693,7 @@
"metadata": {},
"source": [
"You can delete, rebuild and save a metadata bundle in one go with the\n",
"``rebuild()`` method:"
"``rebuildMetadataCache()`` method:"
]
},
{
Expand All @@ -703,7 +703,7 @@
"outputs": [],
"source": [
"localBundle = corpus.corpora.LocalCorpus().metadataBundle\n",
"#_DOCS_SHOW localBundle.rebuild()"
"#_DOCS_SHOW localBundle.rebuildMetadataCache()"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion music21/analysis/discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ def _generateColors(self, numColors=None):
else: # create minPitch maxPitch
maxPitch = numColors

valueRange = maxPitch
valueRange = maxPitch - minPitch
if valueRange == 0:
valueRange = 1 # avoid float division by zero
step = 0
Expand Down
11 changes: 3 additions & 8 deletions music21/audioSearch/recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ def samplesFromRecording(seconds=10.0, storeFile=True,
Returns a list of samples.
'''
try:
# noinspection PyPackageRequirements
import pyaudio # @UnresolvedImport
recordFormatDefault = pyaudio.paInt16
except (ImportError, SystemExit):
pyaudio = None
environLocal.warn("No Pyaudio found. Recording will probably not work.")
recordFormatDefault = 8 # pyaudio.paInt16
# noinspection PyPackageRequirements
import pyaudio # @UnresolvedImport # pylint: disable=import-error
recordFormatDefault = pyaudio.paInt16

if recordFormat is None:
recordFormat = recordFormatDefault
Expand Down
11 changes: 5 additions & 6 deletions music21/audioSearch/transcriber.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,12 @@ def runTranscribe(show=True, plot=True, useMic=True,
if useScale is None:
useScale = scale.ChromaticScale('C4')
# beginning - recording or not
if saveFile is not False:
if saveFile:
waveFilename = environLocal.getRootTempDir() / 'ex.wav'
else:
waveFilename = saveFile
else:
if saveFile is True:
waveFilename = environLocal.getRootTempDir() / 'ex.wav'
elif saveFile is False:
waveFilename = False
else:
waveFilename = saveFile

# the rest of the score
if useMic is True:
Expand Down
2 changes: 1 addition & 1 deletion music21/metadata/bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ def addFromPaths(
accumulatedErrors.extend(result['errors'])
for metadataEntry in result['metadataEntries']:
self._metadataEntries[metadataEntry.corpusPath] = metadataEntry
if (currentIteration % 50) and (storeOnDisk is True) == 0:
if (currentIteration % 50 == 0) and storeOnDisk is True:
self.write()
self.validate()
if storeOnDisk is True:
Expand Down
5 changes: 0 additions & 5 deletions music21/midi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
'DeltaTime',
'MetaEvents', 'ChannelVoiceMessages', 'ChannelModeMessages',
'SysExEvents',
'EnumerationException',
]

import io
Expand Down Expand Up @@ -59,10 +58,6 @@
# good midi reference:
# http://www.sonicspot.com/guide/midifiles.html
# ------------------------------------------------------------------------------
class EnumerationException(exceptions21.Music21Exception):
pass


class MidiException(exceptions21.Music21Exception):
pass

Expand Down
8 changes: 8 additions & 0 deletions music21/midi/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -1064,8 +1064,16 @@ def tempoToMidiEvents(tempoIndication, includeDeltaTime=True):
>>> midi.translate.midiEventsToTempo(events)
<music21.tempo.MetronomeMark maestoso Quarter=90.0>
`None` is returned if the MetronomeMark lacks a number, which can
happen with metric modulation marks.
>>> midi.translate.tempoToMidiEvents(tempo.MetronomeMark(number=None)) is None
True
'''
from music21 import midi as midiModule
if tempoIndication.number is None:
return
mt = None # use a midi track set to None
eventList = []
if includeDeltaTime:
Expand Down
25 changes: 19 additions & 6 deletions music21/musicxml/m21ToXml.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ def setColor(self, mxObject, m21Object):
'''
Sets mxObject['color'] to a normalized version of m21Object.style.color
'''
self.setStyleAttributes(mxObject, m21Object, 'color')
self.setStyleAttributes(mxObject, m21Object, 'color', 'color')
if 'color' in mxObject.attrib: # set
mxObject.attrib['color'] = normalizeColor(mxObject.attrib['color'])

Expand Down Expand Up @@ -2239,8 +2239,7 @@ def setEncoding(self):
mxEncodingDate.text = str(datetime.date.today()) # right format...
# TODO: encoder

if self.scoreMetadata is not None and hasattr(self.scoreMetadata, 'software'):
# TODO: Remove hasattr after all caches are cleared after v5 release.
if self.scoreMetadata is not None:
for software in self.scoreMetadata.software:
mxSoftware = SubElement(mxEncoding, 'software')
mxSoftware.text = software
Expand Down Expand Up @@ -2796,8 +2795,6 @@ def parseFlatElements(self, m, *, backupAfterwards=False):
Note that if the .id is high enough to be an id(x) memory location, then a small
voice number is used instead.
'''
# TODO: fix mid-measure clef change with voices and part-staff in Schoenberg op. 19 no 2
# staff 2, m. 6... -- placed at the beginning of the measure not at the appropriate place.
root = self.xmlRoot
divisions = self.currentDivisions
self.offsetInMeasure = 0.0
Expand Down Expand Up @@ -4059,6 +4056,8 @@ def noteToNotations(self, n, noteIndexInChord=0, chordParent=None):
for artObj in applicableArticulations:
if 'Pizzicato' in artObj.classes:
continue
if 'StringIndication' in artObj.classes and artObj.number < 1:
continue
if 'TechnicalIndication' in artObj.classes:
if mxTechnicalMark is None:
mxTechnicalMark = Element('technical')
Expand Down Expand Up @@ -5500,7 +5499,7 @@ def beamToXml(self, beamObject):

# not to be done: repeater (deprecated)
self.setColor(mxBeam, beamObject)
self.setStyleAttributes(mxBeam, beamObject, 'fan')
self.setStyleAttributes(mxBeam, beamObject, 'fan', 'fan')

return mxBeam

Expand Down Expand Up @@ -6509,6 +6508,20 @@ def testFullMeasureRest(self):
rest = tree.find('.//rest')
self.assertEqual(rest.get('measure'), 'yes')

def testArticulationSpecialCases(self):
from music21 import articulations

n = note.Note()
a = articulations.StringIndication()
n.articulations.append(a)

# Legal values for StringIndication begin at 1
self.assertEqual(a.number, 0)
# Use GEX to go through wellformed object conversion
gex = GeneralObjectExporter(n)
tree = ET.fromstring(gex.parse().decode('utf-8'))
self.assertIsNone(tree.find('.//string'))

def testMeasurePadding(self):
from music21 import converter
s = stream.Score([converter.parse('tinyNotation: 4/4 c4')])
Expand Down
4 changes: 2 additions & 2 deletions music21/musicxml/xmlToM21.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ def setColor(self, mxObject, m21Object):
'''
Sets m21Object.style.color to be the same as color...
'''
self.setStyleAttributes(mxObject, m21Object, 'color')
self.setStyleAttributes(mxObject, m21Object, 'color', 'color')

def setFont(self, mxObject, m21Object):
'''
Expand Down Expand Up @@ -2804,7 +2804,7 @@ def xmlToBeam(self, mxBeam, inputM21=None):
# TODO: get number to preserve
# not to-do: repeater; is deprecated.
self.setColor(mxBeam, beamOut)
self.setStyleAttributes(mxBeam, beamOut, 'fan')
self.setStyleAttributes(mxBeam, beamOut, 'fan', 'fan')

try:
mxType = mxBeam.text.strip()
Expand Down
4 changes: 2 additions & 2 deletions music21/stream/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Name: stream/__init__.py
# Name: stream/base.py
# Purpose: base classes for dealing with groups of positioned objects
#
# Authors: Michael Scott Cuthbert
Expand Down Expand Up @@ -7719,7 +7719,7 @@ def containerInHierarchy(self, el, *, setActiveSite=True) -> Optional['music21.s
if s1.containerInHierarchy(n) is called, it will return m1,
the Measure that contains the note.

Unless `setActiveSite` is False, n's activeStie will be set to m1, and
Unless `setActiveSite` is False, n's activeSite will be set to m1, and
its `.offset` will be the offset in `m1`.

>>> s1 = stream.Score(id='s1')
Expand Down
2 changes: 1 addition & 1 deletion music21/stream/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ def coreGatherMissingSpanners(
that spannerBundle are added. This can be useful, for instance, in restoring
spanners from an excerpt that might already have spanners removed. In
Jacob Tyler Walls's brilliant phrasing, it prevents regrowing zombie spanners
the you thought you had killed.
that you thought you had killed.
Here we will constrain only to spanners also present in another Stream:
Expand Down
2 changes: 1 addition & 1 deletion music21/stream/filters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Name: stream/filter.py
# Name: stream/filters.py
# Purpose: classes for filtering iterators of streams...
#
# Authors: Michael Scott Cuthbert
Expand Down
2 changes: 1 addition & 1 deletion music21/stream/iterator.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ def matchingElements(self, *, restoreActiveSites: bool = True):
<music21.note.Note E>, <music21.note.Note F>, <music21.note.Note G>,
<music21.note.Note A>]
If restoreActiveSties is False then the elements will not have
If restoreActiveSites is False then the elements will not have
their activeSites changed (callers should use it when they do not plan to actually
expose the elements to users, such as in `__len__`).
Expand Down
2 changes: 1 addition & 1 deletion music21/vexflow/toMusic21j.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# Name: vexflow/toM21p.py
# Name: vexflow/toM21j.py
# Purpose: music21 classes for converting music21 objects to music21j
#
# Authors: Michael Scott Cuthbert
Expand Down

0 comments on commit 7fec3f5

Please sign in to comment.