CreativePython 0.3.5__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. {creativepython-0.3.5/src/CreativePython.egg-info → creativepython-1.0.0}/PKG-INFO +2 -4
  2. {creativepython-0.3.5 → creativepython-1.0.0}/pyproject.toml +5 -7
  3. {creativepython-0.3.5 → creativepython-1.0.0}/src/CreativePython/__init__.py +1 -1
  4. creativepython-1.0.0/src/CreativePython/nevmuse/Java-Comparison-Tests/advMetricRunner.pythonSurvey.py +224 -0
  5. creativepython-1.0.0/src/CreativePython/nevmuse/Java-Comparison-Tests/compareMetrics_Java-Vs-Python.py +274 -0
  6. creativepython-1.0.0/src/CreativePython/nevmuse/RunMetrics.py +150 -0
  7. creativepython-1.0.0/src/CreativePython/nevmuse/Surveyor.py +105 -0
  8. creativepython-1.0.0/src/CreativePython/nevmuse/__init__.py +18 -0
  9. creativepython-1.0.0/src/CreativePython/nevmuse/data/Confidence.py +10 -0
  10. creativepython-1.0.0/src/CreativePython/nevmuse/data/Contig.py +63 -0
  11. creativepython-1.0.0/src/CreativePython/nevmuse/data/ExtendedNote.py +165 -0
  12. creativepython-1.0.0/src/CreativePython/nevmuse/data/Histogram.py +66 -0
  13. creativepython-1.0.0/src/CreativePython/nevmuse/data/Judgement.py +9 -0
  14. creativepython-1.0.0/src/CreativePython/nevmuse/data/Measurement.py +108 -0
  15. creativepython-1.0.0/src/CreativePython/nevmuse/data/PianoRoll.py +1509 -0
  16. creativepython-1.0.0/src/CreativePython/nevmuse/data/PianoRollOld.py +1441 -0
  17. creativepython-1.0.0/src/CreativePython/nevmuse/data/__init__.py +21 -0
  18. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_ExtendedNote.py +193 -0
  19. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_Histogram.py +90 -0
  20. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_Measurement.py +110 -0
  21. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_PianoRoll_assertions.py +463 -0
  22. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_PianoRoll_integration.py +189 -0
  23. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_PianoRoll_quantization.py +159 -0
  24. creativepython-1.0.0/src/CreativePython/nevmuse/data/test_PianoRoll_unit.py +326 -0
  25. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/Metric.py +306 -0
  26. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/ZipfMetrics.py +20 -0
  27. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/__init__.py +11 -0
  28. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ChordDensityMetric.py +76 -0
  29. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ChordDistanceMetric.py +87 -0
  30. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ChordMetric.py +74 -0
  31. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ChordNormalizedMetric.py +70 -0
  32. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ChromaticToneMetric.py +45 -0
  33. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ContourBasslineDurationMetric.py +47 -0
  34. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ContourBasslineDurationQuantizedMetric.py +47 -0
  35. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ContourBasslinePitchMetric.py +47 -0
  36. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ContourMelodyDurationMetric.py +47 -0
  37. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ContourMelodyDurationQuantizedMetric.py +47 -0
  38. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/ContourMelodyPitchMetric.py +47 -0
  39. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/DurationBigramMetric.py +58 -0
  40. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/DurationDistanceMetric.py +52 -0
  41. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/DurationMetric.py +45 -0
  42. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/DurationQuantizedBigramMetric.py +52 -0
  43. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/DurationQuantizedDistanceMetric.py +52 -0
  44. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/DurationQuantizedMetric.py +45 -0
  45. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/HarmonicBigramMetric.py +64 -0
  46. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/HarmonicConsonanceMetric.py +96 -0
  47. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/HarmonicIntervalMetric.py +58 -0
  48. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/MelodicBigramMetric.py +58 -0
  49. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/MelodicConsonanceMetric.py +83 -0
  50. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/MelodicIntervalMetric.py +51 -0
  51. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/PitchDistanceMetric.py +54 -0
  52. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/PitchDurationMetric.py +54 -0
  53. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/PitchDurationQuantizedMetric.py +53 -0
  54. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/PitchMetric.py +45 -0
  55. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/RestMetric.py +60 -0
  56. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/__init__.py +64 -0
  57. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_DurationMetric.py +69 -0
  58. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_Metrics_BasicIntervalsAndBigrams.py +126 -0
  59. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_Metrics_ChordsAndConsonance.py +127 -0
  60. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_Metrics_ContoursAndChromatic.py +104 -0
  61. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_Metrics_QuantizedDurationsAndDistances.py +108 -0
  62. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_PitchMetric.py +87 -0
  63. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/simple/test_RestMetric.py +86 -0
  64. creativepython-1.0.0/src/CreativePython/nevmuse/metrics/test_Metric.py +117 -0
  65. creativepython-1.0.0/src/CreativePython/nevmuse/utilities/CSVWriter.py +126 -0
  66. creativepython-1.0.0/src/CreativePython/nevmuse/utilities/PowerLawRandom.py +101 -0
  67. creativepython-1.0.0/src/CreativePython/nevmuse/utilities/__init__.py +11 -0
  68. creativepython-0.3.5/src/_notationRenderer.py → creativepython-1.0.0/src/CreativePython/notationRenderer.py +70 -20
  69. {creativepython-0.3.5 → creativepython-1.0.0/src/CreativePython.egg-info}/PKG-INFO +2 -4
  70. creativepython-1.0.0/src/CreativePython.egg-info/SOURCES.txt +90 -0
  71. {creativepython-0.3.5 → creativepython-1.0.0}/src/CreativePython.egg-info/requires.txt +1 -4
  72. {creativepython-0.3.5 → creativepython-1.0.0}/src/CreativePython.egg-info/top_level.txt +0 -2
  73. {creativepython-0.3.5 → creativepython-1.0.0}/src/gui.py +240 -82
  74. {creativepython-0.3.5 → creativepython-1.0.0}/src/image.py +0 -1
  75. {creativepython-0.3.5 → creativepython-1.0.0}/src/midi.py +6 -1
  76. {creativepython-0.3.5 → creativepython-1.0.0}/src/music.py +163 -72
  77. {creativepython-0.3.5 → creativepython-1.0.0}/src/timer.py +10 -12
  78. {creativepython-0.3.5 → creativepython-1.0.0}/src/zipf.py +316 -22
  79. creativepython-0.3.5/src/CreativePython/examples/ArvoPart.CantusInMemoriam.py +0 -76
  80. creativepython-0.3.5/src/CreativePython/examples/ConcretPH_Xenakis.py +0 -41
  81. creativepython-0.3.5/src/CreativePython/examples/DeepPurple.SmokeOnTheWater.py +0 -67
  82. creativepython-0.3.5/src/CreativePython/examples/JS_Bach.Canon.TriasHarmonica.BWV1072.py +0 -77
  83. creativepython-0.3.5/src/CreativePython/examples/JS_Bach.Canon_1.GoldbergGround.BWV1087.py +0 -39
  84. creativepython-0.3.5/src/CreativePython/examples/Mozart.MusikalischesWurfelspiel.py +0 -101
  85. creativepython-0.3.5/src/CreativePython/examples/PierreCage.StructuresPourDeuxChances.py +0 -47
  86. creativepython-0.3.5/src/CreativePython/examples/RGB_Display.py +0 -73
  87. creativepython-0.3.5/src/CreativePython/examples/TerryRiley.InC.py +0 -34
  88. creativepython-0.3.5/src/CreativePython/examples/arpeggiator1.py +0 -30
  89. creativepython-0.3.5/src/CreativePython/examples/arpeggiator2.py +0 -32
  90. creativepython-0.3.5/src/CreativePython/examples/autumnLeaves.py +0 -77
  91. creativepython-0.3.5/src/CreativePython/examples/axelF.py +0 -26
  92. creativepython-0.3.5/src/CreativePython/examples/biosignals.txt +0 -3555
  93. creativepython-0.3.5/src/CreativePython/examples/boids.py +0 -268
  94. creativepython-0.3.5/src/CreativePython/examples/brownianMelody.py +0 -47
  95. creativepython-0.3.5/src/CreativePython/examples/changesByTupac.py +0 -23
  96. creativepython-0.3.5/src/CreativePython/examples/clementine.py +0 -140
  97. creativepython-0.3.5/src/CreativePython/examples/continuousPitchInstrumentAudio.py +0 -54
  98. creativepython-0.3.5/src/CreativePython/examples/drumExample.py +0 -22
  99. creativepython-0.3.5/src/CreativePython/examples/drumMachinePattern1.py +0 -50
  100. creativepython-0.3.5/src/CreativePython/examples/drumsComeAlive.py +0 -87
  101. creativepython-0.3.5/src/CreativePython/examples/fibonacci.py +0 -19
  102. creativepython-0.3.5/src/CreativePython/examples/findPitchOctave.py +0 -14
  103. creativepython-0.3.5/src/CreativePython/examples/furElise.py +0 -27
  104. creativepython-0.3.5/src/CreativePython/examples/generativeMusic.py +0 -47
  105. creativepython-0.3.5/src/CreativePython/examples/goldenTree.py +0 -55
  106. creativepython-0.3.5/src/CreativePython/examples/guidoWordMusic.py +0 -70
  107. creativepython-0.3.5/src/CreativePython/examples/harmonicesMundi.py +0 -67
  108. creativepython-0.3.5/src/CreativePython/examples/harmonicesMundiRevisisted.py +0 -65
  109. creativepython-0.3.5/src/CreativePython/examples/harmonographLateral.py +0 -47
  110. creativepython-0.3.5/src/CreativePython/examples/harmonographRotary.py +0 -70
  111. creativepython-0.3.5/src/CreativePython/examples/iPianoBlackDown.png +0 -0
  112. creativepython-0.3.5/src/CreativePython/examples/iPianoOctave.png +0 -0
  113. creativepython-0.3.5/src/CreativePython/examples/iPianoParallel.py +0 -73
  114. creativepython-0.3.5/src/CreativePython/examples/iPianoSimple.py +0 -86
  115. creativepython-0.3.5/src/CreativePython/examples/iPianoWhiteCenterDown.png +0 -0
  116. creativepython-0.3.5/src/CreativePython/examples/iPianoWhiteLeftDown.png +0 -0
  117. creativepython-0.3.5/src/CreativePython/examples/iPianoWhiteRightDown.png +0 -0
  118. creativepython-0.3.5/src/CreativePython/examples/midiIn1.py +0 -14
  119. creativepython-0.3.5/src/CreativePython/examples/midiIn2.py +0 -13
  120. creativepython-0.3.5/src/CreativePython/examples/midiIn3.py +0 -25
  121. creativepython-0.3.5/src/CreativePython/examples/midiOut.a.py +0 -12
  122. creativepython-0.3.5/src/CreativePython/examples/midiOut.b.py +0 -14
  123. creativepython-0.3.5/src/CreativePython/examples/midiSynthesizer.py +0 -34
  124. creativepython-0.3.5/src/CreativePython/examples/midiSynthesizer2.py +0 -55
  125. creativepython-0.3.5/src/CreativePython/examples/moondog-bird_slament.wav +0 -0
  126. creativepython-0.3.5/src/CreativePython/examples/musicalSphere.py +0 -178
  127. creativepython-0.3.5/src/CreativePython/examples/note.py +0 -55
  128. creativepython-0.3.5/src/CreativePython/examples/octoplus.py +0 -56
  129. creativepython-0.3.5/src/CreativePython/examples/oscIn1.py +0 -13
  130. creativepython-0.3.5/src/CreativePython/examples/oscIn2.py +0 -19
  131. creativepython-0.3.5/src/CreativePython/examples/pentatonicMelody.py +0 -33
  132. creativepython-0.3.5/src/CreativePython/examples/pianoPhase.py +0 -28
  133. creativepython-0.3.5/src/CreativePython/examples/pianoRollGenerator.py +0 -43
  134. creativepython-0.3.5/src/CreativePython/examples/playNote.py +0 -7
  135. creativepython-0.3.5/src/CreativePython/examples/proteinMusic.py +0 -59
  136. creativepython-0.3.5/src/CreativePython/examples/randomCircles.py +0 -36
  137. creativepython-0.3.5/src/CreativePython/examples/randomCirclesThroughMidiInput.py +0 -52
  138. creativepython-0.3.5/src/CreativePython/examples/randomCirclesTimed.py +0 -67
  139. creativepython-0.3.5/src/CreativePython/examples/retrograde.a.py +0 -22
  140. creativepython-0.3.5/src/CreativePython/examples/retrograde.b.py +0 -36
  141. creativepython-0.3.5/src/CreativePython/examples/retrograde.c.py +0 -41
  142. creativepython-0.3.5/src/CreativePython/examples/rowYourBoat.py +0 -61
  143. creativepython-0.3.5/src/CreativePython/examples/scaleTutor.py +0 -27
  144. creativepython-0.3.5/src/CreativePython/examples/sierpinskiTriangle.py +0 -52
  145. creativepython-0.3.5/src/CreativePython/examples/simpleButtonInstrument.py +0 -34
  146. creativepython-0.3.5/src/CreativePython/examples/simpleCircleInstrument.py +0 -70
  147. creativepython-0.3.5/src/CreativePython/examples/sineMelody.py +0 -23
  148. creativepython-0.3.5/src/CreativePython/examples/sineMelodyPlus.py +0 -28
  149. creativepython-0.3.5/src/CreativePython/examples/sliderControl.py +0 -66
  150. creativepython-0.3.5/src/CreativePython/examples/sonifyBiosignals.py +0 -79
  151. creativepython-0.3.5/src/CreativePython/examples/sonifyImage.py +0 -109
  152. creativepython-0.3.5/src/CreativePython/examples/soundscapeLoutrakiSunset.jpg +0 -0
  153. creativepython-0.3.5/src/CreativePython/examples/stringQuartet.py +0 -36
  154. creativepython-0.3.5/src/CreativePython/examples/textMusic.py +0 -62
  155. creativepython-0.3.5/src/CreativePython/examples/theWayItIs.py +0 -33
  156. creativepython-0.3.5/src/CreativePython/examples/themeAndVariations.py +0 -64
  157. creativepython-0.3.5/src/CreativePython/examples/throwingDice.py +0 -37
  158. creativepython-0.3.5/src/CreativePython/examples/windChimes.py +0 -56
  159. creativepython-0.3.5/src/CreativePython/examples/zipfMetrics.py +0 -81
  160. creativepython-0.3.5/src/CreativePython/resources/550973__luizguilherme_a__clean-guitarr-riff.mp3 +0 -0
  161. creativepython-0.3.5/src/CreativePython/resources/chopper.jpg +0 -0
  162. creativepython-0.3.5/src/CreativePython/resources/de-brazzas-monkey.jpg +0 -0
  163. creativepython-0.3.5/src/CreativePython.egg-info/SOURCES.txt +0 -110
  164. {creativepython-0.3.5 → creativepython-1.0.0}/LICENSE +0 -0
  165. {creativepython-0.3.5 → creativepython-1.0.0}/LICENSE-PSF +0 -0
  166. {creativepython-0.3.5 → creativepython-1.0.0}/MANIFEST.in +0 -0
  167. {creativepython-0.3.5 → creativepython-1.0.0}/README.md +0 -0
  168. {creativepython-0.3.5 → creativepython-1.0.0}/setup.cfg +0 -0
  169. /creativepython-0.3.5/src/_RealtimeAudioPlayer.py → /creativepython-1.0.0/src/CreativePython/RealtimeAudioPlayer.py +0 -0
  170. {creativepython-0.3.5 → creativepython-1.0.0}/src/CreativePython.egg-info/dependency_links.txt +0 -0
  171. {creativepython-0.3.5 → creativepython-1.0.0}/src/bin/libportaudio.2.dylib +0 -0
  172. {creativepython-0.3.5 → creativepython-1.0.0}/src/iannix.py +0 -0
  173. {creativepython-0.3.5 → creativepython-1.0.0}/src/markov.py +0 -0
  174. {creativepython-0.3.5 → creativepython-1.0.0}/src/osc.py +0 -0
  175. {creativepython-0.3.5 → creativepython-1.0.0}/tests/testAnimate.py +0 -0
  176. {creativepython-0.3.5 → creativepython-1.0.0}/tests/testPeer.py +0 -0
  177. {creativepython-0.3.5 → creativepython-1.0.0}/tests/test_keyEvent.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CreativePython
3
- Version: 0.3.5
3
+ Version: 1.0.0
4
4
  Summary: A Python-based software environment for developing algorithmic art projects.
5
5
  Author-email: "Dr. Bill Manaris" <manaris@cofc.edu>, Taj Ballinger <ballingertj@g.cofc.edu>, Trevor Ritchie <ritchiets@g.cofc.edu>
6
6
  License: MIT License
@@ -65,9 +65,7 @@ Requires-Dist: pooch>=1.8
65
65
  Requires-Dist: pypianoroll>=1.0
66
66
  Requires-Dist: verovio>=5.6.0
67
67
  Requires-Dist: pymusicxml>=0.5.6
68
- Provides-Extra: dev
69
- Requires-Dist: build; extra == "dev"
70
- Requires-Dist: twine; extra == "dev"
68
+ Requires-Dist: pyinstaller>=6.16.0
71
69
  Dynamic: license-file
72
70
 
73
71
  # CreativePython
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "CreativePython"
7
- version = "0.3.5"
7
+ version = "1.0.0"
8
8
  description = "A Python-based software environment for developing algorithmic art projects."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -33,12 +33,13 @@ dependencies = [
33
33
  "pooch>=1.8",
34
34
  "pypianoroll>=1.0",
35
35
  "verovio>=5.6.0",
36
- "pymusicxml>=0.5.6"
36
+ "pymusicxml>=0.5.6",
37
+ "pyinstaller>=6.16.0"
37
38
  ]
38
39
 
39
40
  [tool.setuptools]
40
41
  package-dir = {"" = "src"}
41
- py-modules = ["gui", "image", "midi", "music", "osc", "timer", "iannix", "markov", "zipf", "_RealtimeAudioPlayer", "_notationRenderer"]
42
+ py-modules = ["gui", "image", "midi", "music", "osc", "timer", "iannix", "markov", "zipf"]
42
43
 
43
44
  [tool.setuptools.packages.find]
44
45
  where = ["src"]
@@ -47,7 +48,4 @@ where = ["src"]
47
48
  "CreativePython" = ["bin/libportaudio.2.dylib"]
48
49
 
49
50
  [project.urls]
50
- Homepage = "https://jythonmusic.me"
51
-
52
- [project.optional-dependencies]
53
- dev = ["build", "twine"]
51
+ Homepage = "https://jythonmusic.me"
@@ -7,4 +7,4 @@ if platform.system() == "Darwin":
7
7
  with importlib.resources.path("CreativePython.bin", "libportaudio.2.dylib") as libpath:
8
8
  ctypes.CDLL(str(libpath))
9
9
  except Exception:
10
- pass
10
+ pass
@@ -0,0 +1,224 @@
1
+ #######################################################################################################
2
+ # advMetricRunner.jythonSurvey.py Version 1.1 20-Sep-2025 Jimmy Cyganek, David Johnson, John Emerson,
3
+ # Luca Pellicoro, Patrick Roos, Bill Manaris
4
+ #######################################################################################################
5
+
6
+ ### Description: Runs metrics on a folder of MIDI files, these metrics are saved into a CSV file
7
+
8
+ ### 1.1 (Sep. 20, 2025) - Removed DurationDistanceMetric from Surveyor, as it returns innacurate measurements
9
+
10
+ # import libraries
11
+ import os
12
+ import sys
13
+ import unicodedata
14
+
15
+ # add src to path if needed
16
+ srcDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
17
+ if srcDir not in sys.path:
18
+ sys.path.insert(0, srcDir)
19
+
20
+ from music import *
21
+ from ..Surveyor import Surveyor
22
+ from ..metrics.simple import * # import simple metrics
23
+
24
+
25
+ #####
26
+ # set MIDI file path
27
+ midiFolderPath = "Bach AoF Fugues"
28
+
29
+ # using os module, find everything in folder that ends in ".mid"
30
+ folderFileNames = os.listdir(midiFolderPath) # get filenames in folder (as strings)
31
+
32
+
33
+ #####
34
+ # keep only MIDI files
35
+ midiFileNames = []
36
+ for name in folderFileNames: # iterate through all filenames
37
+
38
+ if name[-4:] == ".mid": # is it a MIDI file?
39
+ midiFileNames.append(name) # yes, so remember it
40
+
41
+ # now, midiFileNames contains all the names of MIDI files in midiFolderPath
42
+
43
+
44
+ #####
45
+ # define metrics to apply (we only need to create them once - they will be reused for every MIDI file)
46
+ metrics = [
47
+ PitchMetric(2),
48
+ # PitchDistanceMetric(2),
49
+ PitchDurationMetric(2),
50
+ PitchDurationQuantizedMetric(2), # added Jan. 6 2010
51
+
52
+ ChromaticToneMetric(2),
53
+
54
+ DurationMetric(2),
55
+ DurationBigramMetric(2),
56
+ # DurationDistanceMetric(2), # added Sep. 30 2009
57
+
58
+ DurationQuantizedMetric(2), # added Jan. 6 2010
59
+ DurationQuantizedBigramMetric(2), # added Jan. 7 2010
60
+ DurationQuantizedDistanceMetric(2), # added Jan. 6 2010
61
+
62
+ ContourMelodyPitchMetric(2), # added Jan. 7 2010
63
+ ContourMelodyDurationMetric(2), # added Jan. 7 2010
64
+ ContourMelodyDurationQuantizedMetric(2), # added Jan. 7 2010
65
+
66
+ ContourBasslinePitchMetric(2), # added Jan. 7 2010
67
+ ContourBasslineDurationMetric(2), # added Jan. 7 2010
68
+ ContourBasslineDurationQuantizedMetric(2), # added Jan. 7 2010
69
+
70
+ MelodicIntervalMetric(2),
71
+ MelodicBigramMetric(2),
72
+ MelodicConsonanceMetric(2),
73
+
74
+ HarmonicIntervalMetric(2),
75
+ HarmonicBigramMetric(2),
76
+ HarmonicConsonanceMetric(2),
77
+
78
+ ChordMetric(2),
79
+ RestMetric(2)
80
+ ]
81
+
82
+
83
+ #####
84
+ # function to return metrics for a MIDI file
85
+ def runMetrics(midiFilePath):
86
+
87
+ # load MIDI into Score object
88
+ score = Score("Metric Score")
89
+ Read.midi(score, midiFilePath)
90
+
91
+ # initialize Surveyor and apply metrics to the score
92
+ quantum = 0.25
93
+ surveyor = Surveyor()
94
+ surveyor.survey(score, metrics, quantum)
95
+
96
+ # get measurements for each metric
97
+ measurements = []
98
+ for metric in metrics: # iterate through each metric
99
+
100
+ # get measurement
101
+ measurement = metric.getMeasurement()
102
+ measurements.append(measurement)
103
+
104
+ #print measurements
105
+ return measurements
106
+
107
+ #####
108
+ # create a unique identifier for each piece - used as an attribute in Weka!!!
109
+ index = 0 # initialize
110
+
111
+ # function to create CSV file using list of measurements
112
+ def writeCSVFile(measurements, midiFolderPath, midiFileName, csvfile):
113
+
114
+ global index # used for index Weka attribute
115
+
116
+ index = index + 1 # create unique identifier for this piece
117
+
118
+ csvfile.write("\n") # move down a row
119
+
120
+ # for each metric, get each measurement and add it to the corresponding column
121
+ for measurement in measurements:
122
+
123
+ values = measurement.valuesToArray() # get attribute values for each metric
124
+
125
+ # get value of each measurement and write it to CSV file
126
+ for value in values:
127
+
128
+ csvfile.write(convertToASCII(value) + ",") # write it!!
129
+
130
+ csvfile.write(convertToASCII(index) + ",") # write index value to index column
131
+
132
+ # Caution: remove comma from filename - if any (since CSV files use comma as delimiter)
133
+ noCommaFileName = midiFileName.replace(",", "")
134
+
135
+ # write filename to the filename column (last column)
136
+ csvfile.write(convertToASCII(noCommaFileName))
137
+
138
+ # ***
139
+ print("Wrote row for", midiFileName)
140
+
141
+ #####
142
+ # function to remove non-ASCII characters
143
+ def convertToASCII(string):
144
+
145
+ # ***
146
+ #try:
147
+ # convert input to unicode string (in Python 3, str is already unicode)
148
+ unicodeString = str(string)
149
+
150
+ # convert string to NFKD format (separate characters from accents - creates two characters from one)
151
+ nfkdString = unicodedata.normalize('NFKD', unicodeString)
152
+
153
+ # re-encode to ASCII - ignore non-ASCII characters (like accents or symbols)
154
+ asciiBytes = nfkdString.encode('ascii', 'ignore')
155
+
156
+ # decode back to string (Python 3 returns bytes from encode)
157
+ asciiString = asciiBytes.decode('ascii')
158
+
159
+ # return ASCII-only version of the string
160
+ return asciiString
161
+
162
+ #except: # ***
163
+
164
+ # if there is no error, return as a string
165
+ #return str(string)
166
+
167
+
168
+ ##### Main #####
169
+
170
+ # Goal: process all MIDI files, get their metrics, then put them into CSV file
171
+ # How I plan to do this:
172
+ # - Run runMetrics on the first item in midiFileNames
173
+ # - This will return it's metrics
174
+ # - Create the first row of the CSV (metric measurement names, will become attribute names in Weka) of this first item
175
+ # - Then, run a for loop for every item in midiFileNames, take it's values, and add them as a new row to the CSV
176
+
177
+
178
+ #####
179
+
180
+ # output metrics headers, by running metrics on first MIDI file
181
+
182
+ # create file path to the first MIDI file (midiFileNames[0])
183
+ midiFilePath = os.path.join(midiFolderPath, midiFileNames[0])
184
+
185
+ # run runMetrics on the first item in midiFileNames
186
+ measurements = runMetrics(midiFilePath)
187
+
188
+ # open CSV file for output
189
+ with open(midiFolderPath + "_metrics.csv", mode="w") as csvfile:
190
+
191
+ # output headers for all measurements to CSV file
192
+ for measurement in measurements:
193
+
194
+ keys = measurement.keysToArray() # get attribute names for each metric
195
+
196
+ # output headers for this measurement
197
+ for key in keys:
198
+ csvfile.write(key+",") # write them in CSV file - comma used to separate columns
199
+
200
+ # add final headers
201
+ csvfile.write("Index,") # write index attribute header
202
+ csvfile.write("Filename") # write filename attribute header
203
+
204
+ # run metrics for each MIDI file, and store results in CSV file
205
+ for midiFileName in midiFileNames:
206
+
207
+ # create path to MIDI file
208
+ midiFilePath = os.path.join(midiFolderPath, midiFileName)
209
+
210
+ # calculate metrics for this MIDI file
211
+ measurements = runMetrics(midiFilePath)
212
+
213
+ # write results to CSV file
214
+ writeCSVFile(measurements, midiFolderPath, midiFileName, csvfile)
215
+
216
+ # now, all metrics for all MIDI files have been written to CSV file
217
+
218
+ # ***
219
+ # Is this needed???
220
+ # remember to close CSV file!!!
221
+ #csvfile.close()
222
+
223
+ # ***
224
+ print(midiFolderPath + "_metrics.csv written.")
@@ -0,0 +1,274 @@
1
+ """
2
+ Compare two metrics CSV files cell by cell and compute statistics.
3
+ Tracks average difference, standard deviation, and maximum difference.
4
+ """
5
+
6
+ import csv
7
+ import numpy as np
8
+ from pathlib import Path
9
+ from datetime import datetime
10
+ import sys
11
+ import re
12
+
13
+
14
+ def compareMetricsCsv(file1Path, file2Path, outputFile=None):
15
+ """
16
+ Compare two CSV files cell by cell and compute statistics.
17
+
18
+ Args:
19
+ file1Path: Path to first CSV file (new/comparison version)
20
+ file2Path: Path to second CSV file (original version)
21
+ outputFile: Optional file object to write output to
22
+
23
+ Returns:
24
+ Dictionary with statistics about the differences
25
+ """
26
+
27
+ def printAndLog(message=""):
28
+ """Print to console and optionally to file"""
29
+ print(message)
30
+ if outputFile:
31
+ outputFile.write(message + "\n")
32
+
33
+ # read both CSV files
34
+ with open(file1Path, 'r', encoding='utf-8') as f1:
35
+ reader1 = csv.reader(f1)
36
+ headers1 = next(reader1)
37
+ rows1 = list(reader1)
38
+
39
+ with open(file2Path, 'r', encoding='utf-8') as f2:
40
+ reader2 = csv.reader(f2)
41
+ headers2 = next(reader2)
42
+ rows2 = list(reader2)
43
+
44
+ # verify headers match
45
+ if headers1 != headers2:
46
+ printAndLog("WARNING: Headers do not match!")
47
+ printAndLog(f"File 1 has {len(headers1)} columns")
48
+ printAndLog(f"File 2 has {len(headers2)} columns")
49
+ return None
50
+
51
+ printAndLog(f"Headers match: {len(headers1)} columns")
52
+ printAndLog(f"File 1 rows: {len(rows1)}")
53
+ printAndLog(f"File 2 rows: {len(rows2)}")
54
+
55
+ # verify same number of rows
56
+ if len(rows1) != len(rows2):
57
+ printAndLog("WARNING: Different number of rows!")
58
+ return None
59
+
60
+ # collect all numeric differences
61
+ allDifferences = []
62
+ maxDifference = 0.0
63
+ maxDifferenceLocation = None
64
+
65
+ # statistics by column
66
+ columnStats = {}
67
+ for colIdx, colName in enumerate(headers1):
68
+ columnStats[colName] = []
69
+
70
+ # statistics by metric type
71
+ metricTypeStats = {}
72
+
73
+ # compare each cell
74
+ totalCells = 0
75
+ numericCells = 0
76
+ identicalCells = 0
77
+
78
+ for rowIdx, (row1, row2) in enumerate(zip(rows1, rows2)):
79
+ if len(row1) != len(row2):
80
+ printAndLog(f"WARNING: Row {rowIdx} has different number of columns!")
81
+ continue
82
+
83
+ for colIdx, (val1, val2) in enumerate(zip(row1, row2)):
84
+ totalCells += 1
85
+ colName = headers1[colIdx]
86
+
87
+ # try to compare as numbers
88
+ try:
89
+ num1 = float(val1)
90
+ num2 = float(val2)
91
+ numericCells += 1
92
+
93
+ # compute absolute difference
94
+ diff = abs(num1 - num2)
95
+ allDifferences.append(diff)
96
+ columnStats[colName].append(diff)
97
+
98
+ # extract metric type from column name (e.g., "PitchDuration_0_Slope" -> "PitchDuration")
99
+ metricType = colName.split('_')[0]
100
+ if metricType not in metricTypeStats:
101
+ metricTypeStats[metricType] = []
102
+ metricTypeStats[metricType].append(diff)
103
+
104
+ # track maximum difference
105
+ if diff > maxDifference:
106
+ maxDifference = diff
107
+ maxDifferenceLocation = (rowIdx, colIdx, colName, num1, num2)
108
+
109
+ # track identical cells
110
+ if diff < 1e-10: # essentially zero
111
+ identicalCells += 1
112
+
113
+ except ValueError:
114
+ # non-numeric cell (like filename), check if identical
115
+ if val1 == val2:
116
+ identicalCells += 1
117
+
118
+ # compute statistics
119
+ if len(allDifferences) == 0:
120
+ printAndLog("No numeric differences found!")
121
+ return None
122
+
123
+ avgDifference = np.mean(allDifferences)
124
+ stdDeviation = np.std(allDifferences)
125
+ medianDifference = np.median(allDifferences)
126
+
127
+ # print results
128
+ printAndLog()
129
+ printAndLog("=" * 80)
130
+ printAndLog("OVERALL STATISTICS")
131
+ printAndLog("=" * 80)
132
+ printAndLog()
133
+ printAndLog(f" Total cells compared: {totalCells:>8,}")
134
+ printAndLog(f" Numeric cells: {numericCells:>8,}")
135
+ printAndLog(f" Identical cells: {identicalCells:>8,} ({100*identicalCells/totalCells:>5.2f}%)")
136
+ printAndLog(f" Different cells: {totalCells-identicalCells:>8,} ({100*(totalCells-identicalCells)/totalCells:>5.2f}%)")
137
+ printAndLog()
138
+ printAndLog(" Difference Metrics:")
139
+ printAndLog(f" Average difference: {avgDifference:>12.6f}")
140
+ printAndLog(f" Median difference: {medianDifference:>12.6f}")
141
+ printAndLog(f" Std deviation: {stdDeviation:>12.6f}")
142
+ printAndLog(f" Maximum difference: {maxDifference:>12.6f}")
143
+
144
+ if maxDifferenceLocation:
145
+ rowIdx, colIdx, colName, val1, val2 = maxDifferenceLocation
146
+ printAndLog()
147
+ printAndLog(" Maximum Difference Details:")
148
+ printAndLog(f" Row: {rowIdx + 2} (CSV line {rowIdx + 2})")
149
+ printAndLog(f" Column: {colIdx}")
150
+ printAndLog(f" Column name: {colName}")
151
+ printAndLog(f" Python value: {val1}")
152
+ printAndLog(f" Java value: {val2}")
153
+ printAndLog(f" Absolute difference: {abs(val1 - val2):.10f}")
154
+
155
+ # find columns with largest average differences
156
+ printAndLog()
157
+ printAndLog("=" * 80)
158
+ printAndLog("TOP 10 COLUMNS WITH LARGEST AVERAGE DIFFERENCES")
159
+ printAndLog("=" * 80)
160
+ printAndLog()
161
+
162
+ columnAvgDiffs = {}
163
+ for colName, diffs in columnStats.items():
164
+ if len(diffs) > 0:
165
+ columnAvgDiffs[colName] = np.mean(diffs)
166
+
167
+ sortedColumns = sorted(columnAvgDiffs.items(), key=lambda x: x[1], reverse=True)
168
+ for rank, (colName, avgDiff) in enumerate(sortedColumns[:10], 1):
169
+ printAndLog(f" {rank:2d}. {colName:55s} {avgDiff:.8f}")
170
+
171
+ # find columns with smallest average differences (most accurate)
172
+ printAndLog()
173
+ printAndLog("=" * 80)
174
+ printAndLog("TOP 10 COLUMNS WITH SMALLEST AVERAGE DIFFERENCES (Most Accurate)")
175
+ printAndLog("=" * 80)
176
+ printAndLog()
177
+
178
+ for rank, (colName, avgDiff) in enumerate(reversed(sortedColumns[-10:]), 1):
179
+ printAndLog(f" {rank:2d}. {colName:55s} {avgDiff:.8f}")
180
+
181
+ # analyze by metric type
182
+ printAndLog()
183
+ printAndLog("=" * 80)
184
+ printAndLog("DIFFERENCES BY METRIC TYPE")
185
+ printAndLog("=" * 80)
186
+ printAndLog()
187
+
188
+ metricTypeAvgs = {}
189
+ for metricType, diffs in metricTypeStats.items():
190
+ if len(diffs) > 0:
191
+ metricTypeAvgs[metricType] = {
192
+ 'avg': np.mean(diffs),
193
+ 'max': np.max(diffs),
194
+ 'median': np.median(diffs),
195
+ 'std': np.std(diffs),
196
+ 'count': len(diffs)
197
+ }
198
+
199
+ sortedMetricTypes = sorted(metricTypeAvgs.items(), key=lambda x: x[1]['max'], reverse=True)
200
+
201
+ printAndLog(f"{'Metric Type':<30} {'Max Diff':>12} {'Avg Diff':>12} {'Median':>12} {'Std Dev':>12} {'Count':>8}")
202
+ printAndLog("-" * 80)
203
+ for metricType, stats in sortedMetricTypes:
204
+ printAndLog(f"{metricType:<30} {stats['max']:>12.6f} {stats['avg']:>12.6f} {stats['median']:>12.6f} {stats['std']:>12.6f} {stats['count']:>8,}")
205
+
206
+ return {
207
+ 'avgDifference': avgDifference,
208
+ 'stdDeviation': stdDeviation,
209
+ 'maxDifference': maxDifference,
210
+ 'medianDifference': medianDifference,
211
+ 'totalCells': totalCells,
212
+ 'numericCells': numericCells,
213
+ 'identicalCells': identicalCells,
214
+ 'columnStats': columnStats,
215
+ 'metricTypeStats': metricTypeStats
216
+ }
217
+
218
+
219
+ if __name__ == "__main__":
220
+ # get script directory to make paths relative to script location
221
+ scriptDir = Path(__file__).parent
222
+
223
+ # define file paths relative to script location
224
+ newFile = scriptDir / "Bach AoF Fugues_metrics.csv"
225
+ originalFile = scriptDir / "ORIGINAL-JAVA_Bach AoF Fugues_metrics.csv"
226
+
227
+ # create results directory if it doesn't exist
228
+ resultsDir = scriptDir / "results"
229
+ resultsDir.mkdir(exist_ok=True)
230
+
231
+ # create timestamped output filename
232
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
233
+ outputFilename = resultsDir / f"comparison_{timestamp}.txt"
234
+
235
+ print("=" * 80)
236
+ print("METRICS COMPARISON: Python Implementation vs Java Implementation")
237
+ print("=" * 80)
238
+ print()
239
+ print(f"Python results file: {newFile.name}")
240
+ print(f"Java results file: {originalFile.name}")
241
+ print(f"Output file: {outputFilename.relative_to(scriptDir)}")
242
+ print()
243
+
244
+ # verify files exist
245
+ if not newFile.exists():
246
+ print(f"ERROR: File not found: {newFile}")
247
+ sys.exit(1)
248
+ if not originalFile.exists():
249
+ print(f"ERROR: File not found: {originalFile}")
250
+ sys.exit(1)
251
+
252
+ # run comparison and save to file
253
+ with open(outputFilename, 'w', encoding='utf-8') as outFile:
254
+ # write header to file
255
+ outFile.write("=" * 80 + "\n")
256
+ outFile.write("METRICS COMPARISON: Python Implementation vs Java Implementation\n")
257
+ outFile.write("=" * 80 + "\n")
258
+ outFile.write(f"\nGenerated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
259
+ outFile.write(f"Python results file: {newFile.name}\n")
260
+ outFile.write(f"Java results file: {originalFile.name}\n")
261
+ outFile.write("\n")
262
+
263
+ # run comparison
264
+ results = compareMetricsCsv(newFile, originalFile, outFile)
265
+
266
+ if results:
267
+ print()
268
+ print("=" * 80)
269
+ print("COMPARISON COMPLETE!")
270
+ print("=" * 80)
271
+ print(f"\nResults saved to: {outputFilename}")
272
+ else:
273
+ print("\nComparison failed!")
274
+ sys.exit(1)