streams, byte[] data) {
+ if (Objects.nonNull(data) && data.length > 0)
+ streams.add(new ByteArrayInputStream(data));
+ }
+
+ private static final class OggStreamPacketDecorator extends FlacFrame implements OggStreamPacket {
+
+ private OggStreamPacket decorated;
+
+ public OggStreamPacketDecorator(OggStreamPacket decorated) {
+ this.decorated = decorated;
+ }
+
+ public byte[] getData() {
+ return decorated.getData();
+ }
+
+ @Override
+ public void setData(byte[] data) {
+
+ }
+
+ @Override
+ public int getOggOverheadSize() {
+ return 0;
+ }
+
+ @Override
+ public OggPacket write() {
+ return null;
+ }
+
+ }
}
diff --git a/core/src/main/java/org/gagravarr/flac/FlacOggFile.java b/core/src/main/java/org/gagravarr/flac/FlacOggFile.java
index 8635728..1a200af 100644
--- a/core/src/main/java/org/gagravarr/flac/FlacOggFile.java
+++ b/core/src/main/java/org/gagravarr/flac/FlacOggFile.java
@@ -18,6 +18,7 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -223,6 +224,11 @@ public void close() throws IOException {
}
}
+ @Override
+ public InputStream getInputStream() {
+ return null;
+ }
+
/**
* Return the Ogg-specific version of the Flac Info
*/
diff --git a/core/src/main/java/org/gagravarr/flac/FlacTags.java b/core/src/main/java/org/gagravarr/flac/FlacTags.java
index c1adc53..4c9448b 100644
--- a/core/src/main/java/org/gagravarr/flac/FlacTags.java
+++ b/core/src/main/java/org/gagravarr/flac/FlacTags.java
@@ -15,6 +15,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Arrays;
import org.gagravarr.ogg.IOUtils;
import org.gagravarr.ogg.OggPacket;
@@ -24,75 +25,81 @@
/**
* This is a {@link VorbisComments} with a Flac metadata
- * block header, rather than the usual vorbis one.
+ * block header, rather than the usual vorbis one.
*/
public class FlacTags extends VorbisStyleComments implements OggAudioTagsHeader {
- public FlacTags(OggPacket packet) {
- super(packet, 4);
-
- // Verify the type
- byte type = getData()[0];
- if(type != FlacMetadataBlock.VORBIS_COMMENT) {
- throw new IllegalArgumentException("Invalid type " + type);
- }
- }
- public FlacTags() {
- super();
- }
-
- /**
- * Type plus three byte length
- */
- @Override
- public int getHeaderSize() {
- return 4;
- }
- /**
- * Flac doesn't do the framing bit if the tags are
- * null padded.
- */
- @Override
- protected boolean hasFramingBit() {
- return false;
- }
- /**
- * Type plus three byte length
- */
- @Override
- public void populateMetadataHeader(byte[] b, int dataLength) {
- b[0] = FlacMetadataBlock.VORBIS_COMMENT;
- IOUtils.putInt3BE(b, 1, dataLength);
- }
- @Override
- protected void populateMetadataFooter(OutputStream out) {
- // No footer needed on FLAC Tag Packets
- }
-
- protected static class FlacTagsAsMetadata extends FlacMetadataBlock {
- private FlacTags tags;
- public FlacTagsAsMetadata(byte type, byte[] data) {
- super(type);
- // This is the only metadata which needs the type
- // and length in addition to the main data
- byte[] d = new byte[data.length+4];
- d[0] = FlacMetadataBlock.VORBIS_COMMENT;
- System.arraycopy(data, 0, d, 4, data.length);
- this.tags = new FlacTags(new OggPacket(d));
- }
+ public FlacTags(OggPacket packet) {
+ super(packet, 4);
- @Override
- public byte[] getData() {
- return tags.getData();
- }
-
- @Override
- protected void write(OutputStream out) throws IOException {
- throw new IllegalStateException("Must not call directly");
- }
+ // Verify the type
+ byte type = getData()[0];
+ if (type != FlacMetadataBlock.VORBIS_COMMENT) {
+ throw new IllegalArgumentException("Invalid type " + type);
+ }
+ }
- public FlacTags getTags() {
- return tags;
- }
- }
+ public FlacTags() {
+ super();
+ }
+
+ /**
+ * Type plus three byte length
+ */
+ @Override
+ public int getHeaderSize() {
+ return 4;
+ }
+
+ /**
+ * Flac doesn't do the framing bit if the tags are
+ * null padded.
+ */
+ @Override
+ protected boolean hasFramingBit() {
+ return false;
+ }
+
+ /**
+ * Type plus three byte length
+ */
+ @Override
+ public void populateMetadataHeader(byte[] b, int dataLength) {
+ b[0] = FlacMetadataBlock.VORBIS_COMMENT;
+ IOUtils.putInt3BE(b, 1, dataLength);
+ }
+
+ @Override
+ protected void populateMetadataFooter(OutputStream out) {
+ // No footer needed on FLAC Tag Packets
+ }
+
+ protected static class FlacTagsAsMetadata extends FlacMetadataBlock {
+ private FlacTags tags;
+
+ public FlacTagsAsMetadata(byte type, byte[] data) {
+ super(type);
+ // This is the only metadata which needs the type
+ // and length in addition to the main data
+ byte[] d = new byte[data.length + 4];
+ d[0] = FlacMetadataBlock.VORBIS_COMMENT;
+ System.arraycopy(data, 0, d, 4, data.length);
+ IOUtils.putInt3BE(d, 1, data.length);
+ this.tags = new FlacTags(new OggPacket(d));
+ }
+
+ @Override
+ public byte[] getData() {
+ return tags.getData();
+ }
+
+ @Override
+ protected void write(OutputStream out) throws IOException {
+ throw new IllegalStateException("Must not call directly");
+ }
+
+ public FlacTags getTags() {
+ return tags;
+ }
+ }
}
diff --git a/core/src/main/java/org/gagravarr/ogg/HighLevelOggStreamPacket.java b/core/src/main/java/org/gagravarr/ogg/HighLevelOggStreamPacket.java
index 68e728f..02bca91 100644
--- a/core/src/main/java/org/gagravarr/ogg/HighLevelOggStreamPacket.java
+++ b/core/src/main/java/org/gagravarr/ogg/HighLevelOggStreamPacket.java
@@ -15,9 +15,9 @@
/**
* A high level stream packet sat atop
- * of an OggPacket.
+ * of an OggPacket.
* Provides support for reading and writing
- * new and existing OggPacket instances.
+ * new and existing OggPacket instances.
*/
public abstract class HighLevelOggStreamPacket implements OggStreamPacket {
private OggPacket oggPacket;
@@ -26,6 +26,7 @@ public abstract class HighLevelOggStreamPacket implements OggStreamPacket {
protected HighLevelOggStreamPacket(OggPacket oggPacket) {
this.oggPacket = oggPacket;
}
+
protected HighLevelOggStreamPacket() {
this.oggPacket = null;
}
@@ -35,25 +36,30 @@ protected OggPacket getOggPacket() {
}
public byte[] getData() {
- if(data != null) {
+ return getDecoratedData();
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+
+ private byte[] getDecoratedData() {
+ if (data != null) {
return data;
}
- if(oggPacket != null) {
+ if (oggPacket != null) {
return oggPacket.getData();
}
return null;
}
- public void setData(byte[] data) {
- this.data = data;
- }
/**
* Returns the approximate number of bytes overhead
- * from the underlying {@link OggPacket} / {@link OggPage}
- * structures into which this data is stored.
+ * from the underlying {@link OggPacket} / {@link OggPage}
+ * structures into which this data is stored.
* Will return 0 for packets not yet associated with a page.
*
This information is normally only of interest to information,
- * diagnostic and debugging tools.
+ * diagnostic and debugging tools.
*/
public int getOggOverheadSize() {
if (oggPacket != null) {
@@ -64,7 +70,7 @@ public int getOggOverheadSize() {
}
public OggPacket write() {
- this.oggPacket = new OggPacket(getData());
+ this.oggPacket = new OggPacket(getDecoratedData());
return this.oggPacket;
}
}
diff --git a/core/src/main/java/org/gagravarr/vorbis/VorbisStyleComments.java b/core/src/main/java/org/gagravarr/vorbis/VorbisStyleComments.java
index 9858180..bd94ee4 100644
--- a/core/src/main/java/org/gagravarr/vorbis/VorbisStyleComments.java
+++ b/core/src/main/java/org/gagravarr/vorbis/VorbisStyleComments.java
@@ -30,7 +30,7 @@
/**
* General class for all Vorbis-style comments/tags, as used
- * by things like Vorbis, Opus and FLAC.
+ * by things like Vorbis, Opus and FLAC.
*/
public abstract class VorbisStyleComments extends HighLevelOggStreamPacket implements OggAudioTagsHeader {
public static final String KEY_ARTIST = "artist";
@@ -41,39 +41,39 @@ public abstract class VorbisStyleComments extends HighLevelOggStreamPacket imple
public static final String KEY_DATE = "date";
private String vendor;
- private Map> comments =
- new HashMap>();
+ private Map> comments = new HashMap>();
+ private boolean modified;
public VorbisStyleComments(OggPacket pkt, int dataBeginsAt) {
super(pkt);
byte[] d = pkt.getData();
int vlen = getInt4(d, dataBeginsAt);
- vendor = IOUtils.getUTF8(d, dataBeginsAt+4, vlen);
+ vendor = IOUtils.getUTF8(d, dataBeginsAt + 4, vlen);
int offset = dataBeginsAt + 4 + vlen;
int numComments = getInt4(d, offset);
offset += 4;
- for(int i=0; i= 0x20 && (int)c <= 0x7d &&
- (int)c != 0x3d) {
+ for (char c : tag.toLowerCase(Locale.ROOT).toCharArray()) {
+ if ((int) c >= 0x20 && (int) c <= 0x7d &&
+ (int) c != 0x3d) {
nt.append(c);
}
}
@@ -110,7 +111,7 @@ protected static String normaliseTag(String tag) {
protected String getSingleComment(String normalisedTag) {
List c = comments.get(normalisedTag);
- if(c != null && c.size() > 0) {
+ if (c != null && c.size() > 0) {
return c.get(0);
}
return null;
@@ -119,58 +120,64 @@ protected String getSingleComment(String normalisedTag) {
/**
* Returns the (first) Artist, or null if no
- * Artist tags present.
+ * Artist tags present.
*/
public String getArtist() {
return getSingleComment(KEY_ARTIST);
}
+
/**
* Returns the (first) Album, or null if no
- * Album tags present.
+ * Album tags present.
*/
public String getAlbum() {
return getSingleComment(KEY_ALBUM);
}
+
/**
* Returns the (first) Title, or null if no
- * Title tags present.
+ * Title tags present.
*/
public String getTitle() {
return getSingleComment(KEY_TITLE);
}
+
/**
* Returns the (first) Genre, or null if no
- * Genre tags present.
+ * Genre tags present.
*/
public String getGenre() {
return getSingleComment(KEY_GENRE);
}
+
/**
* Returns the (first) track number as a literal
- * string, eg "4" or "09", or null if
- * no track number tags present;
+ * string, eg "4" or "09", or null if
+ * no track number tags present;
*/
public String getTrackNumber() {
return getSingleComment(KEY_TRACKNUMBER);
}
+
/**
* Returns the track number, as converted into
- * an integer, or -1 if not available / not numeric
+ * an integer, or -1 if not available / not numeric
*/
public int getTrackNumberNumeric() {
String number = getTrackNumber();
- if(number == null) return -1;
+ if (number == null) return -1;
try {
return Integer.parseInt(number);
- } catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
return -1;
}
}
+
/**
* Returns the (first) Date, or null if no
- * Date tags present. Dates are normally stored
- * in ISO8601 date format, i.e. YYYY-MM-DD
+ * Date tags present. Dates are normally stored
+ * in ISO8601 date format, i.e. YYYY-MM-DD
*/
public String getDate() {
return getSingleComment("date");
@@ -178,12 +185,12 @@ public String getDate() {
/**
* Returns all comments for a given tag, in
- * file order. Will return an empty list for
- * tags which aren't present.
+ * file order. Will return an empty list for
+ * tags which aren't present.
*/
public List getComments(String tag) {
- List c = comments.get( normaliseTag(tag) );
- if(c == null) {
+ List c = comments.get(normaliseTag(tag));
+ if (c == null) {
return new ArrayList();
} else {
return c;
@@ -194,13 +201,16 @@ public List getComments(String tag) {
* Removes all comments for a given tag.
*/
public void removeComments(String tag) {
- comments.remove( normaliseTag(tag) );
+ comments.remove(normaliseTag(tag));
+ this.modified = true;
}
+
/**
* Removes all comments across all tags
*/
public void removeAllComments() {
comments.clear();
+ this.modified = true;
}
/**
@@ -208,21 +218,24 @@ public void removeAllComments() {
*/
public void addComment(String tag, String comment) {
String nt = normaliseTag(tag);
- if(! comments.containsKey(nt)) {
+ if (!comments.containsKey(nt)) {
comments.put(nt, new ArrayList());
}
comments.get(nt).add(comment);
+ this.modified = true;
}
+
/**
* Removes any existing comments for a given tag,
- * and replaces them with the supplied list
+ * and replaces them with the supplied list
*/
public void setComments(String tag, List comments) {
String nt = normaliseTag(tag);
- if(this.comments.containsKey(nt)) {
+ if (this.comments.containsKey(nt)) {
this.comments.remove(nt);
}
this.comments.put(nt, comments);
+ this.modified = true;
}
@@ -234,12 +247,24 @@ public Map> getAllComments() {
}
protected abstract int getHeaderSize();
+
protected abstract boolean hasFramingBit();
+
protected abstract void populateMetadataHeader(byte[] data, int packetLength);
+
protected abstract void populateMetadataFooter(OutputStream out);
protected int getInt4(byte[] d, int offset) {
- return (int)IOUtils.getInt4(d, offset);
+ return (int) IOUtils.getInt4(d, offset);
+ }
+
+ @Override
+ public byte[] getData() {
+ if (modified) {
+// write();
+ modified = false;
+ }
+ return super.getData();
}
@Override
@@ -256,7 +281,7 @@ public OggPacket write() {
// Next is the number of comments
int numComments = 0;
- for(List c : comments.values()) {
+ for (List c : comments.values()) {
numComments += c.size();
}
IOUtils.writeInt4(baos, numComments);
@@ -265,8 +290,8 @@ public OggPacket write() {
// an order, unit testing does!
String[] tags = comments.keySet().toArray(new String[comments.size()]);
Arrays.sort(tags);
- for(String tag : tags) {
- for(String value : comments.get(tag)) {
+ for (String tag : tags) {
+ for (String value : comments.get(tag)) {
String comment = tag + '=' + value;
IOUtils.writeUTF8WithLength(baos, comment);
@@ -275,7 +300,7 @@ public OggPacket write() {
// Do a header, if required for the format
populateMetadataFooter(baos);
- } catch(IOException e) {
+ } catch (IOException e) {
// Should never happen!
throw new RuntimeException(e);
}
diff --git a/core/src/test/java/org/gagravarr/flac/TestFlacComments.java b/core/src/test/java/org/gagravarr/flac/TestFlacComments.java
index 7584168..20d0b65 100644
--- a/core/src/test/java/org/gagravarr/flac/TestFlacComments.java
+++ b/core/src/test/java/org/gagravarr/flac/TestFlacComments.java
@@ -15,6 +15,10 @@
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import junit.framework.TestCase;
@@ -25,6 +29,7 @@ public class TestFlacComments extends TestCase {
private InputStream getTestOggFile() throws IOException {
return this.getClass().getResourceAsStream("/testFLAC.oga");
}
+
private InputStream getTestFlacFile() throws IOException {
return this.getClass().getResourceAsStream("/testFLAC.flac");
}
@@ -52,12 +57,40 @@ public void testReadOgg() throws IOException {
flac.close();
ogg.close();
}
+
public void testReadFlac() throws IOException {
FlacNativeFile flac = new FlacNativeFile(getTestFlacFile());
doTestComments(flac.getTags());
flac.close();
}
+ public void testWriteFlac() throws IOException {
+ Path tempFile = Files.createTempFile("test", ".flac");
+ try (FlacNativeFile flac = new FlacNativeFile(getTestFlacFile())) {
+ FlacTags tags = flac.getTags();
+ doTestComments(tags);
+ String newComment = "new_comment";
+ String someText = "some text";
+ tags.addComment(newComment, someText);
+
+ try (InputStream inputStream = flac.getInputStream()) {
+ Files.copy(inputStream, tempFile, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ try (FlacNativeFile changed = new FlacNativeFile(Files.newInputStream(tempFile))) {
+ FlacTags newTags = changed.getTags();
+ assertEquals(1, newTags.getComments(newComment).size());
+ assertEquals(someText, newTags.getComments(newComment).get(0));
+ }
+
+
+ } finally {
+// Files.deleteIfExists(tempFile);
+ Files.copy(tempFile, Paths.get("/Users/valenpo/Developer/logs/flac/stream.flac"), StandardCopyOption.REPLACE_EXISTING);
+ System.out.println(tempFile);
+ }
+ }
+
private void doTestComments(FlacTags tags) {
assertEquals("reference libFLAC 1.2.1 20070917", tags.getVendor());