forked from ShadelessFox/decima
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Audio Player: Add playback support for HZD
MusicResource
- Loading branch information
1 parent
67efb4e
commit 9cabf8d
Showing
24 changed files
with
421 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
...les/decima-ui/src/main/java/com/shade/decima/ui/data/viewer/audio/data/echo/EchoBank.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package com.shade.decima.ui.data.viewer.audio.data.echo; | ||
|
||
import com.shade.platform.model.util.BufferUtils; | ||
import com.shade.util.NotNull; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.util.*; | ||
|
||
public record EchoBank(@NotNull Map<Chunk.Type<?>, Chunk> chunks) { | ||
private static final int ECHO_MAGIC = 'E' | 'C' << 8 | 'H' << 16 | 'O' << 24; | ||
private static final int MEDA_MAGIC = 'M' | 'E' << 8 | 'D' << 16 | 'A' << 24; | ||
private static final int STRL_MAGIC = 'S' | 'T' << 8 | 'R' << 16 | 'L' << 24; | ||
private static final int PICD_MAGIC = 'P' | 'I' << 8 | 'C' << 16 | 'D' << 24; | ||
|
||
public static EchoBank read(@NotNull ByteBuffer buffer) { | ||
if (buffer.getInt() != ECHO_MAGIC || buffer.getInt() != -1) { | ||
throw new IllegalArgumentException("Invalid bank format"); | ||
} | ||
|
||
int size = buffer.getInt(); | ||
Map<Chunk.Type<?>, Chunk> chunks = new HashMap<>(); | ||
|
||
for (int i = 0; i < size / 12; i++) { | ||
int magic = buffer.getInt(); | ||
int offset = buffer.getInt(); | ||
int length = buffer.getInt(); | ||
|
||
if (offset > buffer.limit()) { | ||
throw new IllegalArgumentException("Invalid bank offset"); | ||
} | ||
|
||
ByteBuffer data = buffer.slice(offset, length).order(buffer.order()); | ||
|
||
switch (magic) { | ||
case MEDA_MAGIC -> chunks.put(Chunk.Type.MEDA, Chunk.Media.read(data)); | ||
case STRL_MAGIC -> chunks.put(Chunk.Type.STRL, Chunk.Names.read(data)); | ||
} | ||
} | ||
|
||
return new EchoBank(Collections.unmodifiableMap(chunks)); | ||
} | ||
|
||
@NotNull | ||
public <T extends Chunk> T get(@NotNull Chunk.Type<T> type) { | ||
Chunk chunk = chunks.get(type); | ||
|
||
if (chunk != null) { | ||
return type.type().cast(chunk); | ||
} | ||
|
||
throw new NoSuchElementException("No such chunk: " + type.id()); | ||
} | ||
|
||
public sealed interface Chunk { | ||
record Type<T extends Chunk>(@NotNull String id, @NotNull Class<T> type) { | ||
public static final Type<Media> MEDA = new Type<>("MEDA", Media.class); | ||
public static final Type<Names> STRL = new Type<>("STRL", Names.class); | ||
} | ||
|
||
record Media(@NotNull Entry[] entries) implements Chunk { | ||
public record Entry(int offset, int size) { | ||
public static Entry read(@NotNull ByteBuffer buffer) { | ||
int offset = Math.toIntExact(buffer.getLong()); | ||
int size = Math.toIntExact(buffer.getLong()); | ||
buffer.position(buffer.position() + 32); | ||
return new Entry(offset, size); | ||
} | ||
} | ||
|
||
public static Media read(@NotNull ByteBuffer buffer) { | ||
if (buffer.getInt() != PICD_MAGIC) { | ||
throw new IllegalArgumentException("Invalid media format"); | ||
} | ||
int count = buffer.getInt(); | ||
if (buffer.getLong() != 0) { | ||
throw new IllegalArgumentException("Expected padding"); | ||
} | ||
return new Media(BufferUtils.getObjects(buffer, count, Entry[]::new, Entry::read)); | ||
} | ||
} | ||
|
||
record Names(@NotNull String[] names) implements Chunk { | ||
@NotNull | ||
public static Names read(@NotNull ByteBuffer buffer) { | ||
List<String> names = new ArrayList<>(); | ||
while (buffer.hasRemaining()) { | ||
names.add(BufferUtils.getString(buffer)); | ||
} | ||
return new Names(names.toArray(String[]::new)); | ||
} | ||
} | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
...ima-ui/src/main/java/com/shade/decima/ui/data/viewer/audio/data/mpeg/MpegFrameHeader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package com.shade.decima.ui.data.viewer.audio.data.mpeg; | ||
|
||
/** | ||
* Represents an MPEG audio frame header. | ||
* | ||
* @see <a href="https://www.datavoyage.com/mpgscript/mpeghdr.htm">MPEG Audio Layer I/II/III frame header</a> | ||
*/ | ||
public record MpegFrameHeader(int raw) { | ||
public static final int MPEG_1 = 0b1; | ||
public static final int MPEG_2 = 0b0; | ||
|
||
public static final int LAYER_1 = 0b11; | ||
public static final int LAYER_2 = 0b10; | ||
public static final int LAYER_3 = 0b01; | ||
|
||
private static final int[] BITRATE_V1_L1 = {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}; | ||
private static final int[] BITRATE_V1_L2 = {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}; | ||
private static final int[] BITRATE_V1_L3 = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}; | ||
private static final int[] BITRATE_V2_L1 = {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}; | ||
private static final int[] BITRATE_V2_L2 = {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}; | ||
|
||
public MpegFrameHeader { | ||
if (raw >>> 20 != 0xfff) { | ||
throw new IllegalArgumentException("Invalid frame sync"); | ||
} | ||
} | ||
|
||
public int samplesPerFrame() { | ||
return switch (layerId()) { | ||
case LAYER_1 -> 384; | ||
case LAYER_2 -> 1152; | ||
case LAYER_3 -> mpegId() == MPEG_1 ? 1152 : 576; | ||
default -> throw new IllegalStateException(); | ||
}; | ||
} | ||
|
||
public int samplingRate() { | ||
int sampleRate = switch (frequencyIndex()) { | ||
case 0 -> 44100; | ||
case 1 -> 48000; | ||
case 2 -> 32000; | ||
default -> throw new IllegalStateException(); | ||
}; | ||
return mpegId() == MPEG_1 ? sampleRate : sampleRate >> 1; | ||
} | ||
|
||
public int bitRate() { | ||
if (mpegId() == MPEG_1) { | ||
if (layerId() == LAYER_1) { | ||
return BITRATE_V1_L1[bitRateIndex()]; | ||
} else if (layerId() == LAYER_2) { | ||
return BITRATE_V1_L2[bitRateIndex()]; | ||
} else { | ||
return BITRATE_V1_L3[bitRateIndex()]; | ||
} | ||
} else { | ||
if (layerId() == LAYER_1) { | ||
return BITRATE_V2_L1[bitRateIndex()]; | ||
} else { | ||
return BITRATE_V2_L2[bitRateIndex()]; | ||
} | ||
} | ||
} | ||
|
||
public int paddingSize() { | ||
if (layerId() == LAYER_1) { | ||
return 4; | ||
} else { | ||
return 1; | ||
} | ||
} | ||
|
||
public int frameSize() { | ||
int frameSize = bitRate() * 144000 / samplingRate(); | ||
if (channelMode() == 3) { | ||
frameSize >>= 1; | ||
} | ||
if (protectionBit() == 0) { | ||
frameSize -= 2; | ||
} | ||
if (paddingBit() == 1) { | ||
frameSize += paddingSize(); | ||
} | ||
return frameSize; | ||
} | ||
|
||
public int frameSync() { | ||
return raw >>> 20; | ||
} | ||
|
||
public int mpegId() { | ||
return raw >>> 19 & 1; | ||
} | ||
|
||
public int layerId() { | ||
return raw >>> 17 & 3; | ||
} | ||
|
||
public int protectionBit() { | ||
return raw >>> 16 & 1; | ||
} | ||
|
||
public int bitRateIndex() { | ||
return raw >>> 12 & 15; | ||
} | ||
|
||
public int frequencyIndex() { | ||
return raw >>> 10 & 3; | ||
} | ||
|
||
public int paddingBit() { | ||
return raw >>> 9 & 1; | ||
} | ||
|
||
public int privateBit() { | ||
return raw >>> 8 & 1; | ||
} | ||
|
||
public int channelMode() { | ||
return raw >>> 6 & 3; | ||
} | ||
|
||
public int modeExtension() { | ||
return raw >>> 4 & 3; | ||
} | ||
|
||
public int copyright() { | ||
return raw >>> 3 & 1; | ||
} | ||
|
||
public int original() { | ||
return raw >>> 2 & 1; | ||
} | ||
|
||
public int emphasis() { | ||
return raw & 3; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format( | ||
"MpegFrameHeader[frameSync=%x, mpegId=%d, layerId=%d, protectionBit=%d, bitrateIndex=%d, frequencyIndex=%d, paddingBit=%d, privateBit=%d, channelMode=%d, modeExtension=%d, copyright=%d, original=%d, emphasis=%d]", | ||
frameSync(), mpegId(), layerId(), protectionBit(), bitRateIndex(), frequencyIndex(), paddingBit(), privateBit(), channelMode(), modeExtension(), copyright(), original(), emphasis() | ||
); | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
.../viewer/audio/wwise/AkBankSourceData.java → ...er/audio/data/wwise/AkBankSourceData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
.../viewer/audio/wwise/AkBankSourceType.java → ...er/audio/data/wwise/AkBankSourceType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
.../decima-ui/src/main/java/com/shade/decima/ui/data/viewer/audio/data/wwise/AkHircNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.shade.decima.ui.data.viewer.audio.data.wwise; | ||
|
||
public interface AkHircNode { | ||
int id(); | ||
} |
2 changes: 1 addition & 1 deletion
2
...iewer/audio/wwise/AkMediaInformation.java → .../audio/data/wwise/AkMediaInformation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...data/viewer/audio/wwise/AkMusicTrack.java → ...viewer/audio/data/wwise/AkMusicTrack.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...a/ui/data/viewer/audio/wwise/AkSound.java → ...data/viewer/audio/data/wwise/AkSound.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ui/data/viewer/audio/wwise/WwiseBank.java → ...ta/viewer/audio/data/wwise/WwiseBank.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...i/data/viewer/audio/wwise/WwiseMedia.java → ...a/viewer/audio/data/wwise/WwiseMedia.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.