param([Parameter(Mandatory)][string]$ReferencePath, [Parameter(Mandatory)][string]$CapturedPath, [Parameter(Mandatory)][string]$OutputPath) $ErrorActionPreference='Stop' Add-Type -TypeDefinition @' using System; using System.IO; public static class AndroidPcmComparison { static short[] Read(string path) { using(var r=new BinaryReader(File.OpenRead(path))) { if(new string(r.ReadChars(4))!="RIFF") throw new Exception("RIFF WAV required"); r.ReadInt32(); if(new string(r.ReadChars(4))!="WAVE") throw new Exception("WAVE required"); bool format=false; int rate=0; while(r.BaseStream.Position+8<=r.BaseStream.Length) { string id=new string(r.ReadChars(4)); int size=r.ReadInt32(); long end=r.BaseStream.Position+size; if(id=="fmt ") { int encoding=r.ReadInt16(),channels=r.ReadInt16(); rate=r.ReadInt32(); r.ReadInt32(); r.ReadInt16(); format=encoding==1 && channels==1 && r.ReadInt16()==16 && rate>0; } if(id=="data") { if(!format) throw new Exception("Mono PCM16 required"); var data=new short[size/2]; for(int i=0;ibestEnergy) { bestEnergy=energy; anchor=offset; } } if(bestEnergy<=0 || output.Length0 ? dot/Math.Sqrt(sourceAnchorEnergy*energy) : 0; if(correlation>best) { best=correlation; bestLag=lag; } } double fullDot=0,sourceEnergy=0,outputEnergy=0; int nonzero=0; for(int j=0;j32) nonzero++; } double fullCorrelation=fullDot/Math.Sqrt(sourceEnergy*outputEnergy); return new[]{fullCorrelation,bestLag/24000.0,source.Length/24000.0,output.Length/24000.0,Math.Sqrt(outputEnergy/source.Length),fullDot/sourceEnergy,(double)nonzero}; } } '@ $values=[AndroidPcmComparison]::Compare((Resolve-Path -LiteralPath $ReferencePath).Path,(Resolve-Path -LiteralPath $CapturedPath).Path) $passed=$values[0] -ge 0.95 -and $values[4] -gt 32 -and $values[6] -gt 100 $record=[ordered]@{ reference=$ReferencePath; referenceSha256=(Get-FileHash -LiteralPath $ReferencePath -Algorithm SHA256).Hash; captured=$CapturedPath; capturedSha256=(Get-FileHash -LiteralPath $CapturedPath -Algorithm SHA256).Hash; normalizedCorrelation=$values[0]; onsetSeconds=$values[1]; referenceDurationSeconds=$values[2]; capturedDurationSeconds=$values[3]; rmsPcm=$values[4]; gain=$values[5]; nonSilentSamples=$values[6]; thresholdCorrelation=0.95; passed=$passed; auditoryReview=$false } $record | ConvertTo-Json | Set-Content -LiteralPath $OutputPath -Encoding utf8 Write-Host "실제 PCM 대조: 상관 $($values[0]), 비무음 샘플 $($values[6]), 통과 $passed" if (!$passed) { exit 1 }