View Javadoc

1   /*
2    * Entagged Audio Tag library
3    * Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
4    * 
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2.1 of the License, or (at your option) any later version.
9    *  
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   * 
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18   */
19  package entagged.audioformats.flac.util;
20  
21  import entagged.audioformats.*;
22  import entagged.audioformats.exceptions.*;
23  import entagged.audioformats.ogg.OggTag;
24  
25  import java.io.*;
26  import java.nio.channels.*;
27  import java.util.*;
28  
29  
30  public class FlacTagWriter {
31  	
32  	private Vector metadataBlockPadding = new Vector(1);
33  	private Vector metadataBlockApplication = new Vector(1);
34  	private Vector metadataBlockSeekTable = new Vector(1);
35  	private Vector metadataBlockCueSheet = new Vector(1);
36  
37  	private FlacTagCreator tc = new FlacTagCreator();
38  	private FlacTagReader reader = new FlacTagReader();
39  	
40  	public void delete(RandomAccessFile raf,  RandomAccessFile tempRaf) throws IOException, CannotWriteException {
41  		OggTag tag = null;
42  		try {
43  			tag = reader.read(raf);
44  		} catch(CannotReadException e) {
45  			write(new OggTag(), raf, tempRaf);
46  			return;
47  		}
48  		
49  		OggTag emptyTag = new OggTag();
50  		emptyTag.setVendor(tag.getVendor());
51  		
52  		raf.seek(0);
53  		tempRaf.seek(0);
54  		
55  		write(emptyTag, raf, tempRaf);
56  	}
57  	
58  	public void write( Tag tag, RandomAccessFile raf, RandomAccessFile rafTemp ) throws CannotWriteException, IOException {
59  		//Clean up old datas
60  		metadataBlockPadding.removeAllElements();
61  		metadataBlockApplication.removeAllElements();
62  		metadataBlockSeekTable.removeAllElements();
63  		metadataBlockCueSheet.removeAllElements();
64  		
65  		byte[] b = new byte[4];
66  		raf.readFully(b);
67  		
68  		String flac = new String(b);
69  		if(!flac.equals("fLaC"))
70  			throw new CannotWriteException("This is not a FLAC file");
71  		
72  		boolean isLastBlock = false;
73  		while(!isLastBlock) {
74  			b = new byte[4];
75  			raf.readFully(b);
76  			MetadataBlockHeader mbh = new MetadataBlockHeader(b);
77  		
78  			//System.err.println("BlockType: "+mbh.getBlockTypeString());
79  			switch(mbh.getBlockType()) {
80  				case MetadataBlockHeader.VORBIS_COMMENT : handlePadding(mbh, raf); break;
81  				case MetadataBlockHeader.PADDING : handlePadding(mbh, raf); break;
82  				case MetadataBlockHeader.APPLICATION : handleApplication(mbh, raf); break;
83  				case MetadataBlockHeader.SEEKTABLE : handleSeekTable(mbh, raf); break;
84  				case MetadataBlockHeader.CUESHEET : handleCueSheet(mbh, raf); break;
85  				default : skipBlock(mbh, raf); break;
86  			}
87  
88  			isLastBlock = mbh.isLastBlock();
89  		}
90  		
91  		int availableRoom =  computeAvailableRoom();
92  		int newTagSize = tc.getTagLength(tag);
93  		int neededRoom = newTagSize + computeNeededRoom();
94  		//System.err.println("Av.:"+availableRoom+"|Needed:"+neededRoom);
95  		raf.seek(0);
96  		
97  		if(availableRoom>=neededRoom) {
98  			//OVERWRITE EXISTING TAG
99  			raf.seek(42);
100 		
101 			for(int i = 0; i<metadataBlockApplication.size(); i++) {
102 				raf.write( ((MetadataBlock) metadataBlockApplication.elementAt(i)).getHeader().getBytes());
103 				raf.write( ((MetadataBlock) metadataBlockApplication.elementAt(i)).getData().getBytes());
104 			}
105 
106 			for(int i = 0; i<metadataBlockSeekTable.size(); i++) {
107 				raf.write( ((MetadataBlock) metadataBlockSeekTable.elementAt(i)).getHeader().getBytes());
108 				raf.write( ((MetadataBlock) metadataBlockSeekTable.elementAt(i)).getData().getBytes());
109 			}
110 		
111 			for(int i = 0; i<metadataBlockCueSheet.size(); i++) {
112 				raf.write( ((MetadataBlock) metadataBlockCueSheet.elementAt(i)).getHeader().getBytes());
113 				raf.write( ((MetadataBlock) metadataBlockCueSheet.elementAt(i)).getData().getBytes());
114 			}
115 		
116 			raf.getChannel().write( tc.convert(tag, availableRoom-neededRoom ) );
117 		
118 		} else {
119 			//create new tag with padding (we remove the header and keep the audio data)
120 			
121 			FileChannel fc = raf.getChannel();
122 			b = new byte[42];
123 			raf.readFully(b);
124 			raf.seek(availableRoom+42);
125 
126 			FileChannel tempFC = rafTemp.getChannel();
127 			
128 			rafTemp.write( b );
129 			
130 			for(int i = 0; i<metadataBlockApplication.size(); i++) {
131 				rafTemp.write( ((MetadataBlock) metadataBlockApplication.elementAt(i)).getHeader().getBytes());
132 				rafTemp.write( ((MetadataBlock) metadataBlockApplication.elementAt(i)).getData().getBytes());
133 			}
134 
135 			for(int i = 0; i<metadataBlockSeekTable.size(); i++) {
136 				rafTemp.write( ((MetadataBlock) metadataBlockSeekTable.elementAt(i)).getHeader().getBytes());
137 				rafTemp.write( ((MetadataBlock) metadataBlockSeekTable.elementAt(i)).getData().getBytes());
138 			}
139 		
140 			for(int i = 0; i<metadataBlockCueSheet.size(); i++) {
141 				rafTemp.write( ((MetadataBlock) metadataBlockCueSheet.elementAt(i)).getHeader().getBytes());
142 				rafTemp.write( ((MetadataBlock) metadataBlockCueSheet.elementAt(i)).getData().getBytes());
143 			}
144 			
145 			rafTemp.write( tc.convert(tag, FlacTagCreator.DEFAULT_PADDING ).array());
146 			
147 			tempFC.transferFrom( fc, tempFC.position(), fc.size() );
148 		}
149 	}
150 	
151 	private int computeAvailableRoom() {
152 		int length = 0;
153 		
154 		for(int i = 0; i<metadataBlockApplication.size(); i++)
155 			length += ((MetadataBlock) metadataBlockApplication.elementAt(i)).getLength();
156 		
157 		for(int i = 0; i<metadataBlockSeekTable.size(); i++)
158 			length += ((MetadataBlock) metadataBlockSeekTable.elementAt(i)).getLength();
159 		
160 		for(int i = 0; i<metadataBlockCueSheet.size(); i++)
161 			length += ((MetadataBlock) metadataBlockCueSheet.elementAt(i)).getLength();
162 		
163 		for(int i = 0; i<metadataBlockPadding.size(); i++)
164 			length += ((MetadataBlock) metadataBlockPadding.elementAt(i)).getLength();
165 		
166 		return length;
167 	}
168 	
169 	private int computeNeededRoom() {
170 		int length = 0;
171 		
172 		for(int i = 0; i<metadataBlockApplication.size(); i++)
173 			length += ((MetadataBlock) metadataBlockApplication.elementAt(i)).getLength();
174 		
175 		for(int i = 0; i<metadataBlockSeekTable.size(); i++)
176 			length += ((MetadataBlock) metadataBlockSeekTable.elementAt(i)).getLength();
177 		
178 		for(int i = 0; i<metadataBlockCueSheet.size(); i++)
179 			length += ((MetadataBlock) metadataBlockCueSheet.elementAt(i)).getLength();
180 		
181 		return length;
182 	}
183 		
184 	
185 	private void skipBlock(MetadataBlockHeader mbh, RandomAccessFile raf) throws IOException {
186 		raf.seek(raf.getFilePointer()+mbh.getDataLength());
187 	}
188 	
189 	
190 	private void handlePadding(MetadataBlockHeader mbh, RandomAccessFile raf) throws IOException {
191 		raf.seek(raf.getFilePointer()+mbh.getDataLength());
192 		
193 		MetadataBlockData mbd = new MetadataBlockDataPadding( mbh.getDataLength() );
194 		metadataBlockPadding.add(new MetadataBlock(mbh, mbd));
195 	}
196 	
197 	private void handleApplication(MetadataBlockHeader mbh, RandomAccessFile raf) throws IOException {
198 		byte[] b = new byte[mbh.getDataLength()];
199 		raf.readFully(b);
200 			
201 		MetadataBlockData mbd = new MetadataBlockDataApplication(b);
202 		metadataBlockApplication.add(new MetadataBlock(mbh, mbd));
203 	}
204 	
205 	private void handleSeekTable(MetadataBlockHeader mbh, RandomAccessFile raf) throws IOException {
206 		byte[] b = new byte[mbh.getDataLength()];
207 		raf.readFully(b);
208 			
209 		MetadataBlockData mbd = new MetadataBlockDataSeekTable(b);
210 		metadataBlockSeekTable.add(new MetadataBlock(mbh, mbd));
211 	}
212 	
213 	private void handleCueSheet(MetadataBlockHeader mbh, RandomAccessFile raf) throws IOException {
214 		byte[] b = new byte[mbh.getDataLength()];
215 		raf.readFully(b);
216 			
217 		MetadataBlockData mbd = new MetadataBlockDataCueSheet(b);
218 		metadataBlockCueSheet.add(new MetadataBlock(mbh, mbd));
219 	}
220 
221 }
222