Add Ogg Vorbis identifier.
[sonitus.git] / src / main / java / net / pterodactylus / sonitus / io / OggVorbisIdentifier.java
1 /*
2  * Sonitus - OggVorbisIdentifier.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sonitus.io;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22
23 import net.pterodactylus.sonitus.data.Format;
24
25 import com.jcraft.jogg.Packet;
26 import com.jcraft.jogg.Page;
27 import com.jcraft.jogg.StreamState;
28 import com.jcraft.jogg.SyncState;
29 import com.jcraft.jorbis.Comment;
30 import com.jcraft.jorbis.Info;
31
32 /**
33  * Identifies Ogg Vorbis files.
34  *
35  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
36  */
37 public class OggVorbisIdentifier {
38
39         /** The default size of the read buffer. */
40         private static final int BUFFER_SIZE = 4096;
41
42         /** Suppress default constructor. */
43         private OggVorbisIdentifier() {
44                 /* nothing here. */
45         }
46
47         /**
48          * Tries to parse the given stream as Ogg Vorbis file and returns a {@link
49          * Format} describing the stream.
50          *
51          * @param inputStream
52          * @return
53          * @throws IOException
54          */
55         public static Format identify(InputStream inputStream) throws IOException {
56
57                 /* stuff needed to decode Ogg. */
58                 Packet packet = new Packet();
59                 Page page = new Page();
60                 StreamState streamState = new StreamState();
61                 SyncState syncState = new SyncState();
62
63                 /* stuff needed to decode Vorbis. */
64                 Comment comment = new Comment();
65                 Info info = new Info();
66
67                 /* initialize jorbis. */
68                 syncState.init();
69                 int bufferSize = BUFFER_SIZE;
70                 int index = syncState.buffer(bufferSize);
71                 byte[] buffer = syncState.data;
72
73                 boolean streamStateInitialized = false;
74                 int packetsRead = 0;
75
76                 /* read until we have read the three packets required to decode the header. */
77                 while (packetsRead < 3) {
78                         int read = inputStream.read(buffer, index, bufferSize);
79                         syncState.wrote(read);
80                         switch (syncState.pageout(page)) {
81                                 case -1:
82                                         throw new IOException("Hole in Ogg data!");
83                                 case 1:
84                                         if (!streamStateInitialized) {
85                                                 /* init stream state. */
86                                                 streamState.init(page.serialno());
87                                                 streamState.reset();
88                                                 info.init();
89                                                 comment.init();
90                                                 streamStateInitialized = true;
91                                         }
92                                         if (streamState.pagein(page) == -1) {
93                                                 throw new IOException("Error parsing Ogg data!");
94                                         }
95                                         switch (streamState.packetout(packet)) {
96                                                 case -1:
97                                                         throw new IOException("Error parsing Ogg data!");
98                                                 case 1:
99                                                         info.synthesis_headerin(comment, packet);
100                                                         packetsRead++;
101                                                 default:
102                                                         /* continue. */
103                                         }
104
105                                 default:
106                                         /* continue. */
107                         }
108                         index = syncState.buffer(bufferSize);
109                         buffer = syncState.data;
110                 }
111
112                 return new Format(info.channels, info.rate, "Vorbis");
113         }
114
115 }