Explorar el Código

Merge branch 'tone-tuning' into developer

# Conflicts:
#	KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj
#	KulexiuForStudent/KulexiuForStudent.xcworkspace/xcuserdata/wangzhi.xcuserdatad/UserInterfaceState.xcuserstate
#	KulexiuForStudent/KulexiuForStudent.xcworkspace/xcuserdata/wangzhi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
Steven hace 2 años
padre
commit
b534e405e8
Se han modificado 100 ficheros con 3377 adiciones y 255 borrados
  1. 201 51
      KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj
  2. 1 1
      KulexiuForStudent/KulexiuForStudent.xcodeproj/xcshareddata/xcschemes/KulexiuForStudent.xcscheme
  3. 14 0
      KulexiuForStudent/KulexiuForStudent.xcworkspace/xcshareddata/swiftpm/Package.resolved
  4. 1 1
      KulexiuForStudent/KulexiuForStudent.xcworkspace/xcuserdata/wangzhi.xcuserdatad/WorkspaceSettings.xcsettings
  5. 0 196
      KulexiuForStudent/KulexiuForStudent.xcworkspace/xcuserdata/wangzhi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
  6. 6 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/Contents.json
  7. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/forkButton_bg.imageset/Contents.json
  8. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/forkButton_bg.imageset/forkButton_bg@2x.png
  9. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/forkButton_bg.imageset/forkButton_bg@3x.png
  10. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_choose.imageset/Contents.json
  11. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_choose.imageset/fork_choose@2x.png
  12. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_choose.imageset/fork_choose@3x.png
  13. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_play.imageset/Contents.json
  14. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_play.imageset/fork_play@2x.png
  15. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_play.imageset/fork_play@3x.png
  16. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unPlay.imageset/Contents.json
  17. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unPlay.imageset/fork_unPlay@2x.png
  18. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unPlay.imageset/fork_unPlay@3x.png
  19. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unchoose.imageset/Contents.json
  20. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unchoose.imageset/fork_unchoose@2x.png
  21. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unchoose.imageset/fork_unchoose@3x.png
  22. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/freguqnce_add.imageset/Contents.json
  23. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/freguqnce_add.imageset/freguqnce_add@2x.png
  24. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/freguqnce_add.imageset/freguqnce_add@3x.png
  25. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/frequence_minus.imageset/Contents.json
  26. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/frequence_minus.imageset/frequence_minus@2x.png
  27. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/frequence_minus.imageset/frequence_minus@3x.png
  28. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/plate_bg.imageset/Contents.json
  29. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/plate_bg.imageset/plate_bg@2x.png
  30. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/plate_bg.imageset/plate_bg@3x.png
  31. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/playButton_bg.imageset/Contents.json
  32. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/playButton_bg.imageset/playButton_bg@2x.png
  33. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/playButton_bg.imageset/playButton_bg@3x.png
  34. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/toning_bottom.imageset/Contents.json
  35. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/toning_bottom.imageset/toning_bottom@2x.png
  36. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/toning_bottom.imageset/toning_bottom@3x.png
  37. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_decorate.imageset/Contents.json
  38. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_decorate.imageset/tuning_decorate@2x.png
  39. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_decorate.imageset/tuning_decorate@3x.png
  40. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_transfer.imageset/Contents.json
  41. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_transfer.imageset/tuning_transfer@2x.png
  42. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_transfer.imageset/tuning_transfer@3x.png
  43. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_warning.imageset/Contents.json
  44. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_warning.imageset/tuning_warning@2x.png
  45. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_warning.imageset/tuning_warning@3x.png
  46. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/tuning_setting.imageset/Contents.json
  47. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/tuning_setting.imageset/tuning_setting@2x.png
  48. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/tuning_setting.imageset/tuning_setting@3x.png
  49. 6 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/Contents.json
  50. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/metronome_image.imageset/Contents.json
  51. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/metronome_image.imageset/metronome_image@2x.png
  52. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/metronome_image.imageset/metronome_image@3x.png
  53. 22 0
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/tone_tuning.imageset/Contents.json
  54. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/tone_tuning.imageset/tone_tuning@2x.png
  55. BIN
      KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/tone_tuning.imageset/tone_tuning@3x.png
  56. 1 1
      KulexiuForStudent/KulexiuForStudent/Common/Base/KSWebNavView.m
  57. 1 0
      KulexiuForStudent/KulexiuForStudent/Common/Define/PrefixHeader.pch
  58. 2 2
      KulexiuForStudent/KulexiuForStudent/Module/Home/Controller/HomeViewController.m
  59. 16 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/SmallToolViewController.h
  60. 66 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/SmallToolViewController.m
  61. 16 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/ToneTuningViewController.h
  62. 188 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/ToneTuningViewController.m
  63. 4 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/Model/TuningFunction/KulexiuForStudent-Bridging-Header.h
  64. 213 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/Model/TuningFunction/Tuner.swift
  65. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetBottomButtonView.h
  66. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetBottomButtonView.m
  67. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetBottomButtonView.xib
  68. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetDotView.h
  69. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetDotView.m
  70. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetDotView.xib
  71. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetFunctionView.h
  72. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetFunctionView.m
  73. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetFunctionView.xib
  74. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetNavView.h
  75. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetNavView.m
  76. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetNavView.xib
  77. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetSpeedView.h
  78. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetSpeedView.m
  79. 0 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetSpeedView.xib
  80. 27 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/SmallToolBodyView.h
  81. 77 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/SmallToolBodyView.m
  82. 168 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/SmallToolBodyView.xib
  83. 77 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeView.h
  84. 820 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeView.m
  85. 58 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyle.h
  86. 32 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.h
  87. 131 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.m
  88. 29 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.h
  89. 128 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.m
  90. 19 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/DialPlateView.h
  91. 65 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/DialPlateView.m
  92. 40 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/ToneTuningBodyView.h
  93. 72 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/ToneTuningBodyView.m
  94. 377 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/ToneTuningBodyView.xib
  95. 22 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/TuningNavView.h
  96. 52 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/TuningNavView.m
  97. 92 0
      KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/TuningNavView.xib
  98. 1 1
      KulexiuForStudent/Podfile
  99. 1 1
      KulexiuForStudent/Podfile.lock
  100. 1 1
      KulexiuForStudent/Pods/Manifest.lock

+ 201 - 51
KulexiuForStudent/KulexiuForStudent.xcodeproj/project.pbxproj

@@ -438,6 +438,7 @@
 		BC27A076280FF61300F91E27 /* AccompanyDetailBottomView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC27A075280FF61300F91E27 /* AccompanyDetailBottomView.xib */; };
 		BC27A079280FFA2200F91E27 /* EvaluateDetailModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BC27A078280FFA2200F91E27 /* EvaluateDetailModel.m */; };
 		BC28582B2809036D0024697C /* StudentInfoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BC28582A2809036D0024697C /* StudentInfoModel.m */; };
+		BC2BE91C28F951DE00CB5F92 /* DialPlateView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2BE91B28F951DE00CB5F92 /* DialPlateView.m */; };
 		BC2DFF4B28BDFE740056105A /* HomeTeacherLiveModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2DFF4A28BDFE730056105A /* HomeTeacherLiveModel.m */; };
 		BC2DFF4E28BE068D0056105A /* TeacherStyleFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2DFF4D28BE068D0056105A /* TeacherStyleFlowLayout.m */; };
 		BC2DFF5728BE143A0056105A /* HomeTempLiveCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2DFF5528BE143A0056105A /* HomeTempLiveCell.m */; };
@@ -466,16 +467,6 @@
 		BC49BAEC28D98C500031FF06 /* KSMetronomePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = BC49BAEB28D98C500031FF06 /* KSMetronomePlayer.m */; };
 		BC4CC417288FD689004AD8EC /* LaunchAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CC416288FD689004AD8EC /* LaunchAnimationView.m */; };
 		BC4CF28E28D072C000961C61 /* WidgetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CF28D28D072C000961C61 /* WidgetViewController.m */; };
-		BC4CF29128D072EF00961C61 /* WidgetNavView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CF29028D072EF00961C61 /* WidgetNavView.m */; };
-		BC4CF29328D072F600961C61 /* WidgetNavView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC4CF29228D072F600961C61 /* WidgetNavView.xib */; };
-		BC4CF29628D074DC00961C61 /* WidgetDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CF29528D074DC00961C61 /* WidgetDotView.m */; };
-		BC4CF29828D074E700961C61 /* WidgetDotView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC4CF29728D074E700961C61 /* WidgetDotView.xib */; };
-		BC4CF29B28D0757800961C61 /* WidgetSpeedView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CF29A28D0757800961C61 /* WidgetSpeedView.m */; };
-		BC4CF29D28D0758700961C61 /* WidgetSpeedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC4CF29C28D0758700961C61 /* WidgetSpeedView.xib */; };
-		BC4CF2A028D075CC00961C61 /* WidgetFunctionView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CF29F28D075CC00961C61 /* WidgetFunctionView.m */; };
-		BC4CF2A228D075D400961C61 /* WidgetFunctionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC4CF2A128D075D400961C61 /* WidgetFunctionView.xib */; };
-		BC4CF2A528D1B07800961C61 /* WidgetBottomButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4CF2A428D1B07800961C61 /* WidgetBottomButtonView.m */; };
-		BC4CF2A728D1B08000961C61 /* WidgetBottomButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC4CF2A628D1B08000961C61 /* WidgetBottomButtonView.xib */; };
 		BC50171227FC0D5600F8BCBC /* SubjectChooseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC50171127FC0D5600F8BCBC /* SubjectChooseViewController.m */; };
 		BC50171527FC0D8300F8BCBC /* SubjectChooseBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC50171427FC0D8300F8BCBC /* SubjectChooseBodyView.m */; };
 		BC50171727FC0D8E00F8BCBC /* SubjectChooseBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC50171627FC0D8D00F8BCBC /* SubjectChooseBodyView.xib */; };
@@ -889,6 +880,12 @@
 		BCD457AB286469600010B493 /* PublicNoticeView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD457A92864695F0010B493 /* PublicNoticeView.m */; };
 		BCD457AC286469600010B493 /* PublicNoticeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCD457AA286469600010B493 /* PublicNoticeView.xib */; };
 		BCD457AF28646B580010B493 /* NoticeSourceModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD457AE28646B580010B493 /* NoticeSourceModel.m */; };
+		BCD9294F28F8FCA4006793E4 /* AudioKit in Frameworks */ = {isa = PBXBuildFile; productRef = BCD9294E28F8FCA4006793E4 /* AudioKit */; };
+		BCD9295228F90202006793E4 /* ToneTuningBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD9295128F90202006793E4 /* ToneTuningBodyView.m */; };
+		BCD9295428F90209006793E4 /* ToneTuningBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCD9295328F90209006793E4 /* ToneTuningBodyView.xib */; };
+		BCD9295D28F9447B006793E4 /* WMGaugeViewStyleFlatThin.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD9295828F9447B006793E4 /* WMGaugeViewStyleFlatThin.m */; };
+		BCD9295E28F9447B006793E4 /* WMGaugeViewStyle3D.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD9295A28F9447B006793E4 /* WMGaugeViewStyle3D.m */; };
+		BCD9295F28F9447B006793E4 /* WMGaugeView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD9295B28F9447B006793E4 /* WMGaugeView.m */; };
 		BCD959C928DB071B00B70314 /* MusicTagView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD959C828DB071B00B70314 /* MusicTagView.m */; };
 		BCD959CC28DB0BAB00B70314 /* KSImageShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD959CB28DB0BAB00B70314 /* KSImageShareViewController.m */; };
 		BCDE35862893B0E200A9A560 /* KSLoadingSuccessView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCDE35852893B0E200A9A560 /* KSLoadingSuccessView.m */; };
@@ -945,6 +942,23 @@
 		BCFE54002814E1BE00AD6786 /* HomeVideoGroupModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFE53FE2814E1BE00AD6786 /* HomeVideoGroupModel.m */; };
 		BCFE540328152A8500AD6786 /* KSOrderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFE540228152A8500AD6786 /* KSOrderManager.m */; };
 		BCFE5406281545C600AD6786 /* HomeAlbumModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFE5405281545C500AD6786 /* HomeAlbumModel.m */; };
+		BCFEED4D28F7E4720078A2B7 /* SmallToolViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED4C28F7E4720078A2B7 /* SmallToolViewController.m */; };
+		BCFEED5E28F7E4910078A2B7 /* WidgetFunctionView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED4F28F7E4910078A2B7 /* WidgetFunctionView.m */; };
+		BCFEED5F28F7E4910078A2B7 /* WidgetNavView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED5028F7E4910078A2B7 /* WidgetNavView.m */; };
+		BCFEED6028F7E4910078A2B7 /* WidgetSpeedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED5128F7E4910078A2B7 /* WidgetSpeedView.xib */; };
+		BCFEED6128F7E4910078A2B7 /* WidgetBottomButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED5228F7E4910078A2B7 /* WidgetBottomButtonView.m */; };
+		BCFEED6228F7E4910078A2B7 /* WidgetDotView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED5428F7E4910078A2B7 /* WidgetDotView.m */; };
+		BCFEED6328F7E4910078A2B7 /* WidgetNavView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED5528F7E4910078A2B7 /* WidgetNavView.xib */; };
+		BCFEED6428F7E4910078A2B7 /* WidgetFunctionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED5628F7E4910078A2B7 /* WidgetFunctionView.xib */; };
+		BCFEED6528F7E4910078A2B7 /* WidgetDotView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED5728F7E4910078A2B7 /* WidgetDotView.xib */; };
+		BCFEED6628F7E4910078A2B7 /* WidgetSpeedView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED5828F7E4910078A2B7 /* WidgetSpeedView.m */; };
+		BCFEED6728F7E4910078A2B7 /* WidgetBottomButtonView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED5A28F7E4910078A2B7 /* WidgetBottomButtonView.xib */; };
+		BCFEED6A28F7E4F40078A2B7 /* SmallToolBodyView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED6928F7E4F40078A2B7 /* SmallToolBodyView.m */; };
+		BCFEED6C28F7E51F0078A2B7 /* SmallToolBodyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED6B28F7E51F0078A2B7 /* SmallToolBodyView.xib */; };
+		BCFEED6F28F7E95A0078A2B7 /* ToneTuningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED6E28F7E95A0078A2B7 /* ToneTuningViewController.m */; };
+		BCFEED7328F7F17C0078A2B7 /* TuningNavView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED7228F7F17C0078A2B7 /* TuningNavView.m */; };
+		BCFEED7528F7F1820078A2B7 /* TuningNavView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCFEED7428F7F1820078A2B7 /* TuningNavView.xib */; };
+		BCFEED7C28F810D70078A2B7 /* Tuner.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFEED7B28F810D70078A2B7 /* Tuner.swift */; };
 		C56C141D9D9D478077F14C1E /* Pods_KulexiuForStudent_KulexiuForStudentUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9C170A749B6C49F17AC3246 /* Pods_KulexiuForStudent_KulexiuForStudentUITests.framework */; };
 /* End PBXBuildFile section */
 
@@ -1769,6 +1783,8 @@
 		BC27A078280FFA2200F91E27 /* EvaluateDetailModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EvaluateDetailModel.m; sourceTree = "<group>"; };
 		BC2858292809036C0024697C /* StudentInfoModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StudentInfoModel.h; sourceTree = "<group>"; };
 		BC28582A2809036D0024697C /* StudentInfoModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StudentInfoModel.m; sourceTree = "<group>"; };
+		BC2BE91A28F951DE00CB5F92 /* DialPlateView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DialPlateView.h; sourceTree = "<group>"; };
+		BC2BE91B28F951DE00CB5F92 /* DialPlateView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DialPlateView.m; sourceTree = "<group>"; };
 		BC2DFF4928BDFE730056105A /* HomeTeacherLiveModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeTeacherLiveModel.h; sourceTree = "<group>"; };
 		BC2DFF4A28BDFE730056105A /* HomeTeacherLiveModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeTeacherLiveModel.m; sourceTree = "<group>"; };
 		BC2DFF4C28BE068D0056105A /* TeacherStyleFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TeacherStyleFlowLayout.h; sourceTree = "<group>"; };
@@ -1817,21 +1833,6 @@
 		BC4CC416288FD689004AD8EC /* LaunchAnimationView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchAnimationView.m; sourceTree = "<group>"; };
 		BC4CF28C28D072C000961C61 /* WidgetViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetViewController.h; sourceTree = "<group>"; };
 		BC4CF28D28D072C000961C61 /* WidgetViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetViewController.m; sourceTree = "<group>"; };
-		BC4CF28F28D072EF00961C61 /* WidgetNavView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetNavView.h; sourceTree = "<group>"; };
-		BC4CF29028D072EF00961C61 /* WidgetNavView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetNavView.m; sourceTree = "<group>"; };
-		BC4CF29228D072F600961C61 /* WidgetNavView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WidgetNavView.xib; sourceTree = "<group>"; };
-		BC4CF29428D074DC00961C61 /* WidgetDotView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetDotView.h; sourceTree = "<group>"; };
-		BC4CF29528D074DC00961C61 /* WidgetDotView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetDotView.m; sourceTree = "<group>"; };
-		BC4CF29728D074E700961C61 /* WidgetDotView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WidgetDotView.xib; sourceTree = "<group>"; };
-		BC4CF29928D0757800961C61 /* WidgetSpeedView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetSpeedView.h; sourceTree = "<group>"; };
-		BC4CF29A28D0757800961C61 /* WidgetSpeedView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetSpeedView.m; sourceTree = "<group>"; };
-		BC4CF29C28D0758700961C61 /* WidgetSpeedView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WidgetSpeedView.xib; sourceTree = "<group>"; };
-		BC4CF29E28D075CC00961C61 /* WidgetFunctionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetFunctionView.h; sourceTree = "<group>"; };
-		BC4CF29F28D075CC00961C61 /* WidgetFunctionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetFunctionView.m; sourceTree = "<group>"; };
-		BC4CF2A128D075D400961C61 /* WidgetFunctionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WidgetFunctionView.xib; sourceTree = "<group>"; };
-		BC4CF2A328D1B07800961C61 /* WidgetBottomButtonView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetBottomButtonView.h; sourceTree = "<group>"; };
-		BC4CF2A428D1B07800961C61 /* WidgetBottomButtonView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetBottomButtonView.m; sourceTree = "<group>"; };
-		BC4CF2A628D1B08000961C61 /* WidgetBottomButtonView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WidgetBottomButtonView.xib; sourceTree = "<group>"; };
 		BC50171027FC0D5600F8BCBC /* SubjectChooseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SubjectChooseViewController.h; sourceTree = "<group>"; };
 		BC50171127FC0D5600F8BCBC /* SubjectChooseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SubjectChooseViewController.m; sourceTree = "<group>"; };
 		BC50171327FC0D8300F8BCBC /* SubjectChooseBodyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SubjectChooseBodyView.h; sourceTree = "<group>"; };
@@ -2476,6 +2477,16 @@
 		BCD457AA286469600010B493 /* PublicNoticeView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PublicNoticeView.xib; sourceTree = "<group>"; };
 		BCD457AD28646B580010B493 /* NoticeSourceModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoticeSourceModel.h; sourceTree = "<group>"; };
 		BCD457AE28646B580010B493 /* NoticeSourceModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoticeSourceModel.m; sourceTree = "<group>"; };
+		BCD9295028F90202006793E4 /* ToneTuningBodyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToneTuningBodyView.h; sourceTree = "<group>"; };
+		BCD9295128F90202006793E4 /* ToneTuningBodyView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ToneTuningBodyView.m; sourceTree = "<group>"; };
+		BCD9295328F90209006793E4 /* ToneTuningBodyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ToneTuningBodyView.xib; sourceTree = "<group>"; };
+		BCD9295628F9447B006793E4 /* WMGaugeViewStyle3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeViewStyle3D.h; sourceTree = "<group>"; };
+		BCD9295728F9447B006793E4 /* WMGaugeViewStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeViewStyle.h; sourceTree = "<group>"; };
+		BCD9295828F9447B006793E4 /* WMGaugeViewStyleFlatThin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMGaugeViewStyleFlatThin.m; sourceTree = "<group>"; };
+		BCD9295928F9447B006793E4 /* WMGaugeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeView.h; sourceTree = "<group>"; };
+		BCD9295A28F9447B006793E4 /* WMGaugeViewStyle3D.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMGaugeViewStyle3D.m; sourceTree = "<group>"; };
+		BCD9295B28F9447B006793E4 /* WMGaugeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMGaugeView.m; sourceTree = "<group>"; };
+		BCD9295C28F9447B006793E4 /* WMGaugeViewStyleFlatThin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMGaugeViewStyleFlatThin.h; sourceTree = "<group>"; };
 		BCD959C728DB071B00B70314 /* MusicTagView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MusicTagView.h; sourceTree = "<group>"; };
 		BCD959C828DB071B00B70314 /* MusicTagView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MusicTagView.m; sourceTree = "<group>"; };
 		BCD959CA28DB0BAB00B70314 /* KSImageShareViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KSImageShareViewController.h; sourceTree = "<group>"; };
@@ -2558,6 +2569,33 @@
 		BCFE540228152A8500AD6786 /* KSOrderManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSOrderManager.m; sourceTree = "<group>"; };
 		BCFE5404281545C500AD6786 /* HomeAlbumModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HomeAlbumModel.h; sourceTree = "<group>"; };
 		BCFE5405281545C500AD6786 /* HomeAlbumModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HomeAlbumModel.m; sourceTree = "<group>"; };
+		BCFEED4B28F7E4720078A2B7 /* SmallToolViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SmallToolViewController.h; sourceTree = "<group>"; };
+		BCFEED4C28F7E4720078A2B7 /* SmallToolViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SmallToolViewController.m; sourceTree = "<group>"; };
+		BCFEED4F28F7E4910078A2B7 /* WidgetFunctionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetFunctionView.m; sourceTree = "<group>"; };
+		BCFEED5028F7E4910078A2B7 /* WidgetNavView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetNavView.m; sourceTree = "<group>"; };
+		BCFEED5128F7E4910078A2B7 /* WidgetSpeedView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetSpeedView.xib; sourceTree = "<group>"; };
+		BCFEED5228F7E4910078A2B7 /* WidgetBottomButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetBottomButtonView.m; sourceTree = "<group>"; };
+		BCFEED5328F7E4910078A2B7 /* WidgetSpeedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetSpeedView.h; sourceTree = "<group>"; };
+		BCFEED5428F7E4910078A2B7 /* WidgetDotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetDotView.m; sourceTree = "<group>"; };
+		BCFEED5528F7E4910078A2B7 /* WidgetNavView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetNavView.xib; sourceTree = "<group>"; };
+		BCFEED5628F7E4910078A2B7 /* WidgetFunctionView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetFunctionView.xib; sourceTree = "<group>"; };
+		BCFEED5728F7E4910078A2B7 /* WidgetDotView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetDotView.xib; sourceTree = "<group>"; };
+		BCFEED5828F7E4910078A2B7 /* WidgetSpeedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WidgetSpeedView.m; sourceTree = "<group>"; };
+		BCFEED5928F7E4910078A2B7 /* WidgetBottomButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetBottomButtonView.h; sourceTree = "<group>"; };
+		BCFEED5A28F7E4910078A2B7 /* WidgetBottomButtonView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WidgetBottomButtonView.xib; sourceTree = "<group>"; };
+		BCFEED5B28F7E4910078A2B7 /* WidgetNavView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetNavView.h; sourceTree = "<group>"; };
+		BCFEED5C28F7E4910078A2B7 /* WidgetFunctionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetFunctionView.h; sourceTree = "<group>"; };
+		BCFEED5D28F7E4910078A2B7 /* WidgetDotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WidgetDotView.h; sourceTree = "<group>"; };
+		BCFEED6828F7E4F40078A2B7 /* SmallToolBodyView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SmallToolBodyView.h; sourceTree = "<group>"; };
+		BCFEED6928F7E4F40078A2B7 /* SmallToolBodyView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SmallToolBodyView.m; sourceTree = "<group>"; };
+		BCFEED6B28F7E51F0078A2B7 /* SmallToolBodyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SmallToolBodyView.xib; sourceTree = "<group>"; };
+		BCFEED6D28F7E95A0078A2B7 /* ToneTuningViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ToneTuningViewController.h; sourceTree = "<group>"; };
+		BCFEED6E28F7E95A0078A2B7 /* ToneTuningViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ToneTuningViewController.m; sourceTree = "<group>"; };
+		BCFEED7128F7F17C0078A2B7 /* TuningNavView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TuningNavView.h; sourceTree = "<group>"; };
+		BCFEED7228F7F17C0078A2B7 /* TuningNavView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TuningNavView.m; sourceTree = "<group>"; };
+		BCFEED7428F7F1820078A2B7 /* TuningNavView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TuningNavView.xib; sourceTree = "<group>"; };
+		BCFEED7A28F810D60078A2B7 /* KulexiuForStudent-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KulexiuForStudent-Bridging-Header.h"; sourceTree = "<group>"; };
+		BCFEED7B28F810D70078A2B7 /* Tuner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tuner.swift; sourceTree = "<group>"; };
 		C9C170A749B6C49F17AC3246 /* Pods_KulexiuForStudent_KulexiuForStudentUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KulexiuForStudent_KulexiuForStudentUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D5D730A1D1EC18E5028F1AD7 /* Pods-KulexiuForStudent-KulexiuForStudentUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KulexiuForStudent-KulexiuForStudentUITests.release.xcconfig"; path = "Target Support Files/Pods-KulexiuForStudent-KulexiuForStudentUITests/Pods-KulexiuForStudent-KulexiuForStudentUITests.release.xcconfig"; sourceTree = "<group>"; };
 		DD4D637EF600D0BAE869423D /* Pods_KulexiuForStudentTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_KulexiuForStudentTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -2572,6 +2610,7 @@
 				BCA1136828A3A5CF007FAFB9 /* Accelerate.framework in Frameworks */,
 				BC8B6E152856E20800866917 /* WebKit.framework in Frameworks */,
 				BC8A45CB283DDEA100094BBB /* AVFoundation.framework in Frameworks */,
+				BCD9294F28F8FCA4006793E4 /* AudioKit in Frameworks */,
 				BC71D1042881A2420010F14B /* libSocialQQ.a in Frameworks */,
 				BC71D0FC2881A2420010F14B /* UMShare.framework in Frameworks */,
 				BC71D0F92881A2420010F14B /* UMDevice.framework in Frameworks */,
@@ -4580,8 +4619,12 @@
 		BC4CF28928D058FA00961C61 /* Controller */ = {
 			isa = PBXGroup;
 			children = (
+				BCFEED4B28F7E4720078A2B7 /* SmallToolViewController.h */,
+				BCFEED4C28F7E4720078A2B7 /* SmallToolViewController.m */,
 				BC4CF28C28D072C000961C61 /* WidgetViewController.h */,
 				BC4CF28D28D072C000961C61 /* WidgetViewController.m */,
+				BCFEED6D28F7E95A0078A2B7 /* ToneTuningViewController.h */,
+				BCFEED6E28F7E95A0078A2B7 /* ToneTuningViewController.m */,
 			);
 			path = Controller;
 			sourceTree = "<group>";
@@ -4589,6 +4632,7 @@
 		BC4CF28A28D058FB00961C61 /* Model */ = {
 			isa = PBXGroup;
 			children = (
+				BCFEED7628F7FD620078A2B7 /* TuningFunction */,
 				BC49BAEA28D98C500031FF06 /* KSMetronomePlayer.h */,
 				BC49BAEB28D98C500031FF06 /* KSMetronomePlayer.m */,
 			);
@@ -4598,21 +4642,12 @@
 		BC4CF28B28D058FB00961C61 /* View */ = {
 			isa = PBXGroup;
 			children = (
-				BC4CF28F28D072EF00961C61 /* WidgetNavView.h */,
-				BC4CF29028D072EF00961C61 /* WidgetNavView.m */,
-				BC4CF29228D072F600961C61 /* WidgetNavView.xib */,
-				BC4CF29428D074DC00961C61 /* WidgetDotView.h */,
-				BC4CF29528D074DC00961C61 /* WidgetDotView.m */,
-				BC4CF29728D074E700961C61 /* WidgetDotView.xib */,
-				BC4CF29928D0757800961C61 /* WidgetSpeedView.h */,
-				BC4CF29A28D0757800961C61 /* WidgetSpeedView.m */,
-				BC4CF29C28D0758700961C61 /* WidgetSpeedView.xib */,
-				BC4CF29E28D075CC00961C61 /* WidgetFunctionView.h */,
-				BC4CF29F28D075CC00961C61 /* WidgetFunctionView.m */,
-				BC4CF2A128D075D400961C61 /* WidgetFunctionView.xib */,
-				BC4CF2A328D1B07800961C61 /* WidgetBottomButtonView.h */,
-				BC4CF2A428D1B07800961C61 /* WidgetBottomButtonView.m */,
-				BC4CF2A628D1B08000961C61 /* WidgetBottomButtonView.xib */,
+				BCD9295528F9447B006793E4 /* WMGaugeView */,
+				BCFEED7028F7F16C0078A2B7 /* toneTuning */,
+				BCFEED4E28F7E4910078A2B7 /* Metronome */,
+				BCFEED6828F7E4F40078A2B7 /* SmallToolBodyView.h */,
+				BCFEED6928F7E4F40078A2B7 /* SmallToolBodyView.m */,
+				BCFEED6B28F7E51F0078A2B7 /* SmallToolBodyView.xib */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -6037,6 +6072,20 @@
 			path = images;
 			sourceTree = "<group>";
 		};
+		BCD9295528F9447B006793E4 /* WMGaugeView */ = {
+			isa = PBXGroup;
+			children = (
+				BCD9295928F9447B006793E4 /* WMGaugeView.h */,
+				BCD9295B28F9447B006793E4 /* WMGaugeView.m */,
+				BCD9295728F9447B006793E4 /* WMGaugeViewStyle.h */,
+				BCD9295628F9447B006793E4 /* WMGaugeViewStyle3D.h */,
+				BCD9295A28F9447B006793E4 /* WMGaugeViewStyle3D.m */,
+				BCD9295C28F9447B006793E4 /* WMGaugeViewStyleFlatThin.h */,
+				BCD9295828F9447B006793E4 /* WMGaugeViewStyleFlatThin.m */,
+			);
+			path = WMGaugeView;
+			sourceTree = "<group>";
+		};
 		BCFDA62128BC94250022B497 /* HomeNav */ = {
 			isa = PBXGroup;
 			children = (
@@ -6140,6 +6189,52 @@
 			path = TeacherStyle;
 			sourceTree = "<group>";
 		};
+		BCFEED4E28F7E4910078A2B7 /* Metronome */ = {
+			isa = PBXGroup;
+			children = (
+				BCFEED5928F7E4910078A2B7 /* WidgetBottomButtonView.h */,
+				BCFEED5228F7E4910078A2B7 /* WidgetBottomButtonView.m */,
+				BCFEED5A28F7E4910078A2B7 /* WidgetBottomButtonView.xib */,
+				BCFEED5D28F7E4910078A2B7 /* WidgetDotView.h */,
+				BCFEED5428F7E4910078A2B7 /* WidgetDotView.m */,
+				BCFEED5728F7E4910078A2B7 /* WidgetDotView.xib */,
+				BCFEED5C28F7E4910078A2B7 /* WidgetFunctionView.h */,
+				BCFEED4F28F7E4910078A2B7 /* WidgetFunctionView.m */,
+				BCFEED5628F7E4910078A2B7 /* WidgetFunctionView.xib */,
+				BCFEED5B28F7E4910078A2B7 /* WidgetNavView.h */,
+				BCFEED5028F7E4910078A2B7 /* WidgetNavView.m */,
+				BCFEED5528F7E4910078A2B7 /* WidgetNavView.xib */,
+				BCFEED5328F7E4910078A2B7 /* WidgetSpeedView.h */,
+				BCFEED5828F7E4910078A2B7 /* WidgetSpeedView.m */,
+				BCFEED5128F7E4910078A2B7 /* WidgetSpeedView.xib */,
+			);
+			path = Metronome;
+			sourceTree = "<group>";
+		};
+		BCFEED7028F7F16C0078A2B7 /* toneTuning */ = {
+			isa = PBXGroup;
+			children = (
+				BCFEED7128F7F17C0078A2B7 /* TuningNavView.h */,
+				BCFEED7228F7F17C0078A2B7 /* TuningNavView.m */,
+				BCFEED7428F7F1820078A2B7 /* TuningNavView.xib */,
+				BCD9295028F90202006793E4 /* ToneTuningBodyView.h */,
+				BCD9295128F90202006793E4 /* ToneTuningBodyView.m */,
+				BCD9295328F90209006793E4 /* ToneTuningBodyView.xib */,
+				BC2BE91A28F951DE00CB5F92 /* DialPlateView.h */,
+				BC2BE91B28F951DE00CB5F92 /* DialPlateView.m */,
+			);
+			path = toneTuning;
+			sourceTree = "<group>";
+		};
+		BCFEED7628F7FD620078A2B7 /* TuningFunction */ = {
+			isa = PBXGroup;
+			children = (
+				BCFEED7B28F810D70078A2B7 /* Tuner.swift */,
+				BCFEED7A28F810D60078A2B7 /* KulexiuForStudent-Bridging-Header.h */,
+			);
+			path = TuningFunction;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -6159,6 +6254,9 @@
 			dependencies = (
 			);
 			name = KulexiuForStudent;
+			packageProductDependencies = (
+				BCD9294E28F8FCA4006793E4 /* AudioKit */,
+			);
 			productName = KulexiuForStudent;
 			productReference = 275E8AA527E18F8800DD3F6E /* KulexiuForStudent.app */;
 			productType = "com.apple.product-type.application";
@@ -6210,10 +6308,11 @@
 			isa = PBXProject;
 			attributes = {
 				BuildIndependentTargetsInParallel = 1;
-				LastUpgradeCheck = 1320;
+				LastUpgradeCheck = 1400;
 				TargetAttributes = {
 					275E8AA427E18F8800DD3F6E = {
 						CreatedOnToolsVersion = 13.2.1;
+						LastSwiftMigration = 1400;
 					};
 					275E8ABF27E18F8C00DD3F6E = {
 						CreatedOnToolsVersion = 13.2.1;
@@ -6235,6 +6334,9 @@
 				"zh-Hans",
 			);
 			mainGroup = 275E8A9C27E18F8800DD3F6E;
+			packageReferences = (
+				BCD9294D28F8FCA4006793E4 /* XCRemoteSwiftPackageReference "AudioKit" */,
+			);
 			productRefGroup = 275E8AA627E18F8800DD3F6E /* Products */;
 			projectDirPath = "";
 			projectRoot = "";
@@ -6254,7 +6356,6 @@
 				BCC583CB28A9EC6400BAB4CF /* cloud_animation_7.png in Resources */,
 				BC71D1052881A2420010F14B /* TencentOpenApi_IOS_Bundle.bundle in Resources */,
 				BC802DBF28BC8E350079E350 /* HomeHotLiveCourseView.xib in Resources */,
-				BC4CF29828D074E700961C61 /* WidgetDotView.xib in Resources */,
 				BC71D15D2887F9DB0010F14B /* launchAni.json in Resources */,
 				275E8AB827E18F8B00DD3F6E /* LaunchScreen.storyboard in Resources */,
 				275FA1EF27E7351900CFEA2E /* KSUpdateAlert.xib in Resources */,
@@ -6274,8 +6375,10 @@
 				BCFE53F22812898700AD6786 /* HomeVideoCourseCell.xib in Resources */,
 				BC71D263288804CD0010F14B /* img_40.png in Resources */,
 				BC71D26E288804CD0010F14B /* img_30.png in Resources */,
+				BCFEED7528F7F1820078A2B7 /* TuningNavView.xib in Resources */,
 				BC71D24F288804CD0010F14B /* img_7.png in Resources */,
 				2723B5BA27F157B100E0B90B /* ChatAddressHeaderView.xib in Resources */,
+				BCFEED6328F7E4910078A2B7 /* WidgetNavView.xib in Resources */,
 				BCC583C228A9EC6400BAB4CF /* cloud_animation_28.png in Resources */,
 				BC802D8728B872B40079E350 /* KSLiveAlertView.xib in Resources */,
 				BC8C2C7F28265D8E00FBA5D5 /* KSNewsAlert.xib in Resources */,
@@ -6283,6 +6386,7 @@
 				BC119235280ED97C00A716F7 /* CourseForLiveCell.xib in Resources */,
 				2723B62E27F157D500E0B90B /* GroupApplyChooseAllCell.xib in Resources */,
 				BCFDA61828BC8FDE0022B497 /* HomeHotLiveCell.xib in Resources */,
+				BCFEED6C28F7E51F0078A2B7 /* SmallToolBodyView.xib in Resources */,
 				BC71D245288804CD0010F14B /* img_38.png in Resources */,
 				BC71D276288804CD0010F14B /* img_22.png in Resources */,
 				BCC583D128A9EC6400BAB4CF /* cloud_animation_2.png in Resources */,
@@ -6294,6 +6398,7 @@
 				BC119258280FA85300A716F7 /* HomeworkListCell.xib in Resources */,
 				BC542E5728409EC900633781 /* InstrumentChooseBottonView.xib in Resources */,
 				BC802DAD28BC6EE40079E350 /* HomeHotTalentView.xib in Resources */,
+				BCFEED6428F7E4910078A2B7 /* WidgetFunctionView.xib in Resources */,
 				2723B63227F157D500E0B90B /* GroupSettingBodyView.xib in Resources */,
 				BCA1136228A3A2D0007FAFB9 /* bassmidi.txt in Resources */,
 				BC71D272288804CD0010F14B /* img_27.png in Resources */,
@@ -6317,10 +6422,10 @@
 				BC11928C280FB44300A716F7 /* HomeworkVideoView.xib in Resources */,
 				BCFDA64D28BCA2000022B497 /* live_animation_1.png in Resources */,
 				BCD457A2286313D70010B493 /* NotiferNavView.xib in Resources */,
-				BC4CF29328D072F600961C61 /* WidgetNavView.xib in Resources */,
 				275E8AB527E18F8B00DD3F6E /* Assets.xcassets in Resources */,
 				BCFDA65028BCA2000022B497 /* live_animation_3.png in Resources */,
 				BC71D252288804CD0010F14B /* img_29.png in Resources */,
+				BCFEED6528F7E4910078A2B7 /* WidgetDotView.xib in Resources */,
 				BC71D24D288804CD0010F14B /* img_12.png in Resources */,
 				BC119275280FB01100A716F7 /* AccompanyHomeworkCell.xib in Resources */,
 				BC71D1022881A2420010F14B /* WeiboSDK.bundle in Resources */,
@@ -6394,6 +6499,7 @@
 				BCFDA65628BCA2000022B497 /* accomapny_animation_1.png in Resources */,
 				2723B66327F15CFC00E0B90B /* ModifyNameBodyView.xib in Resources */,
 				BC71D255288804CD0010F14B /* img_5.png in Resources */,
+				BCFEED6728F7E4910078A2B7 /* WidgetBottomButtonView.xib in Resources */,
 				BCB635B327F6E1A600ACFDCF /* LiveRoomBottomView.xib in Resources */,
 				BCB6348327F6D29600ACFDCF /* LiveSeatApplyView.xib in Resources */,
 				BCFDA65328BCA2000022B497 /* accomapny_animation_3.png in Resources */,
@@ -6421,7 +6527,6 @@
 				BC71D26D288804CD0010F14B /* img_18.png in Resources */,
 				BCB6348127F6D29600ACFDCF /* LiveSeatApplyCell.xib in Resources */,
 				BC8A45A4283DC33400094BBB /* KSCloudSettingView.xib in Resources */,
-				BC4CF2A228D075D400961C61 /* WidgetFunctionView.xib in Resources */,
 				2723B62D27F157D500E0B90B /* GroupApplyMemberCell.xib in Resources */,
 				BCB6347427F6D29600ACFDCF /* BaseEmoji.plist in Resources */,
 				BCB9091928530EA500F5FF69 /* KSShopCardView.xib in Resources */,
@@ -6472,6 +6577,7 @@
 				BC11921B280ED6A900A716F7 /* NewClassPopCell.xib in Resources */,
 				BCC583BF28A9EC6400BAB4CF /* cloud_animation_18.png in Resources */,
 				BC8B641728F3B5B300A08D16 /* KSAwardAlertView.xib in Resources */,
+				BCD9295428F90209006793E4 /* ToneTuningBodyView.xib in Resources */,
 				BC71D265288804CD0010F14B /* img_55.png in Resources */,
 				BC71D0FA2881A2420010F14B /* UMSocialSDKResources.bundle in Resources */,
 				BC60E3D2287D592800B05441 /* KSPublicAlertView.xib in Resources */,
@@ -6486,7 +6592,6 @@
 				BCC583BD28A9EC6400BAB4CF /* cloud_animation_25.png in Resources */,
 				BCC583BC28A9EC6400BAB4CF /* cloud_animation_19.png in Resources */,
 				BC8A459F283DC33400094BBB /* SettingPageView.xib in Resources */,
-				BC4CF29D28D0758700961C61 /* WidgetSpeedView.xib in Resources */,
 				BC71D262288804CD0010F14B /* img_54.png in Resources */,
 				BC8C2C5A2823F57100FBA5D5 /* areainfo.json in Resources */,
 				BCFDA64E28BCA2000022B497 /* musicRoom_animation_3.png in Resources */,
@@ -6497,7 +6602,7 @@
 				BC71D24E288804CD0010F14B /* img_16.png in Resources */,
 				27F9033727E87C8B00C08A19 /* MineNavView.xib in Resources */,
 				BC8A45AB283DC33400094BBB /* TrackChooseView.xib in Resources */,
-				BC4CF2A728D1B08000961C61 /* WidgetBottomButtonView.xib in Resources */,
+				BCFEED6028F7E4910078A2B7 /* WidgetSpeedView.xib in Resources */,
 				BCB9FA47286EDCD7005D766B /* KSTipsAlert.xib in Resources */,
 				BCFDA64C28BCA2000022B497 /* live_animation_0.png in Resources */,
 				BC542E40284079E300633781 /* UserAuthBodyView.xib in Resources */,
@@ -6699,6 +6804,7 @@
 				BCFDA66728BDF06E0022B497 /* HomeHotStyleCell.m in Sources */,
 				2779354827E324A60010E277 /* NSObject+ReadDocument.m in Sources */,
 				277935CB27E324A90010E277 /* TAAbstractDotView.m in Sources */,
+				BCFEED6128F7E4910078A2B7 /* WidgetBottomButtonView.m in Sources */,
 				2723B5C627F157B100E0B90B /* GroupListViewCell.m in Sources */,
 				BCB6356E27F6D2A300ACFDCF /* ClassSongMessage.m in Sources */,
 				BCFDA66228BDC3640022B497 /* TalentTeacherModel.m in Sources */,
@@ -6783,6 +6889,7 @@
 				BCB908F12850B08D00F5FF69 /* KSChatMusicShareCell.m in Sources */,
 				BC02381928685087005560CA /* LiveAnimationView.m in Sources */,
 				BC8A45AA283DC33400094BBB /* TrackChooseView.m in Sources */,
+				BCFEED4D28F7E4720078A2B7 /* SmallToolViewController.m in Sources */,
 				BC8A45B2283DC33400094BBB /* CloudFeedbackView.m in Sources */,
 				BCB6355A27F6D2A300ACFDCF /* KSRemoteUserManager.m in Sources */,
 				BC60E3CD287D552800B05441 /* DeleteAccountBodyView.m in Sources */,
@@ -6793,6 +6900,7 @@
 				BCB6355C27F6D2A300ACFDCF /* DisplayCommandMessage.m in Sources */,
 				BCB6356A27F6D2A300ACFDCF /* AccompanyDownloadCallbackMessage.m in Sources */,
 				BCB6359827F6D2AB00ACFDCF /* KSTipsView.m in Sources */,
+				BCD9295F28F9447B006793E4 /* WMGaugeView.m in Sources */,
 				2779354C27E324A70010E277 /* KSGifRefreshHeader.m in Sources */,
 				2779358727E324A80010E277 /* LLCollectionViewCell.m in Sources */,
 				2779352627E324A60010E277 /* UIAlertController+Extend.m in Sources */,
@@ -6811,7 +6919,6 @@
 				2723B62C27F157D500E0B90B /* ApplyBottomView.m in Sources */,
 				BC8A4595283DC33400094BBB /* GCDTimer.m in Sources */,
 				BCB6356127F6D2A300ACFDCF /* SongDownloadCallbackMessage.m in Sources */,
-				BC4CF29128D072EF00961C61 /* WidgetNavView.m in Sources */,
 				277935BA27E324A90010E277 /* FSCalendarWeekdayView.m in Sources */,
 				BCB6354427F6D2A300ACFDCF /* RecentSharedView.m in Sources */,
 				2723B66C27F15CFC00E0B90B /* SettingBodyView.m in Sources */,
@@ -6826,6 +6933,7 @@
 				BC8B6DC32856CAE500866917 /* KSICloudManager.m in Sources */,
 				2779359427E324A80010E277 /* TZAssetCell.m in Sources */,
 				BCB635B227F6E1A600ACFDCF /* LiveRoomBottomView.m in Sources */,
+				BCFEED5E28F7E4910078A2B7 /* WidgetFunctionView.m in Sources */,
 				2723B5CE27F157BE00E0B90B /* KSRCloudMediaManager.m in Sources */,
 				BC02381428685064005560CA /* KSLiveEndView.m in Sources */,
 				2779357027E324A70010E277 /* UITextView_Toolbar.m in Sources */,
@@ -6847,6 +6955,7 @@
 				27F9033627E87C8B00C08A19 /* MineNavView.m in Sources */,
 				BC50171527FC0D8300F8BCBC /* SubjectChooseBodyView.m in Sources */,
 				BC8C2C5C2823F57100FBA5D5 /* KSAddressPickerView.m in Sources */,
+				BCFEED7328F7F17C0078A2B7 /* TuningNavView.m in Sources */,
 				2723B62727F157D500E0B90B /* GroupMemberViewController.m in Sources */,
 				BC119216280ED6A900A716F7 /* MyLiveCourseCell.m in Sources */,
 				2779353727E324A60010E277 /* UIImage+Color.m in Sources */,
@@ -6872,14 +6981,15 @@
 				27F9032927E87C2E00C08A19 /* NetworkingCheckController.m in Sources */,
 				BCFE53EC2812897600AD6786 /* HomeLiveCouseCell.m in Sources */,
 				277935A527E324A80010E277 /* MSSBrowseLoadingImageView.m in Sources */,
-				BC4CF29628D074DC00961C61 /* WidgetDotView.m in Sources */,
 				2779353E27E324A60010E277 /* UIDevice+TFDevice.m in Sources */,
+				BC2BE91C28F951DE00CB5F92 /* DialPlateView.m in Sources */,
 				2723B5C027F157B100E0B90B /* ChatAddressHeaderView.m in Sources */,
 				2723B68127F15D3D00E0B90B /* ModifyNameViewController.m in Sources */,
 				2779359927E324A80010E277 /* NSBundle+TZImagePicker.m in Sources */,
 				2779358E27E324A80010E277 /* WMPlayerModel.m in Sources */,
 				2779354E27E324A70010E277 /* KSAudioSessionManager.m in Sources */,
 				BC8B6E7D285869B500866917 /* KSUMShareManager.m in Sources */,
+				BCFEED5F28F7E4910078A2B7 /* WidgetNavView.m in Sources */,
 				BC802DAB28BC6EDA0079E350 /* HomeHotTalentView.m in Sources */,
 				BCBFDF41281157340052AFE5 /* HomeButtonView.m in Sources */,
 				2779358127E324A80010E277 /* StateView.m in Sources */,
@@ -6902,6 +7012,7 @@
 				BC3A4EB428DAE202001C4428 /* UIImage+KSScreenShot.m in Sources */,
 				BC11927A280FB07F00A716F7 /* AccompanyArrangeCell.m in Sources */,
 				277935B827E324A90010E277 /* FSCalendar.m in Sources */,
+				BCD9295E28F9447B006793E4 /* WMGaugeViewStyle3D.m in Sources */,
 				BCB6356C27F6D2A300ACFDCF /* Whiteboard.m in Sources */,
 				275FA23527E7356B00CFEA2E /* UserInfoManager.m in Sources */,
 				2779353327E324A60010E277 /* UIScreen+Extend.m in Sources */,
@@ -6918,7 +7029,6 @@
 				2779359A27E324A80010E277 /* TZPhotoPreviewController.m in Sources */,
 				2779355E27E324A70010E277 /* KSAudioRecordFileManager.m in Sources */,
 				2779357A27E324A70010E277 /* PIckView.m in Sources */,
-				BC4CF29B28D0757800961C61 /* WidgetSpeedView.m in Sources */,
 				BC119234280ED97C00A716F7 /* CourseForLiveCell.m in Sources */,
 				BCB6346027F6D29600ACFDCF /* KSLiveChatroomKickOut.m in Sources */,
 				2779354127E324A60010E277 /* UIView+ShowProgress.m in Sources */,
@@ -6984,6 +7094,7 @@
 				BCB6353A27F6D2A300ACFDCF /* MessageBaseCell.m in Sources */,
 				2779355027E324A70010E277 /* VoDiskCache.m in Sources */,
 				BCB6353F27F6D2A300ACFDCF /* ChatAreaView.m in Sources */,
+				BCFEED6228F7E4910078A2B7 /* WidgetDotView.m in Sources */,
 				BCB6355627F6D2A300ACFDCF /* HTTPUtility.m in Sources */,
 				2779353927E324A60010E277 /* UIView+XIBView.m in Sources */,
 				2779355827E324A70010E277 /* UIImage+Addtions.m in Sources */,
@@ -7018,6 +7129,7 @@
 				BCB6357127F6D2A300ACFDCF /* RTCService.m in Sources */,
 				2723B62427F157D500E0B90B /* NoticeEditBodyView.m in Sources */,
 				BCB6354327F6D2A300ACFDCF /* RecentSharedWhiteboardCell.m in Sources */,
+				BCFEED6628F7E4910078A2B7 /* WidgetSpeedView.m in Sources */,
 				BCFDA65F28BCAEC80022B497 /* HomeInformationBodyView.m in Sources */,
 				BC0D1F71281015B000C5D9E5 /* VideoCourseCell.m in Sources */,
 				2779352F27E324A60010E277 /* UIView+Hints.m in Sources */,
@@ -7038,6 +7150,7 @@
 				277935A227E324A80010E277 /* TZLocationManager.m in Sources */,
 				BCDE35862893B0E200A9A560 /* KSLoadingSuccessView.m in Sources */,
 				2723B62827F157D500E0B90B /* KSSelectConversationViewController.m in Sources */,
+				BCD9295228F90202006793E4 /* ToneTuningBodyView.m in Sources */,
 				BC0A2265284471300065C1AB /* KSLiveBlockUser.m in Sources */,
 				275FA1E827E7351900CFEA2E /* CustomNavViewController.m in Sources */,
 				BC8A45B5283DC33400094BBB /* EvaluateResultAlert.m in Sources */,
@@ -7045,6 +7158,7 @@
 				275FA24327E73DF600CFEA2E /* InstrumentDescView.m in Sources */,
 				2723B68027F15D3D00E0B90B /* FeedbackViewController.m in Sources */,
 				275FA1ED27E7351900CFEA2E /* KSUpdateManager.m in Sources */,
+				BCFEED7C28F810D70078A2B7 /* Tuner.swift in Sources */,
 				BC27A06F280FF56C00F91E27 /* AccompanyStudentEvaCell.m in Sources */,
 				BCB6355727F6D2A300ACFDCF /* HTTPResult.m in Sources */,
 				BC0212FB27FC61D30040569F /* KSSegmentControl.m in Sources */,
@@ -7062,7 +7176,6 @@
 				277935D327E324A90010E277 /* ALCalendarPicker.m in Sources */,
 				BC542E5828409EC900633781 /* InstrumentChooseCell.m in Sources */,
 				BCB6359427F6D2AB00ACFDCF /* NewClassRoomViewController.m in Sources */,
-				BC4CF2A028D075CC00961C61 /* WidgetFunctionView.m in Sources */,
 				BCFDA62E28BC99410022B497 /* HomeBannerCell.m in Sources */,
 				BCB6356027F6D2A300ACFDCF /* WhiteboardMessage.m in Sources */,
 				2779354F27E324A70010E277 /* VoLRUManager.m in Sources */,
@@ -7120,6 +7233,7 @@
 				BC756CC628FE866100AA9ECB /* UserDetailBottomView.m in Sources */,
 				BC8A45A9283DC33400094BBB /* KSCloudBeatView.m in Sources */,
 				275FA1DF27E7351900CFEA2E /* RCConnectionManager.m in Sources */,
+				BCD9295D28F9447B006793E4 /* WMGaugeViewStyleFlatThin.m in Sources */,
 				275FA22B27E7356B00CFEA2E /* HomeViewController.m in Sources */,
 				BCFE53F12812898700AD6786 /* HomeVideoCourseCell.m in Sources */,
 				BCB908F02850B08D00F5FF69 /* KSChatLiveShareCell.m in Sources */,
@@ -7146,7 +7260,6 @@
 				BC4CF28E28D072C000961C61 /* WidgetViewController.m in Sources */,
 				BCB6353627F6D2A300ACFDCF /* TimeStampCell.m in Sources */,
 				BCB6354D27F6D2A300ACFDCF /* EmptyView.m in Sources */,
-				BC4CF2A528D1B07800961C61 /* WidgetBottomButtonView.m in Sources */,
 				2723B63327F157D500E0B90B /* ChatComplainBodyView.m in Sources */,
 				2779357927E324A70010E277 /* KSImageButton.m in Sources */,
 				BCD959CC28DB0BAB00B70314 /* KSImageShareViewController.m in Sources */,
@@ -7181,6 +7294,7 @@
 				BCB6353427F6D2A300ACFDCF /* InputBarControl.m in Sources */,
 				BC11922B280ED8E800A716F7 /* CourseNavView.m in Sources */,
 				275FA1DB27E7351900CFEA2E /* UINavigationController+KSNavigationBar.m in Sources */,
+				BCFEED6F28F7E95A0078A2B7 /* ToneTuningViewController.m in Sources */,
 				2779352127E324A60010E277 /* NSMutableAttributedString+CZHExtention.m in Sources */,
 				277935C727E324A90010E277 /* SDCollectionViewCell.m in Sources */,
 				BCB6348627F6D29600ACFDCF /* LiveSeatApplyCell.m in Sources */,
@@ -7228,6 +7342,7 @@
 				2723B63427F157D500E0B90B /* GroupApplyMemberCell.m in Sources */,
 				BC5082B4283345A10031DD0A /* KSChatListCell.m in Sources */,
 				BCB6345D27F6D29600ACFDCF /* KSLiveChatroomWelcome.m in Sources */,
+				BCFEED6A28F7E4F40078A2B7 /* SmallToolBodyView.m in Sources */,
 				2779352927E324A60010E277 /* zhPopupController.m in Sources */,
 				BC12636728FEA01F00509E90 /* RecentPracticeModel.m in Sources */,
 				2779359527E324A80010E277 /* TZVideoEditedPreviewController.m in Sources */,
@@ -7388,6 +7503,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
@@ -7447,6 +7563,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
@@ -7492,6 +7609,7 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
 				VALIDATE_PRODUCT = YES;
 			};
 			name = Release;
@@ -7502,6 +7620,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = KulexiuForStudent/KulexiuForStudent.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1.4.1;
@@ -7564,7 +7683,11 @@
 				PRODUCT_BUNDLE_IDENTIFIER = com.Colexiu.KulexiuForStudent;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "KulexiuForStudent/Module/Widget/Model/TuningFunction/KulexiuForStudent-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = Debug;
 		};
@@ -7574,6 +7697,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_ENTITLEMENTS = KulexiuForStudent/KulexiuForStudent.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1.4.1;
@@ -7636,7 +7760,10 @@
 				PRODUCT_BUNDLE_IDENTIFIER = com.Colexiu.KulexiuForStudent;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_OBJC_BRIDGING_HEADER = "KulexiuForStudent/Module/Widget/Model/TuningFunction/KulexiuForStudent-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
+				USER_HEADER_SEARCH_PATHS = "";
 			};
 			name = Release;
 		};
@@ -7644,6 +7771,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = 25A92766B52F3FB4AFA45A9B /* Pods-KulexiuForStudentTests.debug.xcconfig */;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
@@ -7663,6 +7791,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = ED3E62A50E433BC13E633D1C /* Pods-KulexiuForStudentTests.release.xcconfig */;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
@@ -7682,6 +7811,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = 04AE318E8988C2AC148D551B /* Pods-KulexiuForStudent-KulexiuForStudentUITests.debug.xcconfig */;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = P664H7S5LL;
@@ -7699,6 +7829,7 @@
 			isa = XCBuildConfiguration;
 			baseConfigurationReference = D5D730A1D1EC18E5028F1AD7 /* Pods-KulexiuForStudent-KulexiuForStudentUITests.release.xcconfig */;
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = P664H7S5LL;
@@ -7752,6 +7883,25 @@
 			defaultConfigurationName = Release;
 		};
 /* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+		BCD9294D28F8FCA4006793E4 /* XCRemoteSwiftPackageReference "AudioKit" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/AudioKit/AudioKit.git";
+			requirement = {
+				kind = exactVersion;
+				version = 5.0.0;
+			};
+		};
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+		BCD9294E28F8FCA4006793E4 /* AudioKit */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = BCD9294D28F8FCA4006793E4 /* XCRemoteSwiftPackageReference "AudioKit" */;
+			productName = AudioKit;
+		};
+/* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 275E8A9D27E18F8800DD3F6E /* Project object */;
 }

+ 1 - 1
KulexiuForStudent/KulexiuForStudent.xcodeproj/xcshareddata/xcschemes/KulexiuForStudent.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1330"
+   LastUpgradeVersion = "1400"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 14 - 0
KulexiuForStudent/KulexiuForStudent.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -0,0 +1,14 @@
+{
+  "pins" : [
+    {
+      "identity" : "audiokit",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/AudioKit/AudioKit.git",
+      "state" : {
+        "revision" : "36ea09c87bf703291a60e694742d56ff221c56ae",
+        "version" : "5.0.0"
+      }
+    }
+  ],
+  "version" : 2
+}

+ 1 - 1
KulexiuForStudent/KulexiuForStudent.xcworkspace/xcuserdata/wangzhi.xcuserdatad/WorkspaceSettings.xcsettings

@@ -3,7 +3,7 @@
 <plist version="1.0">
 <dict>
 	<key>BuildLocationStyle</key>
-	<string>UseTargetSettings</string>
+	<string>UseAppPreferences</string>
 	<key>CustomBuildLocationType</key>
 	<string>RelativeToDerivedData</string>
 	<key>DerivedDataLocationStyle</key>

+ 0 - 196
KulexiuForStudent/KulexiuForStudent.xcworkspace/xcuserdata/wangzhi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -1,196 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Bucket
-   uuid = "C42D83E3-CB7A-44AF-B9C2-6DDDEC116980"
-   type = "0"
-   version = "2.0">
-   <Breakpoints>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "673B4C4D-71D2-482A-A772-4DAB5404CC51"
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Widget/Model/KSMetronomePlayer.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "161"
-            endingLineNumber = "161"
-            landmarkName = "-generateBuffer:metronomeType:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "432AEDB3-16C0-4CED-891C-3317C59AF2A8"
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Widget/Model/KSMetronomePlayer.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "60"
-            endingLineNumber = "60"
-            landmarkName = "-handleInterruption:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "FCBB811F-5467-47CA-A9C1-440BB1A2E560"
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/AppDelegate.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "805"
-            endingLineNumber = "805"
-            landmarkName = "-applicationWillResignActive:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "48D19C1D-7CA8-41F5-B6D2-62482808D92F"
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Home/Controller/HomeViewController.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "584"
-            endingLineNumber = "584"
-            landmarkName = "-requestHotAlbum"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
-         <BreakpointContent
-            uuid = "B53C8936-0246-4C10-A4ED-11D513BBA445"
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            breakpointStackSelectionBehavior = "1"
-            scope = "1"
-            stopOnStyle = "1">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "1111ED18-417D-42C2-AF8C-16DA1DDDACC4"
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Login/Model/UserInfoManager.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "385"
-            endingLineNumber = "385"
-            landmarkName = "-startUMCountAndLoginCount"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "3BD18AAA-986A-4C4B-AC76-97F242E45E7F"
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/SealClass/Services/Classroom/ClassroomService.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "411"
-            endingLineNumber = "411"
-            landmarkName = "-onReceiveDeviceMessage:withSenderId:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "96F5853A-BD70-4A02-9086-16255829C27B"
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Common/Base/KSNetworkingManager.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "799"
-            endingLineNumber = "799"
-            landmarkName = "+imUserFriendRequest:search:success:faliure:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "C0FA1F71-1707-4AF4-9FFF-9C6B57E831F2"
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Common/Base/KSBaseWKWebViewController.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "536"
-            endingLineNumber = "536"
-            landmarkName = "-handleScriptMessageSource:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "18AD76CB-BAF9-457C-AF3F-6ACC381E87D6"
-            shouldBeEnabled = "No"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Chat/Controller/KSChatConversationViewController.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "449"
-            endingLineNumber = "449"
-            landmarkName = "-willDisplayMessageCell:atIndexPath:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "AA6465A8-5632-47BC-B900-486CE81013FB"
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Chat/View/ShareMusicCellContentView.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "57"
-            endingLineNumber = "57"
-            landmarkName = "-configWithSongName:type:authName:sendAvatar:sendName:userId:tags:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
-            uuid = "3BA78656-E0B8-45EB-BDD5-8F4D055637B3"
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "KulexiuForStudent/Module/Chat/View/KSChatMusicShareCell.m"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "68"
-            endingLineNumber = "68"
-            landmarkName = "-setDataModel:"
-            landmarkType = "7">
-         </BreakpointContent>
-      </BreakpointProxy>
-   </Breakpoints>
-</Bucket>

+ 6 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/Contents.json

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

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/forkButton_bg.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/forkButton_bg.imageset/forkButton_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/forkButton_bg.imageset/forkButton_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_choose.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_choose.imageset/fork_choose@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_choose.imageset/fork_choose@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_play.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_play.imageset/fork_play@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_play.imageset/fork_play@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unPlay.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unPlay.imageset/fork_unPlay@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unPlay.imageset/fork_unPlay@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unchoose.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unchoose.imageset/fork_unchoose@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/fork_unchoose.imageset/fork_unchoose@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/freguqnce_add.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/freguqnce_add.imageset/freguqnce_add@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/freguqnce_add.imageset/freguqnce_add@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/frequence_minus.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/frequence_minus.imageset/frequence_minus@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/frequence_minus.imageset/frequence_minus@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/plate_bg.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/plate_bg.imageset/plate_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/plate_bg.imageset/plate_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/playButton_bg.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/playButton_bg.imageset/playButton_bg@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/playButton_bg.imageset/playButton_bg@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/toning_bottom.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/toning_bottom.imageset/toning_bottom@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/toning_bottom.imageset/toning_bottom@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_decorate.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_decorate.imageset/tuning_decorate@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_decorate.imageset/tuning_decorate@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_transfer.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_transfer.imageset/tuning_transfer@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_transfer.imageset/tuning_transfer@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_warning.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_warning.imageset/tuning_warning@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/TuningImage/tuning_warning.imageset/tuning_warning@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/tuning_setting.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/tuning_setting.imageset/tuning_setting@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/Home/tuning_setting.imageset/tuning_setting@3x.png


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

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

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/metronome_image.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/metronome_image.imageset/metronome_image@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/metronome_image.imageset/metronome_image@3x.png


+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/tone_tuning.imageset/Contents.json

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

BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/tone_tuning.imageset/tone_tuning@2x.png


BIN
KulexiuForStudent/KulexiuForStudent/Assets.xcassets/SmallTool/tone_tuning.imageset/tone_tuning@3x.png


+ 1 - 1
KulexiuForStudent/KulexiuForStudent/Common/Base/KSWebNavView.m

@@ -35,7 +35,7 @@
         make.bottom.mas_equalTo(self.mas_bottom);
         make.centerX.mas_equalTo(self.mas_centerX);
         make.left.mas_equalTo(self.mas_left).offset(70);
-        make.right.mas_equalTo(self.mas_right).offset(-50);
+        make.right.mas_equalTo(self.mas_right).offset(-70);
         make.height.mas_equalTo(44);
     }];
 }

+ 1 - 0
KulexiuForStudent/KulexiuForStudent/Common/Define/PrefixHeader.pch

@@ -148,6 +148,7 @@ shouldPrevent = NO; \
 #define SUBMIT_UUID (YES)
 
 // 预生产环境
+
 //#define hostURL (@"https://ponline.colexiu.com")
 //#define SEALCLASSHOST (@"https://ponline.colexiu.com/api-classroom")
 //#define WEBHOST (@"https://ponline.colexiu.com/student")

+ 2 - 2
KulexiuForStudent/KulexiuForStudent/Module/Home/Controller/HomeViewController.m

@@ -64,7 +64,7 @@
 #import "TeacherStyleModel.h"
 #import "HomeTeacherLiveModel.h"
 
-#import "WidgetViewController.h"
+#import "SmallToolViewController.h"
 #import "KSAwardAlertView.h"
 
 #import "HomeNewMusicView.h"
@@ -1042,7 +1042,7 @@
         HomeMessageModel *model = self.buttonArray[index];
         if (![NSString isEmptyString:model.linkUrl]) {
             if ([model.linkUrl isEqualToString:@"native-metronome"]) {
-                WidgetViewController *ctrl = [[WidgetViewController alloc] init];
+                SmallToolViewController *ctrl = [[SmallToolViewController alloc] init];
                 [self.navigationController pushViewController:ctrl animated:YES];
             }
             else {

+ 16 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/SmallToolViewController.h

@@ -0,0 +1,16 @@
+//
+//  SmallToolViewController.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "KSBaseViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SmallToolViewController : KSBaseViewController
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 66 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/SmallToolViewController.m

@@ -0,0 +1,66 @@
+//
+//  SmallToolViewController.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "SmallToolViewController.h"
+#import "WidgetViewController.h"
+#import "SmallToolBodyView.h"
+#import "ToneTuningViewController.h"
+@interface SmallToolViewController ()
+
+
+@end
+
+@implementation SmallToolViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    [self allocTitle:@"小工具"];
+    [self configUI];
+}
+
+- (void)configUI {
+    SmallToolBodyView *bodyView = [SmallToolBodyView shareInstance];
+    bodyView.frame = CGRectMake(0, 0, KPortraitWidth, KPortraitHeight - kNaviBarHeight);
+    [self.scrollView addSubview:bodyView];
+    
+    MJWeakSelf;
+    [bodyView chooseToolCallback:^(TOOLTYPE type) {
+        [weakSelf chooseToolFunction:type];
+    }];
+}
+
+- (void)chooseToolFunction:(TOOLTYPE)type {
+    switch (type) {
+        case TOOLTYPE_METRONOME:
+        {
+            WidgetViewController *ctrl = [[WidgetViewController alloc] init];
+            [self.navigationController pushViewController:ctrl animated:YES];
+        }
+            break;
+        case TOOLTYPE_TONE:
+        {
+            ToneTuningViewController *ctrl = [[ToneTuningViewController alloc] init];
+            [self.navigationController pushViewController:ctrl animated:YES];
+        }
+            break;
+        default:
+            break;
+    }
+}
+
+/*
+#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

+ 16 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/ToneTuningViewController.h

@@ -0,0 +1,16 @@
+//
+//  ToneTuningViewController.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "KSBaseViewController.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ToneTuningViewController : KSBaseViewController
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 188 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/Controller/ToneTuningViewController.m

@@ -0,0 +1,188 @@
+//
+//  ToneTuningViewController.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "ToneTuningViewController.h"
+#import "TuningNavView.h"
+#import "KulexiuForStudent-swift.h"
+#import "ToneTuningBodyView.h"
+#import "DialPlateView.h"
+
+@interface ToneTuningViewController ()<TunerDelegate>
+
+@property (nonatomic, strong) TuningNavView *navView;
+
+@property (nonatomic, assign) BOOL isRuning;
+
+@property (nonatomic, strong) Tuner *tuner;
+
+
+@property (nonatomic, strong) ToneTuningBodyView *bodyView;
+
+@property (nonatomic, strong) DialPlateView *plateView;
+
+@property (nonatomic, assign) BOOL isTransfer;
+
+@end
+
+@implementation ToneTuningViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    self.ks_prefersNavigationBarHidden = YES;
+    [self configUI];
+}
+
+- (void)configUI {
+    [self.scrollView removeFromSuperview];
+    CAGradientLayer *layer = [self createGradientLayerFromColor:HexRGB(0x1D2027) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x17181C) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth, KPortraitHeight)];
+    layer.cornerRadius = 14.0f;
+    layer.masksToBounds = YES;
+    [self.view.layer addSublayer:layer];
+    
+    [self.view addSubview:self.navView];
+    [self.navView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.top.mas_equalTo(self.view);
+        make.height.mas_equalTo(kNaviBarHeight);
+    }];
+//    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
+//    button.frame = CGRectMake(100, 100, 100, 100);
+//    [button setTitle:@"Start" forState:UIControlStateNormal];
+//    [button setBackgroundColor:HexRGB(0xffffff)];
+//    [self.view addSubview:button];
+//    [button addTarget:self action:@selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
+    [self.view addSubview:self.bodyView];
+    [self.bodyView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.right.mas_equalTo(self.view);
+        make.top.mas_equalTo(self.navView.mas_bottom);
+        make.bottom.mas_equalTo(self.view.mas_bottom).offset(-iPhoneXSafeBottomMargin);
+    }];
+    [self.bodyView.plateView addSubview:self.plateView];
+    [self.plateView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.top.bottom.left.right.mas_equalTo(self.bodyView.plateView);
+    }];
+}
+
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [self startTuner];
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    [self stopTuner];
+}
+
+- (void)clickAction:(UIButton *)sender {
+    if (self.isRuning) {
+        [sender setTitle:@"Stop" forState:UIControlStateNormal];
+        [self stopTuner];
+    }
+    else {
+        [sender setTitle:@"Start" forState:UIControlStateNormal];
+        [self startTuner];
+    }
+}
+
+- (void)startTuner {
+    if (self.isRuning == NO) {
+        self.isRuning = YES;
+        [self.tuner start];
+    }
+}
+
+- (void)stopTuner {
+    if (self.isRuning) {
+        self.isRuning = NO;
+        [self.tuner stop];
+    }
+}
+
+- (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
+    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
+    gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
+    gradientLayer.startPoint = startPoint;
+    gradientLayer.endPoint = endPoint;
+    gradientLayer.frame = bounds;
+    gradientLayer.locations = @[@(0),@(1.0f)];
+    return gradientLayer;
+}
+
+- (TuningNavView *)navView {
+    if (!_navView) {
+        _navView = [TuningNavView shareInstance];
+        MJWeakSelf;
+        [_navView navActionCallback:^(BOOL isBack) {
+            if (isBack) {
+                [weakSelf backAction];
+            }
+            else {
+                [weakSelf showSettingView];
+            }
+        }];
+    }
+    return _navView;
+}
+
+- (void)showSettingView {
+    
+}
+
+- (Tuner *)tuner {
+    if (!_tuner) {
+        _tuner = [[Tuner alloc] initWithThreshold:0 smoothing:0.25];
+        _tuner.delegate = self;
+    }
+    return _tuner;
+}
+
+- (void)tunerDidUpdate:(Tuner *)tuner output:(TunerOutput *)output {
+    if (output.amplitude < 0.01) {
+        
+    }
+    else {
+//        self.plateView.gaugeView.value = output.distance;
+        [self.plateView.gaugeView setValue:output.distance animated:YES];
+        if (self.isTransfer == NO) {
+            self.bodyView.nomalPitch.text = output.pitch;
+            self.bodyView.pitchFrequenceLabel.text = [NSString stringWithFormat:@"%@%zd:%.0fHz", output.pitch, output.octave, output.frequency];
+        }
+        else {
+            
+        }
+    }
+    NSLog(@"-------- %@%zd --- distance :%f frequence : %f" , output.pitch, output.octave, output.distance, output.frequency);
+    
+}
+
+#pragma mark ---- lazying
+- (ToneTuningBodyView *)bodyView {
+    if (!_bodyView) {
+        _bodyView = [ToneTuningBodyView shareInstance];
+        MJWeakSelf;
+        
+    }
+    return _bodyView;
+}
+
+- (DialPlateView *)plateView {
+    if (!_plateView) {
+        _plateView = [[DialPlateView alloc] init];
+    }
+    return _plateView;
+}
+/*
+#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 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/Model/TuningFunction/KulexiuForStudent-Bridging-Header.h

@@ -0,0 +1,4 @@
+//
+//  Use this file to import your target's public headers that you would like to expose to Swift.
+//
+

+ 213 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/Model/TuningFunction/Tuner.swift

@@ -0,0 +1,213 @@
+//
+//  Tuner.swift
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+import Foundation
+import AudioKit
+
+
+private let flats = ["C", "D♭","D","E♭","E","F","G♭","G","A♭","A","B♭","B"]
+private let sharps = ["C", "C♯","D","D♯","E","F","F♯","G","G♯","A","A♯","B"]
+private let frequencies: [Float] = [
+    16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87, // 0
+    32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74, // 1
+    65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.8, 110.0, 116.5, 123.5, // 2
+    130.8, 138.6, 146.8, 155.6, 164.8, 174.6, 185.0, 196.0, 207.7, 220.0, 233.1, 246.9, // 3
+    261.6, 277.2, 293.7, 311.1, 329.6, 349.2, 370.0, 392.0, 415.3, 440.0, 466.2, 493.9, // 4
+    523.3, 554.4, 587.3, 622.3, 659.3, 698.5, 740.0, 784.0, 830.6, 880.0, 932.3, 987.8, // 5
+    1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,             // 6
+    2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,             // 7
+    4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902              // 8
+]
+
+/**
+ Types adopting the TunerDelegate protocol act as callbacks for Tuners and are
+ the mechanism by which you may receive and respond to new information decoded
+ by a Tuner.
+ */
+@objc public protocol TunerDelegate {
+    
+    /**
+     Called by a Tuner on each update.
+     
+     - parameter tuner: Tuner that performed the update.
+     - parameter output: Contains information decoded by the Tuner.
+     */
+    func tunerDidUpdate(_ tuner: Tuner, output: TunerOutput)
+}
+
+// MARK:- TunerOutput
+/**
+ Contains information decoded by a Tuner, such as frequency, octave, pitch, etc.
+ */
+@objc public class TunerOutput: NSObject {
+    
+    /**
+     The octave of the interpreted pitch.
+     */
+    @objc public fileprivate(set) var octave: Int = 0
+    
+    /**
+     The interpreted pitch of the microphone audio.
+     */
+    @objc public fileprivate(set) var pitch: String = ""
+    
+    /**
+     The difference between the frequency of the interpreted pitch and the actual
+     frequency of the microphone audio.
+     
+     For example if the microphone audio has a frequency of 432Hz, the pitch will
+     be interpreted as A4 (440Hz), thus making the distance -8Hz.
+     */
+    @objc public fileprivate(set) var distance: Float = 0.0
+    
+    /**
+     The amplitude of the microphone audio.
+     */
+    @objc public fileprivate(set) var amplitude: Float = 0.0
+    
+    /**
+     The frequency of the microphone audio.
+     */
+    @objc public fileprivate(set) var frequency: Float = 0.0
+    
+    fileprivate override init() {}
+}
+
+
+/**
+ A Tuner uses the devices microphone and interprets the frequency, pitch, etc.
+ */
+@objc public class Tuner: NSObject {
+    
+    fileprivate let smoothingBufferCount = 30
+    
+    fileprivate let threshold: Float
+    fileprivate let smoothing: Float
+    fileprivate var engine: AudioEngine?
+    fileprivate var microphone: AudioEngine.InputNode?
+    fileprivate var pitchTap: PitchTap?
+    fileprivate var silence: Fader?
+    fileprivate var smoothingBuffer: [Float] = []
+    
+    /**
+     Object adopting the TunerDelegate protocol that should receive callbacks
+     from this tuner.
+     */
+    @objc public var delegate: TunerDelegate?
+    
+    /**
+     Initializes a new Tuner.
+     
+     - parameter threshold: The minimum amplitude to recognize, 0 < threshold < 1
+     - parameter smoothing: Exponential smoothing factor, 0 < smoothing < 1
+     
+     */
+    @objc public init(threshold: Float = 0.0, smoothing: Float = 0.25) {
+        self.threshold = Float(min(abs(threshold), 1.0))
+        self.smoothing = Float(min(abs(smoothing), 1.0))
+    }
+    
+    /**
+     Starts the tuner.
+     */
+    @objc public func start() {
+        engine = AudioEngine()
+        microphone = engine!.input
+        silence = Fader(microphone!, gain: 0)
+        /**
+         调大bufferSize ,可以降低采样频率
+         16384 (10次/秒)
+         8192 (20次/秒)
+         4096 (40次/秒)  def
+         */
+        pitchTap = PitchTap(microphone!, bufferSize: 16384, handler: tap_handler)
+        microphone!.start()
+        pitchTap!.start()
+        engine!.output = silence
+        try? engine!.start()
+        
+    }
+    
+    /**
+     Stops the tuner.
+     */
+    @objc public func stop() {
+        microphone!.stop()
+        pitchTap!.stop()
+        engine!.stop()
+    }
+    
+    func tap_handler(freq: [Float], amp: [Float]) -> Void {
+#if DEBUG
+        print("freq -- real_\(freq[0]) -- imag_\(freq[1])" )
+        print("amp -- real_\(amp[0]) -- imag_\(amp[1])")
+#endif
+        if let d = self.delegate {
+            if amp[0] > self.threshold
+            {
+                let amplitude = amp[0]
+                let frequency = freq[0]
+                let output = Tuner.newOutput(frequency, amplitude)
+                DispatchQueue.main.async {
+                    d.tunerDidUpdate(self, output: output)
+                }
+            }
+        }
+    }
+    
+    /**
+     Exponential smoothing:
+     指数平滑算法
+     smoothing 平滑常数
+     https://en.wikipedia.org/wiki/Exponential_smoothing
+     */
+    fileprivate func smooth(_ value: Float) -> Float {
+        var frequency = value
+        if smoothingBuffer.count > 0 {
+            let last = smoothingBuffer.last!
+            frequency = (smoothing * value) + (1.0 - smoothing) * last
+            if smoothingBuffer.count > smoothingBufferCount {
+                smoothingBuffer.removeFirst()
+            }
+        }
+        smoothingBuffer.append(frequency)
+        return frequency
+    }
+    
+    /**
+     Transfer frequency and amplitude to TunerOutput
+    */
+    static func newOutput(_ frequency: Float, _ amplitude: Float) -> TunerOutput {
+        let output = TunerOutput()
+        
+        var norm = frequency
+        while norm > frequencies[frequencies.count - 1] {
+            norm = norm / 2.0
+        }
+        while norm < frequencies[0] {
+            norm = norm * 2.0
+        }
+        
+        var i = -1
+        var min = Float.infinity
+        for n in 0...frequencies.count-1 {
+            let diff = frequencies[n] - norm
+            if abs(diff) < abs(min) {
+                min = diff
+                i = n
+            }
+        }
+        
+        output.octave = i / 12
+        output.frequency = frequency
+        output.amplitude = amplitude
+        output.distance = frequency - frequencies[i]
+        output.pitch = String(format: "%@", sharps[i % sharps.count], flats[i % flats.count])
+        
+        return output
+    }
+}

+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetBottomButtonView.h → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetBottomButtonView.h


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetBottomButtonView.m → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetBottomButtonView.m


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetBottomButtonView.xib → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetBottomButtonView.xib


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetDotView.h → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetDotView.h


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetDotView.m → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetDotView.m


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetDotView.xib → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetDotView.xib


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetFunctionView.h → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetFunctionView.h


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetFunctionView.m → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetFunctionView.m


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetFunctionView.xib → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetFunctionView.xib


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetNavView.h → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetNavView.h


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetNavView.m → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetNavView.m


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetNavView.xib → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetNavView.xib


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetSpeedView.h → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetSpeedView.h


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetSpeedView.m → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetSpeedView.m


+ 0 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WidgetSpeedView.xib → KulexiuForStudent/KulexiuForStudent/Module/Widget/View/Metronome/WidgetSpeedView.xib


+ 27 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/SmallToolBodyView.h

@@ -0,0 +1,27 @@
+//
+//  SmallToolBodyView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSInteger, TOOLTYPE) {
+    TOOLTYPE_METRONOME, // 节拍器
+    TOOLTYPE_TONE,      // 调音器
+};
+
+typedef void(^ToolFunctionCallback)(TOOLTYPE type);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SmallToolBodyView : UIView
+
++ (instancetype)shareInstance;
+
+- (void)chooseToolCallback:(ToolFunctionCallback)callback;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 77 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/SmallToolBodyView.m

@@ -0,0 +1,77 @@
+//
+//  SmallToolBodyView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "SmallToolBodyView.h"
+
+@interface SmallToolBodyView ()
+
+@property (weak, nonatomic) IBOutlet UIView *metrnomeBgView;
+
+@property (weak, nonatomic) IBOutlet UIView *toneBgView;
+
+@property (nonatomic, copy) ToolFunctionCallback callback;
+
+@end
+
+@implementation SmallToolBodyView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    CAGradientLayer *layer = [self createGradientLayerFromColor:HexRGB(0xACFBEC) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x7FEED9) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 32, 114)];
+    layer.cornerRadius = 14.0f;
+    layer.masksToBounds = YES;
+    [self.metrnomeBgView.layer addSublayer:layer];
+    
+    CAGradientLayer *colorLayer = [self createGradientLayerFromColor:HexRGB(0xE2D5FF) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0xCAB2FF) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 32, 114)];
+    colorLayer.cornerRadius = 14.0f;
+    colorLayer.masksToBounds = YES;
+    [self.toneBgView.layer addSublayer:colorLayer];
+}
+
++ (instancetype)shareInstance {
+    SmallToolBodyView *view = [[[NSBundle mainBundle] loadNibNamed:@"SmallToolBodyView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)chooseToolCallback:(ToolFunctionCallback)callback {
+    if (callback) {
+        self.callback = callback;
+    }
+}
+
+- (IBAction)chooseMetronome:(id)sender {
+    if (self.callback) {
+        self.callback(TOOLTYPE_METRONOME);
+    }
+}
+
+- (IBAction)chooseTone:(id)sender {
+    if (self.callback) {
+        self.callback(TOOLTYPE_TONE);
+    }
+}
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+- (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
+    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
+    gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
+    gradientLayer.startPoint = startPoint;
+    gradientLayer.endPoint = endPoint;
+    gradientLayer.frame = bounds;
+    gradientLayer.locations = @[@(0),@(1.0f)];
+    return gradientLayer;
+}
+
+
+@end

+ 168 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/SmallToolBodyView.xib

@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <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="SmallToolBodyView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="536"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7pE-lf-Zsa">
+                    <rect key="frame" x="16" y="18" width="358" height="114"/>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <gestureRecognizers/>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KbL-Cl-bwO">
+                    <rect key="frame" x="16" y="18" width="358" height="114"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="节拍器" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mOb-C9-ek9">
+                            <rect key="frame" x="24" y="24" width="62" height="28"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="28" id="4cy-JE-o1v"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
+                            <color key="textColor" red="0.0" green="0.42745098039215684" blue="0.34509803921568627" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="节拍器,练习好节奏" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fYi-C4-Nd8">
+                            <rect key="frame" x="24" y="58" width="128.66666666666666" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="d06-5s-M0F"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.0" green="0.42745098040000001" blue="0.34509803919999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="metronome_image" translatesAutoresizingMaskIntoConstraints="NO" id="Bog-1X-TMf">
+                            <rect key="frame" x="226" y="0.0" width="132" height="114"/>
+                            <constraints>
+                                <constraint firstAttribute="width" secondItem="Bog-1X-TMf" secondAttribute="height" multiplier="22:19" id="geJ-Gg-GV4"/>
+                            </constraints>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="bottom" secondItem="Bog-1X-TMf" secondAttribute="bottom" id="2U1-Ni-Bfu"/>
+                        <constraint firstAttribute="height" constant="114" id="3WE-5U-XPW"/>
+                        <constraint firstItem="mOb-C9-ek9" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="top" constant="24" id="5IG-oq-ltv"/>
+                        <constraint firstAttribute="trailing" secondItem="Bog-1X-TMf" secondAttribute="trailing" id="CNH-J8-5wM"/>
+                        <constraint firstItem="mOb-C9-ek9" firstAttribute="leading" secondItem="KbL-Cl-bwO" secondAttribute="leading" constant="24" id="WUi-Xk-CB6"/>
+                        <constraint firstItem="fYi-C4-Nd8" firstAttribute="leading" secondItem="mOb-C9-ek9" secondAttribute="leading" id="phj-Lx-O0p"/>
+                        <constraint firstItem="Bog-1X-TMf" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="top" id="qwC-c0-eGn"/>
+                        <constraint firstItem="fYi-C4-Nd8" firstAttribute="top" secondItem="mOb-C9-ek9" secondAttribute="bottom" constant="6" id="vaN-vB-Gwk"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="14"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <outletCollection property="gestureRecognizers" destination="9vs-gn-bTj" appends="YES" id="wzz-JZ-nwz"/>
+                    </connections>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6Es-PT-RKh">
+                    <rect key="frame" x="16" y="148" width="358" height="114"/>
+                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    <gestureRecognizers/>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Usm-eK-CbE">
+                    <rect key="frame" x="16" y="148" width="358" height="114"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="调音器" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a3D-R7-hIK">
+                            <rect key="frame" x="24" y="24" width="62" height="28"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="28" id="H7M-N6-557"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
+                            <color key="textColor" red="0.42352941176470588" green="0.0" blue="0.76470588235294112" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="精准调音,一劳永逸" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="n9K-wq-FY7">
+                            <rect key="frame" x="24" y="58" width="128.66666666666666" height="20"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="20" id="laX-31-CM7"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" red="0.42352941176470588" green="0.0" blue="0.76470588235294112" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tone_tuning" translatesAutoresizingMaskIntoConstraints="NO" id="tri-rT-zBB">
+                            <rect key="frame" x="226" y="0.0" width="132" height="114"/>
+                            <constraints>
+                                <constraint firstAttribute="width" secondItem="tri-rT-zBB" secondAttribute="height" multiplier="22:19" id="SY7-Or-Rqy"/>
+                            </constraints>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="n9K-wq-FY7" firstAttribute="leading" secondItem="a3D-R7-hIK" secondAttribute="leading" id="3vQ-bM-igo"/>
+                        <constraint firstItem="tri-rT-zBB" firstAttribute="top" secondItem="Usm-eK-CbE" secondAttribute="top" id="KCv-Cp-h9E"/>
+                        <constraint firstItem="a3D-R7-hIK" firstAttribute="top" secondItem="Usm-eK-CbE" secondAttribute="top" constant="24" id="QGE-gc-JwY"/>
+                        <constraint firstItem="n9K-wq-FY7" firstAttribute="top" secondItem="a3D-R7-hIK" secondAttribute="bottom" constant="6" id="Rwh-yq-iaI"/>
+                        <constraint firstAttribute="bottom" secondItem="tri-rT-zBB" secondAttribute="bottom" id="hFh-0A-ete"/>
+                        <constraint firstAttribute="height" constant="114" id="jJn-LN-WQj"/>
+                        <constraint firstItem="a3D-R7-hIK" firstAttribute="leading" secondItem="Usm-eK-CbE" secondAttribute="leading" constant="24" id="t1c-0J-O2x"/>
+                        <constraint firstAttribute="trailing" secondItem="tri-rT-zBB" secondAttribute="trailing" id="y7r-SW-qnQ"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="14"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                    <connections>
+                        <outletCollection property="gestureRecognizers" destination="g8h-7k-sbT" appends="YES" id="5CR-UR-wvj"/>
+                    </connections>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="0.97254901960784312" green="0.97647058823529409" blue="0.9882352941176471" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="top" secondItem="Usm-eK-CbE" secondAttribute="top" id="6dg-GT-Ksh"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="bottom" secondItem="KbL-Cl-bwO" secondAttribute="bottom" id="GP7-dl-p1G"/>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="bottom" secondItem="Usm-eK-CbE" secondAttribute="bottom" id="HsQ-Jp-7lQ"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="trailing" secondItem="KbL-Cl-bwO" secondAttribute="trailing" id="KLk-JI-yy5"/>
+                <constraint firstItem="Usm-eK-CbE" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="bottom" constant="16" id="NNX-2g-AL1"/>
+                <constraint firstItem="KbL-Cl-bwO" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="18" id="Q2d-d9-jkt"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="leading" secondItem="KbL-Cl-bwO" secondAttribute="leading" id="SX4-zW-u40"/>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="leading" secondItem="Usm-eK-CbE" secondAttribute="leading" id="UVc-oT-i8l"/>
+                <constraint firstItem="7pE-lf-Zsa" firstAttribute="top" secondItem="KbL-Cl-bwO" secondAttribute="top" id="eIY-He-vXn"/>
+                <constraint firstItem="KbL-Cl-bwO" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="kZn-jQ-9OT"/>
+                <constraint firstItem="Usm-eK-CbE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="nNg-ul-d1k"/>
+                <constraint firstAttribute="trailing" secondItem="KbL-Cl-bwO" secondAttribute="trailing" constant="16" id="r6T-3q-ibo"/>
+                <constraint firstAttribute="trailing" secondItem="Usm-eK-CbE" secondAttribute="trailing" constant="16" id="vZr-Ku-Vfw"/>
+                <constraint firstItem="6Es-PT-RKh" firstAttribute="trailing" secondItem="Usm-eK-CbE" secondAttribute="trailing" id="xtt-yt-JZo"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="metrnomeBgView" destination="7pE-lf-Zsa" id="6s3-XW-LSJ"/>
+                <outlet property="toneBgView" destination="6Es-PT-RKh" id="TbE-VR-gNn"/>
+            </connections>
+            <point key="canvasLocation" x="87.692307692307693" y="68.957345971563981"/>
+        </view>
+        <tapGestureRecognizer id="9vs-gn-bTj">
+            <connections>
+                <action selector="chooseMetronome:" destination="iN0-l3-epB" id="rdo-OF-fwD"/>
+            </connections>
+        </tapGestureRecognizer>
+        <tapGestureRecognizer id="g8h-7k-sbT">
+            <connections>
+                <action selector="chooseTone:" destination="iN0-l3-epB" id="ueq-cr-tj7"/>
+            </connections>
+        </tapGestureRecognizer>
+    </objects>
+    <resources>
+        <image name="metronome_image" width="132" height="114"/>
+        <image name="tone_tuning" width="132" height="114"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 77 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeView.h

@@ -0,0 +1,77 @@
+/*
+ * WMGaugeView.h
+ *
+ * Copyright (C) 2014 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <UIKit/UIKit.h>
+#import "WMGaugeViewStyle.h"
+#import "WMGaugeViewStyleFlatThin.h"
+#import "WMGaugeViewStyle3D.h"
+
+/**
+ * Styling enumerations
+ */
+typedef NS_ENUM(NSUInteger, WMGaugeViewSubdivisionsAlignment) {
+    WMGaugeViewSubdivisionsAlignmentTop,
+    WMGaugeViewSubdivisionsAlignmentCenter,
+    WMGaugeViewSubdivisionsAlignmentBottom
+};
+
+/**
+ * WMGaugeView class
+ */
+@interface WMGaugeView : UIView
+
+/**
+ * WMGaugeView properties
+ */
+@property (nonatomic, readwrite, assign) bool showInnerBackground;
+@property (nonatomic, readwrite, assign) bool showInnerRim;
+@property (nonatomic, readwrite, assign) CGFloat innerRimWidth;
+@property (nonatomic, readwrite, assign) CGFloat innerRimBorderWidth;
+@property (nonatomic, readwrite, assign) CGFloat scalePosition;
+@property (nonatomic, readwrite, assign) CGFloat scaleStartAngle;
+@property (nonatomic, readwrite, assign) CGFloat scaleEndAngle;
+@property (nonatomic, readwrite, assign) CGFloat scaleDivisions;
+@property (nonatomic, readwrite, assign) CGFloat scaleSubdivisions;
+@property (nonatomic, readwrite, assign) bool showScaleShadow;
+@property (nonatomic, readwrite, assign) bool showScale;
+@property (nonatomic, readwrite, assign) bool showScaleValues;
+@property (nonatomic, readwrite, assign) WMGaugeViewSubdivisionsAlignment scalesubdivisionsAligment;
+@property (nonatomic, readwrite, assign) CGFloat scaleDivisionsLength;
+@property (nonatomic, readwrite, assign) CGFloat scaleDivisionsWidth;
+@property (nonatomic, readwrite, assign) CGFloat scaleSubdivisionsLength;
+@property (nonatomic, readwrite, assign) CGFloat scaleSubdivisionsWidth;
+@property (nonatomic, readwrite, strong) UIColor *scaleDivisionColor;
+@property (nonatomic, readwrite, strong) UIColor *scaleSubDivisionColor;
+@property (nonatomic, readwrite, strong) UIFont *scaleFont;
+@property (nonatomic, readwrite, assign) float value;
+@property (nonatomic, readwrite, assign) float minValue;
+@property (nonatomic, readwrite, assign) float maxValue;
+@property (nonatomic, readwrite, assign) bool showRangeLabels;
+@property (nonatomic, readwrite, assign) CGFloat rangeLabelsWidth;
+@property (nonatomic, readwrite, strong) UIFont *rangeLabelsFont;
+@property (nonatomic, readwrite, assign) BOOL adjustRangeLabelSizeToFitWidth;
+@property (nonatomic, readwrite, strong) UIColor *rangeLabelsFontColor;
+@property (nonatomic, readwrite, assign) CGFloat rangeLabelsFontKerning;
+@property (nonatomic, readwrite, strong) NSArray<NSNumber*> *rangeValues;
+@property (nonatomic, readwrite, strong) NSArray<UIColor*> *rangeColors;
+@property (nonatomic, readwrite, strong) NSArray<NSString*> *rangeLabels;
+@property (nonatomic, readwrite, strong) UIColor *unitOfMeasurementColor;
+@property (nonatomic, readwrite, assign) CGFloat unitOfMeasurementVerticalOffset;
+@property (nonatomic, readwrite, strong) UIFont *unitOfMeasurementFont;
+@property (nonatomic, readwrite, strong) NSString *unitOfMeasurement;
+@property (nonatomic, readwrite, assign) bool showUnitOfMeasurement;
+@property (nonatomic, readwrite, strong) id<WMGaugeViewStyle> style;
+
+/**
+ * WMGaugeView public functions
+ */
+- (void)setValue:(float)value animated:(BOOL)animated;
+- (void)setValue:(float)value animated:(BOOL)animated completion:(void (^)(BOOL finished))completion;
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration;
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration completion:(void (^)(BOOL finished))completion;
+
+@end

+ 820 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeView.m

@@ -0,0 +1,820 @@
+/*
+ * WMGaugeView.h
+ *
+ * Copyright (C) 2014 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import "WMGaugeView.h"
+
+/* Scale conversion macro from [0-1] range to view  real size range */
+#define FULL_SCALE(x,y)    (x)*self.bounds.size.width, (y)*self.bounds.size.height
+
+@implementation WMGaugeView
+{
+    /* Drawing rects */
+    CGRect fullRect;
+    CGRect innerRimRect;
+    CGRect innerRimBorderRect;
+    CGRect faceRect;
+    CGRect rangeLabelsRect;
+    CGRect scaleRect;
+
+    /* View center */
+    CGPoint center;
+
+    /* Scale variables */
+    CGFloat scaleRotation;    
+    CGFloat divisionValue;
+    CGFloat subdivisionValue;
+    CGFloat subdivisionAngle;
+    
+    /* Background image */
+    UIImage *background;
+    
+    /* Needle layer */
+    CALayer *rootNeedleLayer;
+
+    /* Annimation completion */
+    void (^animationCompletion)(BOOL);
+}
+
+#pragma mark - Initialization
+
+- (id)initWithFrame:(CGRect)frame
+{
+    self = [super initWithFrame:frame];
+    if (self)
+    {
+        [self initialize];
+    }
+    return self;
+}
+
+- (void)awakeFromNib
+{
+    [super awakeFromNib];
+    [self initialize];
+}
+
+/**
+ *  Set all properties to default values
+ *  Set all private variables to default values
+ */
+- (void)initialize;
+{
+    _style = nil;
+    _showInnerRim = NO;
+    _showInnerBackground = YES;
+    _innerRimWidth = 0.05;
+    _innerRimBorderWidth = 0.005;
+    
+    _scalePosition = 0.025;
+    _scaleStartAngle = 30.0;
+    _scaleEndAngle = 330.0;
+    _scaleDivisions = 12.0;
+    _scaleSubdivisions = 10.0;
+    _showScale = YES;
+    _showScaleShadow = YES;
+    _showScaleValues = YES;
+    _scalesubdivisionsAligment = WMGaugeViewSubdivisionsAlignmentTop;
+    _scaleDivisionsLength = 0.045;
+    _scaleDivisionsWidth = 0.01;
+    _scaleSubdivisionsLength = 0.015;
+    _scaleSubdivisionsWidth = 0.01;
+    
+    _value = 0.0;
+    _minValue = 0.0;
+    _maxValue = 240.0;
+
+    background = nil;
+    
+    _showRangeLabels = NO;
+    _rangeLabelsWidth = 0.05;
+    _rangeLabelsFont = [UIFont fontWithName:@"Helvetica" size:0.05];
+    _adjustRangeLabelSizeToFitWidth = NO;
+    _rangeLabelsFontColor = [UIColor whiteColor];
+    _rangeLabelsFontKerning = 1.0;
+    _rangeValues = nil;
+    _rangeColors = nil;
+    _rangeLabels = nil;
+    
+    _scaleDivisionColor = RGB(68, 84, 105);
+    _scaleSubDivisionColor = RGB(217, 217, 217);
+    
+    _scaleFont = nil;
+    
+    _unitOfMeasurementVerticalOffset = 0.6;
+    _unitOfMeasurementColor = [UIColor whiteColor];
+    _unitOfMeasurementFont = [UIFont fontWithName:@"Helvetica" size:0.04];
+    _unitOfMeasurement = @"";
+    _showUnitOfMeasurement = NO;
+    
+    animationCompletion = nil;
+
+    [self initDrawingRects];
+    [self initScale];
+}
+
+/**
+ *  Initialize all drawing rects and center point
+ */
+- (void)initDrawingRects
+{
+    center = CGPointMake(0.5, 0.5);
+    fullRect = CGRectMake(0.0, 0.0, 1.0, 1.0);
+    
+    _innerRimBorderWidth = _showInnerRim ? _innerRimBorderWidth : 0.0;
+    _innerRimWidth = _showInnerRim ? _innerRimWidth : 0.0;
+    
+    innerRimRect = fullRect;
+    innerRimBorderRect = CGRectMake(innerRimRect.origin.x + _innerRimBorderWidth,
+                                    innerRimRect.origin.y + _innerRimBorderWidth,
+                                    innerRimRect.size.width - 2 * _innerRimBorderWidth,
+                                    innerRimRect.size.height - 2 * _innerRimBorderWidth);
+    faceRect = CGRectMake(innerRimRect.origin.x + _innerRimWidth,
+                          innerRimRect.origin.y + _innerRimWidth,
+                          innerRimRect.size.width - 2 * _innerRimWidth,
+                          innerRimRect.size.height - 2 * _innerRimWidth);
+    rangeLabelsRect = CGRectMake(faceRect.origin.x + (_showRangeLabels ? _rangeLabelsWidth : 0.0),
+                                 faceRect.origin.y + (_showRangeLabels ? _rangeLabelsWidth : 0.0),
+                                 faceRect.size.width - 2 * (_showRangeLabels ? _rangeLabelsWidth : 0.0),
+                                 faceRect.size.height - 2 * (_showRangeLabels ? _rangeLabelsWidth : 0.0));
+    scaleRect = CGRectMake(rangeLabelsRect.origin.x + _scalePosition,
+                           rangeLabelsRect.origin.y + _scalePosition,
+                           rangeLabelsRect.size.width - 2 * _scalePosition,
+                           rangeLabelsRect.size.height - 2 * _scalePosition);
+}
+
+#pragma mark - Drawing
+
+/**
+ * Main drawing entry point 
+ */
+- (void)drawRect:(CGRect)rect
+{
+    if (background == nil)
+    {
+        // Create image context
+        UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale);
+        CGContextRef context = UIGraphicsGetCurrentContext();
+        
+        // Scale context for [0-1] drawings
+        CGContextScaleCTM(context, rect.size.width , rect.size.height);
+
+        // Draw gauge background in image context
+        [self drawGauge:context];
+        
+        // Save background
+        background = UIGraphicsGetImageFromCurrentImageContext();
+        UIGraphicsEndImageContext();
+    }
+    
+    // Drawing background in view
+    [background drawInRect:rect];
+    
+    if (rootNeedleLayer == nil)
+    {
+        // Initialize needle layer
+        rootNeedleLayer = [CALayer new];
+
+        // For performance puporse, the needle layer is not scaled to [0-1] range
+        rootNeedleLayer.frame = self.bounds;
+        [self.layer addSublayer:rootNeedleLayer];
+        
+        // Draw needle
+        [self drawNeedle];
+        
+        // Set needle current value
+        [self setValue:_value animated:NO];
+    }    
+}
+
+/**
+ *  Gauge background drawing
+ */
+- (void)drawGauge:(CGContextRef)context
+{
+    [self drawRim:context];
+
+    if (_showInnerBackground)
+        [self drawFace:context];
+
+    if (_showUnitOfMeasurement)
+        [self drawText:context];
+
+    if (_showScale)
+        [self drawScale:context];
+
+    if (_showRangeLabels)
+        [self drawRangeLabels:context];
+}
+
+/**
+ *  Gauge external rim drawing
+ */
+- (void)drawRim:(CGContextRef)context
+{
+    // TODO
+}
+
+/**
+ *  Gauge inner background drawing
+ */
+- (void)drawFace:(CGContextRef)context
+{
+    if ([_style conformsToProtocol:@protocol(WMGaugeViewStyle)]) {
+        [_style drawFaceWithContext:context inRect:faceRect];
+    }
+}
+
+/**
+ *  Unit of measurement drawing
+ */
+- (void)drawText:(CGContextRef)context
+{
+    CGContextSetShadow(context, CGSizeMake(0.05, 0.05), 2.0);
+    UIFont* font = _unitOfMeasurementFont ? _unitOfMeasurementFont : [UIFont fontWithName:@"Helvetica" size:0.04];
+    UIColor* color = _unitOfMeasurementColor ? _unitOfMeasurementColor : [UIColor whiteColor];
+    NSDictionary* stringAttrs = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : color };
+    NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:_unitOfMeasurement attributes:stringAttrs];
+    CGSize fontWidth;
+    fontWidth = [_unitOfMeasurement sizeWithAttributes:stringAttrs];
+
+    [attrStr drawAtPoint:CGPointMake(0.5 - fontWidth.width / 2.0, _unitOfMeasurementVerticalOffset)];
+}
+
+/**
+ * Scale drawing 
+ */
+- (void)drawScale:(CGContextRef)context
+{
+    CGContextSaveGState(context);
+    [self rotateContext:context fromCenter:center withAngle:DEGREES_TO_RADIANS(180 + _scaleStartAngle)];
+    
+    int totalTicks = _scaleDivisions * _scaleSubdivisions + 1;
+    for (int i = 0; i < totalTicks; i++)
+    {
+        CGFloat offset = 0.0;
+        if (_scalesubdivisionsAligment == WMGaugeViewSubdivisionsAlignmentCenter) offset = (_scaleDivisionsLength - _scaleSubdivisionsLength) / 2.0;
+        if (_scalesubdivisionsAligment == WMGaugeViewSubdivisionsAlignmentBottom) offset = _scaleDivisionsLength - _scaleSubdivisionsLength;
+        
+        CGFloat y1 = scaleRect.origin.y;
+        CGFloat y2 = y1 + _scaleSubdivisionsLength;
+        CGFloat y3 = y1 + _scaleDivisionsLength;
+        
+        float value = [self valueForTick:i];
+        float div = (_maxValue - _minValue) / _scaleDivisions;
+        float mod = (int)value % (int)div;
+        
+        // Division
+        if ((fabsf(mod - 0) < 0.000001) || (fabsf(mod - div) < 0.000001))
+        {
+            // Initialize Core Graphics settings
+            UIColor *color = (_rangeValues && _rangeColors) ? [self rangeColorForValue:value] : _scaleDivisionColor;
+            CGContextSetStrokeColorWithColor(context, color.CGColor);
+            CGContextSetLineWidth(context, _scaleDivisionsWidth);
+            CGContextSetShadow(context, CGSizeMake(0.05, 0.05), _showScaleShadow ? 2.0 : 0.0);
+            
+            // Draw tick
+            CGContextMoveToPoint(context, 0.5, y1);
+            CGContextAddLineToPoint(context, 0.5, y3);
+            CGContextStrokePath(context);
+            
+            // Draw label
+            if(_showScaleValues) {
+                NSString *valueString = [NSString stringWithFormat:@"%0.0f",value];
+                UIFont* font = _scaleFont ? _scaleFont : [UIFont fontWithName:@"Helvetica-Bold" size:0.05];
+                NSDictionary* stringAttrs = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : color };
+                NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:valueString attributes:stringAttrs];
+                CGSize fontWidth;
+                fontWidth = [valueString sizeWithAttributes:stringAttrs];
+                
+                [attrStr drawAtPoint:CGPointMake(0.5 - fontWidth.width / 2.0, y3 + 0.005)];
+            }
+        }
+        // Subdivision
+        else
+        {
+            // Initialize Core Graphics settings
+            UIColor *color = (_rangeValues && _rangeColors) ? [self rangeColorForValue:value] : _scaleSubDivisionColor;
+            CGContextSetStrokeColorWithColor(context, color.CGColor);
+            CGContextSetLineWidth(context, _scaleSubdivisionsWidth);
+            CGContextMoveToPoint(context, 0.5, y1);
+            if (_showScaleShadow) CGContextSetShadow(context, CGSizeMake(0.05, 0.05), 2.0);
+            
+            // Draw tick
+            CGContextMoveToPoint(context, 0.5, y1 + offset);
+            CGContextAddLineToPoint(context, 0.5, y2 + offset);
+            CGContextStrokePath(context);
+        }
+        
+        // Rotate to next tick
+        [self rotateContext:context fromCenter:center withAngle:DEGREES_TO_RADIANS(subdivisionAngle)];
+    }
+    CGContextRestoreGState(context);
+}
+
+/**
+ * scale range labels drawing 
+ */
+- (void)drawRangeLabels:(CGContextRef)context
+{
+    CGContextSaveGState(context);
+    [self rotateContext:context fromCenter:center withAngle:DEGREES_TO_RADIANS(90 + _scaleStartAngle)];
+    CGContextSetShadow(context, CGSizeMake(0.0, 0.0), 0.0);
+    
+    CGFloat maxAngle = _scaleEndAngle - _scaleStartAngle;
+    CGFloat lastStartAngle = 0.0f;
+
+    for (int i = 0; i < _rangeValues.count; i ++)
+    {
+        // Range value
+        float value = ((NSNumber*)[_rangeValues objectAtIndex:i]).floatValue;
+        float valueAngle = (value - _minValue) / (_maxValue - _minValue) * maxAngle;
+        
+        // Range curved shape
+        UIBezierPath *path = [UIBezierPath bezierPath];
+        [path addArcWithCenter:center radius:rangeLabelsRect.size.width / 2.0 startAngle:DEGREES_TO_RADIANS(lastStartAngle) endAngle:DEGREES_TO_RADIANS(valueAngle) - 0.01 clockwise:YES];
+        
+        UIColor *color = _rangeColors[i];
+        [color setStroke];
+        path.lineWidth = _rangeLabelsWidth;
+        [path stroke];
+        
+        // Range curved label
+        [self drawStringAtContext:context string:(NSString*)_rangeLabels[i] withCenter:center radius:rangeLabelsRect.size.width / 2.0 startAngle:DEGREES_TO_RADIANS(lastStartAngle) endAngle:DEGREES_TO_RADIANS(valueAngle)];
+        
+        lastStartAngle = valueAngle;
+    }
+    
+    CGContextRestoreGState(context);
+}
+
+/**
+ * Needle drawing 
+ */
+- (void)drawNeedle
+{
+    if ([_style conformsToProtocol:@protocol(WMGaugeViewStyle)]) {
+        [_style drawNeedleOnLayer:rootNeedleLayer inRect:self.bounds];
+    }
+}
+
+#pragma mark - Tools
+
+/**
+ * Core Graphics rotation in context
+ */
+- (void)rotateContext:(CGContextRef)context fromCenter:(CGPoint)center_ withAngle:(CGFloat)angle
+{
+    CGContextTranslateCTM(context, center_.x, center_.y);
+    CGContextRotateCTM(context, angle);
+    CGContextTranslateCTM(context, -center_.x, -center_.y);
+}
+
+/**
+ * Needle angle computation
+ */
+- (CGFloat)needleAngleForValue:(double)value
+{
+    return DEGREES_TO_RADIANS(_scaleStartAngle + (value - _minValue) / (_maxValue - _minValue) * (_scaleEndAngle - _scaleStartAngle)) + M_PI;
+}
+
+/**
+ * Initialize scale helper values
+ */
+- (void)initScale
+{
+    scaleRotation = (int)(_scaleStartAngle + 180) % 360;
+    divisionValue = (_maxValue - _minValue) / _scaleDivisions;
+    subdivisionValue = divisionValue / _scaleSubdivisions;
+    subdivisionAngle = (_scaleEndAngle - _scaleStartAngle) / (_scaleDivisions * _scaleSubdivisions);
+}
+
+/**
+ * Scale tick value computation
+ */
+- (float)valueForTick:(int)tick
+{
+    return tick * (divisionValue / _scaleSubdivisions) + _minValue;
+}
+
+/**
+ * Scale range label color for value
+ */
+- (UIColor*)rangeColorForValue:(float)value
+{
+    NSInteger length = _rangeValues.count;
+    for (int i = 0; i < length - 1; i++)
+    {
+        if (value < [_rangeValues[i] floatValue])
+            return _rangeColors[i];
+    }
+    if (value <= [_rangeValues[length - 1] floatValue])
+        return _rangeColors[length - 1];
+    return nil;
+}
+
+/**
+ * Draw curved NSSring in context
+ */
+- (void)drawStringAtContext:(CGContextRef) context string:(NSString*)text withCenter:(CGPoint)center_ radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle
+{
+    CGContextSaveGState(context);
+    
+    UIFont* font = _rangeLabelsFont ? _rangeLabelsFont : [UIFont fontWithName:@"Helvetica" size:0.05];
+    UIColor* color = _rangeLabelsFontColor ? _rangeLabelsFontColor : [UIColor whiteColor];
+
+    //add extra character to ensure there is some padding
+    NSString* testText = [text stringByAppendingString:@"$"];
+    while(_adjustRangeLabelSizeToFitWidth) {
+    
+        CGFloat maxWidth = radius * (endAngle - startAngle);
+        CGSize textSize = [testText boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
+                                      options:NSStringDrawingUsesLineFragmentOrigin
+                                   attributes:@{NSFontAttributeName: font}
+                                       context:nil].size;
+
+        if(textSize.height < _rangeLabelsWidth) {
+            break;
+        }
+        
+        font = [font fontWithSize:font.pointSize * 0.9];
+    }
+    
+    NSDictionary* stringAttrs = @{ NSFontAttributeName : font, NSForegroundColorAttributeName : color };
+    CGSize textSize = [text sizeWithAttributes:stringAttrs];
+    
+    float perimeter = 2 * M_PI * radius;
+    float textAngle = textSize.width / perimeter * 2 * M_PI * _rangeLabelsFontKerning;
+    float offset = ((endAngle - startAngle) - textAngle) / 2.0;
+
+    float letterPosition = 0;
+    NSString *lastLetter = @"";
+    
+    [self rotateContext:context fromCenter:center withAngle:startAngle + offset];
+    for (int index = 0; index < [text length]; index++)
+    {
+        NSRange range = {index, 1};
+        NSString* letter = [text substringWithRange:range];
+        NSAttributedString* attrStr = [[NSAttributedString alloc] initWithString:letter attributes:stringAttrs];
+        CGSize charSize = [letter sizeWithAttributes:stringAttrs];
+        float totalWidth = [[NSString stringWithFormat:@"%@%@",lastLetter, letter] sizeWithAttributes:stringAttrs].width;
+        float currentLetterWidth = [letter sizeWithAttributes:stringAttrs].width;
+        float lastLetterWidth = [lastLetter sizeWithAttributes:stringAttrs].width;
+        float kerning = (lastLetterWidth) ? 0.0 : ((currentLetterWidth + lastLetterWidth) - totalWidth);
+        
+        letterPosition += (charSize.width / 2) - kerning;
+        float angle = (letterPosition / perimeter * 2 * M_PI) * _rangeLabelsFontKerning;
+        CGPoint letterPoint = CGPointMake((radius - charSize.height / 2.0) * cos(angle) + center_.x, (radius - charSize.height / 2.0) * sin(angle) + center_.y);
+        
+        CGContextSaveGState(context);
+        CGContextTranslateCTM(context, letterPoint.x, letterPoint.y);
+        CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(angle + M_PI_2);
+        CGContextConcatCTM(context, rotationTransform);
+        CGContextTranslateCTM(context, -letterPoint.x, -letterPoint.y);
+
+        [attrStr drawAtPoint:CGPointMake(letterPoint.x - charSize.width/2 , letterPoint.y - charSize.height)];
+        
+        CGContextRestoreGState(context);
+        
+        letterPosition += charSize.width / 2;
+        lastLetter = letter;
+    }
+    CGContextRestoreGState(context);
+}
+
+/**
+ * Invalidate background
+ * Background will be regenerated during next draw rect
+ */
+- (void)invalidateBackground
+{
+    background = nil;
+    [self initDrawingRects];
+    [self initScale];
+    [self setNeedsDisplay];
+}
+
+
+/**
+ * Invalidate Needle
+ * Needle will be regenerated during next draw rect
+ */
+- (void)invalidateNeedle
+{
+    [rootNeedleLayer removeAllAnimations];
+    rootNeedleLayer.sublayers = nil;
+    rootNeedleLayer = nil;
+    
+    [self setNeedsDisplay];
+}
+
+#pragma mark - Value update
+
+/**
+ * Update gauge value
+ */
+- (void)updateValue:(float)value
+{
+    // Clamp value if out of range
+    if (value > _maxValue)
+        value = _maxValue;
+    else if (value < _minValue)
+        value = _minValue;
+    else
+        value = value;
+    
+    // Set value
+    _value = value;
+}
+
+/**
+ * Update gauge value with animation
+ */
+- (void)setValue:(float)value animated:(BOOL)animated
+{
+    [self setValue:value animated:animated duration:0.8];
+}
+
+/**
+ * Update gauge value with animation and fire a completion block
+ */
+- (void)setValue:(float)value animated:(BOOL)animated completion:(void (^)(BOOL finished))completion
+{
+    [self setValue:value animated:animated duration:0.8 completion:completion];
+}
+
+/**
+ * Update gauge value with animation and duration
+ */
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration
+{
+    [self setValue:value animated:animated duration:duration completion:nil];
+}
+
+/**
+ * Update gauge value with animation, duration and fire a completion block
+ */
+- (void)setValue:(float)value animated:(BOOL)animated duration:(NSTimeInterval)duration completion:(void (^)(BOOL finished))completion
+{
+    animationCompletion = completion;
+    
+    double lastValue = _value;
+    
+    [self updateValue:value];
+    double middleValue = lastValue + (((lastValue + (_value - lastValue) / 2.0) >= 0) ? (_value - lastValue) / 2.0 : (lastValue - _value) / 2.0);
+    
+    // Needle animation to target value
+    // An intermediate "middle" value is used to make sure the needle will follow the right rotation direction
+    
+    CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
+    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    animation.removedOnCompletion = YES;
+    animation.duration = animated ? duration : 0.0;
+    animation.delegate = (id<CAAnimationDelegate>)self;
+    animation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeRotation([self needleAngleForValue:lastValue]  , 0, 0, 1.0)],
+                         [NSValue valueWithCATransform3D:CATransform3DMakeRotation([self needleAngleForValue:middleValue], 0, 0, 1.0)],
+                         [NSValue valueWithCATransform3D:CATransform3DMakeRotation([self needleAngleForValue:_value]     , 0, 0, 1.0)]];
+    
+    if ([_style conformsToProtocol:@protocol(WMGaugeViewStyle)] == NO || [_style needleLayer:rootNeedleLayer willMoveAnimated:animated duration:duration animation:animation] == NO)
+    {
+        rootNeedleLayer.transform = [[animation.values lastObject] CATransform3DValue];
+        [rootNeedleLayer addAnimation:animation forKey:kCATransition];
+    }
+}
+
+#pragma mark - CAAnimation delegate
+
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
+{
+    if (animationCompletion)
+        animationCompletion(flag);
+    
+    animationCompletion = nil;
+}
+
+#pragma mark - Properties
+
+- (void)setValue:(float)value
+{
+    [self setValue:value animated:YES];
+}
+
+- (void)setShowInnerBackground:(bool)showInnerBackground
+{
+    _showInnerBackground = showInnerBackground;
+    [self invalidateBackground];
+}
+
+- (void)setShowInnerRim:(bool)showInnerRim
+{
+    _showInnerRim = showInnerRim;
+    [self invalidateBackground];
+}
+
+- (void)setInnerRimWidth:(CGFloat)innerRimWidth
+{
+    _innerRimWidth = innerRimWidth;
+    [self invalidateBackground];
+}
+
+- (void)setInnerRimBordeWidth:(CGFloat)innerRimBorderWidth
+{
+    _innerRimBorderWidth = innerRimBorderWidth;
+    [self invalidateBackground];
+}
+
+- (void)setScalePosition:(CGFloat)scalePosition
+{
+    _scalePosition = scalePosition;
+    [self invalidateBackground];
+}
+
+- (void)setScaleStartAngle:(CGFloat)scaleStartAngle
+{
+    _scaleStartAngle = scaleStartAngle;
+    [self invalidateBackground];
+}
+
+- (void)setScaleEndAngle:(CGFloat)scaleEndAngle
+{
+    _scaleEndAngle = scaleEndAngle;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisions:(CGFloat)scaleDivisions
+{
+    _scaleDivisions = scaleDivisions;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubdivisions:(CGFloat)scaleSubdivisions
+{
+    _scaleSubdivisions = scaleSubdivisions;
+    [self invalidateBackground];
+}
+
+- (void)setShowScaleShadow:(bool)showScaleShadow
+{
+    _showScaleShadow = showScaleShadow;
+    [self invalidateBackground];
+}
+
+- (void)setShowScale:(bool)showScale
+{
+    _showScale = showScale;
+    [self invalidateBackground];
+}
+
+-(void)setShowScaleValues:(bool)showScaleValues {
+    _showScaleValues = showScaleValues;
+    [self invalidateBackground];
+}
+
+- (void)setScalesubdivisionsAligment:(WMGaugeViewSubdivisionsAlignment)scalesubdivisionsAligment
+{
+    _scalesubdivisionsAligment = scalesubdivisionsAligment;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisionsLength:(CGFloat)scaleDivisionsLength
+{
+    _scaleDivisionsLength = scaleDivisionsLength;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisionsWidth:(CGFloat)scaleDivisionsWidth
+{
+    _scaleDivisionsWidth = scaleDivisionsWidth;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubdivisionsLength:(CGFloat)scaleSubdivisionsLength
+{
+    _scaleSubdivisionsLength = scaleSubdivisionsLength;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubdivisionsWidth:(CGFloat)scaleSubdivisionsWidth
+{
+    _scaleSubdivisionsWidth = scaleSubdivisionsWidth;
+    [self invalidateBackground];
+}
+
+- (void)setScaleDivisionColor:(UIColor *)scaleDivisionColor
+{
+    _scaleDivisionColor = scaleDivisionColor;
+    [self invalidateBackground];
+}
+
+- (void)setScaleSubDivisionColor:(UIColor *)scaleSubDivisionColor
+{
+    _scaleSubDivisionColor = scaleSubDivisionColor;
+    [self invalidateBackground];
+}
+
+- (void)setScaleFont:(UIFont *)scaleFont
+{
+    _scaleFont = scaleFont;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsWidth:(CGFloat)rangeLabelsWidth
+{
+    _rangeLabelsWidth = rangeLabelsWidth;
+    [self invalidateBackground];
+}
+
+- (void)setMinValue:(float)minValue
+{
+    _minValue = minValue;
+    [self invalidateBackground];
+}
+
+- (void)setMaxValue:(float)maxValue
+{
+    _maxValue = maxValue;
+    [self invalidateBackground];
+}
+
+- (void)setRangeValues:(NSArray *)rangeValues
+{
+    _rangeValues = rangeValues;
+    [self invalidateBackground];
+}
+
+- (void)setRangeColors:(NSArray *)rangeColors
+{
+    _rangeColors = rangeColors;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabels:(NSArray *)rangeLabels
+{
+    _rangeLabels = rangeLabels;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurement:(NSString *)unitOfMeasurement
+{
+    _unitOfMeasurement = unitOfMeasurement;
+    [self invalidateBackground];
+}
+
+- (void)setShowUnitOfMeasurement:(bool)showUnitOfMeasurement
+{
+    _showUnitOfMeasurement = showUnitOfMeasurement;
+    [self invalidateBackground];
+}
+
+- (void)setShowRangeLabels:(bool)showRangeLabels
+{
+    _showRangeLabels = showRangeLabels;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsFontKerning:(CGFloat)rangeLabelsFontKerning
+{
+    _rangeLabelsFontKerning = rangeLabelsFontKerning;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurementFont:(UIFont *)unitOfMeasurementFont
+{
+    _unitOfMeasurementFont = unitOfMeasurementFont;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsFont:(UIFont *)rangeLabelsFont
+{
+    _rangeLabelsFont = rangeLabelsFont;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurementVerticalOffset:(CGFloat)unitOfMeasurementVerticalOffset
+{
+    _unitOfMeasurementVerticalOffset = unitOfMeasurementVerticalOffset;
+    [self invalidateBackground];
+}
+
+- (void)setUnitOfMeasurementColor:(UIColor *)unitOfMeasurementColor
+{
+    _unitOfMeasurementColor = unitOfMeasurementColor;
+    [self invalidateBackground];
+}
+
+- (void)setRangeLabelsFontColor:(UIColor *)rangeLabelsFontColor
+{
+    _rangeLabelsFontColor = rangeLabelsFontColor;
+    [self invalidateBackground];
+}
+
+- (void)setStyle:(id<WMGaugeViewStyle>)style {
+    _style = style;
+    [self invalidateBackground];
+    [self invalidateNeedle];
+}
+
+@end

+ 58 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyle.h

@@ -0,0 +1,58 @@
+/*
+ * WMGaugeViewStyle.h
+ *
+ * Copyright (C) 2015 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import <UIKit/UIKit.h>
+
+/* Degrees to radians conversion macro */
+#define DEGREES_TO_RADIANS(degrees) (degrees) / 180.0 * M_PI
+
+/* Colors creation macros */
+#define RGB(r,g,b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0]
+#define RGBA(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a/255.0]
+#define CGRGB(r,g,b) RGB(r,g,b).CGColor
+#define iCGRGB(r,g,b) (id)CGRGB(r,g,b)
+#define CGRGBA(r,g,b,a) RGBA(r,g,b,a).CGColor
+#define iCGRGBA(r,g,b,a) (id)CGRGBA(r,g,b,a)
+
+static inline
+NSArray* CGColorArray(NSArray<UIColor*>*c) {
+    NSMutableArray* _c = [NSMutableArray array];
+    for(UIColor* col in c) {
+        [_c addObject: (id)col.CGColor];
+    }
+    return _c.copy;
+}
+
+static inline
+CGFloat* CGFloatCArray(NSArray<NSNumber*>*a) {
+    CGFloat* ar = malloc(sizeof(*ar) * a.count);
+    [a enumerateObjectsUsingBlock:^(NSNumber* obj, NSUInteger idx, BOOL * _Nonnull stop) {
+        ar[idx] = (CGFloat)obj.doubleValue;
+    }];
+    return ar;
+}
+
+/* Position macros */
+#define FULL_RECT           CGRectMake(0.0, 0.0, 1.0, 1.0)
+#define kCenterX            0.5
+#define kCenterY            0.5
+#define kCenterPoint        CGPointMake(kCenterX, kCenterY)
+
+/* Scale conversion macro from [0-1] range to view  real size range */
+#define FULLSCALE(x,y)    (x)*rect.size.width, (y)*rect.size.height
+
+/**
+ * WMGaugeView style protocol
+ */
+@protocol WMGaugeViewStyle <NSObject>
+@required
+- (void)drawNeedleOnLayer:(CALayer*)layer inRect:(CGRect)rect;
+- (void)drawFaceWithContext:(CGContextRef)context inRect:(CGRect)rect;
+- (BOOL)needleLayer:(CALayer*)layer willMoveAnimated:(BOOL)animated duration:(NSTimeInterval)duration animation:(CAKeyframeAnimation*)animation;
+
+@end

+ 32 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.h

@@ -0,0 +1,32 @@
+/*
+ * WMGaugeViewStyle3D.h
+ *
+ * Copyright (C) 2015 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "WMGaugeViewStyle.h"
+
+@interface WMGaugeViewStyle3D : NSObject<WMGaugeViewStyle>
+
+@property (nonatomic) CGFloat needleWidth;
+@property (nonatomic) CGFloat needleHeight;
+@property (nonatomic) CGFloat needleScrewRadius;
+
+@property (nonatomic) UIColor* leftNeedleColor;
+@property (nonatomic) UIColor* rightNeedleColor;
+
+@property (nonatomic) UIColor* screwFillColor;
+@property (nonatomic) UIColor* screwStrokeColor;
+
+@property (nonatomic) UIColor* borderColor;
+@property (nonatomic) CGFloat borderWidth;
+
+@property (nonatomic) NSArray<UIColor*>* faceGradientColors;
+@property (nonatomic) NSArray<NSNumber*>* faceGradientPositions;
+@property (nonatomic) NSArray<UIColor*>* faceShadowColors;
+@property (nonatomic) NSArray<NSNumber*>* faceShadowPositions;
+
+@end
+

+ 131 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyle3D.m

@@ -0,0 +1,131 @@
+//
+//  WMGaugeViewStyleFlat3D.m
+//  WMGaugeView
+//
+//  Created by Markezana, William on 25/10/15.
+//  Copyright © 2015 Will™. All rights reserved.
+//
+
+#import "WMGaugeViewStyle3D.h"
+
+@implementation WMGaugeViewStyle3D
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _needleWidth = 0.035;
+        _needleHeight = 0.34;
+        _needleScrewRadius = 0.04;
+        
+        _leftNeedleColor = RGB(176, 10, 19);
+        _rightNeedleColor = RGB(252, 18, 30);
+        
+        _screwFillColor = RGB(171, 171, 171);
+        _screwStrokeColor = RGBA(81, 84, 89, 100);
+        
+        _borderColor = RGBA(81, 84, 89, 160);
+        _borderWidth = 0.005;
+        
+        _faceGradientColors = @[RGB(96, 96, 96), RGB(68, 68, 68), RGB(32, 32, 32)];
+        _faceGradientPositions = @[@0.35, @0.96, @0.99];
+        _faceShadowColors = @[RGBA(40, 96, 170, 60), RGBA(15, 34, 98, 80), RGBA(0, 0, 0, 120), RGBA(0, 0, 0, 140)];
+        _faceShadowPositions = @[@0.60, @0.85, @0.96, @0.99];
+    }
+    return self;
+}
+
+- (void)drawNeedleOnLayer:(CALayer*)layer inRect:(CGRect)rect
+{
+    // Left Needle
+    CAShapeLayer *leftNeedleLayer = [CAShapeLayer layer];
+    UIBezierPath *leftNeedlePath = [UIBezierPath bezierPath];
+    [leftNeedlePath moveToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY))];
+    [leftNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX - _needleWidth, kCenterY))];
+    [leftNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY - _needleHeight))];
+    [leftNeedlePath closePath];
+    
+    leftNeedleLayer.path = leftNeedlePath.CGPath;
+    leftNeedleLayer.backgroundColor = [[UIColor clearColor] CGColor];
+    leftNeedleLayer.fillColor = _leftNeedleColor.CGColor;
+    
+    [layer addSublayer:leftNeedleLayer];
+    
+    // Right Needle
+    CAShapeLayer *rightNeedleLayer = [CAShapeLayer layer];
+    UIBezierPath *rightNeedlePath = [UIBezierPath bezierPath];
+    [rightNeedlePath moveToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY))];
+    [rightNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX + _needleWidth, kCenterY))];
+    [rightNeedlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY - _needleHeight))];
+    [rightNeedlePath closePath];
+    
+    rightNeedleLayer.path = rightNeedlePath.CGPath;
+    rightNeedleLayer.backgroundColor = [[UIColor clearColor] CGColor];
+    rightNeedleLayer.fillColor = _rightNeedleColor.CGColor;
+    
+    [layer addSublayer:rightNeedleLayer];
+    
+    // Needle shadow
+    [layer setShadowColor:[[UIColor blackColor] CGColor]];
+    [layer setShadowOffset:CGSizeMake(0, 0)];
+    [layer setShadowOpacity:0.5];
+    [layer setShadowRadius:2.0];
+    
+    // Screw drawing
+    CAShapeLayer *screwLayer = [CAShapeLayer layer];
+    screwLayer.bounds = CGRectMake(FULLSCALE(kCenterX - _needleScrewRadius, kCenterY - _needleScrewRadius), FULLSCALE(_needleScrewRadius * 2.0, _needleScrewRadius * 2.0));
+    screwLayer.position = CGPointMake(FULLSCALE(kCenterX, kCenterY));
+    screwLayer.path = [UIBezierPath bezierPathWithOvalInRect:screwLayer.bounds].CGPath;
+    screwLayer.fillColor = _screwFillColor.CGColor;
+    screwLayer.strokeColor = _screwStrokeColor.CGColor;
+    screwLayer.lineWidth = 1.5;
+    
+    // Screw shadow
+    screwLayer.shadowColor = [[UIColor blackColor] CGColor];
+    screwLayer.shadowOffset = CGSizeMake(0.0, 0.0);
+    screwLayer.shadowOpacity = 0.1;
+    screwLayer.shadowRadius = 2.0;
+    
+    [layer addSublayer:screwLayer];
+}
+
+- (void)drawFaceWithContext:(CGContextRef)context inRect:(CGRect)rect
+{
+    // Default Face
+    NSAssert(_faceGradientPositions.count == _faceGradientColors.count, @"Must have same amout of colors as positions");
+    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
+    CGFloat* positions = CGFloatCArray(_faceGradientPositions);
+    CGGradientRef gradient = CGGradientCreateWithColors(baseSpace, (CFArrayRef)CGColorArray(_faceGradientColors), positions);
+    CGColorSpaceRelease(baseSpace);
+    baseSpace = NULL;
+    CGContextAddEllipseInRect(context, rect);
+    CGContextClip(context);
+    CGContextDrawRadialGradient(context, gradient, kCenterPoint, 0, kCenterPoint, rect.size.width / 2.0, kCGGradientDrawsAfterEndLocation);
+    CGGradientRelease(gradient);
+    gradient = NULL;
+    free(positions); positions = NULL;
+    
+    // Shadow
+    NSAssert(_faceShadowPositions.count == _faceShadowColors.count, @"Must have same amount of colors as positions");
+    baseSpace = CGColorSpaceCreateDeviceRGB();
+    positions = CGFloatCArray(_faceShadowPositions);
+    gradient = CGGradientCreateWithColors(baseSpace, (CFArrayRef)CGColorArray(_faceShadowColors), positions);
+    CGColorSpaceRelease(baseSpace); baseSpace = NULL;
+    CGContextAddEllipseInRect(context, rect);
+    CGContextClip(context);
+    CGContextDrawRadialGradient(context, gradient, kCenterPoint, 0, kCenterPoint, rect.size.width / 2.0, kCGGradientDrawsAfterEndLocation);
+    CGGradientRelease(gradient); gradient = NULL;
+    free(positions); positions = NULL;
+    
+    // Border
+    CGContextSetLineWidth(context, _borderWidth);
+    CGContextSetStrokeColorWithColor(context, _borderColor.CGColor);
+    CGContextAddEllipseInRect(context, rect);
+    CGContextStrokePath(context);
+}
+
+- (BOOL)needleLayer:(CALayer*)layer willMoveAnimated:(BOOL)animated duration:(NSTimeInterval)duration animation:(CAKeyframeAnimation*)animation
+{
+    return NO;
+}
+
+@end

+ 29 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.h

@@ -0,0 +1,29 @@
+/*
+ * WMGaugeViewStyleFlatThin.h
+ *
+ * Copyright (C) 2015 William Markezana <william.markezana@me.com>
+ *
+ */
+
+#import <Foundation/Foundation.h>
+#import "WMGaugeViewStyle.h"
+
+@interface WMGaugeViewStyleFlatThin : NSObject<WMGaugeViewStyle>
+
+@property (nonatomic) CGFloat needleWidth;
+@property (nonatomic) CGFloat needleHeight;
+@property (nonatomic) CGFloat needleScrewRadius;
+
+@property (nonatomic) UIColor* needleColor;
+@property (nonatomic) UIColor* needleScrewColor;
+
+@property (nonatomic) CGFloat externalRingRadius;
+@property (nonatomic) CGFloat internalRingRadius;
+
+@property (nonatomic) UIColor* externalFaceColor;
+@property (nonatomic) UIColor* internalFaceColor;
+
+@property (nonatomic) UIColor* borderColor;
+@property (nonatomic) CGFloat borderWidth;
+
+@end

+ 128 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/WMGaugeView/WMGaugeViewStyleFlatThin.m

@@ -0,0 +1,128 @@
+//
+//  WMGaugeViewStyleFlatThin.m
+//  WMGaugeView
+//
+//  Created by Markezana, William on 25/10/15.
+//  Copyright © 2015 Will™. All rights reserved.
+//
+
+#import "WMGaugeViewStyleFlatThin.h"
+
+@interface WMGaugeViewStyleFlatThin ()
+
+@property (nonatomic) CAShapeLayer *needleLayer;
+
+@end
+
+@implementation WMGaugeViewStyleFlatThin
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _needleWidth = 0.012;
+        _needleHeight = 0.4;
+        _needleScrewRadius = 0.05;
+//        _needleColor = RGB(255, 104, 97);//橘红色
+        _needleColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
+//        _needleScrewColor = RGB(68, 84, 105);//蓝紫灰色
+        _needleScrewColor = [UIColor systemTealColor];
+        
+        _externalRingRadius = 0.24;
+//        _externalFaceColor = RGB(255, 104, 97);
+        _externalFaceColor = [[UIColor redColor] colorWithAlphaComponent:0.2];
+
+        _internalRingRadius = 0.1;
+//        _internalFaceColor = RGB(242, 99, 92);//亮橘红色
+        _internalFaceColor = [[UIColor yellowColor] colorWithAlphaComponent:0.2];
+        
+//        _borderColor = RGBA(81, 84, 89, 160);//火山灰色
+        _borderColor = [UIColor blackColor];
+        _borderWidth = 0;
+    }
+    return self;
+}
+
+- (void)drawNeedleOnLayer:(CALayer*)layer inRect:(CGRect)rect
+{
+    _needleLayer = [CAShapeLayer layer];
+    UIBezierPath *needlePath = [UIBezierPath bezierPath];
+    [needlePath moveToPoint:CGPointMake(FULLSCALE(kCenterX - _needleWidth, kCenterY))];
+    [needlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX + _needleWidth, kCenterY))];
+    [needlePath addLineToPoint:CGPointMake(FULLSCALE(kCenterX, kCenterY - _needleHeight))];
+    [needlePath closePath];
+    
+    _needleLayer.path = needlePath.CGPath;
+    _needleLayer.backgroundColor = [[UIColor clearColor] CGColor];
+    _needleLayer.fillColor = _needleColor.CGColor;
+    _needleLayer.strokeColor = _needleColor.CGColor;
+    _needleLayer.lineWidth = 1.2;
+    
+    // Needle shadow
+    _needleLayer.shadowColor = [[UIColor blackColor] CGColor];
+    _needleLayer.shadowOffset = CGSizeMake(-2.0, -2.0);
+    _needleLayer.shadowOpacity = 0.2;
+    _needleLayer.shadowRadius = 1.2;
+    
+    [layer addSublayer:_needleLayer];
+    
+    // Screw drawing
+    CAShapeLayer *screwLayer = [CAShapeLayer layer];
+    screwLayer.bounds = CGRectMake(FULLSCALE(kCenterX - _needleScrewRadius, kCenterY - _needleScrewRadius), FULLSCALE(_needleScrewRadius * 2.0, _needleScrewRadius * 2.0));
+    screwLayer.position = CGPointMake(FULLSCALE(kCenterX, kCenterY));
+    screwLayer.path = [UIBezierPath bezierPathWithOvalInRect:screwLayer.bounds].CGPath;
+    screwLayer.fillColor = _needleScrewColor.CGColor;
+    
+    // Screw shadow
+    screwLayer.shadowColor = [[UIColor blackColor] CGColor];
+    screwLayer.shadowOffset = CGSizeMake(0.0, 0.0);
+    screwLayer.shadowOpacity = 0.2;
+    screwLayer.shadowRadius = 2.0;
+    
+    [layer addSublayer:screwLayer];
+}
+
+- (void)drawFaceWithContext:(CGContextRef)context inRect:(CGRect)rect
+{
+    // External circle
+    CGRect externalRect = CGRectMake(kCenterX - _externalRingRadius, kCenterY - _externalRingRadius, _externalRingRadius * 2.0, _externalRingRadius * 2.0);
+    CGContextSetFillColorWithColor(context, _externalFaceColor.CGColor);
+    CGContextFillEllipseInRect(context, externalRect);
+
+    // Inner circle
+    CGRect internalRect = CGRectMake(kCenterX - _internalRingRadius, kCenterY - _internalRingRadius, _internalRingRadius * 2.0, _internalRingRadius * 2.0);
+    CGContextSetFillColorWithColor(context, _internalFaceColor.CGColor);
+    CGContextFillEllipseInRect(context, internalRect);
+    
+    // Border
+    CGRect borderRect = CGRectInset(rect, _borderWidth * 0.5, _borderWidth * 0.5);
+    CGContextSetLineWidth(context, _borderWidth);
+    CGContextSetStrokeColorWithColor(context, _borderColor.CGColor);
+    CGContextStrokeEllipseInRect(context, borderRect);
+}
+
+- (BOOL)needleLayer:(CALayer*)layer willMoveAnimated:(BOOL)animated duration:(NSTimeInterval)duration animation:(CAKeyframeAnimation*)animation
+{
+    layer.transform = [[animation.values objectAtIndex:0] CATransform3DValue];
+    CGAffineTransform affineTransform1 = [layer affineTransform];
+    layer.transform = [[animation.values objectAtIndex:1] CATransform3DValue];
+    CGAffineTransform affineTransform2 = [layer affineTransform];
+    layer.transform = [[animation.values lastObject] CATransform3DValue];
+    CGAffineTransform affineTransform3 = [layer affineTransform];
+    
+    _needleLayer.shadowOffset = CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform3);
+    
+    [layer addAnimation:animation forKey:kCATransition];
+    
+    CAKeyframeAnimation * animationShadowOffset = [CAKeyframeAnimation animationWithKeyPath:@"shadowOffset"];
+    animationShadowOffset.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+    animationShadowOffset.removedOnCompletion = YES;
+    animationShadowOffset.duration = animated ? duration : 0.0;
+    animationShadowOffset.values = @[[NSValue valueWithCGSize:CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform1)],
+                                     [NSValue valueWithCGSize:CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform2)],
+                                     [NSValue valueWithCGSize:CGSizeApplyAffineTransform(CGSizeMake(-2.0, -2.0), affineTransform3)]];
+    [_needleLayer addAnimation:animationShadowOffset forKey:kCATransition];
+    
+    return YES;
+}
+
+@end

+ 19 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/DialPlateView.h

@@ -0,0 +1,19 @@
+//
+//  DialPlateView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import <UIKit/UIKit.h>
+#import "WMGaugeView.h"
+NS_ASSUME_NONNULL_BEGIN
+
+/// 表盘View
+@interface DialPlateView : UIView
+
+@property (nonatomic, strong) WMGaugeView *gaugeView;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 65 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/DialPlateView.m

@@ -0,0 +1,65 @@
+//
+//  DialPlateView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import "DialPlateView.h"
+#import "WMGaugeViewStyleFlatThin.h"
+
+@interface DialPlateView ()
+
+
+@end
+
+@implementation DialPlateView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (self) {
+        [self configUI];
+    }
+    return self;
+}
+
+- (void)configUI {
+    self.backgroundColor = [UIColor clearColor];
+    [self addSubview:self.gaugeView];
+    [self.gaugeView mas_makeConstraints:^(MASConstraintMaker *make) {
+        make.left.top.bottom.right.mas_equalTo(self);
+    }];
+    
+}
+
+- (WMGaugeView *)gaugeView {
+    if (!_gaugeView) {
+        _gaugeView = [[WMGaugeView alloc] init];
+        _gaugeView.style = [[WMGaugeViewStyleFlatThin alloc] init];
+        _gaugeView.maxValue = 50.0f;
+        _gaugeView.minValue = -50.0f;
+        _gaugeView.scaleDivisions = 10;
+        _gaugeView.scaleEndAngle = 270;
+        _gaugeView.scaleStartAngle = 90;
+        _gaugeView.scaleSubdivisions = 5;
+        _gaugeView.showScaleShadow = NO;
+        _gaugeView.scaleDivisionsLength = 0.05;
+        _gaugeView.scaleDivisionsWidth = 0.007;
+        _gaugeView.scaleSubdivisionsLength = 0.02;
+        _gaugeView.scaleSubdivisionsWidth = 0.002;
+        _gaugeView.backgroundColor = [UIColor clearColor];
+        _gaugeView.scaleFont = [UIFont systemFontOfSize:0.05 weight:UIFontWeightUltraLight];
+    }
+    return _gaugeView;
+}
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 40 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/ToneTuningBodyView.h

@@ -0,0 +1,40 @@
+//
+//  ToneTuningBodyView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface ToneTuningBodyView : UIView
+
+@property (weak, nonatomic) IBOutlet UIView *plateView;
+
+@property (weak, nonatomic) IBOutlet UIView *pitchBgView;
+
+@property (weak, nonatomic) IBOutlet UILabel *nomalPitch;
+
+@property (weak, nonatomic) IBOutlet UIView *transferView;
+
+@property (weak, nonatomic) IBOutlet UILabel *transferNomalPitch;
+
+@property (weak, nonatomic) IBOutlet UILabel *transferPitch;
+
+@property (weak, nonatomic) IBOutlet UILabel *transferDesc;
+
+@property (weak, nonatomic) IBOutlet UILabel *A4FrequenceLabel;
+
+@property (weak, nonatomic) IBOutlet UILabel *pitchFrequenceLabel;
+
+@property (nonatomic, assign) NSInteger A4Frequence;
+
+
++ (instancetype)shareInstance;
+
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 72 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/ToneTuningBodyView.m

@@ -0,0 +1,72 @@
+//
+//  ToneTuningBodyView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/14.
+//
+
+#import "ToneTuningBodyView.h"
+
+@interface ToneTuningBodyView ()
+
+@property (weak, nonatomic) IBOutlet UIView *minusView;
+
+@property (weak, nonatomic) IBOutlet UIButton *minusButton;
+
+@property (weak, nonatomic) IBOutlet UIView *addView;
+
+@property (weak, nonatomic) IBOutlet UIButton *addButton;
+
+
+@end
+
+@implementation ToneTuningBodyView
+
+- (void)awakeFromNib {
+    [super awakeFromNib];
+    
+    CAGradientLayer *layer = [self createGradientLayerFromColor:HexRGB(0x323D50) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x1C2331) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, KPortraitWidth - 28, 170)];
+    layer.cornerRadius = 11.0f;
+    layer.masksToBounds = YES;
+    [self.pitchBgView.layer addSublayer:layer];
+    
+    CAGradientLayer *minusLayer = [self createGradientLayerFromColor:HexRGB(0x3C495F) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x1C2331) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, 50, 31)];
+    minusLayer.cornerRadius = 15.5f;
+    minusLayer.masksToBounds = YES;
+    [self.minusView.layer addSublayer:minusLayer];
+    
+    CAGradientLayer *addViewLayer = [self createGradientLayerFromColor:HexRGB(0x3C495F) startPoint:CGPointMake(0.5, 0) endColor:HexRGB(0x1C2331) endPoint:CGPointMake(0.5, 1) bounds:CGRectMake(0, 0, 50, 31)];
+    addViewLayer.cornerRadius = 15.5f;
+    addViewLayer.masksToBounds = YES;
+    [self.addView.layer addSublayer:addViewLayer];
+    self.transferView.hidden = YES;
+    
+}
+
++ (instancetype)shareInstance {
+    ToneTuningBodyView *view = [[[NSBundle mainBundle] loadNibNamed:@"ToneTuningBodyView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+
+
+- (CAGradientLayer *)createGradientLayerFromColor:(UIColor *)fromColor startPoint:(CGPoint)startPoint endColor:(UIColor *)endColor endPoint:(CGPoint)endPoint bounds:(CGRect)bounds {
+    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
+    gradientLayer.colors = @[(__bridge id)fromColor.CGColor, (__bridge id)endColor.CGColor];
+    gradientLayer.startPoint = startPoint;
+    gradientLayer.endPoint = endPoint;
+    gradientLayer.frame = bounds;
+    gradientLayer.locations = @[@(0),@(1.0f)];
+    return gradientLayer;
+}
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 377 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/ToneTuningBodyView.xib

@@ -0,0 +1,377 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <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="ToneTuningBodyView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A4=440Hz" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ux8-2g-RqK">
+                    <rect key="frame" x="22" y="10" width="80" height="22"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="22" id="0p2-aR-QPJ"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_warning" translatesAutoresizingMaskIntoConstraints="NO" id="Sbe-L2-liH">
+                    <rect key="frame" x="107" y="15" width="13" height="12"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="13" id="A7f-bT-aBO"/>
+                        <constraint firstAttribute="height" constant="12" id="gve-gP-2q7"/>
+                    </constraints>
+                </imageView>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="plate_bg" translatesAutoresizingMaskIntoConstraints="NO" id="SXB-cz-Qdh">
+                    <rect key="frame" x="8" y="42" width="374" height="254"/>
+                    <constraints>
+                        <constraint firstAttribute="width" secondItem="SXB-cz-Qdh" secondAttribute="height" multiplier="187:127" id="ixC-MD-R46"/>
+                    </constraints>
+                </imageView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Zrx-Pe-2NT">
+                    <rect key="frame" x="43.666666666666657" y="88" width="303" height="303"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="303" id="93S-gk-24h"/>
+                        <constraint firstAttribute="height" constant="303" id="rD4-eg-11F"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g4h-vE-BMO">
+                    <rect key="frame" x="107" y="322.66666666666669" width="50" height="31"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cai-ev-v5E">
+                    <rect key="frame" x="107" y="322.66666666666669" width="50" height="31"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="31" id="Uqc-Hy-CFM"/>
+                        <constraint firstAttribute="width" constant="50" id="aIN-Pe-lUG"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="frequence_minus"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="color" keyPath="shadowUIColor">
+                            <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.27000000000000002" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="15.5"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </button>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uwR-ra-cDH">
+                    <rect key="frame" x="233" y="322.66666666666669" width="50" height="31"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VJU-LE-Pdq">
+                    <rect key="frame" x="233" y="322.66666666666669" width="50" height="31"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="31" id="30u-8l-H8Y"/>
+                        <constraint firstAttribute="width" constant="50" id="nH5-lU-4GN"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="freguqnce_add"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="15.5"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </button>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CALIB" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sZj-ZO-VHR">
+                    <rect key="frame" x="174" y="328" width="42" height="20"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="20" id="NtT-NM-EEj"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="16"/>
+                    <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Em-FC-3EL">
+                    <rect key="frame" x="14" y="380" width="362" height="170"/>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="170" id="z4J-oh-ydh"/>
+                    </constraints>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="11"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="size" keyPath="shadowOffset">
+                            <size key="value" width="0.0" height="2"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowOpacity">
+                            <real key="value" value="1"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="number" keyPath="shadowRadius">
+                            <real key="value" value="10"/>
+                        </userDefinedRuntimeAttribute>
+                        <userDefinedRuntimeAttribute type="boolean" keyPath="maskToBounces" value="YES"/>
+                        <userDefinedRuntimeAttribute type="color" keyPath="shadowUIColor">
+                            <color key="value" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="calibratedRGB"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ZeX-qM-LJD">
+                    <rect key="frame" x="14" y="380" width="362" height="170"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A#" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uIc-iL-EXg">
+                            <rect key="frame" x="155" y="56" width="52" height="46"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="38"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6FP-hm-dfl">
+                            <rect key="frame" x="11" y="154" width="340" height="5"/>
+                            <subviews>
+                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i3w-7a-0hL">
+                                    <rect key="frame" x="0.0" y="0.0" width="0.0" height="5"/>
+                                    <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" id="Ndf-lc-KO2"/>
+                                    </constraints>
+                                </view>
+                            </subviews>
+                            <color key="backgroundColor" red="0.1764705882352941" green="0.21176470588235294" blue="0.28235294117647058" alpha="1" colorSpace="calibratedRGB"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="5" id="3Xi-hl-yD4"/>
+                                <constraint firstAttribute="bottom" secondItem="i3w-7a-0hL" secondAttribute="bottom" id="54u-rc-9uK"/>
+                                <constraint firstItem="i3w-7a-0hL" firstAttribute="top" secondItem="6FP-hm-dfl" secondAttribute="top" id="KB8-r8-QVf"/>
+                                <constraint firstItem="i3w-7a-0hL" firstAttribute="leading" secondItem="6FP-hm-dfl" secondAttribute="leading" id="qxd-Xa-3KK"/>
+                            </constraints>
+                        </view>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ya5-kp-sTW">
+                            <rect key="frame" x="0.0" y="0.0" width="362" height="143"/>
+                            <subviews>
+                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_transfer" translatesAutoresizingMaskIntoConstraints="NO" id="guW-zj-dkh">
+                                    <rect key="frame" x="171" y="80" width="20" height="23"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="20" id="3Ic-Ab-Zdu"/>
+                                        <constraint firstAttribute="height" constant="23" id="Jad-yu-6YO"/>
+                                    </constraints>
+                                </imageView>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="A#" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MNR-s0-re6">
+                                    <rect key="frame" x="68" y="65" width="52" height="53"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="53" id="rPI-W9-m7n"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="38"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="C#" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vku-Yz-AEN">
+                                    <rect key="frame" x="242" y="65" width="53" height="53"/>
+                                    <constraints>
+                                        <constraint firstAttribute="height" constant="53" id="vCR-NZ-KzZ"/>
+                                    </constraints>
+                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="38"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Concert" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FLn-wI-hyg">
+                                    <rect key="frame" x="71.666666666666671" y="38" width="45" height="17"/>
+                                    <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="14"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="F# ins." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tD3-d1-rfd">
+                                    <rect key="frame" x="249" y="38" width="39" height="17"/>
+                                    <fontDescription key="fontDescription" name="DINAlternate-Bold" family="DIN Alternate" pointSize="14"/>
+                                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <constraints>
+                                <constraint firstItem="MNR-s0-re6" firstAttribute="top" secondItem="FLn-wI-hyg" secondAttribute="bottom" constant="10" id="CiQ-l5-VOE"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="top" secondItem="ya5-kp-sTW" secondAttribute="top" constant="80" id="KZl-F4-IKz"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="leading" secondItem="guW-zj-dkh" secondAttribute="trailing" constant="51" id="Lab-An-ath"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="centerY" secondItem="MNR-s0-re6" secondAttribute="centerY" id="N1i-3X-n6R"/>
+                                <constraint firstItem="MNR-s0-re6" firstAttribute="centerX" secondItem="FLn-wI-hyg" secondAttribute="centerX" id="Ozw-yh-mZ2"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="centerY" secondItem="MNR-s0-re6" secondAttribute="centerY" id="Sxq-0v-HVY"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="centerX" secondItem="ya5-kp-sTW" secondAttribute="centerX" id="U6o-GX-a5D"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="top" secondItem="tD3-d1-rfd" secondAttribute="bottom" constant="10" id="tg6-Gb-dMJ"/>
+                                <constraint firstItem="vku-Yz-AEN" firstAttribute="centerX" secondItem="tD3-d1-rfd" secondAttribute="centerX" id="wTA-Ek-KF6"/>
+                                <constraint firstItem="guW-zj-dkh" firstAttribute="leading" secondItem="MNR-s0-re6" secondAttribute="trailing" constant="51" id="x7v-5G-Dlt"/>
+                            </constraints>
+                        </view>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="uIc-iL-EXg" firstAttribute="centerX" secondItem="ZeX-qM-LJD" secondAttribute="centerX" id="2Bp-Ty-UhN"/>
+                        <constraint firstAttribute="bottom" secondItem="6FP-hm-dfl" secondAttribute="bottom" constant="11" id="31N-v4-lk3"/>
+                        <constraint firstItem="ya5-kp-sTW" firstAttribute="top" secondItem="ZeX-qM-LJD" secondAttribute="top" id="8zF-cX-C0k"/>
+                        <constraint firstAttribute="trailing" secondItem="ya5-kp-sTW" secondAttribute="trailing" id="T8f-BR-fRF"/>
+                        <constraint firstItem="ya5-kp-sTW" firstAttribute="leading" secondItem="ZeX-qM-LJD" secondAttribute="leading" id="beh-Nw-l30"/>
+                        <constraint firstItem="6FP-hm-dfl" firstAttribute="leading" secondItem="ZeX-qM-LJD" secondAttribute="leading" constant="11" id="dcC-0h-BqT"/>
+                        <constraint firstItem="6FP-hm-dfl" firstAttribute="top" secondItem="ya5-kp-sTW" secondAttribute="bottom" constant="11" id="ioo-lM-ERL"/>
+                        <constraint firstItem="uIc-iL-EXg" firstAttribute="top" secondItem="ZeX-qM-LJD" secondAttribute="top" constant="56" id="kgc-uR-hoE"/>
+                        <constraint firstAttribute="trailing" secondItem="6FP-hm-dfl" secondAttribute="trailing" constant="11" id="lZt-Ec-22l"/>
+                    </constraints>
+                </view>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tkl-Cw-Mdz">
+                    <rect key="frame" x="6" y="562" width="189" height="90"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="90" id="t6I-nJ-9Qd"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="fork_unchoose" backgroundImage="forkButton_bg"/>
+                </button>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="p0z-qL-bMm">
+                    <rect key="frame" x="195" y="562" width="189" height="90"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="90" id="D2z-en-N9g"/>
+                    </constraints>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" image="fork_unPlay" backgroundImage="playButton_bg"/>
+                </button>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="toning_bottom" translatesAutoresizingMaskIntoConstraints="NO" id="IXi-hL-in1">
+                    <rect key="frame" x="26" y="659" width="338" height="29"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="338" id="Ohx-Sa-5XQ"/>
+                        <constraint firstAttribute="height" constant="29" id="tsc-a8-bbh"/>
+                    </constraints>
+                </imageView>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5sT-Eb-2rJ">
+                    <rect key="frame" x="56" y="264" width="42" height="21"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <nil key="textColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ke2-CX-K7G">
+                    <rect key="frame" x="293" y="264" width="42" height="21"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <nil key="textColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_decorate" translatesAutoresizingMaskIntoConstraints="NO" id="v4Z-dA-iqI">
+                    <rect key="frame" x="37" y="322" width="48" height="32"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="32" id="7Od-xm-q3j"/>
+                        <constraint firstAttribute="width" constant="48" id="9hP-Ux-x02"/>
+                    </constraints>
+                </imageView>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_decorate" translatesAutoresizingMaskIntoConstraints="NO" id="V2v-I8-jzm">
+                    <rect key="frame" x="305" y="322" width="48" height="32"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="48" id="e8K-AZ-0N3"/>
+                        <constraint firstAttribute="height" constant="32" id="qMW-a2-T7G"/>
+                    </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="PnS-Ku-vsa">
+                    <rect key="frame" x="195" y="76" width="0.0" height="0.0"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="leading" secondItem="Cai-ev-v5E" secondAttribute="trailing" constant="17" id="0D9-VV-0Lx"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="leading" secondItem="ZeX-qM-LJD" secondAttribute="leading" id="1nv-ne-dbI"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="bottom" secondItem="Cai-ev-v5E" secondAttribute="bottom" id="3KO-qD-HZ2"/>
+                <constraint firstItem="p0z-qL-bMm" firstAttribute="top" secondItem="7Em-FC-3EL" secondAttribute="bottom" constant="12" id="4SR-6A-ipm"/>
+                <constraint firstItem="p0z-qL-bMm" firstAttribute="leading" secondItem="Tkl-Cw-Mdz" secondAttribute="trailing" id="5EB-Vw-dj2"/>
+                <constraint firstItem="Cai-ev-v5E" firstAttribute="leading" secondItem="v4Z-dA-iqI" secondAttribute="trailing" constant="22" id="6b8-ki-CY9"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="14" id="7ZV-OX-B1b"/>
+                <constraint firstItem="Cai-ev-v5E" firstAttribute="centerY" secondItem="v4Z-dA-iqI" secondAttribute="centerY" id="8Eb-SN-txF"/>
+                <constraint firstItem="IXi-hL-in1" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="Au1-eF-R25"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="leading" secondItem="sZj-ZO-VHR" secondAttribute="trailing" constant="17" id="C3a-5i-dtf"/>
+                <constraint firstItem="p0z-qL-bMm" firstAttribute="width" secondItem="Tkl-Cw-Mdz" secondAttribute="width" id="Dk9-Wv-4mD"/>
+                <constraint firstItem="PnS-Ku-vsa" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="FLp-jq-kpX"/>
+                <constraint firstItem="SXB-cz-Qdh" firstAttribute="top" secondItem="Ux8-2g-RqK" secondAttribute="bottom" constant="10" id="FYQ-Rq-QtT"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="top" secondItem="ZeX-qM-LJD" secondAttribute="top" id="FlV-cS-OgW"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="trailing" secondItem="Cai-ev-v5E" secondAttribute="trailing" id="N2Y-VV-UPi"/>
+                <constraint firstItem="SXB-cz-Qdh" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="8" id="O3Y-u0-MV3"/>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="PEf-sX-DzU"/>
+                <constraint firstItem="Tkl-Cw-Mdz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="6" id="Q9g-JT-4o6"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="top" secondItem="uwR-ra-cDH" secondAttribute="top" id="RZq-Hd-B0P"/>
+                <constraint firstAttribute="trailing" secondItem="p0z-qL-bMm" secondAttribute="trailing" constant="6" id="RoG-8u-1ap"/>
+                <constraint firstItem="V2v-I8-jzm" firstAttribute="centerY" secondItem="VJU-LE-Pdq" secondAttribute="centerY" id="T1g-e0-zDn"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="trailing" secondItem="uwR-ra-cDH" secondAttribute="trailing" id="Tew-m4-zeK"/>
+                <constraint firstItem="Zrx-Pe-2NT" firstAttribute="top" secondItem="SXB-cz-Qdh" secondAttribute="top" constant="46" id="Xd5-nj-nbj"/>
+                <constraint firstItem="V2v-I8-jzm" firstAttribute="leading" secondItem="VJU-LE-Pdq" secondAttribute="trailing" constant="22" id="YYc-Q5-aGD"/>
+                <constraint firstItem="IXi-hL-in1" firstAttribute="top" secondItem="Tkl-Cw-Mdz" secondAttribute="bottom" constant="7" id="YgN-1n-xha"/>
+                <constraint firstAttribute="trailing" secondItem="7Em-FC-3EL" secondAttribute="trailing" constant="14" id="a3n-fN-euB"/>
+                <constraint firstItem="Zrx-Pe-2NT" firstAttribute="top" secondItem="PnS-Ku-vsa" secondAttribute="bottom" constant="12" id="aSM-PX-vLj"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="leading" secondItem="Cai-ev-v5E" secondAttribute="leading" id="btp-Oe-y1i"/>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="centerY" secondItem="Cai-ev-v5E" secondAttribute="centerY" id="cIh-IQ-xRr"/>
+                <constraint firstItem="Tkl-Cw-Mdz" firstAttribute="top" secondItem="7Em-FC-3EL" secondAttribute="bottom" constant="12" id="cJp-sC-nM0"/>
+                <constraint firstItem="Sbe-L2-liH" firstAttribute="centerY" secondItem="Ux8-2g-RqK" secondAttribute="centerY" id="cmi-yM-djK"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="bottom" secondItem="ZeX-qM-LJD" secondAttribute="bottom" id="coV-Qi-RkQ"/>
+                <constraint firstItem="Ux8-2g-RqK" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="22" id="cwN-DO-wkV"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="bottom" secondItem="uwR-ra-cDH" secondAttribute="bottom" id="e0m-sL-feH"/>
+                <constraint firstItem="Ux8-2g-RqK" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="10" id="ev8-FX-ve4"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="leading" secondItem="uwR-ra-cDH" secondAttribute="leading" id="hAr-OA-5ce"/>
+                <constraint firstItem="Sbe-L2-liH" firstAttribute="leading" secondItem="Ux8-2g-RqK" secondAttribute="trailing" constant="5" id="meC-LK-DKS"/>
+                <constraint firstItem="g4h-vE-BMO" firstAttribute="top" secondItem="Cai-ev-v5E" secondAttribute="top" id="nxA-Si-aiG"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="trailing" secondItem="ZeX-qM-LJD" secondAttribute="trailing" id="oKn-54-J78"/>
+                <constraint firstItem="7Em-FC-3EL" firstAttribute="top" secondItem="sZj-ZO-VHR" secondAttribute="bottom" constant="32" id="pZl-CI-zNY"/>
+                <constraint firstItem="VJU-LE-Pdq" firstAttribute="centerY" secondItem="Cai-ev-v5E" secondAttribute="centerY" id="usx-pc-WFX"/>
+                <constraint firstItem="sZj-ZO-VHR" firstAttribute="top" secondItem="SXB-cz-Qdh" secondAttribute="bottom" constant="32" id="v2V-ca-D8A"/>
+                <constraint firstAttribute="trailing" secondItem="SXB-cz-Qdh" secondAttribute="trailing" constant="8" id="vQ0-wP-c03"/>
+                <constraint firstItem="Zrx-Pe-2NT" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="wef-Xs-dNx"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="A4FrequenceLabel" destination="Ux8-2g-RqK" id="Jra-7M-iTB"/>
+                <outlet property="addButton" destination="VJU-LE-Pdq" id="Ap2-3f-er6"/>
+                <outlet property="addView" destination="uwR-ra-cDH" id="aTz-zS-V4w"/>
+                <outlet property="minusButton" destination="Cai-ev-v5E" id="7T1-RS-zM3"/>
+                <outlet property="minusView" destination="g4h-vE-BMO" id="SOP-VD-G0z"/>
+                <outlet property="nomalPitch" destination="uIc-iL-EXg" id="i9k-OK-L2i"/>
+                <outlet property="pitchBgView" destination="7Em-FC-3EL" id="0rG-SE-app"/>
+                <outlet property="pitchFrequenceLabel" destination="PnS-Ku-vsa" id="LlJ-w5-az7"/>
+                <outlet property="plateView" destination="Zrx-Pe-2NT" id="ppk-X8-oX5"/>
+                <outlet property="transferDesc" destination="tD3-d1-rfd" id="vBI-n9-F5P"/>
+                <outlet property="transferNomalPitch" destination="MNR-s0-re6" id="sVk-tj-gtm"/>
+                <outlet property="transferPitch" destination="vku-Yz-AEN" id="ca0-HH-hao"/>
+                <outlet property="transferView" destination="ya5-kp-sTW" id="VPm-KO-4q0"/>
+            </connections>
+            <point key="canvasLocation" x="93.84615384615384" y="-12.085308056872037"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="forkButton_bg" width="182" height="90"/>
+        <image name="fork_unPlay" width="42" height="28"/>
+        <image name="fork_unchoose" width="42" height="28"/>
+        <image name="freguqnce_add" width="14" height="14"/>
+        <image name="frequence_minus" width="14" height="14"/>
+        <image name="plate_bg" width="359" height="254"/>
+        <image name="playButton_bg" width="182" height="90"/>
+        <image name="toning_bottom" width="338" height="29"/>
+        <image name="tuning_decorate" width="48" height="32"/>
+        <image name="tuning_transfer" width="20" height="23"/>
+        <image name="tuning_warning" width="13" height="12"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 22 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/TuningNavView.h

@@ -0,0 +1,22 @@
+//
+//  TuningNavView.h
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import <UIKit/UIKit.h>
+
+typedef void(^TuningNavAction)(BOOL isBack);
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TuningNavView : UIView
+
++ (instancetype)shareInstance;
+
+- (void)navActionCallback:(TuningNavAction)callbcak;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 52 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/TuningNavView.m

@@ -0,0 +1,52 @@
+//
+//  TuningNavView.m
+//  KulexiuForStudent
+//
+//  Created by 王智 on 2022/10/13.
+//
+
+#import "TuningNavView.h"
+
+@interface TuningNavView ()
+
+@property (nonatomic, copy) TuningNavAction callback;
+
+@end
+
+
+@implementation TuningNavView
+
++ (instancetype)shareInstance {
+    TuningNavView *view = [[[NSBundle mainBundle] loadNibNamed:@"TuningNavView" owner:nil options:nil] firstObject];
+    return view;
+}
+
+- (void)navActionCallback:(TuningNavAction)callbcak {
+    if (callbcak) {
+        self.callback = callbcak;
+    }
+}
+
+- (IBAction)backAction:(id)sender {
+    if (self.callback) {
+        self.callback(YES);
+    }
+}
+
+- (IBAction)settingAction:(id)sender {
+    if (self.callback) {
+        self.callback(NO);
+    }
+}
+
+
+
+/*
+// Only override drawRect: if you perform custom drawing.
+// An empty implementation adversely affects performance during animation.
+- (void)drawRect:(CGRect)rect {
+    // Drawing code
+}
+*/
+
+@end

+ 92 - 0
KulexiuForStudent/KulexiuForStudent/Module/Widget/View/toneTuning/TuningNavView.xib

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <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="TuningNavView">
+            <rect key="frame" x="0.0" y="0.0" width="390" height="65"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XRC-8p-p4e">
+                    <rect key="frame" x="0.0" y="21" width="390" height="44"/>
+                    <subviews>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="back_button_white" translatesAutoresizingMaskIntoConstraints="NO" id="EN2-WR-x5g">
+                            <rect key="frame" x="14" y="12" width="12" height="20"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="pFx-8o-VCU">
+                            <rect key="frame" x="0.0" y="0.0" width="44" height="44"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="44" id="QgR-J5-WSv"/>
+                            </constraints>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="backAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="O0M-F8-PQy"/>
+                            </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="kAY-IQ-kPT">
+                            <rect key="frame" x="167" y="11" width="56" height="22"/>
+                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="设置" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIK-YD-KvW">
+                            <rect key="frame" x="312" y="13.666666666666664" width="29" height="17"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                            <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tuning_setting" translatesAutoresizingMaskIntoConstraints="NO" id="YlL-14-R48">
+                            <rect key="frame" x="349" y="12.666666666666664" width="19" height="19"/>
+                        </imageView>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BFU-Xz-2Xu">
+                            <rect key="frame" x="312" y="0.0" width="78" height="44"/>
+                            <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <connections>
+                                <action selector="settingAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="10h-IE-KXJ"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="BFU-Xz-2Xu" secondAttribute="trailing" id="0QV-8r-cmb"/>
+                        <constraint firstItem="pFx-8o-VCU" firstAttribute="leading" secondItem="XRC-8p-p4e" secondAttribute="leading" id="0la-07-HRD"/>
+                        <constraint firstItem="BFU-Xz-2Xu" firstAttribute="top" secondItem="XRC-8p-p4e" secondAttribute="top" id="1ZM-4W-pkR"/>
+                        <constraint firstAttribute="bottom" secondItem="BFU-Xz-2Xu" secondAttribute="bottom" id="MLk-Cq-pRw"/>
+                        <constraint firstItem="FIK-YD-KvW" firstAttribute="leading" secondItem="BFU-Xz-2Xu" secondAttribute="leading" id="NVa-Nb-oCu"/>
+                        <constraint firstItem="kAY-IQ-kPT" firstAttribute="centerX" secondItem="XRC-8p-p4e" secondAttribute="centerX" id="NyJ-R5-o14"/>
+                        <constraint firstItem="YlL-14-R48" firstAttribute="centerY" secondItem="XRC-8p-p4e" secondAttribute="centerY" id="Zc8-0c-TcZ"/>
+                        <constraint firstItem="EN2-WR-x5g" firstAttribute="centerY" secondItem="XRC-8p-p4e" secondAttribute="centerY" id="Zdd-20-X1y"/>
+                        <constraint firstAttribute="height" constant="44" id="cm1-xm-D5Z"/>
+                        <constraint firstAttribute="bottom" secondItem="pFx-8o-VCU" secondAttribute="bottom" id="dGQ-eq-OMp"/>
+                        <constraint firstItem="YlL-14-R48" firstAttribute="centerY" secondItem="FIK-YD-KvW" secondAttribute="centerY" id="e8f-De-izn"/>
+                        <constraint firstItem="YlL-14-R48" firstAttribute="leading" secondItem="FIK-YD-KvW" secondAttribute="trailing" constant="8" id="euB-Go-WMd"/>
+                        <constraint firstAttribute="trailing" secondItem="YlL-14-R48" secondAttribute="trailing" constant="22" id="jwl-X5-VvP"/>
+                        <constraint firstItem="EN2-WR-x5g" firstAttribute="leading" secondItem="XRC-8p-p4e" secondAttribute="leading" constant="14" id="p0O-O5-qtS"/>
+                        <constraint firstItem="pFx-8o-VCU" firstAttribute="top" secondItem="XRC-8p-p4e" secondAttribute="top" id="u6y-gg-F2T"/>
+                        <constraint firstItem="kAY-IQ-kPT" firstAttribute="centerY" secondItem="XRC-8p-p4e" secondAttribute="centerY" id="wBj-40-9AW"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="XRC-8p-p4e" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="SyP-b7-HEW"/>
+                <constraint firstAttribute="bottom" secondItem="XRC-8p-p4e" secondAttribute="bottom" id="ZWl-Cr-0de"/>
+                <constraint firstAttribute="trailing" secondItem="XRC-8p-p4e" secondAttribute="trailing" id="m8p-XJ-5cc"/>
+            </constraints>
+            <nil key="simulatedTopBarMetrics"/>
+            <nil key="simulatedBottomBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="96.92307692307692" y="-104.14691943127961"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="back_button_white" width="12" height="20"/>
+        <image name="tuning_setting" width="19" height="19"/>
+    </resources>
+</document>

+ 1 - 1
KulexiuForStudent/Podfile

@@ -49,7 +49,7 @@ target 'KulexiuForStudent' do
   pod 'AlipaySDK-iOS'
   #lottie 动画库
   pod 'lottie-ios', '~> 2.5'
-  
+  pod 'AudioKit', '~>5.0.0'
   # Pods for KulexiuForStudent
 
   target 'KulexiuForStudentTests' do

+ 1 - 1
KulexiuForStudent/Podfile.lock

@@ -189,6 +189,6 @@ SPEC CHECKSUMS:
   YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
   ZKCycleScrollView: 4b353d17b7f469b245a1c606d5a977e72b940895
 
-PODFILE CHECKSUM: 91b204a946d66914675de95cadc9a38a9c35beb1
+PODFILE CHECKSUM: 392e3069218a89ef3a9ef998902b3b9f8e9df601
 
 COCOAPODS: 1.11.3

+ 1 - 1
KulexiuForStudent/Pods/Manifest.lock

@@ -189,6 +189,6 @@ SPEC CHECKSUMS:
   YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
   ZKCycleScrollView: 4b353d17b7f469b245a1c606d5a977e72b940895
 
-PODFILE CHECKSUM: 91b204a946d66914675de95cadc9a38a9c35beb1
+PODFILE CHECKSUM: 392e3069218a89ef3a9ef998902b3b9f8e9df601
 
 COCOAPODS: 1.11.3

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio