From 330c35064464e374731a727e913cb36b0d817480 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sat, 1 Feb 2020 10:02:04 -0800 Subject: [PATCH 01/16] document and add cents --- Sourcecode/include/mx/api/PitchData.h | 24 ++++++++++++++++++++---- Sourcecode/private/mx/api/PitchData.cpp | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Sourcecode/include/mx/api/PitchData.h b/Sourcecode/include/mx/api/PitchData.h index 0d3303f0b..554e9fa3b 100644 --- a/Sourcecode/include/mx/api/PitchData.h +++ b/Sourcecode/include/mx/api/PitchData.h @@ -69,7 +69,22 @@ namespace mx PitchData(); Step step; + + // the alteration (number of semitones of pitch distance) from the step. for example, if step is 'c' and + // alter is 1, then the sounding pitch is c#. if step is 'd' and alter is -2, then the sounding pitch is + // 'c' (i.e. d double flat). alter only affects the sounding pitch of the note. accidentals are applied + // independently. alter is always required to produce the correct sounding pitch, regardless of key + // signature or accidentals. int alter; + + // additional alteration to the sounding pitch (in hundredths of a semitone). the MusicXML alter value is + // a floating point number to facilitate microtonal music. however for mx::api we wanted the simplicity of + // dealing with integrals for the more common case on non-microtonal music. in order to still support + // microtones without resording to a floating-point alter value, we break out microtonal adjustments to a + // separate 'cents' field, which will be addeded to the alter integral: + // = (double)alter + (cents * 100.0) + double cents; + Accidental accidental; bool isAccidentalParenthetical; bool isAccidentalCautionary; @@ -77,18 +92,19 @@ namespace mx bool isAccidentalBracketed; int octave; - // automatically set the Accidental enum value by - // parsing the alter value + // automatically set the Accidental enum value by parsing the alter value (does not consider the value of + // cents). this is a convenience function that simply adds the correct accidental given the current value + // of alter. for example, if alter is 1, then showAccidental will set the accidental field to 'sharp'. void showAccidental(); - // set the accidental value to 'none' and clear out the - // other accidental-related values + // set the accidental value to 'none' and clear out the other accidental-related values void hideAccidental(); }; MXAPI_EQUALS_BEGIN( PitchData ) MXAPI_EQUALS_MEMBER( step ) MXAPI_EQUALS_MEMBER( alter ) + MXAPI_DOUBLES_EQUALS_MEMBER( cents ) MXAPI_EQUALS_MEMBER( accidental ) MXAPI_EQUALS_MEMBER( isAccidentalParenthetical ) MXAPI_EQUALS_MEMBER( isAccidentalCautionary ) diff --git a/Sourcecode/private/mx/api/PitchData.cpp b/Sourcecode/private/mx/api/PitchData.cpp index d4b932da6..b253f7779 100644 --- a/Sourcecode/private/mx/api/PitchData.cpp +++ b/Sourcecode/private/mx/api/PitchData.cpp @@ -11,6 +11,7 @@ namespace mx PitchData::PitchData() : step{ Step::c } , alter{ 0 } + , cents{ 0.0 } , accidental{Accidental::none} , isAccidentalParenthetical{ false } , isAccidentalCautionary{ false } From 204334367c67b9b9f6b0b5ac3925dd2ddce9e950 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sat, 1 Feb 2020 10:05:50 -0800 Subject: [PATCH 02/16] documentation --- Sourcecode/include/mx/api/PitchData.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Sourcecode/include/mx/api/PitchData.h b/Sourcecode/include/mx/api/PitchData.h index 554e9fa3b..0490357be 100644 --- a/Sourcecode/include/mx/api/PitchData.h +++ b/Sourcecode/include/mx/api/PitchData.h @@ -68,6 +68,7 @@ namespace mx { PitchData(); + // the note name. i.e. c, d, e, f, g, a, b Step step; // the alteration (number of semitones of pitch distance) from the step. for example, if step is 'c' and @@ -85,11 +86,19 @@ namespace mx // = (double)alter + (cents * 100.0) double cents; + // in MusicXML, the accidental is completely independent of the sounding pitch and is only present when you + // actually want to show the accidental in the notated music. i.e. accidental is purely visual. for example, + // if you have a measure consisting of repeated c# notes, you would typically notate this with an accidental + // on the first note only. the rest of the notes of the measure would be 'sharped' by virtue of the first + // note's sharp. in MusicXML, the first note should have an accidental of 'sharp' and an alter of '1', and + // the remaining notes of the measure should have an accidental of 'none' and an alter of '1'. Accidental accidental; bool isAccidentalParenthetical; bool isAccidentalCautionary; bool isAccidentalEditorial; bool isAccidentalBracketed; + + // which octave the note is located in. middle c is in octave 4. int octave; // automatically set the Accidental enum value by parsing the alter value (does not consider the value of From b3ba1e68eb92fff6078bd48f45072960d83a1dbd Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sat, 1 Feb 2020 10:06:27 -0800 Subject: [PATCH 03/16] documentation --- Sourcecode/include/mx/api/PitchData.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Sourcecode/include/mx/api/PitchData.h b/Sourcecode/include/mx/api/PitchData.h index 0490357be..6febe437b 100644 --- a/Sourcecode/include/mx/api/PitchData.h +++ b/Sourcecode/include/mx/api/PitchData.h @@ -66,6 +66,7 @@ namespace mx struct PitchData { + // default construction is middle c (c4) PitchData(); // the note name. i.e. c, d, e, f, g, a, b From 1f02f367a8b5251cf34150074933ac097fd99e16 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sat, 1 Feb 2020 10:17:58 -0800 Subject: [PATCH 04/16] implementation --- Sourcecode/private/mx/impl/NoteFunctions.cpp | 1 + Sourcecode/private/mx/impl/NoteReader.cpp | 11 +++++++++++ Sourcecode/private/mx/impl/NoteReader.h | 2 ++ Sourcecode/private/mx/impl/NoteWriter.cpp | 7 ++++++- Sourcecode/private/mxtest/api/NoteDataTest.cpp | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Sourcecode/private/mx/impl/NoteFunctions.cpp b/Sourcecode/private/mx/impl/NoteFunctions.cpp index 0d92d4076..2565ab7e3 100755 --- a/Sourcecode/private/mx/impl/NoteFunctions.cpp +++ b/Sourcecode/private/mx/impl/NoteFunctions.cpp @@ -110,6 +110,7 @@ namespace mx auto converter = Converter{}; myOutNoteData.pitchData.step = converter.convert( reader.getStep() ); myOutNoteData.pitchData.alter = reader.getAlter(); + myOutNoteData.pitchData.cents = reader.getCents(); myOutNoteData.pitchData.accidental = api::Accidental::none; diff --git a/Sourcecode/private/mx/impl/NoteReader.cpp b/Sourcecode/private/mx/impl/NoteReader.cpp index 5faf400ba..f07c364b3 100644 --- a/Sourcecode/private/mx/impl/NoteReader.cpp +++ b/Sourcecode/private/mx/impl/NoteReader.cpp @@ -64,6 +64,7 @@ namespace mx , myDurationValue( 0.0L ) , myStep( core::StepEnum::c ) , myAlter( 0 ) + , myCents( 0.0 ) , myOctave( 4 ) , myStaffNumber( 0 ) , myVoiceNumber( 0 ) @@ -213,6 +214,16 @@ namespace mx myStep = pitch.getStep()->getValue(); myOctave = pitch.getOctave()->getValue().getValue(); myAlter = static_cast( std::ceil( pitch.getAlter()->getValue().getValue() - 0.5 ) ); + + const auto micro = + std::abs( static_cast( myAlter ) - pitch.getAlter()->getValue().getValue() ); + + const auto microDistance =std::abs( micro ); + + if( microDistance >= 0.000000000001 ) + { + myCents = static_cast( micro * 100.0 ); + } break; } diff --git a/Sourcecode/private/mx/impl/NoteReader.h b/Sourcecode/private/mx/impl/NoteReader.h index 013dc08e6..9f1d1fd14 100644 --- a/Sourcecode/private/mx/impl/NoteReader.h +++ b/Sourcecode/private/mx/impl/NoteReader.h @@ -50,6 +50,7 @@ namespace mx inline long double getDurationValue() const { return myDurationValue; } inline core::StepEnum getStep() const { return myStep; } inline int getAlter() const { return myAlter; } + inline int getCents() const { return myCents; } inline int getOctave() const { return myOctave; } inline int getStaffNumber() const { return myStaffNumber; } inline int getVoiceNumber() const { return myVoiceNumber; } @@ -89,6 +90,7 @@ namespace mx long double myDurationValue; core::StepEnum myStep; int myAlter; + double myCents; int myOctave; int myStaffNumber; int myVoiceNumber; diff --git a/Sourcecode/private/mx/impl/NoteWriter.cpp b/Sourcecode/private/mx/impl/NoteWriter.cpp index 7ab32181b..ddf7690fa 100644 --- a/Sourcecode/private/mx/impl/NoteWriter.cpp +++ b/Sourcecode/private/mx/impl/NoteWriter.cpp @@ -372,8 +372,13 @@ namespace mx pitch->getStep()->setValue( myConverter.convert( myNoteData.pitchData.step ) ); if( myNoteData.pitchData.alter != 0 ) { + core::DecimalType microtones = 0.0; + if( myNoteData.pitchData.cents != 0.0 ) { + microtones = static_cast( myNoteData.pitchData.cents * 100.0 ); + } + const auto alter = static_cast( myNoteData.pitchData.alter ) + microtones; pitch->setHasAlter( true ); - pitch->getAlter()->setValue( core::Semitones{ static_cast( myNoteData.pitchData.alter ) } ); + pitch->getAlter()->setValue( core::Semitones{ alter } ); } pitch->getOctave()->setValue( core::OctaveValue{ myNoteData.pitchData.octave } ); } diff --git a/Sourcecode/private/mxtest/api/NoteDataTest.cpp b/Sourcecode/private/mxtest/api/NoteDataTest.cpp index 5d5434190..34df1dffc 100644 --- a/Sourcecode/private/mxtest/api/NoteDataTest.cpp +++ b/Sourcecode/private/mxtest/api/NoteDataTest.cpp @@ -977,4 +977,6 @@ TEST( notePositionRoundTrip, NoteData ) } T_END; +// TODO - write PitchData::cents tests + #endif From ffd49939d8b021ea877bf969c84e6b370d490eea Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sat, 1 Feb 2020 18:32:08 -0800 Subject: [PATCH 05/16] pitch data test --- .../private/mxtest/api/PitchDataTest.cpp | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Sourcecode/private/mxtest/api/PitchDataTest.cpp diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp new file mode 100644 index 000000000..b3ea5f5c8 --- /dev/null +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -0,0 +1,75 @@ +// MusicXML Class Library +// Copyright (c) by Matthew James Briggs +// Distributed under the MIT License + +#include "mxtest/control/CompileControl.h" +#if defined(MX_COMPILE_API_TESTS) && defined(MX_COMPILE_API_ROUNDTRIP) + +#include "cpul/cpulTestHarness.h" +#include "mxtest/api/RoundTrip.h" +#include "mx/api/DocumentManager.h" +#include "mx/core/Document.h" +#include "mx/core/elements/ScorePartwise.h" +#include "mx/core/elements/PartwisePart.h" +#include "mx/core/elements/PartwiseMeasure.h" +#include "mx/core/elements/MusicDataGroup.h" +#include "mx/core/elements/Note.h" +#include "mx/core/elements/NoteChoice.h" +#include "mx/core/elements/NormalNoteGroup.h" +#include "mx/core/elements/FullNoteGroup.h" +#include "mx/core/elements/FullNoteTypeChoice.h" +#include "mx/core/elements/Pitch.h" +#include "mx/core/elements/Notations.h" +#include "mx/core/elements/NotationsChoice.h" +#include "mx/core/elements/Tied.h" +#include "mx/core/elements/MusicDataChoice.h" +#include "mx/core/elements/Direction.h" +#include "mx/core/elements/DirectionType.h" +#include "mx/core/elements/Offset.h" +#include "mx/core/elements/Pedal.h" + +using namespace std; +using namespace mx::api; +using namespace mxtest; + +TEST( microtones1, PitchData ) +{ + ScoreData score; + score.parts.emplace_back(); + auto& part = score.parts.back(); + part.measures.emplace_back(); + auto& measure = part.measures.back(); + measure.staves.emplace_back(); + auto& staff = measure.staves.back(); + auto& voice = staff.voices[0]; + voice.notes.emplace_back(); + auto& note = voice.notes.back(); + note.pitchData.step = Step::f; + note.pitchData.accidental = Accidental::threeQuartersSharp; + note.pitchData.alter = 1; + note.pitchData.cents = 50.0; + + // round trip it through xml + auto& mgr = DocumentManager::getInstance(); + auto docId = mgr.createFromScore( score ); + std::stringstream ss; + mgr.writeToStream(docId, ss); + mgr.destroyDocument(docId); + const std::string xml = ss.str(); + std::istringstream iss{ xml }; + docId = mgr.createFromStream( iss ); + auto oscore = mgr.getData(docId); + + // get the data after the round trip + const auto& opart = oscore.parts.back(); + const auto& omeasure = opart.measures.back(); + const auto& ostaff = omeasure.staves.back(); + const auto& ovoice = ostaff.voices.at( 0 ); + const auto& onote = ovoice.notes.back(); + CHECK( note.pitchData.step == onote.pitchData.step ); + CHECK( note.pitchData.alter == onote.pitchData.alter ); + CHECK_DOUBLES_EQUAL( 50.0, onote.pitchData.cents, 0.0001 ); +} +T_END; + +#endif From d1ee1f1b096a03449e4fa65e280e781e8b1ebd52 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sat, 1 Feb 2020 18:35:53 -0800 Subject: [PATCH 06/16] run only my new test --- Sourcecode/private/mxtest/api/PitchDataTest.cpp | 6 ++---- Sourcecode/private/mxtest/api/RoundTrip.h | 2 -- .../private/mxtest/control/CompileControl.h | 15 +++++++-------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp index b3ea5f5c8..120b9036b 100644 --- a/Sourcecode/private/mxtest/api/PitchDataTest.cpp +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -3,10 +3,9 @@ // Distributed under the MIT License #include "mxtest/control/CompileControl.h" -#if defined(MX_COMPILE_API_TESTS) && defined(MX_COMPILE_API_ROUNDTRIP) +//#ifdef MX_COMPILE_API_TESTS #include "cpul/cpulTestHarness.h" -#include "mxtest/api/RoundTrip.h" #include "mx/api/DocumentManager.h" #include "mx/core/Document.h" #include "mx/core/elements/ScorePartwise.h" @@ -30,7 +29,6 @@ using namespace std; using namespace mx::api; -using namespace mxtest; TEST( microtones1, PitchData ) { @@ -72,4 +70,4 @@ TEST( microtones1, PitchData ) } T_END; -#endif +//#endif diff --git a/Sourcecode/private/mxtest/api/RoundTrip.h b/Sourcecode/private/mxtest/api/RoundTrip.h index 75049e28f..01a221f44 100644 --- a/Sourcecode/private/mxtest/api/RoundTrip.h +++ b/Sourcecode/private/mxtest/api/RoundTrip.h @@ -5,7 +5,6 @@ #pragma once #include "mxtest/control/CompileControl.h" -#ifdef MX_COMPILE_API_ROUNDTRIP #include "mx/api/DocumentManager.h" #include "mxtest/file/MxFileRepository.h" @@ -45,4 +44,3 @@ namespace mxtest } } -#endif diff --git a/Sourcecode/private/mxtest/control/CompileControl.h b/Sourcecode/private/mxtest/control/CompileControl.h index 3e606fd96..8701bb7ae 100755 --- a/Sourcecode/private/mxtest/control/CompileControl.h +++ b/Sourcecode/private/mxtest/control/CompileControl.h @@ -4,14 +4,13 @@ #pragma once -#define MX_COMPILE_API_TESTS -#define MX_COMPILE_API_ROUNDTRIP -// #define MX_COMPILE_CORE_TESTS -#define MX_COMPILE_IMPL_TESTS -#define MX_COMPILE_IMPORT_TESTS -#define MX_COMPILE_NEW_DECIMAL_TESTS -#define MX_COMPILE_UTILTIY_TESTS -#define MX_COMPILE_XML_TESTS +//#define MX_COMPILE_API_TESTS +//#define MX_COMPILE_API_ROUNDTRIP +//#define MX_COMPILE_CORE_TESTS +//#define MX_COMPILE_IMPL_TESTS +//#define MX_COMPILE_IMPORT_TESTS +//#define MX_COMPILE_NEW_DECIMAL_TESTS +//#define MX_COMPILE_UTILTIY_TESTS // use this to restrict the size of the files that // the test run will open (compile-time constant). From d862ae8e7351e91913dd1374065eb64850c53f72 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sun, 2 Feb 2020 08:38:50 -0800 Subject: [PATCH 07/16] cents bug --- Sourcecode/private/mx/impl/NoteReader.cpp | 9 +- Sourcecode/private/mx/impl/NoteWriter.cpp | 2 +- .../private/mxtest/api/PitchDataTest.cpp | 173 ++++++++++++++---- 3 files changed, 144 insertions(+), 40 deletions(-) diff --git a/Sourcecode/private/mx/impl/NoteReader.cpp b/Sourcecode/private/mx/impl/NoteReader.cpp index f07c364b3..daf03c841 100644 --- a/Sourcecode/private/mx/impl/NoteReader.cpp +++ b/Sourcecode/private/mx/impl/NoteReader.cpp @@ -43,6 +43,7 @@ #include "mx/utility/StringToInt.h" #include +#include namespace mx { @@ -213,16 +214,18 @@ namespace mx const auto& pitch = *fullNoteTypeChoice.getPitch(); myStep = pitch.getStep()->getValue(); myOctave = pitch.getOctave()->getValue().getValue(); - myAlter = static_cast( std::ceil( pitch.getAlter()->getValue().getValue() - 0.5 ) ); + myAlter = static_cast( pitch.getAlter()->getValue().getValue() ); const auto micro = std::abs( static_cast( myAlter ) - pitch.getAlter()->getValue().getValue() ); - const auto microDistance =std::abs( micro ); + const auto microDistance = std::abs( micro ); if( microDistance >= 0.000000000001 ) { - myCents = static_cast( micro * 100.0 ); + const auto theCents = micro * 100.0; + const auto theNarrowCents = static_cast( theCents ); + myCents = theNarrowCents; } break; } diff --git a/Sourcecode/private/mx/impl/NoteWriter.cpp b/Sourcecode/private/mx/impl/NoteWriter.cpp index ddf7690fa..c0003bb39 100644 --- a/Sourcecode/private/mx/impl/NoteWriter.cpp +++ b/Sourcecode/private/mx/impl/NoteWriter.cpp @@ -374,7 +374,7 @@ namespace mx { core::DecimalType microtones = 0.0; if( myNoteData.pitchData.cents != 0.0 ) { - microtones = static_cast( myNoteData.pitchData.cents * 100.0 ); + microtones = static_cast( myNoteData.pitchData.cents / 100.0 ); } const auto alter = static_cast( myNoteData.pitchData.alter ) + microtones; pitch->setHasAlter( true ); diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp index 120b9036b..db9a2d112 100644 --- a/Sourcecode/private/mxtest/api/PitchDataTest.cpp +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -2,6 +2,7 @@ // Copyright (c) by Matthew James Briggs // Distributed under the MIT License +#include #include "mxtest/control/CompileControl.h" //#ifdef MX_COMPILE_API_TESTS @@ -26,47 +27,147 @@ #include "mx/core/elements/DirectionType.h" #include "mx/core/elements/Offset.h" #include "mx/core/elements/Pedal.h" +#include "ezxml/ezxml.h" using namespace std; using namespace mx::api; -TEST( microtones1, PitchData ) +namespace { - ScoreData score; - score.parts.emplace_back(); - auto& part = score.parts.back(); - part.measures.emplace_back(); - auto& measure = part.measures.back(); - measure.staves.emplace_back(); - auto& staff = measure.staves.back(); - auto& voice = staff.voices[0]; - voice.notes.emplace_back(); - auto& note = voice.notes.back(); - note.pitchData.step = Step::f; - note.pitchData.accidental = Accidental::threeQuartersSharp; - note.pitchData.alter = 1; - note.pitchData.cents = 50.0; - - // round trip it through xml - auto& mgr = DocumentManager::getInstance(); - auto docId = mgr.createFromScore( score ); - std::stringstream ss; - mgr.writeToStream(docId, ss); - mgr.destroyDocument(docId); - const std::string xml = ss.str(); - std::istringstream iss{ xml }; - docId = mgr.createFromStream( iss ); - auto oscore = mgr.getData(docId); - - // get the data after the round trip - const auto& opart = oscore.parts.back(); - const auto& omeasure = opart.measures.back(); - const auto& ostaff = omeasure.staves.back(); - const auto& ovoice = ostaff.voices.at( 0 ); - const auto& onote = ovoice.notes.back(); - CHECK( note.pitchData.step == onote.pitchData.step ); - CHECK( note.pitchData.alter == onote.pitchData.alter ); - CHECK_DOUBLES_EQUAL( 50.0, onote.pitchData.cents, 0.0001 ); + ezxml::XElementPtr bruteForceFindFirstElement( const ezxml::XElementPtr root, const std::string& inElementName ) + { + if( !root ) + { + throw std::runtime_error{ "bug in bruteForceFindFirstElement" }; + } + + if( root->getName() == inElementName ) + { + return root; + } + + auto iter = root->begin(); + const auto end = root->end(); + + for( ; iter != end; ++iter ) + { + const auto possible = bruteForceFindFirstElement( iter->clone(), inElementName ); + if( possible && possible->getName() == inElementName ) + { + return possible; + } + } + + return nullptr; + } + + struct Input + { + Step step; + int alter; + double cents; + Accidental accidental; + }; + + struct Output + { + Step step; + int alter; + double cents; + Accidental accidental; + std::string alterString; + }; + + Output pitchDataTest( const Input& input ) + { + ScoreData score; + score.parts.emplace_back(); + auto& part = score.parts.back(); + part.measures.emplace_back(); + auto& measure = part.measures.back(); + measure.staves.emplace_back(); + auto& staff = measure.staves.back(); + auto& voice = staff.voices[0]; + voice.notes.emplace_back(); + auto& note = voice.notes.back(); + note.pitchData.step = input.step; + note.pitchData.accidental = input.accidental; + note.pitchData.alter = input.alter; + note.pitchData.cents = input.cents; + + // round trip it through xml + auto& mgr = DocumentManager::getInstance(); + auto docId = mgr.createFromScore( score ); + std::stringstream ss; + mgr.writeToStream( docId, ss ); + + std::cout << std::endl << ss.str() << std::endl; + + // check the alter value that was written to xml + const auto xdoc = ezxml::XFactory::makeXDoc(); + xdoc->loadStream( ss ); + auto elem = xdoc->getRoot(); + elem = bruteForceFindFirstElement( elem, "alter" ); + const auto alterString = elem->getValue(); + Output output; + output.alterString = alterString; + + mgr.destroyDocument( docId ); + const std::string xml = ss.str(); + std::istringstream iss{ xml }; + docId = mgr.createFromStream( iss ); + auto oscore = mgr.getData( docId ); + mgr.destroyDocument( docId ); + const auto& opart = oscore.parts.back(); + const auto& omeasure = opart.measures.back(); + const auto& ostaff = omeasure.staves.back(); + const auto& ovoice = ostaff.voices.at( 0 ); + const auto& onote = ovoice.notes.back(); + output.step = onote.pitchData.step; + output.alter = onote.pitchData.alter; + output.cents = onote.pitchData.cents; + output.accidental = onote.pitchData.accidental; + return output; + } +} + +TEST( Microtones1, PitchData ) +{ + auto input = Input{}; + input.step = Step::f; + input.alter = 1; + input.cents = 50.0; + input.accidental = Accidental::threeQuartersSharp; + const std::string expectedAlterString = "1.5"; + const int expectedAlter = input.alter; + const double expectedCents = input.cents; + const Accidental expectedAccidental = input.accidental; + const auto output = pitchDataTest( input ); + + CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlter, output.alter ); + CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); + CHECK( expectedAccidental == output.accidental ); +} +T_END; + +TEST( Microtones2, PitchData ) +{ + auto input = Input{}; + input.step = Step::g; + input.alter = 1; + input.cents = 99.999; + input.accidental = Accidental::none; + const std::string expectedAlterString = "1.99999"; + const int expectedAlter = input.alter; + const double expectedCents = input.cents; + const Accidental expectedAccidental = input.accidental; + const auto output = pitchDataTest( input ); + + CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlter, output.alter ); + CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); + CHECK( expectedAccidental == output.accidental ); } T_END; From fa1360101f3356ecb1139780992ee79a7952b16e Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sun, 2 Feb 2020 09:25:51 -0800 Subject: [PATCH 08/16] working --- Sourcecode/private/mx/impl/NoteReader.cpp | 10 +-- Sourcecode/private/mx/impl/NoteReader.h | 2 +- .../private/mxtest/api/PitchDataTest.cpp | 90 +++++++++++++++++-- 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/Sourcecode/private/mx/impl/NoteReader.cpp b/Sourcecode/private/mx/impl/NoteReader.cpp index daf03c841..d5612b866 100644 --- a/Sourcecode/private/mx/impl/NoteReader.cpp +++ b/Sourcecode/private/mx/impl/NoteReader.cpp @@ -214,13 +214,11 @@ namespace mx const auto& pitch = *fullNoteTypeChoice.getPitch(); myStep = pitch.getStep()->getValue(); myOctave = pitch.getOctave()->getValue().getValue(); - myAlter = static_cast( pitch.getAlter()->getValue().getValue() ); - - const auto micro = - std::abs( static_cast( myAlter ) - pitch.getAlter()->getValue().getValue() ); - + const auto xmlAlter = pitch.getAlter()->getValue().getValue(); + const auto intAlter = static_cast( xmlAlter ); + myAlter = intAlter; + const auto micro = xmlAlter - static_cast ( intAlter ); const auto microDistance = std::abs( micro ); - if( microDistance >= 0.000000000001 ) { const auto theCents = micro * 100.0; diff --git a/Sourcecode/private/mx/impl/NoteReader.h b/Sourcecode/private/mx/impl/NoteReader.h index 9f1d1fd14..3701d2e24 100644 --- a/Sourcecode/private/mx/impl/NoteReader.h +++ b/Sourcecode/private/mx/impl/NoteReader.h @@ -50,7 +50,7 @@ namespace mx inline long double getDurationValue() const { return myDurationValue; } inline core::StepEnum getStep() const { return myStep; } inline int getAlter() const { return myAlter; } - inline int getCents() const { return myCents; } + inline double getCents() const { return myCents; } inline int getOctave() const { return myOctave; } inline int getStaffNumber() const { return myStaffNumber; } inline int getVoiceNumber() const { return myVoiceNumber; } diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp index db9a2d112..1aca8a36c 100644 --- a/Sourcecode/private/mxtest/api/PitchDataTest.cpp +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -76,6 +76,7 @@ namespace double cents; Accidental accidental; std::string alterString; + std::string secondAlterString; }; Output pitchDataTest( const Input& input ) @@ -100,8 +101,7 @@ namespace auto docId = mgr.createFromScore( score ); std::stringstream ss; mgr.writeToStream( docId, ss ); - - std::cout << std::endl << ss.str() << std::endl; + mgr.destroyDocument( docId ); // check the alter value that was written to xml const auto xdoc = ezxml::XFactory::makeXDoc(); @@ -112,7 +112,6 @@ namespace Output output; output.alterString = alterString; - mgr.destroyDocument( docId ); const std::string xml = ss.str(); std::istringstream iss{ xml }; docId = mgr.createFromStream( iss ); @@ -127,11 +126,25 @@ namespace output.alter = onote.pitchData.alter; output.cents = onote.pitchData.cents; output.accidental = onote.pitchData.accidental; + + // serialize a second time and check the alter string again + docId = mgr.createFromScore( score ); + ss.str( "" ); + mgr.writeToStream( docId, ss ); + mgr.destroyDocument( docId ); + + // check the alter value that was written to xml + const auto xdoc2 = ezxml::XFactory::makeXDoc(); + xdoc2->loadStream( ss ); + auto elem2 = xdoc->getRoot(); + elem2 = bruteForceFindFirstElement( elem2, "alter" ); + const auto alterString2 = elem->getValue(); + output.secondAlterString = alterString2; return output; } } -TEST( Microtones1, PitchData ) +TEST( ThreeQuarterSharp, PitchData ) { auto input = Input{}; input.step = Step::f; @@ -145,19 +158,41 @@ TEST( Microtones1, PitchData ) const auto output = pitchDataTest( input ); CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlterString, output.secondAlterString ); + CHECK_EQUAL( expectedAlter, output.alter ); + CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); + CHECK( expectedAccidental == output.accidental ); +} +T_END; + +TEST( ThreeQuarterFlat, PitchData ) +{ + auto input = Input{}; + input.step = Step::b; + input.alter = -1; + input.cents = -50.0; + input.accidental = Accidental::threeQuartersFlat; + const std::string expectedAlterString = "-1.5"; + const int expectedAlter = input.alter; + const double expectedCents = input.cents; + const Accidental expectedAccidental = input.accidental; + const auto output = pitchDataTest( input ); + + CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlterString, output.secondAlterString ); CHECK_EQUAL( expectedAlter, output.alter ); CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); CHECK( expectedAccidental == output.accidental ); } T_END; -TEST( Microtones2, PitchData ) +TEST( AlmostDoubleSharp, PitchData ) { auto input = Input{}; input.step = Step::g; input.alter = 1; input.cents = 99.999; - input.accidental = Accidental::none; + input.accidental = Accidental::doubleSharp; const std::string expectedAlterString = "1.99999"; const int expectedAlter = input.alter; const double expectedCents = input.cents; @@ -165,6 +200,49 @@ TEST( Microtones2, PitchData ) const auto output = pitchDataTest( input ); CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlterString, output.secondAlterString ); + CHECK_EQUAL( expectedAlter, output.alter ); + CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); + CHECK( expectedAccidental == output.accidental ); +} +T_END; + +TEST( AlmostDoubleFlat, PitchData ) +{ + auto input = Input{}; + input.step = Step::a; + input.alter = -1; + input.cents = -99.9999; + input.accidental = Accidental::flatFlat; + const std::string expectedAlterString = "-1.999999"; + const int expectedAlter = input.alter; + const double expectedCents = input.cents; + const Accidental expectedAccidental = input.accidental; + const auto output = pitchDataTest( input ); + + CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlterString, output.secondAlterString ); + CHECK_EQUAL( expectedAlter, output.alter ); + CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); + CHECK( expectedAccidental == output.accidental ); +} +T_END; + +TEST( Microtones4, PitchData ) +{ + auto input = Input{}; + input.step = Step::g; + input.alter = 1; + input.cents = -123456789; + input.accidental = Accidental::none; + const std::string expectedAlterString = "-1234566.89"; + const int expectedAlter = -1234566; + const double expectedCents = -89.0; + const Accidental expectedAccidental = input.accidental; + const auto output = pitchDataTest( input ); + + CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlterString, output.secondAlterString ); CHECK_EQUAL( expectedAlter, output.alter ); CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); CHECK( expectedAccidental == output.accidental ); From 5f18fb399529931e21132bdb3c93edb8598a4bb4 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sun, 2 Feb 2020 09:28:15 -0800 Subject: [PATCH 09/16] good --- .../private/mxtest/api/PitchDataTest.cpp | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp index 1aca8a36c..c3bc2fc5e 100644 --- a/Sourcecode/private/mxtest/api/PitchDataTest.cpp +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -228,7 +228,7 @@ TEST( AlmostDoubleFlat, PitchData ) } T_END; -TEST( Microtones4, PitchData ) +TEST( CrazyEdgeCase1, PitchData ) { auto input = Input{}; input.step = Step::g; @@ -249,4 +249,25 @@ TEST( Microtones4, PitchData ) } T_END; +TEST( CrazyEdgeCase2, PitchData ) +{ + auto input = Input{}; + input.step = Step::e; + input.alter = 21; + input.cents = 100.01; + input.accidental = Accidental::sori; + const std::string expectedAlterString = "22.0001"; + const int expectedAlter = 22; + const double expectedCents = 0.01; + const Accidental expectedAccidental = input.accidental; + const auto output = pitchDataTest( input ); + + CHECK_EQUAL( expectedAlterString, output.alterString ); + CHECK_EQUAL( expectedAlterString, output.secondAlterString ); + CHECK_EQUAL( expectedAlter, output.alter ); + CHECK_DOUBLES_EQUAL( expectedCents, output.cents, MX_API_EQUALITY_EPSILON ); + CHECK( expectedAccidental == output.accidental ); +} +T_END; + //#endif From 01d8727465d840852a579b40251231efb1cb115b Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sun, 2 Feb 2020 09:29:32 -0800 Subject: [PATCH 10/16] restore compile control --- Sourcecode/private/mxtest/api/PitchDataTest.cpp | 4 ++-- .../private/mxtest/control/CompileControl.h | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp index c3bc2fc5e..f39ea195a 100644 --- a/Sourcecode/private/mxtest/api/PitchDataTest.cpp +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -4,7 +4,7 @@ #include #include "mxtest/control/CompileControl.h" -//#ifdef MX_COMPILE_API_TESTS +#ifdef MX_COMPILE_API_TESTS #include "cpul/cpulTestHarness.h" #include "mx/api/DocumentManager.h" @@ -270,4 +270,4 @@ TEST( CrazyEdgeCase2, PitchData ) } T_END; -//#endif +#endif diff --git a/Sourcecode/private/mxtest/control/CompileControl.h b/Sourcecode/private/mxtest/control/CompileControl.h index 8701bb7ae..3e606fd96 100755 --- a/Sourcecode/private/mxtest/control/CompileControl.h +++ b/Sourcecode/private/mxtest/control/CompileControl.h @@ -4,13 +4,14 @@ #pragma once -//#define MX_COMPILE_API_TESTS -//#define MX_COMPILE_API_ROUNDTRIP -//#define MX_COMPILE_CORE_TESTS -//#define MX_COMPILE_IMPL_TESTS -//#define MX_COMPILE_IMPORT_TESTS -//#define MX_COMPILE_NEW_DECIMAL_TESTS -//#define MX_COMPILE_UTILTIY_TESTS +#define MX_COMPILE_API_TESTS +#define MX_COMPILE_API_ROUNDTRIP +// #define MX_COMPILE_CORE_TESTS +#define MX_COMPILE_IMPL_TESTS +#define MX_COMPILE_IMPORT_TESTS +#define MX_COMPILE_NEW_DECIMAL_TESTS +#define MX_COMPILE_UTILTIY_TESTS +#define MX_COMPILE_XML_TESTS // use this to restrict the size of the files that // the test run will open (compile-time constant). From ab3b1a00fba2489526f4e71c10c5c21e2e146d27 Mon Sep 17 00:00:00 2001 From: Matt Briggs Date: Sun, 2 Feb 2020 09:33:24 -0800 Subject: [PATCH 11/16] clean up --- Sourcecode/private/mxtest/control/CompileControl.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sourcecode/private/mxtest/control/CompileControl.h b/Sourcecode/private/mxtest/control/CompileControl.h index 3e606fd96..a6ce3b23f 100755 --- a/Sourcecode/private/mxtest/control/CompileControl.h +++ b/Sourcecode/private/mxtest/control/CompileControl.h @@ -13,7 +13,6 @@ #define MX_COMPILE_UTILTIY_TESTS #define MX_COMPILE_XML_TESTS -// use this to restrict the size of the files that -// the test run will open (compile-time constant). +// use this to restrict the size of the files that will be processed during the test run. // 0 indicates no limit -constexpr const int MX_COMPILE_MAX_FILE_SIZE_BYTES = 0; // 1024 * 3; // (1024 * 1024); +constexpr const int MX_COMPILE_MAX_FILE_SIZE_BYTES = 0; From 6826516ff2b3bc654918c563fd05f9c275666c18 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs <6260372+webern@users.noreply.github.com> Date: Sun, 2 Feb 2020 10:05:17 -0800 Subject: [PATCH 12/16] Update Sourcecode/private/mxtest/api/NoteDataTest.cpp --- Sourcecode/private/mxtest/api/NoteDataTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Sourcecode/private/mxtest/api/NoteDataTest.cpp b/Sourcecode/private/mxtest/api/NoteDataTest.cpp index 34df1dffc..d2a95ba11 100644 --- a/Sourcecode/private/mxtest/api/NoteDataTest.cpp +++ b/Sourcecode/private/mxtest/api/NoteDataTest.cpp @@ -978,5 +978,4 @@ TEST( notePositionRoundTrip, NoteData ) T_END; // TODO - write PitchData::cents tests - #endif From 38f98f174f5499c60ef657c829fba69cc8c9b99f Mon Sep 17 00:00:00 2001 From: Matthew James Briggs <6260372+webern@users.noreply.github.com> Date: Sun, 2 Feb 2020 10:05:22 -0800 Subject: [PATCH 13/16] Update Sourcecode/private/mxtest/api/NoteDataTest.cpp --- Sourcecode/private/mxtest/api/NoteDataTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Sourcecode/private/mxtest/api/NoteDataTest.cpp b/Sourcecode/private/mxtest/api/NoteDataTest.cpp index d2a95ba11..5d5434190 100644 --- a/Sourcecode/private/mxtest/api/NoteDataTest.cpp +++ b/Sourcecode/private/mxtest/api/NoteDataTest.cpp @@ -977,5 +977,4 @@ TEST( notePositionRoundTrip, NoteData ) } T_END; -// TODO - write PitchData::cents tests #endif From a9b286b52584f06936312ca7a95c30f9444cba07 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs <6260372+webern@users.noreply.github.com> Date: Sun, 2 Feb 2020 10:13:17 -0800 Subject: [PATCH 14/16] Update Sourcecode/private/mxtest/api/PitchDataTest.cpp --- Sourcecode/private/mxtest/api/PitchDataTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Sourcecode/private/mxtest/api/PitchDataTest.cpp b/Sourcecode/private/mxtest/api/PitchDataTest.cpp index f39ea195a..a2c3b0536 100644 --- a/Sourcecode/private/mxtest/api/PitchDataTest.cpp +++ b/Sourcecode/private/mxtest/api/PitchDataTest.cpp @@ -112,6 +112,7 @@ namespace Output output; output.alterString = alterString; +// deserialize back to ScoreData const std::string xml = ss.str(); std::istringstream iss{ xml }; docId = mgr.createFromStream( iss ); From dc9e19e93651176f9ce5a87a7d5777de5068250e Mon Sep 17 00:00:00 2001 From: Matthew James Briggs <6260372+webern@users.noreply.github.com> Date: Sun, 2 Feb 2020 10:13:30 -0800 Subject: [PATCH 15/16] Update Sourcecode/private/mx/impl/NoteReader.cpp --- Sourcecode/private/mx/impl/NoteReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sourcecode/private/mx/impl/NoteReader.cpp b/Sourcecode/private/mx/impl/NoteReader.cpp index d5612b866..4295ea35e 100644 --- a/Sourcecode/private/mx/impl/NoteReader.cpp +++ b/Sourcecode/private/mx/impl/NoteReader.cpp @@ -43,7 +43,7 @@ #include "mx/utility/StringToInt.h" #include -#include +#include "mx/api/PitchData.h" namespace mx { From 5eb02e73842f0034fc47b6172e625028dd54bb4f Mon Sep 17 00:00:00 2001 From: Matthew James Briggs <6260372+webern@users.noreply.github.com> Date: Sun, 2 Feb 2020 10:13:37 -0800 Subject: [PATCH 16/16] Update Sourcecode/include/mx/api/PitchData.h --- Sourcecode/include/mx/api/PitchData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sourcecode/include/mx/api/PitchData.h b/Sourcecode/include/mx/api/PitchData.h index 6febe437b..03a776a7d 100644 --- a/Sourcecode/include/mx/api/PitchData.h +++ b/Sourcecode/include/mx/api/PitchData.h @@ -84,7 +84,7 @@ namespace mx // dealing with integrals for the more common case on non-microtonal music. in order to still support // microtones without resording to a floating-point alter value, we break out microtonal adjustments to a // separate 'cents' field, which will be addeded to the alter integral: - // = (double)alter + (cents * 100.0) + // = (double)alter + (cents / 100.0) double cents; // in MusicXML, the accidental is completely independent of the sounding pitch and is only present when you