/// <summary> /// 次のストリームを準備する /// </summary> private static bool PrepareNextStream(int index, List <KeyValuePair <string, object> > tags = null) { lock (PrepareMutex) { LastPreparedIndex = index; if (PreparedStream != null) { return(false); } if (index == -1) { PreparedStream = new StopperInputStream(); return(true); } if (index >= CurrentPlaylistRows || index < 0) { return(false); } object[] row = Controller.GetPlaylistRow(index); string filename = (string)row[Controller.GetColumnIndexByName(LibraryDBColumnTextMinimum.file_name)]; try { int tr = Util.Util.GetTrackNumberInt(row[Controller.GetColumnIndexByName("tagTracknumber")].ToString(), 1); PullSoundStreamBase nextStream = DecodeStreamFactory.CreateFileStream(filename, tr, MyCoreComponent.UsePrescan, tags); if (nextStream == null) { return(false); } if (nextStream.Chans == 1) { nextStream = new Mono2StereoFilter(nextStream); } //if (nextStream.Freq == 96000) //{ // nextStream = new FreqConvertFilter(nextStream); //} // prepareにsyncを設定 var inStream = new InputStream(nextStream, row); inStream.Ready = !OutputDeviceFactory.RebuildRequired(OutputDevice, nextStream.Freq, nextStream.Chans, true); inStream.SetEvent(on80Percent, nextStream.LengthSec * 0.80); inStream.SetEvent(onPreFinish, Math.Max(nextStream.LengthSec * 0.90, nextStream.LengthSec - 5)); PreparedStream = inStream; } catch (Exception e) { Logger.Log(e.ToString()); return(false); } } return(true); }
/// <summary> /// デコードストリームに対して出力デバイスをチェックし,必要があれば初期化する /// </summary> /// <param name="stream"></param> private static void EnsureOutputDevice(InputStream stream) { var freq = stream.Freq; var chans = stream.Chans; var isFloat = true; if (OutputDeviceFactory.RebuildRequired(OutputDevice, freq, chans, isFloat) || Pause) { try { if (OutputDevice != null) { var _outputManager = OutputDevice; OutputDevice = null; _outputManager.Dispose(); } } catch (Exception e) { Logger.Error(e); } DisposeCurrentStream(); Pause = false; if (OutputDevice != null) { var _outputManager = OutputDevice; OutputDevice = null; _outputManager.Dispose(); } try { OutputDevice = OutputDeviceFactory.CreateOutputDevice(StreamProc, freq, chans, isFloat, MyCoreComponent.BufferLength, MyCoreComponent.PreferredDeviceName); } catch (NotSupportedException ex) { Logger.Error(ex); } if (OutputDeviceFactory.RebuildRequired(OutputDevice, freq, chans, isFloat)) { Logger.Error("freq: " + freq); Logger.Error("chans: " + chans); Logger.Error("isFloat: " + isFloat); throw new Exception("Can not initialize output device"); } OutputDevice.Volume = Volume; StreamProcHold = 32768; } }
/// <summary> /// 超重要 /// 出力側からの要求に対して音声データを渡すコールバック関数です /// ここでは"必ず"要求された量のデータを埋めて返します。 /// 0. バッファ全体をゼロフィル /// 1. currentStreamから読み込めるだけ読む /// 2. 1.でバッファが埋まらなかった場合、preparedStreamから読み込めるだけ読む /// 3. 2.でバッファが埋まらなくても、バッファが最後まで埋まったとして返す /// (音声データが書き込まれなかったブロックは0. により無音となる) /// </summary> /// <param name="buffer"></param> /// <param name="length"></param> /// <returns></returns> private static uint StreamProc(IntPtr buffer, uint length) { // 参照コピー var _current = CurrentStream; var _prepare = PreparedStream; // StreamProcHoldがある間は出力を抑制する if (StreamProcHold > 0) { StreamProcHold = Math.Max(0, (int)(StreamProcHold - length)); ZeroMemory(buffer, length); return(length); // StreamProcHoldの値に関係なくlengthを返す.StreamProcHoldがキリの悪い値になっていてもこれなら大丈夫 } // 出力が無効なのにStreamProcがよばれた場合はとりあえずゼロフィルしたバッファを返す if (OutputDevice == null || _current == null || _current.Finished) { ZeroMemory(buffer, length); return(length); } // currentStreamから読み出し var read1 = ReadAsPossibleWithGain(_current, buffer, length); // 経過時間をチェック int timesec = (int)_current.PositionSec; if (timesec != ElapsedTime) { ElapsedTime = timesec; Controller._OnElapsedTimeChange(timesec); } // ストリームの途中の場合,読めた部分を返す if (_current.PositionSample < _current.LengthSample) { return(read1); } else { // ストリームの終端の場合 var readTotal = read1; if (_current.Ready && (_prepare == null || OutputDeviceFactory.RebuildRequired(OutputDevice, _prepare.Freq, _prepare.Chans, true))) { // 次のストリームが接続できない場合 // 現在のバッファの内容を使い切るぐらいまで再生終了を遅延させる _current.Ready = false; StreamProcHold = (int)(_current.Freq * _current.SampleBytes * 3 / 2); } else { // 次のストリームにそのまま接続できる場合はまたは前の出力の終了遅延が終わった場合 // _currentの再生を終了し onFinish(_current); // prepareStreamからの読み込みを試す var read2 = ReadAsPossibleWithGain(_prepare, IntPtr.Add(buffer, (int)read1), length - read1); readTotal += read2; } return(readTotal); } }