fixer.go 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  1. package sub_timeline_fixer
  2. //
  3. //import (
  4. // "errors"
  5. // "fmt"
  6. // "github.com/ChineseSubFinder/ChineseSubFinder/pkg/log_helper"
  7. // "github.com/ChineseSubFinder/ChineseSubFinder/pkg/my_util"
  8. // "github.com/ChineseSubFinder/ChineseSubFinder/pkg/sub_helper"
  9. // "github.com/ChineseSubFinder/ChineseSubFinder/pkg/vad"
  10. // "github.com/ChineseSubFinder/ChineseSubFinder/internal/types/sub_timeline_fiexer"
  11. // "github.com/ChineseSubFinder/ChineseSubFinder/internal/types/subparser"
  12. // "github.com/emirpasic/gods/maps/treemap"
  13. // "github.com/emirpasic/gods/utils"
  14. // "github.com/go-echarts/go-echarts/v2/opts"
  15. // "github.com/grd/stat"
  16. // "github.com/james-bowman/nlp/measures/pairwise"
  17. // "github.com/mndrix/tukey"
  18. // "github.com/panjf2000/ants/v2"
  19. // "golang.org/x/net/context"
  20. // "gonum.org/v1/gonum/mat"
  21. // "math"
  22. // "os"
  23. // "sort"
  24. // "strings"
  25. // "sync"
  26. // "time"
  27. //)
  28. //
  29. //type SubTimelineFixer struct {
  30. // log *logrus.Logger
  31. // FixerConfig sub_timeline_fiexer.SubTimelineFixerConfig
  32. //}
  33. //
  34. //func NewSubTimelineFixer(log *logrus.Logger, fixerConfig sub_timeline_fiexer.SubTimelineFixerConfig) *SubTimelineFixer {
  35. // return &SubTimelineFixer{
  36. // log: log,
  37. // FixerConfig: fixerConfig,
  38. // }
  39. //}
  40. //
  41. //// StopWordCounter 停止词统计
  42. //func (s *SubTimelineFixer) StopWordCounter(inString string, per int) []string {
  43. // statisticTimes := make(map[string]int)
  44. // wordsLength := strings.Fields(inString)
  45. //
  46. // for counts, word := range wordsLength {
  47. // // 判断key是否存在,这个word是字符串,这个counts是统计的word的次数。
  48. // word, ok := statisticTimes[word]
  49. // if ok {
  50. // word = word
  51. // statisticTimes[wordsLength[counts]] = statisticTimes[wordsLength[counts]] + 1
  52. // } else {
  53. // statisticTimes[wordsLength[counts]] = 1
  54. // }
  55. // }
  56. //
  57. // stopWords := make([]string, 0)
  58. // mapByValue := sortMapByValue(statisticTimes)
  59. //
  60. // breakIndex := len(mapByValue) * per / 100
  61. // for index, wordInfo := range mapByValue {
  62. // if index > breakIndex {
  63. // break
  64. // }
  65. // stopWords = append(stopWords, wordInfo.Name)
  66. // }
  67. //
  68. // return stopWords
  69. //}
  70. //
  71. //// FixSubTimelineOneOffsetTime 校正整个字幕文件的时间轴,适用于一个偏移值的情况
  72. //func (s *SubTimelineFixer) FixSubTimelineOneOffsetTime(infoSrc *subparser.FileInfo, inOffsetTime float64, desSaveSubFileFullPath string) (string, error) {
  73. //
  74. // /*
  75. // 从解析的实例中,正常来说是可以匹配出所有的 Dialogue 对话的 Start 和 End time 的信息
  76. // 然后找到对应的字幕的文件,进行文件内容的替换来做时间轴的校正
  77. // */
  78. // // 偏移时间
  79. // offsetTime := time.Duration(inOffsetTime*1000) * time.Millisecond
  80. // fixContent := infoSrc.Content
  81. // /*
  82. // 这里进行时间转字符串的时候有一点比较特殊
  83. // 正常来说输出的格式是类似 15:04:05.00
  84. // 那么有个问题,字幕的时间格式是 0:00:12.00, 小时,是个数,除非有跨度到 20 小时的视频,不然小时就应该是个数
  85. // 这就需要一个额外的函数去处理这些情况
  86. // */
  87. // timeFormat := infoSrc.GetTimeFormat()
  88. // for _, srcOneDialogue := range infoSrc.Dialogues {
  89. //
  90. // timeStart, err := my_util.ParseTime(srcOneDialogue.StartTime)
  91. // if err != nil {
  92. // return "", err
  93. // }
  94. // timeEnd, err := my_util.ParseTime(srcOneDialogue.EndTime)
  95. // if err != nil {
  96. // return "", err
  97. // }
  98. //
  99. // fixTimeStart := timeStart.Add(offsetTime)
  100. // fixTimeEnd := timeEnd.Add(offsetTime)
  101. //
  102. // fixContent = strings.ReplaceAll(fixContent, srcOneDialogue.StartTime, my_util.Time2SubTimeString(fixTimeStart, timeFormat))
  103. // fixContent = strings.ReplaceAll(fixContent, srcOneDialogue.EndTime, my_util.Time2SubTimeString(fixTimeEnd, timeFormat))
  104. // }
  105. //
  106. // dstFile, err := os.Create(desSaveSubFileFullPath)
  107. // if err != nil {
  108. // return "", err
  109. // }
  110. // defer func() {
  111. // _ = dstFile.Close()
  112. // }()
  113. // _, err = dstFile.WriteString(fixContent)
  114. // if err != nil {
  115. // return "", err
  116. // }
  117. // return fixContent, nil
  118. //}
  119. //
  120. //// FixSubTimelineByFixResults V2 专用的时间校正函数
  121. //func (s SubTimelineFixer) FixSubTimelineByFixResults(infoSrc *subparser.FileInfo, srcUnitNew *sub_helper.SubUnit, fixedResults []FixResult, desSaveSubFileFullPath string) (string, error) {
  122. //
  123. // startTime := srcUnitNew.GetStartTime(true)
  124. // startTimeBaseDouble := my_util.Time2SecondNumber(startTime)
  125. // /*
  126. // 这里拿到的 fixedResults ,是进行过 V2_FrontAndEndPerSrc 头尾去除
  127. // 那么调整目标字幕的时候,需要考虑截取掉的部分也要算进去
  128. // */
  129. // /*
  130. // 从解析的实例中,正常来说是可以匹配出所有的 Dialogue 对话的 Start 和 End time 的信息
  131. // 然后找到对应的字幕的文件,进行文件内容的替换来做时间轴的校正
  132. // */
  133. // fixContent := infoSrc.Content
  134. // /*
  135. // 这里进行时间转字符串的时候有一点比较特殊
  136. // 正常来说输出的格式是类似 15:04:05.00
  137. // 那么有个问题,字幕的时间格式是 0:00:12.00, 小时,是个数,除非有跨度到 20 小时的视频,不然小时就应该是个数
  138. // 这就需要一个额外的函数去处理这些情况
  139. // */
  140. // timeFormat := infoSrc.GetTimeFormat()
  141. // cacheIndex := 0
  142. // /*
  143. // 这里的理想情况是 Dialogues,每一句话都是递增的对白时间
  144. // 但是实际情况可能是,前面几个对白是特效、音乐的备注,那么他们的跨度可以很大
  145. // 然后才到正常的对话对白,这样就出现不是递增的时间对白情况
  146. // 那么就需要对 Dialogues 进行排序,然后再进行处理
  147. // */
  148. // sort.Sort(subparser.OneDialogueByStartTime(infoSrc.Dialogues))
  149. //
  150. // for index, srcOneDialogue := range infoSrc.Dialogues {
  151. //
  152. // timeStart, err := my_util.ParseTime(srcOneDialogue.StartTime)
  153. // if err != nil {
  154. // return "", err
  155. // }
  156. // timeEnd, err := my_util.ParseTime(srcOneDialogue.EndTime)
  157. // if err != nil {
  158. // return "", err
  159. // }
  160. //
  161. // inOffsetTime := 0.0
  162. // orgStartTimeDouble := my_util.Time2SecondNumber(timeStart)
  163. // for cacheIndex < len(fixedResults) {
  164. //
  165. // inRange, nowOffsetTime := fixedResults[cacheIndex].InRange(startTimeBaseDouble, orgStartTimeDouble)
  166. // if inRange == false {
  167. // // 大于当前的范围,递增一个区间进行再次的判断
  168. // // 但是需要确定的是,递增出来的这个区间的 Index 是有效的,如果是无效的,那么就使用最后一个区间的偏移时间
  169. // cacheIndex++
  170. // continue
  171. // } else {
  172. // inOffsetTime = nowOffsetTime
  173. // break
  174. // }
  175. // }
  176. // if cacheIndex >= len(fixedResults) {
  177. // // 下一个区间的 Index 已经越界了,那么就使用最后一个区间的偏移
  178. // inOffsetTime = fixedResults[len(fixedResults)-1].NewMean
  179. // }
  180. // // 偏移时间
  181. // println(index, inOffsetTime)
  182. // offsetTime := time.Duration(inOffsetTime*1000) * time.Millisecond
  183. // fixTimeStart := timeStart.Add(offsetTime)
  184. // fixTimeEnd := timeEnd.Add(offsetTime)
  185. //
  186. // fixContent = strings.ReplaceAll(fixContent, srcOneDialogue.StartTime, "Index:"+fmt.Sprintf("%d-", index)+my_util.Time2SubTimeString(fixTimeStart, timeFormat))
  187. // fixContent = strings.ReplaceAll(fixContent, srcOneDialogue.EndTime, my_util.Time2SubTimeString(fixTimeEnd, timeFormat))
  188. // }
  189. //
  190. // dstFile, err := os.Create(desSaveSubFileFullPath)
  191. // if err != nil {
  192. // return "", err
  193. // }
  194. // defer func() {
  195. // _ = dstFile.Close()
  196. // }()
  197. // _, err = dstFile.WriteString(fixContent)
  198. // if err != nil {
  199. // return "", err
  200. // }
  201. // return fixContent, nil
  202. //}
  203. //
  204. ///*
  205. // 对于 V1 版本的字幕时间轴校正来说,是有特殊的前置要求的
  206. // 1. 视频要有英文字幕
  207. // 2. 外置的字幕必须是中文的双语字幕(简英、繁英)
  208. //*/
  209. //// GetOffsetTimeV1 暂时只支持英文的基准字幕,源字幕必须是双语中英字幕
  210. //func (s *SubTimelineFixer) GetOffsetTimeV1(infoBase, infoSrc *subparser.FileInfo, staticLineFileSavePath string, debugInfoFileSavePath string) (bool, float64, float64, error) {
  211. //
  212. // var debugInfos = make([]string, 0)
  213. // // 构建基准语料库,目前阶段只需要考虑是 En 的就行了
  214. // var baseCorpus = make([]string, 0)
  215. // var baseDialogueFilterMap = make(map[int]int, 0)
  216. // /*
  217. // 这里原来的写法是所有的 base 的都放进去匹配,这样会带来一些不必要的对白
  218. // 需要剔除空白。那么就需要建立一个转换的字典
  219. // */
  220. // for index, oneDialogueEx := range infoBase.DialoguesFilterEx {
  221. // if oneDialogueEx.EnLine == "" {
  222. // continue
  223. // }
  224. // baseCorpus = append(baseCorpus, oneDialogueEx.EnLine)
  225. // baseDialogueFilterMap[len(baseCorpus)-1] = index
  226. // }
  227. // // 初始化
  228. // pipLine, tfidf, err := NewTFIDF(baseCorpus)
  229. // if err != nil {
  230. // return false, 0, 0, err
  231. // }
  232. //
  233. // /*
  234. // 确认两个字幕间的偏移,暂定的方案是两边都连续匹配上 5 个索引,再抽取一个对话的时间进行修正计算
  235. // */
  236. // maxCompareDialogue := s.FixerConfig.V1_MaxCompareDialogue
  237. // // 基线的长度
  238. // _, docsLength := tfidf.Dims()
  239. // var matchIndexList = make([]MatchIndex, 0)
  240. // sc := NewSubCompare(maxCompareDialogue)
  241. // // 开始比较相似度,默认认为是 Ch_en 就行了
  242. // for srcIndex := 0; srcIndex < len(infoSrc.DialoguesFilterEx); {
  243. //
  244. // srcOneDialogueEx := infoSrc.DialoguesFilterEx[srcIndex]
  245. // // 这里只考虑 英文 的语言
  246. // if srcOneDialogueEx.EnLine == "" {
  247. // srcIndex++
  248. // continue
  249. // }
  250. // // run the query through the same pipeline that was fitted to the corpus and
  251. // // to project it into the same dimensional space
  252. // queryVector, err := pipLine.Transform(srcOneDialogueEx.EnLine)
  253. // if err != nil {
  254. // return false, 0, 0, err
  255. // }
  256. // // iterate over document feature vectors (columns) in the LSI matrix and compare
  257. // // with the query vector for similarity. Similarity is determined by the difference
  258. // // between the angles of the vectors known as the cosine similarity
  259. // highestSimilarity := -1.0
  260. // // 匹配上的基准的索引
  261. // var baseIndex int
  262. // // 这里理论上需要把所有的基线遍历一次,但是,一般来说,两个字幕不可能差距在 50 行
  263. // // 这样的好处是有助于提高搜索的性能
  264. // // 那么就以当前的 src 的位置,向前、向后各 50 来遍历
  265. // nowMaxScanLength := srcIndex + 50
  266. // nowMinScanLength := srcIndex - 50
  267. // if nowMinScanLength < 0 {
  268. // nowMinScanLength = 0
  269. // }
  270. // if nowMaxScanLength > docsLength {
  271. // nowMaxScanLength = docsLength
  272. // }
  273. // for i := nowMinScanLength; i < nowMaxScanLength; i++ {
  274. // similarity := pairwise.CosineSimilarity(queryVector.(mat.ColViewer).ColView(0), tfidf.(mat.ColViewer).ColView(i))
  275. // if similarity > highestSimilarity {
  276. // baseIndex = i
  277. // highestSimilarity = similarity
  278. // }
  279. // }
  280. //
  281. // startBaseIndex, startSrcIndex := sc.GetStartIndex()
  282. // if sc.Add(baseIndex, srcIndex) == false {
  283. // sc.Clear()
  284. // srcIndex = startSrcIndex + 1
  285. // continue
  286. // //sc.Add(baseIndex, srcIndex)
  287. // }
  288. // if sc.Check() == false {
  289. // srcIndex++
  290. // continue
  291. // } else {
  292. // sc.Clear()
  293. // }
  294. //
  295. // matchIndexList = append(matchIndexList, MatchIndex{
  296. // BaseNowIndex: startBaseIndex,
  297. // //BaseNowIndex: baseDialogueFilterMap[startBaseIndex],
  298. // SrcNowIndex: startSrcIndex,
  299. // Similarity: highestSimilarity,
  300. // })
  301. //
  302. // //println(fmt.Sprintf("Similarity: %f Base[%d] %s-%s '%s' <--> Src[%d] %s-%s '%s'",
  303. // // highestSimilarity,
  304. // // baseIndex, infoBase.DialoguesFilterEx[baseIndex].relativelyStartTime, infoBase.DialoguesFilterEx[baseIndex].relativelyEndTime, baseCorpus[baseIndex],
  305. // // srcIndex, srcOneDialogueEx.relativelyStartTime, srcOneDialogueEx.relativelyEndTime, srcOneDialogueEx.EnLine))
  306. //
  307. // srcIndex++
  308. // }
  309. //
  310. // var startDiffTimeLineData = make([]opts.LineData, 0)
  311. // var endDiffTimeLineData = make([]opts.LineData, 0)
  312. // var tmpStartDiffTime = make([]float64, 0)
  313. // var tmpEndDiffTime = make([]float64, 0)
  314. // var startDiffTimeList = make(stat.Float64Slice, 0)
  315. // var endDiffTimeList = make(stat.Float64Slice, 0)
  316. // var xAxis = make([]string, 0)
  317. // // 上面找出了连续匹配 maxCompareDialogue:N 次的字幕语句块
  318. // // 求出平均时间偏移
  319. // for mIndex, matchIndexItem := range matchIndexList {
  320. //
  321. // for i := 0; i < maxCompareDialogue; i++ {
  322. // // 这里会统计连续的这 5 句话的时间差
  323. // //tmpBaseIndex := matchIndexItem.BaseNowIndex + i
  324. // tmpBaseIndex := baseDialogueFilterMap[matchIndexItem.BaseNowIndex+i]
  325. // tmpSrcIndex := matchIndexItem.SrcNowIndex + i
  326. //
  327. // baseTimeStart, err := my_util.ParseTime(infoBase.DialoguesFilterEx[tmpBaseIndex].StartTime)
  328. // if err != nil {
  329. // return false, 0, 0, err
  330. // }
  331. // baseTimeEnd, err := my_util.ParseTime(infoBase.DialoguesFilterEx[tmpBaseIndex].EndTime)
  332. // if err != nil {
  333. // return false, 0, 0, err
  334. // }
  335. // srtTimeStart, err := my_util.ParseTime(infoSrc.DialoguesFilterEx[tmpSrcIndex].StartTime)
  336. // if err != nil {
  337. // return false, 0, 0, err
  338. // }
  339. // srtTimeEnd, err := my_util.ParseTime(infoSrc.DialoguesFilterEx[tmpSrcIndex].EndTime)
  340. // if err != nil {
  341. // return false, 0, 0, err
  342. // }
  343. //
  344. // TimeDiffStart := baseTimeStart.Sub(srtTimeStart)
  345. // TimeDiffEnd := baseTimeEnd.Sub(srtTimeEnd)
  346. //
  347. // startDiffTimeLineData = append(startDiffTimeLineData, opts.LineData{Value: TimeDiffStart.Seconds()})
  348. // endDiffTimeLineData = append(endDiffTimeLineData, opts.LineData{Value: TimeDiffEnd.Seconds()})
  349. //
  350. // tmpStartDiffTime = append(tmpStartDiffTime, TimeDiffStart.Seconds())
  351. // tmpEndDiffTime = append(tmpEndDiffTime, TimeDiffEnd.Seconds())
  352. //
  353. // startDiffTimeList = append(startDiffTimeList, TimeDiffStart.Seconds())
  354. // endDiffTimeList = append(endDiffTimeList, TimeDiffEnd.Seconds())
  355. //
  356. // xAxis = append(xAxis, fmt.Sprintf("%d_%d", mIndex, i))
  357. //
  358. // debugInfos = append(debugInfos, "bs "+infoBase.DialoguesFilterEx[tmpBaseIndex].StartTime+" <-> "+infoBase.DialoguesFilterEx[tmpBaseIndex].EndTime)
  359. // debugInfos = append(debugInfos, "sc "+infoSrc.DialoguesFilterEx[tmpSrcIndex].StartTime+" <-> "+infoSrc.DialoguesFilterEx[tmpSrcIndex].EndTime)
  360. // debugInfos = append(debugInfos, "StartDiffTime: "+fmt.Sprintf("%f", TimeDiffStart.Seconds()))
  361. // //println(fmt.Sprintf("Diff Start-End: %s - %s Base[%d] %s-%s '%s' <--> Src[%d] %s-%s '%s'",
  362. // // TimeDiffStart, TimeDiffEnd,
  363. // // tmpBaseIndex, infoBase.DialoguesFilterEx[tmpBaseIndex].relativelyStartTime, infoBase.DialoguesFilterEx[tmpBaseIndex].relativelyEndTime, infoBase.DialoguesFilterEx[tmpBaseIndex].EnLine,
  364. // // tmpSrcIndex, infoSrc.DialoguesFilterEx[tmpSrcIndex].relativelyStartTime, infoSrc.DialoguesFilterEx[tmpSrcIndex].relativelyEndTime, infoSrc.DialoguesFilterEx[tmpSrcIndex].EnLine))
  365. // }
  366. // debugInfos = append(debugInfos, "---------------------------------------------")
  367. // //println("---------------------------------------------")
  368. // }
  369. //
  370. // oldMean := stat.Mean(startDiffTimeList)
  371. // oldSd := stat.Sd(startDiffTimeList)
  372. // newMean := -1.0
  373. // newSd := -1.0
  374. // per := 1.0
  375. //
  376. // // 如果 SD 较大的时候才需要剔除
  377. // if oldSd > 0.1 {
  378. // var outliersMap = make(map[float64]int, 0)
  379. // outliers, _, _ := tukey.Outliers(0.3, tmpStartDiffTime)
  380. // for _, outlier := range outliers {
  381. // outliersMap[outlier] = 0
  382. // }
  383. // var newStartDiffTimeList = make([]float64, 0)
  384. // for _, f := range tmpStartDiffTime {
  385. //
  386. // _, ok := outliersMap[f]
  387. // if ok == true {
  388. // continue
  389. // }
  390. //
  391. // newStartDiffTimeList = append(newStartDiffTimeList, f)
  392. // }
  393. //
  394. // orgLen := startDiffTimeList.Len()
  395. // startDiffTimeList = make(stat.Float64Slice, 0)
  396. // for _, f := range newStartDiffTimeList {
  397. // startDiffTimeList = append(startDiffTimeList, f)
  398. // }
  399. // newLen := startDiffTimeList.Len()
  400. //
  401. // per = float64(newLen) / float64(orgLen)
  402. //
  403. // newMean = stat.Mean(startDiffTimeList)
  404. // newSd = stat.Sd(startDiffTimeList)
  405. // }
  406. //
  407. // if newMean == -1.0 {
  408. // newMean = oldMean
  409. // }
  410. // if newSd == -1.0 {
  411. // newSd = oldSd
  412. // }
  413. //
  414. // // 不为空的时候,生成调试文件
  415. // if staticLineFileSavePath != "" {
  416. // //staticLineFileSavePath = "bar.html"
  417. // err = SaveStaticLineV1(staticLineFileSavePath, infoBase.Name, infoSrc.Name,
  418. // per, oldMean, oldSd, newMean, newSd, xAxis,
  419. // startDiffTimeLineData, endDiffTimeLineData)
  420. // if err != nil {
  421. // return false, 0, 0, err
  422. // }
  423. // }
  424. //
  425. // // 跳过的逻辑是 mean 是 0 ,那么现在如果判断有问题,缓存的调试文件继续生成,然后强制返回 0 来跳过后续的逻辑
  426. // // 这里需要考虑,找到的连续 5 句话匹配的有多少句,占比整体所有的 Dialogue 是多少,太低也需要跳过
  427. // matchIndexLineCount := len(matchIndexList) * maxCompareDialogue
  428. // //perMatch := float64(matchIndexLineCount) / float64(len(infoSrc.DialoguesFilterEx))
  429. // perMatch := float64(matchIndexLineCount) / float64(len(baseCorpus))
  430. // if perMatch < s.FixerConfig.V1_MinMatchedPercent {
  431. // tmpContent := infoSrc.Name + fmt.Sprintf(" Sequence match %d dialogues (< %f%%), Skip,", s.FixerConfig.V1_MaxCompareDialogue, s.FixerConfig.V1_MinMatchedPercent*100) + fmt.Sprintf(" %f%% ", perMatch*100)
  432. //
  433. // debugInfos = append(debugInfos, tmpContent)
  434. //
  435. // s.log.Infoln(tmpContent)
  436. // } else {
  437. // tmpContent := infoSrc.Name + fmt.Sprintf(" Sequence match %d dialogues,", s.FixerConfig.V1_MaxCompareDialogue) + fmt.Sprintf(" %f%% ", perMatch*100)
  438. //
  439. // debugInfos = append(debugInfos, tmpContent)
  440. //
  441. // s.log.Infoln(tmpContent)
  442. // }
  443. //
  444. // // 输出调试的匹配时间轴信息的列表
  445. // if debugInfoFileSavePath != "" {
  446. // err = my_util.WriteStrings2File(debugInfoFileSavePath, debugInfos)
  447. // if err != nil {
  448. // return false, 0, 0, err
  449. // }
  450. // }
  451. // // 虽然有条件判断是认为有问题的,但是返回值还是要填写除去的
  452. // if perMatch < s.FixerConfig.V1_MinMatchedPercent {
  453. // return false, newMean, newSd, nil
  454. // }
  455. //
  456. // return true, newMean, newSd, nil
  457. //}
  458. //
  459. //// GetOffsetTimeV2 使用内置的字幕校正外置的字幕时间轴
  460. //func (s *SubTimelineFixer) GetOffsetTimeV2(baseUnit, srcUnit *sub_helper.SubUnit, audioVadList []vad.VADInfo) (bool, []FixResult, error) {
  461. //
  462. // // -------------------------------------------------
  463. // /*
  464. // 开始针对对白单元进行匹配
  465. // 下面的逻辑需要参考 FFT识别流程.jpg 这个图示来理解
  466. // 实际实现的时候,会在上述 srcUnit 上,做一个滑动窗口来做匹配,80% 是窗口,20% 用于移动
  467. // 步长固定在 10 步
  468. // */
  469. // audioFloatList := vad.GetFloatSlice(audioVadList)
  470. //
  471. // srcVADLen := len(srcUnit.VADList)
  472. // // 滑动窗口的长度
  473. // srcWindowLen := int(float64(srcVADLen) * s.FixerConfig.V2_WindowMatchPer)
  474. // // 划分为 4 个区域,每一个部分的长度
  475. // const parts = 10
  476. // perPartLen := srcVADLen / parts
  477. // matchedInfos := make([]MatchInfo, 0)
  478. //
  479. // subVADBlockInfos := make([]SubVADBlockInfo, 0)
  480. // for i := 0; i < parts; i++ {
  481. //
  482. // // 滑动窗体的起始 Index
  483. // srcSlideStartIndex := i * perPartLen
  484. // // 滑动的距离
  485. // srcSlideLen := perPartLen
  486. // // 一步的长度
  487. // oneStep := perPartLen / s.FixerConfig.V2_CompareParts
  488. // if srcSlideLen <= 0 {
  489. // srcSlideLen = 1
  490. // }
  491. // if oneStep <= 0 {
  492. // oneStep = 1
  493. // }
  494. // // -------------------------------------------------
  495. // windowInfo := WindowInfo{
  496. // BaseAudioFloatList: audioFloatList,
  497. // BaseUnit: baseUnit,
  498. // SrcUnit: srcUnit,
  499. // MatchedTimes: 0,
  500. // SrcWindowLen: srcWindowLen,
  501. // SrcSlideStartIndex: srcSlideStartIndex,
  502. // SrcSlideLen: srcSlideLen,
  503. // OneStep: oneStep,
  504. // }
  505. // subVADBlockInfos = append(subVADBlockInfos, SubVADBlockInfo{
  506. // Index: i,
  507. // StartIndex: srcSlideStartIndex,
  508. // EndIndex: srcSlideStartIndex + srcSlideLen,
  509. // })
  510. // // 实际 FFT 的匹配逻辑函数
  511. // // 时间轴差值数组
  512. // matchInfo, err := s.slidingWindowProcessorV2(&windowInfo)
  513. // if err != nil {
  514. // return false, nil, err
  515. // }
  516. //
  517. // matchedInfos = append(matchedInfos, *matchInfo)
  518. // }
  519. //
  520. // fixedResults := make([]FixResult, 0)
  521. // sdLessCount := 0
  522. // // 这里的是 matchedInfos 是顺序的
  523. // for index, matchInfo := range matchedInfos {
  524. //
  525. // s.log.Infoln(index, "------------------------------------")
  526. // outCorrelationFixResult := s.calcMeanAndSDV2(matchInfo.StartDiffTimeListEx, matchInfo.StartDiffTimeList)
  527. // s.log.Infoln(fmt.Sprintf("FFTAligner Old Mean: %v SD: %f Per: %v", outCorrelationFixResult.OldMean, outCorrelationFixResult.OldSD, outCorrelationFixResult.Per))
  528. // s.log.Infoln(fmt.Sprintf("FFTAligner New Mean: %v SD: %f Per: %v", outCorrelationFixResult.NewMean, outCorrelationFixResult.NewSD, outCorrelationFixResult.Per))
  529. //
  530. // value, indexMax := matchInfo.StartDiffTimeMap.Max()
  531. // s.log.Infoln("FFTAligner Max score:", fmt.Sprintf("%v", value.(float64)), "Time:", fmt.Sprintf("%v", matchInfo.StartDiffTimeList[indexMax.(int)]))
  532. //
  533. // outCorrelationFixResult.StartVADIndex = index * perPartLen
  534. // outCorrelationFixResult.EndVADIndex = index*perPartLen + perPartLen
  535. // fixedResults = append(fixedResults, outCorrelationFixResult)
  536. //
  537. // if outCorrelationFixResult.NewSD < 0.1 {
  538. // sdLessCount++
  539. // }
  540. // }
  541. //
  542. // // 如果 0.1 sd 以下的占比低于 70% 那么就认为字幕匹配失败
  543. // perLess := float64(sdLessCount) / float64(len(matchedInfos))
  544. // if perLess < 0.7 {
  545. // return false, nil, nil
  546. // }
  547. //
  548. // // matchedInfos 与 fixedResults 是对等的关系,fixedResults 中是计算过 Mean 的值,而 matchedInfos 有原始的值
  549. // for i, info := range matchedInfos {
  550. // for j := 0; j < len(info.IndexMatchWindowInfoMap); j++ {
  551. //
  552. // value, bFound := info.IndexMatchWindowInfoMap[j]
  553. // if bFound == false {
  554. // continue
  555. // }
  556. //
  557. // fixedResults[i].MatchWindowInfos = append(fixedResults[i].MatchWindowInfos, value)
  558. // }
  559. // }
  560. // /*
  561. // 如果 outCorrelationFixResult 的 SD > 0.1,那么大概率这个时间轴的值匹配的有问题,需要向左或者向右找一个值进行继承
  562. // -4 0.001
  563. // -4 0.001
  564. // -4 0.001
  565. // -200 0.1
  566. // -4 0.001
  567. // 比如这种情况,那么就需要向左找到 -4 去继承。
  568. // 具体的实现:
  569. // 找到一个 SD > 0.1 的项目,那么就需要从左边和右边同时对比
  570. // 首先是他们的差值要在 0.3s (绝对值)以内,优先往左边找,如果绝对值成立则判断 SD (SD 必须 < 0.1)
  571. // 如果只是 SD 不成立,那么就继续往左,继续判断差值和 SD。
  572. // 如果都找不到合适的,就要回到”起点“,从右开始找,逻辑一样
  573. // 直到没有找到合适的信息,就报错
  574. // */
  575. // // 进行细节的修正
  576. // for index, fixedResult := range fixedResults {
  577. // // SD 大于 0.1 或者是 当前的 NewMean 与上一个点的 NewMean 差值大于 0.3
  578. // if fixedResult.NewSD >= 0.1 || (index > 1 && math.Abs(fixedResult.NewMean-fixedResults[index-1].NewMean) > 0.3) {
  579. // bok, newMean, newSD := s.fixOnePartV2(index, fixedResults)
  580. // if bok == true {
  581. // fixedResults[index].NewMean = newMean
  582. // fixedResults[index].NewSD = newSD
  583. // }
  584. // }
  585. // }
  586. //
  587. // return true, fixedResults, nil
  588. //}
  589. //
  590. //// fixOnePartV2 轻微地跳动可以根据左或者右去微调
  591. //func (s SubTimelineFixer) fixOnePartV2(startIndex int, fixedResults []FixResult) (bool, float64, float64) {
  592. //
  593. // /*
  594. // 找到这样情况的进行修正
  595. // */
  596. // // 先往左
  597. // if startIndex-1 >= 0 {
  598. // // 说明至少可以往左
  599. // // 如果左边的这个值,与当前值超过了 0.3 的绝对差值,那么是不适合的,就需要往右找
  600. // if math.Abs(fixedResults[startIndex-1].NewMean-fixedResults[startIndex].NewMean) < 0.3 {
  601. // // 差值在接受的范围内,那么就使用这个左边的值去校正当前的值
  602. // return true, fixedResults[startIndex-1].NewMean, fixedResults[startIndex-1].NewSD
  603. // }
  604. // }
  605. //
  606. // // 如果上面的理想情况都没有进去,那么就是这个差值很大
  607. // if fixedResults[startIndex].NewSD > 1 {
  608. // // SD 比较大,可能当前的位置是值是错误的,那么直接就使用左边的值
  609. // /*
  610. // -6.3 0.06
  611. // -146.85 243.83
  612. // */
  613. // if startIndex-1 >= 0 {
  614. // return true, fixedResults[startIndex-1].NewMean, fixedResults[startIndex-1].NewSD
  615. // }
  616. // } else {
  617. // // SD 不是很大,可能就是正常的字幕分段的时间轴偏移的 越接处 !
  618. // // 那么需要取,越接处,前三和后三,进行均值计算
  619. // /*
  620. // -6.21
  621. // -6.22
  622. // -6.29 0.06
  623. //
  624. // -7.13 0.14 越接处
  625. //
  626. // -7.32
  627. // -7.31
  628. // -7.44
  629. // */
  630. // left3Mean := 0.0
  631. // right3Mean := 0.0
  632. // // 向左,三个或者三个位置
  633. // if startIndex-3 >= 0 {
  634. // left3Mean = float64(fixedResults[startIndex-1].NewMean+fixedResults[startIndex-2].NewMean+fixedResults[startIndex-3].NewMean) / 3.0
  635. // } else if startIndex-2 >= 0 {
  636. // left3Mean = float64(fixedResults[startIndex-1].NewMean+fixedResults[startIndex-2].NewMean) / 2.0
  637. // } else {
  638. // return false, 0, 0
  639. // }
  640. // // 向右,三个或者三个位置
  641. // if startIndex+3 >= 0 {
  642. // right3Mean = float64(fixedResults[startIndex+1].NewMean+fixedResults[startIndex+2].NewMean+fixedResults[startIndex+3].NewMean) / 3.0
  643. // } else if startIndex+2 >= 0 {
  644. // right3Mean = float64(fixedResults[startIndex+1].NewMean+fixedResults[startIndex+2].NewMean) / 2.0
  645. // } else {
  646. // return false, 0, 0
  647. // }
  648. // // 将这个匹配的段中的子分段的时间轴偏移都进行一次计算,推算出到底是怎么样的配比可以得到这样的偏移结论
  649. // for i, info := range fixedResults[startIndex].MatchWindowInfos {
  650. //
  651. // perPartLen := info.EndVADIndex - info.StartVADIndex
  652. // op := OverParts{}
  653. // // xLen 计算公式见推到公式截图
  654. // xLen := (info.TimeDiffStartCorrelation*float64(perPartLen) - right3Mean*float64(perPartLen)) / (left3Mean - right3Mean)
  655. // yLen := float64(perPartLen) - xLen
  656. //
  657. // op.XLen = xLen
  658. // op.YLen = yLen
  659. // op.XMean = left3Mean
  660. // op.YMean = right3Mean
  661. //
  662. // fixedResults[startIndex].IsOverParts = true
  663. // fixedResults[startIndex].MatchWindowInfos[i].OP = op
  664. // }
  665. //
  666. // return true, fixedResults[startIndex+1].NewMean, fixedResults[startIndex+1].NewSD
  667. // }
  668. //
  669. // return false, 0, 0
  670. //}
  671. //
  672. //// slidingWindowProcessorV2 滑动窗口计算时间轴偏移
  673. //func (s *SubTimelineFixer) slidingWindowProcessorV2(windowInfo *WindowInfo) (*MatchInfo, error) {
  674. //
  675. // // -------------------------------------------------
  676. // var bUseSubOrAudioAsBase = true
  677. // if windowInfo.BaseUnit == nil && windowInfo.BaseAudioFloatList != nil {
  678. // // 使用 音频 来进行匹配
  679. // bUseSubOrAudioAsBase = false
  680. // } else if windowInfo.BaseUnit != nil {
  681. // // 使用 字幕 来进行匹配
  682. // bUseSubOrAudioAsBase = true
  683. // } else {
  684. // return nil, errors.New("GetOffsetTimeV2 input baseUnit or AudioVad is nil")
  685. // }
  686. // // -------------------------------------------------
  687. // outMatchInfo := MatchInfo{
  688. // IndexMatchWindowInfoMap: make(map[int]MatchWindowInfo, 0),
  689. // StartDiffTimeList: make([]float64, 0),
  690. // StartDiffTimeMap: treemap.NewWith(utils.Float64Comparator),
  691. // StartDiffTimeListEx: make(stat.Float64Slice, 0),
  692. // }
  693. // fixFunc := func(i interface{}) error {
  694. // inData := i.(InputData)
  695. // // -------------------------------------------------
  696. // // 开始匹配
  697. // // 这里的对白单元,当前的 Base 进行对比,详细示例见图解。Step 2 中橙色的区域
  698. // fffAligner := NewFFTAligner(DefaultMaxOffsetSeconds, SampleRate)
  699. // var bok = false
  700. // var nowBaseStartTime = 0.0
  701. // var offsetIndex = 0
  702. // var score = 0.0
  703. // srcMaxLen := 0
  704. // // 图解,参考 Step 3
  705. // if bUseSubOrAudioAsBase == false {
  706. // // 使用 音频 来进行匹配
  707. // // 去掉头和尾,具体百分之多少,见 V2_FrontAndEndPerBase
  708. // audioCutLen := int(float64(len(inData.BaseAudioVADList)) * s.FixerConfig.V2_FrontAndEndPerBase)
  709. //
  710. // srcMaxLen = windowInfo.SrcWindowLen + inData.OffsetIndex
  711. // if srcMaxLen >= len(inData.SrcUnit.GetVADFloatSlice()) {
  712. // srcMaxLen = len(inData.SrcUnit.GetVADFloatSlice()) - 1
  713. // }
  714. // offsetIndex, score = fffAligner.Fit(inData.BaseAudioVADList[audioCutLen:len(inData.BaseAudioVADList)-audioCutLen], inData.SrcUnit.GetVADFloatSlice()[inData.OffsetIndex:srcMaxLen])
  715. // realOffsetIndex := offsetIndex + audioCutLen
  716. // if realOffsetIndex < 0 {
  717. // return nil
  718. // }
  719. // // offsetIndex 这里得到的是 10ms 为一个单位的 OffsetIndex
  720. // nowBaseStartTime = vad.GetAudioIndex2Time(realOffsetIndex)
  721. //
  722. // } else {
  723. // // 使用 字幕 来进行匹配
  724. //
  725. // srcMaxLen = inData.OffsetIndex + windowInfo.SrcWindowLen
  726. // if srcMaxLen >= len(inData.SrcUnit.GetVADFloatSlice()) {
  727. // srcMaxLen = len(inData.SrcUnit.GetVADFloatSlice()) - 1
  728. // }
  729. // offsetIndex, score = fffAligner.Fit(inData.BaseUnit.GetVADFloatSlice(), inData.SrcUnit.GetVADFloatSlice()[inData.OffsetIndex:srcMaxLen])
  730. // if offsetIndex < 0 {
  731. // return nil
  732. // }
  733. // bok, nowBaseStartTime = inData.BaseUnit.GetIndexTimeNumber(offsetIndex, true)
  734. // if bok == false {
  735. // return nil
  736. // }
  737. // }
  738. // // 需要校正的字幕
  739. // bok, nowSrcStartTime := inData.SrcUnit.GetIndexTimeNumber(inData.OffsetIndex, true)
  740. // if bok == false {
  741. // return nil
  742. // }
  743. // // 时间差值
  744. // TimeDiffStartCorrelation := nowBaseStartTime - nowSrcStartTime
  745. // s.log.Debugln("------------")
  746. // s.log.Debugln("OffsetTime:", fmt.Sprintf("%v", TimeDiffStartCorrelation),
  747. // "offsetIndex:", offsetIndex,
  748. // "score:", fmt.Sprintf("%v", score))
  749. //
  750. // mutexFixV2.Lock()
  751. // // 这里的未必的顺序的,所以才有 IndexMatchWindowInfoMap 的存在的意义
  752. // outMatchInfo.IndexMatchWindowInfoMap[inData.Index] = MatchWindowInfo{TimeDiffStartCorrelation: TimeDiffStartCorrelation,
  753. // StartVADIndex: inData.OffsetIndex,
  754. // EndVADIndex: srcMaxLen}
  755. // outMatchInfo.StartDiffTimeList = append(outMatchInfo.StartDiffTimeList, TimeDiffStartCorrelation)
  756. // outMatchInfo.StartDiffTimeListEx = append(outMatchInfo.StartDiffTimeListEx, TimeDiffStartCorrelation)
  757. // outMatchInfo.StartDiffTimeMap.Put(score, windowInfo.MatchedTimes)
  758. // windowInfo.MatchedTimes++
  759. // mutexFixV2.Unlock()
  760. // // -------------------------------------------------
  761. // return nil
  762. // }
  763. // // -------------------------------------------------
  764. // antPool, err := ants.NewPoolWithFunc(s.FixerConfig.V2_FixThreads, func(inData interface{}) {
  765. // data := inData.(InputData)
  766. // defer data.Wg.Done()
  767. // ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.FixerConfig.V2_SubOneUnitProcessTimeOut)*time.Second)
  768. // defer cancel()
  769. //
  770. // done := make(chan error, 1)
  771. // panicChan := make(chan interface{}, 1)
  772. // go func() {
  773. // defer func() {
  774. // if p := recover(); p != nil {
  775. // panicChan <- p
  776. // }
  777. //close(done)
  778. //close(panicChan)
  779. // }()
  780. //
  781. // done <- fixFunc(inData)
  782. // }()
  783. //
  784. // select {
  785. // case err := <-done:
  786. // if err != nil {
  787. // s.log.Errorln("GetOffsetTimeV2.NewPoolWithFunc done with Error", err.Error())
  788. // }
  789. // return
  790. // case p := <-panicChan:
  791. // s.log.Errorln("GetOffsetTimeV2.NewPoolWithFunc got panic", p)
  792. // return
  793. // case <-ctx.Done():
  794. // s.log.Errorln("GetOffsetTimeV2.NewPoolWithFunc got time out", ctx.Err())
  795. // return
  796. // }
  797. // })
  798. // if err != nil {
  799. // return nil, err
  800. // }
  801. // defer antPool.Release()
  802. // // -------------------------------------------------
  803. // wg := sync.WaitGroup{}
  804. // index := 0
  805. // for i := windowInfo.SrcSlideStartIndex; i < windowInfo.SrcSlideStartIndex+windowInfo.SrcSlideLen-1; {
  806. // wg.Add(1)
  807. //
  808. // if bUseSubOrAudioAsBase == true {
  809. // // 使用字幕
  810. // err = antPool.Invoke(InputData{Index: index, BaseUnit: *windowInfo.BaseUnit, SrcUnit: *windowInfo.SrcUnit, OffsetIndex: i, Wg: &wg})
  811. // } else {
  812. // // 使用音频
  813. // err = antPool.Invoke(InputData{Index: index, BaseAudioVADList: windowInfo.BaseAudioFloatList, SrcUnit: *windowInfo.SrcUnit, OffsetIndex: i, Wg: &wg})
  814. // }
  815. //
  816. // if err != nil {
  817. // s.log.Errorln("GetOffsetTimeV2 ants.Invoke", err)
  818. // }
  819. //
  820. // i += windowInfo.OneStep
  821. // index++
  822. // }
  823. // wg.Wait()
  824. //
  825. // return &outMatchInfo, nil
  826. //}
  827. //
  828. //func (s *SubTimelineFixer) calcMeanAndSDV2(startDiffTimeList stat.Float64Slice, tmpStartDiffTime []float64) FixResult {
  829. //
  830. // oldMean := stat.Mean(startDiffTimeList)
  831. // oldSd := stat.Sd(startDiffTimeList)
  832. // newMean := MinValue
  833. // newSd := MinValue
  834. // per := 1.0
  835. //
  836. // if len(tmpStartDiffTime) < 3 {
  837. // return FixResult{
  838. // 0,
  839. // 0,
  840. // oldMean,
  841. // oldSd,
  842. // oldMean,
  843. // oldSd,
  844. // per,
  845. // false,
  846. // make([]MatchWindowInfo, 0),
  847. // }
  848. // }
  849. //
  850. // // 如果 SD 较大的时候才需要剔除
  851. // if oldSd > 0.1 {
  852. // var outliersMap = make(map[float64]int, 0)
  853. // outliers, _, _ := tukey.Outliers(0.3, tmpStartDiffTime)
  854. // for _, outlier := range outliers {
  855. // outliersMap[outlier] = 0
  856. // }
  857. // var newStartDiffTimeList = make([]float64, 0)
  858. // for _, f := range tmpStartDiffTime {
  859. //
  860. // _, ok := outliersMap[f]
  861. // if ok == true {
  862. // continue
  863. // }
  864. //
  865. // newStartDiffTimeList = append(newStartDiffTimeList, f)
  866. // }
  867. //
  868. // orgLen := startDiffTimeList.Len()
  869. // startDiffTimeList = make(stat.Float64Slice, 0)
  870. // for _, f := range newStartDiffTimeList {
  871. // startDiffTimeList = append(startDiffTimeList, f)
  872. // }
  873. // newLen := startDiffTimeList.Len()
  874. //
  875. // per = float64(newLen) / float64(orgLen)
  876. //
  877. // newMean = stat.Mean(startDiffTimeList)
  878. // newSd = stat.Sd(startDiffTimeList)
  879. // }
  880. //
  881. // if my_util.IsEqual(newMean, MinValue) == true {
  882. // newMean = oldMean
  883. // }
  884. // if my_util.IsEqual(newSd, MinValue) == true {
  885. // newSd = oldSd
  886. // }
  887. // return FixResult{
  888. // 0,
  889. // 0,
  890. // oldMean,
  891. // oldSd,
  892. // newMean,
  893. // newSd,
  894. // per,
  895. // false,
  896. // make([]MatchWindowInfo, 0),
  897. // }
  898. //}
  899. //
  900. //// GetOffsetTimeV3 使用内置的字幕校正外置的字幕时间轴
  901. //func (s *SubTimelineFixer) GetOffsetTimeV3(infoBase, infoSrc, orgFix *subparser.FileInfo, audioVadList []vad.VADInfo) error {
  902. //
  903. // // -------------------------------------------------
  904. // var bUseSubOrAudioAsBase = true
  905. // if infoBase == nil && audioVadList != nil {
  906. // // 使用 音频 来进行匹配
  907. // bUseSubOrAudioAsBase = false
  908. // } else if infoBase != nil {
  909. // // 使用 字幕 来进行匹配
  910. // bUseSubOrAudioAsBase = true
  911. // } else {
  912. // return errors.New("GetOffsetTimeV2 input baseUnit or AudioVad is nil")
  913. // }
  914. // // -------------------------------------------------
  915. // audioFloatList := vad.GetFloatSlice(audioVadList)
  916. // baseUnitNew, err := sub_helper.GetVADInfoFeatureFromSubNew(infoBase, 0)
  917. // if err != nil {
  918. // return err
  919. // }
  920. // srcUnitNew, err := sub_helper.GetVADInfoFeatureFromSubNew(infoSrc, 0)
  921. // if err != nil {
  922. // return err
  923. // }
  924. // /*
  925. // 上面直接得到所有的输入源,都是完整的一个文件,字幕 or 音频
  926. // 然后根据字幕文件每一个对白进行匹配,这里就使用 V2_FrontAndEndPerSrc 进行字幕的选择,不打算从第一句话开始
  927. // 那么假如 一共有 100 句话,V2_FrontAndEndPerSrc 是 0.2,那么就是从 20 - 80 句话进行匹配计算
  928. // 然后 < 20 的就继承 20 的偏移,> 80 的就继承 80 的偏移即可
  929. // 那么现在就需要从对白中开始遍历
  930. // */
  931. // fffAligner := NewFFTAligner(DefaultMaxOffsetSeconds, SampleRate)
  932. // err2, done := s.caleOne(0.1, srcUnitNew, fffAligner, baseUnitNew)
  933. // if done {
  934. // return err2
  935. // }
  936. // err2, done = s.caleOne(0.2, srcUnitNew, fffAligner, baseUnitNew)
  937. // if done {
  938. // return err2
  939. // }
  940. //
  941. // skipSubLen := int(float64(len(infoSrc.DialoguesFilter)) * s.FixerConfig.V2_FrontAndEndPerSrc)
  942. //
  943. // sort.Sort(subparser.OneDialogueByStartTime(infoSrc.DialoguesFilter))
  944. // sort.Sort(subparser.OneDialogueByStartTime(orgFix.DialoguesFilter))
  945. //
  946. // for i := 0; i < len(infoSrc.DialoguesFilter); i++ {
  947. //
  948. // // 得到的是真实的时间
  949. // srcOneDialogueNow := infoSrc.DialoguesFilter[i]
  950. // srcTimeStartNow, err := my_util.ParseTime(srcOneDialogueNow.StartTime)
  951. // if err != nil {
  952. // return err
  953. // }
  954. // orgFixOneDialogueNow := orgFix.DialoguesFilter[i]
  955. // orgFixTimeStartNow, err := my_util.ParseTime(orgFixOneDialogueNow.StartTime)
  956. // if err != nil {
  957. // return err
  958. // }
  959. //
  960. // println("Index:", i, "srcTimeStartOrg:", srcTimeStartNow.Format("15:04:05.000"),
  961. // "src-fix-offset:", my_util.Time2SecondNumber(orgFixTimeStartNow)-my_util.Time2SecondNumber(srcTimeStartNow))
  962. // }
  963. // println("------------------")
  964. // for i := skipSubLen; i < len(infoSrc.DialoguesFilter)-skipSubLen-1; i++ {
  965. //
  966. // var bok = false
  967. // var nowBaseStartTime = 0.0
  968. // var offsetIndex = 0
  969. // var score = 0.0
  970. // const next = 20
  971. // const secondRange = 45
  972. // // -------------------------------------------------
  973. // srcOneDialogueNow := infoSrc.DialoguesFilter[i]
  974. // iNext := i + next
  975. // if iNext >= len(infoSrc.DialoguesFilter)-skipSubLen-1 {
  976. // iNext = len(infoSrc.DialoguesFilter) - skipSubLen - 1
  977. // }
  978. // srcOneDialogueNext := infoSrc.DialoguesFilter[iNext]
  979. // // 得到的是真实的时间
  980. // srcTimeStartNow, err := my_util.ParseTime(srcOneDialogueNow.StartTime)
  981. // if err != nil {
  982. // return err
  983. // }
  984. // srcTimeEndNext, err := my_util.ParseTime(srcOneDialogueNext.EndTime)
  985. // if err != nil {
  986. // return err
  987. // }
  988. // orgFixOneDialogueNow := orgFix.DialoguesFilter[i]
  989. // orgFixTimeStartNow, err := my_util.ParseTime(orgFixOneDialogueNow.StartTime)
  990. // if err != nil {
  991. // return err
  992. // }
  993. // // -------------------------------------------------
  994. // // 需要转换为 VAD 的对应 Index,需要减去 baseTime,然后根据 10ms 进行计算
  995. // // -------------------------------------------------
  996. // // Src
  997. // srcStartOffsetTimeNow := srcUnitNew.RealTimeToOffsetTime(srcTimeStartNow)
  998. // srcStartTimeVADIndexNow := int(my_util.Time2SecondNumber(srcStartOffsetTimeNow) * 100)
  999. //
  1000. // srcEndOffsetTimeNext := srcUnitNew.RealTimeToOffsetTime(srcTimeEndNext)
  1001. // srcEndTimeVADIndexNext := int(my_util.Time2SecondNumber(srcEndOffsetTimeNext) * 100)
  1002. // // -------------------------------------------------
  1003. // if bUseSubOrAudioAsBase == false {
  1004. // // 使用 音频 来进行匹配
  1005. //
  1006. // } else {
  1007. // // 使用 字幕 来进行匹配
  1008. // // -------------------------------------------------
  1009. // // Base
  1010. // baseStartOffsetTimeNow := baseUnitNew.RealTimeToOffsetTime(srcTimeStartNow).Add(-secondRange * time.Second)
  1011. // baseStartTimeVADIndexNow := int(my_util.Time2SecondNumber(baseStartOffsetTimeNow) * 100)
  1012. //
  1013. // baseEndOffsetTimeNext := baseUnitNew.RealTimeToOffsetTime(srcTimeEndNext).Add(secondRange * time.Second)
  1014. // baseEndTimeVADIndexNext := int(my_util.Time2SecondNumber(baseEndOffsetTimeNext) * 100)
  1015. // if baseEndTimeVADIndexNext >= len(baseUnitNew.VADList)-1 {
  1016. // baseEndTimeVADIndexNext = len(baseUnitNew.VADList) - 1
  1017. // }
  1018. // // -------------------------------------------------
  1019. // offsetIndex, score = fffAligner.Fit(baseUnitNew.GetVADFloatSlice()[baseStartTimeVADIndexNow:baseEndTimeVADIndexNext], srcUnitNew.GetVADFloatSlice()[srcStartTimeVADIndexNow:srcEndTimeVADIndexNext])
  1020. // if offsetIndex < 0 {
  1021. // //return nil
  1022. // continue
  1023. // }
  1024. // bok, nowBaseStartTime = baseUnitNew.GetIndexTimeNumber(baseStartTimeVADIndexNow+offsetIndex, true)
  1025. // if bok == false {
  1026. // return nil
  1027. // }
  1028. // }
  1029. // // 需要校正的字幕
  1030. // bok, nowSrcStartTime := srcUnitNew.GetIndexTimeNumber(srcStartTimeVADIndexNow, true)
  1031. // if bok == false {
  1032. // return nil
  1033. // }
  1034. // // 时间差值
  1035. // TimeDiffStartCorrelation := nowBaseStartTime - nowSrcStartTime
  1036. //
  1037. // println("Index:", i, "srcTimeStartOrg:", srcTimeStartNow.Format("15:04:05.000"),
  1038. // "OffsetTime:", TimeDiffStartCorrelation, "Score:", score,
  1039. // "ChangedTime:", srcTimeStartNow.Add(time.Duration(TimeDiffStartCorrelation*1000)*time.Millisecond).Format("15:04:05.000"),
  1040. // "OrgFixTime:", orgFixTimeStartNow.Format("15:04:05.000"),
  1041. // "src-fix-offset:", my_util.Time2SecondNumber(orgFixTimeStartNow)-my_util.Time2SecondNumber(srcTimeStartNow))
  1042. // }
  1043. //
  1044. // //if baseStartTimeVADIndexNow > 3600000 {
  1045. // // baseStartTimeVADIndexNow = 0
  1046. // //}
  1047. //
  1048. // println(len(audioFloatList))
  1049. // println(len(baseUnitNew.VADList))
  1050. // println(len(srcUnitNew.VADList))
  1051. //
  1052. // return nil
  1053. //}
  1054. //
  1055. //func (s *SubTimelineFixer) caleOne(cutPer float64, srcUnitNew *sub_helper.SubUnit, fffAligner *FFTAligner, baseUnitNew *sub_helper.SubUnit) (error, bool) {
  1056. // srcVADLen := len(srcUnitNew.VADList)
  1057. // srcCutStartIndex := int(float64(srcVADLen) * cutPer)
  1058. // offsetIndexAll, scoreAll := fffAligner.Fit(baseUnitNew.GetVADFloatSlice(), srcUnitNew.GetVADFloatSlice()[srcCutStartIndex:srcVADLen-srcCutStartIndex])
  1059. // bok, nowBaseStartTime := baseUnitNew.GetIndexTimeNumber(0+offsetIndexAll, true)
  1060. // if bok == false {
  1061. // return nil, true
  1062. // }
  1063. // // 需要校正的字幕
  1064. // bok, nowSrcStartTime := srcUnitNew.GetIndexTimeNumber(srcCutStartIndex, true)
  1065. // if bok == false {
  1066. // return nil, true
  1067. // }
  1068. // // 时间差值
  1069. // TimeDiffStartCorrelation := nowBaseStartTime - nowSrcStartTime
  1070. // bok, srcIndexCutTime := srcUnitNew.GetIndexTime(srcCutStartIndex, true)
  1071. // if bok == false {
  1072. // return nil, true
  1073. // }
  1074. // println(srcIndexCutTime.Format("15:04:05.000"), TimeDiffStartCorrelation, scoreAll)
  1075. // return nil, false
  1076. //}
  1077. //
  1078. //const FixMask = "-fix"
  1079. //const MinValue = -9999.0
  1080. //
  1081. //var mutexFixV2 sync.Mutex