public void 登録する(チップ種別 chipType, int subChipId, VariablePath サウンドファイルパス) { if (File.Exists(サウンドファイルパス.数なしパス)) { lock (this._Sound利用権) { // すでに辞書に存在してるなら、解放して削除する。 if (this._チップtoコンテキスト.ContainsKey((chipType, subChipId))) { this._チップtoコンテキスト[(chipType, subChipId)]?.Dispose(); this._チップtoコンテキスト.Remove((chipType, subChipId)); } // コンテキストを作成する。 var context = new Cコンテキスト(this._多重度); // サウンドファイルを読み込んでデコードする。 context.SampleSource = SampleSourceFactory.Create(App.サウンドデバイス, サウンドファイルパス); // 多重度分のサウンドを生成する。 for (int i = 0; i < context.Sounds.Length; i++) { context.Sounds[i] = new Sound(App.サウンドデバイス, context.SampleSource); } // コンテキストを辞書に追加する。 this._チップtoコンテキスト.Add((chipType, subChipId), context); Log.Info($"ドラムサウンドを生成しました。[({chipType.ToString()},{subChipId}) = {サウンドファイルパス.変数付きパス}]"); } }
/// <summary> /// WAVファイルを登録する。 /// </summary> /// <param name="wav番号">登録する番号。0~1295。すでに登録されている場合は上書き更新される。</param> /// <param name="サウンドファイル">登録するサウンドファイルのパス。</param> public void 登録する(SoundDevice device, int wav番号, VariablePath サウンドファイル, bool 多重再生する) { #region " パラメータチェック。" //---------------- if (null == device) { throw new ArgumentNullException(); } if ((0 > wav番号) || (1295 < wav番号)) { throw new ArgumentOutOfRangeException($"WAV番号が範囲を超えています。[{wav番号}]"); } if (!(File.Exists(サウンドファイル.数なしパス))) { Log.WARNING($"サウンドファイルが存在しません。[{サウンドファイル.変数付きパス}]"); return; } //---------------- #endregion // 先に SampleSource を生成する。 var sampleSource = (ISampleSource)null; try { sampleSource = SampleSourceFactory.Create(device, サウンドファイル.数なしパス); } catch { Log.WARNING($"サウンドのデコードに失敗しました。[{サウンドファイル.変数付きパス}"); return; } // すでに登録済みなら解放する。 if (this._WavContexts.ContainsKey(wav番号)) { this._WavContexts[wav番号].Dispose(); this._WavContexts.Remove(wav番号); } // 新しいContextを生成して登録する。 var context = new WavContext(wav番号, (多重再生する) ? this._既定の多重度 : 1); context.SampleSource = sampleSource; for (int i = 0; i < context.Sounds.Length; i++) { context.Sounds[i] = new Sound(device, context.SampleSource); } this._WavContexts.Add(wav番号, context); Log.Info($"サウンドを読み込みました。[{サウンドファイル.変数付きパス}]"); }
private ISampleSource?_SampleSourceを生成する(VariablePath 音声ファイルの絶対パス) { ISampleSource?sampleSource = null; // キャッシュにある? lock (this._キャッシュ用排他) { if (this._サンプルソースキャッシュ.ContainsKey(音声ファイルの絶対パス.数なしパス)) { // あるなら、対応する ISampleSource を取得。 sampleSource = this._サンプルソースキャッシュ[音声ファイルの絶対パス.数なしパス]; } } if (sampleSource is null) { // ファイルから ISampleSource を生成する。 sampleSource = SampleSourceFactory.Create(Global.App.サウンドデバイス, 音声ファイルの絶対パス); if (sampleSource is null) { return(null); // 失敗した } // ISampleSource をキャッシュに登録する。 lock (this._キャッシュ用排他) { this._サンプルソースキャッシュ.Add(音声ファイルの絶対パス.数なしパス, sampleSource); this._キャッシュ世代リスト.Add(音声ファイルの絶対パス.数なしパス); } // キャッシュが一定数を超えたら、一番古いものから削除する。 if (_キャッシュする個数 < this._キャッシュ世代リスト.Count) { lock (this._キャッシュ用排他) { var path = this._キャッシュ世代リスト.Last(); // リストの末尾の最後の this._サンプルソースキャッシュ[path].Dispose(); // ISampleSource を解放して this._キャッシュ世代リスト.Remove(path); // キャッシュからも this._サンプルソースキャッシュ.Remove(path); // 削除。 } } } else { // キャッシュの世代リストで、今取得したソースが一番若くなるように操作する。 lock (this._キャッシュ用排他) { this._キャッシュ世代リスト.Remove(音声ファイルの絶対パス.数なしパス); // 冤罪の位置から除去して this._キャッシュ世代リスト.Add(音声ファイルの絶対パス.数なしパス); // 一番最新の位置へ。 } } // ISampleSource を返す。 return(sampleSource); }
internal Sound(AudioDevice audioDevice, string file, SoundFlags flags) : base(audioDevice) { if (string.IsNullOrEmpty(file)) { throw new ArgumentNullException("file"); } if (!System.IO.File.Exists(file)) { throw new System.IO.FileNotFoundException("file", file); } this.File = file; this.Flags = flags; this.Supports3D = flags.HasFlag(SoundFlags.Support3D); this.IsStreamed = flags.HasFlag(SoundFlags.Streamed); this.AllowRead = flags.HasFlag(SoundFlags.AllowRead); instances = new List <WeakReference <SoundInstance> >(); if (!IsStreamed) { using (var source = SampleSourceFactory.FromFile(file)) { var bufferCount = Supports3D ? source.Channels : 1; var channelCount = Supports3D ? 1 : source.Channels; var sampleRate = source.SampleRate; var samples = source.ReadAll(); buffers = new List <AudioBuffer <short> >(); short[] samplesChannel = new short[samples.Length / bufferCount]; for (int i = 0; i < bufferCount; i++) { SampleConverter.To16Bit(samples, samplesChannel, i, bufferCount); var buffer = new AudioBuffer <short>(audioDevice, !AllowRead); buffer.SetData(AudioFormat.Short16, channelCount, samplesChannel, sampleRate); buffers.Add(buffer); } } } }
protected override void On開始する() { // グローバルリソースを生成。(最低限。残りは起動ステージから グローバルリソースを生成する() が呼び出されたときに行われる。) App進行描画.乱数 = new Random(DateTime.Now.Millisecond); //App進行描画.システム設定 = システム設定.読み込む(); --> App() で初期化する。 App進行描画.WAVキャッシュレンタル = new キャッシュデータレンタル <CSCore.ISampleSource>() { ファイルからデータを生成する = (path) => SampleSourceFactory.Create(App進行描画.サウンドデバイス, path, App進行描画.ユーザ管理.ログオン中のユーザ.再生速度), }; App進行描画.サウンドデバイス = new サウンドデバイス(CSCore.CoreAudioAPI.AudioClientShareMode.Shared) { 音量 = 0.5f, // マスタ音量(小:0~1:大)... 0.5を超えるとだいたいWASAPI共有モードのリミッターに抑制されるようになる }; App進行描画.サウンドタイマ = new SoundTimer(App進行描画.サウンドデバイス); App進行描画.ドラムサウンド = new ドラムサウンド(); App進行描画.システムサウンド = new システムサウンド(); //App進行描画.システムサウンド.読み込む(); --> 起動ステージで行う。 App進行描画.入力管理 = new 入力管理(this.AppForm.キーボード) { キーバインディングを取得する = () => App進行描画.システム設定.キー割り当て, キーバインディングを保存する = () => App進行描画.システム設定.保存する(), }; App進行描画.入力管理.初期化する(); App進行描画.ユーザ管理 = new ユーザ管理(); App進行描画.ユーザ管理.ユーザリスト.SelectItem((user) => (user.ユーザID == "AutoPlayer")); // ひとまずAutoPlayerを選択。 // ステージを生成。(残りは起動ステージから グローバルリソースを生成する() が呼び出されたときにおこなれる。) this.起動ステージ = new 起動.起動ステージ(); this.演奏ステージ_ビュアーモード = new 演奏.演奏ステージ_ビュアーモード(); // 最初のステージを設定し、活性化する。 this.現在のステージ = this.起動ステージ; this.現在のステージ.活性化する(); }
protected override void On開始() { using (Log.Block(FDKUtilities.現在のメソッド名)) { // グローバルリソースを生成。(最低限。残りは起動ステージから グローバルリソースを生成する() が呼び出されたときに行われる。) App進行描画.乱数 = new Random(DateTime.Now.Millisecond); //App進行描画.システム設定 = システム設定.読み込む(); --> App() で初期化する。 App進行描画.WAVキャッシュレンタル = new キャッシュデータレンタル <CSCore.ISampleSource>() { ファイルからデータを生成する = (path) => SampleSourceFactory.Create(App進行描画.サウンドデバイス, path, App進行描画.ユーザ管理.ログオン中のユーザ.再生速度), }; App進行描画.サウンドデバイス = new SoundDevice(CSCore.CoreAudioAPI.AudioClientShareMode.Shared); App進行描画.サウンドデバイス.音量 = 0.5f; // マスタ音量(小:0~1:大)... 0.5を超えるとだいたいWASAPI共有モードのリミッターに抑制されるようになる // ※↑「音量」はコンストラクタの実行後でないと set できないので、初期化子にはしないこと。(挙動は不明) App進行描画.サウンドタイマ = new SoundTimer(App進行描画.サウンドデバイス); App進行描画.ドラムサウンド = new ドラムサウンド(); App進行描画.システムサウンド = new システムサウンド(); //App進行描画.システムサウンド.読み込む(); --> 起動ステージで行う。 App進行描画.入力管理 = new 入力管理(App進行描画.システム設定, this.AppForm.キーボード); App進行描画.ユーザ管理 = new ユーザ管理(); App進行描画.ユーザ管理.ユーザリスト.SelectItem((user) => (user.ユーザID == "AutoPlayer")); // ひとまずAutoPlayerを選択。 // ステージを生成。(残りは起動ステージから グローバルリソースを生成する() が呼び出されたときにおこなれる。) this.起動ステージ = new 起動.起動ステージ(); this.演奏ステージ_ビュアーモード = new 演奏.演奏ステージ_ビュアーモード(); // 最初のステージを設定し、活性化する。 this.現在のステージ = this.起動ステージ; this.現在のステージ.活性化する(); } }
/// <summary> /// <see cref="_音声ファイルパス"/>から、<see cref="_SampleSource"/>と<see cref="Sound"/> を生成する。 /// </summary> private void _プレビュー音声を生成する() { if (null != this.Sound) { return; // 生成済み } // (1) サンプルソースを生成する。 if (null == this._SampleSource) { // 未生成の場合、生成する。 this._SampleSource = SampleSourceFactory.Create( App進行描画.サウンドデバイス, new VariablePath(this._音声ファイルパス).数なしパス, 再生速度: 1.0); // プレビューは常に再生速度 = 1.0 if (null == this._SampleSource) { return; } } // (2) サンプルソースからサウンドを生成する。 this.Sound = new Sound(App進行描画.サウンドデバイス, this._SampleSource); }
protected override void On活性化(グラフィックデバイス gd) { using (Log.Block(FDKUtilities.現在のメソッド名)) { Debug.Assert(null != App.演奏スコア, "演奏スコアが指定されていません。"); this.キャプチャ画面 = null; this.成績 = new 成績(); this.成績.スコアと設定を反映する(App.演奏スコア, App.ユーザ管理.ログオン中のユーザ); this._描画開始チップ番号 = -1; this._小節線色 = new SolidColorBrush(gd.D2DDeviceContext, Color.White); this._拍線色 = new SolidColorBrush(gd.D2DDeviceContext, Color.LightGray); this._ドラムチップ画像の矩形リスト = new 矩形リスト(@"$(System)images\ドラムチップ矩形.xml"); // デバイスリソースは持たないので、子Activityではない。 this._現在進行描画中の譜面スクロール速度の倍率 = App.ユーザ管理.ログオン中のユーザ.譜面スクロール速度; this._ドラムチップアニメ = new LoopCounter(0, 200, 3); this._背景動画 = null; this._BGM = null; this._背景動画開始済み = false; this._BGM再生開始済み = false; //this._デコード済みWaveSource = null; --> キャッシュなので消さない。 this._プレイヤー名表示.前 = App.ユーザ管理.ログオン中のユーザ.ユーザ名; this._チップの演奏状態 = new Dictionary <チップ, チップの演奏状態>(); foreach (var chip in App.演奏スコア.チップリスト) { this._チップの演奏状態.Add(chip, new チップの演奏状態(chip)); } if (null != App.演奏スコア) { #region " 背景動画とBGMを生成する。" //---------------- if (App.演奏スコア.背景動画ファイル名.Nullでも空でもない()) { #region " (A) SST準拠の動画とBGM " //---------------- Log.Info("背景動画とBGMを読み込みます。"); // 動画を子リストに追加。 this.子リスト.Add(this._背景動画 = new 動画(App.演奏スコア.背景動画ファイル名)); // 動画から音声パートを抽出して BGM を作成。 try { this._デコード済みWaveSource?.Dispose(); this._デコード済みWaveSource = SampleSourceFactory.Create(App.サウンドデバイス, App.演奏スコア.背景動画ファイル名); this._BGM?.Dispose(); this._BGM = new Sound(App.サウンドデバイス, this._デコード済みWaveSource); } catch (InvalidDataException) { // DTXの動画のようにサウンドを含まない動画の場合、この例外が発生するだろう。 Log.WARNING("背景動画ファイルからBGMを生成することに失敗しました。"); this._BGM = null; } //---------------- #endregion } else if (0 < App.演奏スコア.dicAVI.Count) { #region " (B) DTX準拠の動画 " //---------------- Log.Info("背景動画を読み込みます。"); // #AVIzz がいくつ宣言されてても、最初のAVIだけを対象とする。 var path = Path.Combine(App.演奏スコア.PATH_WAV, App.演奏スコア.dicAVI.ElementAt(0).Value); // 動画を子リストに追加。 this.子リスト.Add(this._背景動画 = new 動画(path)); // BGM パートは使わない。 this._BGM = null; //---------------- #endregion } else { Log.Info("背景動画とBGMはありません。"); } //---------------- #endregion #region " WAVを生成する(ある場合)。" //---------------- App.WAV管理 = new 曲.WAV管理(); foreach (var kvp in App.演奏スコア.dicWAV) { var path = Path.Combine(App.演奏スコア.PATH_WAV, kvp.Value.ファイルパス); App.WAV管理.登録する(App.サウンドデバイス, kvp.Key, path.ToVariablePath(), kvp.Value.多重再生する); } //---------------- #endregion } this.現在のフェーズ = フェーズ.フェードイン; this._初めての進行描画 = true; } }
internal SoundInstance(AudioDevice audioDevice, Sound sound) : base(audioDevice) { if (sound == null) { throw new ArgumentNullException("sound"); } if (sound.IsDisposed) { throw new ArgumentException("Sound is disposed.", "sound"); } this.Sound = sound; IDs = new List <int>(); if (sound.IsStreamed) { source = SampleSourceFactory.FromFile(sound.File); } int bufferCount; if (sound.IsStreamed) { bufferCount = sound.Supports3D ? source.Channels : 1; } else { bufferCount = sound.Buffers.Count; } IDs.AddRange(AL.GenSources(bufferCount)); DotGame.OpenAL.AudioDevice.CheckALError(); if (sound.IsStreamed) { ringbuffers = new AudioBuffer <short> [bufferCount, RINGBUFFER_COUNT]; for (int i = 0; i < bufferCount; i++) { for (int j = 0; j < RINGBUFFER_COUNT; j++) { ringbuffers[i, j] = new AudioBuffer <short>(AudioDeviceInternal, false); } } ringBufferIndex = 0; } else { var buffers = sound.Buffers; for (int i = 0; i < buffers.Count; i++) { AL.Source(IDs[i], ALSourcei.Buffer, buffers[i].ID); DotGame.OpenAL.AudioDevice.CheckALError(); } } if (AudioDevice.Capabilities.SupportsEfx) { directFilter = AudioDeviceInternal.Efx.GenFilter(); AudioDeviceInternal.Efx.Filter(directFilter, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass); Set(ALSourcei.EfxDirectFilter, directFilter); } sound.Register(this); }