~lfaraone/ubuntu/maverick/tuxguitar/fix-mistaken-merge-20100527

« back to all changes in this revision

Viewing changes to TuxGuitar-musicxml/src/org/herac/tuxguitar/io/musicxml/MusicXMLWriter.java

  • Committer: Bazaar Package Importer
  • Author(s): Philippe Coval
  • Date: 2008-06-19 00:30:30 UTC
  • mfrom: (1.1.1 upstream) (2.1.3 hardy)
  • Revision ID: james.westby@ubuntu.com-20080619003030-agens2gvd5m4dacu
New upstream release (Closes: #481728) also (LP: #176979, #212207)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.herac.tuxguitar.io.musicxml;
 
2
 
 
3
import java.io.OutputStream;
 
4
import java.util.Iterator;
 
5
 
 
6
import javax.xml.parsers.DocumentBuilder;
 
7
import javax.xml.parsers.DocumentBuilderFactory;
 
8
import javax.xml.transform.OutputKeys;
 
9
import javax.xml.transform.Result;
 
10
import javax.xml.transform.Source;
 
11
import javax.xml.transform.Transformer;
 
12
import javax.xml.transform.TransformerFactory;
 
13
import javax.xml.transform.dom.DOMSource;
 
14
import javax.xml.transform.stream.StreamResult;
 
15
 
 
16
import org.herac.tuxguitar.io.base.TGFileFormatException;
 
17
import org.herac.tuxguitar.player.base.MidiInstrument;
 
18
import org.herac.tuxguitar.song.managers.TGSongManager;
 
19
import org.herac.tuxguitar.song.models.TGBeat;
 
20
import org.herac.tuxguitar.song.models.TGDuration;
 
21
import org.herac.tuxguitar.song.models.TGMeasure;
 
22
import org.herac.tuxguitar.song.models.TGNote;
 
23
import org.herac.tuxguitar.song.models.TGSong;
 
24
import org.herac.tuxguitar.song.models.TGString;
 
25
import org.herac.tuxguitar.song.models.TGTempo;
 
26
import org.herac.tuxguitar.song.models.TGTimeSignature;
 
27
import org.herac.tuxguitar.song.models.TGTrack;
 
28
import org.herac.tuxguitar.song.models.TGTupleto;
 
29
import org.w3c.dom.Attr;
 
30
import org.w3c.dom.Document;
 
31
import org.w3c.dom.Node;
 
32
 
 
33
public class MusicXMLWriter {
 
34
        
 
35
        private static final String[] NOTE_NAMES = new String[]{"C","D","E","F","G","A","B"};
 
36
        
 
37
        private static final int NOTE_SHARPS[] = new int[]{0,0,1,1,2,3,3,4,4,5,5,6};
 
38
        
 
39
        private static final int NOTE_FLATS[] = new int[]{0,1,1,2,2,3,4,4,5,5,6,6};
 
40
        
 
41
        private static final boolean[] NOTE_ALTERATIONS = new boolean[]{false,true,false,true,false,false,true,false,true,false,true,false};
 
42
        
 
43
        private static final String[] DURATION_NAMES = new String[]{ "whole", "half", "quarter", "eighth", "16th", "32nd", "64th", };
 
44
        
 
45
        private static final int DURATION_DIVISIONS = (int)TGDuration.QUARTER_TIME;
 
46
        
 
47
        private static final int[] DURATION_VALUES = new int[]{
 
48
                DURATION_DIVISIONS * 4, // WHOLE
 
49
                DURATION_DIVISIONS * 2, // HALF
 
50
                DURATION_DIVISIONS * 1, // QUARTER
 
51
                DURATION_DIVISIONS / 2, // EIGHTH
 
52
                DURATION_DIVISIONS / 4, // SIXTEENTH
 
53
                DURATION_DIVISIONS / 8, // THIRTY_SECOND
 
54
                DURATION_DIVISIONS / 16, // SIXTY_FOURTH
 
55
        };
 
56
        
 
57
        private TGSongManager manager;
 
58
        
 
59
        private OutputStream stream;
 
60
        
 
61
        private Document document;
 
62
        
 
63
        public MusicXMLWriter(OutputStream stream){
 
64
                this.stream = stream;
 
65
        }
 
66
        
 
67
        public void writeSong(TGSong song) throws TGFileFormatException{
 
68
                try {
 
69
                        this.manager = new TGSongManager();
 
70
                        this.manager.setSong(song);
 
71
                        this.document = newDocument();
 
72
                        
 
73
                        Node node = this.addNode(this.document,"score-partwise");
 
74
                        this.writeHeaders(node);
 
75
                        this.writeSong(node);
 
76
                        this.saveDocument();
 
77
                        
 
78
                        this.stream.flush();
 
79
                        this.stream.close();
 
80
                }catch(Throwable throwable){
 
81
                        throw new TGFileFormatException("Could not write song!.",throwable);
 
82
                }
 
83
        }
 
84
        
 
85
        private void writeHeaders(Node parent){
 
86
                this.writeWork(parent);
 
87
                this.writeIdentification(parent);
 
88
        }
 
89
        
 
90
        private void writeWork(Node parent){
 
91
                this.addNode(this.addNode(parent,"work"),"work-title",this.manager.getSong().getName());
 
92
        }
 
93
        
 
94
        private void writeIdentification(Node parent){
 
95
                Node identification = this.addNode(parent,"identification");
 
96
                this.addNode(this.addNode(identification,"encoding"), "software", "TuxGuitar");
 
97
                this.addAttribute(this.addNode(identification,"creator",this.manager.getSong().getAuthor()),"type","composer");
 
98
        }
 
99
        
 
100
        private void writeSong(Node parent){
 
101
                this.writePartList(parent);
 
102
                this.writeParts(parent);
 
103
        }
 
104
        
 
105
        private void writePartList(Node parent){
 
106
                Node partList = this.addNode(parent,"part-list");
 
107
                
 
108
                Iterator tracks = this.manager.getSong().getTracks();
 
109
                while(tracks.hasNext()){
 
110
                        TGTrack track = (TGTrack)tracks.next();
 
111
                        
 
112
                        Node scoreParts = this.addNode(partList,"score-part");
 
113
                        this.addAttribute(scoreParts, "id", "P" + track.getNumber());
 
114
                        
 
115
                        this.addNode(scoreParts, "part-name", track.getName());
 
116
                        
 
117
                        Node scoreInstrument = this.addAttribute(this.addNode(scoreParts, "score-instrument"), "id", "P" + track.getNumber() + "-I1");
 
118
                        this.addNode(scoreInstrument, "instrument-name",MidiInstrument.INSTRUMENT_LIST[track.getChannel().getInstrument()].getName());
 
119
                        
 
120
                        Node midiInstrument = this.addAttribute(this.addNode(scoreParts, "midi-instrument"), "id", "P" + track.getNumber() + "-I1");
 
121
                        this.addNode(midiInstrument, "midi-channel",Integer.toString(track.getChannel().getChannel() + 1));
 
122
                        this.addNode(midiInstrument, "midi-program",Integer.toString(track.getChannel().getInstrument() + 1));
 
123
                }
 
124
        }
 
125
        
 
126
        private void writeParts(Node parent){
 
127
                Iterator tracks = this.manager.getSong().getTracks();
 
128
                while(tracks.hasNext()){
 
129
                        TGTrack track = (TGTrack)tracks.next();
 
130
                        Node part = this.addAttribute(this.addNode(parent,"part"), "id", "P" + track.getNumber());
 
131
                        
 
132
                        TGMeasure previous = null;
 
133
                        
 
134
                        Iterator measures = track.getMeasures();
 
135
                        while(measures.hasNext()){
 
136
                                TGMeasure measure = (TGMeasure)measures.next();
 
137
                                Node measureNode = this.addAttribute(this.addNode(part,"measure"), "number",Integer.toString(measure.getNumber()));
 
138
                                
 
139
                                this.writeMeasureAttributes(measureNode, measure, previous);
 
140
                                this.writeDirection(measureNode, measure, previous);
 
141
                                this.writeBeats(measureNode, measure);
 
142
                                
 
143
                                previous = measure;
 
144
                        }
 
145
                }
 
146
        }
 
147
        
 
148
        private void writeMeasureAttributes(Node parent,TGMeasure measure, TGMeasure previous){
 
149
                boolean divisionChanges = (previous == null);
 
150
                boolean keyChanges = (previous == null || measure.getKeySignature() != previous.getKeySignature());
 
151
                boolean clefChanges = (previous == null || measure.getClef() != previous.getClef());
 
152
                boolean timeSignatureChanges = (previous == null || !measure.getTimeSignature().isEqual(previous.getTimeSignature()));
 
153
                boolean tuningChanges = (measure.getNumber() == 1);
 
154
                if(divisionChanges || keyChanges || clefChanges || timeSignatureChanges){
 
155
                        Node measureAttributes = this.addNode(parent,"attributes");
 
156
                        if(divisionChanges){
 
157
                                this.addNode(measureAttributes,"divisions",Integer.toString(DURATION_DIVISIONS));
 
158
                        }
 
159
                        if(keyChanges){
 
160
                                this.writeKeySignature(measureAttributes, measure.getKeySignature());
 
161
                        }
 
162
                        if(clefChanges){
 
163
                                this.writeClef(measureAttributes,measure.getClef());
 
164
                        }
 
165
                        if(timeSignatureChanges){
 
166
                                this.writeTimeSignature(measureAttributes,measure.getTimeSignature());
 
167
                        }
 
168
                        if(tuningChanges){
 
169
                                this.writeTuning(measureAttributes, measure.getTrack());
 
170
                        }
 
171
                }
 
172
        }
 
173
        
 
174
        private void writeTuning(Node parent, TGTrack track){
 
175
                Node staffDetailsNode = this.addNode(parent,"staff-details");
 
176
                this.addNode(staffDetailsNode, "staff-lines", Integer.toString( track.stringCount() ));
 
177
                for( int i = track.stringCount() ; i > 0 ; i --){
 
178
                        TGString string = track.getString( i );
 
179
                        Node stringNode = this.addNode(staffDetailsNode, "staff-tuning");
 
180
                        this.addAttribute(stringNode, "line", Integer.toString( (track.stringCount() - string.getNumber()) + 1 ) );
 
181
                        this.addNode(stringNode, "tuning-step", NOTE_NAMES[ NOTE_SHARPS[ (string.getValue() % 12) ] ] );
 
182
                        this.addNode(stringNode, "tuning-octave", Integer.toString(string.getValue() / 12) );
 
183
                }
 
184
        }
 
185
        
 
186
        private void writeTimeSignature(Node parent, TGTimeSignature ts){
 
187
                Node node = this.addNode(parent,"time");
 
188
                this.addNode(node,"beats",Integer.toString(ts.getNumerator()));
 
189
                this.addNode(node,"beat-type",Integer.toString(ts.getDenominator().getValue()));
 
190
        }
 
191
        
 
192
        private void writeKeySignature(Node parent, int ks){
 
193
                int value = ks;
 
194
                if(value != 0){
 
195
                        value = ( (((ks - 1) % 7) + 1) * ( ks > 7?-1:1));
 
196
                }
 
197
                Node key = this.addNode(parent,"key");
 
198
                this.addNode(key,"fifths",Integer.toString( value ));
 
199
                this.addNode(key,"mode","major");
 
200
        }
 
201
        
 
202
        private void writeClef(Node parent, int clef){
 
203
                Node node = this.addNode(parent,"clef");
 
204
                if(clef == TGMeasure.CLEF_TREBLE){
 
205
                        this.addNode(node,"sign","G");
 
206
                        this.addNode(node,"line","2");
 
207
                }
 
208
                else if(clef == TGMeasure.CLEF_BASS){
 
209
                        this.addNode(node,"sign","F");
 
210
                        this.addNode(node,"line","4");
 
211
                }
 
212
                else if(clef == TGMeasure.CLEF_TENOR){
 
213
                        this.addNode(node,"sign","G");
 
214
                        this.addNode(node,"line","2");
 
215
                }
 
216
                else if(clef == TGMeasure.CLEF_ALTO){
 
217
                        this.addNode(node,"sign","G");
 
218
                        this.addNode(node,"line","2");
 
219
                }
 
220
        }
 
221
        
 
222
        private void writeDirection(Node parent, TGMeasure measure, TGMeasure previous){
 
223
                boolean tempoChanges = (previous == null || measure.getTempo().getValue() != previous.getTempo().getValue());
 
224
                
 
225
                if(tempoChanges){
 
226
                        Node direction = this.addAttribute(this.addNode(parent,"direction"),"placement","above");
 
227
                        this.writeMeasureTempo(direction, measure.getTempo());
 
228
                }
 
229
        }
 
230
        
 
231
        private void writeMeasureTempo(Node parent,TGTempo tempo){
 
232
                this.addAttribute(this.addNode(parent,"sound"),"tempo",Integer.toString(tempo.getValue()));
 
233
        }
 
234
        
 
235
        private void writeBeats(Node parent, TGMeasure measure){
 
236
                int ks = measure.getKeySignature();
 
237
                int beatCount = measure.countBeats();
 
238
                for(int b = 0; b < beatCount; b ++){
 
239
                        TGBeat beat = measure.getBeat( b );
 
240
                        
 
241
                        if(beat.isRestBeat()){
 
242
                                Node noteNode = this.addNode(parent,"note");
 
243
                                this.addNode(noteNode,"rest");
 
244
                                this.addNode(noteNode,"voice","1");
 
245
                                this.writeDuration(noteNode, beat.getDuration());
 
246
                        }
 
247
                        else{
 
248
                                int noteCount = beat.countNotes();
 
249
                                for(int n = 0; n < noteCount; n ++){
 
250
                                        TGNote note = beat.getNote( n );
 
251
                                        
 
252
                                        Node noteNode = this.addNode(parent,"note");
 
253
                                        int value = (note.getBeat().getMeasure().getTrack().getString(note.getString()).getValue() + note.getValue());
 
254
                                        
 
255
                                        Node pitchNode = this.addNode(noteNode,"pitch");
 
256
                                        this.addNode(pitchNode,"step",NOTE_NAMES[ (ks <= 7 ? NOTE_SHARPS[value % 12] : NOTE_FLATS[value % 12] )]);
 
257
                                        this.addNode(pitchNode,"octave",Integer.toString(value / 12));
 
258
                                        if(NOTE_ALTERATIONS[ value % 12 ]){
 
259
                                                this.addNode(pitchNode,"alter", ( ks <= 7 ? "1" : "-1" ) );
 
260
                                        }
 
261
                                        
 
262
                                        Node technicalNode = this.addNode(this.addNode(noteNode, "notations"), "technical");
 
263
                                        this.addNode(technicalNode,"fret", Integer.toString( note.getValue() ));
 
264
                                        this.addNode(technicalNode,"string", Integer.toString( note.getString() ));
 
265
                                        
 
266
                                        this.addNode(noteNode,"voice","1");
 
267
                                        this.writeDuration(noteNode, beat.getDuration());
 
268
                                        
 
269
                                        if(note.isTiedNote()){
 
270
                                                this.addAttribute(this.addNode(noteNode,"tie"),"type","stop");
 
271
                                        }
 
272
                                        if(n > 0){
 
273
                                                this.addNode(noteNode,"chord");
 
274
                                        }
 
275
                                }
 
276
                        }
 
277
                }
 
278
        }
 
279
        
 
280
        private void writeDuration(Node parent, TGDuration duration){
 
281
                int index = duration.getIndex();
 
282
                if( index >=0 && index <= 6 ){
 
283
                        int value = (DURATION_VALUES[ index ] * duration.getTupleto().getTimes() / duration.getTupleto().getEnters());
 
284
                        if(duration.isDotted()){
 
285
                                value += (value / 2);
 
286
                        }
 
287
                        else if(duration.isDoubleDotted()){
 
288
                                value += ((value / 4) * 3);
 
289
                        }
 
290
                        
 
291
                        this.addNode(parent,"duration",Integer.toString(value));
 
292
                        this.addNode(parent,"type",DURATION_NAMES[ index ]);
 
293
                        
 
294
                        if(duration.isDotted()){
 
295
                                this.addNode(parent,"dot");
 
296
                        }
 
297
                        else if(duration.isDoubleDotted()){
 
298
                                this.addNode(parent,"dot");
 
299
                                this.addNode(parent,"dot");
 
300
                        }
 
301
                        
 
302
                        if(!duration.getTupleto().isEqual(TGTupleto.NORMAL)){
 
303
                                Node tupleto = this.addNode(parent,"time-modification");
 
304
                                this.addNode(tupleto,"actual-notes",Integer.toString(duration.getTupleto().getEnters()));
 
305
                                this.addNode(tupleto,"normal-notes",Integer.toString(duration.getTupleto().getTimes()));
 
306
                        }
 
307
                }
 
308
        }
 
309
        
 
310
        private Node addAttribute(Node node, String name, String value){
 
311
                Attr attribute = this.document.createAttribute(name);
 
312
                attribute.setNodeValue(value);
 
313
                node.getAttributes().setNamedItem(attribute);
 
314
                return node;
 
315
        }
 
316
        
 
317
        private Node addNode(Node parent, String name){
 
318
                Node node = this.document.createElement(name);
 
319
                parent.appendChild(node);
 
320
                return node;
 
321
        }
 
322
        
 
323
        private Node addNode(Node parent, String name, String content){
 
324
                Node node = this.addNode(parent, name);
 
325
                node.setTextContent(content);
 
326
                return node;
 
327
        }
 
328
        
 
329
        private Document newDocument() {
 
330
                try {
 
331
                        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 
332
                        DocumentBuilder builder = factory.newDocumentBuilder();
 
333
                        Document document = builder.newDocument();
 
334
                        return document;
 
335
                }catch(Throwable throwable){
 
336
                        throwable.printStackTrace();
 
337
                }
 
338
                return null;
 
339
        }
 
340
        
 
341
        private void saveDocument() {
 
342
                try {
 
343
                        TransformerFactory xformFactory = TransformerFactory.newInstance();
 
344
                        Transformer idTransform = xformFactory.newTransformer();
 
345
                        Source input = new DOMSource(this.document);
 
346
                        Result output = new StreamResult(this.stream);
 
347
                        idTransform.setOutputProperty(OutputKeys.INDENT, "yes");
 
348
                        idTransform.transform(input, output);
 
349
                }catch(Throwable throwable){
 
350
                        throwable.printStackTrace();
 
351
                }
 
352
        }
 
353
}