Added #2132 Show and export streamed sound (SoundStreamHead/SoundStreamBlock) in frame ranges

Fixed #1194 FLA Export - stream sound export
This commit is contained in:
Jindra Petřík
2023-12-30 18:06:08 +01:00
parent 35dda78ffe
commit bd82522f16
20 changed files with 652 additions and 307 deletions
@@ -0,0 +1,167 @@
/*
* Copyright (C) 2010-2023 JPEXS, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.jpexs.decompiler.flash.timeline;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.base.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.base.SoundTag;
import com.jpexs.decompiler.flash.treeitems.Openable;
import com.jpexs.decompiler.flash.treeitems.TreeItem;
import com.jpexs.decompiler.flash.types.sound.SoundExportFormat;
import com.jpexs.decompiler.flash.types.sound.SoundFormat;
import com.jpexs.helpers.ByteArrayRange;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author JPEXS
*/
public class SoundStreamFrameRange implements TreeItem, SoundTag {
public int startFrame;
public int endFrame;
public List<SoundStreamBlockTag> blocks = new ArrayList<>();
private final SoundStreamHeadTypeTag head;
public SoundStreamFrameRange(SoundStreamHeadTypeTag head) {
this.head = head;
}
@Override
public Openable getOpenable() {
return head.getOpenable();
}
@Override
public boolean isModified() {
return false;
}
@Override
public SoundExportFormat getExportFormat() {
return head.getExportFormat();
}
@Override
public boolean importSupported() {
return false; //??
}
@Override
public int getSoundRate() {
return head.getSoundRate();
}
@Override
public boolean getSoundType() {
return head.getSoundType();
}
@Override
public List<ByteArrayRange> getRawSoundData() {
List<ByteArrayRange> ret = new ArrayList<>();
for (SoundStreamBlockTag block : blocks) {
ByteArrayRange data = block.streamSoundData;
if (getSoundFormatId() == SoundFormat.FORMAT_MP3) {
ret.add(data.getSubRange(4, data.getLength() - 4));
} else {
ret.add(data);
}
}
return ret;
}
@Override
public int getSoundFormatId() {
return head.getSoundFormatId();
}
@Override
public long getTotalSoundSampleCount() {
return blocks.size() * head.getSoundSampleCount();
}
@Override
public boolean getSoundSize() {
return head.getSoundSize();
}
@Override
public String getCharacterExportFileName() {
return head.getCharacterExportFileName() + "_" + (startFrame + 1) + "-" + (endFrame + 1);
}
@Override
public String getName() {
return "SoundStreamBlocks";
}
@Override
public SoundFormat getSoundFormat() {
return head.getSoundFormat();
}
@Override
public void setSoundSize(boolean soundSize) {
//?
}
@Override
public void setSoundType(boolean soundType) {
//?
}
@Override
public void setSoundSampleCount(long soundSampleCount) {
//?
}
@Override
public void setSoundCompression(int soundCompression) {
//?
}
@Override
public void setSoundRate(int soundRate) {
//?
}
@Override
public int getCharacterId() {
return head.getCharacterId();
}
public SoundStreamHeadTypeTag getHead() {
return head;
}
@Override
public String toString() {
return "SoundStreamBlocks (frame " + (startFrame + 1) + " - " + (endFrame + 1) + ")";
}
@Override
public boolean isReadOnly() {
return head.isReadOnly();
}
@Override
public String getFlaExportName() {
return head.getFlaExportName() + "_" + (startFrame + 1) + "-" + (endFrame + 1);
}
}
@@ -118,7 +118,7 @@ public class Timeline {
private final Map<ASMSource, Integer> actionFrames = new HashMap<>();
private final Map<Integer, List<SoundStreamBlockTag>> soundStramBlocks = new LinkedHashMap<>();
private final Map<Integer, List<SoundStreamFrameRange>> soundStreamRanges = new LinkedHashMap<>();
private AS2Package as2RootPackage;
@@ -174,9 +174,9 @@ public class Timeline {
return depthMaxFrame;
}
public List<SoundStreamBlockTag> getSoundStreamBlocks(SoundStreamHeadTypeTag head) {
public List<SoundStreamFrameRange> getSoundStreamBlocks(SoundStreamHeadTypeTag head) {
ensureInitialized();
return soundStramBlocks.get(head.getCharacterId());
return soundStreamRanges.get(head.getCharacterId());
}
public Tag getParentTag() {
@@ -194,7 +194,7 @@ public class Timeline {
asmSources.clear();
asmSourceContainers.clear();
actionFrames.clear();
soundStramBlocks.clear();
soundStreamRanges.clear();
otherTags.clear();
this.id = id;
this.swf = swf;
@@ -576,13 +576,23 @@ public class Timeline {
}
private void populateSoundStreamBlocks(int containerId, Iterable<Tag> tags) {
List<SoundStreamBlockTag> blocks = null;
List<SoundStreamFrameRange> ranges = null;
SoundStreamFrameRange range = null;
final int MIN_NUM_FRAMES_NO_SOUND = 2;
int numFramesNoSound = MIN_NUM_FRAMES_NO_SOUND;
boolean frameHasSound = false;
int frame = 0;
SoundStreamHeadTypeTag head = null;
for (Tag t : tags) {
if (t instanceof SoundStreamHeadTypeTag) {
SoundStreamHeadTypeTag head = (SoundStreamHeadTypeTag) t;
head = (SoundStreamHeadTypeTag) t;
head.setCharacterId(containerId);
blocks = new ArrayList<>();
soundStramBlocks.put(containerId, blocks);
ranges = new ArrayList<>();
range = new SoundStreamFrameRange(head);
range.startFrame = -1;
range.endFrame = -1;
numFramesNoSound = MIN_NUM_FRAMES_NO_SOUND;
soundStreamRanges.put(containerId, ranges);
continue;
}
if (t instanceof DefineExternalStreamSound) {
@@ -595,14 +605,41 @@ public class Timeline {
DefineSpriteTag sprite = (DefineSpriteTag) t;
populateSoundStreamBlocks(sprite.getCharacterId(), sprite.getTags());
}
if (t instanceof ShowFrameTag) {
frame++;
if (frameHasSound) {
numFramesNoSound = 0;
} else {
numFramesNoSound++;
}
frameHasSound = false;
}
if (blocks == null) {
if (ranges == null) {
continue;
}
if (range == null) {
continue;
}
if (t instanceof SoundStreamBlockTag) {
blocks.add((SoundStreamBlockTag) t);
}
if (t instanceof SoundStreamBlockTag) {
if (numFramesNoSound >= MIN_NUM_FRAMES_NO_SOUND && range.endFrame > -1) {
ranges.add(range);
range = new SoundStreamFrameRange(head);
range.startFrame = -1;
range.endFrame = -1;
}
range.blocks.add((SoundStreamBlockTag) t);
if (range.startFrame == -1) {
range.startFrame = frame;
}
range.endFrame = frame;
frameHasSound = true;
}
}
if (range != null && ranges != null && range.endFrame > -1) {
ranges.add(range);
}
}