Package org.cmc.music.myid3.id3v2

Source Code of org.cmc.music.myid3.id3v2.MyID3v2Write$Filter

package org.cmc.music.myid3.id3v2;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;

import org.cmc.music.common.ID3FrameType;
import org.cmc.music.common.ID3WriteException;
import org.cmc.music.metadata.IMusicMetadata;
import org.cmc.music.metadata.ImageData;
import org.cmc.music.metadata.MusicMetadata;
import org.cmc.music.metadata.MusicMetadataSet;
import org.cmc.music.myid3.MyID3Listener;
import org.cmc.music.util.Debug;

public class MyID3v2Write implements MyID3v2Constants
{

  private final int id3v2_version = 3;

  private byte[] getHeaderFooter(int body_length, boolean is_footer)
      throws ID3WriteException
  {
    byte result[] = new byte[10];

    int index = 0;
    if (is_footer)
    {
      result[index++] = 0x33; // 3
      result[index++] = 0x44; // D
      result[index++] = 0x49; // I
    } else
    {
      result[index++] = 0x49; // I
      result[index++] = 0x44; // D
      result[index++] = 0x33; // 3
    }

    if (id3v2_version == 4)
      result[index++] = 0x04; // version
    else if (id3v2_version == 3)
      result[index++] = 0x03; // version
    else
      throw new ID3WriteException("id3v2_version: " + id3v2_version);

    result[index++] = 0x00;

    int flags = 0; // charles
    if (id3v2_version == 4)
      flags |= HEADER_FLAG_ID3v24_FOOTER_PRESENT;
    else if (id3v2_version == 3)
    {
    } else
      throw new ID3WriteException("id3v2_version: " + id3v2_version);

    result[index++] = (byte) flags;

    writeSynchSafeInt(result, index, body_length);

    return result;
  }

  private final void writeSynchSafeInt(byte bytes[], int start, int value)
      throws ID3WriteException
  {
    bytes[start + 3] = (byte) (value & 0x7f);
    value >>= 7;
    bytes[start + 2] = (byte) (value & 0x7f);
    value >>= 7;
    bytes[start + 1] = (byte) (value & 0x7f);
    value >>= 7;
    bytes[start + 0] = (byte) (value & 0x7f);

    value >>= 7;
    if (value != 0)
      throw new ID3WriteException("Value to large for synch safe int: "
          + value);
  }

  private ID3v2OutputFrame toFrame(String longFrameID, Number frameOrder,
      String value1, String value2) throws UnsupportedEncodingException,
      IOException
  {
    if (longFrameID.startsWith("T"))
    {
      return toFrameText(longFrameID, frameOrder, value1, value2);
    } else if (longFrameID.equals("COMM"))
    {
      return toFrameCOMM(longFrameID, frameOrder, value1);
    } else
    {
      // TODO: should we throw an exception here?
      Debug.debug();
      Debug.debug("frame_type.long_id", longFrameID);
      Debug.debug("not text");
      Debug.dumpStack();
      return null;
    }
  }

  private static boolean canEncodeStringInISO(String s)
      throws UnsupportedEncodingException
  {
    byte bytes[] = s.getBytes(CHAR_ENCODING_ISO);
    String check1 = new String(bytes, CHAR_ENCODING_ISO);

    return check1.equals(s);
  }

  private static byte[] encodeString(String s, boolean use_iso)
      throws UnsupportedEncodingException, IOException
  {
    if (use_iso)
      return s.getBytes(CHAR_ENCODING_ISO);
    else
    {
      byte bytes[] = s.getBytes(CHAR_ENCODING_UTF_16);

      // Windows Media Player can't handle UTF-16, big-endian.
      // switch to UTF-16, little-endian.
      if (((0xff & bytes[0]) == 0xFE) && ((0xff & bytes[1]) == 0xFF))
      {
        // manually switch UTF 16 byte order
        for (int i = 0; i < bytes.length; i += 2)
        {
          byte temp = bytes[i];
          bytes[i] = bytes[i + 1];
          bytes[i + 1] = temp;
        }
      }
      return bytes;
    }
  }

  // TODO: convert Object params to String params.
  private static ID3v2OutputFrame toFrameText(String longFrameID,
      Number frameOrder, String value1, String value2)
      throws UnsupportedEncodingException, IOException
  {
    boolean use_iso = canEncodeStringInISO(value1);
    if (value2 != null)
      use_iso &= canEncodeStringInISO(value2);

    int char_encoding_code = use_iso ? CHAR_ENCODING_CODE_ISO_8859_1
        : CHAR_ENCODING_CODE_UTF_16_WITH_BOM;
    // : CHAR_ENCODING_CODE_UTF_8;

    byte string_1_bytes[] = encodeString(value1, use_iso);
    byte string_2_bytes[] = null;
    if (value2 != null)
      string_2_bytes = encodeString(value2, use_iso);

    // Debug.debug("use_iso", use_iso);
    // Debug.debug("string_1_bytes", string_1_bytes);
    // Debug.debug("s2", s2);

    // int frame_length = kFRAME_HEADER_LENGTH + string_bytes.length + 1;
    int result_length = string_1_bytes.length + 1;
    if (string_2_bytes != null)
      result_length += string_2_bytes.length + 1;
    byte result[] = new byte[result_length];
    int index = 0;

    result[index++] = (byte) char_encoding_code;
    System.arraycopy(string_1_bytes, 0, result, index,
        string_1_bytes.length);
    index += string_1_bytes.length;

    if (string_2_bytes != null)
    {
      result[index++] = (byte) char_encoding_code;
      System.arraycopy(string_2_bytes, 0, result, index,
          string_2_bytes.length);
    }

    return new ID3v2OutputFrame(longFrameID, frameOrder, result);
  }

  private static ID3v2OutputFrame toFrameImage(String longFrameID,
      Number frameOrder, ImageData imageData)
  // byte imageData[], String mimeType, String description,
      // int pictureType)
      throws UnsupportedEncodingException, IOException
  {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    boolean use_iso = canEncodeStringInISO(imageData.description);

    int char_encoding_code = use_iso ? CHAR_ENCODING_CODE_ISO_8859_1
        : CHAR_ENCODING_CODE_UTF_16_WITH_BOM;

    baos.write(char_encoding_code);

    byte mimeTypeBytes[] = encodeString(imageData.mimeType, true);
    // Debug.debug("mimeType", mimeType);
    // Debug.debug("mimeTypeBytes", mimeTypeBytes);
    baos.write(mimeTypeBytes);
    baos.write(0);

    baos.write(0xff & imageData.pictureType);

    byte descriptionBytes[] = encodeString(imageData.description, use_iso);
    baos.write(descriptionBytes);
    // Debug.debug("description", description);
    // Debug.debug("descriptionBytes", descriptionBytes);
    baos.write(0);

    baos.write(imageData.imageData);

    byte frameBytes[] = baos.toByteArray();

    // Debug.debug("frameBytes", frameBytes);

    return new ID3v2OutputFrame(longFrameID, frameOrder, frameBytes);
  }

  private static class FrameFactory implements IFrameFactory
  {
    public ID3v2OutputFrame createUserTextFrame(String value1, String value2)
        throws UnsupportedEncodingException, IOException
    {
      ID3FrameType frameType = ID3FrameType.USERTEXT;
      String longFrameID = frameType.longID;
      Number frameOrder = frameType.getFrameOrder();
      return toFrameText(longFrameID, frameOrder, value1, value2);
    }

    public ID3v2OutputFrame createCommentFrame(String value)
        throws UnsupportedEncodingException, IOException
    {
      ID3FrameType frameType = ID3FrameType.COMMENT;
      String longFrameID = frameType.longID;
      Number frameOrder = frameType.getFrameOrder();
      return toFrameCOMM(longFrameID, frameOrder, value);
    }

    public ID3v2OutputFrame createPictureFrame(ImageData imageData)
        throws UnsupportedEncodingException, IOException
    {
      ID3FrameType frameType = ID3FrameType.PICTURE;
      String longFrameID = frameType.longID;
      Number frameOrder = frameType.getFrameOrder();
      return toFrameImage(longFrameID, frameOrder, imageData);
    }

    public ID3v2OutputFrame createTextFrame(ID3FrameType frameType,
        String value) throws UnsupportedEncodingException, IOException
    {
      return createTextFrame(frameType, value, null);
    }

    public ID3v2OutputFrame createTextFrame(ID3FrameType frameType,
        String value1, String value2)
        throws UnsupportedEncodingException, IOException
    {
      String longFrameID = frameType.longID;
      Number frameOrder = frameType.getFrameOrder();
      return toFrameText(longFrameID, frameOrder, value1, value2);
    }
  }

  // TODO: convert Object params to String params.
  private static ID3v2OutputFrame toFrameCOMM(String longFrameID,
      Number frameOrder, String value)
      throws UnsupportedEncodingException, IOException
  {
    String s;
    if (value instanceof String)
      s = (String) value;
    else
    {
      Debug
          .debug("Bad value ", value + " (" + Debug.getType(value)
              + ")");
      Debug.dumpStack();
      return null;
    }

    boolean use_iso = canEncodeStringInISO(s);

    int char_encoding_code = use_iso ? CHAR_ENCODING_CODE_ISO_8859_1
        : CHAR_ENCODING_CODE_UTF_16_WITH_BOM;
    // : CHAR_ENCODING_CODE_UTF_8;

    byte string_bytes[] = encodeString(s, use_iso);

    // int frame_length = kFRAME_HEADER_LENGTH + string_bytes.length + 1;
    int result_length = string_bytes.length + 1 + 3 + 1;

    byte result[] = new byte[result_length];
    int index = 0;

    result[index++] = (byte) char_encoding_code;
    result[index++] = (byte) 0; // language
    result[index++] = (byte) 0; // language
    result[index++] = (byte) 0; // language

    // summary
    result[index++] = (byte) 0; // divider

    System.arraycopy(string_bytes, 0, result, index, string_bytes.length);

    return new ID3v2OutputFrame(longFrameID, frameOrder, result);
  }

  private List toFrames(MyID3Listener listener, boolean strict,
      IMusicMetadata metadata) throws UnsupportedEncodingException,
      IOException, ID3WriteException
  {
    // make a local copy.
    metadata = new MusicMetadata(metadata);

    IFrameFactory frameFactory = new FrameFactory();
    return ID3v2FrameTranslation.translateMetadataToFrames(listener,
        strict, metadata, frameFactory);
  }

  private static final Comparator FRAME_SORTER = new Comparator() {
    public int compare(Object o1, Object o2)
    {
      ID3v2OutputFrame f1 = (ID3v2OutputFrame) o1;
      ID3v2OutputFrame f2 = (ID3v2OutputFrame) o2;

      int fo1 = f1.frameOrder.intValue();
      int fo2 = f2.frameOrder.intValue();
      if (fo1 != fo2)
        return fo1 - fo2;

      return f1.longFrameID.compareTo(f2.longFrameID);
    }
  };

  public interface Filter
  {
    public boolean filter(String frameid);
  }

  private byte[] writeFrames(Filter filter, List frames)
      throws ID3WriteException, IOException
  {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    Collections.sort(frames, FRAME_SORTER);

    for (int i = 0; i < frames.size(); i++)
    {
      ID3v2OutputFrame frame = (ID3v2OutputFrame) frames.get(i);

      String frame_id = frame.longFrameID;
      if (frame_id.length() != 4)
        throw new ID3WriteException("frame_id has bad length: "
            + frame_id + " (" + frame_id.length() + ")");

      if (filter != null && filter.filter(frame_id))
      {
        continue;
      }

      // baos.write(frame_id );
      baos.write((byte) frame_id.charAt(0));
      baos.write((byte) frame_id.charAt(1));
      baos.write((byte) frame_id.charAt(2));
      baos.write((byte) frame_id.charAt(3));

      int length = frame.bytes.length;

      if (id3v2_version == 4)
      {
        baos.write((byte) (0x7f & (length >> 21)));
        baos.write((byte) (0x7f & (length >> 14)));
        baos.write((byte) (0x7f & (length >> 7)));
        baos.write((byte) (0x7f & (length)));
      } else if (id3v2_version == 3)
      {
        baos.write((byte) (0xff & (length >> 24)));
        baos.write((byte) (0xff & (length >> 16)));
        baos.write((byte) (0xff & (length >> 8)));
        baos.write((byte) (0xff & (length)));
      } else
        throw new ID3WriteException("id3v2_version: " + id3v2_version);

      // int flags = frame.flags;
      int flags = 0;
      if (id3v2_version == 4)
      {
        if (frame.flags.getTagAlterPreservation())
          flags |= FRAME_FLAG_ID3v24_TAG_ALTER_PRESERVATION;
        if (frame.flags.getFileAlterPreservation())
          flags |= FRAME_FLAG_ID3v24_FILE_ALTER_PRESERVATION;
        if (frame.flags.getReadOnly())
          flags |= FRAME_FLAG_ID3v24_READ_ONLY;
        if (frame.flags.getGroupingIdentity())
          flags |= FRAME_FLAG_ID3v24_GROUPING_IDENTITY;
        if (frame.flags.getCompression())
          flags |= FRAME_FLAG_ID3v24_COMPRESSION;
        if (frame.flags.getEncryption())
          flags |= FRAME_FLAG_ID3v24_ENCRYPTION;
        if (frame.flags.getUnsynchronisation())
          flags |= FRAME_FLAG_ID3v24_UNSYNCHRONISATION;
        if (frame.flags.getDataLengthIndicator())
          flags |= FRAME_FLAG_ID3v24_DATA_LENGTH_INDICATOR;
      } else if (id3v2_version == 3)
      {
        if (frame.flags.getTagAlterPreservation())
          flags |= FRAME_FLAG_ID3v23_TAG_ALTER_PRESERVATION;
        if (frame.flags.getFileAlterPreservation())
          flags |= FRAME_FLAG_ID3v23_FILE_ALTER_PRESERVATION;
        if (frame.flags.getReadOnly())
          flags |= FRAME_FLAG_ID3v23_READ_ONLY;
        if (frame.flags.getGroupingIdentity())
          flags |= FRAME_FLAG_ID3v23_GROUPING_IDENTITY;
        if (frame.flags.getCompression())
          flags |= FRAME_FLAG_ID3v23_COMPRESSION;
        if (frame.flags.getEncryption())
          flags |= FRAME_FLAG_ID3v23_ENCRYPTION;
      } else
        throw new ID3WriteException("id3v2_version: " + id3v2_version);

      baos.write((byte) (0xff & (flags >> 8)));
      baos.write((byte) (0xff & (flags)));

      baos.write(frame.bytes);
    }

    return baos.toByteArray();
  }

  private void checkTags(MyID3Listener listener, MusicMetadataSet set,
      List frames, boolean strict) throws UnsupportedEncodingException,
      IOException, ID3WriteException
  {
    if (set == null || set.id3v2Raw == null)
      return;

    Vector old_frames = set.id3v2Raw.frames;
    if (old_frames == null)
      return;

    Vector new_frame_ids = new Vector();
    for (int i = 0; i < frames.size(); i++)
    {
      ID3v2OutputFrame frame = (ID3v2OutputFrame) frames.get(i);

      new_frame_ids.add(frame.longFrameID);
    }

    Vector final_frame_ids = new Vector(new_frame_ids);
    for (int i = 0; i < old_frames.size(); i++)
    {
      MyID3v2Frame oldFrame = (MyID3v2Frame) old_frames.get(i);

      String longFrameID;
      Number frameOrder;
      {
        ID3FrameType frame_type = ID3FrameType.get(oldFrame.frameID);
        if (frame_type != null)
        {
          longFrameID = frame_type.longID;
          frameOrder = frame_type.getFrameOrder();
        } else if (oldFrame.frameID.length() == 4)
        {
          longFrameID = oldFrame.frameID;
          frameOrder = ID3FrameType.DEFAULT_FRAME_ORDER;
        } else
        {
          if (strict)
            throw new ID3WriteException("unknown frame type: "
                + oldFrame.frameID);
          else if (null != listener)
          {
            listener.log("unknown frame type", oldFrame.frameID);
            listener.log("unknown old_tag", oldFrame);
            listener.log(Debug.getStackTrace());
          }

          continue;
        }
      }

      if (new_frame_ids.contains(longFrameID))
        continue;

      if (null != listener)
        listener.log("adding missing frame", longFrameID);

      if (oldFrame instanceof MyID3v2FrameText)
      {
        MyID3v2FrameText text_frame = (MyID3v2FrameText) oldFrame;

        // ID3FrameType frame_type =
        // ID3FrameType.get(old_frame.frame_id);
        if (null != listener)
        {
          listener.log("text_frame", text_frame);
          listener.log("frame_type", longFrameID);
        }

        ID3v2OutputFrame frame = toFrame(longFrameID, frameOrder,
            text_frame.value, text_frame.value2);

        if (frame != null)
        {
          frames.add(frame);
          final_frame_ids.add(frame.longFrameID);
        } else
        {
          if (strict)
            throw new ID3WriteException("Couldn't write frame: "
                + longFrameID);
          else if (null != listener)
          {
            listener.log("Couldn't write frame", longFrameID);
            listener.log(Debug.getStackTrace());
          }
        }
      } else if (oldFrame instanceof MyID3v2FrameImage)
      {
        MyID3v2FrameImage imageFrame = (MyID3v2FrameImage) oldFrame;

        if (null != listener)
        {
          listener.log("imageFrame", imageFrame);
          listener.log("frame_type", longFrameID);
        }

        ID3v2OutputFrame frame = toFrameImage(longFrameID, frameOrder,
            imageFrame.getImageData());

        frames.add(frame);
        final_frame_ids.add(frame.longFrameID);
      } else
      {
        MyID3v2FrameData data = (MyID3v2FrameData) oldFrame;
        if (data.flags.getTagAlterPreservation())
          continue;
        // if(data.flags.getTagAlterPreservation())
        // continue;
        if (data.frameID.length() == 4)
        {
          // if(data.flags.getCompression() ||
          // data.flags.getUnsynchronisation() || )
          // int flags = data.flags.flags;
          ID3v2OutputFrame frame = new ID3v2OutputFrame(data.frameID,
              data.dataBytes, data.flags);
          frames.add(frame);
          final_frame_ids.add(frame.longFrameID);
          continue;
        }

        if (strict)
          throw new ID3WriteException(
              "Couldn't preserve data frame: " + data.frameID);
        else if (null != listener)
        {
          listener.log("Couldn't preserve data frame", data.frameID);
          listener.log(Debug.getStackTrace());
        }
      }
      // if()
    }
    // Debug.debug("final_frame_ids", final_frame_ids);
  }

  public byte[] toTag(MyID3Listener listener, MusicMetadataSet set,
      IMusicMetadata values, boolean strict) throws Exception
  {
    return toTag(listener, null, set, values, strict);
  }

  public byte[] toTag(MyID3Listener listener, Filter filter,
      MusicMetadataSet set, IMusicMetadata values, boolean strict)
      throws UnsupportedEncodingException, IOException, ID3WriteException
  {
    // Debug.debug("raw values ", values);

    List frames = toFrames(listener, strict, values);

    // Debug.debug("raw frames", frames);

    checkTags(listener, set, frames, strict);

    if (null != listener)
    {
      for (int i = 0; i < frames.size(); i++)
      {
        ID3v2OutputFrame frame = (ID3v2OutputFrame) frames.get(i);
        listener.log("frame", frame.longFrameID);
      }
    }
    // Debug.debug("checked frames", frames);

    byte frame_bytes[] = writeFrames(filter, frames);

    // Debug.debug("frame_bytes", frame_bytes);

    byte extended_header[] = {};
    byte padding[] = {};

    int body_length = extended_header.length + frame_bytes.length
        + padding.length;

    byte header[] = getHeaderFooter(body_length, false);

    // Debug.debug("body_length", body_length);
    // Debug.debug("header", header);
    byte footer[];
    if (id3v2_version == 4)
      footer = getHeaderFooter(body_length, true);
    else if (id3v2_version == 3)
      footer = null;
    else
      throw new ID3WriteException("id3v2_version: " + id3v2_version);

    // Debug.debug("footer", footer);

    int resultLength = header.length + extended_header.length
        + frame_bytes.length + padding.length;
    if (footer != null)
      resultLength += footer.length;
    byte result[] = new byte[resultLength];

    int index = 0;
    System.arraycopy(header, 0, result, index, header.length);
    index += header.length;
    System.arraycopy(frame_bytes, 0, result, index, frame_bytes.length);
    if (footer != null)
    {
      index += frame_bytes.length;
      System.arraycopy(footer, 0, result, index, footer.length);
    }
    // index += footer.length;

    return result;
  }
}
TOP

Related Classes of org.cmc.music.myid3.id3v2.MyID3v2Write$Filter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.