Commit 0fadcbc6 authored by Andreas Cord-Landwehr's avatar Andreas Cord-Landwehr
Browse files

Re-implement parsing of Phonemes

parent bebdbd89
......@@ -23,6 +23,7 @@
#include "core/language.h"
#include "core/unit.h"
#include "core/phrase.h"
#include "core/phonemegroup.h"
#include "core/resources/languageresource.h"
#include "core/resources/courseresource.h"
......@@ -60,6 +61,9 @@ void TestCourseResource::loadCourseResource()
{
std::unique_ptr<Language> language(new Language);
language->setId("de");
auto group = language->addPhonemeGroup("id", "title");
group->addPhoneme("g", "G");
group->addPhoneme("u", "U");
std::vector<std::unique_ptr<Language>> languages;
languages.push_back(std::move(language));
ResourceRepositoryStub repository(std::move(languages));
......@@ -93,7 +97,7 @@ void TestCourseResource::loadCourseResource()
QCOMPARE(firstPhrase->text(), "Guten Tag.");
QCOMPARE(firstPhrase->soundFileUrl(), courseDirectory + "de_01.ogg");
QCOMPARE(firstPhrase->type(), Phrase::Type::Sentence);
QVERIFY(firstPhrase->phonemes().isEmpty());
QCOMPARE(firstPhrase->phonemes().count(), 2);
}
void TestCourseResource::unitAddAndRemoveHandling()
......
......@@ -17,7 +17,10 @@
<text>Guten Tag.</text>
<soundFile>de_01.ogg</soundFile>
<type>sentence</type>
<phonemes></phonemes>
<phonemes>
<phonemeID>g</phonemeID>
<phonemeID>u</phonemeID>
</phonemes>
</phrase>
<phrase>
<id>2</id>
......
......@@ -30,6 +30,7 @@
class QString;
class Language;
class Unit;
class Phoneme;
class ARTIKULATECORE_EXPORT ICourse : public QObject
{
......
......@@ -30,10 +30,7 @@ Language::Language()
{
}
Language::~Language()
{
qDeleteAll(m_phonemeGroups);
}
Language::~Language() = default;
QString Language::id() const
{
......@@ -85,40 +82,36 @@ void Language::setFile(const QUrl &file)
m_file = file;
}
QList<Phoneme *> Language::phonemes() const
QVector<std::shared_ptr<Phoneme>> Language::phonemes() const
{
QList<Phoneme *> list;
foreach (PhonemeGroup *group, m_phonemeGroups) {
list.append(group->phonemes());
QVector<std::shared_ptr<Phoneme>> list;
for (auto group : m_phonemeGroups) {
list << group->phonemes();
}
return list;
}
QList<PhonemeGroup*> Language::phonemeGroups() const
QVector<std::shared_ptr<PhonemeGroup>> Language::phonemeGroups() const
{
return m_phonemeGroups;
}
PhonemeGroup * Language::addPhonemeGroup(const QString &identifier, const QString &title)
std::shared_ptr<PhonemeGroup> Language::addPhonemeGroup(const QString &identifier, const QString &title)
{
QList<PhonemeGroup *>::ConstIterator iter = m_phonemeGroups.constBegin();
while (iter != m_phonemeGroups.constEnd()) {
if (QString::compare((*iter)->id(), identifier) == 0) {
for (auto group : m_phonemeGroups) {
if (QString::compare(group->id(), identifier) == 0) {
qCWarning(ARTIKULATE_LOG) << "Pronunciation Group identifier already registered, aborting";
return nullptr;
return std::shared_ptr<PhonemeGroup>();
}
++iter;
}
PhonemeGroup *newGroup = new PhonemeGroup();
newGroup->setId(identifier);
newGroup->setTitle(title);
m_phonemeGroups.append(newGroup);
connect(newGroup, &PhonemeGroup::phonemeAdded, this, &Language::phonemesChanged);
connect(newGroup, &PhonemeGroup::phonemeRemoved, this, &Language::phonemesChanged);
std::shared_ptr<PhonemeGroup> phonemeGroup(new PhonemeGroup);
phonemeGroup->setId(identifier);
phonemeGroup->setTitle(title);
m_phonemeGroups.append(phonemeGroup);
connect(phonemeGroup.get(), &PhonemeGroup::phonemeAdded, this, &Language::phonemesChanged);
emit phonemeGroupsChanged();
return newGroup;
return phonemeGroup;
}
......@@ -22,8 +22,9 @@
#define LANGUAGE_H
#include "artikulatecore_export.h"
#include <memory>
#include <QObject>
#include <QList>
#include <QVector>
#include <QUrl>
class QString;
......@@ -49,10 +50,9 @@ public:
void setTitle(const QString &title);
QUrl file() const;
void setFile(const QUrl &file);
QList<Phoneme *> phonemes() const;
Phoneme * addPhoneme(const QString &identifier, const QString &title);
QList<PhonemeGroup *> phonemeGroups() const;
PhonemeGroup * addPhonemeGroup(const QString &identifier, const QString &title);
QVector<std::shared_ptr<Phoneme>> phonemes() const;
QVector<std::shared_ptr<PhonemeGroup>> phonemeGroups() const;
std::shared_ptr<PhonemeGroup> addPhonemeGroup(const QString &identifier, const QString &title);
Q_SIGNALS:
void idChanged();
......@@ -67,7 +67,7 @@ private:
QString m_title;
QString m_i18nTitle;
QUrl m_file;
QList<PhonemeGroup *> m_phonemeGroups;
QVector<std::shared_ptr<PhonemeGroup>> m_phonemeGroups;
};
#endif // LANGUAGE_H
......@@ -23,8 +23,8 @@
#include "artikulate_debug.h"
PhonemeGroup::PhonemeGroup(QObject *parent)
: QObject(parent)
PhonemeGroup::PhonemeGroup()
: QObject(nullptr)
{
}
......@@ -73,61 +73,48 @@ void PhonemeGroup::setDescription(const QString &description)
emit descriptionChanged();
}
QList< Phoneme* > PhonemeGroup::phonemes() const
QVector<std::shared_ptr<Phoneme>> PhonemeGroup::phonemes() const
{
return m_phonemes;
}
bool PhonemeGroup::contains(Phoneme *phoneme) const
bool PhonemeGroup::contains(std::shared_ptr<Phoneme> phoneme) const
{
QList<Phoneme *>::ConstIterator iter = m_phonemes.constBegin();
while (iter != m_phonemes.constEnd()) {
if (QString::compare((*iter)->id(), phoneme->id()) == 0) {
for (auto testPhoneme : m_phonemes) {
if (QString::compare(testPhoneme->id(), phoneme->id()) == 0) {
return true;
}
++iter;
}
return false;
}
void PhonemeGroup::addPhoneme(Phoneme *phoneme)
std::shared_ptr<Phoneme> PhonemeGroup::addPhoneme(std::unique_ptr<Phoneme> phoneme)
{
QList<Phoneme *>::ConstIterator iter = m_phonemes.constBegin();
while (iter != m_phonemes.constEnd()) {
if (QString::compare((*iter)->id(), phoneme->id()) == 0) {
qCWarning(ARTIKULATE_LOG) << "Phoneme identifier already registered in group "<< m_title <<", aborting";
return;
}
++iter;
std::shared_ptr<Phoneme> newPhoneme(std::move(phoneme));
if (!contains(newPhoneme)) {
m_phonemes.append(newPhoneme);
}
else {
qCWarning(ARTIKULATE_LOG) << "Phoneme identifier already registered in group "<< m_title <<", aborting";
}
m_phonemes.append(phoneme);
return std::shared_ptr<Phoneme>();
}
Phoneme * PhonemeGroup::addPhoneme(const QString &identifier, const QString &title)
std::shared_ptr<Phoneme> PhonemeGroup::addPhoneme(const QString &identifier, const QString &title)
{
Q_ASSERT(!identifier.isEmpty());
// check that identifier is not used
QList<Phoneme *>::ConstIterator iter = m_phonemes.constBegin();
while (iter != m_phonemes.constEnd()) {
if (QString::compare((*iter)->id(), identifier) == 0) {
for (auto phoneme : m_phonemes) {
if (QString::compare(phoneme->id(), identifier) == 0) {
qCWarning(ARTIKULATE_LOG) << "Phoneme identifier " << identifier <<" already registered in group "
<< m_title <<", aborting";
return nullptr;
return std::shared_ptr<Phoneme>();
}
++iter;
}
// create phoneme and add it
Phoneme *newPhoneme = new Phoneme();
std::unique_ptr<Phoneme> newPhoneme(new Phoneme);
newPhoneme->setId(identifier);
newPhoneme->setTitle(title);
addPhoneme(newPhoneme);
return newPhoneme;
}
void PhonemeGroup::removePhoneme(Phoneme *phoneme)
{
m_phonemes.removeOne(phoneme);
return addPhoneme(std::move(newPhoneme));
}
......@@ -22,9 +22,10 @@
#define PHONEMEGROUP_H
#include "artikulatecore_export.h"
#include <memory>
#include <QVector>
#include <QObject>
#include <QMap>
#include "phoneme.h"
class QString;
class Phoneme;
......@@ -39,7 +40,7 @@ class ARTIKULATECORE_EXPORT PhonemeGroup : public QObject
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
public:
explicit PhonemeGroup(QObject *parent = 0);
explicit PhonemeGroup();
~PhonemeGroup() override;
QString id() const;
void setId(const QString &id);
......@@ -47,31 +48,29 @@ public:
void setTitle(const QString &title);
QString description() const;
void setDescription(const QString &description);
void addPhoneme(Phoneme *phoneme);
Phoneme * addPhoneme(const QString &identifier, const QString &title);
void removePhoneme(Phoneme *phoneme);
QList<Phoneme *> phonemes() const;
std::shared_ptr<Phoneme> addPhoneme(std::unique_ptr<Phoneme> phoneme);
std::shared_ptr<Phoneme> addPhoneme(const QString &identifier, const QString &title);
QVector<std::shared_ptr<Phoneme>> phonemes() const;
/**
* Checks by identifier comparison whether phoneme is registered in this group.
*
* \param poneme is the phoneme to be checked for if registered
* \return true if registered, false otherwise
*/
bool contains(Phoneme *phoneme) const;
bool contains(std::shared_ptr<Phoneme> phoneme) const;
signals:
Q_SIGNALS:
void idChanged();
void titleChanged();
void descriptionChanged();
void phonemeAdded(const Phoneme&);
void phonemeRemoved(const Phoneme&);
private:
Q_DISABLE_COPY(PhonemeGroup)
QString m_id;
QString m_title;
QString m_description;
QList<Phoneme *> m_phonemes;
QVector<std::shared_ptr<Phoneme>> m_phonemes;
};
#endif // PHONEMEGROUP_H
......@@ -47,10 +47,7 @@ Phrase::Phrase(QObject *parent)
connect(this, &Phrase::excludedChanged, this, &Phrase::modified);
}
Phrase::~Phrase()
{
}
Phrase::~Phrase() = default;
QString Phrase::id() const
{
......@@ -310,7 +307,7 @@ void Phrase::updateProgress(Phrase::Progress progress)
}
}
QList<Phoneme *> Phrase::phonemes() const
QVector<Phoneme *> Phrase::phonemes() const
{
return m_phonemes;
}
......
......@@ -26,6 +26,7 @@
#include <QObject>
#include <QUrl>
#include <QList>
#include <QVector>
#include <QTemporaryFile>
class QString;
......@@ -96,7 +97,7 @@ public:
void setEditState(const QString &stateString);
QUrl sound() const;
void setSound(const QUrl &soundFile);
QList<Phoneme *> phonemes() const;
QVector<Phoneme *> phonemes() const;
bool isExcluded() const;
void setExcluded(bool excluded = false);
int progress() const;
......@@ -132,7 +133,7 @@ private:
unsigned m_trainingProgress;
int m_skipCounter; // count how many skips occurred since last progress update
bool m_excludedFromUnit;
QList<Phoneme *> m_phonemes;
QVector<Phoneme *> m_phonemes;
QUrl m_nativeSoundFile;
};
......
......@@ -69,7 +69,7 @@ QDomDocument CourseParser::loadDomDocument(const QUrl &path, const QXmlSchema &s
return document;
}
std::vector<std::unique_ptr<Unit>> CourseParser::parseUnits(const QUrl &path)
std::vector<std::unique_ptr<Unit>> CourseParser::parseUnits(const QUrl &path, QVector<std::shared_ptr<Phoneme>> phonemes)
{
std::vector<std::unique_ptr<Unit>> units;
......@@ -96,7 +96,7 @@ std::vector<std::unique_ptr<Unit>> CourseParser::parseUnits(const QUrl &path)
if (xml.name() == "units") {
continue;
} else if (xml.name() == "unit") {
auto unit = parseUnit(xml, path, elementOk);
auto unit = parseUnit(xml, path, phonemes, elementOk);
if (elementOk) {
units.push_back(std::move(unit));
}
......@@ -115,7 +115,7 @@ std::vector<std::unique_ptr<Unit>> CourseParser::parseUnits(const QUrl &path)
return units;
}
std::unique_ptr<Unit> CourseParser::parseUnit(QXmlStreamReader &xml, const QUrl &path, bool &ok)
std::unique_ptr<Unit> CourseParser::parseUnit(QXmlStreamReader &xml, const QUrl &path, QVector<std::shared_ptr<Phoneme>> phonemes, bool &ok)
{
std::unique_ptr<Unit> unit(new Unit);
ok = true;
......@@ -143,7 +143,7 @@ std::unique_ptr<Unit> CourseParser::parseUnit(QXmlStreamReader &xml, const QUrl
// nothing to do
}
else if (xml.name() == "phrase") {
auto phrase = parsePhrase(xml, path, elementOk);
auto phrase = parsePhrase(xml, path, phonemes, elementOk);
if (elementOk) {
unit->addPhrase(phrase);
}
......@@ -160,9 +160,9 @@ std::unique_ptr<Unit> CourseParser::parseUnit(QXmlStreamReader &xml, const QUrl
return unit;
}
Phrase * CourseParser::parsePhrase(QXmlStreamReader &xml, const QUrl &path, bool &ok)
Phrase * CourseParser::parsePhrase(QXmlStreamReader &xml, const QUrl &path, QVector<std::shared_ptr<Phoneme>> phonemes, bool &ok)
{
Phrase * phrase = new Phrase(nullptr);
Phrase * phrase = new Phrase;
ok = true;
if (xml.tokenType() != QXmlStreamReader::StartElement
......@@ -194,7 +194,12 @@ Phrase * CourseParser::parsePhrase(QXmlStreamReader &xml, const QUrl &path, bool
+ '/' + parseElement(xml, elementOk)));
ok &= elementOk;
} else if (xml.name() == "phonemes") {
parsePhonemeIds(xml, elementOk); //TODO register language phonemes at phrase
auto parsedPhonemeIds = parsePhonemeIds(xml, elementOk);
for (auto phoneme : phonemes) {
if (parsedPhonemeIds.contains(phoneme->id())) {
phrase->addPhoneme(phoneme.get());
}
}
ok &= elementOk;
} else if (xml.name() == "type") {
const QString type = parseElement(xml, elementOk);
......@@ -245,18 +250,13 @@ QStringList CourseParser::parsePhonemeIds(QXmlStreamReader &xml, bool &ok)
xml.readNext();
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "phonemes")) {
xml.readNext();
if (xml.name() == "phoneme") {
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "phoneme")) {
if (xml.tokenType() == QXmlStreamReader::StartElement) {
bool elementOk{ false };
if (xml.name() == "phonemeID") {
ids.append(parseElement(xml, elementOk));
ok &= elementOk;
} else {
qCWarning(ARTIKULATE_PARSER()) << "Skipping unknown token" << xml.name();
}
}
xml.readNext();
if (xml.tokenType() == QXmlStreamReader::StartElement) {
if (xml.name() == "phonemeID") {
bool elementOk{ false };
ids.append(parseElement(xml, elementOk));
ok &= elementOk;
} else {
qCWarning(ARTIKULATE_PARSER()) << "Skipping unknown token" << xml.name();
}
}
}
......@@ -275,7 +275,7 @@ QString CourseParser::parseElement(QXmlStreamReader& xml, bool &ok)
QString elementName = xml.name().toString();
xml.readNext();
// qCDebug(ARTIKULATE_PARSER()) << "parsed: " << elementName << " / " << xml.text().toString();
qCDebug(ARTIKULATE_PARSER()) << "parsed: " << elementName << " / " << xml.text().toString();
return xml.text().toString();
}
......
......@@ -29,6 +29,7 @@ class ICourse;
class Unit;
class Phrase;
class Phoneme;
class IResourceRepository;
class QXmlSchema;
class QJSonDocument;
class QDomDocument;
......@@ -59,15 +60,15 @@ public:
*/
static QDomDocument loadDomDocument(const QUrl &path, const QXmlSchema &schema);
static std::vector<std::unique_ptr<Unit>> parseUnits(const QUrl &path);
static std::vector<std::unique_ptr<Unit>> parseUnits(const QUrl &path, QVector<std::shared_ptr<Phoneme>> phonemes = QVector<std::shared_ptr<Phoneme>>());
static QDomDocument serializedDocument(ICourse *course, bool trainingExport);
static QDomElement serializedPhrase(Phrase *phrase, QDomDocument &document);
static bool exportCourseToGhnsPackage(ICourse *course, const QString &exportPath);
private:
static std::unique_ptr<Unit> parseUnit(QXmlStreamReader &xml, const QUrl &path, bool &ok);
static Phrase * parsePhrase(QXmlStreamReader &xml, const QUrl &path, bool &ok);
static std::unique_ptr<Unit> parseUnit(QXmlStreamReader &xml, const QUrl &path, QVector<std::shared_ptr<Phoneme>> phonemes, bool &ok);
static Phrase * parsePhrase(QXmlStreamReader &xml, const QUrl &path, QVector<std::shared_ptr<Phoneme>> phonemes, bool &ok);
static QStringList parsePhonemeIds(QXmlStreamReader &xml, bool &ok);
static QString parseElement(QXmlStreamReader &xml, bool &ok);
};
......
......@@ -76,7 +76,8 @@ void CourseResourcePrivate::loadCourse(CourseResource *parent)
return;
}
auto units = CourseParser::parseUnits(m_file);
QVector<std::shared_ptr<Phoneme>> phonemes = m_language->phonemes();
auto units = CourseParser::parseUnits(m_file, phonemes);
for (auto &unit : units) {
parent->addUnit(std::move(unit));
}
......
......@@ -103,12 +103,6 @@ QString LanguageResource::i18nTitle()
return d->m_i18nTitle;
}
void LanguageResource::close()
{
// do nothing
// language files are never closed
}
bool LanguageResource::isOpen() const
{
return (d->m_language != nullptr);
......@@ -152,7 +146,7 @@ std::shared_ptr<Language> LanguageResource::language()
!groupNode.isNull();
groupNode = groupNode.nextSiblingElement())
{
PhonemeGroup *group = d->m_language->addPhonemeGroup(
auto group = d->m_language->addPhonemeGroup(
groupNode.firstChildElement(QStringLiteral("id")).text(),
groupNode.firstChildElement(QStringLiteral("title")).text());
group->setDescription(groupNode.attribute(QStringLiteral("description")));
......
......@@ -57,11 +57,6 @@ public:
*/
bool isOpen() const;
/**
* close resource without writing changes back to file
*/
void close();
/**
* \return path to resource file
*/
......
......@@ -22,6 +22,7 @@
#include "courseparser.h"
#include "core/language.h"
#include "core/unit.h"
#include "core/phrase.h"
#include "editablecourseresource.h"
#include "core/phoneme.h"
#include "core/phonemegroup.h"
......
......@@ -41,10 +41,10 @@ Unit::Unit(QObject *parent)
Unit::~Unit()
{
for (auto phrase : m_phraseList) {
for (auto phrase : m_phrases) {
phrase->deleteLater();
}
m_phraseList.clear();
m_phrases.clear();
m_phraseSignalMapper->deleteLater();
}
......@@ -100,15 +100,15 @@ void Unit::setTitle(const QString &title)
}
}
QList< Phrase* > Unit::phraseList() const
QList<Phrase*> Unit::phraseList() const
{
return m_phraseList;
return m_phrases;
}
void Unit::addPhrase(Phrase *phrase)
{
QList<Phrase *>::ConstIterator iter = m_phraseList.constBegin();
while (iter != m_phraseList.constEnd()) {
auto iter = m_phrases.constBegin();
while (iter != m_phrases.constEnd()) {
if (phrase->id() == (*iter)->id()) {
qCWarning(ARTIKULATE_LOG()) << "Phrase is already contained in this unit, aborting";
return;
......@@ -116,8 +116,8 @@ void Unit::addPhrase(Phrase *phrase)
++iter;
}
phrase->setUnit(this);
emit phraseAboutToBeAdded(phrase, m_phraseList.length());
m_phraseList.append(phrase);
emit phraseAboutToBeAdded(phrase, m_phrases.length());
m_phrases.append(phrase);
m_phraseSignalMapper->setMapping(phrase, phrase->id());
emit phraseAdded(phrase);
......@@ -132,19 +132,17 @@ void Unit::addPhrase(Phrase *phrase)
QList<Phrase *> Unit::excludedSkeletonPhraseList() const
{
QList<Phrase *> excludedPhraseList;
QList<Phrase *>::ConstIterator iter = m_phraseList.constBegin();
while (iter != m_phraseList.constEnd()) {
if ((*iter)->isExcluded() == true) {
excludedPhraseList.append(*iter);
for (auto phrase : m_phrases) {
if (phrase->isExcluded() == true) {
excludedPhraseList.append(phrase);
}
++iter;
}
return excludedPhraseList;
}
void Unit::excludeSkeletonPhrase(const QString &phraseId)
{
foreach (Phrase *phrase, m_phraseList) {
for (auto phrase : m_phrases) {
if (phrase->id() == phraseId) {
phrase->setExcluded(true);
emit modified();
......@@ -156,7 +154,7 @@ void Unit::excludeSkeletonPhrase(const QString &phraseId)
void Unit::includeSkeletonPhrase(const QString &phraseId)
{
foreach (Phrase *phrase, m_phraseList) {
for (auto phrase : m_phrases) {
if (phrase->id() == phraseId) {
phrase->setExcluded(false);
emit modified();
......