<Compile Include="メディア\サウンド\WASAPI\Sound.cs" />
<Compile Include="メディア\サウンド\WASAPI\SoundDevice.cs" />
<Compile Include="メディア\サウンド\WASAPI\SoundTimer.cs" />
+ <Compile Include="メディア\サウンド\WASAPI\XaResampledWaveSource.cs" />
+ <Compile Include="メディア\サウンド\WASAPI\XAWaveSource.cs" />
<Compile Include="メディア\テクスチャ.cs" />
<Compile Include="メディア\テクスチャフォント.cs" />
<Compile Include="メディア\ビットマップ付きテクスチャ.cs" />
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
+ <ItemGroup>
+ <None Include="xadec.dll">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file
/// デコード後のオーディオデータのすべての長さ[byte]。
/// </summary>
public long Length
- => this._EncodedWaveData.Length;
+ => this._DecodedWaveData.Length;
/// <summary>
/// 現在の再生位置[byte]。
/// コンストラクタ。
/// 指定されたファイルを指定されたフォーマットでデコードし、内部にオンメモリで保管する。
/// </summary>
- public MediaFoundationWaveSource( string path, WaveFormat deviceFormat )
+ public MediaFoundationWaveSource( string ファイルパス, WaveFormat deviceFormat )
{
+ var path = Folder.絶対パスに含まれるフォルダ変数を展開して返す( ファイルパス );
+
this.WaveFormat = new WaveFormat(
deviceFormat.SampleRate, // 指定されたレート
32, // 32bit 固定
}
// (4) ストリームの内容を byte 配列に出力する。
- this._EncodedWaveData = waveStream.ToArray();
+ this._DecodedWaveData = waveStream.ToArray();
}
}
/// </summary>
public void Dispose()
{
- this._EncodedWaveData = null;
+ this._DecodedWaveData = null;
FDKUtilities.解放する( ref this._MediaType );
}
public int Read( byte[] buffer, int offset, int count )
{
// ※ 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
- if( ( null == this._EncodedWaveData ) || ( null == buffer ) )
+ if( ( null == this._DecodedWaveData ) || ( null == buffer ) )
return 0;
long 読み込み可能な最大count = ( this.Length - this._Position );
if( 0 < count )
{
Buffer.BlockCopy(
- src: this._EncodedWaveData,
+ src: this._DecodedWaveData,
srcOffset: (int) this._Position,
dst: buffer,
dstOffset: offset,
}
private MFMediaType _MediaType = null;
- private byte[] _EncodedWaveData = null;
+ private byte[] _DecodedWaveData = null;
private long _Position = 0;
}
}
/// デコード後のオーディオデータのすべての長さ[byte]。
/// </summary>
public long Length
- => this._EncodedWaveData.Length;
+ => this._DecodedWaveData.Length;
/// <summary>
/// 現在の再生位置[byte]。
// resampler.Length はサンプル単位ではなくフレーム単位。
var サイズbyte = resampler.Length * resampler.WaveFormat.Channels; // 実際のサイズはチャンネル倍ある。
- this._EncodedWaveData = new byte[ サイズbyte ];
- resampler.Read( this._EncodedWaveData, 0, (int) サイズbyte ); // でもこっちはバイト単位。
+ this._DecodedWaveData = new byte[ サイズbyte ];
+ resampler.Read( this._DecodedWaveData, 0, (int) サイズbyte ); // でもこっちはバイト単位。
}
}
public int Read( byte[] buffer, int offset, int count )
{
// ※ 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
- if( ( null == this._EncodedWaveData ) || ( null == buffer ) )
+ if( ( null == this._DecodedWaveData ) || ( null == buffer ) )
return 0;
long 読み込み可能な最大count = ( this.Length - this._Position );
if( 0 < count )
{
Buffer.BlockCopy(
- src: this._EncodedWaveData,
+ src: this._DecodedWaveData,
srcOffset: (int) this._Position,
dst: buffer,
dstOffset: offset,
/// </summary>
public void Dispose()
{
- this._EncodedWaveData = null;
+ this._DecodedWaveData = null;
}
- private byte[] _EncodedWaveData = null;
+ private byte[] _DecodedWaveData = null;
private long _Position = 0;
}
}
namespace FDK.メディア.サウンド.WASAPI
{
/// <summary>
- /// 指定されたメディアファイル(動画, 音楽)を Vorbis としてデコードして、CSCore.IWaveSource オブジェクトを生成する。
+ /// 指定されたメディアファイル(動画, 音楽)を Vorbis としてデコードして、CSCore.ISampleSource オブジェクトを生成する。
/// リサンプラーなし版。
/// 参照:<seealso cref="https://cscore.codeplex.com/SourceControl/latest#Samples/NVorbisIntegration/Program.cs"/>
/// </summary>
public static ISampleSource Create( SoundDevice device, string ファイルパス )
{
var path = Folder.絶対パスに含まれるフォルダ変数を展開して返す( ファイルパス );
+ var ext = Path.GetExtension( path ).ToLower();
#region " NVorbis を試みる "
//----------------
- try
+ if( ".ogg" == ext )
{
- using( var audioStream = new FileStream( path, FileMode.Open ) )
+ try
{
- return new NVorbisResampledWaveSource( audioStream, device.WaveFormat )
- .ToSampleSource();
+ using( var audioStream = new FileStream( path, FileMode.Open ) )
+ {
+ return new NVorbisResampledWaveSource( audioStream, device.WaveFormat )
+ .ToSampleSource();
+ }
+ }
+ catch
+ {
+ // ダメだったので次へ。
}
- }
- catch
- {
- // ダメだったので次へ。
}
//----------------
#endregion
- #region " CSCore を試みる "
+ #region " XA を試みる "
//----------------
- try
+ if( ".xa" == ext )
{
- // 対応できるフォーマットは MediaFoundation とダブるので、こちらの実装は不要かも。
- //return new CSCoreSampleSource( path );
- }
- catch
- {
- // ダメだったので次へ。
+ try
+ {
+ return new XaResampledWaveSource( path, device.WaveFormat )
+ .ToSampleSource();
+ }
+ catch
+ {
+ // ダメだったので次へ。
+ }
}
//----------------
#endregion
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using CSCore;
+using CSCore.DSP;
+
+namespace FDK.メディア.サウンド.WASAPI
+{
+ /// <summary>
+ /// 指定されたメディアファイルを XA としてデコードして、CSCore.IWaveSource オブジェクトを生成する。
+ /// リサンプラーなし版。
+ /// </summary>
+ unsafe class XAWaveSource : IWaveSource
+ {
+ public bool CanSeek => true; // オンメモリなので常にサポートする。
+
+ public WaveFormat WaveFormat
+ {
+ get;
+ protected set;
+ } = null;
+
+ /// <summary>
+ /// デコード後のオーディオデータのすべての長さ[byte]。
+ /// </summary>
+ public long Length
+ => this._DecodedWaveData.Length;
+
+ /// <summary>
+ /// 現在の再生位置[byte]。
+ /// </summary>
+ public long Position
+ {
+ get
+ => this._Position;
+ set
+ => this._Position = FDKUtilities.位置をブロック境界単位にそろえて返す( value, this.WaveFormat.BlockAlign );
+ }
+
+ /// <summary>
+ /// コンストラクタ。
+ /// 指定されたファイルを指定されたフォーマットでデコードし、内部にオンメモリで保管する。
+ /// </summary>
+ public XAWaveSource( string ファイルパス, WaveFormat deviceFormat )
+ {
+ var path = Folder.絶対パスに含まれるフォルダ変数を展開して返す( ファイルパス );
+
+ var xaheader = new XAHEADER();
+ var srcBuf = (byte[]) null;
+
+ #region " XAHEADER と XAデータ を読み込みむ。"
+ //----------------
+ using( var br = new BinaryReader( new FileStream( path, FileMode.Open ) ) )
+ {
+ xaheader.id = br.ReadUInt32();
+ xaheader.nDataLen = br.ReadUInt32();
+ xaheader.nSamples = br.ReadUInt32();
+ xaheader.nSamplesPerSec = br.ReadUInt16();
+ xaheader.nBits = br.ReadByte();
+ xaheader.nChannels = br.ReadByte();
+ xaheader.nLoopPtr = br.ReadUInt32();
+ xaheader.befL = new short[ 2 ];
+ xaheader.befL[ 0 ] = br.ReadInt16();
+ xaheader.befL[ 1 ] = br.ReadInt16();
+ xaheader.befR = new short[ 2 ];
+ xaheader.befR[ 0 ] = br.ReadInt16();
+ xaheader.befR[ 1 ] = br.ReadInt16();
+ xaheader.pad = new byte[ 4 ];
+ xaheader.pad = br.ReadBytes( 4 );
+
+ srcBuf = br.ReadBytes( (int) xaheader.nDataLen );
+ }
+ //----------------
+ #endregion
+
+ var waveformatex = new WAVEFORMATEX();
+ var handlePtr = IntPtr.Zero;
+
+ #region " XAファイルをオープンし、Waveフォーマットとハンドルを取得。"
+ //----------------
+ handlePtr = xaDecodeOpen( ref xaheader, out waveformatex );
+
+ if( null == handlePtr || IntPtr.Zero == handlePtr )
+ throw new Exception( $"xaDecodeOpen に失敗しました。[{ファイルパス}]" );
+ //----------------
+ #endregion
+
+ #region " Waveフォーマットを WaveFormat プロパティに設定。"
+ //----------------
+ if( 0 == waveformatex.cbSize )
+ {
+ this.WaveFormat = new WaveFormat(
+ (int) waveformatex.nSamplesPerSec,
+ (int) waveformatex.wBitsPerSample,
+ (int) waveformatex.nChannels,
+ (AudioEncoding) waveformatex.wFormatTag );
+ }
+ else
+ {
+ var msg = $"デコード後のフォーマットが WAVEFORMATEX 型になる XA には未対応です。[{ファイルパス}]";
+ Log.ERROR( msg );
+ throw new Exception( msg );
+ }
+ //----------------
+ #endregion
+
+ #region " デコード後のPCMサイズ[byte]を取得し、バッファを確保する。"
+ //----------------
+ if( !( xaDecodeSize( handlePtr, xaheader.nDataLen, out uint decodedWaveDataLength ) ) )
+ {
+ var msg = $"xaDecodeSize に失敗しました。[{ファイルパス}]";
+ Log.ERROR( msg );
+ throw new Exception( msg );
+ }
+ this._DecodedWaveData = new byte[ decodedWaveDataLength ];
+ //----------------
+ #endregion
+
+ #region " デコードする。"
+ //----------------
+ unsafe
+ {
+ fixed ( byte* pXaBuf = srcBuf )
+ fixed ( byte* pPcmBuf = this._DecodedWaveData )
+ {
+ var xastreamheader = new XASTREAMHEADER() {
+ pSrc = pXaBuf,
+ nSrcLen = xaheader.nDataLen,
+ nSrcUsed = 0,
+ pDst = pPcmBuf,
+ nDstLen = decodedWaveDataLength,
+ nDstUsed = 0,
+ };
+ if( !( xaDecodeConvert( handlePtr, ref xastreamheader ) ) )
+ {
+ var msg = $"xaDecodeConvert に失敗しました。[{ファイルパス}]";
+ Log.ERROR( msg );
+ throw new Exception( msg );
+ }
+ }
+ }
+ //----------------
+ #endregion
+
+ #region " XAファイルを閉じる。"
+ //----------------
+ if( !( xaDecodeClose( handlePtr ) ) )
+ {
+ var msg = $"xaDecodeClose に失敗しました。[{ファイルパス}]";
+ Log.ERROR( msg );
+ throw new Exception( msg );
+ }
+ //----------------
+ #endregion
+ }
+
+ /// <summary>
+ /// 解放する。
+ /// </summary>
+ public void Dispose()
+ {
+ this._DecodedWaveData = null;
+ }
+
+ /// <summary>
+ /// 連続したデータを読み込み、<see cref="Position"/> を読み込んだ数だけ進める。
+ /// </summary>
+ /// <param name="buffer">読み込んだデータを格納するための配列。</param>
+ /// <param name="offset"><paramref name="buffer"/> に格納を始める位置。</param>
+ /// <param name="count">読み込む最大のデータ数。</param>
+ /// <returns><paramref name="buffer"/> に読み込んだデータの総数。</returns>
+ public int Read( byte[] buffer, int offset, int count )
+ {
+ // ※ 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
+ if( ( null == this._DecodedWaveData ) || ( null == buffer ) )
+ return 0;
+
+ long 読み込み可能な最大count = ( this.Length - this._Position );
+ if( count > 読み込み可能な最大count )
+ count = (int) 読み込み可能な最大count;
+
+ if( 0 < count )
+ {
+ Buffer.BlockCopy(
+ src: this._DecodedWaveData,
+ srcOffset: (int) this._Position,
+ dst: buffer,
+ dstOffset: offset,
+ count: count );
+
+ this._Position += count;
+ }
+
+ return count;
+ }
+
+ private byte[] _DecodedWaveData = null;
+ private long _Position = 0;
+
+ #region " Win32(xsdec.dll) "
+ //----------------
+ [StructLayout( LayoutKind.Sequential )]
+ public struct WAVEFORMATEX
+ {
+ public ushort wFormatTag;
+ public ushort nChannels;
+ public uint nSamplesPerSec;
+ public uint nAvgBytesPerSec;
+ public ushort nBlockAlign;
+ public ushort wBitsPerSample;
+ public ushort cbSize;
+ }
+
+ [StructLayout( LayoutKind.Sequential )]
+ public struct XASTREAMHEADER
+ {
+ public byte* pSrc;
+ public uint nSrcLen;
+ public uint nSrcUsed;
+ public byte* pDst;
+ public uint nDstLen;
+ public uint nDstUsed;
+ }
+
+ [StructLayout( LayoutKind.Sequential )]
+ public struct XAHEADER
+ {
+ public uint id;
+ public uint nDataLen;
+ public uint nSamples;
+ public ushort nSamplesPerSec;
+ public byte nBits;
+ public byte nChannels;
+ public uint nLoopPtr;
+ [MarshalAs( UnmanagedType.ByValArray, SizeConst = 2 )]
+ public short[] befL;
+ [MarshalAs( UnmanagedType.ByValArray, SizeConst = 2 )]
+ public short[] befR;
+ [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 )]
+ public byte[] pad;
+ }
+
+ [DllImport( "xadec.dll", EntryPoint = "xaDecodeOpen", CallingConvention = CallingConvention.Cdecl )]
+ public extern static IntPtr xaDecodeOpen( ref XAHEADER pxah, out WAVEFORMATEX pwfx );
+
+ [DllImport( "xadec.dll", EntryPoint = "xaDecodeClose", CallingConvention = CallingConvention.Cdecl )]
+ public extern static bool xaDecodeClose( IntPtr hxas );
+
+ [DllImport( "xadec.dll", EntryPoint = "xaDecodeSize", CallingConvention = CallingConvention.Cdecl )]
+ public extern static bool xaDecodeSize( IntPtr hxas, uint slen, out uint pdlen );
+
+ [DllImport( "xadec.dll", EntryPoint = "xaDecodeConvert", CallingConvention = CallingConvention.Cdecl )]
+ public extern static bool xaDecodeConvert( IntPtr hxas, ref XASTREAMHEADER psh );
+ //----------------
+ #endregion
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using CSCore;
+using CSCore.DSP;
+
+namespace FDK.メディア.サウンド.WASAPI
+{
+ /// <summary>
+ /// 指定されたメディアファイルを XA としてデコードして、CSCore.IWaveSource オブジェクトを生成する。
+ /// リサンプラーあり版。
+ /// </summary>
+ class XaResampledWaveSource : IWaveSource
+ {
+ public bool CanSeek => true; // オンメモリなので常にサポートできる。
+
+ /// <summary>
+ /// デコード&リサンプル後のオーディオデータのフォーマット。
+ /// </summary>
+ public WaveFormat WaveFormat
+ {
+ get;
+ protected set;
+ } = null;
+
+ /// <summary>
+ /// デコード後のオーディオデータのすべての長さ[byte]。
+ /// </summary>
+ public long Length
+ => this._DecodedWaveData.Length;
+
+ /// <summary>
+ /// 現在の再生位置[byte]。
+ /// </summary>
+ public long Position
+ {
+ get
+ => this._Position;
+ set
+ => this._Position = FDKUtilities.位置をブロック境界単位にそろえて返す( value, this.WaveFormat.BlockAlign );
+ }
+
+ /// <summary>
+ /// コンストラクタ。
+ /// 指定されたXAファイルを指定されたフォーマットでデコードし、内部にオンメモリで保管する。
+ /// </summary>
+ public XaResampledWaveSource( string ファイルパス, WaveFormat deviceFormat )
+ {
+ this.WaveFormat = new WaveFormat(
+ deviceFormat.SampleRate,
+ 32,
+ deviceFormat.Channels,
+ AudioEncoding.IeeeFloat );
+
+ // リサンプルなし版で生成して、それを this.WaveFormat に合わせてリサンプルしたデータ(byte[])を保管する。
+ using( var xaSource = new XAWaveSource( ファイルパス, deviceFormat ) )
+ using( var resampler = new DmoResampler( xaSource, this.WaveFormat ) )
+ {
+ // resampler.Length はサンプル単位ではなくフレーム単位。
+ var サイズbyte = resampler.Length * resampler.WaveFormat.Channels; // 実際のサイズはチャンネル倍ある。
+
+ this._DecodedWaveData = new byte[ サイズbyte ];
+ resampler.Read( this._DecodedWaveData, 0, (int) サイズbyte ); // でもこっちはバイト単位。
+ }
+ }
+
+ /// <summary>
+ /// 連続したデータを読み込み、<see cref="Position"/> を読み込んだ数だけ進める。
+ /// </summary>
+ /// <param name="buffer">読み込んだデータを格納するための配列。</param>
+ /// <param name="offset"><paramref name="buffer"/> に格納を始める位置。</param>
+ /// <param name="count">読み込む最大のデータ数。</param>
+ /// <returns><paramref name="buffer"/> に読み込んだデータの総数。</returns>
+ public int Read( byte[] buffer, int offset, int count )
+ {
+ // ※ 音がめちゃくちゃになるとうざいので、このメソッド内では例外を出さないこと。
+ if( ( null == this._DecodedWaveData ) || ( null == buffer ) )
+ return 0;
+
+ long 読み込み可能な最大count = ( this.Length - this._Position );
+ if( count > 読み込み可能な最大count )
+ count = (int) 読み込み可能な最大count;
+
+ if( 0 < count )
+ {
+ Buffer.BlockCopy(
+ src: this._DecodedWaveData,
+ srcOffset: (int) this._Position,
+ dst: buffer,
+ dstOffset: offset,
+ count: count );
+
+ this._Position += count;
+ }
+
+ return count;
+ }
+
+ /// <summary>
+ /// 解放する。
+ /// </summary>
+ public void Dispose()
+ {
+ this._DecodedWaveData = null;
+ }
+
+ private byte[] _DecodedWaveData = null;
+ private long _Position = 0;
+ }
+}