/// <summary>
        /// Converts sample format to toFormat and returns new instance of PcmData.
        /// pcmFrom is not changed.
        /// </summary>
        /// <param name="toFormat">sample format to convert</param>
        /// <returns>Newly instanciated PcmData</returns>
        public PcmData Convert(PcmData pcmFrom, WasapiCS.SampleFormatType toFormat, BitsPerSampleConvArgs args)
        {
            if (args == null)
            {
                args = new BitsPerSampleConvArgs(NoiseShapingType.None);
            }

            var fromFormat = WasapiCS.BitAndFormatToSampleFormatType(pcmFrom.BitsPerSample, pcmFrom.ValidBitsPerSample,
                                                                     SampleFormatInfo.VrtToBft(pcmFrom.SampleValueRepresentationType));

            if (fromFormat == WasapiCS.SampleFormatType.Unknown ||
                toFormat == WasapiCS.SampleFormatType.Unknown)
            {
                return(null);
            }

            var newSampleArray = mConvert[(int)fromFormat][(int)toFormat](pcmFrom, toFormat, args);

            PcmData newPcmData = new PcmData();

            newPcmData.CopyHeaderInfoFrom(pcmFrom);
            newPcmData.SetFormat(pcmFrom.NumChannels,
                                 WasapiCS.SampleFormatTypeToUseBitsPerSample(toFormat),
                                 WasapiCS.SampleFormatTypeToValidBitsPerSample(toFormat), pcmFrom.SampleRate,
                                 SampleFormatInfo.BftToVrt(WasapiCS.SampleFormatTypeToBitFormatType(toFormat)), pcmFrom.NumFrames);
            newPcmData.SetSampleArray(newSampleArray);

            return(newPcmData);
        }
Beispiel #2
0
        private PcmData ReadWavFile(string path)
        {
            PcmData pcmData = new PcmData();

            using (BinaryReader br = new BinaryReader(
                       File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))) {
                var  wavR        = new WavReader();
                bool readSuccess = wavR.ReadHeaderAndSamples(br, 0, -1);
                if (!readSuccess)
                {
                    return(null);
                }
                pcmData.SetFormat(wavR.NumChannels, wavR.BitsPerSample, wavR.BitsPerSample,
                                  wavR.SampleRate, wavR.SampleValueRepresentationType, wavR.NumFrames);
                pcmData.SetSampleArray(wavR.GetSampleArray());
            }

            return(pcmData);
        }
Beispiel #3
0
        public static PcmData ReadWav(BinaryReader br)
        {
            var reader = new WavReader();

            if (!reader.ReadHeaderAndSamples(br, 0, -1))
            {
                return(null);
            }

            var pcm = new PcmData();

            pcm.AlbumTitle  = reader.AlbumName;
            pcm.ArtistName  = reader.ArtistName;
            pcm.DisplayName = reader.Title;

            pcm.SetFormat(reader.NumChannels, reader.BitsPerSample,
                          reader.ValidBitsPerSample, reader.SampleRate,
                          reader.SampleValueRepresentationType, reader.NumFrames);

            pcm.SetSampleArray(reader.GetSampleArray());

            return(pcm);
        }
        private void m_USAQworker_DoWork(object sender, DoWorkEventArgs e)
        {
            // System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;

            USWorkerArgs args = (USWorkerArgs)e.Argument;

            PcmData pcmDataIn = ReadWavFile(args.inputPath);

            if (null == pcmDataIn)
            {
                e.Result = string.Format("WAVファイル 読み込み失敗: {0}", args.inputPath);
                return;
            }

            // ファイル読み込み完了。
            if (args.addJitter)
            {
                // ジッター負荷の場合、サンプリング周波数は変更しない。
                args.resampleFrequency = pcmDataIn.SampleRate;
            }

            if (args.resampleFrequency < pcmDataIn.SampleRate)
            {
                e.Result = string.Format("エラー: ダウンサンプルは対応していません {0} from={1} to={2}",
                                         args.inputPath, pcmDataIn.SampleRate, args.resampleFrequency);
                return;
            }
            if (0x7fff0000L < pcmDataIn.NumFrames * 4 * pcmDataIn.NumChannels * args.resampleFrequency / pcmDataIn.SampleRate)
            {
                e.Result = string.Format("エラー: リサンプル後のファイルサイズが2GBを超えそうなので中断しました {0}",
                                         args.inputPath);
                return;
            }

            m_USAQworker.ReportProgress(1);

            var conv = new WasapiPcmUtil.PcmFormatConverter(pcmDataIn.NumChannels);

            pcmDataIn = conv.Convert(pcmDataIn, WasapiCS.BitAndFormatToSampleFormatType(32, 32, WasapiCS.BitFormatType.SFloat), null);
            PcmData pcmDataOut = new PcmData();

            pcmDataOut.CopyFrom(pcmDataIn);
            int sampleTotalTo = (int)(args.resampleFrequency * pcmDataIn.NumFrames / pcmDataIn.SampleRate);

            {   // PcmDataOutのサンプルレートとサンプル数を更新する。
                byte[] outSampleArray = new byte[(long)sampleTotalTo * pcmDataOut.NumChannels * 4];
                pcmDataOut.SetSampleArray(sampleTotalTo, outSampleArray);
                pcmDataOut.SampleRate = args.resampleFrequency;
                outSampleArray        = null;
            }

            // 再サンプルテーブル作成
            args.resamplePosArray = null;
            args.fractionArray    = null;
            if (args.addJitter)
            {
                // ジッター付加の場合、サンプルレートは変更しない。
                args.resamplePosArray = new int[pcmDataIn.NumFrames];
                args.fractionArray    = new double[pcmDataIn.NumFrames];

                /*
                 * sampleRate        == 96000 Hz
                 * jitterFrequency   == 50 Hz
                 * jitterPicoseconds == 1 ps の場合
                 *
                 * サンプル位置posのθ= 2 * PI * pos * 50 / 96000 (ラジアン)
                 *
                 * サンプル間隔= 1/96000秒 = 10.4 μs
                 *
                 * 1ms = 10^-3秒
                 * 1μs= 10^-6秒
                 * 1ns = 10^-9秒
                 * 1ps = 10^-12秒
                 *
                 * 1psのずれ                     x サンプルのずれ
                 * ───────────── = ─────────
                 * 10.4 μs(1/96000)sのずれ      1 サンプルのずれ
                 *
                 * 1psのサンプルずれA = 10^-12 ÷ (1/96000) (サンプルのずれ)
                 *
                 * サンプルを採取する位置= pos + Asin(θ)
                 *
                 */

                args.thetaCoefficientSeqJitter = 2.0 * Math.PI * args.sequentialJitterFrequency / pcmDataIn.SampleRate;
                args.ampSeqJitter  = 1.0e-12 * pcmDataIn.SampleRate * args.sequentialJitterPicoseconds;
                args.ampTpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.tpdfJitterPicoseconds;
                args.ampRpdfJitter = 1.0e-12 * pcmDataIn.SampleRate * args.rpdfJitterPicoseconds;

                PrepareResamplePosArray(
                    args, pcmDataIn.SampleRate, pcmDataOut.SampleRate,
                    (int)pcmDataIn.NumFrames, sampleTotalTo,
                    args.resamplePosArray, args.fractionArray);
            }

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            int hr = 0;

            if (args.device == ProcessDevice.Gpu)
            {
                hr = GpuUpsample(args, pcmDataIn, pcmDataOut);
            }
            else
            {
                hr = CpuUpsample(args, pcmDataIn, pcmDataOut);
            }

            // args.resamplePosArrayは中でコピーされるのでここで不要になる。
            args.resamplePosArray = null;
            args.fractionArray    = null;

            if (m_USAQworker.CancellationPending)
            {
                e.Result = string.Format("キャンセル完了。");
                e.Cancel = true;
                return;
            }
            if (hr < 0)
            {
                e.Result = string.Format("Upsample エラー 0x{0:X8}", hr);
                return;
            }
            sw.Stop();

            // 成功した。レベル制限する。
            float scale = pcmDataOut.LimitLevelOnFloatRange();

            if (args.outputVRT != PcmData.ValueRepresentationType.SFloat)
            {
                // ビットフォーマット変更。
                var formatConv = new WasapiPcmUtil.PcmFormatConverter(pcmDataOut.NumChannels);
                pcmDataOut = formatConv.Convert(pcmDataOut,
                                                WasapiCS.BitAndFormatToSampleFormatType(args.outputBitsPerSample, args.outputBitsPerSample, (WasapiCS.BitFormatType)args.outputVRT), null);
            }

            try {
                WriteWavFile(pcmDataOut, args.outputPath);
            } catch (IOException ex) {
                // 書き込みエラー。
                e.Result = ex.ToString();
                return;
            }

            e.Result = string.Format("書き込み成功。処理時間 {0}秒\r\n",
                                     sw.ElapsedMilliseconds * 0.001);
            if (scale < 1.0f)
            {
                e.Result = string.Format("書き込み成功。処理時間 {0}秒。" +
                                         "レベルオーバーのため音量調整{1}dB({2}倍)しました。\r\n",
                                         sw.ElapsedMilliseconds * 0.001,
                                         20.0 * Math.Log10(scale), scale);
            }
            m_USAQworker.ReportProgress(100);
        }
        private bool ItUpsampleDo(FirWorkerArgs args, PcmData pcmDataIn, out PcmData pcmDataOut)
        {
            pcmDataOut = new PcmData();
            pcmDataOut.SetFormat(pcmDataIn.NumChannels, 64, 64,
                                 pcmDataIn.SampleRate * mFreqMagnitude,
                                 PcmData.ValueRepresentationType.SFloat, pcmDataIn.NumFrames * mFreqMagnitude);
            pcmDataOut.SetSampleArray(new byte[pcmDataOut.NumFrames * pcmDataOut.BitsPerFrame / 8]);

            var pcm = pcmDataOut;

            switch (mItUpsampleType)
            {
            case ItUpsampleType.ImpulseTrain:
                Parallel.For(0, pcmDataIn.NumFrames, (pos) => {
                    for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch)
                    {
                        var v = pcmDataIn.GetSampleValueInDouble(ch, pos);
                        pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude, v);
                    }
                });
                break;

            case ItUpsampleType.SampleHold:
                Parallel.For(0, pcmDataIn.NumFrames, (pos) => {
                    for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch)
                    {
                        var v = pcmDataIn.GetSampleValueInDouble(ch, pos);
                        for (int i = 0; i < mFreqMagnitude; ++i)
                        {
                            pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v);
                        }
                    }
                });
                break;

            case ItUpsampleType.Linear:
                Parallel.For(0, pcmDataIn.NumFrames - 1, (pos) => {
                    // 0 <= pos <= NumFrames-2まで実行する
                    for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch)
                    {
                        var v0 = pcmDataIn.GetSampleValueInDouble(ch, pos);
                        var v1 = pcmDataIn.GetSampleValueInDouble(ch, pos + 1);
                        for (int i = 0; i < mFreqMagnitude; ++i)
                        {
                            var ratio = (double)i / mFreqMagnitude;
                            var v     = v0 * (1 - ratio) + v1 * ratio;
                            pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v);
                        }
                    }
                });

                // 最後の1区間は0に向かう
                for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch)
                {
                    var pos = pcmDataIn.NumFrames - 1;
                    var v   = pcmDataIn.GetSampleValueInDouble(ch, pos);
                    for (int i = 0; i < mFreqMagnitude; ++i)
                    {
                        pcm.SetSampleValueInDouble(ch, pos * mFreqMagnitude + i, v * (mFreqMagnitude - i - 1) / mFreqMagnitude);
                    }
                }
                break;

            case ItUpsampleType.Cubic: {
                var    h = new double[4 * mFreqMagnitude];
                double a = -0.5;
                for (int i = 0; i < mFreqMagnitude; ++i)
                {
                    double t = (double)i / mFreqMagnitude;
                    h[mFreqMagnitude * 2 + i] = (a + 2) * t * t * t - (a + 3) * t * t + 1;
                    h[mFreqMagnitude * 2 - i] = h[mFreqMagnitude * 2 + i];
                }
                for (int i = mFreqMagnitude; i < mFreqMagnitude * 2; ++i)
                {
                    double t = (double)i / mFreqMagnitude;
                    h[mFreqMagnitude * 2 + i] = a * t * t * t - 5 * a * t * t + 8 * a * t - 4 * a;
                    h[mFreqMagnitude * 2 - i] = h[mFreqMagnitude * 2 + i];
                }
                Parallel.For(0, pcmDataOut.NumFrames, (pos) => {
                        for (int ch = 0; ch < pcmDataIn.NumChannels; ++ch)
                        {
                            var x = new double[4 * mFreqMagnitude];
                            for (int i = 0; i < 4 * mFreqMagnitude; ++i)
                            {
                                if (0 == (pos + i) % mFreqMagnitude)
                                {
                                    x[i] = pcmDataIn.GetSampleValueInDouble(ch, (pos + i) / mFreqMagnitude);
                                }
                            }
                            double v = 0;
                            for (int i = 0; i < 4 * mFreqMagnitude; ++i)
                            {
                                v += h[i] * x[i];
                            }
                            pcm.SetSampleValueInDouble(ch, pos, v);
                        }
                    });
            }
            break;

            default:
                System.Diagnostics.Debug.Assert(false);
                break;
            }

            return(true);
        }