Skip to content

Commit

Permalink
scd parsing overhaul (TODO: mutli-channel stuff)
Browse files Browse the repository at this point in the history
  • Loading branch information
0ceal0t committed Mar 4, 2024
1 parent ba8b2b2 commit 5f24c8b
Show file tree
Hide file tree
Showing 7 changed files with 325 additions and 313 deletions.
70 changes: 20 additions & 50 deletions VFXEditor/Formats/ScdFormat/Music/AudioPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ public class AudioPlayer {

private bool IsVorbis => Entry.Format == SscfWaveFormat.Vorbis;

private int ConverterSamplesOut = 0;
private int ConverterSecondsOut = 0;
private int ConverterSamples = 0;
private float ConverterSeconds = 0f;

private bool LoopTimeInitialized = false;
private bool LoopTimeRefreshing = false;
private double LoopStartTime = 0;
Expand All @@ -50,11 +45,10 @@ public AudioPlayer( ScdAudioEntry entry ) {
public void Draw() {
using var tabBar = ImRaii.TabBar( "Tabs" );
if( !tabBar ) return;

DrawPlayer();
DrawChannels();
DrawConverter();
ProcessQueue(); // Loop, etc.
Entry.DrawTabs();
}

public void DrawMiniPlayer() {
Expand All @@ -64,6 +58,7 @@ public void DrawMiniPlayer() {
}

private void DrawControls() {
using( var style = ImRaii.PushStyle( ImGuiStyleVar.FramePadding, ImGui.GetStyle().FramePadding with { X = 4 } ) )
using( var font = ImRaii.PushFont( UiBuilder.IconFont ) ) {
if( State == PlaybackState.Stopped ) {
if( ImGui.Button( FontAwesomeIcon.Play.ToIconString() ) ) Play();
Expand Down Expand Up @@ -92,8 +87,9 @@ private void DrawControls() {
}

if( State != PlaybackState.Stopped && !Entry.NoLoop && LoopTimeInitialized && Plugin.Configuration.SimulateScdLoop ) {
var startX = 221f * ( LoopStartTime / TotalTime );
var endX = 221f * ( LoopEndTime / TotalTime );
var dragWidth = ImGui.GetStyle().GrabMinSize + 4f;
var startX = ( 221f - dragWidth ) * ( LoopStartTime / TotalTime ) + ( dragWidth / 2f );
var endX = ( 221f - dragWidth ) * ( LoopEndTime / TotalTime ) + ( dragWidth / 2f );

var startPos = drawPos + new Vector2( ( float )startX - 2, 0 );
var endPos = drawPos + new Vector2( ( float )endX - 2, 0 );
Expand All @@ -109,7 +105,6 @@ private void DrawControls() {
private void DrawPlayer() {
using var tabItem = ImRaii.TabItem( "Music" );
if( !tabItem ) return;

using var _ = ImRaii.PushId( "Music" );

DrawControls();
Expand Down Expand Up @@ -139,17 +134,17 @@ private void DrawPlayer() {
}
UiUtils.Tooltip( "Replace sound file" );

var loopStartEnd = new int[2] { Entry.LoopStart, Entry.LoopEnd };
var loop = Entry.Data.GetLoopTime();
ImGui.SetNextItemWidth( 246f );
if( ImGui.InputInt2( "##LoopStartEnd", ref loopStartEnd[0] ) ) {
Entry.LoopStart = loopStartEnd[0];
Entry.LoopEnd = loopStartEnd[1];
if( ImGui.InputFloat2( "##LoopStartEnd", ref loop ) ) {
Entry.LoopStart = Entry.Data.TimeToBytes( loop.X );
Entry.LoopEnd = Entry.Data.TimeToBytes( loop.Y );
}

ImGui.SameLine();
if( UiUtils.DisabledButton( "Update", Plugin.Configuration.SimulateScdLoop ) ) RefreshLoopStartEndTime();
ImGui.SameLine();
ImGui.Text( "Loop Start/End (Bytes)" );
ImGui.Text( "Loop Time" );

ImGui.TextDisabled( $"{Entry.Format} / {Entry.NumChannels} Ch / {Entry.SampleRate}Hz / 0x{Entry.DataLength:X8} bytes" );
}
Expand Down Expand Up @@ -181,37 +176,6 @@ private void DrawChannels() {
if( ImGui.Button( "Update" ) ) Reset();
}

private void DrawConverter() {
using var tabItem = ImRaii.TabItem( "Convertor" );
if( !tabItem ) return;

using var _ = ImRaii.PushId( "Convertor" );

ImGui.TextDisabled( "Utilities to generate byte values which can be used for loop start/end" );

// Bytes
ImGui.SetNextItemWidth( 100 ); ImGui.InputInt( "##SamplesIn", ref ConverterSamples, 0, 0 );
ImGui.SameLine();
ImGui.PushFont( UiBuilder.IconFont ); ImGui.Text( FontAwesomeIcon.ArrowRight.ToIconString() ); ImGui.PopFont();
ImGui.SameLine();
ImGui.SetNextItemWidth( 100 ); ImGui.InputInt( "##SamplesOut", ref ConverterSamplesOut, 0, 0, ImGuiInputTextFlags.ReadOnly );
ImGui.SameLine();
if( ImGui.Button( "Samples to Bytes" ) ) {
ConverterSamplesOut = Entry.Data.SamplesToBytes( ConverterSamples );
}

// Time
ImGui.SetNextItemWidth( 100 ); ImGui.InputFloat( "##SecondsIn", ref ConverterSeconds, 0, 0 );
ImGui.SameLine();
ImGui.PushFont( UiBuilder.IconFont ); ImGui.Text( FontAwesomeIcon.ArrowRight.ToIconString() ); ImGui.PopFont();
ImGui.SameLine();
ImGui.SetNextItemWidth( 100 ); ImGui.InputInt( $"##SecondsOut", ref ConverterSecondsOut, 0, 0, ImGuiInputTextFlags.ReadOnly );
ImGui.SameLine();
if( ImGui.Button( "Seconds to Bytes" ) ) {
ConverterSecondsOut = Entry.Data.TimeToBytes( ConverterSeconds );
}
}

private void ProcessQueue() {
var currentState = State;
var justQueued = false;
Expand All @@ -226,7 +190,7 @@ private void ProcessQueue() {
}
}
}
else if( currentState == PlaybackState.Playing && !Entry.NoLoop && Plugin.Configuration.SimulateScdLoop && LoopTimeInitialized && Math.Abs( LoopEndTime - CurrentTime ) < 0.03f ) {
else if( currentState == PlaybackState.Playing && !Entry.NoLoop && Plugin.Configuration.SimulateScdLoop && LoopTimeInitialized && Math.Abs( LoopEndTime - CurrentTime ) < 0.1f ) {
if( QueueSeek == -1 ) {
QueueSeek = LoopStartTime;
justQueued = true;
Expand Down Expand Up @@ -299,7 +263,10 @@ public void UpdateVolume() {

private void ImportDialog() {
FileBrowserManager.OpenFileDialog( "Import File", "Audio files{.ogg,.wav},.*", ( bool ok, string res ) => {
if( ok ) ScdFile.Import( res, Entry );
if( ok ) {
Reset();
Entry.File.Import( res, Entry );
}
} );
}

Expand All @@ -316,7 +283,7 @@ private void SaveOggDialog() {
FileBrowserManager.SaveFileDialog( "Select a Save Location", ".ogg", "ExportedSound", "ogg", ( bool ok, string res ) => {
if( ok ) {
var data = ( ScdVorbis )Entry.Data;
File.WriteAllBytes( res, data.DecodedData );
File.WriteAllBytes( res, data.Data );
}
} );
}
Expand All @@ -330,8 +297,11 @@ private void SaveOggDialog() {
private async void RefreshLoopStartEndTime() {
if( LoopTimeRefreshing ) return;
LoopTimeRefreshing = true;

await Task.Run( () => {
Entry.Data.BytesToLoopStartEnd( Entry.LoopStart, Entry.LoopEnd, out LoopStartTime, out LoopEndTime );
var loop = Entry.Data.GetLoopTime();
LoopStartTime = loop.X;
LoopEndTime = loop.Y;
LoopTimeInitialized = true;
LoopTimeRefreshing = false;
} );
Expand Down
65 changes: 39 additions & 26 deletions VFXEditor/Formats/ScdFormat/Music/Data/ScdAdpcm.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
using NAudio.Wave;
using System.IO;
using System.Numerics;
using VfxEditor.Formats.ScdFormat.Utils;

namespace VfxEditor.ScdFormat.Music.Data {
public class ScdAdpcm : ScdAudioData {
public byte[] WaveHeader;
public byte[] Data;
public WaveFormat Format;
public readonly WaveFormat Format;
public readonly byte[] WaveHeader;
public readonly byte[] Data;

public ScdAdpcm( BinaryReader reader, ScdAudioEntry entry ) {
WaveHeader = reader.ReadBytes( entry.FirstFrame - entry.AuxChunkData.Length );
public ScdAdpcm( WaveFormat format, byte[] waveHeader, byte[] data, ScdAudioEntry entry ) : base( entry ) {
Format = format;
WaveHeader = waveHeader;
Data = data;
}

public ScdAdpcm( BinaryReader reader, int headerSize, ScdAudioEntry entry ) : base( entry ) {
WaveHeader = reader.ReadBytes( headerSize );
Data = reader.ReadBytes( entry.DataLength );

using var ms = new MemoryStream( WaveHeader );
Expand All @@ -27,7 +34,17 @@ public override void Write( BinaryWriter writer ) {
writer.Write( Data );
}

public static void ImportWav( string path, ScdAudioEntry entry ) {
public override int SamplesToBytes( int samples ) => Format.BitsPerSample * samples / 8;

public override int TimeToBytes( float time ) => ( int )( Format.AverageBytesPerSecond * time );

public override Vector2 GetLoopTime() => new( Entry.LoopStart / Format.AverageBytesPerSecond, Entry.LoopEnd / Format.AverageBytesPerSecond );

public override int GetSubInfoSize() => WaveHeader.Length;

// ===================

public static ScdAudioEntry ImportWav( string path, ScdAudioEntry oldEntry ) {
var waveFileCheck = new WaveFileReader( path );
if( waveFileCheck.WaveFormat.Encoding == WaveFormatEncoding.Adpcm ) {
Dalamud.Log( "Already Adpcm, skipping conversion" );
Expand All @@ -40,14 +57,12 @@ public static void ImportWav( string path, ScdAudioEntry entry ) {

if( !File.Exists( ScdManager.ConvertWav ) ) {
Dalamud.Error( "Could not conver to ADPCM" );
return;
return null;
}

var data = ( ScdAdpcm )entry.Data;
using var waveFile = new WaveFileReader( ScdManager.ConvertWav );

var rawData = File.ReadAllBytes( ScdManager.ConvertWav );
var waveFormat = waveFile.WaveFormat;
var format = waveFile.WaveFormat;

using var ms = new MemoryStream( rawData );
using var br = new BinaryReader( ms );
Expand All @@ -56,32 +71,30 @@ public static void ImportWav( string path, ScdAudioEntry entry ) {
br.ReadInt32(); // WAVE
br.ReadInt32(); // fmt
var headerLength = br.ReadInt32();
data.WaveHeader = br.ReadBytes( headerLength );

var waveHeader = br.ReadBytes( headerLength );
var magic = br.ReadInt32();
while( magic != 0x61746164 ) { // data
var size = br.ReadInt32();
br.ReadBytes( size );
magic = br.ReadInt32();
}
var dataLength = br.ReadInt32();
data.Data = br.ReadBytes( dataLength );

data.Format = waveFormat;
entry.DataLength = dataLength;
entry.FirstFrame = headerLength + entry.AuxChunkData.Length;
entry.SampleRate = waveFormat.SampleRate;
entry.NumChannels = waveFormat.Channels;
entry.BitsPerSample = ( short )waveFormat.BitsPerSample;
}
var data = br.ReadBytes( dataLength );

public override int SamplesToBytes( int samples ) => Format.BitsPerSample * samples / 8;
// Create new entry
var entry = new ScdAudioEntry(
oldEntry,
dataLength,
format.Channels,
format.SampleRate,
SscfWaveFormat.MsAdPcm
);

public override int TimeToBytes( float time ) => ( int )( Format.AverageBytesPerSecond * time );
// Create new data
var adpcm = new ScdAdpcm( format, waveHeader, data, entry );

public override void BytesToLoopStartEnd( int loopStart, int loopEnd, out double startTime, out double endTime ) {
startTime = ( double )loopStart / Format.AverageBytesPerSecond;
endTime = ( double )loopEnd / Format.AverageBytesPerSecond;
entry.Data = adpcm;
return entry;
}
}
}
11 changes: 10 additions & 1 deletion VFXEditor/Formats/ScdFormat/Music/Data/ScdAudioData.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
using NAudio.Wave;
using System.IO;
using System.Numerics;

namespace VfxEditor.ScdFormat.Music.Data {
public abstract class ScdAudioData {
public readonly ScdAudioEntry Entry;

public ScdAudioData( ScdAudioEntry entry ) {
Entry = entry;
}

public abstract WaveStream GetStream();

public abstract int SamplesToBytes( int samples );

public abstract int TimeToBytes( float time );

public abstract void BytesToLoopStartEnd( int loopStart, int loopEnd, out double startTime, out double endTime );
public abstract Vector2 GetLoopTime();

public abstract void Write( BinaryWriter writer );

public abstract int GetSubInfoSize();
}
}
Loading

0 comments on commit 5f24c8b

Please sign in to comment.