dsh-fish-pet 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,16 +2,16 @@
2
2
  # 一次连续采集,检测到"说完"立刻上传,不再固定切片
3
3
  param(
4
4
  [int]$MaxMs = 10000,
5
- [int]$SilenceMs = 600,
5
+ [int]$SilenceMs = 900,
6
6
  [int]$Boost = 1,
7
- [int]$SilenceGate = 700
7
+ [int]$SilenceGate = 600
8
8
  )
9
9
 
10
- # 单实例:防止耳朵双开(双开会重复识别、重复回话)
11
- try {
12
- $earMutex = New-Object System.Threading.Mutex($false, 'Global\dsh-fish-ear-single')
13
- if (-not $earMutex.WaitOne(0)) { exit 0 }
14
- } catch { }
10
+ # 单实例:防止耳朵双开(双开会重复识别、重复回话)
11
+ try {
12
+ $earMutex = New-Object System.Threading.Mutex($false, 'Global\dsh-fish-ear-single')
13
+ if (-not $earMutex.WaitOne(0)) { exit 0 }
14
+ } catch { }
15
15
  $ErrorActionPreference = 'Stop'
16
16
  try { Add-Type -AssemblyName System.Net.Http -ErrorAction Stop } catch { }
17
17
  $DataDir = Join-Path $env:USERPROFILE '.dsh\fish-pet'
@@ -28,6 +28,7 @@ if (Test-Path $CfgPath) {
28
28
  $cj = Get-Content -LiteralPath $CfgPath -Raw | ConvertFrom-Json
29
29
  if ($cj.voice) {
30
30
  if ($cj.voice.silenceGate) { $SilenceGate = [int]$cj.voice.silenceGate }
31
+ if ($cj.voice.silenceMs) { $SilenceMs = [int]$cj.voice.silenceMs }
31
32
  if ($cj.voice.boost) { $Boost = [int]$cj.voice.boost }
32
33
  if ($cj.voice.engine) { $script:engine = [string]$cj.voice.engine }
33
34
  if ($cj.voice.localConf) { $script:localConf = [double]$cj.voice.localConf }
@@ -133,19 +134,25 @@ public class EarVad
133
134
  all.AddRange(buf);
134
135
  if (peakInt > RawPeak) RawPeak = peakInt;
135
136
  if (peakInt < gate) silent += packetMs; else silent = 0;
136
- if (silent >= silenceMs) break;
137
+ if (silent >= silenceMs && (total - silent) >= 1200) break;
137
138
  }
138
139
  }
139
140
  ac.Stop();
140
141
  if (!started || all.Count < rate / 2) return false;
141
142
 
142
- // 轻声增益:整段峰值偏低时放大,响的句子不动
143
- float maxA = 0; double sumSq = 0; int cntAll = 0;
144
- for (int i = 0; i < all.Count; i++) { float a = Math.Abs(all[i]); if (a > maxA) maxA = a; sumSq += all[i] * all[i]; cntAll++; }
145
- double peakInt2 = maxA * 32767.0;
146
- double gain2 = 1.0;
147
- if (peakInt2 > 150 && peakInt2 < 8000) { gain2 = Math.Min(12.0, 20000.0 / peakInt2); }
148
-
143
+ // 轻声增益:整段峰值偏低时放大,响的句子不动
144
+ float maxA = 0; double sumSq = 0; int cntAll = 0;
145
+ for (int i = 0; i < all.Count; i++) { float a = Math.Abs(all[i]); if (a > maxA) maxA = a; sumSq += all[i] * all[i]; cntAll++; }
146
+ double peakInt2 = maxA * 32767.0;
147
+ double gain2 = 1.0;
148
+ // 峰值归一化:把这段音频拉到约 70% 满幅,轻声也能被云端听清
149
+ if (peakInt2 > 200) {
150
+ gain2 = 22936.0 / peakInt2;
151
+ if (gain2 < 1.0) gain2 = 1.0;
152
+ if (gain2 > 35.0) gain2 = 35.0;
153
+ // 极轻的声音额外再提一点(>35 倍会明显失真,所以封顶)
154
+ }
155
+
149
156
  double step = (double)rate / 16000.0;
150
157
  int framesIn = all.Count / ch;
151
158
  int outCount = (int)(framesIn / step);
@@ -263,6 +270,12 @@ $script:lastFishCheck = Get-Date
263
270
  if (-not $key) { Start-Sleep -Seconds 2; continue }
264
271
 
265
272
  $ok = [EarVad]::Capture($MaxMs, $SilenceGate, $SilenceMs, $Boost, 2)
273
+
274
+ # 录音过程中鱼开口说话(或刚说完)→ 这段是自己的声音,丢掉
275
+ $talkF = Join-Path $DataDir 'asr.talking'
276
+ if (Test-Path $talkF) { continue }
277
+ $pauseF = Join-Path $DataDir 'asr.pause'
278
+ if ((Test-Path $pauseF) -and (((Get-Date) - (Get-Item $pauseF).LastWriteTime).TotalSeconds -lt 2.0)) { continue }
266
279
  if (-not $ok) {
267
280
  Start-Sleep -Milliseconds 80
268
281
  continue
@@ -290,23 +303,23 @@ $script:lastFishCheck = Get-Date
290
303
  }
291
304
  }
292
305
  if (-not $text -and $engine -ne 'local') {
293
- if ($null -eq $script:http) {
294
- $script:http = New-Object System.Net.Http.HttpClient
295
- $script:http.Timeout = [TimeSpan]::FromSeconds(30)
296
- $script:http.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue('Bearer', $key)
297
- }
298
- $json = ''
299
- try {
300
- $mp = New-Object System.Net.Http.MultipartFormDataContent
301
- $bytes = [IO.File]::ReadAllBytes($wav)
302
- $bc = New-Object System.Net.Http.ByteArrayContent((,$bytes))
303
- $bc.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('audio/wav')
304
- $mp.Add($bc, 'file', 'a.wav')
305
- $mp.Add((New-Object System.Net.Http.StringContent('Qwen/Qwen3-ASR-1.7B')), 'model')
306
- $mp.Add((New-Object System.Net.Http.StringContent('zh')), 'language')
307
- $resp = $script:http.PostAsync('https://api.siliconflow.cn/v1/audio/transcriptions', $mp).GetAwaiter().GetResult()
308
- $json = $resp.Content.ReadAsStringAsync().GetAwaiter().GetResult()
309
- try { $resp.Dispose(); $mp.Dispose() } catch { }
306
+ if ($null -eq $script:http) {
307
+ $script:http = New-Object System.Net.Http.HttpClient
308
+ $script:http.Timeout = [TimeSpan]::FromSeconds(30)
309
+ $script:http.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue('Bearer', $key)
310
+ }
311
+ $json = ''
312
+ try {
313
+ $mp = New-Object System.Net.Http.MultipartFormDataContent
314
+ $bytes = [IO.File]::ReadAllBytes($wav)
315
+ $bc = New-Object System.Net.Http.ByteArrayContent((,$bytes))
316
+ $bc.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('audio/wav')
317
+ $mp.Add($bc, 'file', 'a.wav')
318
+ $mp.Add((New-Object System.Net.Http.StringContent('Qwen/Qwen3-ASR-1.7B')), 'model')
319
+ # 不指定 language,交给模型自动判断中英文(指定 zh 时英文会被转成奇怪文字)
320
+ $resp = $script:http.PostAsync('https://api.siliconflow.cn/v1/audio/transcriptions', $mp).GetAwaiter().GetResult()
321
+ $json = $resp.Content.ReadAsStringAsync().GetAwaiter().GetResult()
322
+ try { $resp.Dispose(); $mp.Dispose() } catch { }
310
323
  } catch { try { Add-Content -LiteralPath $ErrFile -Value ((Get-Date -Format 'HH:mm:ss') + ' HTTP ' + $_.Exception.Message) -Encoding UTF8 } catch { } }
311
324
  if ($json) {
312
325
  try { $obj = $json | ConvertFrom-Json; $text = [string]$obj.text; $via = 'cloud' } catch { }
@@ -315,9 +328,14 @@ $script:lastFishCheck = Get-Date
315
328
  Remove-Item -LiteralPath $wav -Force -ErrorAction SilentlyContinue
316
329
  Add-Content -LiteralPath $LevelFile -Value ((Get-Date -Format 'HH:mm:ss') + "`tengine=" + $via + "`ttext=" + $text) -Encoding UTF8
317
330
  if ($text) { $text = $text.Trim() }
318
- if (-not $text -and [EarVad]::RawPeak -ge 800) {
319
- Add-Content -LiteralPath $OutFile -Value ((Get-Date -Format 'HH:mm:ss') + "`t?`t(没听清)") -Encoding UTF8
320
- }
331
+ # 过滤乱语种:泰文/日文假名/西里尔/阿拉伯等说明识别跑偏了,当没听清
332
+ if ($text -and ($text -match '[\u0e00-\u0eff\u3040-\u30ff\u0400-\u04ff\u0600-\u06ff]')) {
333
+ Add-Content -LiteralPath $OutFile -Value ((Get-Date -Format 'HH:mm:ss') + "`t?`t(识别跑偏:" + $text + ")") -Encoding UTF8
334
+ $text = ''
335
+ }
336
+ if (-not $text -and [EarVad]::RawPeak -ge 800) {
337
+ Add-Content -LiteralPath $OutFile -Value ((Get-Date -Format 'HH:mm:ss') + "`t?`t(没听清)") -Encoding UTF8
338
+ }
321
339
  if ($text -and ($text -match '[\u4e00-\u9fa5]' -or $text.Length -ge 4)) {
322
340
  Add-Content -LiteralPath $OutFile -Value ((Get-Date -Format 'HH:mm:ss') + "`t1`t" + $text) -Encoding UTF8
323
341
  }