diff --git a/include/lomse_ldp_analyser.h b/include/lomse_ldp_analyser.h index ae931a16..c4b696c0 100644 --- a/include/lomse_ldp_analyser.h +++ b/include/lomse_ldp_analyser.h @@ -103,6 +103,19 @@ class SlursBuilder : public RelationBuilder }; +//--------------------------------------------------------------------------------------- +// helper class to save octave-shift info items, match them and build the octave-shift lines +class OctaveShiftBuilder : public RelationBuilder +{ +public: + OctaveShiftBuilder(ostream& reporter, LdpAnalyser* pAnalyser) + : RelationBuilder(reporter, pAnalyser, "octaveShift", "octave-shift") {} + virtual ~OctaveShiftBuilder() {} + + void add_relation_to_staffobjs(ImoOctaveShiftDto* pEndInfo) override; +}; + + //--------------------------------------------------------------------------------------- // helper class to save beam info items, match them and build the beams @@ -174,6 +187,7 @@ class LdpAnalyser : public Analyser OldBeamsBuilder* m_pOldBeamsBuilder; TupletsBuilder* m_pTupletsBuilder; SlursBuilder* m_pSlursBuilder; + OctaveShiftBuilder* m_pOctaveShifBuilder; map m_lyricIndex; vector m_lyrics; vector m_lyricsPlacement; diff --git a/include/lomse_ldp_elements.h b/include/lomse_ldp_elements.h index 55d9f7ac..9d8bcfa8 100644 --- a/include/lomse_ldp_elements.h +++ b/include/lomse_ldp_elements.h @@ -65,6 +65,7 @@ enum ELdpElement k_textbox, k_line, k_lyric, + k_octave_shift, k_tie, k_tuplet, //pauses and breath marks diff --git a/src/parser/ldp/lomse_ldp_analyser.cpp b/src/parser/ldp/lomse_ldp_analyser.cpp index b63b2b26..2abf021a 100644 --- a/src/parser/ldp/lomse_ldp_analyser.cpp +++ b/src/parser/ldp/lomse_ldp_analyser.cpp @@ -3700,6 +3700,7 @@ class NoteRestAnalyser : public ElementAnalyser ImoTupletDto* m_pTupletInfo; ImoBeamDto* m_pBeamInfo; ImoSlurDto* m_pSlurDto; + ImoOctaveShiftDto* m_pOctaveShiftDto; ImoFermata* m_pFermata; ImoTimeModificationDto* m_pTimeModifDto; std::string m_srcOldBeam; @@ -3713,6 +3714,7 @@ class NoteRestAnalyser : public ElementAnalyser , m_pTupletInfo(nullptr) , m_pBeamInfo(nullptr) , m_pSlurDto(nullptr) + , m_pOctaveShiftDto(nullptr) , m_pFermata(nullptr) , m_pTimeModifDto(nullptr) , m_srcOldBeam("") @@ -3853,6 +3855,9 @@ class NoteRestAnalyser : public ElementAnalyser //slur add_slur_info(pNote); + //octave-shift + add_octave_shift_info(pNR); + //chord if (!fIsRest && fInChord) { @@ -3880,7 +3885,7 @@ class NoteRestAnalyser : public ElementAnalyser void analyse_note_rest_options(ImoNoteRest* pNR) { - // { | | | } + // { | | | | } while( more_params_to_analyse() ) { @@ -3905,6 +3910,10 @@ class NoteRestAnalyser : public ElementAnalyser { set_voice_element(pNR); } + else if (type == k_octave_shift) + { + m_pOctaveShiftDto = static_cast( proceed(k_octave_shift, nullptr) ); + } else break; @@ -4203,6 +4212,112 @@ class NoteRestAnalyser : public ElementAnalyser } } + void add_octave_shift_info(ImoNoteRest* pNR) + { + if (m_pOctaveShiftDto) + { + m_pOctaveShiftDto->set_staffobj(pNR); + m_pAnalyser->add_relation_info(m_pOctaveShiftDto); + } + } +}; + + +//@-------------------------------------------------------------------------------------- +//@ = (octaveShift num [][color] ) +//@ num = octaveShift number. integer +//@ = { start | stop } +//@ = { 8u | 8d | 15u | 15d } +//@ +//@ Example: +//@ (octaveShift 18 start 8d) +//@ + +class octaveShiftAnalyser : public ElementAnalyser +{ +protected: + ImoOctaveShiftDto* m_pInfo = nullptr; + +public: + octaveShiftAnalyser(LdpAnalyser* pAnalyser, ostream& reporter, LibraryScope& libraryScope, + ImoObj* pAnchor) + : ElementAnalyser(pAnalyser, reporter, libraryScope, pAnchor) {} + + void do_analysis() override + { + Document* pDoc = m_pAnalyser->get_document_being_analysed(); + m_pInfo = static_cast( + ImFactory::inject(k_imo_octave_shift_dto, pDoc)); + m_pInfo->set_id( get_node_id() ); + m_pInfo->set_line_number( m_pAnalysedNode->get_line_number() ); + + // num + if (get_mandatory(k_number)) + m_pInfo->set_octave_shift_number( get_integer_value(0) ); + + // (label) + if (!get_mandatory(k_label) || !set_start_stop()) + { + error_msg("Missing or invalid value for octaveShift start/stop. Octave-shift ignored."); + delete m_pInfo; + return; + } + + // (label) + if (m_pInfo->is_start()) + { + if (get_optional(k_label)) + { + set_octave_shift_type(); + + // [] + if (get_optional(k_color)) + m_pInfo->set_color( get_color_param() ); + } + } + else + { +// int id = m_pAnalyser->get_octave_shift_id_and_close(num); + } + + error_if_more_elements(); + + m_pAnalysedNode->set_imo(m_pInfo); + } + +protected: + + bool set_start_stop() + { + const std::string& value = m_pParamToAnalyse->get_value(); + if (value == "start") + m_pInfo->set_start(true); + else if (value == "stop") + m_pInfo->set_start(false); + else + return false; //error + return true; //ok + } + + void set_octave_shift_type() + { + const std::string& value = m_pParamToAnalyse->get_value(); + int size = 0; //any value, just to initialize it + if (value == "8u") + size =-7; + else if (value == "8d") + size = 7; + else if (value == "15u") + size = -14; + else if (value == "15d") + size = 14; + else + { + error_msg("Invalid octave-shift size '" + value + "'. Changed to 8u."); + size = -7; + } + m_pInfo->set_shift_steps(size); + } }; //@-------------------------------------------------------------------------------------- @@ -6449,6 +6564,7 @@ LdpAnalyser::LdpAnalyser(ostream& reporter, LibraryScope& libraryScope, Document , m_pOldBeamsBuilder(nullptr) , m_pTupletsBuilder(nullptr) , m_pSlursBuilder(nullptr) + , m_pOctaveShifBuilder(nullptr) , m_pCurScore(nullptr) , m_pLastScore(nullptr) , m_pImoDoc(nullptr) @@ -6495,6 +6611,7 @@ void LdpAnalyser::delete_relation_builders() delete m_pOldBeamsBuilder; delete m_pTupletsBuilder; delete m_pSlursBuilder; + delete m_pOctaveShifBuilder; } //--------------------------------------------------------------------------------------- @@ -6507,6 +6624,7 @@ ImoObj* LdpAnalyser::analyse_tree_and_get_object(LdpTree* tree) m_pOldBeamsBuilder = LOMSE_NEW OldBeamsBuilder(m_reporter, this); m_pTupletsBuilder = LOMSE_NEW TupletsBuilder(m_reporter, this); m_pSlursBuilder = LOMSE_NEW SlursBuilder(m_reporter, this); + m_pOctaveShifBuilder = LOMSE_NEW OctaveShiftBuilder(m_reporter, this); m_pTree = tree; m_curStaff = 0; @@ -6723,6 +6841,8 @@ void LdpAnalyser::add_relation_info(ImoObj* pDto) m_pBeamsBuilder->add_item_info(static_cast(pDto)); else if (pDto->is_tuplet_dto()) m_pTupletsBuilder->add_item_info(static_cast(pDto)); + else if (pDto->is_octave_shift_dto()) + m_pOctaveShifBuilder->add_item_info(static_cast(pDto)); } //--------------------------------------------------------------------------------------- @@ -6733,6 +6853,7 @@ void LdpAnalyser::clear_pending_relations() m_pBeamsBuilder->clear_pending_items(); m_pOldBeamsBuilder->clear_pending_old_beams(); m_pTupletsBuilder->clear_pending_items(); + m_pOctaveShifBuilder->clear_pending_items(); m_lyrics.clear(); m_lyricIndex.clear(); @@ -7101,6 +7222,7 @@ ElementAnalyser* LdpAnalyser::new_analyser(ELdpElement type, ImoObj* pAnchor) case k_name: return LOMSE_NEW InstrNameAnalyser(this, m_reporter, m_libraryScope, pAnchor); case k_newSystem: return LOMSE_NEW ControlAnalyser(this, m_reporter, m_libraryScope, pAnchor); case k_note: return LOMSE_NEW NoteRestAnalyser(this, m_reporter, m_libraryScope, pAnchor); + case k_octave_shift: return LOMSE_NEW octaveShiftAnalyser(this, m_reporter, m_libraryScope, pAnchor); case k_opt: return LOMSE_NEW OptAnalyser(this, m_reporter, m_libraryScope, pAnchor); case k_orderedlist: return LOMSE_NEW ListAnalyser(this, m_reporter, m_libraryScope, pAnchor); case k_pageLayout: return LOMSE_NEW PageLayoutAnalyser(this, m_reporter, m_libraryScope, pAnchor); @@ -7314,6 +7436,33 @@ void SlursBuilder::add_relation_to_staffobjs(ImoSlurDto* pEndInfo) +//======================================================================================= +// OctaveShiftBuilder implementation +//======================================================================================= +void OctaveShiftBuilder::add_relation_to_staffobjs(ImoOctaveShiftDto* pEndInfo) +{ + m_matches.push_back(pEndInfo); + Document* pDoc = m_pAnalyser->get_document_being_analysed(); + + ImoOctaveShiftDto* pStartInfo = m_matches.front(); + ImoOctaveShift* pShift = static_cast( + ImFactory::inject(k_imo_octave_shift, pDoc, pStartInfo->get_id()) ); + pShift->set_octave_shift_number( pStartInfo->get_octave_shift_number() ); + pShift->set_shift_steps( pStartInfo->get_shift_steps() ); + pShift->set_color( pStartInfo->get_color() ); + + std::list::iterator it; + for (it = m_matches.begin(); it != m_matches.end(); ++it) + { + ImoNoteRest* pNR = (*it)->get_staffobj(); + //ImoOctaData* pData = ImFactory::inject_slur_data(pDoc, *it); + //pNote->include_in_relation(pShift, pData); + pNR->include_in_relation(pShift, nullptr); + } +} + + + //======================================================================================= // BeamsBuilder implementation //======================================================================================= diff --git a/src/parser/lomse_ldp_factory.cpp b/src/parser/lomse_ldp_factory.cpp index eec45977..817d5e59 100644 --- a/src/parser/lomse_ldp_factory.cpp +++ b/src/parser/lomse_ldp_factory.cpp @@ -161,7 +161,8 @@ LdpFactory::LdpFactory() m_TypeToName[k_no] = "no"; m_TypeToName[k_normal] = "normal"; m_TypeToName[k_note] = "n"; //note - m_TypeToName[k_opt] = "opt"; + m_TypeToName[k_slur] = "slur"; + m_TypeToName[k_octave_shift] = "octaveShift"; m_TypeToName[k_orderedlist] = "orderedlist"; m_TypeToName[k_padding] = "padding"; m_TypeToName[k_padding_top] = "padding-top"; @@ -368,6 +369,7 @@ LdpFactory::LdpFactory() m_NameToFunctor["newSystem"] = LOMSE_NEW LdpElementFunctor; m_NameToFunctor["no"] = LOMSE_NEW LdpElementFunctor; m_NameToFunctor["normal"] = LOMSE_NEW LdpElementFunctor; + m_NameToFunctor["octaveShift"] = LOMSE_NEW LdpElementFunctor; m_NameToFunctor["opt"] = LOMSE_NEW LdpElementFunctor; m_NameToFunctor["orderedlist"] = LOMSE_NEW LdpElementFunctor; m_NameToFunctor["padding"] = LOMSE_NEW LdpElementFunctor; diff --git a/src/tests/lomse_test_ldp_analyser.cpp b/src/tests/lomse_test_ldp_analyser.cpp index 8e95a8ee..3e4807b7 100644 --- a/src/tests/lomse_test_ldp_analyser.cpp +++ b/src/tests/lomse_test_ldp_analyser.cpp @@ -2042,6 +2042,175 @@ SUITE(LdpAnalyserTest) if (pRoot && !pRoot->is_document()) delete pRoot; } + // octaveShift ---------------------------------------------------------------------- + + TEST_FIXTURE(LdpAnalyserTestFixture, octaveShift_01) + { + //@01. octaveShift start: minimum content parsed ok + + stringstream errormsg; + Document doc(m_libraryScope); + LdpParser parser(errormsg, m_libraryScope.ldp_factory()); + stringstream expected; + //expected << "Line 0. " << endl; + parser.parse_text("(octaveShift 18 start 8d)"); + LdpTree* tree = parser.get_ldp_tree(); + LdpAnalyser a(errormsg, m_libraryScope, &doc); + ImoObj* pRoot = a.analyse_tree(tree, "string:"); + CHECK( pRoot->is_octave_shift_dto() == true ); + CHECK( check_errormsg(errormsg, expected) ); + ImoOctaveShiftDto* pInfo = dynamic_cast( pRoot ); + CHECK( pInfo != nullptr ); + if (pInfo) + { + CHECK( pInfo->is_start() == true ); + CHECK( pInfo->get_octave_shift_number() == 18 ); + CHECK( pInfo->get_shift_steps() == 7 ); + CHECK( !is_different(pInfo->get_color(), Color(0,0,0)) ); + CHECK( pInfo->get_staff() == 0 ); + } + + delete tree->get_root(); + // coverity[check_after_deref] + if (pRoot && !pRoot->is_document()) delete pRoot; + } + + TEST_FIXTURE(LdpAnalyserTestFixture, octaveShift_02) + { + //@02. invalid shift octaves + + stringstream errormsg; + Document doc(m_libraryScope); + LdpParser parser(errormsg, m_libraryScope.ldp_factory()); + stringstream expected; + expected << "Line 0. Invalid octave-shift size '12u'. Changed to 8u." << endl; + parser.parse_text("(octaveShift 18 start 12u)"); + LdpTree* tree = parser.get_ldp_tree(); + LdpAnalyser a(errormsg, m_libraryScope, &doc); + ImoObj* pRoot = a.analyse_tree(tree, "string:"); + CHECK( pRoot->is_octave_shift_dto() == true ); + CHECK( check_errormsg(errormsg, expected) ); + ImoOctaveShiftDto* pInfo = dynamic_cast( pRoot ); + CHECK( pInfo != nullptr ); + if (pInfo) + { + CHECK( pInfo->is_start() == true ); + CHECK( pInfo->get_octave_shift_number() == 18 ); + CHECK( pInfo->get_shift_steps() == -7 ); + CHECK( !is_different(pInfo->get_color(), Color(0,0,0)) ); + CHECK( pInfo->get_staff() == 0 ); + } + + delete tree->get_root(); + // coverity[check_after_deref] + if (pRoot && !pRoot->is_document()) delete pRoot; + } + + TEST_FIXTURE(LdpAnalyserTestFixture, octaveShift_03) + { + //@03. invalid start/stop value + + stringstream errormsg; + Document doc(m_libraryScope); + LdpParser parser(errormsg, m_libraryScope.ldp_factory()); + stringstream expected; + expected << "Line 0. Missing or invalid value for octaveShift start/stop. Octave-shift ignored." << endl; + parser.parse_text("(octaveShift 18 end)"); + LdpTree* tree = parser.get_ldp_tree(); + LdpAnalyser a(errormsg, m_libraryScope, &doc); + ImoObj* pRoot = a.analyse_tree(tree, "string:"); + CHECK( pRoot == nullptr ); + CHECK( check_errormsg(errormsg, expected) ); + + delete tree->get_root(); + } + + TEST_FIXTURE(LdpAnalyserTestFixture, octaveShift_04) + { + //@04. Octave-shift relationship correctly built + + stringstream errormsg; + Document doc(m_libraryScope); + LdpParser parser(errormsg, m_libraryScope.ldp_factory()); + stringstream expected; + //expected << "Line 0. " << endl; + parser.parse_text("(musicData (n c4 q (octaveShift 12 start 15u)) (n c4 e (octaveShift 12 stop)))"); + LdpTree* tree = parser.get_ldp_tree(); + LdpAnalyser a(errormsg, m_libraryScope, &doc); + ImoObj* pRoot = a.analyse_tree(tree, "string:"); + + CHECK( check_errormsg(errormsg, expected) ); + + ImoMusicData* pMusic = static_cast( pRoot ); + CHECK( pMusic != nullptr ); + ImoObj::children_iterator it = pMusic->begin(); + ImoNote* pNote = dynamic_cast( *it ); + ImoOctaveShift* pShift = static_cast( pNote->find_relation(k_imo_octave_shift) ); + CHECK( pShift->get_octave_shift_number() == 12 ); + CHECK( pShift->get_num_objects() == 2 ); + CHECK( pShift->get_shift_steps() == -14 ); + CHECK( !is_different(pShift->get_color(), Color(0,0,0)) ); + + ImoNote* pStartNote = dynamic_cast( pShift->get_start_object() ); + ImoNote* pEndNote = dynamic_cast( pShift->get_end_object() ); + + ImoNote* pNote1 = dynamic_cast( *it ); + CHECK( pNote1 == pStartNote ); + + ++it; + ImoNote* pNote2 = dynamic_cast( *it ); + CHECK( pNote2 == pEndNote ); + + + delete tree->get_root(); + // coverity[check_after_deref] + if (pRoot && !pRoot->is_document()) delete pRoot; + } + + TEST_FIXTURE(LdpAnalyserTestFixture, octaveShift_05) + { + //@05. Octave-shift has color + + stringstream errormsg; + Document doc(m_libraryScope); + LdpParser parser(errormsg, m_libraryScope.ldp_factory()); + stringstream expected; + //expected << "Line 0. " << endl; + parser.parse_text("(musicData (n c4 q (octaveShift 5 start 8d (color #ff0000))) " + "(n c4 e (octaveShift 5 stop)))"); + LdpTree* tree = parser.get_ldp_tree(); + LdpAnalyser a(errormsg, m_libraryScope, &doc); + ImoObj* pRoot = a.analyse_tree(tree, "string:"); + + CHECK( check_errormsg(errormsg, expected) ); + + ImoMusicData* pMusic = static_cast( pRoot ); + CHECK( pMusic != nullptr ); + ImoObj::children_iterator it = pMusic->begin(); + ImoNote* pNote = dynamic_cast( *it ); + ImoOctaveShift* pShift = static_cast( pNote->find_relation(k_imo_octave_shift) ); + CHECK( pShift->get_octave_shift_number() == 5 ); + CHECK( pShift->get_num_objects() == 2 ); + CHECK( pShift->get_shift_steps() == 7 ); + CHECK( !is_different(pShift->get_color(), Color(255,0,0)) ); + + ImoNote* pStartNote = dynamic_cast( pShift->get_start_object() ); + ImoNote* pEndNote = dynamic_cast( pShift->get_end_object() ); + + ImoNote* pNote1 = dynamic_cast( *it ); + CHECK( pNote1 == pStartNote ); + + ++it; + ImoNote* pNote2 = dynamic_cast( *it ); + CHECK( pNote2 == pEndNote ); + + + delete tree->get_root(); + // coverity[check_after_deref] + if (pRoot && !pRoot->is_document()) delete pRoot; + } + + // rest ----------------------------------------------------------------------------- TEST_FIXTURE(LdpAnalyserTestFixture, Analyser_Rest) diff --git a/test-scores/01050-octave-shift.lms b/test-scores/01050-octave-shift.lms new file mode 100644 index 00000000..f2e97d76 --- /dev/null +++ b/test-scores/01050-octave-shift.lms @@ -0,0 +1,19 @@ +(score (vers 2.0) (instrument (musicData + (clef G) + (time 4 4) + (n e6 q (octaveShift 1 start 15u)) + (n g6 q (stem down)) + (n c6 q) + (n e6 q) + (barline) + (n c6 q) + (n g6 q (stem down)(octaveShift 1 stop)) + (n e4 q (stem down)(octaveShift 2 start 8d)) + (n g3 q (stem down)) + (barline) + (n c4 q) + (n e4 q) + (n c4 q) + (n g3 q (stem down)(octaveShift 2 stop)) + (barline end) +))) diff --git a/test-scores/01051-octave-shift.lms b/test-scores/01051-octave-shift.lms new file mode 100644 index 00000000..3317823b --- /dev/null +++ b/test-scores/01051-octave-shift.lms @@ -0,0 +1,85 @@ +(score (vers 2.0) (instrument (staves 2) (musicData + (clef G p1) + (clef F4 p2) + (time 4 4) + (n e6 q v1 p1 (octaveShift 1 start 15u)) + (n g6 q) + (n c7 q) + (n e7 q) + (r q v2 p2 (octaveShift 2 start 15u)) + (n g5 q) + (n c5 q) + (r q (octaveShift 2 stop)) + (barline) + + (n c7 q v1 p1) + (n g6 q (octaveShift 1 stop)) + (n g4 q (octaveShift 3 start 8d)) + (n e4 q (octaveShift 3 stop)) + (n c3 q v2 p2) + (n g3 q) + (n e3 q) + (n g1 q (octaveShift 4 start 15d (color #ff0000))) + (barline) + + (n c5 q v1 p1) + (n e5 q) + (n c5 q) + (n g4 q) + (n c2 q v2 p2 (octaveShift 4 stop)) + (n g3 q) + (n c3 q (octaveShift 5 start 8u (color #4bc11f))) + (n g2 q (octaveShift 5 stop)) + (barline) + + (n c5 q v1 p1) + (n g6 q (octaveShift 6 start 8u)) + (n g5 q) + (n e5 q) + (n c3 q v2 p2) + (n g3 q) + (n e2 q (octaveShift 7 start 8d)) + (n c2 q) + (barline) + + (n c6 q v1 p1) + (n d6 q) + (n e6 q) + (n f6 q) + (n c2 q v2 p2) + (n d2 q) + (n e2 q) + (n f2 q) + (barline) + + (n c6 q v1 p1) + (n d6 q) + (n e6 q) + (n f6 q (octaveShift 6 stop)) + (n c2 q v2 p2) + (n d2 q) + (n e2 q) + (n f2 q) + (barline) + + (n c5 q v1 p1) + (n d5 q) + (n e5 q) + (n f5 q) + (n c2 q v2 p2) + (n d2 q) + (n e2 q (octaveShift 7 stop)) + (n f3 q) + (barline) + + + (n c5 q v1 p1) + (n d5 q) + (n e5 q) + (n f5 q) + (n c3 q v2 p2) + (n d3 q) + (n e3 q) + (n f3 q) + (barline end) +)))