1 /***************************************************************************
2 **                                                                        **
3 **  Polyphone, a soundfont editor                                         **
4 **  Copyright (C) 2013-2020 Davy Triponney                                **
5 **                                                                        **
6 **  This program is free software: you can redistribute it and/or modify  **
7 **  it under the terms of the GNU General Public License as published by  **
8 **  the Free Software Foundation, either version 3 of the License, or     **
9 **  (at your option) any later version.                                   **
10 **                                                                        **
11 **  This program is distributed in the hope that it will be useful,       **
12 **  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
13 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          **
14 **  GNU General Public License for more details.                          **
15 **                                                                        **
16 **  You should have received a copy of the GNU General Public License     **
17 **  along with this program. If not, see http://www.gnu.org/licenses/.    **
18 **                                                                        **
19 ****************************************************************************
20 **           Author: Davy Triponney                                       **
21 **  Website/Contact: https://www.polyphone-soundfonts.com                 **
22 **             Date: 01.01.2013                                           **
23 ***************************************************************************/
24 
25 #include "inputparsergrandorgue.h"
26 #include "soundfontmanager.h"
27 #include "grandorguerank.h"
28 #include "grandorguestop.h"
29 #include "grandorguedatathrough.h"
30 
InputParserGrandOrgue()31 InputParserGrandOrgue::InputParserGrandOrgue() : AbstractInputParser(),
32     _godt(new GrandOrgueDataThrough())
33 {
34 
35 }
36 
~InputParserGrandOrgue()37 InputParserGrandOrgue::~InputParserGrandOrgue()
38 {
39     while (!_ranks.empty())
40         delete _ranks.take(_ranks.firstKey());
41     while (!_stops.empty())
42         delete _stops.take(_stops.firstKey());
43     delete _godt;
44 }
45 
processInternal(QString fileName,SoundfontManager * sm,bool & success,QString & error,int & sf2Index,QString & tempFilePath)46 void InputParserGrandOrgue::processInternal(QString fileName, SoundfontManager * sm, bool &success, QString &error, int &sf2Index, QString &tempFilePath)
47 {
48     Q_UNUSED(sm)
49     Q_UNUSED(tempFilePath)
50 
51     // Initialization
52     _currentSection = SECTION_UNKNOWN;
53     _currentIndex = -1;
54     _ranks.clear();
55     _stops.clear();
56 
57     // Parse the file
58     _rootDir = QFileInfo(fileName).dir().path();
59     if (_rootDir.endsWith('/'))
60         _rootDir = _rootDir.left(_rootDir.size() - 1);
61     parseFile(fileName, success, error);
62     if (!success)
63         return;
64 
65     // Create a sf2
66     createSf2(sf2Index, fileName);
67     success = true;
68 }
69 
parseFile(QString filename,bool & success,QString & error)70 void InputParserGrandOrgue::parseFile(QString filename, bool &success, QString &error)
71 {
72     // Open the file
73     QFile inputFile(filename);
74     if (!inputFile.open(QIODevice::ReadOnly))
75     {
76         success = false;
77         error = tr("Cannot open file \"%1\"").arg(filename);
78         return;
79     }
80 
81     QTextStream in(&inputFile);
82     while (!in.atEnd())
83     {
84         // Read one line, skip empty lines or comments
85         QString line = in.readLine();
86 
87         // Remove comments
88         int tmp;
89         if ((tmp = line.indexOf(';')) != -1)
90         {
91             line = line.left(tmp);
92         }
93 
94         // Trim the line and skip it if empty
95         line = line.trimmed();
96         if (line.isEmpty())
97             continue;
98 
99         if (line.startsWith("[") && line.endsWith("]"))
100         {
101             // New section
102             startSection(line.mid(1, line.length() - 2).toLower());
103         }
104         else
105         {
106             // Process data
107             tmp = line.indexOf('=');
108             QString key = line.left(tmp).trimmed().toLower();
109             QString value = line.right(line.length() - tmp - 1).trimmed().replace('\\', '/');
110             if (!key.isEmpty() && !value.isEmpty())
111                 processData(key, value);
112         }
113     }
114     inputFile.close();
115 
116     success = true;
117 }
118 
startSection(QString sectionName)119 void InputParserGrandOrgue::startSection(QString sectionName)
120 {
121     // Track ranks and stops
122     if (sectionName.startsWith("rank"))
123     {
124         bool ok = false;
125         _currentIndex = sectionName.right(sectionName.length() - 4).toInt(&ok);
126         if (!ok)
127         {
128             _currentSection = SECTION_UNKNOWN;
129             _currentIndex = -1;
130         }
131         else
132             _currentSection = SECTION_RANK;
133     }
134     else if (sectionName.startsWith("stop"))
135     {
136         bool ok = false;
137         _currentIndex = sectionName.right(sectionName.length() - 4).toInt(&ok);
138         if (!ok)
139         {
140             _currentSection = SECTION_UNKNOWN;
141             _currentIndex = -1;
142         }
143         else
144             _currentSection = SECTION_STOP;
145     }
146     else if (sectionName == "organ")
147     {
148         _currentSection = SECTION_ORGAN;
149         _currentIndex = -1;
150     }
151     else
152         _currentSection = SECTION_UNKNOWN;
153 }
154 
processData(QString key,QString value)155 void InputParserGrandOrgue::processData(QString key, QString value)
156 {
157     switch (_currentSection)
158     {
159     case SECTION_RANK:
160         if (_currentIndex != -1)
161         {
162             if (!_ranks.contains(_currentIndex))
163                 _ranks[_currentIndex] = new GrandOrgueRank(_rootDir, _godt, _currentIndex);
164             _ranks[_currentIndex]->readData(key, value);
165         }
166         break;
167     case SECTION_STOP:
168         if (_currentIndex != -1)
169         {
170             if (!_stops.contains(_currentIndex))
171                 _stops[_currentIndex] = new GrandOrgueStop(_rootDir, _godt, _currentIndex);
172             _stops[_currentIndex]->readData(key, value);
173         }
174         break;
175     case SECTION_ORGAN:
176         _organProperties[key] = value;
177         break;
178     case SECTION_UNKNOWN:
179         // Nothing
180         break;
181     }
182 }
183 
createSf2(int & sf2Index,QString filename)184 void InputParserGrandOrgue::createSf2(int &sf2Index, QString filename)
185 {
186     // Create a new soundfont, store the sf2 index
187     SoundfontManager * sm = SoundfontManager::getInstance();
188     EltID idSf2(elementSf2, -1, -1, -1, -1);
189     idSf2.indexSf2 = sm->add(idSf2);
190     sf2Index = idSf2.indexSf2;
191 
192     // Title, comment
193     QFileInfo fileInfo(filename);
194     sm->set(idSf2, champ_name, fileInfo.completeBaseName());
195     sm->set(idSf2, champ_ICMT, this->getComment());
196 
197     // Pre-process everything
198     foreach (GrandOrgueRank * rank, _ranks)
199         rank->preProcess();
200     foreach (GrandOrgueStop * stop, _stops)
201         stop->preProcess();
202     _godt->finalizePreprocess();
203 
204     // Process stops for creating presets and instruments
205     foreach (GrandOrgueStop * stop, _stops)
206         stop->process(sm, sf2Index, _ranks);
207 }
208 
getComment()209 QString InputParserGrandOrgue::getComment()
210 {
211     QString comment = "";
212     if (_organProperties.contains("churchname"))
213         comment = "Church name: " + _organProperties["churchname"];
214     if (_organProperties.contains("churchaddress"))
215         comment += (comment.isEmpty() ? QString("") : "\n") +
216                 "Church address: " + _organProperties["churchaddress"];
217     if (_organProperties.contains("organbuilder"))
218         comment += (comment.isEmpty() ? QString("") : "\n") +
219                 "Organ builder: " + _organProperties["organbuilder"];
220     if (_organProperties.contains("organbuilddate"))
221         comment += (comment.isEmpty() ? QString("") : "\n") +
222                 "Organ build date: " + _organProperties["organbuilddate"];
223     if (_organProperties.contains("organcomments"))
224         comment += (comment.isEmpty() ? QString("") : "\n") +
225                 "Organ comments: " + _organProperties["organcomments"];
226     if (_organProperties.contains("recordingdetails"))
227         comment += (comment.isEmpty() ? QString("") : "\n") +
228                 "Recording details: " + _organProperties["recordingdetails"];
229     return comment;
230 }
231