bool Run(string inFile, float scale, string firCoeffsFile, string outFile) { int rv; // 係数ファイルを読む。 var coeffs = ReadCoeffsFile(firCoeffsFile); if (coeffs.Length == 0) { Console.WriteLine("Error: FIR coeffs file read error: {0}", firCoeffsFile); return(false); } Console.WriteLine(" FIR taps = {0}", coeffs.Length); // coeffsをスケールする。 for (int i = 0; i < coeffs.Length; ++i) { coeffs[i] = coeffs[i] * scale; } var writePcmList = new List <LargeArray <byte> >(); Metadata meta; byte[] picture = null; { var fr = new FlacRW(); // 入力FLACファイルを読む。 rv = fr.DecodeAll(inFile); if (rv < 0) { Console.WriteLine("Error: FLAC read error {0}: {1}", FlacRW.ErrorCodeToStr(rv), inFile); return(false); } fr.GetDecodedMetadata(out meta); if (0 < meta.pictureBytes) { picture = new byte[meta.pictureBytes]; rv = fr.GetDecodedPicture(out picture, picture.Length); if (rv < 0) { Console.WriteLine("Error: FLAC get picture error {0}: {1}", FlacRW.ErrorCodeToStr(rv), inFile); return(false); } } Console.WriteLine(" SampleRate={0}Hz, BitDepth={1}, Channels={2}", meta.sampleRate, meta.bitsPerSample, meta.channels); for (int ch = 0; ch < meta.channels; ++ch) { writePcmList.Add(null); } // FIR係数を畳み込む。 // 申し訳程度の最適化:2チャンネル音声の場合2スレッドで並列動作。 float maxMagnitude = 0; Parallel.For(0, meta.channels, ch => { var inData = fr.GetFloatPcmOfChannel(ch, 0, meta.totalSamples); #if true var conv = new FIRConvolution(); var convoluted = conv.Convolution(inData, coeffs); if (maxMagnitude < conv.MaxMagnitude) { maxMagnitude = conv.MaxMagnitude; } var pcm = FlacRW.ConvertToByteArrayPCM(convoluted, meta.BytesPerSample); #else // debug: output unchanged input file var pcm = FlacRW.ConvertToByteArrayPCM(inData, meta.BytesPerSample); #endif writePcmList[ch] = pcm; }); if (8388607.0f / 8388608.0f < maxMagnitude) { Console.WriteLine("Error: convolution result PCM value overflow. Please reduce input PCM gain further by {0:0.00} dB", 20.0 * Math.Log10(maxMagnitude)); return(false); } else { Console.WriteLine(" Max PCM magnitude = {0}", maxMagnitude); } fr.DecodeEnd(); } { // FLACファイルを書き込む。 var fw = new FlacRW(); fw.EncodeInit(meta); for (int ch = 0; ch < meta.channels; ++ch) { fw.EncodeAddPcm(ch, writePcmList[ch]); } if (picture != null) { fw.EncodeSetPicture(picture); } rv = fw.EncodeRun(outFile); if (rv < 0) { Console.WriteLine("Error: FLAC write error {0}: {1}", FlacRW.ErrorCodeToStr(rv), outFile); return(false); } fw.EncodeEnd(); } return(true); }
private int Convert1(ConvertArgs ca) { int hr = 0; CallEvent(EventCallbackTypes.Started, PROGRESS_STARTED, hr); hr = mResampleGpu.ChooseAdapter(ca.mGpuId); if (hr < 0) { CallEvent(EventCallbackTypes.InitGpuAdapterFailed, 0, hr); return(hr); } var flacR = new FlacRW(); hr = flacR.DecodeAll(ca.mInPath); if (hr < 0) { CallEvent(EventCallbackTypes.ReadFailed, 0, hr); return(hr); } else { CallEvent(EventCallbackTypes.ReadCompleted, PROGRESS_READ_END, hr); } Metadata metaR; flacR.GetDecodedMetadata(out metaR); var inPcmOfCh = new List <float[]>(); for (int ch = 0; ch < metaR.channels; ++ch) { var pcm = new LargeArray <byte>(metaR.BytesPerSample * metaR.totalSamples); int fragmentSamples = 1024 * 1024; for (long posSamples = 0; posSamples < metaR.totalSamples; posSamples += fragmentSamples) { int copySamples = fragmentSamples; if ((metaR.totalSamples - posSamples) < copySamples) { copySamples = (int)(metaR.totalSamples - posSamples); } var b = new byte[metaR.BytesPerSample * copySamples]; flacR.GetPcmOfChannel(ch, posSamples, ref b, copySamples); pcm.CopyFrom(b, 0, metaR.BytesPerSample * posSamples, metaR.BytesPerSample * copySamples); } var pcmData = new PcmData(); pcmData.SetFormat(1, metaR.bitsPerSample, metaR.bitsPerSample, metaR.sampleRate, PcmData.ValueRepresentationType.SInt, metaR.totalSamples); pcmData.SetSampleLargeArray(pcm); var fb = new float[metaR.totalSamples]; for (long i = 0; i < metaR.totalSamples; ++i) { fb[i] = pcmData.GetSampleValueInFloat(0, i); } pcmData = null; pcm = null; inPcmOfCh.Add(fb); } { CallEvent(EventCallbackTypes.PrepareDataCompleted, PROGRESS_PREPARE_END, hr); } System.Diagnostics.Debug.Assert(0.5 <= ca.mSampleRateScale & ca.mSampleRateScale <= 2.0); int sampleRateTo = (int)(ca.mSampleRateScale * metaR.sampleRate); int sampleTotalTo = (int)(ca.mSampleRateScale * metaR.totalSamples); // metaW: 出力フォーマット。 var metaW = new Metadata(metaR); if (ca.mSampleRateScale < 1.0) { // 曲の長さを縮めると、エイリアシング雑音が出るのでローパスフィルターが必要になる。 // 出力サンプルレートを倍にしてローパスフィルターを省略。 sampleRateTo = (int)(2.0 * ca.mSampleRateScale * metaR.sampleRate); sampleTotalTo = (int)(2.0 * ca.mSampleRateScale * metaR.totalSamples); metaW.sampleRate *= 2; } metaW.bitsPerSample = 24; metaW.totalSamples = sampleTotalTo; // ローパスフィルターが不要になる条件。 System.Diagnostics.Debug.Assert(metaR.sampleRate <= metaW.sampleRate); var outPcmOfCh = new List <float[]>(); for (int ch = 0; ch < metaR.channels; ++ch) { var inPcm = inPcmOfCh[ch]; mResampleGpu.Setup(SINC_CONVOLUTION_N, inPcm, (int)metaR.totalSamples, metaR.sampleRate, sampleRateTo, sampleTotalTo); for (int i = 0; i < sampleTotalTo; i += GPU_WORK_COUNT) { // 出力サンプル数countの調整。 int count = GPU_WORK_COUNT; if (sampleTotalTo < i + count) { count = sampleTotalTo - i; } hr = mResampleGpu.Dispatch(i, count); if (hr < 0) { CallEvent(EventCallbackTypes.ConvertFailed, 0, hr); return(hr); } else { float progress0to1 = ((float)ch / metaR.channels) + (1.0f / metaR.channels) * ((float)i / sampleTotalTo); int percent = (int)(PROGRESS_CONV_START + progress0to1 * (PROGRESS_CONV_END - PROGRESS_CONV_START)); CallEvent(EventCallbackTypes.ConvProgress, percent, hr); } } var outPcm = new float[sampleTotalTo]; mResampleGpu.ResultGetFromGpuMemory(outPcm); outPcmOfCh.Add(outPcm); mResampleGpu.Unsetup(); } CallEvent(EventCallbackTypes.WriteStarted, PROGRESS_CONV_END, hr); var flacW = new FlacRW(); hr = flacW.EncodeInit(metaW); if (hr < 0) { CallEvent(EventCallbackTypes.WriteFailed, 0, hr); return(hr); } if (0 < metaR.pictureBytes) { // 画像。 byte[] metaPicture = null; flacR.GetDecodedPicture(out metaPicture, metaR.pictureBytes); hr = flacW.EncodeSetPicture(metaPicture); if (hr < 0) { CallEvent(EventCallbackTypes.WriteFailed, 0, hr); return(hr); } } for (int ch = 0; ch < metaW.channels; ++ch) { // 24bitのbyteAry作成。 var floatAry = outPcmOfCh[ch]; var byteAry = new LargeArray <byte>(3 * metaW.totalSamples); //< 24bitなので1サンプルあたり3バイト。 for (long i = 0; i < metaW.totalSamples; ++i) { var b = PcmDataUtil.ConvertTo24bitLE(floatAry[i]); byteAry.CopyFrom(b, 0, i * 3, 3); } flacW.EncodeAddPcm(ch, byteAry); } hr = flacW.EncodeRun(ca.mOutPath); if (hr < 0) { CallEvent(EventCallbackTypes.WriteFailed, 0, hr); return(hr); } flacW.EncodeEnd(); flacR.DecodeEnd(); return(hr); }