Skip to content

Commit

Permalink
implement translation of non-traditional keys
Browse files Browse the repository at this point in the history
  • Loading branch information
webern committed Apr 1, 2020
1 parent c04e527 commit ecf149b
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 57 deletions.
10 changes: 5 additions & 5 deletions Sourcecode/include/mx/api/KeyComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace mx
{

// KeyComponent is only used for non-traditional key signatures. It facilitates specifying the
// exact accidentals that are found in a key signature.
// exact accidentals that are found in the key signature.
struct KeyComponent
{
// The note name that is to be altered by the key signature.
Expand All @@ -21,9 +21,9 @@ namespace mx
// The amount that notes of this note name should be altered, in semitones.
int alter;

// Additional amount that notes of this note name should be altered, in cents. If alter = 1 and alterCents =
// 25.1 then the note is altered by 1.251 semitones in total.
double alterCents;
// Additional amount that notes of this note name should be altered, in cents. If alter = 1 and cents = 25.1
// then the note is altered by 1.251 semitones in total.
double cents;

// The accidental to display for this note name.
Accidental accidental;
Expand All @@ -33,7 +33,7 @@ namespace mx
MXAPI_EQUALS_BEGIN( KeyComponent )
MXAPI_EQUALS_MEMBER( step )
MXAPI_EQUALS_MEMBER( alter )
MXAPI_EQUALS_MEMBER( alterCents )
MXAPI_EQUALS_MEMBER( cents )
MXAPI_EQUALS_MEMBER( accidental )
MXAPI_EQUALS_END;

Expand Down
6 changes: 3 additions & 3 deletions Sourcecode/include/mx/api/KeyData.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ namespace mx

// Supports the creation of customized, non-traditional key signatures by specifying the exact note
// alterations. When custom is non-empty, then fifths and mode are ignored.
std::vector<KeyComponent> custom;
std::vector<KeyComponent> nonTraditional;

KeyData()
: fifths{ 0 }
, cancel{ 0 }
, mode{ KeyMode::unspecified }
, tickTimePosition{ 0 }
, staffIndex{ -1 }
, custom{}
, nonTraditional{}
{

}
Expand All @@ -95,7 +95,7 @@ namespace mx
MXAPI_EQUALS_MEMBER( mode )
MXAPI_EQUALS_MEMBER( tickTimePosition )
MXAPI_EQUALS_MEMBER( staffIndex )
MXAPI_EQUALS_MEMBER( custom )
MXAPI_EQUALS_MEMBER( nonTraditional )
MXAPI_EQUALS_END;

MXAPI_NOT_EQUALS_AND_VECTORS( KeyData );
Expand Down
30 changes: 29 additions & 1 deletion Sourcecode/private/mx/impl/Converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,34 @@ namespace mx
{
return findApiItem( kindMap, api::ChordKind::unspecified, value );
}


mx::core::DecimalType Converter::convertToAlter( int semitones, double cents )
{
double alter = 0.0;
if( semitones != 0 || cents != 0.0 )
{
double microtones = 0.0;
if( cents != 0.0 ) {
microtones = cents / 100.0;
}
alter = semitones + microtones;
}
return alter;
}

std::pair<int, double> Converter::convertToSemitonesAndCents( mx::core::DecimalType xmlAlter )
{
double myCents = 0.0;
const auto intAlter = static_cast<int>( xmlAlter );
const auto micro = xmlAlter - static_cast<mx::core::DecimalType> ( intAlter );
const auto microDistance = std::abs( micro );
if( microDistance >= 0.000000000001 )
{
const auto theCents = micro * 100.0;
const auto theNarrowCents = static_cast<decltype(mx::api::PitchData::cents)>( theCents );
myCents = theNarrowCents;
}
return std::make_pair( intAlter, myCents );
}
}
}
8 changes: 7 additions & 1 deletion Sourcecode/private/mx/impl/Converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "mx/core/elements/OrnamentsChoice.h"
#include "mx/core/elements/TechnicalChoice.h"
#include "mx/core/Enums.h"
#include "mx/core/Decimals.h"

#include <map>

Expand All @@ -25,6 +26,8 @@ namespace mx
class Converter
{
public:
// TODO - all of these functions should be static

core::StepEnum convert( api::Step value ) const;
api::Step convert( core::StepEnum value ) const;

Expand Down Expand Up @@ -114,7 +117,10 @@ namespace mx

core::KindValue convert( api::ChordKind value ) const;
api::ChordKind convert( core::KindValue value ) const;


static mx::core::DecimalType convertToAlter( int semitones, double cents );
static std::pair<int, double> convertToSemitonesAndCents( mx::core::DecimalType alter );

const static std::map<core::StepEnum, api::Step> stepMap;
const static std::map<core::NoteTypeValue, api::DurationName> durationMap;
const static std::map<core::NoteheadValue, api::Notehead> noteheadMap;
Expand Down
25 changes: 24 additions & 1 deletion Sourcecode/private/mx/impl/MeasureReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,30 @@ namespace mx

if( keyType == core::KeyChoice::Choice::nonTraditionalKey )
{
// TODO - support non-traditional keys
api::KeyData keyData;
const auto& nonTraditionalKeyParts = key.getKeyChoice()->getNonTraditionalKeySet();
for( const auto& nonTraditionalKeyPart : nonTraditionalKeyParts )
{
api::KeyComponent keyComponent{};

if( nonTraditionalKeyPart->getHasKeyAccidental() )
{
keyComponent.accidental = myConverter.convert( nonTraditionalKeyPart->getKeyAccidental()->getValue() );
}

const auto alter = nonTraditionalKeyPart->getKeyAlter()->getValue().getValue();
if( alter != 0.0 )
{
const auto semitoneAndCents = Converter::convertToSemitonesAndCents( alter );
keyComponent.alter = semitoneAndCents.first;
keyComponent.cents = semitoneAndCents.second;
}

keyComponent.step = myConverter.convert( nonTraditionalKeyPart->getKeyStep()->getValue() );
keyData.nonTraditional.emplace_back( keyComponent );
}

myOutMeasureData.keys.emplace_back( std::move( keyData ) );
continue;
}

Expand Down
25 changes: 16 additions & 9 deletions Sourcecode/private/mx/impl/NoteReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

#include <map>
#include "mx/api/PitchData.h"
#include "Converter.h"

namespace mx
{
Expand Down Expand Up @@ -214,16 +215,22 @@ namespace mx
const auto& pitch = *fullNoteTypeChoice.getPitch();
myStep = pitch.getStep()->getValue();
myOctave = pitch.getOctave()->getValue().getValue();
const auto xmlAlter = pitch.getAlter()->getValue().getValue();
const auto intAlter = static_cast<int>( xmlAlter );
myAlter = intAlter;
const auto micro = xmlAlter - static_cast<mx::core::DecimalType> ( intAlter );
const auto microDistance = std::abs( micro );
if( microDistance >= 0.000000000001 )
// const auto xmlAlter = pitch.getAlter()->getValue().getValue();
// const auto intAlter = static_cast<int>( xmlAlter );
// myAlter = intAlter;
// const auto micro = xmlAlter - static_cast<mx::core::DecimalType> ( intAlter );
// const auto microDistance = std::abs( micro );
// if( microDistance >= 0.000000000001 )
// {
// const auto theCents = micro * 100.0;
// const auto theNarrowCents = static_cast<decltype(mx::api::PitchData::cents)>( theCents );
// myCents = theNarrowCents;
// }
if( pitch.getHasAlter() )
{
const auto theCents = micro * 100.0;
const auto theNarrowCents = static_cast<decltype(mx::api::PitchData::cents)>( theCents );
myCents = theNarrowCents;
const auto semitonesAndCents = mx::impl::Converter::convertToSemitonesAndCents( pitch.getAlter()->getValue().getValue() );
myAlter = semitonesAndCents.first;
myCents = semitonesAndCents.second;
}
break;
}
Expand Down
15 changes: 8 additions & 7 deletions Sourcecode/private/mx/impl/NoteWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,14 @@ namespace mx
myOutFullNoteTypeChoice->setChoice( core::FullNoteTypeChoice::Choice::pitch );
auto pitch = myOutFullNoteTypeChoice->getPitch();
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<core::DecimalType>( myNoteData.pitchData.cents / 100.0 );
}
const auto alter = static_cast<core::DecimalType>( myNoteData.pitchData.alter ) + microtones;
if( myNoteData.pitchData.alter != 0 || myNoteData.pitchData.cents != 0.0 )
{
const auto alter = Converter::convertToAlter( myNoteData.pitchData.alter, myNoteData.pitchData.cents );
// core::DecimalType microtones = 0.0;
// if( myNoteData.pitchData.cents != 0.0 ) {
// microtones = static_cast<core::DecimalType>( myNoteData.pitchData.cents / 100.0 );
// }
// const auto alter = static_cast<core::DecimalType>( myNoteData.pitchData.alter ) + microtones;
pitch->setHasAlter( true );
pitch->getAlter()->setValue( core::Semitones{ alter } );
}
Expand Down
77 changes: 71 additions & 6 deletions Sourcecode/private/mx/impl/PropertiesWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#include "mx/core/Elements.h"
#include "mx/impl/PropertiesWriter.h"
#include "mx/core/elements/Properties.h"
#include "mx/core/elements/Clef.h"
#include "mx/core/elements/Directive.h"
#include "mx/core/elements/Divisions.h"
#include "mx/core/elements/EditorialGroup.h"
#include "mx/core/elements/Footnote.h"
#include "mx/core/elements/KeyAccidental.h"
#include "mx/core/elements/Instruments.h"
#include "mx/core/elements/Key.h"
#include "mx/core/elements/Level.h"
Expand Down Expand Up @@ -155,32 +157,95 @@ namespace mx

void PropertiesWriter::writeKey( int staffIndex, const api::KeyData& inKeyData )
{
// TODO - support non-traditional keys
// TODO - support placement and other attributes

auto key = core::makeKey();

if( staffIndex >= 0 )
{
key->getAttributes()->hasNumber = true;
key->getAttributes()->number = core::StaffNumber{ staffIndex + 1 };
}


if( inKeyData.nonTraditional.empty() )
{
writeTraditionalKey( staffIndex, inKeyData, key );
}
else
{
writeNonTraditionalKey( staffIndex, inKeyData, key );
}

myProperties->addKey( key );

// auto key = core::makeKey();
//
// if( staffIndex >= 0 )
// {
// key->getAttributes()->hasNumber = true;
// key->getAttributes()->number = core::StaffNumber{ staffIndex + 1 };
// }
//
// key->getKeyChoice()->setChoice( core::KeyChoice::Choice::traditionalKey );
// auto traditionalKey = key->getKeyChoice()->getTraditionalKey();
// traditionalKey->getFifths()->setValue( core::FifthsValue{ inKeyData.fifths } );
//
// if ( inKeyData.cancel != 0 )
// {
// traditionalKey->setHasCancel( true );
// traditionalKey->getCancel()->setValue( core::FifthsValue{ inKeyData.cancel } );
// }
//
// if( inKeyData.mode == api::KeyMode::major || inKeyData.mode == api::KeyMode::minor )
// {
// traditionalKey->setHasMode( true );
// traditionalKey->getMode()->setValue( core::ModeValue{ inKeyData.mode == api::KeyMode::major ? core::ModeEnum::major : core::ModeEnum::minor } );
// }
// myProperties->addKey( key );
}

void PropertiesWriter::writeNonTraditionalKey( int staffIndex, const api::KeyData& inKeyData, mx::core::KeyPtr& key )
{
Converter converter;
key->getKeyChoice()->setChoice( core::KeyChoice::Choice::nonTraditionalKey );
for( const auto& keyComponent : inKeyData.nonTraditional )
{
auto nt = core::makeNonTraditionalKey();
if( keyComponent.accidental != api::Accidental::none )
{
nt->setHasKeyAccidental( true );
const auto a = nt->getKeyAccidental();

a->setValue( converter.convert( keyComponent.accidental ) );
}

const auto isUnknown = keyComponent.step == api::Step::unspecified; // || keyComponent.step == api::Step::count;
const auto step = isUnknown ? api::Step::c : keyComponent.step;
nt->getKeyStep()->setValue( converter.convert( step ) );

const auto alter = Converter::convertToAlter( keyComponent.alter, keyComponent.cents );
nt->getKeyAlter()->setValue( core::Semitones{ alter } );
key->getKeyChoice()->addNonTraditionalKey( nt );
}
}

void PropertiesWriter::writeTraditionalKey( int staffIndex, const api::KeyData& inKeyData, mx::core::KeyPtr& key )
{
key->getKeyChoice()->setChoice( core::KeyChoice::Choice::traditionalKey );
auto traditionalKey = key->getKeyChoice()->getTraditionalKey();
traditionalKey->getFifths()->setValue( core::FifthsValue{ inKeyData.fifths } );

if ( inKeyData.cancel != 0 )
{
traditionalKey->setHasCancel( true );
traditionalKey->getCancel()->setValue( core::FifthsValue{ inKeyData.cancel } );
}

if( inKeyData.mode == api::KeyMode::major || inKeyData.mode == api::KeyMode::minor )
{
traditionalKey->setHasMode( true );
traditionalKey->getMode()->setValue( core::ModeValue{ inKeyData.mode == api::KeyMode::major ? core::ModeEnum::major : core::ModeEnum::minor } );
}
myProperties->addKey( key );
}


Expand Down
4 changes: 4 additions & 0 deletions Sourcecode/private/mx/impl/PropertiesWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace mx
using PartwiseMeasurePtr = std::shared_ptr<PartwiseMeasure>;
class Properties;
using PropertiesPtr = std::shared_ptr<Properties>;
class Key;
using KeyPtr = std::shared_ptr<Key>;
}

namespace impl
Expand All @@ -43,6 +45,8 @@ namespace mx

void writeDivisions( int value );
void writeKey( int staffIndex, const api::KeyData& inKeyData );
void writeTraditionalKey( int staffIndex, const api::KeyData& inKeyData, mx::core::KeyPtr& ioKey );
void writeNonTraditionalKey( int staffIndex, const api::KeyData& inKeyData, mx::core::KeyPtr& ioKey );
void writeTime( const api::TimeSignatureData& value );
void writeNumStaves( int value );
void writeClef( int staffIndex, const api::ClefData& inClefData );
Expand Down
Loading

0 comments on commit ecf149b

Please sign in to comment.