From 8542c331d9d13924a804a90e5818bcca98f84d73 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Sun, 7 Apr 2019 18:20:35 -0700 Subject: [PATCH 1/5] work on test options --- .circleci/Dockerfile.test | 3 +- .circleci/config.yml | 5 ++- CMakeLists.txt | 48 +++++++++++++++------- README.md | 5 ++- Sourcecode/mxtest/control/CompileControl.h | 2 +- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/.circleci/Dockerfile.test b/.circleci/Dockerfile.test index ead894ed0..cf4c4e461 100644 --- a/.circleci/Dockerfile.test +++ b/.circleci/Dockerfile.test @@ -3,4 +3,5 @@ FROM matthewjamesbriggs/mxci:v001 ENV BASEDIR="/mx" WORKDIR $BASEDIR COPY . . -CMD cmake . && make -j12 && ./MxTest +CMD cmake -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=on . && make -j12 && ./MxTest +# CMD cmake --version \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml index 958f884af..63c11796a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -45,7 +45,10 @@ jobs: - run: name: Cmake - command: cmake . + command: | + [ "$(git rev-parse --abbrev-ref HEAD)" == "master" ] && \ + cmake -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=on . || \ + cmake -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=off . - run: name: Build diff --git a/CMakeLists.txt b/CMakeLists.txt index f2862bff7..ec7caa949 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,14 @@ cmake_minimum_required(VERSION 2.8.12.2) project(Mx) +option(MX_BUILD_TESTS "build or skip the test suite" OFF) + +option( + MX_BUILD_CORE_TESTS + "build or skip the tests in the 'core' namespace, requires MX_BUILD_TESTS" + OFF +) + #set(CMAKE_BINARY_DIR "./Build") set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) @@ -55,19 +63,31 @@ file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Sourcecode/mxtest/file/PathRoot.h #endif" ) -find_package( Threads ) # MxTest -add_executable(MxTest ${SRC_MX_TEST_API} ${SRC_MX_TEST_CONTROL} ${SRC_MX_TEST_CORE} ${SRC_MX_TEST_FILE} ${SRC_MX_TEST_IMPL} ${SRC_MX_TEST_IMPORT} ${SRC_MX_TEST_UTILITY} ${SRC_MX_TEST_XML} ${SRC_CPUL}) -source_group( "api" FILES ${SRC_MX_TEST_API} ) -source_group( "control" FILES ${SRC_MX_TEST_CONTROL} ) -source_group( "core" FILES ${SRC_MX_TEST_CORE} ) -source_group( "file" FILES ${SRC_MX_TEST_FILE} ) -source_group( "impl" FILES ${SRC_MX_TEST_IMPL} ) -source_group( "import" FILES ${SRC_MX_TEST_IMPORT} ) -source_group( "utility" FILES ${SRC_MX_TEST_UTILITY} ) -source_group( "xml" FILES ${SRC_MX_TEST_XML} ) -source_group( "cpul" FILES ${SRC_CPUL} ) -target_link_libraries(MxTest Mx) -target_link_libraries(MxTest ${CMAKE_THREAD_LIBS_INIT}) -set_property(TARGET MxTest PROPERTY CXX_STANDARD 14) \ No newline at end of file +if(MX_BUILD_TESTS) + message("tests will be compiled") + if(MX_BUILD_CORE_TESTS) + message("core tests will be compiled") + # add_compile_definitions(MX_COMPILE_CORE_TESTS) + add_definitions(-DMX_COMPILE_CORE_TESTS) + else() + message("core tests will not be compiled") + endif() + find_package( Threads ) + add_executable(MxTest ${SRC_MX_TEST_API} ${SRC_MX_TEST_CONTROL} ${SRC_MX_TEST_CORE} ${SRC_MX_TEST_FILE} ${SRC_MX_TEST_IMPL} ${SRC_MX_TEST_IMPORT} ${SRC_MX_TEST_UTILITY} ${SRC_MX_TEST_XML} ${SRC_CPUL}) + source_group( "api" FILES ${SRC_MX_TEST_API} ) + source_group( "control" FILES ${SRC_MX_TEST_CONTROL} ) + source_group( "core" FILES ${SRC_MX_TEST_CORE} ) + source_group( "file" FILES ${SRC_MX_TEST_FILE} ) + source_group( "impl" FILES ${SRC_MX_TEST_IMPL} ) + source_group( "import" FILES ${SRC_MX_TEST_IMPORT} ) + source_group( "utility" FILES ${SRC_MX_TEST_UTILITY} ) + source_group( "xml" FILES ${SRC_MX_TEST_XML} ) + source_group( "cpul" FILES ${SRC_CPUL} ) + target_link_libraries(MxTest Mx) + target_link_libraries(MxTest ${CMAKE_THREAD_LIBS_INIT}) + set_property(TARGET MxTest PROPERTY CXX_STANDARD 14) +else() + message("tests will not be compiled") +endif() diff --git a/README.md b/README.md index f90962267..27b92ac87 100755 --- a/README.md +++ b/README.md @@ -3,12 +3,15 @@ MusicXML Class Library - Author: Matthew James Briggs - License: MIT -- Version: 0.3 +- Version: 1.0 - Supported MusicXML Version: 3.0 - Language: C++14 ----------------------------------------- +Master: [![CircleCI](https://circleci.com/gh/webern/MusicXML-Class-Library/tree/master.svg?style=svg)](https://circleci.com/gh/webern/MusicXML-Class-Library/tree/master) +Develop: [![CircleCI](https://circleci.com/gh/webern/MusicXML-Class-Library/tree/develop.svg?style=svg)](https://circleci.com/gh/webern/MusicXML-Class-Library/tree/develop) + ## Introduction This project is a C++ class library for working with MusicXML files. MusicXML filea are represented in a strongly-typed object-oriented class structure. MusicXML can be deserialized into the class structure and serialized from class structure to MusicXML. diff --git a/Sourcecode/mxtest/control/CompileControl.h b/Sourcecode/mxtest/control/CompileControl.h index bacab4ce7..3e606fd96 100755 --- a/Sourcecode/mxtest/control/CompileControl.h +++ b/Sourcecode/mxtest/control/CompileControl.h @@ -6,7 +6,7 @@ #define MX_COMPILE_API_TESTS #define MX_COMPILE_API_ROUNDTRIP -#define MX_COMPILE_CORE_TESTS +// #define MX_COMPILE_CORE_TESTS #define MX_COMPILE_IMPL_TESTS #define MX_COMPILE_IMPORT_TESTS #define MX_COMPILE_NEW_DECIMAL_TESTS From d154bc8feaf2cd4979751e4055f3a7420ea36020 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Sun, 7 Apr 2019 18:44:49 -0700 Subject: [PATCH 2/5] work on examples --- CMakeLists.txt | 19 +++++++++++++++++++ Sourcecode/mx/examples/Read.cpp | 1 + Sourcecode/mx/examples/Write.cpp | 0 3 files changed, 20 insertions(+) create mode 100644 Sourcecode/mx/examples/Read.cpp create mode 100644 Sourcecode/mx/examples/Write.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec7caa949..67b0e9f6b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,3 +91,22 @@ if(MX_BUILD_TESTS) else() message("tests will not be compiled") endif() + +# MxExamples +if(MX_BUILD_EXAMPLES) + message("examples will be compiled") + find_package( Threads ) + add_executable(MxRead ${SOURCE}/mx/examples/Read.cpp) + add_executable(MxWrite ${SOURCE}/mx/examples/Write.cpp) + target_link_libraries(MxRead Mx ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(MxWrite Mx ${CMAKE_THREAD_LIBS_INIT}) + set_property(TARGET MxRead PROPERTY CXX_STANDARD 14) + set_property(TARGET MxWrite PROPERTY CXX_STANDARD 14) + + # TODO - create a proper 'include' directory + target_include_directories(MxRead ${SOURCE}/mx/api) + target_include_directories(MxWrite ${SOURCE}/mx/api) + +else() + message("examples will not be compiled") +endif() diff --git a/Sourcecode/mx/examples/Read.cpp b/Sourcecode/mx/examples/Read.cpp new file mode 100644 index 000000000..58bee5ead --- /dev/null +++ b/Sourcecode/mx/examples/Read.cpp @@ -0,0 +1 @@ +Read.cpp \ No newline at end of file diff --git a/Sourcecode/mx/examples/Write.cpp b/Sourcecode/mx/examples/Write.cpp new file mode 100644 index 000000000..e69de29bb From 5828e749bf40101d49a0e9094f0d4d4340f94bf9 Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Sun, 7 Apr 2019 20:11:57 -0700 Subject: [PATCH 3/5] work on examples --- .circleci/Dockerfile.test | 9 +- .idea/.name | 1 + .idea/codeStyles/Project.xml | 87 +++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/mx.iml | 2 + .idea/vcs.xml | 6 + CMakeLists.txt | 4 +- README.md | 256 +++++++++++++++++++++++++-- Sourcecode/mx/examples/Read.cpp | 103 ++++++++++- Sourcecode/mx/examples/Write.cpp | 134 ++++++++++++++ Sourcecode/mxtest/file/PathRoot.h | 8 +- 13 files changed, 611 insertions(+), 19 deletions(-) create mode 100644 .idea/.name create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/mx.iml create mode 100644 .idea/vcs.xml diff --git a/.circleci/Dockerfile.test b/.circleci/Dockerfile.test index cf4c4e461..2bf379954 100644 --- a/.circleci/Dockerfile.test +++ b/.circleci/Dockerfile.test @@ -3,5 +3,12 @@ FROM matthewjamesbriggs/mxci:v001 ENV BASEDIR="/mx" WORKDIR $BASEDIR COPY . . -CMD cmake -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=on . && make -j12 && ./MxTest +CMD cmake \ + -DMX_BUILD_TESTS=on \ + -DMX_BUILD_CORE_TESTS=on \ + . && \ + make -j12 && \ + ./MxTest && \ + ./MxRead && \ + ./MxWrite # CMD cmake --version \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 000000000..a6a59ca01 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Mx \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..31c5ccdce --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..8822db8f1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..3c89db64a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/mx.iml b/.idea/mx.iml new file mode 100644 index 000000000..f08604bb6 --- /dev/null +++ b/.idea/mx.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..94a25f7f4 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 67b0e9f6b..8a11fe605 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,8 +104,8 @@ if(MX_BUILD_EXAMPLES) set_property(TARGET MxWrite PROPERTY CXX_STANDARD 14) # TODO - create a proper 'include' directory - target_include_directories(MxRead ${SOURCE}/mx/api) - target_include_directories(MxWrite ${SOURCE}/mx/api) + target_include_directories(MxRead PRIVATE ${SOURCE}/mx/api) + target_include_directories(MxWrite PRIVATE ${SOURCE}/mx/api) else() message("examples will not be compiled") diff --git a/README.md b/README.md index 27b92ac87..ae9162387 100755 --- a/README.md +++ b/README.md @@ -56,6 +56,242 @@ Visual Studio will open with a solution file and be ready to compile. ``` ## Usage + +The `mx::api` namespace is intended to be a (somewhat) simplified wrapping `api` for MusicXML. It should be slightly more intuitive than manipulating the DOM representation directly. + +#### Writing MusicXML with `mx::api` +```C++ +#include +#include +#include +#include + +#include "mx/api/DocumentManager.h" +#include "mx/api/ScoreData.h" + +// set this to 1 if you want to see the xml in your console +#define MX_WRITE_THIS_TO_THE_CONSOLE 1 + +int main(int argc, const char * argv[]) +{ + using namespace mx::api; + const auto qticks = 4; + + // create a score + auto score = ScoreData{}; + score.workTitle = "Mx Example"; + score.composer = "Matthew James Briggs"; + score.copyright = "Copyright (c) 2019"; + score.ticksPerQuarter = qticks; + + // create a part + score.parts.emplace_back( PartData{} ); + auto& part = score.parts.back(); + + // give the part a name + part.name = "Flute"; + part.abbreviation = "Fl."; + part.displayName = "Flute"; + part.displayAbbreviation = "Fl."; + + // give the part an instrument + part.instrumentData.soundID = SoundID::windFlutesFlute; + part.instrumentData.midiData.channel = 1; + part.instrumentData.midiData.program = 74; + + // add a measure + part.measures.emplace_back( MeasureData{} ); + auto& measure = part.measures.back(); + measure.timeSignature.beats = 4; + measure.timeSignature.beatType = 4; + measure.timeSignature.isImplicit = false; + + // add a staff + measure.staves.emplace_back( StaffData{} ); + auto& staff = measure.staves.back(); + + // set the clef + auto clef = ClefData{}; + clef.setTreble(); + staff.clefs.emplace_back( clef ); + + // add a voice + staff.voices[0] = VoiceData{}; + auto& voice = staff.voices.at( 0 ); + + const auto quarter = qticks; + const auto half = qticks * 2; + const auto eighth = qticks / 2; + + // add a few notes + auto currentTime = 0; + auto note = NoteData{}; + note.pitchData.step = Step::d; + note.pitchData.alter = 1; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::sharp; + note.durationData.durationName = DurationName::half; + note.durationData.durationTimeTicks = half; + note.tickTimePosition = currentTime; + voice.notes.push_back( note ); + + // advance our time + currentTime += half; + + note.pitchData.step = Step::e; + note.pitchData.alter = 0; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::none; + note.durationData.durationName = DurationName::eighth; + note.durationData.durationTimeTicks = eighth; + note.tickTimePosition = currentTime; + // beams are handled explicitly in musicxml + note.beams.push_back( Beam::begin ); // start an eighth-note beam + voice.notes.push_back( note ); + currentTime += eighth; + + note.pitchData.step = Step::f; + note.pitchData.alter = 0; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::none; + note.durationData.durationName = DurationName::eighth; + note.tickTimePosition = currentTime; + note.durationData.durationTimeTicks = eighth; + note.beams.clear(); + note.beams.push_back( Beam::end ); // end the eighth-note beam + voice.notes.push_back( note ); + currentTime += eighth; + + note.pitchData.step = Step::e; + note.pitchData.alter = 0; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::none; + note.durationData.durationName = DurationName::quarter; + note.durationData.durationTimeTicks = quarter; + note.tickTimePosition = currentTime; + note.beams.clear(); + voice.notes.push_back( note ); + + // the document manager is the liaison between our score data and the MusicXML DOM. + // it completely hides the MusicXML DOM from us when using mx::api + auto& mgr = DocumentManager::getInstance(); + const auto documentID = mgr.createFromScore( score ); + + // write to the console + #if MX_WRITE_THIS_TO_THE_CONSOLE + mgr.writeToStream( documentID, std::cout ); + std::cout << std::endl; + #endif + + // write to a file + mgr.writeToFile( documentID, "./example.musicxml" ); + + return 0; +} +``` + +#### Reading MusicXML with `mx::api` + +```C++ +#include "mx/api/DocumentManager.h" +#include "mx/api/ScoreData.h" + +#include +#include +#include +#include + +#define MX_IS_A_SUCCESS 0 +#define MX_IS_A_FAILURE 1 + +constexpr const char* const xml = R"( + + + + + + Music + + + + + + 1 + + 0 + + + + G + 2 + + + + + C + 4 + + 4 + whole + + + + +)"; + +int main(int argc, const char * argv[]) +{ + using namespace mx::api; + + // create a reference to the singleton which holds documents in memory for us + auto& mgr = DocumentManager::getInstance(); + + // place the xml from above into a stream object + std::istringstream istr{ xml }; + + // ask the document manager to parse the xml into memory for us, returns a document ID. + const auto documentID = mgr.createFromStream( istr ); + + // get the structural representation of the score from the document manager + const auto score = mgr.getData( documentID ); + + // we need to explicitly destroy the document from memory + mgr.destroyDocument(documentID); + + // make sure we have exactly one part + if( score.parts.size() != 1 ) + { + return MX_IS_A_FAILURE; + } + + // drill down into the data structure to retrieve the note + const auto& part = score.parts.at( 0 ); + const auto& measure = part.measures.at( 0 ); + const auto& staff = measure.staves.at( 0 ); + const auto& voice = staff.voices.at( 0 ); + const auto& note = voice.notes.at( 0 ); + + if( note.durationData.durationName != DurationName::whole ) + { + return MX_IS_A_FAILURE; + } + + if( note.pitchData.step != Step::c ) + { + return MX_IS_A_FAILURE; + } + + return MX_IS_A_SUCCESS; +} +``` + +## Core Implementation Details + The MusicXML classes are tightly bound to the musicxml.xsd specification. MusicXML can be challenging to use and the mx class structure mirrors the complexity of the MusicXML specification. A facade or api for simplifying the interactions with MusicXML documents is planned for future development. ##### Namespaces @@ -417,24 +653,22 @@ int main(int argc, const char * argv[]) ### Unit Test Framework -An executable program named MxTest is also included in the project. MxTest utilizes the CppUnitLite macro library by Michael Feathers. Licensing of this library is not clear, here is a link to the source of this library http://c2.com/cgi/wiki?CppUnitLite +An executable program named MxTest is also included in the project. MxTest utilizes the CppUnitLite macro library by Michael Feathers. Licensing of this library is not clear, [here is a link](http://c2.com/cgi/wiki?CppUnitLite) to the source of this library. CppUnitLite appears to be abandonware, but it is very useful. Here are some additional CppUnitLite links -http://www.objectmentor.com/resources/downloads.html -https://github.com/smikes/CppUnitLite + * [http://www.objectmentor.com/resources/downloads.html](http://www.objectmentor.com/resources/downloads.html) + * [https://github.com/smikes/CppUnitLite](https://github.com/smikes/CppUnitLite) + * [https://github.com/webern/CppUnitLite](https://github.com/webern/CppUnitLite) The tests are slow to compile, see the *Compiling* section for more info on how to skip compilation of the tests. -### Known Issues and Todo List -- There is no easy way to "deep copy" anything. Each element and attributes class needs a "clone" function. -- XsID uniqueness is not constrained. Should it be? -- XsIDREF's, especially in the ScorePartList are not constrained to relate to the PartList XsID's. Thus it is possible to create an unparsable MusicXML document. A feature could be added to enforce this one-to-one constraint. Should this feature be added in the core model? Maybe not. -- A simplified and stable facade API should be added to make it much, much easier to understand and interact with MusicXML documents. +### Release Notes -### Historical Notes and Release Notes -**Historical Note: October 6, 2016:** Significant progress has been made on the `api` namespace, which is a simplified set of data structures to represent a MusicXML document. These are being implemented as mostly-POD structs. Currently the importing of data into these structures is well-underway, but the exporting from these data structures has not been implemented. + * **Version 1.0** April 2019: A more-or-less stable version of `mx::api` + * **Version 0.3** Early 2019 + * **Version 0.2** August 21, 2016 Adds the ability to import MusicXML documents into MusicXML Classes. -**Release: Version 0.2 August 21, 2016** Adds the ability to import MusicXML documents into MusicXML Classes. +**Historical Note: October 6, 2016:** Significant progress has been made on the `api` namespace, which is a simplified set of data structures to represent a MusicXML document. These are being implemented as mostly-POD structs. Currently the importing of data into these structures is well-underway, but the exporting from these data structures has not been implemented. **Historical Note: August 16, 2016:** All tests are passing (core, xml and import). The remaining items to do on the ximport feature are - search for all `\\TODO's` and fix those that can be fixed diff --git a/Sourcecode/mx/examples/Read.cpp b/Sourcecode/mx/examples/Read.cpp index 58bee5ead..2a60ff8f6 100644 --- a/Sourcecode/mx/examples/Read.cpp +++ b/Sourcecode/mx/examples/Read.cpp @@ -1 +1,102 @@ -Read.cpp \ No newline at end of file +#include +#include +#include +#include + +// TODO - create a proper include folder +// normally we would want to include like this +// #include "mx/api/DocumentManager.h" +// however because our cmake system is a little bit +// broken, we are importing from the mx/api directory +// directly. + +#include "DocumentManager.h" +#include "ScoreData.h" + +#define MX_IS_A_SUCCESS 0 +#define MX_IS_A_FAILURE 1 + +constexpr const char* const xml = R"( + + + + + + Music + + + + + + 1 + + 0 + + + + G + 2 + + + + + C + 4 + + 4 + whole + + + + +)"; + +int main(int argc, const char * argv[]) +{ + using namespace mx::api; + + // create a reference to the singleton which holds documents in memory for us + auto& mgr = DocumentManager::getInstance(); + + // place the xml from above into a stream object + std::istringstream istr{ xml }; + + // ask the document manager to parse the xml into memory for us, returns a document ID. + const auto documentID = mgr.createFromStream( istr ); + + // get the structural representation of the score from the document manager + const auto score = mgr.getData( documentID ); + + // we need to explicitly destroy the document from memory + mgr.destroyDocument(documentID); + + // make sure we have exactly one part + if( score.parts.size() != 1 ) + { + return MX_IS_A_FAILURE; + } + + // drill down into the data structure to retrieve the note + const auto& part = score.parts.at( 0 ); + const auto& measure = part.measures.at( 0 ); + const auto& staff = measure.staves.at( 0 ); + const auto& voice = staff.voices.at( 0 ); + const auto& note = voice.notes.at( 0 ); + + if( note.durationData.durationName != DurationName::whole ) + { + return MX_IS_A_FAILURE; + } + + if( note.pitchData.step != Step::c ) + { + return MX_IS_A_FAILURE; + } + + return MX_IS_A_SUCCESS; +} \ No newline at end of file diff --git a/Sourcecode/mx/examples/Write.cpp b/Sourcecode/mx/examples/Write.cpp index e69de29bb..2dc1cf05d 100644 --- a/Sourcecode/mx/examples/Write.cpp +++ b/Sourcecode/mx/examples/Write.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include + +// TODO - create a proper include folder +// normally we would want to include like this +// #include "mx/api/DocumentManager.h" +// however because our cmake system is a little bit +// broken, we are importing from the mx/api directory +// directly. + +#include "DocumentManager.h" +#include "ScoreData.h" + +// set this to 1 if you want to see the xml in your console +#define MX_WRITE_THIS_TO_THE_CONSOLE 1 + +int main(int argc, const char * argv[]) +{ + using namespace mx::api; + const auto qticks = 4; + + // create a score + auto score = ScoreData{}; + score.workTitle = "Mx Example"; + score.composer = "Matthew James Briggs"; + score.copyright = "Copyright (c) 2019"; + score.ticksPerQuarter = qticks; + + // create a part + score.parts.emplace_back( PartData{} ); + auto& part = score.parts.back(); + + // give the part a name + part.name = "Flute"; + part.abbreviation = "Fl."; + part.displayName = "Flute"; + part.displayAbbreviation = "Fl."; + + // give the part an instrument + part.instrumentData.soundID = SoundID::windFlutesFlute; + part.instrumentData.midiData.channel = 1; + part.instrumentData.midiData.program = 74; + + // add a measure + part.measures.emplace_back( MeasureData{} ); + auto& measure = part.measures.back(); + measure.timeSignature.beats = 4; + measure.timeSignature.beatType = 4; + measure.timeSignature.isImplicit = false; + + // add a staff + measure.staves.emplace_back( StaffData{} ); + auto& staff = measure.staves.back(); + + // set the clef + auto clef = ClefData{}; + clef.setTreble(); + staff.clefs.emplace_back( clef ); + + // add a voice + staff.voices[0] = VoiceData{}; + auto& voice = staff.voices.at( 0 ); + + const auto quarter = qticks; + const auto half = qticks * 2; + const auto eighth = qticks / 2; + + // add a few notes + auto currentTime = 0; + auto note = NoteData{}; + note.pitchData.step = Step::d; + note.pitchData.alter = 1; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::sharp; + note.durationData.durationName = DurationName::half; + note.durationData.durationTimeTicks = half; + note.tickTimePosition = currentTime; + voice.notes.push_back( note ); + + // advance our time + currentTime += half; + + note.pitchData.step = Step::e; + note.pitchData.alter = 0; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::none; + note.durationData.durationName = DurationName::eighth; + note.durationData.durationTimeTicks = eighth; + note.tickTimePosition = currentTime; + // beams are handled explicitly in musicxml + note.beams.push_back( Beam::begin ); // start an eighth-note beam + voice.notes.push_back( note ); + currentTime += eighth; + + note.pitchData.step = Step::f; + note.pitchData.alter = 0; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::none; + note.durationData.durationName = DurationName::eighth; + note.tickTimePosition = currentTime; + note.durationData.durationTimeTicks = eighth; + note.beams.clear(); + note.beams.push_back( Beam::end ); // end the eighth-note beam + voice.notes.push_back( note ); + currentTime += eighth; + + note.pitchData.step = Step::e; + note.pitchData.alter = 0; + note.pitchData.octave = 5; + note.pitchData.accidental = Accidental::none; + note.durationData.durationName = DurationName::quarter; + note.durationData.durationTimeTicks = quarter; + note.tickTimePosition = currentTime; + note.beams.clear(); + voice.notes.push_back( note ); + + // the document manager is the liaison between our score data and the MusicXML DOM. + // it completely hides the MusicXML DOM from us when using mx::api + auto& mgr = DocumentManager::getInstance(); + const auto documentID = mgr.createFromScore( score ); + + // write to the console + #if MX_WRITE_THIS_TO_THE_CONSOLE + mgr.writeToStream( documentID, std::cout ); + std::cout << std::endl; + #endif + + // write to a file + mgr.writeToFile( documentID, "./example.musicxml" ); + + return 0; +} \ No newline at end of file diff --git a/Sourcecode/mxtest/file/PathRoot.h b/Sourcecode/mxtest/file/PathRoot.h index b2de5688b..d078801ca 100644 --- a/Sourcecode/mxtest/file/PathRoot.h +++ b/Sourcecode/mxtest/file/PathRoot.h @@ -1,13 +1,13 @@ -// This file is auto generated -// To override these paths, pass in these macro definitions to the compiler. +// This file is auto generated by CMake +// To override these paths, pass in macro definitions to the compiler. #pragma once // The absolute path to the root of the repository. #ifndef MX_REPO_ROOT_PATH -#define MX_REPO_ROOT_PATH "/Users/mjb/Documents/repos/mx" +#define MX_REPO_ROOT_PATH "/Users/mjb/repos/mx" #endif // The absolute path to the binary output directory. #ifndef MX_BINARY_OUTPUT_PATH -#define MX_BINARY_OUTPUT_PATH "/Users/mjb/Documents/repos/mx/cmake-build-debug" +#define MX_BINARY_OUTPUT_PATH "/Users/mjb/repos/mx/cmake-build-debug" #endif \ No newline at end of file From 2873a68670ab4f8442abe7d40801bda1948a977d Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Sun, 7 Apr 2019 20:16:15 -0700 Subject: [PATCH 4/5] build examples on circleci --- .circleci/Dockerfile.test | 1 + .circleci/config.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/Dockerfile.test b/.circleci/Dockerfile.test index 2bf379954..0071a1157 100644 --- a/.circleci/Dockerfile.test +++ b/.circleci/Dockerfile.test @@ -6,6 +6,7 @@ COPY . . CMD cmake \ -DMX_BUILD_TESTS=on \ -DMX_BUILD_CORE_TESTS=on \ + -DMX_BUILD_EXAMPLES=on \ . && \ make -j12 && \ ./MxTest && \ diff --git a/.circleci/config.yml b/.circleci/config.yml index 63c11796a..74237e8fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,8 +47,8 @@ jobs: name: Cmake command: | [ "$(git rev-parse --abbrev-ref HEAD)" == "master" ] && \ - cmake -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=on . || \ - cmake -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=off . + cmake -DMX_BUILD_EXAMPLES=on -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=on . || \ + cmake -DMX_BUILD_EXAMPLES=on -DMX_BUILD_TESTS=on -DMX_BUILD_CORE_TESTS=off . - run: name: Build From 1f7cda2013b72a5d3c72706de88086155bc1990d Mon Sep 17 00:00:00 2001 From: Matthew James Briggs Date: Sun, 7 Apr 2019 20:17:38 -0700 Subject: [PATCH 5/5] run examples on circleci --- .circleci/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 74237e8fd..f7cee6eaf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,6 +54,10 @@ jobs: name: Build command: make + - run: + name: Run Examples + command: ./MxRead && ./MxWrite + - run: name: Test command: ./MxTest