sub_unit.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. package sub_helper
  2. import (
  3. "bufio"
  4. "fmt"
  5. "math"
  6. "os"
  7. "time"
  8. "github.com/allanpk716/ChineseSubFinder/pkg"
  9. "github.com/allanpk716/ChineseSubFinder/pkg/vad"
  10. )
  11. type SubUnit struct {
  12. baseTime time.Time // 这个是基础的时间,后续需要减去这个,不然与导出的片段字幕去对比会有一个起始时间的偏差
  13. offsetStartTime time.Time // 相对时间,这个时间会减去 baseTime 再存储
  14. offsetEndTime time.Time // 相对时间,这个时间会减去 baseTime 再存储
  15. VADList []vad.VADInfo // 注意这里存储的是真实时间
  16. subCount int
  17. firstAdd bool
  18. outVADBytes []byte
  19. outVADFloats []float64
  20. }
  21. func NewSubUnit() *SubUnit {
  22. return &SubUnit{
  23. VADList: make([]vad.VADInfo, 0),
  24. subCount: 0,
  25. firstAdd: false,
  26. outVADBytes: make([]byte, 0),
  27. outVADFloats: make([]float64, 0),
  28. }
  29. }
  30. func (s *SubUnit) Add(oneSubStartTime, oneSubEndTime time.Time) {
  31. //oneSubStartTime = my_util.MakeFloor10msMultipleFromTime(oneSubStartTime)
  32. //oneSubEndTime = my_util.MakeFloor10msMultipleFromTime(oneSubEndTime)
  33. if s.firstAdd == false {
  34. // 第一次 Add 需要给 baseTime 赋值
  35. s.baseTime = oneSubStartTime
  36. s.offsetStartTime = s.RealTimeToOffsetTime(oneSubStartTime)
  37. s.firstAdd = true
  38. }
  39. s.offsetEndTime = oneSubEndTime.Add(-pkg.Time2Duration(s.baseTime))
  40. // 添加 Start
  41. s.VADList = append(s.VADList, *vad.NewVADInfoBase(true, time.Duration((pkg.Time2SecondNumber(oneSubStartTime))*math.Pow10(9))))
  42. // 添加 End
  43. s.VADList = append(s.VADList, *vad.NewVADInfoBase(false, time.Duration((pkg.Time2SecondNumber(oneSubEndTime))*math.Pow10(9))))
  44. s.subCount++
  45. }
  46. // AddAndInsert 添加一句对白进来,并且填充中间的空白,间隔 10ms。传入的时间是真实的时间
  47. func (s *SubUnit) AddAndInsert(oneSubStartTime, oneSubEndTime time.Time) {
  48. /*
  49. 这里有个比较有意思的细节,字幕拆分到 dialogue 的时候,可能连续的多个 dialogue 是时间轴连续的
  50. 但是实际上的语言就是可以分为几个句子的
  51. 那么,在本函数中,就需要判断插入的时候,与上一句话的时间轴关系,前置无需进行句子的合并
  52. 如果两句话时间轴是连续的(差值为0),那么就要主动修改这一点,采取的方案可以是
  53. 1. 前后各 0.001 秒即可
  54. 2. 后面这一句向后 0.002 秒(暂时优先考虑这个,容易实现)
  55. */
  56. //oneSubStartTime = my_util.MakeFloor10msMultipleFromTime(oneSubStartTime)
  57. //oneSubEndTime = my_util.MakeFloor10msMultipleFromTime(oneSubEndTime)
  58. // 不是第一次添加,那么就需要把两句对白中间间隔的 active == false 的插入,插入间隙
  59. if len(s.VADList) > 0 {
  60. nowStartTime := s.RealTimeToOffsetTime(oneSubStartTime)
  61. nowStartOffsetTime := pkg.Time2SecondNumber(nowStartTime)
  62. nowEndOffsetTime := s.GetEndTimeNumber(false)
  63. needAddRange := nowStartOffsetTime - nowEndOffsetTime
  64. if needAddRange == 0 {
  65. // 说明是连续的句子,向后加 0.002 秒 addMoreTime
  66. addMore := time.Duration((s.GetEndTimeNumber(true) + addMoreTime) * math.Pow10(9))
  67. s.VADList = append(s.VADList, *vad.NewVADInfoBase(false, addMore))
  68. // 因为是连续的两句话的时间轴,强制插入了一个点,那么就需要在这句话的 Start 部分向后延迟对应的秒数
  69. oneSubStartTime = oneSubStartTime.Add(time.Duration(addMoreTime * math.Pow10(9)))
  70. } else {
  71. for i := 0.0; i < needAddRange; {
  72. s.VADList = append(s.VADList, *vad.NewVADInfoBase(false, time.Duration((s.GetEndTimeNumber(true)+i)*math.Pow10(9))))
  73. i += perWindows
  74. }
  75. }
  76. }
  77. if s.firstAdd == false {
  78. // 第一次 Add 需要给 baseTime 赋值
  79. s.baseTime = oneSubStartTime
  80. s.offsetStartTime = s.RealTimeToOffsetTime(oneSubStartTime)
  81. s.firstAdd = true
  82. }
  83. s.offsetEndTime = oneSubEndTime.Add(-pkg.Time2Duration(s.baseTime))
  84. nowStartTime := s.RealTimeToOffsetTime(oneSubStartTime)
  85. nowEndTime := s.RealTimeToOffsetTime(oneSubEndTime)
  86. nowStartOffsetTime := pkg.Time2SecondNumber(nowStartTime)
  87. nowEndOffsetTime := pkg.Time2SecondNumber(nowEndTime)
  88. needAddRange := nowEndOffsetTime - nowStartOffsetTime
  89. for i := 0.0; i < needAddRange; {
  90. s.VADList = append(s.VADList, *vad.NewVADInfoBase(true, time.Duration((pkg.Time2SecondNumber(oneSubStartTime)+i)*math.Pow10(9))))
  91. i += perWindows
  92. }
  93. s.subCount++
  94. }
  95. // AddBaseTime 如果 BaseTime 还有偏移,可以在 Add 和 AddAndInsert 逻辑完成后,调用此方法去调整基准时间
  96. func (s *SubUnit) AddBaseTime(addBaseTime time.Duration) {
  97. s.baseTime = s.baseTime.Add(addBaseTime)
  98. }
  99. // SetBaseTime 设置基准时间
  100. func (s *SubUnit) SetBaseTime(setBaseTime time.Time) {
  101. s.baseTime = setBaseTime
  102. }
  103. func (s *SubUnit) SetOffsetStartTime(realStartTime time.Time) {
  104. s.offsetStartTime = s.RealTimeToOffsetTime(realStartTime)
  105. }
  106. func (s *SubUnit) SetOffsetEndTime(realEndTime time.Time) {
  107. s.offsetEndTime = s.RealTimeToOffsetTime(realEndTime)
  108. }
  109. // GetDialogueCount 获取这个对白单元由几个对话
  110. func (s SubUnit) GetDialogueCount() int {
  111. return s.subCount
  112. }
  113. // GetVADByteSlice 获取 VAD 的 byte 数组信息
  114. func (s *SubUnit) GetVADByteSlice() []byte {
  115. if len(s.outVADBytes) != len(s.VADList) {
  116. s.outVADBytes = make([]byte, len(s.VADList))
  117. for i := 0; i < len(s.VADList); i++ {
  118. if s.VADList[i].Active == true {
  119. s.outVADBytes[i] = 1
  120. } else {
  121. s.outVADBytes[i] = 0
  122. }
  123. }
  124. }
  125. return s.outVADBytes
  126. }
  127. // GetVADFloatSlice 获取 VAD 的 float64 数组信息
  128. func (s *SubUnit) GetVADFloatSlice() []float64 {
  129. if len(s.outVADFloats) != len(s.VADList) {
  130. s.outVADFloats = make([]float64, len(s.VADList))
  131. for i := 0; i < len(s.VADList); i++ {
  132. if s.VADList[i].Active == true {
  133. s.outVADFloats[i] = 1
  134. } else {
  135. s.outVADFloats[i] = -1
  136. }
  137. }
  138. }
  139. return s.outVADFloats
  140. }
  141. // GetStartTimeNumber 获取这个单元的起始时间,单位是秒
  142. func (s SubUnit) GetStartTimeNumber(realOrOffsetTime bool) float64 {
  143. return pkg.Time2SecondNumber(s.GetStartTime(realOrOffsetTime))
  144. }
  145. // GetStartTime 获取这个单元的起始时间
  146. func (s SubUnit) GetStartTime(realOrOffsetTime bool) time.Time {
  147. if realOrOffsetTime == true {
  148. return s.offsetStartTime.Add(pkg.Time2Duration(s.baseTime))
  149. } else {
  150. return s.offsetStartTime
  151. }
  152. }
  153. // GetEndTimeNumber 获取这个单元的结束时间,单位是秒
  154. func (s SubUnit) GetEndTimeNumber(realOrOffsetTime bool) float64 {
  155. return pkg.Time2SecondNumber(s.GetEndTime(realOrOffsetTime))
  156. }
  157. // GetEndTime 获取这个单元的起始时间
  158. func (s SubUnit) GetEndTime(realOrOffsetTime bool) time.Time {
  159. if realOrOffsetTime == true {
  160. return s.offsetEndTime.Add(pkg.Time2Duration(s.baseTime))
  161. } else {
  162. return s.offsetEndTime
  163. }
  164. }
  165. // GetIndexTime 当前 OffsetIndex 的时间
  166. func (s SubUnit) GetIndexTime(index int, realOrOffsetTime bool) (bool, time.Time) {
  167. if index >= len(s.VADList) {
  168. return false, time.Time{}
  169. }
  170. if realOrOffsetTime == true {
  171. return true, time.Time{}.Add(s.VADList[index].Time)
  172. } else {
  173. return true, time.Time{}.Add(s.VADList[index].Time).Add(-pkg.Time2Duration(s.baseTime))
  174. }
  175. }
  176. // GetIndexTimeNumber 当前 OffsetIndex 的时间
  177. func (s SubUnit) GetIndexTimeNumber(index int, realOrOffsetTime bool) (bool, float64) {
  178. bok, outTime := s.GetIndexTime(index, realOrOffsetTime)
  179. if bok == false {
  180. return false, 0
  181. }
  182. return true, pkg.Time2SecondNumber(outTime)
  183. }
  184. // GetTimelineRange 开始到结束的时间长度,单位是秒
  185. func (s SubUnit) GetTimelineRange() float64 {
  186. return s.GetEndTimeNumber(false) - s.GetStartTimeNumber(false)
  187. }
  188. // GetOffsetTimeNumber 偏移时间,单位是秒
  189. func (s SubUnit) GetOffsetTimeNumber() float64 {
  190. return pkg.Time2SecondNumber(s.baseTime)
  191. }
  192. // GetFFMPEGCutRangeString 这里会生成导出 FFMPEG 的参数字段,起始时间和结束的时间长度
  193. // 以当前的 VAD 信息为基准,正负 expandTimeRange(秒为单位) 来生成截取的片段时间轴信息
  194. func (s SubUnit) GetFFMPEGCutRangeString(expandTimeRange float64) (string, string, time.Time, float64) {
  195. var tmpStartTime time.Time
  196. if s.GetStartTimeNumber(true)-expandTimeRange < 0 {
  197. tmpStartTime = time.Time{}
  198. } else {
  199. startTime := s.GetStartTime(true)
  200. subTime := time.Duration(expandTimeRange) * time.Second
  201. tmpStartTime = startTime.Add(-subTime)
  202. }
  203. return fmt.Sprintf("%d:%d:%d.%d", tmpStartTime.Hour(), tmpStartTime.Minute(), tmpStartTime.Second(), tmpStartTime.Nanosecond()/1000/1000),
  204. fmt.Sprintf("%f", s.GetTimelineRange()+2*expandTimeRange),
  205. tmpStartTime,
  206. s.GetTimelineRange() + expandTimeRange
  207. }
  208. // GetExpandRangeIndex 导出扩展的起始时间和结束的时间,整个多出的参数只适用于整体的字幕范围,局部不试用
  209. // 以当前的 VAD 信息为基准,正负 expandTimeRange(秒为单位) 来生成截取的片段时间轴信息
  210. // 向左偏移的时候是可知有多少可以移动的,越界就置为 0
  211. // 向右移动的时候,总长度是未知的,所以返回的值需要在外部重新 Check 是否会越界
  212. func (s SubUnit) GetExpandRangeIndex(expandTimeRange float64) (int, int) {
  213. var tmpStartTimeIndex int
  214. var tmpEndTimeIndex int
  215. // 起始时间 -> OffsetIndex
  216. if s.GetStartTimeNumber(true)-expandTimeRange < 0 {
  217. // 向左偏移的时候是可知有多少可以移动的,越界就置为 0
  218. tmpStartTimeIndex = 0
  219. } else {
  220. // 没有越界就直接用得到的毫秒差值去推算 index 的偏移位置
  221. startTime := s.GetStartTime(true)
  222. subTime := time.Duration(expandTimeRange) * time.Second
  223. tmpStartTime := startTime.Add(-subTime)
  224. // 需要从秒换算到偏移的 OffsetIndex 数值,一共多少份
  225. tmpStartTimeIndex = int(pkg.Time2SecondNumber(tmpStartTime) / perWindows)
  226. }
  227. // 结束时间 -> OffsetIndex
  228. // 向右移动的时候,总长度是未知的,所以返回的值需要在外部重新 Check 是否会越界
  229. endTime := s.GetEndTime(true)
  230. subTime := time.Duration(expandTimeRange) * time.Second
  231. tmpEndTime := endTime.Add(subTime)
  232. // 需要从秒换算到偏移的 OffsetIndex 数值,一共多少份
  233. tmpEndTimeIndex = int(pkg.Time2SecondNumber(tmpEndTime) / perWindows)
  234. return tmpStartTimeIndex, tmpEndTimeIndex
  235. }
  236. // RealTimeToOffsetTime 真实时间转偏移时间
  237. func (s SubUnit) RealTimeToOffsetTime(realTime time.Time) time.Time {
  238. dd := pkg.Time2Duration(s.baseTime)
  239. return realTime.Add(-dd)
  240. }
  241. // Save2Txt 导出为 float64 的内容
  242. func (s SubUnit) Save2Txt(outFileFPath string, oneLine bool) error {
  243. file, err := os.OpenFile(outFileFPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
  244. if err != nil {
  245. return err
  246. }
  247. defer file.Close()
  248. //写入文件时,使用带缓存的 *Writer
  249. write := bufio.NewWriter(file)
  250. for i := 0; i < len(s.VADList); i++ {
  251. active := 0.0
  252. if s.VADList[i].Active == true {
  253. active = 1.0
  254. }
  255. if oneLine == true {
  256. _, err = write.WriteString(fmt.Sprintf("%v", active))
  257. if err != nil {
  258. return err
  259. }
  260. } else {
  261. _, err = write.WriteString(fmt.Sprintf("%v\n", active))
  262. if err != nil {
  263. return err
  264. }
  265. }
  266. }
  267. err = write.Flush()
  268. if err != nil {
  269. return err
  270. }
  271. return nil
  272. }
  273. const perWindows = float64(vad.FrameDuration) / 1000
  274. const addMoreTime = 0.002