Antiphons
Antiphons, in practice always accompany psalms and canticles. (In a very, very few cases there are psalms without an associated antiphon, but they are the exception. For the psalms in each hour there are two patterns:
1) There is one antiphon, said or sung before the psalms, and then repeated after. In the Sarum and older Roman use this was varied by having the first iteration truncated after its incipit, but I decided (following the change to Roman use in the 20th Century) to drop this variant. (The term Double to represent some feasts, retained for some decade after the change, originally meant the level of feast serious enough that an antiphon would be repeated in full, twice.). This pattern applies to all the minor hours, and to the major hours (Lauds and Vespers) under certain circumstances.
2) There is one antiphon per psalm/canticle, said before and after each psalm, with the Gloria Patri preceding the second iteration. This is restricted to Lauds and Vespers, each of which has five iterations, with a canticle taking the place of one psalm for Lauds.
We model a set of antiphons fairly simply:
class IAntiphons
{
public:
virtual ~IAntiphons();
virtual uint16_t antiphons() const = 0;
virtual void formatAntiphon(const IEncapsulatedOfficeFormatter &inFormatter,
const uint16_t inIndex) const = 0;
};
Essentially, the interface can tell you how many antiphons are in a set, and will handle formatting a specified antiphon from the set. It is, by design, immutable.
The single-antiphon model for the lesser hours allows a common shared implementation:
class LesserHoursAntiphons : public IAntiphons
{
public:
~LesserHoursAntiphons() override;
protected:
explicit LesserHoursAntiphons(const std::string &inStr) : m_antiphon(inStr) {}
public:
uint16_t antiphons() const override
{
return 1;
}
const std::string& getText() const { return m_antiphon; }
void formatAntiphon(const IEncapsulatedOfficeFormatter &inFormatter,
const uint16_t inIndex) const override;
private:
std::string m_antiphon;
};
We only ever have one antiphon, and formatting it is a simple callback: Note that its constructor is protected, rendering it instantiable only in a specialized implementation.
void LesserHoursAntiphons::formatAntiphon(const IEncapsulatedOfficeFormatter &inFormatter,
const uint16_t inIndex) const
{
if (m_antiphon.length() > 0)
inFormatter.formatAntiphon(m_antiphon);
}
This common content model gets used in implementations which vary primarily in the details of their constructors.
class SextAntiphons : public LesserHoursAntiphons
{
public:
explicit SextAntiphons(const std::string &inString)
: LesserHoursAntiphons(inString)
{ }
explicit SextAntiphons(const Days inDay)
: LesserHoursAntiphons(DaysToText(inDay))
{ }
explicit SextAntiphons(const HymnSeasons inSeason)
: LesserHoursAntiphons(SeasonsToText(inSeason))
{ }
explicit SextAntiphons(const SpecialDays inDay)
: LesserHoursAntiphons(SpecialDaysToText(inDay))
{ }
~SextAntiphons() override;
private:
static std::string DaysToText(const Days inDay);
static std::string SeasonsToText(const HymnSeasons inSeason);
static std::string SpecialDaysToText(const SpecialDays inDay);
};
This allows the antiphons for the hour of Sext to be set by specifying the day of the week (the ultimate default)
std::string SextAntiphons::DaysToText(const Days inDay) {
if (inDay == Days::SUNDAY)
return "Let praise and glory resound from the lips of all, to the Father, and the only-begotten: and equal praise be given forver to the Holy Spirit."s;
else
return "Let me not: be disappointed of my hope."s;
}
the season of the year, which may take precedence,
std::string SextAntiphons::SeasonsToText(const HymnSeasons inSeason) {
if ((inSeason == HymnSeasons::NORMAL) || (inSeason == HymnSeasons::SEPTUAGESIMA))
return "Let me not: be disappointed of my hope."s;
else if (inSeason == HymnSeasons::ADVENT)
return "When thou comest: deliver us, O Lord."s;
else if ((inSeason == HymnSeasons::LENT)
|| (inSeason == HymnSeasons::MID_LENT))
return "Let us approve ourselves in much patience: in many fastings, by the armour of righteousness"s;
else if (inSeason == HymnSeasons::PASSIONTIDE)
return "O my people, what have I done unto thee: and wherein have I wearied thee? Testify against me."s;
else if (inSeason == HymnSeasons::EASTER)
return "Alleluia: alleluia, alleluia."s;
else
return {};
}
--note that unlike where we are dealing with parsed-in data where we want to catch irregularities, here we err on the side of allowing default values, here an empty string -- and (unusually) for a few days such as Maundy Thursday or the days in Easter Week:
std::string SextAntiphons::SpecialDaysToText(const SpecialDays inDay) {
if (inDay == SpecialDays::NONE)
return "Let me not: be disappointed of my hope."s;
else if ((inDay == SpecialDays::MAUNDY_THURSDAY)
|| (inDay == SpecialDays::GOOD_FRIDAY)
|| (inDay == SpecialDays::HOLY_SATURDAY))
return "Christ became obedient for us unto death: even the death of the Cross."s;
else if (inDay == SpecialDays::EASTER_WEEK)
return "His countenance was like lightning, and his raiment as white as snow, alleluia, alleluia."s;
else
return {};
}
The special cases returning empty strings would correspond to cases where an enum was deliberately subverted by forcing a number into an enum value via a static cast.
In all cases this is set in the initializer-list by having a static function convert the parameter to te constructor into a string.
Lauds Antiphons
Lauds antiphons are somewhat special, as for many feats and special Sundays the antiphons are then borrowed for other later hours, so the Lauds antiphon interface is extended to formatting an antiphon based on hour, or to retrieve the associated text value.In a few cases the name of the feast day is relevant as well, so there is an accessor for it:
class ILaudsAntiphons : public IAntiphons
{
public:
~ILaudsAntiphons() override;
virtual const std::string &getName() const = 0;
virtual const std::string& getAntiphon(const OfficeNames inOffice) const = 0;
virtual bool useAsAntiphonsForLesserHours() const = 0;
virtual void
formatAntiphonForHour(const IEncapsulatedOfficeFormatter &inFormatter,
const OfficeNames inName) const = 0;
};
This is an interface rather than just a concrete class because it gets referenced quite heavily and reducing dependencies accordingly is more useful. The concrete class, as with the lesser hours, has a constructor which sets values based on special days, but the range of Lauds Antiphons is too great for the other simple constructors used for the lesser hours; all other cases are handled by the general constructor.
class LaudsAntiphons : public ILaudsAntiphons
{
public:
LaudsAntiphons() {}
~LaudsAntiphons() override;
LaudsAntiphons(const std::string &inName, std::span<std::string> inAntiphons,
const bool inUseForLesserHours);
LaudsAntiphons(const SpecialDays inDay, const Days inDayOfWeek);
const std::string & getName() const override
{
return m_name;
}
bool useAsAntiphonsForLesserHours() const override
{
return m_useForLesserHours;
}
void formatAntiphon(const IEncapsulatedOfficeFormatter &inFormatter,
const uint16_t inIndex) const override;
void formatAntiphonForHour(const IEncapsulatedOfficeFormatter &inFormatter,
const OfficeNames inName) const override;
const std::string& getAntiphon(const OfficeNames inOffice) const override;
uint16_t antiphons() const override
{
if (m_antiphons[1].empty())
{
if (m_antiphons[0].empty())
return 0;
return 1;
}
return 5;
}
private:
std::array<std::string, 5> m_antiphons;
std::string m_name;
bool m_useForLesserHours = false;
};
Those bodies are straightforward and will not be detailed here. The general constructor is constrained to allow for only five, one, or zero values being set, which allows the formatting rules to be simplified.
The implementation of the per-hour formatting encodes the invariable rules for selecting an antiphon for Lauds for a subsequent hour:
const std::string& LaudsAntiphons::getAntiphon(const OfficeNames inOffice) const
{
const static std::string nullrval;
if (!m_useForLesserHours)
return nullrval;
switch (inOffice)
{
using enum OfficeNames;
case COMPLINE:
break;
case LAUDS:
[[fallthrough]];
case PRIME:
[[fallthrough]];
case VESPERS:
return m_antiphons[0];
break;
case TERCE:
return m_antiphons[1];
break;
case SEXT:
return m_antiphons[2];
break;
case NONE:
return m_antiphons[4];
break;
}
return nullrval;
}
Note the use of the new fallthrough directive.
Vespers Antiphons
The Vespers Antiphons have their own peculiarities: there are differences between first and second vespers services sometimes the antiphons from first vespers are referenced elsewhere and often the antiphon for second vespers is taken from Lauds. They are also far more often one antiphon rather than five.
class VespersAntiphons : public IAntiphons
{
public:
explicit VespersAntiphons(const Days &inDay);
VespersAntiphons(std::span<std::string> inFullAntiphons,
const std::string &inContext);
explicit VespersAntiphons(const ILaudsAntiphons &inLA);
explicit VespersAntiphons(const std::string &inAnt)
: m_singleAntiphon(inAnt), m_useSingleAntiphon(true)
{ }
explicit VespersAntiphons(const SpecialDays inDay);
~VespersAntiphons() override;
uint16_t antiphons() const override
{
return m_useSingleAntiphon ? 1 : 5;
}
void getAntiphons(std::vector<std::string> &outVector) const;
void formatAntiphon(const IEncapsulatedOfficeFormatter &inFormatter,
const uint16_t inIndex) const override;
private:
std::array<std::string, 5> m_fullAntiphons;
std::string m_singleAntiphon;
bool m_useSingleAntiphon;
};
There is nothing particularly interesting about the implementations.
Comments
Post a Comment