/// <summary> /// 指定チャンネルのPCMデータを取得。 /// </summary> /// <param name="ch">チャンネル番号 0から始まる。</param> /// <param name="posSamples">取得開始サンプル番号。</param> /// <param name="copySamples">取得するサンプル数。</param> /// <returns>float型のPCMデータ。-1 ≦ v < +1</returns> public LargeArray <float> GetFloatPcmOfChannel(int ch, long posSamples, long copySamples) { System.Diagnostics.Debug.Assert(mDecodedMetadata != null); System.Diagnostics.Debug.Assert(mPcmAllBuffer != null); System.Diagnostics.Debug.Assert(0 <= ch && ch < mDecodedMetadata.channels); int bpf = mDecodedMetadata.BytesPerFrame; int bps = mDecodedMetadata.BytesPerSample; // copySamplesを決定します。 long totalSamples = mPcmAllBuffer.LongLength / bpf; if (totalSamples < posSamples + copySamples) { copySamples = (int)(totalSamples - posSamples); } if (copySamples < 0) { copySamples = 0; } var samples = new LargeArray <float>(copySamples); long toIdx = 0; switch (bps) { case 2: for (long pos = posSamples; pos < posSamples + copySamples; ++pos) { long idx = bpf * pos + ch * bps; short vS = (short)(mPcmAllBuffer.At(idx) + (mPcmAllBuffer.At(idx + 1) << 8)); float vF = vS / 32768.0f; samples.Set(toIdx++, vF); //Console.WriteLine("{0:+00000;-00000}", vS); } break; case 3: for (long pos = posSamples; pos < posSamples + copySamples; ++pos) { long idx = bpf * pos + ch * bps; int vI = (int)((mPcmAllBuffer.At(idx + 0) << 8) + (mPcmAllBuffer.At(idx + 1) << 16) + (mPcmAllBuffer.At(idx + 2) << 24)); //Console.WriteLine("{0:+00000000;-00000000}", vI); float vF = vI / 2147483648.0f; samples.Set(toIdx++, vF); } break; default: throw new ArgumentException(string.Format("Unexpected BytesPerSample {0}", bps)); } return(samples); }
public LargeArray <WWComplex> ForwardFft(LargeArray <WWComplex> aFrom) { if (aFrom == null || aFrom.LongLength != mNumPoints) { throw new ArgumentOutOfRangeException("aFrom"); } var aTo = new LargeArray <WWComplex>(aFrom.LongLength); var aTmp0 = new LargeArray <WWComplex>(mNumPoints); for (int i = 0; i < aTmp0.LongLength; ++i) { aTmp0.Set(i, aFrom.At((long)mBitReversalTable.At(i))); } var aTmp1 = new LargeArray <WWComplex>(mNumPoints); for (int i = 0; i < aTmp1.LongLength; ++i) { aTmp1.Set(i, WWComplex.Zero()); } var aTmps = new LargeArray <WWComplex> [2]; aTmps[0] = aTmp0; aTmps[1] = aTmp1; for (int i = 0; i < mNumStage - 1; ++i) { FftStageN(i, aTmps[((i & 1) == 1) ? 1 : 0], aTmps[((i & 1) == 0) ? 1 : 0]); } FftStageN(mNumStage - 1, aTmps[(((mNumStage - 1) & 1) == 1) ? 1 : 0], aTo); return(aTo); }
public LargeArray <WWComplex> InverseFft(LargeArray <WWComplex> aFrom, double?compensation = null) { for (int i = 0; i < aFrom.LongLength; ++i) { var t = new WWComplex(aFrom.At(i).real, -aFrom.At(i).imaginary); aFrom.Set(i, t); } var aTo = ForwardFft(aFrom); double c = 1.0 / mNumPoints; if (compensation != null) { c = (double)compensation; } for (int i = 0; i < aTo.LongLength; ++i) { var t = new WWComplex(aTo.At(i).real *c, -aTo.At(i).imaginary *c); aTo.Set(i, t); } return(aTo); }
public WWRadix2FftLargeArray(long numPoints) { if (!Functions.IsPowerOfTwo(numPoints) || numPoints < 2) { throw new ArgumentException("numPoints must be power of two integer and larger than 2"); } mNumPoints = numPoints; mWn = new LargeArray <WWComplex>(mNumPoints); for (long i = 0; i < mNumPoints; ++i) { double angle = -2.0 * Math.PI * i / mNumPoints; mWn.Set(i, new WWComplex(Math.Cos(angle), Math.Sin(angle))); } // mNumStage == log_2(mNumPoints) long t = mNumPoints; for (int i = 0; 0 < t; ++i) { t >>= 1; mNumStage = i; } mBitReversalTable = new LargeArray <ulong>(mNumPoints); for (long i = 0; i < mNumPoints; ++i) { mBitReversalTable.Set(i, BitReversal(mNumStage, (ulong)i)); } }
/// <summary> /// dataにcoeffsを畳み込む。 /// </summary> public LargeArray <float> Convolution(LargeArray <float> data, float[] coeffs) { mMaxMagnitude = 0; var r = new LargeArray <float>(data.LongLength); // rIdx: 出力サンプル列rの書き込みidx。1づつ増える。 for (long rIdx = 0; rIdx < data.LongLength; ++rIdx) { // v: 畳み込み結果。 float v = 0; // dIdx: dataの読み出し要素番号。 long dIdx = rIdx - coeffs.Length / 2; // cIdx: coeffsの読み出し要素番号。1づつ減る。 for (int cIdx = coeffs.Length - 1; 0 <= cIdx; --cIdx) { // dataの範囲外を読まないようにする。 if (0 <= dIdx && dIdx < data.LongLength) { v += data.At(dIdx++) * coeffs[cIdx]; } } if (mMaxMagnitude < Math.Abs(v)) { mMaxMagnitude = v; } r.Set(rIdx, v); } return(r); }
private LargeArray <byte> CreatePcmSamples(double[] mls, WasapiCS.SampleFormatType sft, int numCh, int playCh) { int sampleBytes = (WasapiCS.SampleFormatTypeToUseBitsPerSample(sft) / 8); int frameBytes = sampleBytes * numCh; long bytes = (long)mls.Length * frameBytes; var r = new LargeArray <byte>(bytes); long writePos = 0; for (long i = 0; i < mls.Length; ++i) { for (int ch = 0; ch < numCh; ++ch) { if (ch != playCh) { for (int c = 0; c < sampleBytes; ++c) { r.Set(writePos++, 0); } } else { int v = 0x7fffffff; // -6dBする。 v /= 2; if (mls[i] < 0) { v = -v; } uint uV = (uint)v; for (int c = 0; c < sampleBytes; ++c) { byte b = (byte)(uV >> (8 * (4 - sampleBytes + c))); r.Set(writePos++, b); } } } } return(r); }
public SampleData1ch(long size) { sdmData = new LargeArray <byte>(size); pos = 0; // 念のため無音をセットする。 for (long i = 0; i < size; ++i) { sdmData.Set(i, 0x69); } }
private LargeArray <float> StereoToMono(LargeArray <byte> v) { var m = new LargeArray <float>(v.LongLength / 4); for (long i = 0; i < v.LongLength / 4; ++i) { short l = (short)((int)v.At(i * 4 + 0) + 256 * v.At(i * 4 + 1)); short r = (short)((int)v.At(i * 4 + 2) + 256 * v.At(i * 4 + 3)); short mix = (short)((l + r) / 2); m.Set(i, mix / 32768.0f); } return(m); }
public static LargeArray <double> BlackmanWindow(long length) { // nは奇数 System.Diagnostics.Debug.Assert((length & 1) == 1); var window = new LargeArray <double>(length); // 教科書通りに計算すると両端の値が0.0になって // せっかくのデータが0にされて勿体無いので両端(pos==0とpos==length-1)の値はカットし、両端を1ずつ広げる long m = length + 1; for (long i = 0; i < length; ++i) { long pos = i + 1; double v = 0.42 - 0.5 * Math.Cos(2.0 * Math.PI * pos / m) + 0.08 * Math.Cos(4.0 * Math.PI * pos / m); window.Set(i, v); } return(window); }
private void FftStageN(int stageNr, LargeArray <WWComplex> x, LargeArray <WWComplex> y) { /* * stage0: 2つの入力データにバタフライ演算 (length=8の時) 4回 (nRepeat=4, nSubRepeat=2) * y[0] = x[0] + w_n^(0*4) * x[1] * y[1] = x[0] + w_n^(1*4) * x[1] * * y[2] = x[2] + w_n^(0*4) * x[3] * y[3] = x[2] + w_n^(1*4) * x[3] * * y[4] = x[4] + w_n^(0*4) * x[5] * y[5] = x[4] + w_n^(1*4) * x[5] * * y[6] = x[6] + w_n^(0*4) * x[7] * y[7] = x[6] + w_n^(1*4) * x[7] */ /* * stage1: 4つの入力データにバタフライ演算 (length=8の時) 2回 (nRepeat=2, nSubRepeat=4) * y[0] = x[0] + w_n^(0*2) * x[2] * y[1] = x[1] + w_n^(1*2) * x[3] * y[2] = x[0] + w_n^(2*2) * x[2] * y[3] = x[1] + w_n^(3*2) * x[3] * * y[4] = x[4] + w_n^(0*2) * x[6] * y[5] = x[5] + w_n^(1*2) * x[7] * y[6] = x[4] + w_n^(2*2) * x[6] * y[7] = x[5] + w_n^(3*2) * x[7] */ /* * stage2: 8つの入力データにバタフライ演算 (length=8の時) 1回 (nRepeat=1, nSubRepeat=8) * y[0] = x[0] + w_n^(0*1) * x[4] * y[1] = x[1] + w_n^(1*1) * x[5] * y[2] = x[2] + w_n^(2*1) * x[6] * y[3] = x[3] + w_n^(3*1) * x[7] * y[4] = x[0] + w_n^(4*1) * x[4] * y[5] = x[1] + w_n^(5*1) * x[5] * y[6] = x[2] + w_n^(6*1) * x[6] * y[7] = x[3] + w_n^(7*1) * x[7] */ /* * stageN: */ long nRepeat = Pow2(mNumStage - stageNr - 1); long nSubRepeat = mNumPoints / nRepeat; for (long i = 0; i < nRepeat; ++i) { long offsBase = i * nSubRepeat; bool allZero = true; for (long j = 0; j < nSubRepeat / 2; ++j) { long offs = offsBase + (j % (nSubRepeat / 2)); if (Double.Epsilon < x.At(offs).Magnitude()) { allZero = false; break; } if (Double.Epsilon < x.At(offs + nSubRepeat / 2).Magnitude()) { allZero = false; break; } } if (allZero) { for (long j = 0; j < nSubRepeat; ++j) { y.Set(j + offsBase, WWComplex.Zero()); } } else { for (long j = 0; j < nSubRepeat; ++j) { long offs = offsBase + (j % (nSubRepeat / 2)); var t = x.At(offs); var t2 = WWComplex.Mul(mWn.At(j * nRepeat), x.At(offs + nSubRepeat / 2)); var t3 = WWComplex.Add(t, t2); y.Set(j + offsBase, t3); } } } }
public static LargeArray <byte> ConvertTo32bitInt( int bitsPerSample, long numSamples, PcmData.ValueRepresentationType valueType, LargeArray <byte> data) { if (bitsPerSample == 32 && valueType == PcmData.ValueRepresentationType.SInt) { // すでに所望の形式。 return(data); } var data32 = new LargeArray <byte>(numSamples * 4); switch (valueType) { case PcmData.ValueRepresentationType.SInt: switch (bitsPerSample) { case 16: for (long i = 0; i < numSamples; ++i) { short v16 = (short)( (uint)data.At(i * 2) + ((uint)data.At(i * 2 + 1) << 8)); int v32 = v16; v32 *= 65536; data32.Set(i * 4 + 0, 0); data32.Set(i * 4 + 1, 0); data32.Set(i * 4 + 2, (byte)((v32 & 0x00ff0000) >> 16)); data32.Set(i * 4 + 3, (byte)((v32 & 0xff000000) >> 24)); } return(data32); case 24: for (long i = 0; i < numSamples; ++i) { int v32 = (int)( ((uint)data.At(i * 3 + 0) << 8) + ((uint)data.At(i * 3 + 1) << 16) + ((uint)data.At(i * 3 + 2) << 24)); data32.Set(i * 4 + 0, 0); data32.Set(i * 4 + 1, (byte)((v32 & 0x0000ff00) >> 8)); data32.Set(i * 4 + 2, (byte)((v32 & 0x00ff0000) >> 16)); data32.Set(i * 4 + 3, (byte)((v32 & 0xff000000) >> 24)); } return(data32); case 32: // 所望の形式。 System.Diagnostics.Debug.Assert(false); return(null); case 64: for (long i = 0; i < numSamples; ++i) { // 16.48 fixed point int v32 = (int)( ((uint)data.At(i * 8 + 2) << 0) + ((uint)data.At(i * 8 + 3) << 8) + ((uint)data.At(i * 8 + 4) << 16) + ((uint)data.At(i * 8 + 5) << 24)); data32.Set(i * 4 + 0, (byte)((v32 & 0x000000ff) >> 0)); data32.Set(i * 4 + 1, (byte)((v32 & 0x0000ff00) >> 8)); data32.Set(i * 4 + 2, (byte)((v32 & 0x00ff0000) >> 16)); data32.Set(i * 4 + 3, (byte)((v32 & 0xff000000) >> 24)); } return(data32); default: System.Diagnostics.Debug.Assert(false); return(null); } case PcmData.ValueRepresentationType.SFloat: for (long i = 0; i < numSamples; ++i) { double v; switch (bitsPerSample) { case 32: { var b4 = new byte[4]; data.CopyTo(i * 4, ref b4, 0, 4); v = BitConverter.ToSingle(b4, 0); } break; case 64: { var b8 = new byte[8]; data.CopyTo(i * 8, ref b8, 0, 8); v = BitConverter.ToDouble(b8, 0); } break; default: System.Diagnostics.Debug.Assert(false); return(null); } int v32 = 0; if (1.0f <= v) { v32 = Int32.MaxValue; } else if (v < -1.0f) { v32 = Int32.MinValue; } else { long vMax = -((long)Int32.MinValue); long v64 = (long)(v * vMax); if (Int32.MaxValue < v64) { v64 = Int32.MaxValue; } v32 = (int)v64; } data32.Set(i * 4 + 0, (byte)((v32 & 0x000000ff) >> 0)); data32.Set(i * 4 + 1, (byte)((v32 & 0x0000ff00) >> 8)); data32.Set(i * 4 + 2, (byte)((v32 & 0x00ff0000) >> 16)); data32.Set(i * 4 + 3, (byte)((v32 & 0xff000000) >> 24)); } return(data32); default: System.Diagnostics.Debug.Assert(false); return(null); } }
/// <summary> /// float配列のPCMデータを指定ビットデプスのbyte配列PCMデータにする。 /// </summary> public static LargeArray <byte> ConvertToByteArrayPCM(LargeArray <float> from, int bytesPerSample) { var w = new LargeArray <byte>(from.LongLength * bytesPerSample); long wIdx = 0; switch (bytesPerSample) { case 2: for (long pos = 0; pos < from.LongLength; ++pos) { // 値vを取得。 float vF = from.At(pos); if (vF < -1.0f) { vF = -1.0f; } if (32767.0f / 32768.0f < vF) { vF = 32767.0f / 32768.0f; } // 型変換。 short vS = (short)(vF * 32768.0f); //Console.WriteLine("{0:+00000;-00000}", vS); // 書き込み。 w.Set(wIdx++, (byte)(vS & 0xff)); w.Set(wIdx++, (byte)((vS >> 8) & 0xff)); } break; case 3: for (long pos = 0; pos < from.LongLength; ++pos) { // 値vを取得。 float vF = from.At(pos); if (vF < -1.0f) { vF = -1.0f; } if (8388607.0f / 8388608.0f < vF) { vF = 8388607.0f / 8388608.0f; } // 型変換。 int vI = (int)(vF * 2147483648.0f); // 書き込み。 w.Set(wIdx++, (byte)((vI >> 8) & 0xff)); w.Set(wIdx++, (byte)((vI >> 16) & 0xff)); w.Set(wIdx++, (byte)((vI >> 24) & 0xff)); } break; default: throw new ArgumentException(string.Format("Unexpected BytesPerSample {0}", bytesPerSample)); } return(w); }