Browse Source

接入音频合成流程

Steven 1 năm trước cách đây
mục cha
commit
70938feea5
100 tập tin đã thay đổi với 4470 bổ sung267 xóa
  1. 140 0
      KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj
  2. 6 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/Contents.json
  3. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_left_bg.imageset/Contents.json
  4. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_left_bg.imageset/align_left_bg@2x.png
  5. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_left_bg.imageset/align_left_bg@3x.png
  6. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_right_bg.imageset/Contents.json
  7. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_right_bg.imageset/align_right_bg@2x.png
  8. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_right_bg.imageset/align_right_bg@3x.png
  9. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/aligningSlider_bg.imageset/Contents.json
  10. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/aligningSlider_bg.imageset/aligningSlider_bg@2x.png
  11. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/aligningSlider_bg.imageset/aligningSlider_bg@3x.png
  12. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/cancleMerge_image.imageset/Contents.json
  13. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/cancleMerge_image.imageset/cancleMerge_image@2x.png
  14. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/cancleMerge_image.imageset/cancleMerge_image@3x.png
  15. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_bg.imageset/Contents.json
  16. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_bg.imageset/disc_bg@2x.png
  17. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_bg.imageset/disc_bg@3x.png
  18. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_dot.imageset/Contents.json
  19. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_dot.imageset/disc_dot@2x.png
  20. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_dot.imageset/disc_dot@3x.png
  21. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergeControl_show.imageset/Contents.json
  22. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergeControl_show.imageset/mergeControl_show@2x.png
  23. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergeControl_show.imageset/mergeControl_show@3x.png
  24. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause.imageset/Contents.json
  25. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause.imageset/mergePlayer_pause@2x.png
  26. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause.imageset/mergePlayer_pause@3x.png
  27. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause_white.imageset/Contents.json
  28. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause_white.imageset/mergePlayer_pause_white@2x.png
  29. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause_white.imageset/mergePlayer_pause_white@3x.png
  30. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play.imageset/Contents.json
  31. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play.imageset/mergePlayer_play@2x.png
  32. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play.imageset/mergePlayer_play@3x.png
  33. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play_white.imageset/Contents.json
  34. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play_white.imageset/mergePlayer_play_white@2x.png
  35. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play_white.imageset/mergePlayer_play_white@3x.png
  36. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_music_bg.imageset/Contents.json
  37. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_music_bg.imageset/merge_music_bg@2x.png
  38. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_music_bg.imageset/merge_music_bg@3x.png
  39. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_next.imageset/Contents.json
  40. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_next.imageset/merge_next@2x.png
  41. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_next.imageset/merge_next@3x.png
  42. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_playerBg.imageset/Contents.json
  43. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_playerBg.imageset/merge_playerBg@2x.png
  44. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_playerBg.imageset/merge_playerBg@3x.png
  45. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_upload.imageset/Contents.json
  46. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_upload.imageset/merge_upload@2x.png
  47. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_upload.imageset/merge_upload@3x.png
  48. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/music_coverBg.imageset/Contents.json
  49. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/music_coverBg.imageset/music_coverBg@2x.png
  50. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/music_coverBg.imageset/music_coverBg@3x.png
  51. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/point_stick.imageset/Contents.json
  52. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/point_stick.imageset/point_stick@2x.png
  53. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/point_stick.imageset/point_stick@3x.png
  54. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pointing_arrow.imageset/Contents.json
  55. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pointing_arrow.imageset/pointing_arrow@2x.png
  56. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pointing_arrow.imageset/pointing_arrow@3x.png
  57. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pub_music_placeholder.imageset/Contents.json
  58. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pub_music_placeholder.imageset/pub_music_placeholder@2x.png
  59. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pub_music_placeholder.imageset/pub_music_placeholder@3x.png
  60. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/slider_bubble.imageset/Contents.json
  61. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/slider_bubble.imageset/slider_bubble@2x.png
  62. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/slider_bubble.imageset/slider_bubble@3x.png
  63. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/stylus_image.imageset/Contents.json
  64. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/stylus_image.imageset/stylus_image@2x.png
  65. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/stylus_image.imageset/stylus_image@3x.png
  66. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/sureButton_img.imageset/Contents.json
  67. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/sureButton_img.imageset/sureButton_img@2x.png
  68. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/sureButton_img.imageset/sureButton_img@3x.png
  69. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/vinyl_image.imageset/Contents.json
  70. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/vinyl_image.imageset/vinyl_image@2x.png
  71. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/vinyl_image.imageset/vinyl_image@3x.png
  72. 530 170
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSAccompanyWebViewController.m
  73. 4 2
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSBaseWKWebViewController.m
  74. 36 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.h
  75. 62 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.m
  76. 3 1
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSUploadManager.h
  77. 6 57
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSUploadManager.m
  78. 7 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSVideoRecordManager.h
  79. 41 28
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSVideoRecordManager.m
  80. 14 0
      KulexiuForStudent/KulexiuForStudent/Common/Base/LoadingManager/KSHudLoagingManager.h
  81. 27 9
      KulexiuForStudent/KulexiuForStudent/Common/Base/LoadingManager/KSHudLoagingManager.m
  82. 22 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/AudioPlayAnimationView/KSAudioPlayAnimationView.h
  83. 315 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/AudioPlayAnimationView/KSAudioPlayAnimationView.m
  84. 21 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAudioAnimationView.h
  85. 104 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAudioAnimationView.m
  86. 95 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAudioAnimationView.xib
  87. 32 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMediaMergeView.h
  88. 1078 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMediaMergeView.m
  89. 37 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMergeAudioControlView.h
  90. 337 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMergeAudioControlView.m
  91. 403 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMergeAudioControlView.xib
  92. 24 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSNewAlertView.h
  93. 116 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSNewAlertView.m
  94. 126 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSNewAlertView.xib
  95. 36 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerSliderView.h
  96. 176 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerSliderView.m
  97. 77 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerSliderView.xib
  98. 16 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerView.h
  99. 37 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerView.m
  100. 36 0
      KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/MusicPublistAlert.h

+ 140 - 0
KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj

@@ -511,6 +511,23 @@
 		BC2DFF5D28C097DC0056105A /* styleAnimation.json in Resources */ = {isa = PBXBuildFile; fileRef = BC2DFF5C28C097DC0056105A /* styleAnimation.json */; };
 		BC36A5082AB406A5002FEFB1 /* GroupOwnerTagView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC36A5072AB406A5002FEFB1 /* GroupOwnerTagView.m */; };
 		BC36A50A2AB406AB002FEFB1 /* GroupOwnerTagView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC36A5092AB406AB002FEFB1 /* GroupOwnerTagView.xib */; };
+		BC38C3CF2AF8930D00ABFCC2 /* kSNewPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3CE2AF8930D00ABFCC2 /* kSNewPlayer.m */; };
+		BC38C3D32AF8936300ABFCC2 /* KSMediaEditor.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3D12AF8936300ABFCC2 /* KSMediaEditor.m */; };
+		BC38C3E82AF893B400ABFCC2 /* KSPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3D52AF893B300ABFCC2 /* KSPlayerView.m */; };
+		BC38C3E92AF893B400ABFCC2 /* KSMediaMergeView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3D72AF893B300ABFCC2 /* KSMediaMergeView.m */; };
+		BC38C3EA2AF893B400ABFCC2 /* KSVideoPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3DC2AF893B300ABFCC2 /* KSVideoPlayerView.m */; };
+		BC38C3EB2AF893B400ABFCC2 /* KSAudioAnimationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC38C3DD2AF893B300ABFCC2 /* KSAudioAnimationView.xib */; };
+		BC38C3EC2AF893B400ABFCC2 /* KSAudioPlayAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3DF2AF893B300ABFCC2 /* KSAudioPlayAnimationView.m */; };
+		BC38C3ED2AF893B400ABFCC2 /* KSMergeAudioControlView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC38C3E22AF893B300ABFCC2 /* KSMergeAudioControlView.xib */; };
+		BC38C3EE2AF893B400ABFCC2 /* KSPlayerSliderView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3E32AF893B300ABFCC2 /* KSPlayerSliderView.m */; };
+		BC38C3EF2AF893B400ABFCC2 /* KSPlayerSliderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC38C3E42AF893B300ABFCC2 /* KSPlayerSliderView.xib */; };
+		BC38C3F02AF893B400ABFCC2 /* KSMergeAudioControlView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3E52AF893B300ABFCC2 /* KSMergeAudioControlView.m */; };
+		BC38C3F12AF893B400ABFCC2 /* KSAudioAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3E72AF893B300ABFCC2 /* KSAudioAnimationView.m */; };
+		BC38C3F42AF8B3BA00ABFCC2 /* UIView+KSCovertImage.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3F32AF8B3BA00ABFCC2 /* UIView+KSCovertImage.m */; };
+		BC38C3F82AF8B3E000ABFCC2 /* KSNewAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3F62AF8B3E000ABFCC2 /* KSNewAlertView.m */; };
+		BC38C3F92AF8B3E000ABFCC2 /* KSNewAlertView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC38C3F72AF8B3E000ABFCC2 /* KSNewAlertView.xib */; };
+		BC38C3FD2AF8B3F900ABFCC2 /* MusicPublistAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC38C3FA2AF8B3F800ABFCC2 /* MusicPublistAlert.xib */; };
+		BC38C3FE2AF8B3F900ABFCC2 /* MusicPublistAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = BC38C3FC2AF8B3F900ABFCC2 /* MusicPublistAlert.m */; };
 		BC3A4EAB28DAC0CD001C4428 /* ShareLiveDisplayView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3A4EAA28DAC0CD001C4428 /* ShareLiveDisplayView.m */; };
 		BC3A4EAD28DAC0D6001C4428 /* ShareLiveDisplayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC3A4EAC28DAC0D6001C4428 /* ShareLiveDisplayView.xib */; };
 		BC3A4EB128DAE074001C4428 /* KSQRCreateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3A4EB028DAE074001C4428 /* KSQRCreateManager.m */; };
@@ -2033,6 +2050,35 @@
 		BC36A5072AB406A5002FEFB1 /* GroupOwnerTagView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GroupOwnerTagView.m; sourceTree = "<group>"; };
 		BC36A5092AB406AB002FEFB1 /* GroupOwnerTagView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GroupOwnerTagView.xib; sourceTree = "<group>"; };
 		BC3818192A40262200B74F26 /* KSDomain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSDomain.h; sourceTree = "<group>"; };
+		BC38C3CD2AF8930D00ABFCC2 /* kSNewPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kSNewPlayer.h; sourceTree = "<group>"; };
+		BC38C3CE2AF8930D00ABFCC2 /* kSNewPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = kSNewPlayer.m; sourceTree = "<group>"; };
+		BC38C3D12AF8936300ABFCC2 /* KSMediaEditor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSMediaEditor.m; sourceTree = "<group>"; };
+		BC38C3D22AF8936300ABFCC2 /* KSMediaEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSMediaEditor.h; sourceTree = "<group>"; };
+		BC38C3D52AF893B300ABFCC2 /* KSPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSPlayerView.m; sourceTree = "<group>"; };
+		BC38C3D62AF893B300ABFCC2 /* KSAudioAnimationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSAudioAnimationView.h; sourceTree = "<group>"; };
+		BC38C3D72AF893B300ABFCC2 /* KSMediaMergeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSMediaMergeView.m; sourceTree = "<group>"; };
+		BC38C3D82AF893B300ABFCC2 /* KSMergeAudioControlView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSMergeAudioControlView.h; sourceTree = "<group>"; };
+		BC38C3D92AF893B300ABFCC2 /* KSPlayerSliderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSPlayerSliderView.h; sourceTree = "<group>"; };
+		BC38C3DB2AF893B300ABFCC2 /* KSVideoPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSVideoPlayerView.h; sourceTree = "<group>"; };
+		BC38C3DC2AF893B300ABFCC2 /* KSVideoPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSVideoPlayerView.m; sourceTree = "<group>"; };
+		BC38C3DD2AF893B300ABFCC2 /* KSAudioAnimationView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KSAudioAnimationView.xib; sourceTree = "<group>"; };
+		BC38C3DF2AF893B300ABFCC2 /* KSAudioPlayAnimationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSAudioPlayAnimationView.m; sourceTree = "<group>"; };
+		BC38C3E02AF893B300ABFCC2 /* KSAudioPlayAnimationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSAudioPlayAnimationView.h; sourceTree = "<group>"; };
+		BC38C3E12AF893B300ABFCC2 /* KSPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSPlayerView.h; sourceTree = "<group>"; };
+		BC38C3E22AF893B300ABFCC2 /* KSMergeAudioControlView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KSMergeAudioControlView.xib; sourceTree = "<group>"; };
+		BC38C3E32AF893B300ABFCC2 /* KSPlayerSliderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSPlayerSliderView.m; sourceTree = "<group>"; };
+		BC38C3E42AF893B300ABFCC2 /* KSPlayerSliderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KSPlayerSliderView.xib; sourceTree = "<group>"; };
+		BC38C3E52AF893B300ABFCC2 /* KSMergeAudioControlView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSMergeAudioControlView.m; sourceTree = "<group>"; };
+		BC38C3E62AF893B300ABFCC2 /* KSMediaMergeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSMediaMergeView.h; sourceTree = "<group>"; };
+		BC38C3E72AF893B300ABFCC2 /* KSAudioAnimationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSAudioAnimationView.m; sourceTree = "<group>"; };
+		BC38C3F22AF8B3B900ABFCC2 /* UIView+KSCovertImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+KSCovertImage.h"; sourceTree = "<group>"; };
+		BC38C3F32AF8B3BA00ABFCC2 /* UIView+KSCovertImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+KSCovertImage.m"; sourceTree = "<group>"; };
+		BC38C3F52AF8B3E000ABFCC2 /* KSNewAlertView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSNewAlertView.h; sourceTree = "<group>"; };
+		BC38C3F62AF8B3E000ABFCC2 /* KSNewAlertView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSNewAlertView.m; sourceTree = "<group>"; };
+		BC38C3F72AF8B3E000ABFCC2 /* KSNewAlertView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KSNewAlertView.xib; sourceTree = "<group>"; };
+		BC38C3FA2AF8B3F800ABFCC2 /* MusicPublistAlert.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MusicPublistAlert.xib; sourceTree = "<group>"; };
+		BC38C3FB2AF8B3F900ABFCC2 /* MusicPublistAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MusicPublistAlert.h; sourceTree = "<group>"; };
+		BC38C3FC2AF8B3F900ABFCC2 /* MusicPublistAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MusicPublistAlert.m; sourceTree = "<group>"; };
 		BC3A4EA928DAC0CD001C4428 /* ShareLiveDisplayView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShareLiveDisplayView.h; sourceTree = "<group>"; };
 		BC3A4EAA28DAC0CD001C4428 /* ShareLiveDisplayView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShareLiveDisplayView.m; sourceTree = "<group>"; };
 		BC3A4EAC28DAC0D6001C4428 /* ShareLiveDisplayView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareLiveDisplayView.xib; sourceTree = "<group>"; };
@@ -3621,6 +3667,7 @@
 		2779336127E3249C0010E277 /* Common */ = {
 			isa = PBXGroup;
 			children = (
+				BC38C3CC2AF892FB00ABFCC2 /* MediaMerge */,
 				2779336227E3249C0010E277 /* Tools */,
 				2779346A27E324A10010E277 /* Define */,
 				2779346E27E324A10010E277 /* ThirdPart */,
@@ -5157,6 +5204,82 @@
 			path = KulexiuForStudent/Module/Home/View/TeacherStyle/AnimationSource;
 			sourceTree = SOURCE_ROOT;
 		};
+		BC38C3CB2AF892D900ABFCC2 /* MusicPlayer */ = {
+			isa = PBXGroup;
+			children = (
+				BC38C3CD2AF8930D00ABFCC2 /* kSNewPlayer.h */,
+				BC38C3CE2AF8930D00ABFCC2 /* kSNewPlayer.m */,
+			);
+			path = MusicPlayer;
+			sourceTree = "<group>";
+		};
+		BC38C3CC2AF892FB00ABFCC2 /* MediaMerge */ = {
+			isa = PBXGroup;
+			children = (
+				BC38C3D42AF893B300ABFCC2 /* AudioMerge */,
+				BC38C3D02AF8936300ABFCC2 /* MediaEditor */,
+				BC38C3CB2AF892D900ABFCC2 /* MusicPlayer */,
+			);
+			path = MediaMerge;
+			sourceTree = "<group>";
+		};
+		BC38C3D02AF8936300ABFCC2 /* MediaEditor */ = {
+			isa = PBXGroup;
+			children = (
+				BC38C3D22AF8936300ABFCC2 /* KSMediaEditor.h */,
+				BC38C3D12AF8936300ABFCC2 /* KSMediaEditor.m */,
+			);
+			path = MediaEditor;
+			sourceTree = "<group>";
+		};
+		BC38C3D42AF893B300ABFCC2 /* AudioMerge */ = {
+			isa = PBXGroup;
+			children = (
+				BC38C3DE2AF893B300ABFCC2 /* AudioPlayAnimationView */,
+				BC38C3D62AF893B300ABFCC2 /* KSAudioAnimationView.h */,
+				BC38C3E72AF893B300ABFCC2 /* KSAudioAnimationView.m */,
+				BC38C3DD2AF893B300ABFCC2 /* KSAudioAnimationView.xib */,
+				BC38C3FB2AF8B3F900ABFCC2 /* MusicPublistAlert.h */,
+				BC38C3FC2AF8B3F900ABFCC2 /* MusicPublistAlert.m */,
+				BC38C3FA2AF8B3F800ABFCC2 /* MusicPublistAlert.xib */,
+				BC38C3F52AF8B3E000ABFCC2 /* KSNewAlertView.h */,
+				BC38C3F62AF8B3E000ABFCC2 /* KSNewAlertView.m */,
+				BC38C3F72AF8B3E000ABFCC2 /* KSNewAlertView.xib */,
+				BC38C3E62AF893B300ABFCC2 /* KSMediaMergeView.h */,
+				BC38C3D72AF893B300ABFCC2 /* KSMediaMergeView.m */,
+				BC38C3F22AF8B3B900ABFCC2 /* UIView+KSCovertImage.h */,
+				BC38C3F32AF8B3BA00ABFCC2 /* UIView+KSCovertImage.m */,
+				BC38C3D82AF893B300ABFCC2 /* KSMergeAudioControlView.h */,
+				BC38C3E52AF893B300ABFCC2 /* KSMergeAudioControlView.m */,
+				BC38C3E22AF893B300ABFCC2 /* KSMergeAudioControlView.xib */,
+				BC38C3D92AF893B300ABFCC2 /* KSPlayerSliderView.h */,
+				BC38C3E32AF893B300ABFCC2 /* KSPlayerSliderView.m */,
+				BC38C3E42AF893B300ABFCC2 /* KSPlayerSliderView.xib */,
+				BC38C3E12AF893B300ABFCC2 /* KSPlayerView.h */,
+				BC38C3D52AF893B300ABFCC2 /* KSPlayerView.m */,
+				BC38C3DA2AF893B300ABFCC2 /* VideoPlayerView */,
+			);
+			path = AudioMerge;
+			sourceTree = "<group>";
+		};
+		BC38C3DA2AF893B300ABFCC2 /* VideoPlayerView */ = {
+			isa = PBXGroup;
+			children = (
+				BC38C3DB2AF893B300ABFCC2 /* KSVideoPlayerView.h */,
+				BC38C3DC2AF893B300ABFCC2 /* KSVideoPlayerView.m */,
+			);
+			path = VideoPlayerView;
+			sourceTree = "<group>";
+		};
+		BC38C3DE2AF893B300ABFCC2 /* AudioPlayAnimationView */ = {
+			isa = PBXGroup;
+			children = (
+				BC38C3E02AF893B300ABFCC2 /* KSAudioPlayAnimationView.h */,
+				BC38C3DF2AF893B300ABFCC2 /* KSAudioPlayAnimationView.m */,
+			);
+			path = AudioPlayAnimationView;
+			sourceTree = "<group>";
+		};
 		BC3A4EA828DAC071001C4428 /* ShareLiveView */ = {
 			isa = PBXGroup;
 			children = (
@@ -6977,6 +7100,7 @@
 				BCC583CD28A9EC6400BAB4CF /* cloud_animation_4.png in Resources */,
 				2779355727E324A70010E277 /* KSPremissionAlert.xib in Resources */,
 				2723B5C327F157B100E0B90B /* ChatNavView.xib in Resources */,
+				BC38C3EB2AF893B400ABFCC2 /* KSAudioAnimationView.xib in Resources */,
 				BCC583BB28A9EC6400BAB4CF /* cloud_animation_27.png in Resources */,
 				BCCE95EF2AB05ED000AB6385 /* TenantHotAlbumView.xib in Resources */,
 				BCFE53F22812898700AD6786 /* HomeVideoCourseCell.xib in Resources */,
@@ -7083,6 +7207,7 @@
 				BC71D268288804CD0010F14B /* img_25.png in Resources */,
 				BC7E77112900F39700EB37AF /* TuningForkSettingView.xib in Resources */,
 				BCB5B30B2ABC1347005BF25D /* KSImageDisplayView.xib in Resources */,
+				BC38C3F92AF8B3E000ABFCC2 /* KSNewAlertView.xib in Resources */,
 				BCCE95D92AB0440400AB6385 /* singleMusic_Ani.json in Resources */,
 				BC36A50A2AB406AB002FEFB1 /* GroupOwnerTagView.xib in Resources */,
 				BCC583C928A9EC6400BAB4CF /* cloud_animation_11.png in Resources */,
@@ -7125,6 +7250,7 @@
 				BC27A06E280FF56C00F91E27 /* AccompanyEvaluateCell.xib in Resources */,
 				BC71D27B288804CD0010F14B /* img_21.png in Resources */,
 				BCFDA65628BCA2000022B497 /* accomapny_animation_1.png in Resources */,
+				BC38C3ED2AF893B400ABFCC2 /* KSMergeAudioControlView.xib in Resources */,
 				2723B66327F15CFC00E0B90B /* ModifyNameBodyView.xib in Resources */,
 				BC71D255288804CD0010F14B /* img_5.png in Resources */,
 				BCFEED6728F7E4910078A2B7 /* WidgetBottomButtonView.xib in Resources */,
@@ -7136,6 +7262,7 @@
 				BCC0F6662A8CD8F500C4EFA4 /* TXDanChatCell.xib in Resources */,
 				BCFDA65728BCA2000022B497 /* musicRoom_animation.json in Resources */,
 				BC8418412AC2D96C00D8F90E /* PasswordCheckBodyView.xib in Resources */,
+				BC38C3FD2AF8B3F900ABFCC2 /* MusicPublistAlert.xib in Resources */,
 				BCC0F6622A8CD8F500C4EFA4 /* MainDisplayView.xib in Resources */,
 				BC71D278288804CD0010F14B /* img_20.png in Resources */,
 				BC71D247288804CD0010F14B /* img_0.png in Resources */,
@@ -7149,6 +7276,7 @@
 				BCC583CE28A9EC6400BAB4CF /* cloud_animation_0.png in Resources */,
 				BCFE53ED2812897600AD6786 /* HomeLiveCouseCell.xib in Resources */,
 				BC71D256288804CD0010F14B /* img_14.png in Resources */,
+				BC38C3EF2AF893B400ABFCC2 /* KSPlayerSliderView.xib in Resources */,
 				275FA24527E73E0100CFEA2E /* InstrumentDescView.xib in Resources */,
 				BCC583B628A9EC6400BAB4CF /* cloud_animation_20.png in Resources */,
 				BC11922D280ED8E800A716F7 /* CourseNavView.xib in Resources */,
@@ -7462,6 +7590,7 @@
 				BC8B6DC92856CFB800866917 /* KSDocument.m in Sources */,
 				BC12639828FF8E0F00509E90 /* HomeNewMusicView.m in Sources */,
 				275FA1E927E7351900CFEA2E /* KSNetTypeManager.m in Sources */,
+				BC38C3F42AF8B3BA00ABFCC2 /* UIView+KSCovertImage.m in Sources */,
 				BC756CC128FE7D9500AA9ECB /* UserDetailNavView.m in Sources */,
 				2723B66B27F15CFC00E0B90B /* PhoneCheckBodyView.m in Sources */,
 				27F9033027E87C2E00C08A19 /* AudioRecordManager.m in Sources */,
@@ -7507,6 +7636,7 @@
 				BC83A83A2AD2825F0033D48B /* TenantNotiferMessageCell.m in Sources */,
 				277935A027E324A80010E277 /* UIView+TZLayout.m in Sources */,
 				BCA3DBBD29014B9800AE1C49 /* TunerForkManager.swift in Sources */,
+				BC38C3EC2AF893B400ABFCC2 /* KSAudioPlayAnimationView.m in Sources */,
 				2779351227E324A50010E277 /* KSNetworkAccessibleManager.m in Sources */,
 				BCCE95CC2AB03BE200AB6385 /* TenantDragView.m in Sources */,
 				2779357227E324A70010E277 /* SearchView.m in Sources */,
@@ -7548,6 +7678,7 @@
 				BC02381928685087005560CA /* LiveAnimationView.m in Sources */,
 				BC0D95112AC26E4000E54D3F /* UIImageView+DisplayImage.m in Sources */,
 				BCC0F6122A8CD89400C4EFA4 /* TXClassroomViewController.m in Sources */,
+				BC38C3D32AF8936300ABFCC2 /* KSMediaEditor.m in Sources */,
 				BC8A45AA283DC33400094BBB /* TrackChooseView.m in Sources */,
 				BCFEED4D28F7E4720078A2B7 /* SmallToolViewController.m in Sources */,
 				BC106C3A2A9338A7000759A9 /* TXSeatContainerView.m in Sources */,
@@ -7566,6 +7697,7 @@
 				275FA1EB27E7351900CFEA2E /* KSBaseWKWebViewController.m in Sources */,
 				BC106C232A933869000759A9 /* TXLiveMessageCenter.m in Sources */,
 				BCB909142852EF0000F5FF69 /* KSDragWindow.m in Sources */,
+				BC38C3CF2AF8930D00ABFCC2 /* kSNewPlayer.m in Sources */,
 				BC5F765029001C8D00B433E0 /* HomeDragButton.m in Sources */,
 				BC2888802A8102A20064B773 /* KSChatConversationViewController.m in Sources */,
 				2779353B27E324A60010E277 /* UrlDecode.m in Sources */,
@@ -7615,6 +7747,7 @@
 				BC8C2C5C2823F57100FBA5D5 /* KSAddressPickerView.m in Sources */,
 				BCFEED7328F7F17C0078A2B7 /* TuningNavView.m in Sources */,
 				2723B62727F157D500E0B90B /* GroupMemberViewController.m in Sources */,
+				BC38C3EE2AF893B400ABFCC2 /* KSPlayerSliderView.m in Sources */,
 				BC119216280ED6A900A716F7 /* MyLiveCourseCell.m in Sources */,
 				BC0D95392AC2AB0D00E54D3F /* PasswordModifyBodyView.m in Sources */,
 				2779353727E324A60010E277 /* UIImage+Color.m in Sources */,
@@ -7682,6 +7815,7 @@
 				277935B827E324A90010E277 /* FSCalendar.m in Sources */,
 				BC106C4D2A933E75000759A9 /* LiveRoomHeadView.m in Sources */,
 				BCD9295E28F9447B006793E4 /* WMGaugeViewStyle3D.m in Sources */,
+				BC38C3F82AF8B3E000ABFCC2 /* KSNewAlertView.m in Sources */,
 				275FA23527E7356B00CFEA2E /* UserInfoManager.m in Sources */,
 				2779353327E324A60010E277 /* UIScreen+Extend.m in Sources */,
 				BC106C132A933829000759A9 /* TXControlMemberMic.m in Sources */,
@@ -7749,6 +7883,7 @@
 				2779359327E324A80010E277 /* TZImageCropManager.m in Sources */,
 				BCCC361A2AAF0D77000D60CA /* INSSettingBodyView.m in Sources */,
 				2723B66727F15CFC00E0B90B /* ModifyBodyView.m in Sources */,
+				BC38C3FE2AF8B3F900ABFCC2 /* MusicPublistAlert.m in Sources */,
 				2779357727E324A70010E277 /* GRScanManager.m in Sources */,
 				277935D527E324A90010E277 /* ALCalendarManager.m in Sources */,
 				BC02130127FC6ADD0040569F /* UIView+SubViewExtension.m in Sources */,
@@ -7849,6 +7984,7 @@
 				BC119223280ED6F500A716F7 /* AccompanyLessonModel.m in Sources */,
 				277935B227E324A90010E277 /* UIView+MSSLayout.m in Sources */,
 				BCCC36272AAF4346000D60CA /* KSTenantHomeViewController.m in Sources */,
+				BC38C3EA2AF893B400ABFCC2 /* KSVideoPlayerView.m in Sources */,
 				BCFDA61228BC8FCE0022B497 /* HomeHotVideoCell.m in Sources */,
 				275FA1E427E7351900CFEA2E /* KSWebNavView.m in Sources */,
 				BC106C182A933829000759A9 /* TXLiveMessageShopRush.m in Sources */,
@@ -7907,6 +8043,7 @@
 				BC106C352A9338A7000759A9 /* TXUISeatMember.m in Sources */,
 				2779355427E324A70010E277 /* VoNetWorking.m in Sources */,
 				275FA1EE27E7351900CFEA2E /* KSBaseViewController.m in Sources */,
+				BC38C3F12AF893B400ABFCC2 /* KSAudioAnimationView.m in Sources */,
 				BCF425DD2AB8665200BCD942 /* TenantHomeBannerView.m in Sources */,
 				2723B63827F157D500E0B90B /* GroupApplyChooseCell.m in Sources */,
 				BC8B6DC62856CAF600866917 /* KSDocumentViewController.m in Sources */,
@@ -7937,6 +8074,7 @@
 				BC28887D2A8102890064B773 /* KSGroupConversationController.m in Sources */,
 				277935C027E324A90010E277 /* FSCalendarTransitionCoordinator.m in Sources */,
 				BC802DA328BC5F8D0079E350 /* HomeHotMusicCellView.m in Sources */,
+				BC38C3E92AF893B400ABFCC2 /* KSMediaMergeView.m in Sources */,
 				BC494A8028696BC300CCD343 /* MusicRoomListModel.m in Sources */,
 				BCB9FA4A286EDE1A005D766B /* BaseAlertView.m in Sources */,
 				2779355B27E324A70010E277 /* KSStatusView.m in Sources */,
@@ -8039,6 +8177,7 @@
 				BCB5B3092ABC1341005BF25D /* KSImageDisplayView.m in Sources */,
 				BC0D95312AC2AA9900E54D3F /* ForgetPasswordBodyView.m in Sources */,
 				BCC0F6712A8CD8F500C4EFA4 /* TXMainToolView.m in Sources */,
+				BC38C3F02AF893B400ABFCC2 /* KSMergeAudioControlView.m in Sources */,
 				BC7663172827E49900C91A1D /* NotiferMessageCell.m in Sources */,
 				BC8A45A7283DC33400094BBB /* CloudControlButton.m in Sources */,
 				BCC0F6C32A8CDDEB00C4EFA4 /* RoomMember.m in Sources */,
@@ -8129,6 +8268,7 @@
 				277935BC27E324A90010E277 /* FSCalendarExtensions.m in Sources */,
 				2779356227E324A70010E277 /* KSRecordPowerAnimationView.m in Sources */,
 				BCB9FA27286D799B005D766B /* KSScanViewController.m in Sources */,
+				BC38C3E82AF893B400ABFCC2 /* KSPlayerView.m in Sources */,
 				BCFE53FA28129A5600AD6786 /* TeacherStyleModel.m in Sources */,
 				2779355527E324A70010E277 /* DiskFreeSpaceManager.m in Sources */,
 				BCC0F65D2A8CD8F500C4EFA4 /* TXClassroomMainContainer.m in Sources */,

+ 6 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_left_bg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "align_left_bg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "align_left_bg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_left_bg.imageset/align_left_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_left_bg.imageset/align_left_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_right_bg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "align_right_bg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "align_right_bg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_right_bg.imageset/align_right_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/align_right_bg.imageset/align_right_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/aligningSlider_bg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "aligningSlider_bg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "aligningSlider_bg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/aligningSlider_bg.imageset/aligningSlider_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/aligningSlider_bg.imageset/aligningSlider_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/cancleMerge_image.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "cancleMerge_image@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "cancleMerge_image@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/cancleMerge_image.imageset/cancleMerge_image@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/cancleMerge_image.imageset/cancleMerge_image@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_bg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "disc_bg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "disc_bg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_bg.imageset/disc_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_bg.imageset/disc_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_dot.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "disc_dot@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "disc_dot@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_dot.imageset/disc_dot@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/disc_dot.imageset/disc_dot@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergeControl_show.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "mergeControl_show@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "mergeControl_show@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergeControl_show.imageset/mergeControl_show@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergeControl_show.imageset/mergeControl_show@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "mergePlayer_pause@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "mergePlayer_pause@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause.imageset/mergePlayer_pause@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause.imageset/mergePlayer_pause@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause_white.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "mergePlayer_pause_white@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "mergePlayer_pause_white@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause_white.imageset/mergePlayer_pause_white@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_pause_white.imageset/mergePlayer_pause_white@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "mergePlayer_play@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "mergePlayer_play@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play.imageset/mergePlayer_play@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play.imageset/mergePlayer_play@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play_white.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "mergePlayer_play_white@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "mergePlayer_play_white@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play_white.imageset/mergePlayer_play_white@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/mergePlayer_play_white.imageset/mergePlayer_play_white@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_music_bg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "merge_music_bg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "merge_music_bg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_music_bg.imageset/merge_music_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_music_bg.imageset/merge_music_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_next.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "merge_next@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "merge_next@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_next.imageset/merge_next@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_next.imageset/merge_next@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_playerBg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "merge_playerBg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "merge_playerBg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_playerBg.imageset/merge_playerBg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_playerBg.imageset/merge_playerBg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_upload.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "merge_upload@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "merge_upload@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_upload.imageset/merge_upload@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/merge_upload.imageset/merge_upload@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/music_coverBg.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "music_coverBg@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "music_coverBg@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/music_coverBg.imageset/music_coverBg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/music_coverBg.imageset/music_coverBg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/point_stick.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "point_stick@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "point_stick@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/point_stick.imageset/point_stick@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/point_stick.imageset/point_stick@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pointing_arrow.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "pointing_arrow@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "pointing_arrow@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pointing_arrow.imageset/pointing_arrow@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pointing_arrow.imageset/pointing_arrow@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pub_music_placeholder.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "pub_music_placeholder@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "pub_music_placeholder@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pub_music_placeholder.imageset/pub_music_placeholder@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/pub_music_placeholder.imageset/pub_music_placeholder@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/slider_bubble.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "slider_bubble@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "slider_bubble@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/slider_bubble.imageset/slider_bubble@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/slider_bubble.imageset/slider_bubble@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/stylus_image.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "stylus_image@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "stylus_image@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/stylus_image.imageset/stylus_image@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/stylus_image.imageset/stylus_image@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/sureButton_img.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "sureButton_img@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "sureButton_img@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/sureButton_img.imageset/sureButton_img@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/sureButton_img.imageset/sureButton_img@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/vinyl_image.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "vinyl_image@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "vinyl_image@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/vinyl_image.imageset/vinyl_image@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/PlayerImage/vinyl_image.imageset/vinyl_image@3x.png


+ 530 - 170
KulexiuForStudent/KulexiuForStudent/Common/Base/KSAccompanyWebViewController.m

@@ -21,7 +21,11 @@
 #define KSMidiSongFileKey (@"KSDownloadMidiSong")
 #import "KulexiuForStudent-swift.h"
 
-@interface KSAccompanyWebViewController ()<KSAQRecordManagerDelegate,KSAudioSessionManagerDelegate,PlayerEngineDelegate,TunerDelegate>
+#import "kSNewPlayer.h"
+#import "KSMediaEditor.h"
+#import "KSMediaMergeView.h"
+
+@interface KSAccompanyWebViewController ()<KSAQRecordManagerDelegate,KSAudioSessionManagerDelegate,PlayerEngineDelegate,TunerDelegate,kSNewPlayerManagerDelegate>
 
 @property (nonatomic, strong) KSAudioSessionManager *audioSessionManager;
 
@@ -75,6 +79,44 @@
 
 @property (nonatomic, strong) Tuner *tuner;
 
+// 延迟校准开始时发送 AdjustStart消息标识
+@property (nonatomic, assign) BOOL isDelayCheckStart;
+// 检测延迟播放器
+@property (nonatomic, strong) kSNewPlayer *dingPlayer;
+
+@property (nonatomic, strong) kSNewPlayer *dongPlayer;
+
+// 音频播放器
+@property (nonatomic, strong) kSNewPlayer *musicPlayer;
+// 录音开始时间
+@property (nonatomic, assign) NSTimeInterval recordStartTime;
+// 播放开始时间
+@property (nonatomic, assign) NSTimeInterval playerStartTime;
+// 播放延迟
+@property (nonatomic, assign) NSInteger offsetTime;
+
+
+@property (nonatomic, assign) BOOL dingPlayerReady;
+@property (nonatomic, assign) BOOL dongPlayerReady;
+
+@property (nonatomic, assign) BOOL musicPlayerReady;
+
+@property (nonatomic, strong) NSDictionary *playerParm;
+
+@property (nonatomic, strong) NSMutableArray *delayArray;
+
+@property (nonatomic, assign) NSInteger checkIndex;
+
+@property (nonatomic, assign) NSInteger firstFrequence;
+
+@property (nonatomic, assign) NSInteger secondFrequence;
+
+@property (nonatomic, strong) NSURL *bgAudioUrl;
+
+@property (nonatomic, strong) NSURL *recordUrl;
+
+@property (nonatomic, strong) NSString *accompanyUrl;
+
 @end
 
 @implementation KSAccompanyWebViewController
@@ -112,6 +154,72 @@
     }
 }
 
+- (void)resetPlayerConfig {
+    [self freeMp3Player]; // 若之前有播放器,剔除
+    self.firstFrequence = 800;
+    self.secondFrequence = 800;
+    self.dingPlayerReady = NO;
+    self.dongPlayerReady = NO;
+    self.musicPlayerReady = NO;
+    self.recordStartTime = 0;
+    self.playerStartTime = 0;
+    self.offsetTime = 0;
+}
+
+- (void)initMp3Player:(NSString *)musicUrl checkUrl:(NSString *)checkUrl {
+    [self resetPlayerConfig];
+    self.accompanyUrl = musicUrl;
+    [self.musicPlayer preparePlaySongWithUrl:musicUrl];
+    [self.dingPlayer preparePlaySongWithUrl:checkUrl];
+    [self.dongPlayer preparePlaySongWithUrl:checkUrl];
+    
+    [self prepareMusic];
+}
+
+- (void)prepareMusic {
+    
+    self.dingPlayer.isMute = YES;
+    [self.dingPlayer startPlay];
+    self.dongPlayer.isMute = YES;
+    [self.dongPlayer startPlay];
+    self.musicPlayer.isMute = YES;
+    [self.musicPlayer startPlay];
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        [self.musicPlayer puasePlay];
+        self.musicPlayer.isMute = NO;
+        [self.dingPlayer puasePlay];
+        self.dingPlayer.isMute = NO;
+        [self.dongPlayer puasePlay];
+        self.dongPlayer.isMute = NO;
+        [self configPlayerDelegate];
+    });
+}
+
+-  (void)freeMp3Player {
+    if (_dingPlayer) {
+        [self.dingPlayer freePlayer];
+    }
+    if (_dongPlayer) {
+        [self.dongPlayer freePlayer];
+    }
+    if (_musicPlayer) {
+        [self.musicPlayer freePlayer];
+    }
+}
+
+- (void)stopMp3Player {
+    if (_dingPlayer) {
+        [self.dingPlayer puasePlay];
+    }
+    if (_dongPlayer) {
+        [self.dongPlayer puasePlay];
+    }
+    if (_musicPlayer) {
+        [self.musicPlayer puasePlay];
+    }
+}
+
+
 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
@@ -120,11 +228,23 @@
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterForeground) name:@"appEnterForeground" object:nil];
 }
 
+- (void)checkMessage:(id)message {
+    NSDictionary *result = [message mj_JSONObject];
+    NSString *type = [[result ks_dictionaryValueForKey:@"header"] ks_stringValueForKey:@"type"];
+    NSString *commond = [[result ks_dictionaryValueForKey:@"header"] ks_stringValueForKey:@"commond"];
+    if ([type isEqualToString:@"DELAY_CHECK"] && [commond isEqualToString:@"recordEnd"]) {
+        NSDictionary *body = [result ks_dictionaryValueForKey:@"body"];
+        NSTimeInterval micDelay = [body ks_doubleValueForKey:@"firstNoteDelayDuration"] - self.offsetTime;
+        [self.delayArray addObject:[NSNumber numberWithDouble:micDelay]];
+    }
+}
+
 - (void)connectSocketService {
     
     MJWeakSelf;
     self.socketManager.didReceiveMessage = ^(id  _Nonnull message) {
         dispatch_async(dispatch_get_main_queue(), ^{
+            [weakSelf checkMessage:message];
             // api
             NSMutableDictionary *sendMessage = [NSMutableDictionary dictionary];
             [sendMessage setValue:@"sendResult" forKey:@"api"];
@@ -137,7 +257,7 @@
         dispatch_async(dispatch_get_main_queue(), ^{
             
             if (!isSuccess) {
-                NSLog(@"-----连接失败");
+                //                NSLog(@"-----连接失败");
                 if (weakSelf.hasSendStartMessage) {
                     if (weakSelf.AQManager.isRunning) {
                         NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
@@ -147,19 +267,19 @@
                         [weakSelf stopRecordService];
                         weakSelf.isCompareStart = NO;
                         weakSelf.isSoundCheckStart = NO;
+                        weakSelf.isDelayCheckStart = NO;
                     }
                 }
                 else if (weakSelf.evaluatParm) {
                     NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:weakSelf.evaluatParm];
-                    NSLog(@"%@",sendParm);
                     NSDictionary *valueDic = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
                     [valueDic setValue:@"服务异常,请重试" forKey:@"reson"];
-                    NSLog(@"%@",valueDic);
                     [sendParm setValue:valueDic forKey:@"content"];
                     [weakSelf postMessage:sendParm];
                     weakSelf.hasSendStartMessage = YES;
                     weakSelf.isCompareStart = NO;
                     weakSelf.isSoundCheckStart = NO;
+                    weakSelf.isDelayCheckStart = NO;
                 }
                 else { // 其他断开
                     if (weakSelf.AQManager.isRunning) {
@@ -170,19 +290,19 @@
                         [weakSelf stopRecordService];
                         weakSelf.isCompareStart = NO;
                         weakSelf.isSoundCheckStart = NO;
+                        weakSelf.isDelayCheckStart = NO;
                     }
                 }
             }
             else {
-                NSLog(@"-----连接成功");
+                //                NSLog(@"-----连接成功");
                 if (weakSelf.hasSendStartMessage == NO && weakSelf.evaluatParm) {
                     NSDictionary *content = [weakSelf.evaluatParm ks_dictionaryValueForKey:@"content"];
                     NSString *sendData = [weakSelf configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
                     [weakSelf sendDataToSocketService:sendData];
                     [weakSelf postMessage:weakSelf.evaluatParm];
                     weakSelf.hasSendStartMessage = YES;
-                    NSLog(@"---- send musicXML message");
-                }
+                    }
             }
         });
     };
@@ -217,11 +337,11 @@
         config.selectionGranularity = WKSelectionGranularityDynamic;
         config.allowsInlineMediaPlayback = YES;
         config.mediaTypesRequiringUserActionForPlayback = NO;
-
+        
         config.processPool = [KSBaseWKWebViewController singleWkProcessPool];
         config.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
         [self configUserAgent:config];
-
+        
         //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
         WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
         //这个类主要用来做native与JavaScript的交互管理
@@ -250,11 +370,8 @@
             make.top.mas_equalTo(self.view.mas_top);
             make.bottom.mas_equalTo(self.view.mas_bottom);
         }];
-        if (@available(iOS 11.0, *)) {
-            self.myWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
-        } else {
-            // Fallback on earlier versions
-        }
+        self.myWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
+        
         [self setupProgress];
         
         [self loadRequest];
@@ -303,6 +420,10 @@
         self.playerEngine = nil;
     }
     [self stopTuner];
+    BOOL isBack = [self isViewPopDismiss];
+    if (isBack) {
+        [self freeMp3Player];
+    }
 }
 
 - (void)sendDataToSocketService:(id)data {
@@ -320,53 +441,64 @@
         NSLog(@"---- receive parm %@", [parm mj_JSONString]);
         // 回到主线程
         dispatch_async(dispatch_get_main_queue(), ^{
-             if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startEvaluating"]) { // 开始评测
-                  [self configRecordManager];
-                  self.hasSendStartMessage = NO;
-                  PREMISSIONTYPE isOk = [RecordCheckManager checkPermissionShowAlert:NO showInView:nil];
-                  if (isOk == PREMISSIONTYPE_YES) {
-                       self.evaluatParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                       // 如果socket 连上了
-                       if (self.socketManager.socketReadyState == SR_OPEN) {
-                            NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                            NSString *sendData = [self configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
-                            [self sendDataToSocketService:sendData];
-                            [self postMessage:parm];
-                            self.hasSendStartMessage = YES;
-                       }
-                       else {
-                            [self connectSocketService];
-                       }
-                  }
-                  else {
-                       NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
-                                                  @"content" : @{@"reson":@"没有麦克风权限"}
-                       };
-                       [self postMessage:postParm];
-                  }
-             }
+            if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"createMusicPlayer"]) {
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                NSString *musicUrl = [content ks_stringValueForKey:@"musicSrc"];
+                NSString *checkUrl = [content ks_stringValueForKey:@"tuneSrc"];
+                self.playerParm = parm;
+                [self initMp3Player:musicUrl checkUrl:checkUrl];
+            }
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startEvaluating"]) { // 开始评测
+                [self configRecordManager];
+                self.hasSendStartMessage = NO;
+                PREMISSIONTYPE isOk = [RecordCheckManager checkPermissionShowAlert:NO showInView:nil];
+                if (isOk == PREMISSIONTYPE_YES) {
+                    self.evaluatParm = [NSMutableDictionary dictionaryWithDictionary:parm];
+                    // 如果socket 连上了
+                    if (self.socketManager.socketReadyState == SR_OPEN) {
+                        NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                        NSString *sendData = [self configDataCommond:@"musicXml" body:content type:@"SOUND_COMPARE"];
+                        [self sendDataToSocketService:sendData];
+                        [self postMessage:parm];
+                        self.hasSendStartMessage = YES;
+                    }
+                    else {
+                        [self connectSocketService];
+                    }
+                }
+                else {
+                    NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
+                                               @"content" : @{@"reson":@"没有麦克风权限"}
+                    };
+                    [self postMessage:postParm];
+                }
+            }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endEvaluating"]) {// 停止评测
-                 self.evaluatParm = nil;
-                 [self stopRecordService];
-                 [self postMessage:parm];
-                 [self sendEndMessage];
+                self.evaluatParm = nil;
+                [self stopRecordService];
+                [self postMessage:parm];
+                [self sendEndMessage];
+                [self stopMp3Player]; // 停止播放
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cancelEvaluating"]) { // 取消评测
-                 self.evaluatParm = nil;
-                 [self stopRecordService];
-                 [self postMessage:parm];
-                 [self sendEndMessage];
+                self.evaluatParm = nil;
+                [self stopRecordService];
+                [self postMessage:parm];
+                [self sendEndMessage];
+                [self stopMp3Player]; // 停止播放
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startRecording"]) { //  开始录制
-//                 self.recordParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                 [self postMessage:parm];
-                 self.isCompareStart = YES;
-                 [self startRecordService];
+                if (self.videoRecordManager) {
+                    [self.videoRecordManager clearVideoFile];
+                }
+                [self postMessage:parm];
+                self.isCompareStart = YES;
+                [self startRecordService];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endRecording"]) { // 停止录音
-                 self.recordParm = nil;
-                 [self stopRecordService];
-                 [self postMessage:parm];
+                self.recordParm = nil;
+                [self stopRecordService];
+                [self postMessage:parm];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"pauseRecording"]) {
                 [self puaseRecordService];
@@ -381,13 +513,13 @@
             }
             // 发送消息给socket service
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyMessage"]) {
-                 [self sendMessageToSocket:parm];
+                [self sendMessageToSocket:parm];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"proxyServiceMessage"]) {
-                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                 NSString *sendData = [content mj_JSONString];
-                 NSLog(@"proxyServiceMessage ------- %@",sendData);
-                 [self sendDataToSocketService:sendData];
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                NSString *sendData = [content mj_JSONString];
+                NSLog(@"proxyServiceMessage ------- %@",sendData);
+                [self sendDataToSocketService:sendData];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openCamera"]) { // 开启摄像头
                 PREMISSIONTYPE canOpenCamera = [RecordCheckManager checkCameraPremissionAvaiable:NO showInView:nil];
@@ -438,6 +570,7 @@
                 [self postMessage:parm];
                 self.isCompareStart = NO;
                 self.isSoundCheckStart = YES;
+                self.isDelayCheckStart = NO;
                 [self startRecordService];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endSoundCheck"]) {
@@ -450,19 +583,13 @@
                 NSMutableDictionary *sendParm = [NSMutableDictionary dictionaryWithDictionary:parm];
                 NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:[sendParm ks_dictionaryValueForKey:@"content"]];
                 if (self.videoRecordManager) {
+                    
                     MJWeakSelf;
-                    [self.videoRecordManager uploadRecordVideoSuccess:^(NSString * _Nonnull videoUrl) {
-                        [contentParm setValue:@"success" forKey:@"type"];
-                        [contentParm setValue:videoUrl forKey:@"filePath"];
-                        [contentParm setValue:@"上传成功" forKey:@"message"];
-                        [sendParm setValue:contentParm forKey:@"content"];
-                        
-                        [weakSelf postMessage:sendParm];
-                    } failure:^(NSString * _Nonnull desc) {
-                        [contentParm setValue:@"error" forKey:@"type"];
-                        [contentParm setValue:desc forKey:@"message"];
-                        [sendParm setValue:contentParm forKey:@"content"];
-                        [weakSelf postMessage:sendParm];
+                    [self.videoRecordManager saveVideoCallback:^(BOOL isSuccess, NSString * _Nullable message) {
+                        if (isSuccess) {
+                            [LOADING_MANAGER MBShowAUTOHidingInWindow:@"已保存到相册"];
+                            [weakSelf uploadVideoWithParm:contentParm sendParm:sendParm];
+                        }
                     }];
                 }
             }
@@ -470,15 +597,15 @@
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDetail"]) { // 初始化方法
                 /**
                  api: 'cloudDetail',
-                     content: {
-                         midi: '',
-                         denominator: 4,
-                         numerator: 4,
-                         // xml整体原始速度
-                         originalSpeed: 90,
-                         // 间隔(ms)
-                         interval: 50
-                     }
+                 content: {
+                 midi: '',
+                 denominator: 4,
+                 numerator: 4,
+                 // xml整体原始速度
+                 originalSpeed: 90,
+                 // 间隔(ms)
+                 interval: 50
+                 }
                  */
                 
                 [self configAudioSession];
@@ -510,16 +637,16 @@
                         
                     }];
                 }
-                        
+                
             }
-
+            
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudGetMediaStatus"]) { // 获取播放状态
-            /**
-             api: 'cloudGetMediaStatus',
+                /**
+                 api: 'cloudGetMediaStatus',
                  content: {
-                     status: 'init' | 'play' | 'suspend'
+                 status: 'init' | 'play' | 'suspend'
                  }
-             */
+                 */
                 NSString *engineStatus = @"init";
                 if (self.initEngineSuccess) {
                     if (self.isPlaying) {
@@ -539,18 +666,18 @@
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudPlay"]) { // 播放
                 /**
                  api: 'cloudPlay',
-                     content: {
-                         // 当前曲目id
-                         songID: 0,
-                         // xml整体原始速度
-                         originalSpeed: 90,
-                         // 当前选择速度
-                         speed: 90,
-                         // 开始时间(ms)
-                         startTime: 0
-                         // 播放频率
-                         hertz:440
-                     }
+                 content: {
+                 // 当前曲目id
+                 songID: 0,
+                 // xml整体原始速度
+                 originalSpeed: 90,
+                 // 当前选择速度
+                 speed: 90,
+                 // 开始时间(ms)
+                 startTime: 0
+                 // 播放频率
+                 hertz:440
+                 }
                  */
                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
                 self.currentSongId = [content ks_stringValueForKey:@"songID"];
@@ -571,26 +698,26 @@
                 [self postMessage:parm];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSuspend"]) { // 暂停
-                 /**
-                  api: 'cloudSuspend',
-                  content: {
-                  // 当前曲目id
-                  songID: 0,
-                  }
-                  */
-                 [self stopPlayAction];
-                 [self postMessage:parm];
-                 
+                /**
+                 api: 'cloudSuspend',
+                 content: {
+                 // 当前曲目id
+                 songID: 0,
+                 }
+                 */
+                [self stopPlayAction];
+                [self postMessage:parm];
+                
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudSetCurrentTime"]) { // // 跳转指定位置
                 /**
                  api: 'cloudSetCurrentTime',
-                     content: {
-                         // 当前曲目id
-                         songID: 0,
-                         // 指定位置时间(ms)
-                         currentTime: 0
-                     }
+                 content: {
+                 // 当前曲目id
+                 songID: 0,
+                 // 指定位置时间(ms)
+                 currentTime: 0
+                 }
                  */
                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
                 Float64 currentTime = [content ks_doubleValueForKey:@"currentTime"];
@@ -602,14 +729,14 @@
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudChangeSpeed"]) { // 调速 范围(45-270整数)
                 /**
                  api: 'cloudChangeSpeed',
-                     content: {
-                         // 当前曲目id
-                         songID: 0,
-                         // 调整速度
-                         speed: 90
-                         // xml整体原始速度
-                         originalSpeed: 90,
-                     }
+                 content: {
+                 // 当前曲目id
+                 songID: 0,
+                 // 调整速度
+                 speed: 90
+                 // xml整体原始速度
+                 originalSpeed: 90,
+                 }
                  */
                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
                 float speed = [content ks_floatValueForKey:@"speed"];
@@ -624,14 +751,14 @@
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudVolume"]) {
                 /**
                  api: 'cloudVolume',
-                     content: {
-                         parts: [
-                             {
-                                 name: '',
-                                 volume: 90,//0-100
-                             }
-                         ]
-                     }
+                 content: {
+                 parts: [
+                 {
+                 name: '',
+                 volume: 90,//0-100
+                 }
+                 ]
+                 }
                  */
                 NSLog(@"-cloudVolume -----%@",parm);
                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
@@ -647,15 +774,15 @@
                 [self.playerEngine getAllTrackVolume];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudMetronome"]) { // 节拍器播放
-                 self.metronomeParm = [NSMutableDictionary dictionaryWithDictionary:parm];
-                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                 NSInteger repeatCount = [content ks_integerValueForKey:@"repeat"];
-                 NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
-                 NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
-                 NSInteger supplement = [content ks_integerValueForKey:@"supplement"];
-                 NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
-                 self.beatType = [self getBeatTypeFromString:beatString];
-                 [self showBeatViewRepeatCount:repeatCount supplement:supplement];
+                self.metronomeParm = [NSMutableDictionary dictionaryWithDictionary:parm];
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                NSInteger repeatCount = [content ks_integerValueForKey:@"repeat"];
+                NSInteger denominator = [content ks_integerValueForKey:@"denominator"];
+                NSInteger numerator = [content ks_integerValueForKey:@"numerator"];
+                NSInteger supplement = [content ks_integerValueForKey:@"supplement"];
+                NSString *beatString = [NSString stringWithFormat:@"%zd/%zd", numerator,denominator];
+                self.beatType = [self getBeatTypeFromString:beatString];
+                [self showBeatViewRepeatCount:repeatCount supplement:supplement];
             }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudDestroy"]) { // 销毁播放器
                 self.initEngineSuccess = NO;
@@ -676,16 +803,6 @@
                     [self removeCustomLoadingView];
                 }
             }
-            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudToggleFollow"]) { // 跟音
-                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
-                NSString *status = [content ks_stringValueForKey:@"state"];
-                if ([status isEqualToString:@"start"]) { // 开始
-                    [self startTuner];
-                }
-                else if ([status isEqualToString:@"end"]) { // 结束
-                    [self stopTuner];
-                }
-            }
             else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudAccompanyMessage"]) { // 获取伴奏
                 NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
                 NSString *accompanyUrl = [content ks_stringValueForKey:@"accompanyUrl"];
@@ -701,7 +818,7 @@
                         [self downloadUrl:accompanyUrl success:^{
                             [weakSelf configVideoRecord:fileUrl];
                         } faliure:nil];
-
+                        
                     }
                 }
                 else {
@@ -709,6 +826,100 @@
                     [self configVideoRecord:nil];
                 }
             }
+            
+            // 跟音
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"cloudToggleFollow"]) {
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                NSString *status = [content ks_stringValueForKey:@"state"];
+                if ([status isEqualToString:@"start"]) { // 开始
+                    [self startTuner];
+                }
+                else if ([status isEqualToString:@"end"]) { // 结束
+                    [self stopTuner];
+                }
+            }
+            // 延迟检测
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"startTune"]) { // 延迟测试开始
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                NSInteger count = [content ks_integerValueForKey:@"count"];
+                if (count == 0) {
+                    [self.delayArray removeAllObjects];
+                    self.checkIndex = 0;
+                }
+                self.checkIndex += 1;
+                [self configRecordManager];
+                [self postMessage:parm];
+                self.isCompareStart = NO;
+                self.isSoundCheckStart = NO;
+                self.isDelayCheckStart = YES;
+                [self startRecordService];
+            }
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"endTune"]) { // 延迟测试结束
+                
+                self.isDelayCheckStart = NO;
+                [self stopRecordService];
+                [self postMessage:parm];
+                [self stopMp3Player];
+                [self sendAdjustEndMessage]; // 发送结束消息
+            }
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"finishTune"]) { // 延迟测试结束
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                
+                NSInteger errorCount = 0;
+                NSInteger totalDelay = 0;
+                for (NSInteger index = 0 ; index < self.delayArray.count; index++) {
+                    NSInteger micDelay = [self.delayArray[index] integerValue];
+                    if (micDelay > 10 && micDelay < 100) {
+                        totalDelay += micDelay;
+                    }
+                    else {
+                        errorCount++;
+                    }
+                }
+                NSMutableDictionary *contentParm = [NSMutableDictionary dictionaryWithDictionary:content];
+                if (errorCount > 1) { // 错误次数过多
+                    [contentParm setValue:@(NO) forKey:@"result"];
+                }
+                else {
+                    [contentParm setValue:@(YES) forKey:@"result"];
+                    NSInteger averageDelay = totalDelay / (self.delayArray.count - errorCount);
+                    UserDefaultSet([NSNumber numberWithDouble:averageDelay], @"micDelay");
+                }
+                NSMutableDictionary *sendParm =  [NSMutableDictionary dictionary];
+                [sendParm setValue:@"finishTune" forKey:@"api"];
+                [sendParm setValue:contentParm forKey:@"content"];
+                [self postMessage:sendParm];
+                [self.delayArray removeAllObjects];
+                self.checkIndex = 0;
+            }
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"getDeviceDelay"]) {
+                NSInteger micDelay = [UserDefaultObjectForKey(@"micDelay") integerValue];
+                NSMutableDictionary *content = [NSMutableDictionary dictionaryWithDictionary:[parm ks_dictionaryValueForKey:@"content"]];
+                [content setValue:[NSNumber numberWithInteger:micDelay] forKey:@"value"];
+                NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
+                [sendParm setValue:@"getDeviceDelay" forKey:@"api"];
+                [sendParm setValue:content forKey:@"content"];
+                [self postMessage:sendParm];
+            }
+            // 音视频合成
+            else if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"openAdjustRecording"]) {
+                KSMediaMergeView *mergeView = [[KSMediaMergeView alloc] init];
+                [self.view addSubview:mergeView];
+                [mergeView mas_makeConstraints:^(MASConstraintMaker *make) {
+                    make.left.right.top.bottom.mas_equalTo(self.view);
+                }];
+                NSDictionary *content = [parm ks_dictionaryValueForKey:@"content"];
+                mergeView.recordId = [content ks_stringValueForKey:@"recordId"];
+                mergeView.songName = [content ks_stringValueForKey:@"title"];
+                mergeView.coverImage = [content ks_stringValueForKey:@"coverImg"];
+                if (self.AQManager) {
+                    self.recordUrl = self.AQManager.audioUrl;
+                }
+                MJWeakSelf;
+                [mergeView configWithVideoUrl:self.videoRecordManager.videoFileURL bgAudioUrl:self.bgAudioUrl remoteBgUrl:self.accompanyUrl  recordUrl:self.recordUrl offsetTime:self.offsetTime mergeCallback:^{
+                    [weakSelf musicPublishCallBack:content];
+                }];
+            }
             else {
                 [super handleScriptMessageSource:parm];
             }
@@ -716,7 +927,29 @@
         });
     }
 }
-    
+
+- (void)musicPublishCallBack:(NSDictionary *)content {
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:@"hideComplexButton" forKey:@"api"];
+    [parm setValue:@{} forKey:@"content"];
+    [self postMessage:parm];
+}
+- (void)uploadVideoWithParm:(NSMutableDictionary *)contentParm sendParm:(NSMutableDictionary *)sendParm {
+    MJWeakSelf;
+    [self.videoRecordManager uploadRecordVideoSuccess:^(NSString * _Nonnull videoUrl) {
+        [contentParm setValue:@"success" forKey:@"type"];
+        [contentParm setValue:videoUrl forKey:@"filePath"];
+        [contentParm setValue:@"上传成功" forKey:@"message"];
+        [sendParm setValue:contentParm forKey:@"content"];
+        
+        [weakSelf postMessage:sendParm];
+    } failure:^(NSString * _Nonnull desc) {
+        [contentParm setValue:@"error" forKey:@"type"];
+        [contentParm setValue:desc forKey:@"message"];
+        [sendParm setValue:contentParm forKey:@"content"];
+        [weakSelf postMessage:sendParm];
+    }];
+}
 - (void)showAlertWithMessage:(NSString *)message type:(CHECKDEVICETYPE)deviceType {
     [KSPremissionAlert shareInstanceDisplayImage:deviceType message:message showInView:self.view cancel:^{
         
@@ -728,15 +961,15 @@
 
 - (void)openSettingView {
     [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
-
+    
 }
 
 - (void)downloadUrl:(NSString *)url success:(void(^)(void))success faliure:(void(^)(void))faliure {
-//    [LOADING_MANAGER showHUD];
+    
     [KSNetworkingManager downloadFileRequestWithFileUrl:url progress:^(int64_t bytesRead, int64_t totalBytes) {
         
     } success:^(NSURL * _Nonnull fileUrl) {
-//        [LOADING_MANAGER removeHUD];
+        
         if ([self saveAccompanyFileWithUrl:fileUrl accompanyUrl:url]) {
             success();
         }
@@ -774,11 +1007,11 @@
 }
 
 - (void)downloadMidiFile:(NSString *)midiUrl success:(void(^)(void))success faliure:(void(^)(void))faliure {
-//    [LOADING_MANAGER showHUD];
+    
     [KSNetworkingManager downloadFileRequestWithFileUrl:midiUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
         
     } success:^(NSURL * _Nonnull fileUrl) {
-//        [LOADING_MANAGER removeHUD];
+        
         if ([self saveMidiFileWithUrl:fileUrl midiUrl:midiUrl]) {
             success();
         }
@@ -898,6 +1131,28 @@
     });
 }
 
+- (void)sendAdjustEndMessage {
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        // 上传停止的信息 发送给服务端
+        NSString *endMessage = @"recordEnd";
+        NSString *endData = [self configDataCommond:endMessage body:nil type:@"DELAY_CHECK"];
+        [self sendDataToSocketService:endData];
+        self.isDelayCheckStart = NO;
+        NSLog(@"---- send adjust end message");
+    });
+}
+
+- (void)sendOffsetTimeToService {
+    // 上传停止的信息 发送给服务端
+    NSString *offsetMessage = @"audioPlayStart";
+    NSTimeInterval micDelay = [UserDefault(@"micDelay") doubleValue];
+    NSDictionary *dic = @{@"offsetTime" : [NSNumber numberWithInteger:self.offsetTime], @"micDelay": [NSNumber numberWithInteger:micDelay]};
+    NSString *endData = [self configDataCommond:offsetMessage body:dic type:@"SOUND_COMPARE"];
+    NSLog(@"------ %@", endData);
+    [self sendDataToSocketService:endData];
+    self.isCompareStart = NO;
+}
+
 #pragma mark-------- KSAQRecordManagerDelegate
 - (void)recordInterruption {
     NSDictionary *postParm = @{@"api" : @"cancelEvaluating",
@@ -969,11 +1224,6 @@
         NSString *startMessage = @"recordStart";
         NSString *startString = [self configDataCommond:startMessage body:nil type:@"SOUND_COMPARE"];
         [self sendDataToSocketService:startString];
-         
-//         // 发送开始录制的消息给H5
-//         if (self.recordParm) {
-//              [self postMessage:self.recordParm];
-//         }
     }
     else if (self.isSoundCheckStart) { // 校音开始
         
@@ -983,7 +1233,22 @@
         NSString *startString = [self configDataCommond:checkStartMessage body:nil type:@"PITCH_DETECTION"];
         [self sendDataToSocketService:startString];
     }
-//        NSLog(@"--------- send audio data length %d", length);
+    else if (self.isDelayCheckStart) {
+        NSLog(@"--------- send delay check start message");
+        _isDelayCheckStart = NO;
+        NSString *checkStartMessage = @"recordStart";
+        NSInteger frequence = 0.0f;
+        if (self.checkIndex % 2 == 0) {
+            frequence = self.secondFrequence;
+        }
+        else {
+            frequence = self.firstFrequence;
+        }
+        NSDictionary *parm = @{@"HZ" : @(frequence)};
+        NSString *startString = [self configDataCommond:checkStartMessage body:parm type:@"DELAY_CHECK"];
+        [self sendDataToSocketService:startString];
+    }
+    //        NSLog(@"--------- send audio data length %d", length);
     [self sendDataToSocketService:pushData];
 }
 
@@ -1137,7 +1402,6 @@
     [content setValue:@(currentTime*1000) forKey:@"currentTime"];
     [sendParm setValue:content forKey:@"content"];
     [self postMessage:sendParm];
-//     NSLog(@"------time source %@", content);
 }
 
 - (void)playEnd {
@@ -1285,14 +1549,110 @@
     return hasSaveFile;
 }
 
-/*
-#pragma mark - Navigation
+#pragma mark ---- player
+- (void)configPlayerDelegate {
+    self.dingPlayer.delegate = self;
+    self.dongPlayer.delegate = self;
+    self.musicPlayer.delegate = self;
+}
+- (kSNewPlayer *)dingPlayer {
+    if (!_dingPlayer) {
+        _dingPlayer = [[kSNewPlayer alloc] init];
+    }
+    return _dingPlayer;
+}
 
-// In a storyboard-based application, you will often want to do a little preparation before navigation
-- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
-    // Get the new view controller using [segue destinationViewController].
-    // Pass the selected object to the new view controller.
+- (kSNewPlayer *)dongPlayer {
+    if (!_dongPlayer) {
+        _dongPlayer = [[kSNewPlayer alloc] init];
+    }
+    return _dongPlayer;
+}
+
+- (kSNewPlayer *)musicPlayer {
+    if (!_musicPlayer) {
+        _musicPlayer = [[kSNewPlayer alloc] init];
+    }
+    return _musicPlayer;
 }
-*/
+
+#pragma mark ---- new player delegate
+- (void)getSongCurrentTime:(NSInteger)currentTime andTotalTime:(NSInteger)totalTime andProgress:(CGFloat)progress currentInterval:(NSTimeInterval)currentInterval playTime:(NSTimeInterval)playTime inPlayer:(kSNewPlayer *)player {
+    
+    if (player == self.dingPlayer || player == self.dongPlayer) {
+        if (playTime >= 300 && playTime < 310 && self.recordStartTime > 0) {
+            NSLog(@" --- check player start play time %f", currentInterval*1000 - playTime);
+            self.playerStartTime = currentInterval*1000 - playTime;
+            self.offsetTime = self.playerStartTime - self.recordStartTime;
+            NSLog(@"--------- check player offset time -- %zd", self.offsetTime);
+        }
+    }
+    
+    if (playTime >= 300 && playTime < 310 && player == self.musicPlayer) {
+        if (self.recordStartTime > 0) {
+            NSLog(@" --- music player start play time %f", currentInterval*1000 - playTime);
+            self.playerStartTime = currentInterval*1000 - playTime;
+            self.offsetTime = self.playerStartTime - self.recordStartTime;
+            NSLog(@"--------- music play offset time -- %zd", self.offsetTime);
+            [self sendOffsetTimeToService];
+        }
+        NSLog(@"------- record start time %f", self.recordStartTime);
+    }
+    
+    // 回调进度
+    if (player == self.musicPlayer) {
+        //          NSLog(@"------ music play progress - %f", progress);
+        // 回调进度
+        NSDictionary *parm = @{
+            @"api" : @"playProgress",
+            @"content" : @{@"currentTime" : [NSNumber numberWithInteger:currentTime],
+                           @"totalDuration" : [NSNumber numberWithInteger:totalTime],
+            }
+        };
+        //          NSLog(@" -----music play progress  %@---- ", parm);
+        [self postMessage:parm];
+    }
+}
+
+- (void)sendPlayerReadyMsg {
+    if (self.playerParm) {
+        [self postMessage:self.playerParm];
+    }
+}
+
+- (void)preparePlay:(kSNewPlayer *)player {
+    if (player == self.musicPlayer) {
+        self.musicPlayerReady = YES;
+    }
+    else if (player == self.dingPlayer) {
+        self.dingPlayerReady = YES;
+        
+    }
+    else if (player == self.dongPlayer) {
+        self.dongPlayerReady = YES;
+    }
+    // 如果都准备好
+    if (self.musicPlayerReady && self.dingPlayerReady && self.dongPlayerReady) {
+        [self sendPlayerReadyMsg];
+    }
+}
+
+
+- (NSMutableArray *)delayArray {
+    if (!_delayArray) {
+        _delayArray = [NSMutableArray array];
+    }
+    return _delayArray;
+}
+
+/*
+ #pragma mark - Navigation
+ 
+ // In a storyboard-based application, you will often want to do a little preparation before navigation
+ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
+ // Get the new view controller using [segue destinationViewController].
+ // Pass the selected object to the new view controller.
+ }
+ */
 
 @end

+ 4 - 2
KulexiuForStudent/KulexiuForStudent/Common/Base/KSBaseWKWebViewController.m

@@ -1284,8 +1284,10 @@ typedef NS_ENUM(NSInteger, CHOOSETYPE) {
         [LOADING_MANAGER showHUD];
         NSString *suffix = [NSString stringWithFormat:@".%@",[[fileName componentsSeparatedByString:@"."] lastObject]];
         NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:fileUrl]];
-        [[KSUploadManager shareInstance] configBucketName:self.bucketName];
-        [[KSUploadManager shareInstance] uploadFile:fileData fileName:@"file" fileSuffix:suffix successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
+        [UPLOAD_MANAGER configBucketName:self.bucketName];
+        [UPLOAD_MANAGER uploadFile:fileData fileName:@"file" fileSuffix:suffix progress:^(int64_t bytesWritten, int64_t totalBytes) {
+            
+        } successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
             [LOADING_MANAGER removeHUD];
             NSString *fileUrl = [fileUrlArray lastObject];
             if (self.chooseFileParm) { // 回调

+ 36 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.h

@@ -958,6 +958,42 @@ NS_ASSUME_NONNULL_BEGIN
 /// @param success 成功
 /// @param faliure 失败
 + (void)setPasswordRequest:(NSString *)post password:(NSString *)password code:(NSString *)code success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
+
+
+// /userMusic/save
+
+/// 保存草稿、发布作品
+/// @param post post
+/// @param jsonConfig json参数
+/// @param img 图片url
+/// @param videoUrl 附件地址
+/// @param accompanyUrl 伴奏地址
+/// @param desc 描述
+/// @param type 草稿 DRAFT 作品FORMAL,
+/// @param musicPracticeRecordId 云教练id
+/// @param success 成功
+/// @param faliure 失败
++ (void)saveMusicMessage:(NSString *)post jsonConfig:(NSString *)jsonConfig img:(NSString *)img videoUrl:(NSString *)videoUrl accompanyUrl:(NSString *)accompanyUrl desc:(NSString *)desc type:(NSString *)type musicPracticeRecordId:(NSString *)musicPracticeRecordId success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
+
+// /userMusic/page
+
+/// 我的作品分页查询
+/// @param post post json
+/// @param type 草稿 DRAFT 作品FORMAL
+/// @param page 分页
+/// @param rows 条数
+/// @param success 成功
+/// @param faliure 失败
++ (void)userMusicPageRequest:(NSString *)post type:(NSString *)type page:(NSInteger)page rows:(NSInteger)rows success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
+
+// /userMusic/remove
+
+/// 作品删除
+/// @param post post form
+/// @param musicId 作品id
+/// @param success 成功
+/// @param faliure 失败
++ (void)userMusicRemoveRequest:(NSString *)post musicId:(NSString *)musicId success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure;
 @end
 
 NS_ASSUME_NONNULL_END

+ 62 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSNetworkingManager.m

@@ -1844,4 +1844,66 @@
     NSString *url = [NSString stringWithFormat:@"%@%@",hostURL, @"/api-auth/user/setPassword"];
     [self request:post andWithUrl:url and:parm success:success faliure:faliure];
 }
+
+// edu-app/userMusic/save
+
+/// 保存草稿、发布作品
+/// @param post post
+/// @param jsonConfig json参数
+/// @param img 图片url
+/// @param videoUrl 附件地址
+/// @param accompanyUrl 伴奏地址
+/// @param desc 描述
+/// @param type 草稿 DRAFT 作品FORMAL,
+/// @param musicPracticeRecordId 云教练id
+/// @param success 成功
+/// @param faliure 失败
++ (void)saveMusicMessage:(NSString *)post jsonConfig:(NSString *)jsonConfig img:(NSString *)img videoUrl:(NSString *)videoUrl accompanyUrl:(NSString *)accompanyUrl desc:(NSString *)desc type:(NSString *)type musicPracticeRecordId:(NSString *)musicPracticeRecordId success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure {
+    [self configRequestMethodJSON];
+    NSString *url = [NSString stringWithFormat:@"%@%@", hostURL, @"api-student/userMusic/save"];
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:jsonConfig forKey:@"jsonConfig"];
+    [parm setValue:img forKey:@"img"];
+    [parm setValue:videoUrl forKey:@"videoUrl"];
+    [parm setValue:accompanyUrl forKey:@"accompanyUrl"];
+    [parm setValue:desc forKey:@"desc"];
+    [parm setValue:type forKey:@"type"];
+    [parm setValue:musicPracticeRecordId forKey:@"musicPracticeRecordId"];
+    
+    [self request:post andWithUrl:url and:parm success:success faliure:faliure];
+}
+
+// edu-app/userMusic/page
+
+/// 我的作品分页查询
+/// @param post post
+/// @param type 草稿 DRAFT 作品FORMAL
+/// @param page 分页
+/// @param rows 条数
+/// @param success 成功
+/// @param faliure 失败
++ (void)userMusicPageRequest:(NSString *)post type:(NSString *)type page:(NSInteger)page rows:(NSInteger)rows success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure {
+    [self configRequestMethodJSON];
+    NSString *url = [NSString stringWithFormat:@"%@%@", hostURL, @"api-student/userMusic/page"];
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:@(page) forKey:@"page"];
+    [parm setValue:@(rows) forKey:@"rows"];
+    [parm setValue:type forKey:@"type"];
+    [self request:post andWithUrl:url and:parm success:success faliure:faliure];
+}
+
+// edu-app/userMusic/remove
+
+/// 作品删除
+/// @param post post form
+/// @param musicId 作品id
+/// @param success 成功
+/// @param faliure 失败
++ (void)userMusicRemoveRequest:(NSString *)post musicId:(NSString *)musicId success:(void(^)(NSDictionary *dic))success faliure:(void(^)(NSError *error))faliure {
+    [self configRequestMethodForm];
+    NSString *url = [NSString stringWithFormat:@"%@%@", hostURL, @"api-student/userMusic/remove"];
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:musicId forKey:@"id"];
+    [self request:post andWithUrl:url and:parm success:success faliure:faliure];
+}
 @end

+ 3 - 1
KulexiuForStudent/KulexiuForStudent/Common/Base/KSUploadManager.h

@@ -11,6 +11,8 @@ typedef void(^KSUploadSuccess)(NSMutableArray * _Nonnull fileUrlArray);
 typedef void(^KSUploadFailer)(NSError * _Nullable error, NSString * _Nullable descMessaeg);
 typedef void(^KSUploadProgress)(int64_t bytesWritten,int64_t totalBytes);
 
+#define UPLOAD_MANAGER ([KSUploadManager shareInstance])
+
 NS_ASSUME_NONNULL_BEGIN
 
 @interface KSUploadManager : NSObject
@@ -25,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (void)videoUpload:(NSData *)fileData fileName:(NSString *)fileName fileSuffix:(NSString *)fileSuffix progress:(KSUploadProgress)uploadProgress successCallback:(KSUploadSuccess)success faliure:(KSUploadFailer)faliure;
 
-- (void)uploadFile:(NSData *)fileData fileName:(NSString *)fileName fileSuffix:(NSString *)fileSuffix successCallback:(KSUploadSuccess)success faliure:(KSUploadFailer)faliure;
+- (void)uploadFile:(NSData *)fileData fileName:(NSString *)fileName fileSuffix:(NSString *)fileSuffix progress:(KSUploadProgress)uploadProgress successCallback:(KSUploadSuccess)success faliure:(KSUploadFailer)faliure;
 
 @end
 

+ 6 - 57
KulexiuForStudent/KulexiuForStudent/Common/Base/KSUploadManager.m

@@ -6,11 +6,10 @@
 //
 
 #import "KSUploadManager.h"
-#import <KS3YunSDK.h>
 #import "NSDate+Extension.h"
 #import <QCloudCOSXML/QCloudCOSXMLTransfer.h>
 
-@interface KSUploadManager ()<KingSoftServiceRequestDelegate,QCloudSignatureProvider>
+@interface KSUploadManager ()<QCloudSignatureProvider>
 
 @property (nonatomic, copy) KSUploadSuccess successCallback;
 
@@ -22,15 +21,6 @@
 
 @property (assign, nonatomic) long long fileSize;
 
-// 上传位置全路径
-@property (nonatomic, strong) NSString *uploadBucket;
-
-// 访问位置全路径
-@property (nonatomic, strong) NSString *receiveBucket;
-
-// bucket
-@property (nonatomic, strong) NSString *bucketName;
-
 // 腾讯cos bucket name
 @property (nonatomic, strong) NSString *cosBucketName;
 
@@ -66,21 +56,10 @@
 }
 
 - (void)configBucketName:(NSString *)bucketName {
-    if ([NSString isEmptyString:bucketName]) {
-        bucketName = @"daya";
-    }
-    self.bucketName = bucketName;
-    self.uploadBucket = [NSString stringWithFormat:@"%@.ks3-cn-beijing.ksyuncs.com",bucketName];
-    self.receiveBucket = [NSString stringWithFormat:@"%@.ks3-cn-beijing.ksyuncs.com",bucketName];
-    [[KS3Client initialize] setBucketDomain:self.uploadBucket];
+
 }
 
 - (void)configCilentBucket {
-    self.bucketName = @"daya";
-    self.uploadBucket = @"daya.ks3-cn-beijing.ksyuncs.com";
-    self.receiveBucket = @"daya.ks3-cn-beijing.ksyuncs.com";
-    [[KS3Client initialize] setBucketDomain:self.uploadBucket];
-    
     self.cosBucketName = @"daya-online-1303457149";
 }
 
@@ -222,13 +201,16 @@
     }];
 }
 
-- (void)uploadFile:(NSData *)fileData fileName:(NSString *)fileName fileSuffix:(NSString *)fileSuffix successCallback:(KSUploadSuccess)success faliure:(KSUploadFailer)faliure {
+- (void)uploadFile:(NSData *)fileData fileName:(NSString *)fileName fileSuffix:(NSString *)fileSuffix progress:(KSUploadProgress)uploadProgress successCallback:(KSUploadSuccess)success faliure:(KSUploadFailer)faliure {
     if (success) {
         self.successCallback = success;
     }
     if (faliure) {
         self.faliureCallback = faliure;
     }
+    if (uploadProgress) {
+        self.uploadProgress = uploadProgress;
+    }
     NSString *folderString = [self getDayFolder];
     NSString *uploadFileName = [NSString stringWithFormat:@"%@%@%@_%@%@%@",FILE_DOMAIN,folderString,[NSDate getCurrentTimestampFor64],UserDefault(UIDKey), fileName,fileSuffix];
     
@@ -249,39 +231,6 @@
     }];
 }
 
-- (void)request:(KS3ServiceRequest *)request didSendData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten totalBytesExpectedToWrite:(long long)totalBytesExpectedToWrite {
-    if ([request isKindOfClass:[KS3PutObjectRequest class]]) {
-        
-        long long alreadyTotalWriten = totalBytesWritten;
-        double progress = alreadyTotalWriten * 1.0  / _fileSize;
-        NSLog(@"upload progress: %f", progress);
-        if (self.uploadProgress) {
-            self.uploadProgress(bytesWritten, _fileSize);
-        }
-    }
-}
-- (void)request:(KS3Request *)request didReceiveResponse:(NSURLResponse *)response {
-    NSInteger statusCode = ((NSHTTPURLResponse*) response).statusCode;
-    if ( (statusCode>= 200 && statusCode <300) || statusCode == 304) {
-        NSLog(@"Put object success");
-        NSMutableArray *fileUrlArray = [NSMutableArray array];
-        [fileUrlArray addObject:self.videoLinkUrl];
-        self.successCallback(fileUrlArray);
-    }
-    else {
-        NSLog(@"Put object failed");
-        if (self.faliureCallback) {
-            self.faliureCallback(nil, @"上传文件失败");
-        }
-    }
-    
-}
-
-- (void)request:(KS3ServiceRequest *)request didFailWithError:(NSError *)error {
-    if (self.faliureCallback) {
-        self.faliureCallback(nil, @"上传文件失败");
-    }
-}
 
 - (void)uploadFileToOssWithSource:(NSString *)fileName fileData:(NSData *)fileData progress:(KSUploadProgress)uploadProgress successCallback:(void(^)(NSString *fileUrl))success faliure:(void(^)(void))faliure {
     self.cosFileName = fileName;

+ 7 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/KSVideoRecordManager.h

@@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property (nonatomic, assign) BOOL skipSaveRecord;
 
+@property (nonatomic, strong, readonly) NSURL *videoFileURL;
+
+
 - (BOOL)getSessionStatusisActive;
 
 - (instancetype)initSessionRecordCallback:(KSVideoRecordCallback)callback;
@@ -38,8 +41,12 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (void)stopRecord;
 
+- (void)saveVideoCallback:(KSVideoRecordCallback)callback;
+
 - (void)uploadRecordVideoSuccess:(void(^)(NSString *videoUrl))success failure:(void(^)(NSString *desc))faliure;
 
+- (void)clearVideoFile;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 41 - 28
KulexiuForStudent/KulexiuForStudent/Common/Base/KSVideoRecordManager.m

@@ -11,6 +11,7 @@
 #import <AssetsLibrary/AssetsLibrary.h>
 #import "TZImageManager.h"
 #import "KSVideoEditor.h"
+#import "KSUploadManager.h"
 
 @interface KSVideoRecordManager ()<AVCaptureFileOutputRecordingDelegate>
 
@@ -89,7 +90,7 @@
     AVCaptureDevice *videoCaptureDevice = [self cameraDeviceWithPosition:(AVCaptureDevicePositionFront)];
     if (!videoCaptureDevice) {
         if (self.callback) {
-            self.callback(NO, @"获取后置摄像头失败!");
+            self.callback(NO, @"获取摄像头失败!");
         }
     }
     _videoCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoCaptureDevice error:&error];
@@ -104,7 +105,7 @@
     }
     else {
         if (self.callback) {
-            self.callback(NO, @"无法添加视频输入对象");
+            self.callback(NO, @"摄像头被占用!");
         }
     }
     if (_ignoreAudio == NO) {
@@ -123,7 +124,7 @@
         }
         else {
             if (self.callback) {
-                self.callback(NO, @"无法添加音频输入对象!");
+                self.callback(NO, @"麦克风被占用!");
             }
         }
     }
@@ -185,7 +186,7 @@
         }
         else {
             if (self.callback) {
-                self.callback(NO, @"无法添加音频输入对象");
+                self.callback(NO, @"麦克风被占用!");
             }
         }
         
@@ -303,20 +304,28 @@
 
     // 暂时存储文件地址
     self.videoFileURL = outputFileURL;
+    [self resetSession];
+    self.skipSaveRecord = NO;
+}
+
+- (void)saveVideoCallback:(KSVideoRecordCallback)callback {
     // 保存文件
     if (self.skipSaveRecord == NO) {
         if (_ignoreAudio == NO) {
-            [self saveVideoToAsset:self.videoFileURL];
+            [self saveVideoToAsset:self.videoFileURL callback:^(BOOL isSuccess, NSString * _Nullable message) {
+                callback(isSuccess, message);
+            }];
         }
         else {
-            [self addBackgroundMuisc:self.audioUrl];
+            [self addBackgroundMuisc:self.audioUrl callback:^(BOOL isSuccess, NSString * _Nullable message) {
+                callback(isSuccess, message);
+            }];
         }
     }
-    self.skipSaveRecord = NO;
 }
 
 // 生成文件 合并音轨
-- (void)addBackgroundMuisc:(NSURL *)audioUrl {
+- (void)addBackgroundMuisc:(NSURL *)audioUrl callback:(KSVideoRecordCallback)callback {
     AVURLAsset* audioAsset =[AVURLAsset URLAssetWithURL:audioUrl options:nil];
     
     CMTime audioDuration = audioAsset.duration;
@@ -326,7 +335,11 @@
     NSLog(@"%f",audioDurationSeconds);
     [KSVideoEditor addBackgroundMiusicWithVideoUrlStr:self.videoFileURL audioUrl:audioUrl bgAudioUrl:self.bgAudioUrl start:0 end:audioDurationSeconds isOriginalSound:NO oriVolume:0 newVolume:100 completion:^(NSString * _Nonnull outPath, BOOL isSuccess) {
         if (isSuccess) {
-            [self saveVideoToAsset:[NSURL fileURLWithPath:outPath]];
+            [self saveVideoToAsset:[NSURL fileURLWithPath:outPath] callback:^(BOOL isSuccess, NSString * _Nullable message) {
+                if (callback) {
+                    callback(isSuccess, message);
+                }
+            }];
         }
         else {
             
@@ -337,20 +350,19 @@
 
 
 // 保存到相册
-- (void)saveVideoToAsset:(NSURL *)videoUrl {
+- (void)saveVideoToAsset:(NSURL *)videoUrl callback:(KSVideoRecordCallback)callback {
     [LOADING_MANAGER MBShowInWindow:@"视频处理中..."];
     [[TZImageManager manager] saveVideoWithUrl:videoUrl completion:^(PHAsset *asset, NSError *error) {
         if (!error) {
             self.videoAsset = asset;
             dispatch_main_async_safe(^{
                 [LOADING_MANAGER removeHUD];
-                if (self.callback) {
-                    self.callback(YES, @"保存成功");
+                if (callback) {
+                    callback(YES, @"已保存到相册");
+                }
+                else if (self.callback) {
+                    self.callback(YES, @"已保存到相册");
                 }
-                
-                // 删除文件
-                [self removeVideoWithPath:self.videoFileURL.path];
-                [self removeVideoWithPath:videoUrl.path];
                 // 重置
                 [self resetSession];
                 
@@ -359,12 +371,12 @@
         else {
             dispatch_main_async_safe(^{
                 [LOADING_MANAGER removeHUD];
-                if (self.callback) {
+                if (callback) {
+                    callback(NO, @"保存视频错误");
+                }
+                else if (self.callback) {
                     self.callback(NO, @"保存视频错误");
                 }
-                // 删除文件
-                [self removeVideoWithPath:self.videoFileURL.path];
-                [self removeVideoWithPath:videoUrl.path];
                 // 重置
                 [self resetSession];
                 
@@ -373,16 +385,19 @@
     }];
 }
 
+- (void)clearVideoFile {
+    if (self.videoFileURL) {
+        [self removeVideoWithPath:self.videoFileURL.path];
+        self.videoFileURL = nil;
+    }
+}
+
 // 上传视频
 - (void)uploadRecordVideoSuccess:(void (^)(NSString * _Nonnull))success failure:(void (^)(NSString * _Nonnull))faliure {
     if (self.videoAsset) {
-        dispatch_main_async_safe(^{
-            [LOADING_MANAGER MBShowInWindow:@"视频导出中..."];
-        });
+        
         [[TZImageManager manager] getVideoOutputPathWithAsset:self.videoAsset presetName:self.presentName success:^(NSString *outputPath) {
-            dispatch_main_async_safe(^{
-                [LOADING_MANAGER removeHUD];
-            });
+            
             NSLog(@"视频导出到本地完成,沙盒路径为:%@",outputPath);
             NSData *outputData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:outputPath]]; //压缩后的视频
             NSLog(@"导出后的视频:%@",[NSString stringWithFormat:@"%.2fM",(CGFloat)outputData.length/(1024*1024)]);
@@ -393,11 +408,9 @@
             
         } failure:^(NSString *errorMessage, NSError *error) {
             dispatch_main_async_safe(^{
-                [LOADING_MANAGER removeHUD];
                 faliure(@"视频导出失败");
             });
             NSLog(@"视频导出失败:%@,error:%@",errorMessage, error);
-            
         }];
     }
     else {

+ 14 - 0
KulexiuForStudent/KulexiuForStudent/Common/Base/LoadingManager/KSHudLoagingManager.h

@@ -6,6 +6,8 @@
 //
 
 #import <Foundation/Foundation.h>
+#import <MBProgressHUD/MBProgressHUD.h>
+#import "KSCustomLoadingView.h"
 
 #define LOADING_MANAGER ([KSHudLoagingManager shareInstance])
 
@@ -13,18 +15,30 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface KSHudLoagingManager : NSObject
 
+@property (nonatomic, strong) MBProgressHUD *HUD;
+
+@property (nonatomic, strong) KSCustomLoadingView *loadingView;
+
 + (instancetype)shareInstance;
 
+- (void)showCustomLoading:(NSString *)text;
+
+- (void)removeCustomLoading;
+
 - (void)showHUD;
 
 - (void)removeHUD;
 
+- (void)removeHUDNoDelay;
+
 - (void)MBPShow:(NSString*)str inView:(UIView *)displayView;
 
 - (void)MBShowAUTOHidingInWindow:(NSString *)str;
 
 - (void)MBShowInWindow:(NSString *)str;
 
+- (MBProgressHUD *)MBPShowLoadingHubWithText:(NSString *)text;
+
 // 提示后续操作
 - (void)KSShowMsg:(NSString *)message inView:(UIView *)displayView promptCompletion:(void(^)(void))promptCompletion;
 // 提示后续操作

+ 27 - 9
KulexiuForStudent/KulexiuForStudent/Common/Base/LoadingManager/KSHudLoagingManager.m

@@ -6,14 +6,12 @@
 //
 
 #import "KSHudLoagingManager.h"
-#import <MBProgressHUD/MBProgressHUD.h>
 #import "UIView+Hints.h"
+
 #define PROMPT_TIME  1.5f
 
 @interface KSHudLoagingManager ()
 
-@property (nonatomic, strong) MBProgressHUD *HUD;
-
 @end
 
 @implementation KSHudLoagingManager
@@ -28,6 +26,16 @@
     return manager;
 }
 
+- (void)showCustomLoading:(NSString *)text {
+    self.loadingView.displayText = text;
+    dispatch_main_async_safe(^{
+        [self.loadingView showLoadingView];
+    });
+}
+
+- (void)removeCustomLoading {
+    [self.loadingView hideLoadingView];
+}
 - (void)showHUD {
     dispatch_main_async_safe(^{
         [self removeLoadingView];
@@ -49,12 +57,17 @@
     });
 }
 
+- (void)removeHUDNoDelay {
+    [self removeLoadingView];
+}
+
 - (void)removeLoadingView {
-    
-    if (self.HUD) {
-        [self.HUD removeFromSuperview];
-    }
-    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+    dispatch_main_async_safe(^{
+        if (self.HUD) {
+            [self.HUD removeFromSuperview];
+        }
+        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+    });
 }
 
 - (void)MBPShow:(NSString *)str inView:(UIView *)displayView {
@@ -112,7 +125,12 @@
             promptCompletion();
         });
     });
-    
 }
 
+- (KSCustomLoadingView *)loadingView {
+    if (!_loadingView) {
+        _loadingView = [KSCustomLoadingView shareInstance];
+    }
+    return _loadingView;
+}
 @end

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/AudioPlayAnimationView/KSAudioPlayAnimationView.h

@@ -0,0 +1,22 @@
+//
+//  KSAudioPlayAnimationView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/9/6.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSAudioPlayAnimationView : UIView
+
+- (void)startAnimation;
+
+- (void)stopAnimation;
+
+- (void)setSoundsValue:(CGFloat)value;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 315 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/AudioPlayAnimationView/KSAudioPlayAnimationView.m

@@ -0,0 +1,315 @@
+//
+//  KSAudioPlayAnimationView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/9/6.
+//
+
+#import "KSAudioPlayAnimationView.h"
+
+@interface KSAudioPlayAnimationView ()
+
+@property (nonatomic, assign) CGFloat itemLineWidth;
+
+@property (nonatomic, assign) CGFloat itemLineSeparate;
+
+
+
+@property (nonatomic, strong) UIColor *itemColor;
+
+@property (nonatomic, strong) UIColor *itemBackColor;
+
+@property (nonatomic, strong) CADisplayLink *displayLink;
+
+
+@property (nonatomic, assign) CGFloat soundsValue;
+
+
+@end
+
+@implementation KSAudioPlayAnimationView
+
+- (instancetype)init{
+
+    if (self = [super init]) {
+
+        _itemLineWidth = 5;
+
+        _itemLineSeparate = 8;
+
+        _soundsValue = 0;
+
+        _itemColor =  HexRGB(0x2DC7AA);
+
+        _itemBackColor =  HexRGB(0xffffff);
+
+    }
+    return self;
+}
+
+- (void)setFrame:(CGRect)frame{
+
+    [super setFrame:frame];
+
+    [self setItems];
+
+    [self stopLayer];
+}
+
+
+
+- (void)setSoundsValue:(CGFloat)value{
+
+    _soundsValue = value;
+}
+
+
+
+- (void)setItems{
+
+    [self removeAllSubLayers];
+
+    CGFloat width = self.frame.size.width;
+
+    NSInteger numberItem = (width - self.itemLineWidth) / (_itemLineWidth + _itemLineSeparate);
+
+    
+
+    CGFloat layer_w = _itemLineWidth;
+
+    CGFloat layer_x = _itemLineSeparate;
+
+    CGFloat layer_h = self.frame.size.height;
+
+    CGFloat layer_y = self.frame.size.height - layer_h;
+
+    
+
+    CGFloat min_x = (width - 48 - 34 * 2) / 2;
+
+    CGFloat max_x = (width - (width - 48 - 34 * 2) / 2);
+
+    for (int i = 0; i < numberItem; i++) {
+
+        layer_x = _itemLineSeparate + (_itemLineSeparate + _itemLineWidth) * i;
+
+        if (layer_x > min_x && layer_x < max_x) {
+
+            continue;
+
+        }
+
+
+
+        layer_y = [self layer_y:i];
+
+        layer_h = [self layer_h:layer_y];
+
+        
+
+        UIBezierPath *itemLinePath = [UIBezierPath bezierPath];
+
+        [itemLinePath moveToPoint:CGPointMake(0, layer_y)];
+
+        [itemLinePath addLineToPoint:CGPointMake(0, layer_y + layer_h)];
+
+        
+
+        CAShapeLayer *itemline = [CAShapeLayer layer];
+
+        itemline.lineCap       = kCALineCapButt;
+
+        itemline.lineJoin      = kCALineJoinRound;
+
+        [itemline setLineWidth:layer_w * 2];
+
+        itemline.strokeColor   = [self.itemColor CGColor];
+
+        
+
+        itemline.path = [itemLinePath CGPath];
+
+        
+
+        UIView *dynamicView = [[UIView alloc]initWithFrame:CGRectMake(layer_x, layer_y, layer_w, layer_h)];
+
+        dynamicView.backgroundColor = self.itemBackColor;
+
+        dynamicView.layer.masksToBounds = YES;
+
+        dynamicView.layer.cornerRadius = layer_w / 2;
+
+        dynamicView.tag = 111;
+
+        
+
+        [dynamicView.layer addSublayer:itemline];
+
+        [self addSubview:dynamicView];
+
+    }
+
+}
+
+
+
+- (CGFloat)layer_y:(int)i{
+
+    CGFloat layer_y = 0;
+
+    if (i % 4 == 0) {
+
+        layer_y = (self.height - self.height * 2 / 4) / 2;
+
+    }else if (i % 4 == 1){
+
+        layer_y = (self.height - self.height * 3 / 4) / 2;
+
+    }else if (i % 4 == 2){
+
+        layer_y = 0;
+
+    }else{
+
+        layer_y = (self.height - self.height * 3 / 4) / 2;;
+
+    }
+
+    return layer_y;
+
+}
+
+
+
+- (CGFloat)layer_h:(CGFloat)layer_y{
+
+    return (self.height - layer_y * 2);
+
+}
+
+
+
+- (void)removeAllSubLayers{
+
+    for (UIView *view in self.subviews) {
+
+        [view removeFromSuperview];
+
+    }
+
+}
+
+
+
+- (void)updateItemLevel{
+
+    NSArray *subViews = self.subviews;
+
+    for (int i = 0; i < subViews.count; i++) {
+
+        UIView *subView = subViews[i];
+
+        if (subView.tag == 111) {
+
+            NSArray *layers = subView.layer.sublayers;
+
+            CAShapeLayer *lineItem = (CAShapeLayer*)layers.firstObject;
+
+            CGFloat height = arc4random() % (NSInteger)self.frame.size.height * 2;
+
+            height *= self.soundsValue;
+
+            CGFloat value = (1 - height * 1.0 / self.frame.size.height);
+
+            value = value < 0 ? 0 : value;
+
+            lineItem.strokeStart = value;
+
+            lineItem.strokeEnd = 1;
+
+        }
+
+    }
+
+}
+
+
+
+- (void)stopLayer{
+
+    NSArray *subViews = self.subviews;
+
+    for (int i = 0; i < subViews.count; i++) {
+
+        UIView *subView = subViews[i];
+
+        if (subView.tag == 111) {
+
+            NSArray *layers = subView.layer.sublayers;
+
+            CAShapeLayer *lineItem = (CAShapeLayer*)layers.firstObject;
+
+            lineItem.strokeStart = 1;
+
+            lineItem.strokeEnd = 1;
+
+        }
+
+    }
+
+}
+
+
+
+#pragma mark - displayLink 定时器  动画
+
+- (CADisplayLink *)displayLink{
+
+    if (!_displayLink) {
+
+        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(start)];
+
+    }
+
+    return _displayLink;
+
+}
+
+
+
+- (void)start{
+
+    [self updateItemLevel];
+
+}
+
+
+
+- (void)startAnimation{
+
+    self.displayLink.frameInterval = 6;
+
+    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
+
+}
+
+
+
+- (void)stopAnimation{
+
+    if (self.displayLink) {
+        [self.displayLink invalidate];
+        self.displayLink = nil;
+        [self stopLayer];
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 21 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAudioAnimationView.h

@@ -0,0 +1,21 @@
+//
+//  KSAudioAnimationView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSAudioAnimationView : UIView
+
+@property (nonatomic, assign) BOOL isPlay;
+
++ (instancetype)shareInstance;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 104 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAudioAnimationView.m

@@ -0,0 +1,104 @@
+//
+//  KSAudioAnimationView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import "KSAudioAnimationView.h"
+
+@interface KSAudioAnimationView ()
+
+// 💿旋转view
+@property (weak, nonatomic) IBOutlet UIView *discView;
+
+@property (weak, nonatomic) IBOutlet UIImageView *music_image;
+
+@property (nonatomic, strong) UIImageView *stylusImage;
+
+@end
+
+@implementation KSAudioAnimationView
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    [self addSubview:self.stylusImage];
+    self.stylusImage.frame = CGRectMake(132, 17, 43, 117);
+    [self setAnchorPoint:CGPointMake(28.5 / 43, 25.0 / 117) forView:self.stylusImage];
+    [self transformStylusView:YES];
+}
+- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)stylusImage {
+    CGPoint oldOrigin = stylusImage.frame.origin;
+    stylusImage.layer.anchorPoint = anchorPoint;
+    CGPoint newOrigin = stylusImage.frame.origin;
+    CGPoint transition;
+    transition.x = newOrigin.x - oldOrigin.x;
+    transition.y = newOrigin.y - oldOrigin.y;
+    stylusImage.center = CGPointMake(stylusImage.center.x - transition.x, stylusImage.center.y - transition.y);
+}
+
+- (void)transformStylusView:(BOOL)isHide {
+    if (isHide) {
+        self.stylusImage.transform = CGAffineTransformIdentity;
+        self.stylusImage.transform = CGAffineTransformMakeRotation(M_PI_2);
+    }
+    else {
+        self.stylusImage.transform = CGAffineTransformIdentity;
+    }
+}
+
+
+
+
++ (instancetype)shareInstance {
+    KSAudioAnimationView *view = [[[NSBundle mainBundle] loadNibNamed:@"KSAudioAnimationView" owner:nil options:nil] firstObject];
+    view.frame = CGRectMake(0, 0, 200, 200);
+    return view;
+}
+
+
+- (void)setIsPlay:(BOOL)isPlay {
+    _isPlay = isPlay;
+    if (isPlay) {
+        [self addRotationAnimation];
+        [UIView animateWithDuration:1 animations:^{
+            [self transformStylusView:NO];
+        }];
+    }
+    else {
+        [self removeAnimation];
+        [UIView animateWithDuration:1 animations:^{
+            [self transformStylusView:YES];
+        }];
+    }
+}
+
+- (void)addRotationAnimation {
+    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
+    rotationAnimation.toValue = [NSNumber numberWithFloat:M_PI *2.0];
+    rotationAnimation.duration = 4.0f;
+    rotationAnimation.cumulative = YES;
+    rotationAnimation.repeatCount = CGFLOAT_MAX;
+    [self.discView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
+}
+
+- (void)removeAnimation {
+    [self.discView.layer removeAllAnimations];
+}
+
+
+
+- (UIImageView *)stylusImage {
+    if (!_stylusImage) {
+        _stylusImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"stylus_image"]];
+    }
+    return _stylusImage;
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 95 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSAudioAnimationView.xib

@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KSAudioAnimationView">
+            <rect key="frame" x="0.0" y="0.0" width="200" height="200"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="disc_bg" translatesAutoresizingMaskIntoConstraints="NO" id="lFV-It-Bm6">
+                    <rect key="frame" x="20" y="19" width="160" height="162"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="162" id="Ta9-nk-h9I"/>
+                        <constraint firstAttribute="width" constant="160" id="Tna-yG-6fx"/>
+                    </constraints>
+                </imageView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9rU-kO-9oA">
+                    <rect key="frame" x="37.666666666666657" y="37" width="125" height="126"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="vinyl_image" translatesAutoresizingMaskIntoConstraints="NO" id="4la-AT-u8a">
+                            <rect key="frame" x="0.0" y="0.0" width="125" height="126"/>
+                        </imageView>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pub_music_placeholder" translatesAutoresizingMaskIntoConstraints="NO" id="lVm-zv-Cjp">
+                            <rect key="frame" x="28.333333333333336" y="29" width="68" height="68"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="68" id="Hfl-IX-QGD"/>
+                                <constraint firstAttribute="height" constant="68" id="JIb-Al-3Z6"/>
+                            </constraints>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="34"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="lVm-zv-Cjp" firstAttribute="centerY" secondItem="9rU-kO-9oA" secondAttribute="centerY" id="bvX-pt-zFO"/>
+                        <constraint firstAttribute="bottom" secondItem="4la-AT-u8a" secondAttribute="bottom" id="k6r-mb-QBC"/>
+                        <constraint firstItem="4la-AT-u8a" firstAttribute="leading" secondItem="9rU-kO-9oA" secondAttribute="leading" id="onS-d1-2aK"/>
+                        <constraint firstItem="lVm-zv-Cjp" firstAttribute="centerX" secondItem="9rU-kO-9oA" secondAttribute="centerX" id="uQx-nX-QGw"/>
+                        <constraint firstItem="4la-AT-u8a" firstAttribute="top" secondItem="9rU-kO-9oA" secondAttribute="top" id="wzV-sD-CQ6"/>
+                        <constraint firstAttribute="trailing" secondItem="4la-AT-u8a" secondAttribute="trailing" id="zgz-VT-r2R"/>
+                    </constraints>
+                </view>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="disc_dot" translatesAutoresizingMaskIntoConstraints="NO" id="HET-TE-ojn">
+                    <rect key="frame" x="92.666666666666671" y="92.666666666666671" width="15" height="15"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="15" id="DqX-FY-VWd"/>
+                        <constraint firstAttribute="height" constant="15" id="Z1s-oL-yaK"/>
+                    </constraints>
+                </imageView>
+                <imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="stylus_image" translatesAutoresizingMaskIntoConstraints="NO" id="BbL-Qa-JrN">
+                    <rect key="frame" x="132" y="17" width="43" height="117"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="117" id="tph-20-Zry"/>
+                        <constraint firstAttribute="width" constant="43" id="vap-ud-LAc"/>
+                    </constraints>
+                </imageView>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="lFV-It-Bm6" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="GXc-Zo-uni"/>
+                <constraint firstItem="9rU-kO-9oA" firstAttribute="centerX" secondItem="lFV-It-Bm6" secondAttribute="centerX" id="LW3-gO-4q0"/>
+                <constraint firstItem="9rU-kO-9oA" firstAttribute="centerY" secondItem="lFV-It-Bm6" secondAttribute="centerY" id="Moj-RK-hoi"/>
+                <constraint firstItem="BbL-Qa-JrN" firstAttribute="trailing" secondItem="lFV-It-Bm6" secondAttribute="trailing" constant="-5" id="WOU-6T-tLp"/>
+                <constraint firstItem="BbL-Qa-JrN" firstAttribute="top" secondItem="lFV-It-Bm6" secondAttribute="top" constant="-2" id="duw-YQ-bd5"/>
+                <constraint firstItem="lFV-It-Bm6" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="gzL-Wi-8kW"/>
+                <constraint firstItem="HET-TE-ojn" firstAttribute="centerY" secondItem="lFV-It-Bm6" secondAttribute="centerY" id="l1V-zT-gbc"/>
+                <constraint firstItem="HET-TE-ojn" firstAttribute="centerX" secondItem="lFV-It-Bm6" secondAttribute="centerX" id="m0l-RH-T8x"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="discView" destination="9rU-kO-9oA" id="dhR-nO-WjR"/>
+                <outlet property="music_image" destination="lVm-zv-Cjp" id="uNV-wi-8S3"/>
+            </connections>
+            <point key="canvasLocation" x="12.977099236641221" y="-86.267605633802816"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="disc_bg" width="160" height="162"/>
+        <image name="disc_dot" width="15" height="15"/>
+        <image name="pub_music_placeholder" width="205" height="205"/>
+        <image name="stylus_image" width="43" height="117"/>
+        <image name="vinyl_image" width="125" height="126"/>
+    </resources>
+</document>

+ 32 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMediaMergeView.h

@@ -0,0 +1,32 @@
+//
+//  KSMediaMergeView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^MergeCallback)(void);
+typedef void(^DraftEditCallback)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSMediaMergeView : UIView
+
+@property (nonatomic, strong) NSString *recordId;
+
+@property (nonatomic, strong) NSString *songName;
+
+@property (nonatomic, strong) NSString *coverImage;
+
+@property (nonatomic, strong) NSString *desc;
+
+// 偏移时间 (录制声音和伴奏声音的偏移  + 伴奏提前 - 伴奏延迟 ms )
+- (void)configWithVideoUrl:(NSURL *)videoUrl bgAudioUrl:(NSURL *)bgAudioUrl remoteBgUrl:(NSString *)remoteBgUrl recordUrl:(NSURL *)recordUrl offsetTime:(NSInteger)offsetTime mergeCallback:(MergeCallback)callback;
+
+- (void)configRemoteVideoUrl:(NSString *)remoteVideoUrl bgAudioUrl:(NSString *)remoteBgAudioUrl recordUrl:(NSString *)remoteRecrodUrl jsonConfig:(NSString *)jsonConfig callback:(DraftEditCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 1078 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMediaMergeView.m

@@ -0,0 +1,1078 @@
+//
+//  KSMediaMergeView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import "KSMediaMergeView.h"
+#import "KSMergeAudioControlView.h"
+#import "KSAudioAnimationView.h"
+#import "KSMediaEditor.h"
+#import "KSPlayerView.h"
+#import "KSPlayerSliderView.h"
+#import "kSNewPlayer.h"
+#import "KSVideoPlayerView.h"
+#import "TZImageManager.h"
+#import "KSUploadManager.h"
+#import "KSAudioPlayAnimationView.h"
+#import "KSNewAlertView.h"
+#import "MusicPublistAlert.h"
+#import "KSMediaManager.h"
+
+@interface KSMediaMergeView ()<kSNewPlayerManagerDelegate,KSVideoPlayerViewDelegate>
+
+@property (nonatomic, strong) KSAudioAnimationView *animationView;
+
+@property (nonatomic, strong) KSMergeAudioControlView *contrlView;
+
+@property (nonatomic, strong) NSURL *videoUrl;
+
+@property (nonatomic, strong) NSURL *bgAudioUrl;
+
+@property (nonatomic, strong) NSURL *recordUrl;
+
+@property (nonatomic, assign) NSInteger offsetTime;
+
+@property (nonatomic, strong) KSPlayerView *playerView;
+
+@property (nonatomic, strong) KSPlayerSliderView *playControlView;
+
+@property (nonatomic, strong) kSNewPlayer *bgPlayer;
+
+@property (nonatomic, strong) kSNewPlayer *recordPlayer;
+
+@property (nonatomic, assign) BOOL isPause;
+
+@property (nonatomic, strong) NSString *filePath;
+
+
+@property (nonatomic, assign) NSInteger originalOffset;
+
+@property (nonatomic, strong) KSVideoPlayerView *videoView;
+
+@property (nonatomic, assign) BOOL isVideoPlay;
+
+@property (nonatomic, assign) BOOL isHideControlView;
+
+@property (nonatomic, strong) UIButton *showButton;
+
+@property (nonatomic, strong) PHAsset *videoAsset;
+
+@property (nonatomic, strong) NSString *presentName;
+
+@property (nonatomic, strong) KSAudioPlayAnimationView *playAnimationView;
+
+@property (nonatomic, strong) NSTimer *timer;
+
+// 远端资源
+@property (nonatomic, strong) NSString *remoteVideoUrl;
+
+@property (nonatomic, strong) NSString *remoteBgAudioUrl;
+
+@property (nonatomic, strong) NSString *remoteRecrodUrl;
+
+@property (nonatomic, strong) NSString *jsonConfig;
+
+
+@property (nonatomic, assign) NSInteger originalVolume;
+
+@property (nonatomic, assign) NSInteger accompanyVolume;
+
+@property (nonatomic, strong) dispatch_group_t requestGroup;
+
+@property (nonatomic, copy) DraftEditCallback draftCallback;
+
+@property (nonatomic, strong) NSDictionary *preJsonDic;
+
+@property (nonatomic, strong) KSNewAlertView *alertView;
+
+@property (nonatomic, assign) BOOL hasSave;
+
+@property (nonatomic, copy) MergeCallback mergeCallback;
+
+@property (nonatomic, strong) MusicPublistAlert *publishAlert;
+
+@property (nonatomic, strong) KSMediaManager *mediaManager;
+
+@property (nonatomic, strong) UIImage *settingImage;    // 图片
+
+@property (nonatomic, assign) BOOL hasModify;
+
+@end
+
+@implementation KSMediaMergeView
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        self.hasModify = NO;
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterBackground) name:@"appEnterBackground" object:nil];
+    }
+    return self;
+}
+
+- (void)appEnterBackground {
+    [self stopPlay];
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (self) {
+    }
+    return self;
+}
+
+- (void)configWithVideoUrl:(NSURL *)videoUrl bgAudioUrl:(NSURL *)bgAudioUrl remoteBgUrl:(NSString *)remoteBgUrl recordUrl:(NSURL *)recordUrl offsetTime:(NSInteger)offsetTime mergeCallback:(MergeCallback)callback {
+    if (callback) {
+        self.mergeCallback = callback;
+    }
+    if (videoUrl) {
+        self.isVideoPlay = YES;
+    }
+    else {
+        self.isVideoPlay = NO;
+    }
+    self.remoteBgAudioUrl = remoteBgUrl;
+    self.videoUrl = videoUrl;
+    self.bgAudioUrl = bgAudioUrl;
+    self.recordUrl = recordUrl;
+    if (offsetTime > 300) {
+        offsetTime = 300;
+    }
+    self.originalOffset = 0;
+    [self.contrlView configWithOffsetTime:0];
+    self.contrlView.hideBackView = NO;
+    [self configPlayer];
+    [self configUI];
+}
+
+- (void)configRemoteVideoUrl:(NSString *)remoteVideoUrl bgAudioUrl:(NSString *)remoteBgAudioUrl recordUrl:(NSString *)remoteRecrodUrl jsonConfig:(NSString *)jsonConfig callback:(DraftEditCallback)callback {
+    if (callback) {
+        self.draftCallback = callback;
+    }
+    if (![NSString isEmptyString:remoteVideoUrl]) {
+        self.isVideoPlay = YES;
+    }
+    else {
+        self.isVideoPlay = NO;
+    }
+    self.remoteVideoUrl = remoteVideoUrl;
+    self.remoteBgAudioUrl = remoteBgAudioUrl;
+    self.remoteRecrodUrl = remoteRecrodUrl;
+    
+    self.preJsonDic = [jsonConfig mj_JSONObject];
+    self.originalOffset = [self.preJsonDic ks_integerValueForKey:@"offset"];
+    self.offsetTime = self.originalOffset;
+    [self.contrlView configWithOffsetTime:self.originalOffset];
+    self.contrlView.hideBackView = YES;
+    [self downloadFileSource];
+}
+
+- (void)downloadFileSource {
+    [LOADING_MANAGER showCustomLoading:@"资源下载中..."];
+    
+    [self downloadVideo];
+    [self downloadAccompany];
+    [self downloadAudio];
+    dispatch_group_notify(self.requestGroup, dispatch_get_main_queue(), ^{
+        [LOADING_MANAGER removeCustomLoading];
+        [self configPlayer];
+        [self configUI];
+    });
+}
+
+- (void)downloadVideo {
+    if (![NSString isEmptyString:self.remoteVideoUrl]) {
+        dispatch_group_enter(self.requestGroup);
+        [KSNetworkingManager downloadFileRequestWithFileUrl:self.remoteVideoUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
+            
+        } success:^(NSURL * _Nonnull fileUrl) {
+            dispatch_group_leave(self.requestGroup);
+            self.videoUrl = fileUrl;
+        } faliure:^(NSError * _Nonnull error) {
+            dispatch_group_leave(self.requestGroup);
+
+        }];
+    }
+}
+
+- (void)downloadAudio {
+    if (![NSString isEmptyString:self.remoteRecrodUrl]) {
+        dispatch_group_enter(self.requestGroup);
+
+        [KSNetworkingManager downloadFileRequestWithFileUrl:self.remoteRecrodUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
+            
+        } success:^(NSURL * _Nonnull fileUrl) {
+            dispatch_group_leave(self.requestGroup);
+            self.recordUrl = fileUrl;
+        } faliure:^(NSError * _Nonnull error) {
+            dispatch_group_leave(self.requestGroup);
+        }];
+    }
+}
+
+- (void)downloadAccompany {
+    if (![NSString isEmptyString:self.remoteBgAudioUrl]) {
+        dispatch_group_enter(self.requestGroup);
+        [KSNetworkingManager downloadFileRequestWithFileUrl:self.remoteBgAudioUrl progress:^(int64_t bytesRead, int64_t totalBytes) {
+            
+        } success:^(NSURL * _Nonnull fileUrl) {
+            dispatch_group_leave(self.requestGroup);
+            self.bgAudioUrl = fileUrl;
+        } faliure:^(NSError * _Nonnull error) {
+            dispatch_group_leave(self.requestGroup);
+
+        }];
+    }
+}
+- (void)configPlayer {
+
+    if (self.bgAudioUrl) {
+        [self.bgPlayer preparePlayNativeSongWithPath:self.bgAudioUrl];
+    }
+    
+    if (self.recordUrl) {
+        [self.recordPlayer preparePlayNativeSongWithPath:self.recordUrl];
+    }
+    if (self.isVideoPlay) {
+        [self.videoView preparePlayNativeVideoWithPath:self.videoUrl];
+        self.videoView.isMute = YES;
+    }
+    
+    // 音量同步
+    NSInteger accompanyVolume = 100;
+    if ([[self.preJsonDic allKeys] containsObject:@"accompanyVolume"]) {
+        accompanyVolume = [self.preJsonDic ks_integerValueForKey:@"accompanyVolume"];
+    }
+    self.accompanyVolume = accompanyVolume;
+    self.bgPlayer.volume = accompanyVolume / 100.0;
+
+    NSInteger originalVolume = 100;
+    if ([[self.preJsonDic allKeys] containsObject:@"originalVolume"]) {
+        originalVolume = [self.preJsonDic ks_integerValueForKey:@"originalVolume"];
+    }
+    self.originalVolume = originalVolume;
+    self.recordPlayer.volume = originalVolume / 100.0;
+
+    [self.contrlView configRecordVolume:originalVolume bgVolume:accompanyVolume];
+}
+
+- (void)configUI {
+    self.backgroundColor = [UIColor whiteColor];
+    if (self.isVideoPlay) {
+        [self addSubview:self.videoView];
+    }
+    else {
+        [self addSubview:self.playerView];
+    }
+    
+    [self addSubview:self.showButton];
+    self.showButton.hidden = YES;
+    self.showButton.userInteractionEnabled = NO;
+    
+    [self addSubview:self.contrlView];
+    [self.contrlView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.right.top.bottom.mas_equalTo(self);
+        make.width.mas_equalTo(289);
+    }];
+    
+    if (self.isVideoPlay) {
+        [self.videoView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.top.bottom.mas_equalTo(self);
+            make.right.mas_equalTo(self.contrlView.mas_left).offset(20);
+        }];
+    }
+    else { // 音频
+        // 添加投影
+        UIImageView *shadowImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"merge_music_bg"]];
+        [self.playerView addSubview:shadowImage];
+        
+        
+        [self.playerView addSubview:self.playAnimationView];
+        [self.playAnimationView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.centerX.mas_equalTo(self.playerView.mas_centerX);
+            make.centerY.mas_equalTo(self.playerView.mas_centerY);
+            make.width.mas_equalTo(424);
+            make.height.mas_equalTo(80);
+        }];
+        
+        [self.playerView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.left.top.bottom.mas_equalTo(self);
+            make.right.mas_equalTo(self.contrlView.mas_left).offset(20);
+        }];
+        
+        [self.playerView addSubview:self.animationView];
+        [self.animationView mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.centerX.mas_equalTo(self.playerView.mas_centerX);
+            make.centerY.mas_equalTo(self.playerView.mas_centerY);
+            make.width.height.mas_equalTo(200);
+        }];
+        [shadowImage mas_makeConstraints:^(MASConstraintMaker *make) {
+            make.width.mas_equalTo(284);
+            make.height.mas_equalTo(151);
+            make.centerX.mas_equalTo(self.playerView.mas_centerX);
+            make.top.mas_equalTo(self.animationView.mas_top).offset(86);
+        }];
+    }
+    
+    [self addSubview:self.playControlView];
+    [self.playControlView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.mas_equalTo(self.mas_left);
+        make.right.mas_equalTo(self.contrlView.mas_left).offset(-20);
+        make.height.mas_equalTo(44);
+        make.bottom.mas_equalTo(self.mas_bottom).offset(-30);
+    }];
+    
+    [self.playControlView configViewDisplayColor:self.isVideoPlay];
+    NSString *imgName = self.isVideoPlay ? @"back_button_white" : @"back_black";
+    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
+    [backButton setImage:[UIImage imageNamed:imgName] forState:UIControlStateNormal];
+    [backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
+    [self addSubview:backButton];
+    [backButton mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.width.height.mas_equalTo(44);
+        make.left.mas_equalTo(self.mas_left).offset(10);
+        make.top.mas_equalTo(self.mas_top).offset(10);
+    }];
+    float totalTime = [self.recordPlayer getTotalDuration];
+    [self.playControlView configWithDuration:totalTime];
+    
+    UILabel *songLabel = [[UILabel alloc] init];
+    songLabel.font = [UIFont systemFontOfSize:16.0f weight:UIFontWeightSemibold];
+    songLabel.text = [NSString returnNoNullStringWithString:self.songName];
+    songLabel.textColor = self.isVideoPlay ? HexRGB(0xffffff) : HexRGB(0x131415);
+    [self addSubview:songLabel];
+    [songLabel mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.mas_equalTo(backButton.mas_right);
+        make.centerY.mas_equalTo(backButton.mas_centerY);
+        make.height.mas_equalTo(22);
+        make.right.mas_greaterThanOrEqualTo(self.contrlView.mas_left).offset(20);
+    }];
+    [self startPlay];
+}
+
+- (void)backAction {
+    if (self.hasModify) {
+        MJWeakSelf;
+        [self.alertView configTitle:@"提示" descMessage:@"是否将本次录制的作品保存为草稿?" leftButtonTitle:@"取消" rightButtonTitle:@"确认" leftButtonAction:^{
+            [weakSelf removeViewTips:NO];
+        } rightButtonAction:^{
+            [weakSelf saveCurrentDraft:YES];
+        }];
+        [self.alertView showAlert];
+    }
+    else {
+        if (self.mergeCallback) {
+            [self removeViewTips:NO];
+        }
+        else {
+            [self removeView];
+        }
+    }
+}
+
+#pragma mark ----- lazying
+- (KSAudioAnimationView *)animationView {
+    if (!_animationView) {
+        _animationView = [KSAudioAnimationView shareInstance];
+    }
+    return _animationView;
+}
+
+- (KSPlayerView *)playerView {
+    if (!_playerView) {
+        _playerView = [[KSPlayerView alloc] init];
+    }
+    return _playerView;
+}
+
+- (KSPlayerSliderView *)playControlView {
+    if (!_playControlView) {
+        _playControlView = [KSPlayerSliderView shareInstance];
+        MJWeakSelf;
+        [_playControlView sliderControlAction:^(PLAYERTYPE type, NSInteger rate) {
+            [weakSelf playSliderAction:type rate:rate];
+        }];
+    }
+    return _playControlView;
+}
+
+- (void)startPlay {
+    self.playControlView.isPlay = YES;
+    self.animationView.isPlay = YES;
+    NSInteger recordPlayerPosition = self.playControlView.playScheduleTime*1000;
+    NSLog(@"--recordPlayer start---");
+    [self.recordPlayer seekToTimePlay:recordPlayerPosition];
+    NSInteger realOffsetTime = self.offsetTime;
+    NSInteger offsetTime = recordPlayerPosition + realOffsetTime;
+    if (offsetTime > 0) {
+        [self.bgPlayer seekToTimePlay:labs(offsetTime)];
+    }
+    else {
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(labs(offsetTime) * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
+            NSLog(@"--bgPlayer start---");
+
+            [self.bgPlayer startPlay];
+        });
+    }
+    
+    if (self.isVideoPlay) {
+        [self.videoView seekToTimePlay:recordPlayerPosition];
+    }
+    else {
+        [self.playAnimationView startAnimation];
+        [self startTimer];
+    }
+}
+
+- (void)stopPlay {
+    self.animationView.isPlay = NO;
+    self.playControlView.isPlay = NO;
+    [self.bgPlayer puasePlay];
+    [self.recordPlayer puasePlay];
+    if (self.isVideoPlay) {
+        [self.videoView puasePlay];
+    }
+    else {
+        [self.playAnimationView stopAnimation];
+        [self stopTimer];
+    }
+}
+
+- (void)playSliderAction:(PLAYERTYPE)type rate:(NSInteger)rate {
+    switch (type) {
+        case PLAYERTYPE_PLAY:
+        {
+            [self startPlay];
+        }
+            break;
+        case PLAYERTYPE_PAUSE:
+        {
+            [self stopPlay];
+        }
+            break;
+        case PLAYERTYPE_RATE:
+        {
+            [self.recordPlayer seekOffsetTime:rate*1000];
+            NSInteger realOffsetTime = self.offsetTime;
+
+            NSInteger offsetTime = rate*1000 + realOffsetTime;
+            if (offsetTime > 0) {
+                [self.bgPlayer seekOffsetTime:labs(offsetTime)];
+            }
+            else {
+                [self.bgPlayer puasePlay];
+                if (self.recordPlayer.isPlaying) {
+                    
+                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(labs(offsetTime) * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
+                        [self.bgPlayer startPlay];
+                    });
+                }
+                else {
+                    [self.bgPlayer seekToStart];
+                }
+                
+            }
+            
+            if (self.isVideoPlay) {
+                [self.videoView seekOffsetTime:rate*1000];
+            }
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)startTimer {
+    [self.timer setFireDate:[NSDate distantPast]];
+}
+
+- (void)stopTimer {
+    [self.timer setFireDate:[NSDate distantFuture]];
+}
+
+- (KSMergeAudioControlView *)contrlView {
+    if (!_contrlView) {
+        _contrlView = [KSMergeAudioControlView shareIntance];
+        MJWeakSelf;
+        [_contrlView controlActionCallback:^(MERGEACTION type, NSInteger recordVolume, NSInteger bgVolume, NSInteger offsetTime) {
+            [weakSelf megerAction:type recordVolume:recordVolume bgVolume:bgVolume offsetTime:offsetTime];
+        }];
+    }
+    return _contrlView;
+}
+
+
+- (void)megerAction:(MERGEACTION)action recordVolume:(NSInteger)recordVolume bgVolume:(NSInteger)bgVolume offsetTime:(NSInteger)offsetTime {
+    switch (action) {
+        case MERGEACTION_CANCLE:
+        {
+            [self backAction];
+        }
+            break;
+        case MERGEACTION_MODIFY:
+        {
+            self.hasModify = YES;
+            self.accompanyVolume = bgVolume;
+            self.originalVolume = recordVolume;
+            
+            self.bgPlayer.volume = bgVolume / 100.0;
+            self.recordPlayer.volume = recordVolume / 100.0;
+        }
+            break;
+        case MERGEACTION_DELAY:  // offset
+        {
+            self.hasModify = YES;
+            if (self.bgPlayer.isPlaying) {
+                CMTime time = [self.recordPlayer getCurrentPlayTime];
+                NSInteger newDelayTime = CMTimeGetSeconds(time) * 1000 + offsetTime;
+                [self.bgPlayer seekOffsetTime:newDelayTime];
+            }
+            
+            self.offsetTime = offsetTime;
+        }
+            break;
+        case MERGEACTION_SAVE: // 保存
+        {
+            [self saveCurrentDraft:NO];
+        }
+            break;
+        case MERGEACTION_UPLOAD:  // 上传
+        {
+            [self showPublishAlert];
+            
+        }
+            break;
+        case MERGEACTION_HIDEVIEW:
+        {
+            [self hideControlView];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)showPublishAlert {
+    self.publishAlert = [MusicPublistAlert shareInstance];
+    self.publishAlert.musicName.text = [NSString returnNoNullStringWithString:self.songName];
+    [self.publishAlert.musicImage sd_setImageWithURL:[NSURL URLWithString:[self.coverImage getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:@"pub_music_placeholder"]];
+    self.publishAlert.userName.text = [NSString returnNoNullStringWithString:UserDefault(NicknameKey)];
+    MJWeakSelf;
+    [self.publishAlert chooseAction:^(PUBLISH_ACTION type) {
+        [weakSelf publishAction:type];
+    }];
+    [self.publishAlert showAlert];
+}
+
+- (void)publishAction:(PUBLISH_ACTION)type {
+    switch (type) {
+        case PUBLISH_ACTION_PUBLISH:
+        {
+            self.desc = [NSString isEmptyString:self.publishAlert.textView.text] ? @"我发布了一首演奏作品,快来听听吧~" :self.publishAlert.textView.text;
+            if (self.settingImage) { // 上传图片
+                [self updateWithCoverImage:self.settingImage];
+            }
+            else { // 直接上传
+                [self publishMusic];
+            }
+        }
+            break;
+        case PUBLISH_ACTION_CHOOSEIMG:
+        {
+            // 调用相册
+            self.mediaManager = [[KSMediaManager alloc] init];
+            self.mediaManager.mediaType = MEDIATYPE_PHOTO;
+            self.mediaManager.maxPhotoNumber = 1;
+            self.mediaManager.baseCtrl = [self findViewController];
+
+            self.mediaManager.needCropImage = NO;
+            MJWeakSelf;
+            [self.mediaManager noAlertCallback:^(NSString * _Nullable videoUrl, NSMutableArray * _Nullable imageArray, NSMutableArray * _Nullable imageAsset) {
+                [weakSelf refreshViewCoverImg:imageArray];
+            }];
+            [self.mediaManager pushImagePickerController];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)updateWithCoverImage:(UIImage *)image {
+    NSData *imgData = [UIImage turnsImaegDataByImage:image];
+    NSString *fileName = @"musicCoverImg";
+    [UPLOAD_MANAGER configBucketName:@"daya"];
+    [[KSUploadManager shareInstance] uploadImage:imgData fileName:fileName successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
+        NSString *avatarUrl = [fileUrlArray lastObject];
+        self.coverImage = avatarUrl;
+        [self publishMusic];
+    } faliure:^(NSError * _Nullable error, NSString *descMessaeg) {
+        if ([NSString isEmptyString:descMessaeg]) {
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:descMessaeg];
+        }
+    }];
+}
+
+- (void)refreshViewCoverImg:(NSMutableArray *)imageArray {
+    UIImage *img = [imageArray lastObject];
+    self.settingImage = img;
+    [self.publishAlert.musicImage setImage:self.settingImage];
+    
+}
+
+
+- (void)publishMusic {
+    if (self.isVideoPlay) {
+        [LOADING_MANAGER showCustomLoading:@"视频合成中..."];
+        [KSMediaEditor mixVideoWithRecordAudio:self.recordUrl recordVolume:self.recordPlayer.volume bgAudio:self.bgAudioUrl bgAudioVolume:self.bgPlayer.volume offsetTime:self.offsetTime videoUrlStr:self.videoUrl completion:^(NSString * _Nonnull outPath, BOOL isSuccess, NSString * _Nonnull desc) {
+            // 保存文件到指定文件夹
+            if (isSuccess) {
+                self.filePath = outPath;
+                MJWeakSelf;
+                [self saveVideoToAsset:outPath isFormal:YES callback:^(NSString *videoUrl) {
+                    [weakSelf saveMusic:YES isFormal:YES fileUrl:videoUrl];
+                }];
+            }
+            else {
+                [LOADING_MANAGER removeCustomLoading];
+            }
+        }];
+    }
+    else {
+        [LOADING_MANAGER showCustomLoading:@"音频合成中..."];
+        NSInteger realOffsetTime = self.offsetTime;
+        [KSMediaEditor mixRecordAudio:self.recordUrl recordVolume:self.recordPlayer.volume bgAudio:self.bgAudioUrl bgAudioVolume:self.bgPlayer.volume offsetTime:realOffsetTime completion:^(NSString * _Nonnull outPath, BOOL isSuccess, NSString * _Nonnull desc) {
+            // 保存文件到指定文件夹
+            if (isSuccess) {
+                self.filePath = outPath;
+                MJWeakSelf;
+                [self sendAudioWithPath:outPath isFormal:YES success:^(NSString *audioUrl) {
+                    [weakSelf saveMusic:NO isFormal:YES fileUrl:audioUrl];
+                } failure:^(NSString *desc) {
+                    [LOADING_MANAGER MBShowAUTOHidingInWindow:@"文件上传失败"];
+                }];
+            }
+            else {
+                [LOADING_MANAGER removeCustomLoading];
+            }
+        }];
+    }
+    
+}
+
+- (void)saveCurrentDraft:(BOOL)needBack {
+    self.hasModify = NO;
+    if (self.isVideoPlay) {
+        if (self.remoteVideoUrl) {
+            // 保存草稿
+            [self saveMusic:YES isFormal:NO fileUrl:self.remoteVideoUrl needBack:needBack];
+        }
+        else {
+            MJWeakSelf;
+            [self saveVideoURLToAsset:self.videoUrl isFormal:NO callback:^(NSString *videoUrl) {
+                // 保存草稿
+                [weakSelf saveMusic:YES isFormal:NO fileUrl:videoUrl needBack:needBack];
+            }];
+        }
+    }
+    else {
+        if (self.remoteRecrodUrl) {
+            [self saveMusic:NO isFormal:NO fileUrl:self.remoteRecrodUrl needBack:needBack];
+        }
+        else {
+            MJWeakSelf;
+            [self sendAudioWithURLPath:self.recordUrl isFormal:NO success:^(NSString *audioUrl) {
+                
+                [weakSelf saveMusic:NO isFormal:NO fileUrl:audioUrl needBack:needBack];
+            } failure:^(NSString *desc) {
+                [LOADING_MANAGER MBShowAUTOHidingInWindow:@"文件上传失败"];
+            }];
+        }
+    }
+}
+
+- (void)removeViewTips:(BOOL)needTips {
+    [self freePlayer];
+    [self removeFromSuperview];
+    if (self.draftCallback) {
+        self.draftCallback();
+    }
+    if (needTips && self.mergeCallback) {
+        self.mergeCallback();
+    }
+}
+
+- (void)removeView {
+    [self removeViewTips:YES];
+}
+
+- (void)freePlayer {
+    if (_bgPlayer) {
+         [self.bgPlayer freePlayer];
+    }
+    if (_recordPlayer) {
+         [self.recordPlayer freePlayer];
+    }
+    if (_videoView) {
+        [self.videoView freePlayer];
+    }
+}
+
+- (kSNewPlayer *)bgPlayer {
+    if (!_bgPlayer) {
+        _bgPlayer = [[kSNewPlayer alloc] init];
+        _bgPlayer.delegate = self;
+    }
+    return _bgPlayer;
+}
+
+- (kSNewPlayer *)recordPlayer {
+    if (!_recordPlayer) {
+        _recordPlayer = [[kSNewPlayer alloc] init];
+        _recordPlayer.delegate = self;
+    }
+    return _recordPlayer;
+}
+
+
+#pragma mark ----- player delegate
+- (void)getSongCurrentTime:(NSInteger)currentTime andTotalTime:(NSInteger)totalTime andProgress:(CGFloat)progress currentInterval:(NSTimeInterval)currentInterval playTime:(NSTimeInterval)playTime inPlayer:(kSNewPlayer *_Nonnull)player {
+    if (player == self.recordPlayer) {
+//        NSLog(@"--- ----  ----- %f", playTime);
+        self.playControlView.playScheduleTime = (NSInteger)(playTime / 1000);
+    }
+    else {
+        NSLog(@"--- ---- bgPlayer   ----- %f", playTime);
+    }
+}
+
+- (void)playFinished:(kSNewPlayer *)player {
+    if (player == self.recordPlayer) {
+        [self.recordPlayer puasePlay];
+        [self.recordPlayer seekToStart];
+        
+        [self.bgPlayer puasePlay];
+        [self.bgPlayer seekToStart];
+        self.isPause = NO;
+        self.animationView.isPlay = NO;
+        self.playControlView.isPlay = NO;
+        [self.playAnimationView stopAnimation];
+        [self stopTimer];
+        if (self.isVideoPlay) {
+            [self.videoView puasePlay];
+            [self.videoView seekToStart];
+        }
+    }
+}
+
+
+- (KSVideoPlayerView *)videoView {
+    if (!_videoView) {
+        _videoView = [[KSVideoPlayerView alloc] init];
+        _videoView.delegate = self;
+    }
+    return _videoView;
+}
+
+- (UIButton *)showButton {
+    if (!_showButton) {
+        _showButton = [UIButton buttonWithType:UIButtonTypeCustom];
+        _showButton.frame = CGRectMake(KLandscapeWidth-30, (KLandscapeHeight-30)/2.0, 30, 30);
+        [_showButton setImage:[UIImage imageNamed:@"mergeControl_show"] forState:UIControlStateNormal];
+        [_showButton addTarget:self action:@selector(showControlView) forControlEvents:UIControlEventTouchUpInside];
+    }
+    return _showButton;
+}
+
+- (void)hideControlView {
+    [UIView animateWithDuration:0.5f animations:^{
+        [self.contrlView mas_remakeConstraints:^(MASConstraintMaker *make) {
+            make.top.bottom.mas_equalTo(self);
+            make.width.mas_equalTo(289);
+            make.right.mas_equalTo(self.mas_right).offset(289);
+        }];
+    } completion:^(BOOL finished) {
+        self.showButton.hidden = NO;
+        self.showButton.userInteractionEnabled = YES;
+    }];
+}
+
+- (void)showControlView {
+    [UIView animateWithDuration:0.5f animations:^{
+        [self.contrlView mas_remakeConstraints:^(MASConstraintMaker *make) {
+            make.right.top.bottom.mas_equalTo(self);
+            make.width.mas_equalTo(289);
+        }];
+    } completion:^(BOOL finished) {
+        self.showButton.hidden = YES;
+        self.showButton.userInteractionEnabled = NO;
+    }];
+}
+
+
+- (void)saveVideoURLToAsset:(NSURL *)videoUrl isFormal:(BOOL)isFormal callback:(void(^)(NSString *videoUrl))callback {
+    
+    [[TZImageManager manager] saveVideoWithUrl:videoUrl completion:^(PHAsset *asset, NSError *error) {
+        if (!error) {
+            self.videoAsset = asset;
+            dispatch_main_async_safe(^{
+                
+                if (callback) {
+                    [self uploadRecordVideoisFormal:isFormal Success:^(NSString *uploadVideoUrl) {
+                        callback(uploadVideoUrl);
+                        
+                    } failure:^(NSString *desc) {
+                        [LOADING_MANAGER MBShowAUTOHidingInWindow:@"上传视频失败"];
+                    }];
+                }
+                
+            });
+        }
+        else {
+            dispatch_main_async_safe(^{
+                [LOADING_MANAGER MBShowAUTOHidingInWindow:@"保存视频错误"];
+            });
+        }
+    }];
+}
+
+- (void)saveVideoToAsset:(NSString *)saveVideoUrl isFormal:(BOOL)isFormal callback:(void(^)(NSString *videoUrl))callback {
+    NSURL *fileUrl = [NSURL fileURLWithPath:saveVideoUrl];
+    [self saveVideoURLToAsset:fileUrl isFormal:isFormal callback:^(NSString *videoUrl) {
+        callback(videoUrl);
+    }];
+}
+
+- (void)saveMusic:(BOOL)isVideo isFormal:(BOOL)isFormal fileUrl:(NSString *)fileUrl  {
+    
+    [self saveMusic:isVideo isFormal:isFormal fileUrl:fileUrl needBack:NO];
+}
+
+- (void)saveMusic:(BOOL)isVideo isFormal:(BOOL)isFormal fileUrl:(NSString *)fileUrl needBack:(BOOL)needBack {
+    NSString *type = isFormal ? @"FORMAL" : @"DRAFT";
+    NSMutableDictionary *parm = [NSMutableDictionary dictionary];
+    [parm setValue:@(self.offsetTime) forKey:@"offset"];
+    [parm setValue:@(self.originalVolume) forKey:@"originalVolume"];
+    [parm setValue:@(self.accompanyVolume) forKey:@"accompanyVolume"];
+    self.jsonConfig = [parm mj_JSONString];
+    [LOADING_MANAGER showCustomLoading:@"加载中..."];
+    [KSNetworkingManager saveMusicMessage:KS_POST jsonConfig:self.jsonConfig img:self.coverImage videoUrl:fileUrl accompanyUrl:self.remoteBgAudioUrl desc:self.desc type:type musicPracticeRecordId:self.recordId success:^(NSDictionary * _Nonnull dic) {
+        [LOADING_MANAGER removeCustomLoading];
+        if ([dic ks_integerValueForKey:@"code"] == 200) {
+            if (isFormal) {
+                [LOADING_MANAGER KSShowMsg:@"发布成功" promptCompletion:^{
+                    [self removeView];
+                }];
+            }
+            else {
+                [LOADING_MANAGER KSShowMsg:@"保存成功" promptCompletion:^{
+                    self.hasModify = NO;
+                    if (needBack) {
+                        [self removeViewTips:NO];
+                    }
+                }];
+            }
+        }
+        else {
+            [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
+        }
+    } faliure:^(NSError * _Nonnull error) {
+        [LOADING_MANAGER removeCustomLoading];
+    }];
+}
+
+// 上传视频
+- (void)uploadRecordVideoisFormal:(BOOL)isFormal Success:(void(^)(NSString *uploadVideoUrl))success failure:(void(^)(NSString *desc))faliure {
+    if (self.videoAsset) {
+        if (self.videoAsset) {
+
+            [[TZImageManager manager] getVideoOutputPathWithAsset:self.videoAsset presetName:self.presentName success:^(NSString *outputPath) {
+
+                NSLog(@"视频导出到本地完成,沙盒路径为:%@",outputPath);
+                NSData *outputData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:outputPath]]; //压缩后的视频
+                NSLog(@"导出后的视频:%@",[NSString stringWithFormat:@"%.2fM",(CGFloat)outputData.length/(1024*1024)]);
+                // 上传
+                dispatch_main_async_safe(^{
+                    [self sendVideoActionWith:outputPath isFormal:isFormal success:success failure:faliure];
+                });
+                
+            } failure:^(NSString *errorMessage, NSError *error) {
+                dispatch_main_async_safe(^{
+                    faliure(@"视频导出失败");
+                });
+                NSLog(@"视频导出失败:%@,error:%@",errorMessage, error);
+                
+            }];
+        }
+        else {
+            faliure(@"未找到视频资源");
+        }
+    }
+    else {
+        faliure(@"未找到视频资源");
+    }
+}
+
+
+- (void)sendVideoActionWith:(NSString *)fileUrl isFormal:(BOOL)isFormal success:(void (^)(NSString * _Nonnull))success failure:(void (^)(NSString * _Nonnull))faliure {
+    NSString *tips = isFormal ? @"正在上传作品" : @"正在上传草稿";
+    [LOADING_MANAGER showCustomLoading:tips];
+    NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:fileUrl]];
+    NSString *suffix = [NSString stringWithFormat:@".%@",[fileUrl pathExtension]];
+    [UPLOAD_MANAGER configBucketName:@"daya"];
+    [[KSUploadManager shareInstance] videoUpload:fileData fileName:@"video" fileSuffix:suffix progress:^(int64_t bytesWritten, int64_t totalBytes) {
+        // 显示进度
+        int progress = (int)(bytesWritten / totalBytes * 100);
+        __block NSString *tipsMessage = [NSString stringWithFormat:@"%@\n%d%%",tips,progress];
+        dispatch_main_async_safe(^{
+            [LOADING_MANAGER.loadingView setDisplayText:tipsMessage];
+        });
+    } successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
+        dispatch_main_async_safe(^{
+            [LOADING_MANAGER removeCustomLoading];
+            NSString *fileUrl = [fileUrlArray lastObject];
+            success(fileUrl);
+        });
+        
+        
+    } faliure:^(NSError * _Nullable error, NSString * _Nullable descMessaeg) {
+        dispatch_main_async_safe(^{
+            [LOADING_MANAGER removeCustomLoading];
+            faliure(descMessaeg);
+        });
+        
+    }];
+}
+
+- (void)sendAudioWithURLPath:(NSURL *)fileUrl isFormal:(BOOL)isFormal success:(void(^)(NSString *audioUrl))success failure:(void(^)(NSString *desc))faliure {
+    NSString *tips = isFormal ? @"正在上传作品" : @"正在上传草稿";
+
+    [LOADING_MANAGER showCustomLoading:tips];
+    NSData *fileData = [NSData dataWithContentsOfURL:fileUrl];
+    NSString *suffix = [NSString stringWithFormat:@".%@",[fileUrl pathExtension]];
+    [UPLOAD_MANAGER configBucketName:@"daya"];
+    [UPLOAD_MANAGER uploadFile:fileData fileName:@"evaluateAudio" fileSuffix:suffix progress:^(int64_t bytesWritten, int64_t totalBytes) {
+        // 显示进度
+        int progress = (int)(bytesWritten / totalBytes * 100);
+        __block NSString *tipsMessage = [NSString stringWithFormat:@"%@\n%d%%",tips,progress];
+        dispatch_main_async_safe(^{
+            [LOADING_MANAGER.loadingView setDisplayText:tipsMessage];
+        });
+    } successCallback:^(NSMutableArray * _Nonnull fileUrlArray) {
+        [LOADING_MANAGER removeCustomLoading];
+        NSString *fileUrl = [fileUrlArray lastObject];
+        success(fileUrl);
+    } faliure:^(NSError * _Nullable error, NSString * _Nullable descMessaeg) {
+        [LOADING_MANAGER removeCustomLoading];
+        faliure(descMessaeg);
+    }];
+    
+}
+
+- (void)sendAudioWithPath:(NSString *)filePath isFormal:(BOOL)isFormal success:(void(^)(NSString *audioUrl))success failure:(void(^)(NSString *desc))faliure {
+    NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
+    [self sendAudioWithURLPath:fileUrl isFormal:isFormal success:success failure:faliure];
+}
+
+- (NSString *)presentName {
+    NSString *presentName = AVAssetExportPresetMediumQuality;
+    if ([[NSString deviceVersion] containsString:@"iPhone 12"]) {
+        
+        presentName =  AVAssetExportPreset640x480;
+    }
+    else {
+        presentName = AVAssetExportPresetMediumQuality;
+    }
+    return presentName;
+}
+
+- (void)removeVideoWithPath:(NSString *)videoUrl {
+    NSFileManager *fileMamager = [NSFileManager defaultManager];
+    if ([fileMamager fileExistsAtPath:videoUrl]) {
+        [fileMamager removeItemAtPath:videoUrl error:nil];
+    }
+}
+
+- (KSAudioPlayAnimationView *)playAnimationView {
+    if (!_playAnimationView) {
+        _playAnimationView = [[KSAudioPlayAnimationView alloc] init];
+        _playAnimationView.frame = CGRectMake(0, 0, 424, 80);
+    }
+    return _playAnimationView;
+}
+
+- (NSTimer *)timer{
+    
+    if (!_timer) {
+        MJWeakSelf;
+        _timer = [NSTimer scheduledTimerWithTimeInterval:0.5f repeats:YES block:^(NSTimer * _Nonnull timer) {
+            [weakSelf timerAction];
+        }];
+        [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
+        [_timer setFireDate:[NSDate distantFuture]];
+    }
+    return _timer;
+}
+
+- (void)timerAction {
+    float value = drand48();
+    [self.playAnimationView setSoundsValue:value];
+}
+
+#pragma mark -- 重置定时器
+- (void)resetTimer{
+    [self.timer setFireDate:[NSDate distantPast]];
+    [_timer invalidate];
+    _timer = nil;
+}
+
+- (void)removeAll{
+    
+    if (_timer) {
+        [_timer invalidate];
+        _timer = nil;
+    }
+}
+- (void)dealloc {
+    [self removeAll];
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+#pragma mark ----- lazy
+- (dispatch_group_t)requestGroup {
+    if (!_requestGroup) {
+        _requestGroup = dispatch_group_create();
+    }
+    return _requestGroup;
+}
+
+- (KSNewAlertView *)alertView {
+    if (!_alertView) {
+        _alertView = [KSNewAlertView shareInstance];
+        
+    }
+    return _alertView;
+}
+
+- (UIViewController *)findViewController {
+    for (UIView *view = self; view; view = view.superview) {
+        UIResponder *nextResponder = [view nextResponder];
+        if ([nextResponder isKindOfClass:[UIViewController class]]) {
+            return (UIViewController *)nextResponder;
+        }
+    }
+    return nil;
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 37 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMergeAudioControlView.h

@@ -0,0 +1,37 @@
+//
+//  KSMergeAudioControlView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, MERGEACTION) {
+    MERGEACTION_CANCLE,    // 重录
+    MERGEACTION_SAVE,      // 保存到本地
+    MERGEACTION_UPLOAD,    // 上传
+    MERGEACTION_MODIFY,    // 修改
+    MERGEACTION_DELAY,     // 延迟
+    MERGEACTION_HIDEVIEW,  // 隐藏
+};
+
+typedef void(^MergeAudioAction)(MERGEACTION type, NSInteger recordVolume, NSInteger bgVolume, NSInteger offsetTime);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSMergeAudioControlView : UIView
+
+@property (nonatomic, assign) BOOL hideBackView;
+
++ (instancetype)shareIntance;
+
+- (void)configWithOffsetTime:(NSInteger)offsetTime;
+
+- (void)configRecordVolume:(NSInteger)recordVolume bgVolume:(NSInteger)bgVolume;
+
+- (void)controlActionCallback:(MergeAudioAction)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 337 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMergeAudioControlView.m

@@ -0,0 +1,337 @@
+//
+//  KSMergeAudioControlView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import "KSMergeAudioControlView.h"
+#import "UIView+KSCovertImage.h"
+
+@interface KSMergeAudioControlView ()<UIGestureRecognizerDelegate>
+
+@property (weak, nonatomic) IBOutlet UIView *bgView;
+
+@property (weak, nonatomic) IBOutlet UIView *sliderView;
+
+@property (weak, nonatomic) IBOutlet UIView *pointView;
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *pointLeft;
+
+@property (nonatomic, copy) MergeAudioAction callback;
+
+@property (nonatomic, assign) NSInteger recordVolume;
+@property (weak, nonatomic) IBOutlet UISlider *recordSlider;
+
+@property (nonatomic, assign) NSInteger bgVolume;
+
+@property (weak, nonatomic) IBOutlet UISlider *bgSlider;
+
+@property (nonatomic, assign) NSInteger offsetTime;
+
+@property (weak, nonatomic) IBOutlet UILabel *offsetTipsLabel;
+
+@property (nonatomic, strong) UITapGestureRecognizer *recordProgressTap;
+
+@property (nonatomic,strong) UITapGestureRecognizer *bgProgressTap;
+
+@property (weak, nonatomic) IBOutlet UIView *recordBubble;
+@property (weak, nonatomic) IBOutlet UILabel *recordBubbleLabel;
+@property (weak, nonatomic) IBOutlet UIView *recordGestureView;
+
+@property (weak, nonatomic) IBOutlet UIView *bgBubble;
+@property (weak, nonatomic) IBOutlet UILabel *bgVolumeBubble;
+@property (weak, nonatomic) IBOutlet UIView *bgGestureView;
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recordCenterX;
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bgCenterX;
+
+@property (weak, nonatomic) IBOutlet UILabel *recordTitle;
+
+@property (weak, nonatomic) IBOutlet UIImageView *reRecordImage;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recordImgWidth;
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *recordImageRight;
+
+@property (weak, nonatomic) IBOutlet UIButton *recordButton;
+
+@end
+
+
+@implementation KSMergeAudioControlView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    self.bgView.layer.cornerRadius = 16;
+    self.bgView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMinXMaxYCorner;
+    UIView *sliderImg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)];
+    sliderImg.layer.cornerRadius = 6.0f;
+    sliderImg.layer.masksToBounds = YES;
+    sliderImg.backgroundColor = HexRGB(0x2DC7AA);
+    UIImage *image = [UIView convertViewToImage:sliderImg];
+    [self.recordSlider setThumbImage:image forState:UIControlStateNormal];
+    [self.recordSlider setThumbImage:image forState:UIControlStateHighlighted];
+    
+    [self.bgSlider setThumbImage:image forState:UIControlStateNormal];
+    [self.bgSlider setThumbImage:image forState:UIControlStateHighlighted];
+    [self configDefaultVolume];
+    
+    self.recordTitle.hidden = NO;
+    self.reRecordImage.hidden = NO;
+    self.recordImageRight.constant = 20.0f;
+    self.recordImgWidth.constant = 24.0f;
+    self.recordButton.userInteractionEnabled = YES;
+}
+
+- (void)configDefaultVolume {
+    self.recordVolume = 100;
+    self.recordSlider.value = self.recordVolume;
+    self.bgVolume = 100;
+    self.bgSlider.value = self.bgVolume;
+}
+
+- (void)configWithOffsetTime:(NSInteger)offsetTime {
+    offsetTime = offsetTime / 10 * 10;
+    self.offsetTime = offsetTime;
+}
+
+- (void)configRecordVolume:(NSInteger)recordVolume bgVolume:(NSInteger)bgVolume {
+    self.recordVolume = recordVolume;
+    self.recordSlider.value = recordVolume;
+    self.bgVolume = bgVolume;
+    self.bgSlider.value = bgVolume;
+}
+
++ (instancetype)shareIntance {
+    KSMergeAudioControlView *view = [[[NSBundle mainBundle] loadNibNamed:@"KSMergeAudioControlView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+
+- (void)controlActionCallback:(MergeAudioAction)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+#pragma mark ----- 音量控制
+- (IBAction)changeRecordVolume:(UISlider *)sender {
+//    [self configRecordVolumeValue:sender.value];
+    
+}
+
+- (IBAction)changeBgVolume:(UISlider *)sender {
+//    [self configBgVolumeValue:sender.value];
+    
+}
+
+- (void)configRecordVolumeValue:(NSInteger)volume {
+    self.recordVolume = volume;
+    self.recordBubble.hidden = NO;
+    self.recordBubbleLabel.text = [NSString stringWithFormat:@"%zd",volume];
+    self.recordCenterX.constant = 30 + ((volume *1.0)/ 100)*237;
+    if (self.callback) {
+        self.callback(MERGEACTION_MODIFY, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+}
+
+- (void)configBgVolumeValue:(NSInteger)volume {
+    self.bgVolume = volume;
+    self.bgBubble.hidden =NO;
+    self.bgVolumeBubble.text = [NSString stringWithFormat:@"%zd",volume];
+    self.bgCenterX.constant = 30 + ((volume *1.0)/ 100)*237;
+    if (self.callback) {
+        self.callback(MERGEACTION_MODIFY, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+}
+
+- (IBAction)cancleMerge:(id)sender {
+    if (self.callback) {
+        self.callback(MERGEACTION_CANCLE, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+}
+
+- (IBAction)saveFile:(id)sender {
+    if (self.callback) {
+        self.callback(MERGEACTION_SAVE, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+}
+
+- (IBAction)uploadFile:(id)sender {
+    if (self.callback) {
+        self.callback(MERGEACTION_UPLOAD, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
+    if ([[(UITouch *)touches.anyObject view] isEqual:self.recordGestureView]) {
+        self.recordBubble.hidden = NO;
+    }
+    else if ([[(UITouch *)touches.anyObject view] isEqual:self.bgGestureView]) {
+        self.bgBubble.hidden = NO;
+    }
+    [super touchesBegan:touches withEvent:event];
+}
+
+- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
+    if (![[(UITouch *)touches.anyObject view] isEqual:self.sliderView] && ![[(UITouch *)touches.anyObject view] isEqual:self.recordGestureView] && ![[(UITouch *)touches.anyObject view] isEqual:self.bgGestureView] && ![[(UITouch *)touches.anyObject view] isEqual:self]) {
+        return;
+    }
+    [super touchesMoved:touches withEvent:event];
+    if ([[(UITouch *)touches.anyObject view] isEqual:self.sliderView]) {
+        CGPoint tempPoint = [touches.anyObject locationInView:self.sliderView];
+        if (tempPoint.x <= 90.5) {
+            NSInteger offset = (NSInteger)(tempPoint.x - 90.5) / 3 * 10;
+            if (offset < -300) {
+                offset = -300;
+            }
+            self.offsetTime = offset;
+        }
+        else if (tempPoint.x > 90.5) {
+            NSInteger offset = (NSInteger)(tempPoint.x - 90.5) / 3 * 10;
+            if (offset > 300) {
+                offset = 300;
+            }
+            self.offsetTime = offset;
+        }
+    }
+    else if ([[(UITouch *)touches.anyObject view] isEqual:self.recordGestureView]) {
+        CGPoint tempPoint = [touches.anyObject locationInView:self.recordGestureView];
+        CGFloat xPosition = tempPoint.x;
+        CGFloat width = self.recordGestureView.frame.size.width;
+        if (xPosition < 0) {
+            xPosition = 0;
+        }
+        else if (xPosition > width) {
+            xPosition = width;
+        }
+        NSInteger value =  (xPosition / width) * 100;
+         NSLog(@"---- record volume ---- %zd", value);
+        [self.recordSlider setValue:value animated:YES];
+        [self configRecordVolumeValue:value];
+    }
+    else if ([[(UITouch *)touches.anyObject view] isEqual:self.bgGestureView]) {
+        CGPoint tempPoint = [touches.anyObject locationInView:self.bgGestureView];
+        CGFloat xPosition = tempPoint.x;
+        CGFloat width = self.recordGestureView.frame.size.width;
+        if (xPosition < 0) {
+            xPosition = 0;
+        }
+        else if (xPosition > width) {
+            xPosition = width;
+        }
+        NSInteger value =  (xPosition / width) * 100;
+         NSLog(@"---- bg volume ---- %zd", value);
+        [self.bgSlider setValue:value animated:YES];
+        [self configBgVolumeValue:value];
+    }
+}
+
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
+    if ([[(UITouch *)touches.anyObject view] isEqual:self.recordGestureView]) {
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
+            self.recordBubble.hidden = YES;
+        });
+        return;
+    }
+    else if ([[(UITouch *)touches.anyObject view] isEqual:self.bgGestureView]) {
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(500 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
+            self.bgBubble.hidden = YES;
+        });
+        return;
+    }
+    if (![[(UITouch *)touches.anyObject view] isEqual:self.sliderView] && ![[(UITouch *)touches.anyObject view] isEqual:self]) {
+        return;
+    }
+    [super touchesEnded:touches withEvent:event];
+
+    if (self.callback) {
+        self.callback(MERGEACTION_DELAY, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+    
+}
+
+- (IBAction)leftAction:(id)sender {
+    if (self.offsetTime <= -300) {
+        self.offsetTime = -300;
+        return;
+    }
+    self.offsetTime -= 10;
+    
+}
+- (IBAction)rightAction:(id)sender {
+    if (self.offsetTime >= 300) {
+        self.offsetTime = 300;
+        return;
+    }
+    self.offsetTime += 10;
+}
+
+- (IBAction)hideAction:(id)sender {
+    if (self.callback) {
+        self.callback(MERGEACTION_HIDEVIEW, self.recordVolume, self.bgVolume, self.offsetTime);
+    }
+}
+
+//进度条的点击事件
+- (void)actionTapGesture:(UITapGestureRecognizer *)gesture {
+    if (gesture.view == self.recordGestureView) {
+        CGPoint touchLocation = [gesture locationInView:self.recordGestureView];
+        CGFloat value = (self.recordSlider.maximumValue - self.recordSlider.minimumValue) * (touchLocation.x/self.recordSlider.frame.size.width);
+        [self.recordSlider setValue:value animated:YES];
+        [self configRecordVolumeValue:value];
+    }
+    else {
+        CGPoint touchLocation = [gesture locationInView:self.bgGestureView];
+        CGFloat value = (self.bgSlider.maximumValue - self.bgSlider.minimumValue) * (touchLocation.x/self.bgSlider.frame.size.width);
+        [self.bgSlider setValue:value animated:YES];
+        [self configBgVolumeValue:value];
+    }
+    
+}
+
+#pragma mark ------ lazying
+- (void)setOffsetTime:(NSInteger)offsetTime {
+    _offsetTime = offsetTime;
+    NSString *tips = @"";
+    if (offsetTime > 0) {
+        tips = [NSString stringWithFormat:@"已为您对齐演奏伴奏,演奏延后%zd毫秒", labs(offsetTime)];
+        self.pointLeft.constant = 87 + labs(offsetTime) / 10 * 3;
+    }
+    else if (offsetTime == 0) {
+        tips = @"演奏伴奏没有对齐?试试调整这里";
+        self.pointLeft.constant = 87;
+    }
+    else {
+        tips = [NSString stringWithFormat:@"已为您对齐演奏伴奏,演奏提前%zd毫秒", labs(offsetTime)];
+        self.pointLeft.constant = 87 - labs(offsetTime) / 10 * 3;
+    }
+    self.offsetTipsLabel.text = tips;
+}
+
+- (void)setHideBackView:(BOOL)hideBackView {
+    _hideBackView = hideBackView;
+    if (hideBackView) {
+        self.recordTitle.hidden = YES;
+        self.reRecordImage.hidden = YES;
+        self.recordImageRight.constant = 0.0f;
+        self.recordImgWidth.constant = 0.0f;
+        self.recordButton.userInteractionEnabled = NO;
+    }
+    else {
+        self.recordTitle.hidden = NO;
+        self.reRecordImage.hidden = NO;
+        self.recordImageRight.constant = 20.0f;
+        self.recordImgWidth.constant = 24.0f;
+        self.recordButton.userInteractionEnabled = YES;
+    }
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 403 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSMergeAudioControlView.xib

@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KSMergeAudioControlView">
+            <rect key="frame" x="0.0" y="0.0" width="289" height="388"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dNG-od-Opv">
+                    <rect key="frame" x="0.0" y="0.0" width="289" height="388"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="音量调节" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZEM-tW-WX9">
+                            <rect key="frame" x="111.66666666666669" y="14" width="66" height="22"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="22" id="CkE-ww-wZM"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
+                            <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ffV-Sq-WQc">
+                            <rect key="frame" x="0.0" y="43" width="289" height="1"/>
+                            <color key="backgroundColor" red="0.94901960784313721" green="0.94901960784313721" blue="0.94901960784313721" alpha="1" colorSpace="calibratedRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="1" id="pHP-YP-eJt"/>
+                            </constraints>
+                        </view>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="演奏音量" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oyf-C4-Pe3">
+                            <rect key="frame" x="26.000000000000004" y="62" width="57.333333333333343" height="17"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <slider opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="pQK-tY-S1I">
+                            <rect key="frame" x="24" y="95" width="251" height="31"/>
+                            <color key="minimumTrackTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <color key="maximumTrackTintColor" red="0.90980392156862744" green="0.92549019607843142" blue="0.94509803921568625" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <color key="thumbTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <connections>
+                                <action selector="changeRecordVolume:" destination="iN0-l3-epB" eventType="valueChanged" id="WcD-V3-Jqt"/>
+                            </connections>
+                        </slider>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="伴奏音量" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="if2-R5-Tul">
+                            <rect key="frame" x="26" y="145" width="58" height="17"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <slider opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="100" minValue="0.0" maxValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="ucS-Gd-cx3">
+                            <rect key="frame" x="24" y="178" width="251" height="31"/>
+                            <color key="minimumTrackTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <color key="maximumTrackTintColor" red="0.90980392156862744" green="0.92549019607843142" blue="0.94509803921568625" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <color key="thumbTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <connections>
+                                <action selector="changeBgVolume:" destination="iN0-l3-epB" eventType="valueChanged" id="vwy-ge-2aE"/>
+                            </connections>
+                        </slider>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DAF-iV-Mgw">
+                            <rect key="frame" x="26" y="173" width="247" height="40"/>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        </view>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VTj-cX-p66">
+                            <rect key="frame" x="0.0" y="320" width="289" height="1"/>
+                            <color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="calibratedRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="1" id="ik8-sT-hkK"/>
+                            </constraints>
+                        </view>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="53S-gG-Fsg">
+                            <rect key="frame" x="69" y="333" width="151" height="35"/>
+                            <color key="backgroundColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="35" id="VUg-Kv-Va1"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="发布作品"/>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="17.5"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="uploadFile:" destination="iN0-l3-epB" eventType="touchUpInside" id="qXh-WC-GVb"/>
+                            </connections>
+                        </button>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="cancleMerge_image" translatesAutoresizingMaskIntoConstraints="NO" id="Jqd-bC-HWT">
+                            <rect key="frame" x="25" y="331" width="24" height="24"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="24" id="GDO-qk-rQt"/>
+                                <constraint firstAttribute="height" constant="24" id="mlw-wb-WQT"/>
+                            </constraints>
+                        </imageView>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="重录" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f5Q-wh-6l8">
+                            <rect key="frame" x="26.666666666666671" y="356" width="21" height="12"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                            <color key="textColor" red="0.46666666666666667" green="0.46666666666666667" blue="0.46666666666666667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GuK-qa-jwH">
+                            <rect key="frame" x="17" y="331" width="40" height="37"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="40" id="gMx-Pt-NwD"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="cancleMerge:" destination="iN0-l3-epB" eventType="touchUpInside" id="RBc-FO-fdQ"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="保存" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YP9-QE-4Lm">
+                            <rect key="frame" x="241.66666666666666" y="356" width="20.999999999999972" height="12"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                            <color key="textColor" red="0.46666666670000001" green="0.46666666670000001" blue="0.46666666670000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="merge_upload" translatesAutoresizingMaskIntoConstraints="NO" id="Q7z-0G-roa">
+                            <rect key="frame" x="240" y="331" width="24" height="24"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="24" id="5Ex-Ib-cs2"/>
+                                <constraint firstAttribute="width" constant="24" id="SCB-Ec-x6K"/>
+                            </constraints>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Mqk-Mg-Pni">
+                            <rect key="frame" x="232" y="331" width="40" height="37"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="40" id="FfO-hZ-DUG"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="saveFile:" destination="iN0-l3-epB" eventType="touchUpInside" id="Arf-7p-jFz"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="伴奏对齐" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XPO-nW-Uox">
+                            <rect key="frame" x="26" y="228" width="58" height="17"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="96g-7r-vnm">
+                            <rect key="frame" x="26" y="248.66666666666663" width="27" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="Ggw-dF-h9G"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" image="align_left_bg"/>
+                            <connections>
+                                <action selector="leftAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="fHh-PV-Mtc"/>
+                            </connections>
+                        </button>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="已为您对齐演奏伴奏,演奏提前10毫秒" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ses-18-GMw">
+                            <rect key="frame" x="47" y="290" width="195" height="15"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="15" id="To3-OM-9PS"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="11"/>
+                            <color key="textColor" red="0.5607843137254902" green="0.5607843137254902" blue="0.5607843137254902" alpha="1" colorSpace="calibratedRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="slK-h7-P25">
+                            <rect key="frame" x="59" y="257" width="181" height="23"/>
+                            <subviews>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="aligningSlider_bg" translatesAutoresizingMaskIntoConstraints="NO" id="ppH-Ks-AgY">
+                                    <rect key="frame" x="0.0" y="6" width="181" height="11"/>
+                                </imageView>
+                            </subviews>
+                            <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                            <constraints>
+                                <constraint firstAttribute="trailing" secondItem="ppH-Ks-AgY" secondAttribute="trailing" id="V7Q-w0-1MR"/>
+                                <constraint firstAttribute="height" constant="23" id="jp0-LD-kPW"/>
+                                <constraint firstItem="ppH-Ks-AgY" firstAttribute="centerY" secondItem="slK-h7-P25" secondAttribute="centerY" id="o9x-cZ-qzF"/>
+                                <constraint firstItem="ppH-Ks-AgY" firstAttribute="leading" secondItem="slK-h7-P25" secondAttribute="leading" id="x73-e1-xZ4"/>
+                            </constraints>
+                        </view>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="imR-Qo-Jpf">
+                            <rect key="frame" x="246" y="248.66666666666663" width="27" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="6gg-eX-Lfg"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" image="align_right_bg"/>
+                            <connections>
+                                <action selector="rightAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="pk0-Pk-bEc"/>
+                            </connections>
+                        </button>
+                        <view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KFO-kx-bI9">
+                            <rect key="frame" x="146" y="252" width="8" height="24"/>
+                            <subviews>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pointing_arrow" translatesAutoresizingMaskIntoConstraints="NO" id="Wt9-xF-pLo">
+                                    <rect key="frame" x="0.0" y="0.0" width="8" height="6"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="8" id="L1t-Mv-ebz"/>
+                                        <constraint firstAttribute="height" constant="6" id="g7P-YK-NrW"/>
+                                    </constraints>
+                                </imageView>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="point_stick" translatesAutoresizingMaskIntoConstraints="NO" id="ybl-yR-QRx">
+                                    <rect key="frame" x="2" y="9" width="3" height="15"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="15" id="BdK-jy-WQc"/>
+                                        <constraint firstAttribute="width" constant="3" id="WJp-Yc-BWm"/>
+                                    </constraints>
+                                </imageView>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstAttribute="bottom" secondItem="ybl-yR-QRx" secondAttribute="bottom" id="0PF-Hl-wAk"/>
+                                <constraint firstItem="Wt9-xF-pLo" firstAttribute="top" secondItem="KFO-kx-bI9" secondAttribute="top" id="3yv-JW-2Pw"/>
+                                <constraint firstItem="ybl-yR-QRx" firstAttribute="top" secondItem="Wt9-xF-pLo" secondAttribute="bottom" constant="3" id="6CG-rN-o2i"/>
+                                <constraint firstAttribute="trailing" secondItem="Wt9-xF-pLo" secondAttribute="trailing" id="F71-iq-Bhw"/>
+                                <constraint firstItem="Wt9-xF-pLo" firstAttribute="leading" secondItem="KFO-kx-bI9" secondAttribute="leading" id="UeI-vi-xMw"/>
+                                <constraint firstItem="ybl-yR-QRx" firstAttribute="leading" secondItem="KFO-kx-bI9" secondAttribute="leading" constant="2" id="VlN-V2-8da"/>
+                            </constraints>
+                        </view>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="merge_next" translatesAutoresizingMaskIntoConstraints="NO" id="gDH-uC-2DO">
+                            <rect key="frame" x="8" y="187" width="14" height="14"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MX8-jr-euo">
+                            <rect key="frame" x="0.0" y="174" width="20" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="20" id="OFn-Iy-6CH"/>
+                                <constraint firstAttribute="height" constant="40" id="s34-0G-hQ8"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="hideAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="qyP-te-BXC"/>
+                            </connections>
+                        </button>
+                        <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nyI-H7-gwi">
+                            <rect key="frame" x="13" y="54" width="34" height="34"/>
+                            <subviews>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="slider_bubble" translatesAutoresizingMaskIntoConstraints="NO" id="ahK-NK-SMi">
+                                    <rect key="frame" x="0.0" y="0.0" width="34" height="34"/>
+                                </imageView>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="100" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oYa-cu-i4y">
+                                    <rect key="frame" x="4" y="4" width="26" height="17"/>
+                                    <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
+                                    <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="calibratedRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstItem="ahK-NK-SMi" firstAttribute="leading" secondItem="nyI-H7-gwi" secondAttribute="leading" id="EMl-Ys-Vv1"/>
+                                <constraint firstAttribute="trailing" secondItem="ahK-NK-SMi" secondAttribute="trailing" id="H4j-le-wjD"/>
+                                <constraint firstAttribute="bottom" secondItem="ahK-NK-SMi" secondAttribute="bottom" id="Xgf-qc-1hG"/>
+                                <constraint firstItem="oYa-cu-i4y" firstAttribute="centerX" secondItem="nyI-H7-gwi" secondAttribute="centerX" id="ejz-pz-k8f"/>
+                                <constraint firstItem="ahK-NK-SMi" firstAttribute="top" secondItem="nyI-H7-gwi" secondAttribute="top" id="eku-h5-9mJ"/>
+                                <constraint firstItem="oYa-cu-i4y" firstAttribute="top" secondItem="nyI-H7-gwi" secondAttribute="top" constant="4" id="lH8-xm-u7O"/>
+                            </constraints>
+                        </view>
+                        <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Z4q-fo-efi">
+                            <rect key="frame" x="13" y="137" width="34" height="34"/>
+                            <subviews>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="slider_bubble" translatesAutoresizingMaskIntoConstraints="NO" id="qaQ-ih-J2u">
+                                    <rect key="frame" x="0.0" y="0.0" width="34" height="34"/>
+                                </imageView>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="100" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TSI-LH-Hac">
+                                    <rect key="frame" x="4" y="4" width="26" height="17"/>
+                                    <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
+                                    <color key="textColor" red="0.074509803920000006" green="0.078431372550000003" blue="0.08235294118" alpha="1" colorSpace="calibratedRGB"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstAttribute="bottom" secondItem="qaQ-ih-J2u" secondAttribute="bottom" id="LPG-Hp-6Nw"/>
+                                <constraint firstItem="qaQ-ih-J2u" firstAttribute="top" secondItem="Z4q-fo-efi" secondAttribute="top" id="STk-ay-vnl"/>
+                                <constraint firstItem="qaQ-ih-J2u" firstAttribute="leading" secondItem="Z4q-fo-efi" secondAttribute="leading" id="V5S-da-cyV"/>
+                                <constraint firstItem="TSI-LH-Hac" firstAttribute="top" secondItem="Z4q-fo-efi" secondAttribute="top" constant="4" id="jM5-5i-DJN"/>
+                                <constraint firstItem="TSI-LH-Hac" firstAttribute="centerX" secondItem="Z4q-fo-efi" secondAttribute="centerX" id="pzq-an-1hO"/>
+                                <constraint firstAttribute="trailing" secondItem="qaQ-ih-J2u" secondAttribute="trailing" id="vFJ-Hx-o68"/>
+                            </constraints>
+                        </view>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DzT-go-dxS">
+                            <rect key="frame" x="26" y="90" width="247" height="40"/>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        </view>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="Q7z-0G-roa" firstAttribute="leading" secondItem="53S-gG-Fsg" secondAttribute="trailing" constant="20" id="3Cw-bb-fyr"/>
+                        <constraint firstAttribute="trailing" secondItem="Q7z-0G-roa" secondAttribute="trailing" constant="25" id="3Tk-kO-5hd"/>
+                        <constraint firstItem="DAF-iV-Mgw" firstAttribute="top" secondItem="ucS-Gd-cx3" secondAttribute="top" constant="-5" id="5Uc-4c-awL"/>
+                        <constraint firstItem="ffV-Sq-WQc" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" id="5b1-03-HFl"/>
+                        <constraint firstItem="DAF-iV-Mgw" firstAttribute="trailing" secondItem="ucS-Gd-cx3" secondAttribute="trailing" id="5wr-ml-Vww"/>
+                        <constraint firstItem="KFO-kx-bI9" firstAttribute="bottom" secondItem="slK-h7-P25" secondAttribute="bottom" constant="-4" id="63b-Bu-bya"/>
+                        <constraint firstItem="Mqk-Mg-Pni" firstAttribute="top" secondItem="Q7z-0G-roa" secondAttribute="top" id="77v-Ks-NYn"/>
+                        <constraint firstItem="ucS-Gd-cx3" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="26" id="7rR-H1-Deg"/>
+                        <constraint firstItem="ses-18-GMw" firstAttribute="centerX" secondItem="dNG-od-Opv" secondAttribute="centerX" id="83a-36-AvC"/>
+                        <constraint firstItem="96g-7r-vnm" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="26" id="A0h-S5-779"/>
+                        <constraint firstItem="ucS-Gd-cx3" firstAttribute="top" secondItem="Z4q-fo-efi" secondAttribute="bottom" constant="7" id="Brh-qW-hLO"/>
+                        <constraint firstItem="DAF-iV-Mgw" firstAttribute="bottom" secondItem="ucS-Gd-cx3" secondAttribute="bottom" constant="5" id="Cmy-M2-R1u"/>
+                        <constraint firstItem="oyf-C4-Pe3" firstAttribute="top" secondItem="ffV-Sq-WQc" secondAttribute="bottom" constant="18" id="EGe-wi-jn3"/>
+                        <constraint firstItem="ucS-Gd-cx3" firstAttribute="top" secondItem="if2-R5-Tul" secondAttribute="bottom" constant="16" id="Eyw-26-LDZ"/>
+                        <constraint firstItem="f5Q-wh-6l8" firstAttribute="bottom" secondItem="GuK-qa-jwH" secondAttribute="bottom" id="IBd-T0-PP8"/>
+                        <constraint firstItem="53S-gG-Fsg" firstAttribute="leading" secondItem="Jqd-bC-HWT" secondAttribute="trailing" constant="20" id="JYZ-kj-UgG"/>
+                        <constraint firstItem="VTj-cX-p66" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" id="Juc-95-lKd"/>
+                        <constraint firstAttribute="trailing" secondItem="pQK-tY-S1I" secondAttribute="trailing" constant="16" id="Jxt-fn-sQw"/>
+                        <constraint firstItem="DAF-iV-Mgw" firstAttribute="leading" secondItem="ucS-Gd-cx3" secondAttribute="leading" id="KY2-wc-mdW"/>
+                        <constraint firstItem="ffV-Sq-WQc" firstAttribute="top" secondItem="dNG-od-Opv" secondAttribute="top" constant="43" id="Kfi-tr-DEO"/>
+                        <constraint firstItem="imR-Qo-Jpf" firstAttribute="centerY" secondItem="96g-7r-vnm" secondAttribute="centerY" id="Mff-dk-R7V"/>
+                        <constraint firstItem="YP9-QE-4Lm" firstAttribute="centerX" secondItem="Q7z-0G-roa" secondAttribute="centerX" id="MjW-OK-XDC"/>
+                        <constraint firstItem="YP9-QE-4Lm" firstAttribute="bottom" secondItem="Mqk-Mg-Pni" secondAttribute="bottom" id="NPw-x6-w1G"/>
+                        <constraint firstItem="Jqd-bC-HWT" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="25" id="QHn-11-YHg"/>
+                        <constraint firstAttribute="trailing" secondItem="ffV-Sq-WQc" secondAttribute="trailing" id="QYz-xj-o4P"/>
+                        <constraint firstItem="Jqd-bC-HWT" firstAttribute="top" secondItem="VTj-cX-p66" secondAttribute="bottom" constant="10" id="Qii-R1-RO0"/>
+                        <constraint firstItem="pQK-tY-S1I" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="26" id="QmU-HT-h32"/>
+                        <constraint firstItem="ZEM-tW-WX9" firstAttribute="centerX" secondItem="dNG-od-Opv" secondAttribute="centerX" id="QoR-XG-IZJ"/>
+                        <constraint firstItem="if2-R5-Tul" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="26" id="Tbj-uW-5vm"/>
+                        <constraint firstItem="if2-R5-Tul" firstAttribute="top" secondItem="pQK-tY-S1I" secondAttribute="bottom" constant="20" id="U6A-D9-FMf"/>
+                        <constraint firstItem="MX8-jr-euo" firstAttribute="centerY" secondItem="gDH-uC-2DO" secondAttribute="centerY" id="W2M-EL-mO7"/>
+                        <constraint firstItem="Z4q-fo-efi" firstAttribute="centerX" secondItem="dNG-od-Opv" secondAttribute="leading" constant="30" id="WVs-oc-QrV"/>
+                        <constraint firstItem="slK-h7-P25" firstAttribute="top" secondItem="XPO-nW-Uox" secondAttribute="bottom" constant="12" id="ZPv-OR-vc2"/>
+                        <constraint firstItem="f5Q-wh-6l8" firstAttribute="centerX" secondItem="Jqd-bC-HWT" secondAttribute="centerX" id="aUL-2e-p91"/>
+                        <constraint firstItem="ZEM-tW-WX9" firstAttribute="top" secondItem="dNG-od-Opv" secondAttribute="top" constant="14" id="ddY-o0-jeB"/>
+                        <constraint firstAttribute="trailing" secondItem="VTj-cX-p66" secondAttribute="trailing" id="dw9-31-S22"/>
+                        <constraint firstItem="MX8-jr-euo" firstAttribute="centerY" secondItem="dNG-od-Opv" secondAttribute="centerY" id="e5b-Qo-rqf"/>
+                        <constraint firstItem="53S-gG-Fsg" firstAttribute="top" secondItem="VTj-cX-p66" secondAttribute="bottom" constant="12" id="e5p-cL-oZ8"/>
+                        <constraint firstItem="DzT-go-dxS" firstAttribute="trailing" secondItem="pQK-tY-S1I" secondAttribute="trailing" id="eDg-VV-OiA"/>
+                        <constraint firstItem="oyf-C4-Pe3" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="26" id="fSo-c2-9fa"/>
+                        <constraint firstItem="Jqd-bC-HWT" firstAttribute="centerX" secondItem="GuK-qa-jwH" secondAttribute="centerX" id="gAv-2s-iSN"/>
+                        <constraint firstItem="slK-h7-P25" firstAttribute="leading" secondItem="96g-7r-vnm" secondAttribute="trailing" constant="6" id="gWj-rN-OUA"/>
+                        <constraint firstItem="XPO-nW-Uox" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="26" id="h5x-Vo-RBz"/>
+                        <constraint firstItem="MX8-jr-euo" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" id="hPK-xS-fSR"/>
+                        <constraint firstItem="gDH-uC-2DO" firstAttribute="leading" secondItem="dNG-od-Opv" secondAttribute="leading" constant="8" id="hut-3H-gU3"/>
+                        <constraint firstAttribute="bottom" secondItem="VTj-cX-p66" secondAttribute="bottom" constant="67" id="iPG-s9-hwk"/>
+                        <constraint firstItem="imR-Qo-Jpf" firstAttribute="leading" secondItem="slK-h7-P25" secondAttribute="trailing" constant="6" id="iTt-Wy-aYO"/>
+                        <constraint firstItem="nyI-H7-gwi" firstAttribute="centerX" secondItem="dNG-od-Opv" secondAttribute="leading" constant="30" id="jdI-be-CFN"/>
+                        <constraint firstItem="DzT-go-dxS" firstAttribute="top" secondItem="pQK-tY-S1I" secondAttribute="top" constant="-5" id="kMt-a5-3io"/>
+                        <constraint firstItem="Q7z-0G-roa" firstAttribute="top" secondItem="VTj-cX-p66" secondAttribute="bottom" constant="10" id="kNw-jw-Ek9"/>
+                        <constraint firstItem="pQK-tY-S1I" firstAttribute="top" secondItem="oyf-C4-Pe3" secondAttribute="bottom" constant="16" id="lye-Zg-T75"/>
+                        <constraint firstItem="slK-h7-P25" firstAttribute="centerY" secondItem="96g-7r-vnm" secondAttribute="centerY" id="n4k-sg-aoH"/>
+                        <constraint firstItem="ses-18-GMw" firstAttribute="top" secondItem="slK-h7-P25" secondAttribute="bottom" constant="10" id="nwQ-Zl-n0Z"/>
+                        <constraint firstItem="YP9-QE-4Lm" firstAttribute="top" secondItem="Q7z-0G-roa" secondAttribute="bottom" constant="1" id="pNI-cJ-kcy"/>
+                        <constraint firstItem="Q7z-0G-roa" firstAttribute="centerX" secondItem="Mqk-Mg-Pni" secondAttribute="centerX" id="r2q-WP-Qet"/>
+                        <constraint firstItem="GuK-qa-jwH" firstAttribute="top" secondItem="Jqd-bC-HWT" secondAttribute="top" id="rZ4-6e-EoW"/>
+                        <constraint firstItem="DzT-go-dxS" firstAttribute="bottom" secondItem="pQK-tY-S1I" secondAttribute="bottom" constant="5" id="s20-jc-4Hn"/>
+                        <constraint firstItem="DzT-go-dxS" firstAttribute="leading" secondItem="pQK-tY-S1I" secondAttribute="leading" id="sW8-PU-ecw"/>
+                        <constraint firstAttribute="trailing" secondItem="ucS-Gd-cx3" secondAttribute="trailing" constant="16" id="sh2-B9-nHe"/>
+                        <constraint firstItem="KFO-kx-bI9" firstAttribute="leading" secondItem="slK-h7-P25" secondAttribute="leading" constant="87" id="vNR-7w-pR5"/>
+                        <constraint firstItem="XPO-nW-Uox" firstAttribute="top" secondItem="ucS-Gd-cx3" secondAttribute="bottom" constant="20" id="vQT-M9-wQP"/>
+                        <constraint firstItem="pQK-tY-S1I" firstAttribute="top" secondItem="nyI-H7-gwi" secondAttribute="bottom" constant="7" id="wzO-w2-Rcm"/>
+                        <constraint firstItem="f5Q-wh-6l8" firstAttribute="top" secondItem="Jqd-bC-HWT" secondAttribute="bottom" constant="1" id="xpQ-65-ym0"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="dNG-od-Opv" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="212-6t-WIu"/>
+                <constraint firstAttribute="trailing" secondItem="dNG-od-Opv" secondAttribute="trailing" id="K4F-h5-0Yi"/>
+                <constraint firstItem="dNG-od-Opv" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="jcL-nx-PVA"/>
+                <constraint firstAttribute="bottom" secondItem="dNG-od-Opv" secondAttribute="bottom" id="vSF-hl-liI"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="bgBubble" destination="Z4q-fo-efi" id="9pi-JV-cZS"/>
+                <outlet property="bgCenterX" destination="WVs-oc-QrV" id="fX5-MY-kKa"/>
+                <outlet property="bgGestureView" destination="DAF-iV-Mgw" id="Dcv-IM-HaR"/>
+                <outlet property="bgSlider" destination="ucS-Gd-cx3" id="VCY-3N-zgV"/>
+                <outlet property="bgView" destination="dNG-od-Opv" id="1DR-aZ-txJ"/>
+                <outlet property="bgVolumeBubble" destination="TSI-LH-Hac" id="MMM-Pk-mfI"/>
+                <outlet property="offsetTipsLabel" destination="ses-18-GMw" id="HI9-Fn-LA7"/>
+                <outlet property="pointLeft" destination="vNR-7w-pR5" id="Th0-hj-S4p"/>
+                <outlet property="pointView" destination="KFO-kx-bI9" id="HqS-ma-hGF"/>
+                <outlet property="reRecordImage" destination="Jqd-bC-HWT" id="sff-6V-G78"/>
+                <outlet property="recordBubble" destination="nyI-H7-gwi" id="biq-QL-T1A"/>
+                <outlet property="recordBubbleLabel" destination="oYa-cu-i4y" id="OWU-Kh-jDU"/>
+                <outlet property="recordButton" destination="GuK-qa-jwH" id="2zl-Nw-mob"/>
+                <outlet property="recordCenterX" destination="jdI-be-CFN" id="7w2-4g-DCI"/>
+                <outlet property="recordGestureView" destination="DzT-go-dxS" id="wJ7-Hp-AGw"/>
+                <outlet property="recordImageRight" destination="JYZ-kj-UgG" id="jhx-pI-5Oz"/>
+                <outlet property="recordImgWidth" destination="GDO-qk-rQt" id="WPb-Fx-8yt"/>
+                <outlet property="recordSlider" destination="pQK-tY-S1I" id="kTK-c2-yi8"/>
+                <outlet property="recordTitle" destination="f5Q-wh-6l8" id="loJ-19-rjA"/>
+                <outlet property="sliderView" destination="slK-h7-P25" id="cTD-pY-jtL"/>
+            </connections>
+            <point key="canvasLocation" x="80.152671755725194" y="-176.05633802816902"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="align_left_bg" width="27" height="23"/>
+        <image name="align_right_bg" width="27" height="23"/>
+        <image name="aligningSlider_bg" width="181" height="11"/>
+        <image name="cancleMerge_image" width="24" height="24"/>
+        <image name="merge_next" width="14" height="14"/>
+        <image name="merge_upload" width="24" height="24"/>
+        <image name="point_stick" width="3" height="15"/>
+        <image name="pointing_arrow" width="8" height="6"/>
+        <image name="slider_bubble" width="34" height="34"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 24 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSNewAlertView.h

@@ -0,0 +1,24 @@
+//
+//  KSNewAlertView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/11/1.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^KSNewAlertCallback)(void);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSNewAlertView : UIView
+
++ (instancetype)shareInstance;
+
+- (void)configTitle:(NSString *)headTitle descMessage:(NSString *)textDesc leftButtonTitle:(NSString *)leftTitle rightButtonTitle:(NSString *)rightTitle leftButtonAction:(KSNewAlertCallback)leftCallback rightButtonAction:(KSNewAlertCallback)rightCallback;
+
+- (void)showAlert;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 116 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSNewAlertView.m

@@ -0,0 +1,116 @@
+//
+//  KSNewAlertView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/11/1.
+//
+
+#import "KSNewAlertView.h"
+
+@interface KSNewAlertView ()<UIGestureRecognizerDelegate>
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *descTop;
+
+@property (weak, nonatomic) IBOutlet UIView *displayView;
+
+@property (weak, nonatomic) IBOutlet UILabel *headTitle;
+
+@property (weak, nonatomic) IBOutlet UILabel *descLabel;
+
+@property (nonatomic, copy) KSNewAlertCallback  leftCallback;
+@property (nonatomic, copy) KSNewAlertCallback  rightCallback;
+
+@property (weak, nonatomic) IBOutlet UIButton *leftButton;
+
+@property (weak, nonatomic) IBOutlet UIButton *rightButton;
+
+@end
+
+@implementation KSNewAlertView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
+    tapGesture.delegate = self;
+    [self addGestureRecognizer:tapGesture];
+}
+
++ (instancetype)shareInstance {
+    KSNewAlertView *view = [[[NSBundle mainBundle] loadNibNamed:@"KSNewAlertView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)configTitle:(NSString *)headTitle descMessage:(NSString *)textDesc leftButtonTitle:(NSString *)leftTitle rightButtonTitle:(NSString *)rightTitle leftButtonAction:(KSNewAlertCallback)leftCallback rightButtonAction:(KSNewAlertCallback)rightCallback {
+    if (leftCallback) {
+        self.leftCallback = leftCallback;
+    }
+    if (rightCallback) {
+        self.rightCallback = rightCallback;
+    }
+    self.headTitle.text = [NSString returnNoNullStringWithString:headTitle];
+    if (![NSString isEmptyString:textDesc]) {
+        self.descTop.constant = 20.0f;
+        NSString *messgage = [NSString returnNoNullStringWithString:textDesc];
+        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+        [paragraphStyle setLineSpacing:4];//调整行间距
+        [paragraphStyle setAlignment:NSTextAlignmentCenter];
+        paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
+        
+        NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:messgage attributes:@{NSParagraphStyleAttributeName:paragraphStyle,NSFontAttributeName:[UIFont systemFontOfSize:16.0f],NSForegroundColorAttributeName:HexRGB(0x666666)}];
+        self.descLabel.attributedText = attrStr;
+    }
+    else {
+        self.descTop.constant = 0.0f;
+        self.descLabel.text = @"";
+    }
+    
+    [self.leftButton setTitle:leftTitle forState:UIControlStateNormal];
+    [self.rightButton setTitle:rightTitle forState:UIControlStateNormal];
+}
+
+- (IBAction)leftAction:(id)sender {
+    [self hideAlert];
+    if (self.leftCallback) {
+        self.leftCallback();
+    }
+}
+
+- (IBAction)rightAction:(id)sender {
+    [self hideAlert];
+    if (self.rightCallback) {
+        self.rightCallback();
+    }
+}
+
+- (void)showAlert {
+    UIWindow *window = [NSObject getKeyWindow];
+    [window addSubview:self];
+    [self mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.bottom.mas_equalTo(window);
+    }];
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
+    if ([touch.view isDescendantOfView:self.displayView]) {
+        return NO;
+    }
+    return YES;
+}
+
+- (void)tapAction:(UITapGestureRecognizer *)gesture {
+    [self hideAlert];
+}
+
+- (void)hideAlert {
+    [self removeFromSuperview];
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 126 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSNewAlertView.xib

@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KSNewAlertView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oyS-1q-O1d">
+                    <rect key="frame" x="39" y="341.33333333333331" width="315" height="169.33333333333331"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="确认删除吗?" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Slo-qy-OZT">
+                            <rect key="frame" x="107" y="20" width="101.33333333333331" height="25"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="25" id="va5-wn-xrw"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
+                            <color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请确认是否要退出群聊?" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pLi-4S-pqd">
+                            <rect key="frame" x="20" y="65" width="275" height="19.333333333333329"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                            <color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tNV-Mp-5Ya">
+                            <rect key="frame" x="15" y="109.33333333333337" width="135" height="40"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="取消">
+                                <color key="titleColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            </state>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="20"/>
+                                </userDefinedRuntimeAttribute>
+                                <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
+                                    <real key="value" value="1"/>
+                                </userDefinedRuntimeAttribute>
+                                <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
+                                    <color key="value" red="0.85882352939999995" green="0.85882352939999995" blue="0.85882352939999995" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="leftAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="wsm-xC-b05"/>
+                            </connections>
+                        </button>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hO7-EM-ujs">
+                            <rect key="frame" x="165" y="109.33333333333337" width="135" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="bbj-dv-61F"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="确认" backgroundImage="sureButton_img"/>
+                            <userDefinedRuntimeAttributes>
+                                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                                    <real key="value" value="20"/>
+                                </userDefinedRuntimeAttribute>
+                                <userDefinedRuntimeAttribute type="color" keyPath="borderColor">
+                                    <color key="value" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                </userDefinedRuntimeAttribute>
+                                <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
+                                    <real key="value" value="1"/>
+                                </userDefinedRuntimeAttribute>
+                            </userDefinedRuntimeAttributes>
+                            <connections>
+                                <action selector="rightAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="iAu-Ot-1ZK"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <constraints>
+                        <constraint firstItem="hO7-EM-ujs" firstAttribute="top" secondItem="pLi-4S-pqd" secondAttribute="bottom" constant="25" id="0zT-a3-0QI"/>
+                        <constraint firstItem="Slo-qy-OZT" firstAttribute="top" secondItem="oyS-1q-O1d" secondAttribute="top" constant="20" id="3Ic-7M-3ru"/>
+                        <constraint firstItem="pLi-4S-pqd" firstAttribute="top" secondItem="Slo-qy-OZT" secondAttribute="bottom" constant="20" id="ENP-YL-WnI"/>
+                        <constraint firstAttribute="trailing" secondItem="pLi-4S-pqd" secondAttribute="trailing" constant="20" id="KtC-MI-rKJ"/>
+                        <constraint firstItem="Slo-qy-OZT" firstAttribute="centerX" secondItem="oyS-1q-O1d" secondAttribute="centerX" id="XMU-du-dy0"/>
+                        <constraint firstAttribute="bottom" secondItem="hO7-EM-ujs" secondAttribute="bottom" constant="20" id="aP7-OR-TYi"/>
+                        <constraint firstItem="hO7-EM-ujs" firstAttribute="leading" secondItem="tNV-Mp-5Ya" secondAttribute="trailing" constant="15" id="bJU-bO-VQB"/>
+                        <constraint firstItem="tNV-Mp-5Ya" firstAttribute="width" secondItem="hO7-EM-ujs" secondAttribute="width" id="dvY-Af-9Z7"/>
+                        <constraint firstAttribute="trailing" secondItem="hO7-EM-ujs" secondAttribute="trailing" constant="15" id="fbo-wZ-TGI"/>
+                        <constraint firstItem="tNV-Mp-5Ya" firstAttribute="height" secondItem="hO7-EM-ujs" secondAttribute="height" id="g7z-dP-tRB"/>
+                        <constraint firstAttribute="width" constant="315" id="jbM-ff-SUC"/>
+                        <constraint firstItem="tNV-Mp-5Ya" firstAttribute="bottom" secondItem="hO7-EM-ujs" secondAttribute="bottom" id="lM4-4z-Sk1"/>
+                        <constraint firstItem="pLi-4S-pqd" firstAttribute="leading" secondItem="oyS-1q-O1d" secondAttribute="leading" constant="20" id="lRE-nF-14p"/>
+                        <constraint firstItem="tNV-Mp-5Ya" firstAttribute="leading" secondItem="oyS-1q-O1d" secondAttribute="leading" constant="15" id="oRp-k5-3AZ"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="calibratedRGB"/>
+            <constraints>
+                <constraint firstItem="oyS-1q-O1d" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="Q9a-sZ-UHF"/>
+                <constraint firstItem="oyS-1q-O1d" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="QIB-48-3mW"/>
+            </constraints>
+            <connections>
+                <outlet property="descLabel" destination="pLi-4S-pqd" id="BG3-lI-SsC"/>
+                <outlet property="descTop" destination="ENP-YL-WnI" id="YMK-N9-cmI"/>
+                <outlet property="displayView" destination="oyS-1q-O1d" id="Xwh-GA-I6Y"/>
+                <outlet property="headTitle" destination="Slo-qy-OZT" id="8dF-Ey-fho"/>
+                <outlet property="leftButton" destination="tNV-Mp-5Ya" id="F48-Cn-xgV"/>
+                <outlet property="rightButton" destination="hO7-EM-ujs" id="a0o-gi-Qa4"/>
+            </connections>
+            <point key="canvasLocation" x="60" y="-11"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="sureButton_img" width="122" height="40"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 36 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerSliderView.h

@@ -0,0 +1,36 @@
+//
+//  KSPlayerSliderView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, PLAYERTYPE) {
+    PLAYERTYPE_PLAY,   // 播放
+    PLAYERTYPE_PAUSE,  // 暂停
+    PLAYERTYPE_RATE,   // 进度
+};
+
+typedef void(^KSPlayerSliderActionCallback)(PLAYERTYPE type, NSInteger rate);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSPlayerSliderView : UIView
+
+- (void)configViewDisplayColor:(BOOL)isWhite;
+
+@property (nonatomic, assign) BOOL isPlay;
+
+@property (nonatomic, assign) NSInteger playScheduleTime;
+
++ (instancetype)shareInstance;
+
+- (void)configWithDuration:(NSInteger)totalDuration;
+
+- (void)sliderControlAction:(KSPlayerSliderActionCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 176 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerSliderView.m

@@ -0,0 +1,176 @@
+//
+//  KSPlayerSliderView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import "KSPlayerSliderView.h"
+#import "UIView+KSCovertImage.h"
+
+@interface KSPlayerSliderView ()<UIGestureRecognizerDelegate>
+
+@property (weak, nonatomic) IBOutlet UIButton *stautusButton;
+
+@property (weak, nonatomic) IBOutlet UISlider *progressSlider;
+
+@property (weak, nonatomic) IBOutlet UILabel *playTime;
+
+@property (weak, nonatomic) IBOutlet UILabel *fullTime;
+
+@property (nonatomic, copy) KSPlayerSliderActionCallback callback;
+
+@property (nonatomic, assign) NSInteger totalDuration;
+
+//是否正在拖曳进度条
+@property (nonatomic,assign) BOOL isDragingSlider;
+//视频进度条的单击手势&播放器的单击手势
+@property (nonatomic,strong) UITapGestureRecognizer *progressTap;
+
+@property (nonatomic, assign) BOOL isWhite;
+
+@end
+
+@implementation KSPlayerSliderView
+
+- (void)configViewDisplayColor:(BOOL)isWhite {
+    self.isWhite = isWhite;
+    if (isWhite) {
+        UIView *sliderImg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)];
+        sliderImg.layer.cornerRadius = 6.0f;
+        sliderImg.layer.masksToBounds = YES;
+        sliderImg.backgroundColor = HexRGB(0xffffff);
+        UIImage *image = [UIView convertViewToImage:sliderImg];
+        [self.progressSlider setThumbImage:image forState:UIControlStateNormal];
+        [self.progressSlider setThumbImage:image forState:UIControlStateHighlighted];
+        [self.progressSlider setMinimumTrackTintColor:HexRGB(0xffffff)];
+        [self.progressSlider setMaximumTrackTintColor:HexRGBAlpha(0xffffff, 0.3f)];
+        self.playTime.textColor = HexRGB(0xffffff);
+        self.fullTime.textColor = HexRGB(0xffffff);
+    }
+    else {
+        UIView *sliderImg = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 12, 12)];
+        sliderImg.layer.cornerRadius = 6.0f;
+        sliderImg.layer.masksToBounds = YES;
+        sliderImg.backgroundColor = HexRGB(0x269EFE);
+        UIImage *image = [UIView convertViewToImage:sliderImg];
+        [self.progressSlider setThumbImage:image forState:UIControlStateNormal];
+        [self.progressSlider setThumbImage:image forState:UIControlStateHighlighted];
+        [self.progressSlider setMinimumTrackTintColor:HexRGB(0x2DC7AA)];
+        [self.progressSlider setMaximumTrackTintColor:HexRGB(0xffffff)];
+        
+        self.playTime.textColor = HexRGB(0x131415);
+        self.fullTime.textColor = HexRGB(0x131415);
+    }
+    self.isPlay = NO;
+}
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    self.playScheduleTime = 0;
+    
+    //进度条的拖拽事件
+    [self.progressSlider addTarget:self action:@selector(stratDragSlide:)  forControlEvents:UIControlEventValueChanged];
+    //进度条的点击事件
+    [self.progressSlider addTarget:self action:@selector(updateProgress:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
+    //给进度条添加单击手势
+    self.progressTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(actionTapGesture:)];
+    self.progressTap.delegate = self;
+    [self.progressSlider addGestureRecognizer:self.progressTap];
+}
+
+
+
++ (instancetype)shareInstance {
+    KSPlayerSliderView *view = [[[NSBundle mainBundle] loadNibNamed:@"KSPlayerSliderView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)configWithDuration:(NSInteger)totalDuration {
+    self.totalDuration = totalDuration;
+    self.progressSlider.minimumValue = 0;
+    self.progressSlider.maximumValue = totalDuration;
+}
+
+
+
+- (void)sliderControlAction:(KSPlayerSliderActionCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)playButtonAction:(id)sender {
+    self.isPlay = !self.isPlay;
+    if (self.callback) {
+        PLAYERTYPE type = self.isPlay ? PLAYERTYPE_PLAY : PLAYERTYPE_PAUSE;
+        self.callback(type, self.playScheduleTime);
+    }
+}
+
+#pragma mark--开始拖曳sidle
+- (void)stratDragSlide:(UISlider *)slider{
+    self.isDragingSlider = YES;
+}
+
+#pragma mark
+#pragma mark - 播放进度
+- (void)updateProgress:(UISlider *)slider {
+    self.playScheduleTime = slider.value;
+    self.isDragingSlider = NO;
+    if (self.callback) {
+        self.callback(PLAYERTYPE_RATE, self.playScheduleTime);
+    }
+}
+
+//视频进度条的点击事件
+- (void)actionTapGesture:(UITapGestureRecognizer *)sender {
+    CGPoint touchLocation = [sender locationInView:self.progressSlider];
+    CGFloat value = (self.progressSlider.maximumValue - self.progressSlider.minimumValue) * (touchLocation.x/self.progressSlider.frame.size.width);
+    [self.progressSlider setValue:value animated:YES];
+    [self configScheduleTime:self.progressSlider.value];
+    if (self.callback) {
+        self.callback(PLAYERTYPE_RATE, self.playScheduleTime);
+    }
+}
+
+
+- (void)configScheduleTime:(NSInteger)playTime {
+    _playScheduleTime = playTime;
+    self.playTime.text  = [NSString stringWithFormat:@"%.2d:%.2d", (int)playTime / 60, (int)playTime % 60];
+}
+
+- (void)setIsPlay:(BOOL)isPlay {
+    _isPlay = isPlay;
+    NSString *imgName = @"";
+    if (isPlay) {
+        imgName = self.isWhite ? @"mergePlayer_pause_white" : @"mergePlayer_pause";
+    }
+    else {
+        imgName = self.isWhite ? @"mergePlayer_play_white" : @"mergePlayer_play";
+    }
+    [self.stautusButton setImage:[UIImage imageNamed:imgName] forState:UIControlStateNormal];
+}
+
+- (void)setTotalDuration:(NSInteger)totalDuration {
+    _totalDuration = totalDuration;
+    self.fullTime.text  = [NSString stringWithFormat:@"%.2d:%.2d", (int)totalDuration / 60, (int)totalDuration % 60];
+}
+
+- (void)setPlayScheduleTime:(NSInteger)playScheduleTime {
+    _playScheduleTime = playScheduleTime;
+    self.playTime.text  = [NSString stringWithFormat:@"%.2d:%.2d", (int)playScheduleTime / 60, (int)playScheduleTime % 60];
+    if (_isDragingSlider == NO) {
+        self.progressSlider.value = playScheduleTime;
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 77 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerSliderView.xib

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="KSPlayerSliderView">
+            <rect key="frame" x="0.0" y="0.0" width="393" height="70"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hlN-nG-Uba">
+                    <rect key="frame" x="13" y="10" width="44" height="44"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="44" id="Maf-g9-agS"/>
+                        <constraint firstAttribute="height" constant="44" id="Rwh-PH-1hM"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="mergePlayer_play"/>
+                    <connections>
+                        <action selector="playButtonAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="TLj-tv-IXc"/>
+                    </connections>
+                </button>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VYy-l7-dPg">
+                    <rect key="frame" x="333" y="23.666666666666671" width="40" height="17"/>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                    <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hkd-3d-Ojv">
+                    <rect key="frame" x="57" y="22" width="40" height="20"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="20" id="yDl-lG-zvk"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
+                    <color key="textColor" red="0.074509803921568626" green="0.078431372549019607" blue="0.082352941176470587" alpha="1" colorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="i72-ge-RXd">
+                    <rect key="frame" x="107" y="17" width="216" height="31"/>
+                    <color key="minimumTrackTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                    <color key="maximumTrackTintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <color key="thumbTintColor" red="0.1764705882" green="0.78039215689999997" blue="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                </slider>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="VYy-l7-dPg" firstAttribute="centerY" secondItem="hlN-nG-Uba" secondAttribute="centerY" id="2TK-HN-6XO"/>
+                <constraint firstItem="VYy-l7-dPg" firstAttribute="leading" secondItem="i72-ge-RXd" secondAttribute="trailing" constant="12" id="53c-Kf-vCV"/>
+                <constraint firstItem="i72-ge-RXd" firstAttribute="centerY" secondItem="hlN-nG-Uba" secondAttribute="centerY" id="6SB-Vq-2PR"/>
+                <constraint firstItem="Hkd-3d-Ojv" firstAttribute="centerY" secondItem="hlN-nG-Uba" secondAttribute="centerY" id="6u6-V5-6qZ"/>
+                <constraint firstItem="Hkd-3d-Ojv" firstAttribute="leading" secondItem="hlN-nG-Uba" secondAttribute="trailing" id="ADX-2i-LTc"/>
+                <constraint firstItem="hlN-nG-Uba" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="13" id="W8S-zw-Y9b"/>
+                <constraint firstItem="i72-ge-RXd" firstAttribute="leading" secondItem="Hkd-3d-Ojv" secondAttribute="trailing" constant="12" id="YZR-Gg-TPp"/>
+                <constraint firstAttribute="trailing" secondItem="VYy-l7-dPg" secondAttribute="trailing" constant="20" id="eQN-Si-ypK"/>
+                <constraint firstItem="hlN-nG-Uba" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="lFP-fr-zQD"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="fullTime" destination="VYy-l7-dPg" id="db8-BT-kau"/>
+                <outlet property="playTime" destination="Hkd-3d-Ojv" id="s5o-Mf-KKc"/>
+                <outlet property="progressSlider" destination="i72-ge-RXd" id="yBl-2b-xmP"/>
+                <outlet property="stautusButton" destination="hlN-nG-Uba" id="bJJ-Ko-eux"/>
+            </connections>
+            <point key="canvasLocation" x="26.717557251908396" y="109.85915492957747"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="mergePlayer_play" width="18" height="18"/>
+    </resources>
+</document>

+ 16 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerView.h

@@ -0,0 +1,16 @@
+//
+//  KSPlayerView.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KSPlayerView : UIView
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 37 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/KSPlayerView.m

@@ -0,0 +1,37 @@
+//
+//  KSPlayerView.m
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/8/29.
+//
+
+#import "KSPlayerView.h"
+
+@implementation KSPlayerView
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        [self configUI];
+    }
+    return self;
+}
+
+- (void)configUI {
+//    self.backgroundColor = [UIColor cyanColor];
+    UIImageView *imageview = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"merge_playerBg"]];
+    imageview.contentMode = UIViewContentModeScaleToFill;
+    [self addSubview:imageview];
+    [imageview mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.bottom.mas_equalTo(self);
+    }];
+}
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 36 - 0
KulexiuForStudent/KulexiuForStudent/Common/MediaMerge/AudioMerge/MusicPublistAlert.h

@@ -0,0 +1,36 @@
+//
+//  MusicPublistAlert.h
+//  KulexiuSchoolStudent
+//
+//  Created by 王智 on 2023/11/1.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, PUBLISH_ACTION) {
+    PUBLISH_ACTION_CHOOSEIMG,
+    PUBLISH_ACTION_PUBLISH,
+};
+
+typedef void(^MusicPublishCallback)(PUBLISH_ACTION type);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MusicPublistAlert : UIView
+
+@property (weak, nonatomic) IBOutlet UITextView *textView;
+
+@property (weak, nonatomic) IBOutlet UIImageView *musicImage;
+
+@property (weak, nonatomic) IBOutlet UILabel *musicName;
+
+@property (weak, nonatomic) IBOutlet UILabel *userName;
+
++ (instancetype)shareInstance;
+
+- (void)chooseAction:(MusicPublishCallback)callback;
+
+- (void)showAlert;
+@end
+
+NS_ASSUME_NONNULL_END

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác