Răsfoiți Sursa

im消息语音功能

fancy 5 ani în urmă
părinte
comite
e68a64f700
31 a modificat fișierele cu 2571 adăugiri și 34 ștergeri
  1. 130 0
      o2ios/O2Platform.xcodeproj/project.pbxproj
  2. 96 5
      o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift
  3. 1 0
      o2ios/O2Platform/App/IM-聊天/Model/IMConversationInfo.swift
  4. 23 0
      o2ios/O2Platform/App/IM-聊天/O2IM.swift
  5. 31 0
      o2ios/O2Platform/App/IM-聊天/View/IMAudioView.swift
  6. 57 0
      o2ios/O2Platform/App/IM-聊天/View/IMAudioView.xib
  7. 96 0
      o2ios/O2Platform/App/IM-聊天/View/IMChatAudioView.swift
  8. 57 0
      o2ios/O2Platform/App/IM-聊天/View/IMChatAudioView.xib
  9. 64 11
      o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.swift
  10. 64 10
      o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.swift
  11. 3 6
      o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.swift
  12. 22 0
      o2ios/O2Platform/Assets.xcassets/im/chat_audio_play_Normal.imageset/Contents.json
  13. BIN
      o2ios/O2Platform/Assets.xcassets/im/chat_audio_play_Normal.imageset/chat_icon_audio_play_Normal@2x.png
  14. BIN
      o2ios/O2Platform/Assets.xcassets/im/chat_audio_play_Normal.imageset/chat_icon_audio_play_Normal@3x.png
  15. 2 2
      o2ios/O2Platform/Framework/Utils/FileUtil.swift
  16. 17 0
      o2ios/O2Platform/Framework/lame/AmrCodec.h
  17. 15 0
      o2ios/O2Platform/Framework/lame/ConvertMp3.h
  18. 66 0
      o2ios/O2Platform/Framework/lame/ConvertMp3.m
  19. 1323 0
      o2ios/O2Platform/Framework/lame/lame.h
  20. BIN
      o2ios/O2Platform/Framework/lame/libmp3lame.a
  21. BIN
      o2ios/O2Platform/Framework/lame/opencore-amr/lib/libopencore-amrnb.a
  22. BIN
      o2ios/O2Platform/Framework/lame/opencore-amr/lib/libopencore-amrwb.a
  23. 34 0
      o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrnb/interf_dec.h
  24. 50 0
      o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrnb/interf_enc.h
  25. 36 0
      o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrwb/dec_if.h
  26. 33 0
      o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrwb/if_rom.h
  27. BIN
      o2ios/O2Platform/Framework/lame/vo-amrwbenc/lib/libvo-amrwbenc.a
  28. 34 0
      o2ios/O2Platform/Framework/lame/vo-amrwbenc/vo-amrwbenc/enc_if.h
  29. 78 0
      o2ios/O2Platform/Manager/AudioPlayerManager.swift
  30. 235 0
      o2ios/O2Platform/Manager/O2RecordVoiceManager.swift
  31. 4 0
      o2ios/O2Platform/O2Platform-Bridging-Header.h

+ 130 - 0
o2ios/O2Platform.xcodeproj/project.pbxproj

@@ -142,6 +142,17 @@
 		B14E0C0C2484F1F0008AF6AE /* O2WebsocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14E0C0B2484F1F0008AF6AE /* O2WebsocketManager.swift */; };
 		B1534E3F21F712EA00CC8C35 /* O2DemoAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1534E3E21F712EA00CC8C35 /* O2DemoAlertView.swift */; };
 		B158E95E215DD3F500AB2727 /* AIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = B158E95D215DD3F500AB2727 /* AIConstants.swift */; };
+		B15BE0BE2499BCEF008CD1DB /* O2RecordVoiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15BE0BD2499BCEF008CD1DB /* O2RecordVoiceManager.swift */; };
+		B15BE0EE2499DD7E008CD1DB /* IMChatAudioView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15BE0ED2499DD7E008CD1DB /* IMChatAudioView.swift */; };
+		B15BE0F02499DD90008CD1DB /* IMChatAudioView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B15BE0EF2499DD90008CD1DB /* IMChatAudioView.xib */; };
+		B15BE106249A06BF008CD1DB /* libvo-amrwbenc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B15BE0F7249A06BE008CD1DB /* libvo-amrwbenc.a */; };
+		B15BE107249A06BF008CD1DB /* ConvertMp3.m in Sources */ = {isa = PBXBuildFile; fileRef = B15BE0F8249A06BE008CD1DB /* ConvertMp3.m */; };
+		B15BE108249A06BF008CD1DB /* libmp3lame.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B15BE0F9249A06BE008CD1DB /* libmp3lame.a */; };
+		B15BE109249A06BF008CD1DB /* libopencore-amrwb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B15BE100249A06BE008CD1DB /* libopencore-amrwb.a */; };
+		B15BE10A249A06BF008CD1DB /* libopencore-amrnb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B15BE101249A06BE008CD1DB /* libopencore-amrnb.a */; };
+		B15BE10C249A0EFE008CD1DB /* IMAudioView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B15BE10B249A0EFE008CD1DB /* IMAudioView.xib */; };
+		B15BE10E249A107C008CD1DB /* IMAudioView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15BE10D249A107C008CD1DB /* IMAudioView.swift */; };
+		B15BE111249A6002008CD1DB /* AudioPlayerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15BE110249A6002008CD1DB /* AudioPlayerManager.swift */; };
 		B15D26EC235EF7480092F8B8 /* genstrings.sh in Resources */ = {isa = PBXBuildFile; fileRef = B15D26EB235EF7480092F8B8 /* genstrings.sh */; };
 		B15F80F3210EB93000B81F35 /* OOCalendarMainMonthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F80F2210EB93000B81F35 /* OOCalendarMainMonthViewController.swift */; };
 		B15F8129210EF15E00B81F35 /* OOCalendarEventViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15F8128210EF15E00B81F35 /* OOCalendarEventViewController.swift */; };
@@ -1448,6 +1459,25 @@
 		B14E0C0B2484F1F0008AF6AE /* O2WebsocketManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2WebsocketManager.swift; sourceTree = "<group>"; };
 		B1534E3E21F712EA00CC8C35 /* O2DemoAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2DemoAlertView.swift; sourceTree = "<group>"; };
 		B158E95D215DD3F500AB2727 /* AIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIConstants.swift; sourceTree = "<group>"; };
+		B15BE0BD2499BCEF008CD1DB /* O2RecordVoiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2RecordVoiceManager.swift; sourceTree = "<group>"; };
+		B15BE0ED2499DD7E008CD1DB /* IMChatAudioView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMChatAudioView.swift; sourceTree = "<group>"; };
+		B15BE0EF2499DD90008CD1DB /* IMChatAudioView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMChatAudioView.xib; sourceTree = "<group>"; };
+		B15BE0F2249A06BE008CD1DB /* AmrCodec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AmrCodec.h; sourceTree = "<group>"; };
+		B15BE0F5249A06BE008CD1DB /* enc_if.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = enc_if.h; sourceTree = "<group>"; };
+		B15BE0F7249A06BE008CD1DB /* libvo-amrwbenc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libvo-amrwbenc.a"; sourceTree = "<group>"; };
+		B15BE0F8249A06BE008CD1DB /* ConvertMp3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConvertMp3.m; sourceTree = "<group>"; };
+		B15BE0F9249A06BE008CD1DB /* libmp3lame.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libmp3lame.a; sourceTree = "<group>"; };
+		B15BE0FA249A06BE008CD1DB /* ConvertMp3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConvertMp3.h; sourceTree = "<group>"; };
+		B15BE0FD249A06BE008CD1DB /* if_rom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = if_rom.h; sourceTree = "<group>"; };
+		B15BE0FE249A06BE008CD1DB /* dec_if.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dec_if.h; sourceTree = "<group>"; };
+		B15BE100249A06BE008CD1DB /* libopencore-amrwb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopencore-amrwb.a"; sourceTree = "<group>"; };
+		B15BE101249A06BE008CD1DB /* libopencore-amrnb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopencore-amrnb.a"; sourceTree = "<group>"; };
+		B15BE103249A06BE008CD1DB /* interf_dec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interf_dec.h; sourceTree = "<group>"; };
+		B15BE104249A06BE008CD1DB /* interf_enc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interf_enc.h; sourceTree = "<group>"; };
+		B15BE105249A06BE008CD1DB /* lame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lame.h; sourceTree = "<group>"; };
+		B15BE10B249A0EFE008CD1DB /* IMAudioView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IMAudioView.xib; sourceTree = "<group>"; };
+		B15BE10D249A107C008CD1DB /* IMAudioView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMAudioView.swift; sourceTree = "<group>"; };
+		B15BE110249A6002008CD1DB /* AudioPlayerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerManager.swift; sourceTree = "<group>"; };
 		B15D26BF235EF0850092F8B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/CloudFile.storyboard; sourceTree = "<group>"; };
 		B15D26EA235EF0940092F8B8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/CloudFile.strings; sourceTree = "<group>"; };
 		B15D26EB235EF7480092F8B8 /* genstrings.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = genstrings.sh; sourceTree = "<group>"; };
@@ -2372,6 +2402,7 @@
 				B1BC8CDA216B3D5F00AF571F /* libc++.tbd in Frameworks */,
 				B130E648223B774D00B68354 /* image_picker.framework in Frameworks */,
 				E4D23119209EF74D00837868 /* libssl.a in Frameworks */,
+				B15BE106249A06BF008CD1DB /* libvo-amrwbenc.a in Frameworks */,
 				E4C24A222080A44300E426B0 /* CFNetwork.framework in Frameworks */,
 				B1AE4E2621A3DCC100183FCD /* O2OA_Auth_SDK.framework in Frameworks */,
 				B130E64A223B774D00B68354 /* shared_preferences.framework in Frameworks */,
@@ -2382,13 +2413,16 @@
 				E40958C7208051DE000FECC3 /* CoreTelephony.framework in Frameworks */,
 				E4418C5F1DDC1AC80066348D /* CoreGraphics.framework in Frameworks */,
 				E4375B12207CABAB0065A880 /* SystemConfiguration.framework in Frameworks */,
+				B15BE108249A06BF008CD1DB /* libmp3lame.a in Frameworks */,
 				B19BCE8A2228272500BD454C /* App.framework in Frameworks */,
 				B130E649223B774D00B68354 /* path_provider.framework in Frameworks */,
 				E4375B1A207CABE90065A880 /* AudioToolbox.framework in Frameworks */,
+				B15BE10A249A06BF008CD1DB /* libopencore-amrnb.a in Frameworks */,
 				E4375B14207CABB30065A880 /* AddressBook.framework in Frameworks */,
 				E4375B18207CABD20065A880 /* AssetsLibrary.framework in Frameworks */,
 				E4375B16207CABC90065A880 /* CoreMotion.framework in Frameworks */,
 				E4375B10207CABA30065A880 /* CoreLocation.framework in Frameworks */,
+				B15BE109249A06BF008CD1DB /* libopencore-amrwb.a in Frameworks */,
 				E4375B0E207CAB980065A880 /* CoreData.framework in Frameworks */,
 				E4375B0C207CAB860065A880 /* libz.tbd in Frameworks */,
 				E4375B08207CAB770065A880 /* libsqlite3.0.tbd in Frameworks */,
@@ -2597,6 +2631,10 @@
 				B1489CAD2491FF13009EE9FD /* IMChatEmojiBarView.xib */,
 				B1489CAF2492045D009EE9FD /* IMChatEmojiItemCell.swift */,
 				B1489CB02492045D009EE9FD /* IMChatEmojiItemCell.xib */,
+				B15BE0ED2499DD7E008CD1DB /* IMChatAudioView.swift */,
+				B15BE0EF2499DD90008CD1DB /* IMChatAudioView.xib */,
+				B15BE10B249A0EFE008CD1DB /* IMAudioView.xib */,
+				B15BE10D249A107C008CD1DB /* IMAudioView.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -2668,6 +2706,82 @@
 			path = vm;
 			sourceTree = "<group>";
 		};
+		B15BE0F1249A06BE008CD1DB /* lame */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE0F2249A06BE008CD1DB /* AmrCodec.h */,
+				B15BE0F3249A06BE008CD1DB /* vo-amrwbenc */,
+				B15BE0F8249A06BE008CD1DB /* ConvertMp3.m */,
+				B15BE0F9249A06BE008CD1DB /* libmp3lame.a */,
+				B15BE0FA249A06BE008CD1DB /* ConvertMp3.h */,
+				B15BE0FB249A06BE008CD1DB /* opencore-amr */,
+				B15BE105249A06BE008CD1DB /* lame.h */,
+			);
+			path = lame;
+			sourceTree = "<group>";
+		};
+		B15BE0F3249A06BE008CD1DB /* vo-amrwbenc */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE0F4249A06BE008CD1DB /* vo-amrwbenc */,
+				B15BE0F6249A06BE008CD1DB /* lib */,
+			);
+			path = "vo-amrwbenc";
+			sourceTree = "<group>";
+		};
+		B15BE0F4249A06BE008CD1DB /* vo-amrwbenc */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE0F5249A06BE008CD1DB /* enc_if.h */,
+			);
+			path = "vo-amrwbenc";
+			sourceTree = "<group>";
+		};
+		B15BE0F6249A06BE008CD1DB /* lib */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE0F7249A06BE008CD1DB /* libvo-amrwbenc.a */,
+			);
+			path = lib;
+			sourceTree = "<group>";
+		};
+		B15BE0FB249A06BE008CD1DB /* opencore-amr */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE0FC249A06BE008CD1DB /* opencore-amrwb */,
+				B15BE0FF249A06BE008CD1DB /* lib */,
+				B15BE102249A06BE008CD1DB /* opencore-amrnb */,
+			);
+			path = "opencore-amr";
+			sourceTree = "<group>";
+		};
+		B15BE0FC249A06BE008CD1DB /* opencore-amrwb */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE0FD249A06BE008CD1DB /* if_rom.h */,
+				B15BE0FE249A06BE008CD1DB /* dec_if.h */,
+			);
+			path = "opencore-amrwb";
+			sourceTree = "<group>";
+		};
+		B15BE0FF249A06BE008CD1DB /* lib */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE100249A06BE008CD1DB /* libopencore-amrwb.a */,
+				B15BE101249A06BE008CD1DB /* libopencore-amrnb.a */,
+			);
+			path = lib;
+			sourceTree = "<group>";
+		};
+		B15BE102249A06BE008CD1DB /* opencore-amrnb */ = {
+			isa = PBXGroup;
+			children = (
+				B15BE103249A06BE008CD1DB /* interf_dec.h */,
+				B15BE104249A06BE008CD1DB /* interf_enc.h */,
+			);
+			path = "opencore-amrnb";
+			sourceTree = "<group>";
+		};
 		B165CC9D2241DB9F00373B66 /* DatePickerDialogSwift */ = {
 			isa = PBXGroup;
 			children = (
@@ -2918,6 +3032,8 @@
 				B1E0BAA02378FC01001D741F /* O2JPushManager.swift */,
 				B1ABAA0B237E498C0027EC48 /* O2VersionManager.swift */,
 				B14E0C0B2484F1F0008AF6AE /* O2WebsocketManager.swift */,
+				B15BE0BD2499BCEF008CD1DB /* O2RecordVoiceManager.swift */,
+				B15BE110249A6002008CD1DB /* AudioPlayerManager.swift */,
 			);
 			path = Manager;
 			sourceTree = "<group>";
@@ -3898,6 +4014,7 @@
 		E4B887821D9D48F1002E1A46 /* Framework */ = {
 			isa = PBXGroup;
 			children = (
+				B15BE0F1249A06BE008CD1DB /* lame */,
 				E4B69721207602840062F6E8 /* O2API */,
 				E4B8892E1D9D6308002E1A46 /* Utils */,
 				B142B04E230FB53E00E7D127 /* MagicBytesMimeType */,
@@ -5432,6 +5549,7 @@
 				E46E6CA01DD41F5D00AB7561 /* ZSSrightjustify@2x.png in Resources */,
 				E46E6CA91DD41F5D00AB7561 /* ZSStextcolor.png in Resources */,
 				E4C24C1F2085849500E426B0 /* o2Theme.bundle in Resources */,
+				B15BE0F02499DD90008CD1DB /* IMChatAudioView.xib in Resources */,
 				E4B781671DF8E556007B58A9 /* information.storyboard in Resources */,
 				E46E6CAA1DD41F5D00AB7561 /* ZSStextcolor@2x.png in Resources */,
 				E40E24C020B7DA3C009F8BE7 /* OOMeetingRoomTableHeaderView.xib in Resources */,
@@ -5616,6 +5734,7 @@
 				E4C24C49208D7EDE00E426B0 /* OOContactSearchSectionHeaderView.xib in Resources */,
 				E46E6C691DD41F5D00AB7561 /* ZSSbgcolor.png in Resources */,
 				E428AF4620A95FE800D964B9 /* OOAttanceTotalController.xib in Resources */,
+				B15BE10C249A0EFE008CD1DB /* IMAudioView.xib in Resources */,
 				B165CD6B2242093500373B66 /* Info.plist in Resources */,
 				E4C24C48208D7EDE00E426B0 /* OOContactGroupHeaderView.xib in Resources */,
 				E4B888DB1D9D48F1002E1A46 /* 隶变体.ttf in Resources */,
@@ -5987,6 +6106,7 @@
 				E4C24BB420844F3C00E426B0 /* JCMessageContentViewType.swift in Sources */,
 				E4C24B9420844F3C00E426B0 /* JCGroupSettingCell.swift in Sources */,
 				E40E24D320B7DA3C009F8BE7 /* OOMeetingPersonSelectHeaderView.swift in Sources */,
+				B15BE0BE2499BCEF008CD1DB /* O2RecordVoiceManager.swift in Sources */,
 				B1DB6EA5237E6F2C00D7BA94 /* O2VersionInfoModel.swift in Sources */,
 				E4C24B9E20844F3C00E426B0 /* JCUpdateMemberCell.swift in Sources */,
 				09E02E9A1F1745E700579887 /* ZLTextView.swift in Sources */,
@@ -6049,6 +6169,7 @@
 				B1EE2CD02281771600842F48 /* O2JsApiUtil.swift in Sources */,
 				E4B6975020762EA00062F6E8 /* OOPasswordTextField.swift in Sources */,
 				E491664C1E03C09C006133C5 /* O2Account.swift in Sources */,
+				B15BE111249A6002008CD1DB /* AudioPlayerManager.swift in Sources */,
 				E46E6CBA1DD41F5D00AB7561 /* CYRToken.m in Sources */,
 				E4C24C53208D7EDE00E426B0 /* OOContactGroupHeaderView.swift in Sources */,
 				E4C24BD120844F3C00E426B0 /* JCNoteNameViewController.swift in Sources */,
@@ -6196,6 +6317,7 @@
 				09E02E021F12736C00579887 /* PersonV2.swift in Sources */,
 				B1B0102F23586B34002BF874 /* CFFileTableViewCell.swift in Sources */,
 				E40E24AE20B7DA3C009F8BE7 /* OOMeetingAcceptController.swift in Sources */,
+				B15BE107249A06BF008CD1DB /* ConvertMp3.m in Sources */,
 				09E02E041F127CAF00579887 /* IdentityV2.swift in Sources */,
 				E42E41CE1E6ED13C00C5B5C7 /* CMS_PublishInfo.swift in Sources */,
 				E4B888801D9D48F1002E1A46 /* File.swift in Sources */,
@@ -6216,6 +6338,7 @@
 				E4B888C41D9D48F1002E1A46 /* CollectDeviceData.swift in Sources */,
 				E4B888731D9D48F1002E1A46 /* Company.swift in Sources */,
 				E4C24B9B20844F3C00E426B0 /* JCNetworkTipsCell.swift in Sources */,
+				B15BE10E249A107C008CD1DB /* IMAudioView.swift in Sources */,
 				E45DA8E91DACC11600E0735D /* BaseWebViewUIViewController.swift in Sources */,
 				E424BAD61EE105BB0078DB47 /* O2Logger.swift in Sources */,
 				E491664A1E038D09006133C5 /* O2AlamofireSource.swift in Sources */,
@@ -6267,6 +6390,7 @@
 				E40E24D220B7DA3C009F8BE7 /* OOFormDateIntervalItemView.swift in Sources */,
 				E4184B251DB4F28800FCC907 /* SMobileChangeViewController.swift in Sources */,
 				E40E24B320B7DA3C009F8BE7 /* OOMeetingMeetingRoomManageController.swift in Sources */,
+				B15BE0EE2499DD7E008CD1DB /* IMChatAudioView.swift in Sources */,
 				E40E24D820B7DA3C009F8BE7 /* OOMeetingPersonFooterView.swift in Sources */,
 				B14E078A230141AC00AE85A0 /* ContactGroupPickerViewController.swift in Sources */,
 				E4B697C520764A2D0062F6E8 /* O2InformationAPI.swift in Sources */,
@@ -6738,6 +6862,9 @@
 					"$(PROJECT_DIR)/O2Platform/framework/thirdlibs",
 					"$(PROJECT_DIR)/O2Platform/framework/MegviiLicMgr-iOS-SDK",
 					"$(PROJECT_DIR)/O2Platform/framework/O2OA_Flutter_SDK",
+					"$(PROJECT_DIR)/O2Platform/Framework/lame/vo-amrwbenc/lib",
+					"$(PROJECT_DIR)/O2Platform/Framework/lame",
+					"$(PROJECT_DIR)/O2Platform/Framework/lame/opencore-amr/lib",
 				);
 				MARKETING_VERSION = 5.0.7;
 				OTHER_LDFLAGS = (
@@ -6851,6 +6978,9 @@
 					"$(PROJECT_DIR)/O2Platform/framework/thirdlibs",
 					"$(PROJECT_DIR)/O2Platform/framework/MegviiLicMgr-iOS-SDK",
 					"$(PROJECT_DIR)/O2Platform/framework/O2OA_Flutter_SDK",
+					"$(PROJECT_DIR)/O2Platform/Framework/lame/vo-amrwbenc/lib",
+					"$(PROJECT_DIR)/O2Platform/Framework/lame",
+					"$(PROJECT_DIR)/O2Platform/Framework/lame/opencore-amr/lib",
 				);
 				MARKETING_VERSION = 5.0.7;
 				OTHER_LDFLAGS = (

+ 96 - 5
o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift

@@ -28,13 +28,20 @@ class IMChatViewController: UIViewController {
     //底部工具栏
     @IBOutlet weak var bottomBar: UIView!
 
-    private let emojiBarHeight = 256
+    private let emojiBarHeight = 196
     //表情窗口
     private lazy var emojiBar: IMChatEmojiBarView = {
         let view = Bundle.main.loadNibNamed("IMChatEmojiBarView", owner: self, options: nil)?.first as! IMChatEmojiBarView
         view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: emojiBarHeight.toCGFloat)
         return view
     }()
+    //语音录制按钮
+    private lazy var audioBtnView: IMChatAudioView = {
+        let view = Bundle.main.loadNibNamed("IMChatAudioView", owner: self, options: nil)?.first as! IMChatAudioView
+        view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: emojiBarHeight.toCGFloat)
+        view.delegate = self
+        return view
+    }()
     //预览文件
     private lazy var previewVC: CloudFilePreviewController = {
         return CloudFilePreviewController()
@@ -49,6 +56,7 @@ class IMChatViewController: UIViewController {
     private var chatMessageList: [IMMessageInfo] = []
     private var page = 1
     private var isShowEmoji = false
+    private var isShowAudioView = false
     private var bottomBarHeight = 64 //底部输入框 表情按钮 的高度
     private let bottomToolbarHeight = 46 //底部工具栏 麦克风 相册 相机等按钮的位置
 
@@ -256,6 +264,21 @@ class IMChatViewController: UIViewController {
         self.scrollMessageToBottom()
         return msgId
     }
+    
+    //发送消息前 先载入界面
+    private func prepareForSendFileMsg(tempMessage: IMMessageBodyInfo) -> String {
+        let message = IMMessageInfo()
+        let msgId = UUID().uuidString
+        message.body = tempMessage.toJSONString()
+        message.id = msgId
+        message.conversationId = self.conversation?.id
+        message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
+        message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
+        //添加到界面
+        self.chatMessageList.append(message)
+        self.scrollMessageToBottom()
+        return msgId
+    }
 
     //上传图片到服务器并发送消息
     private func uploadImageAndSendMsg(messageId: String, imageData: Data, fileName: String) {
@@ -286,8 +309,37 @@ class IMChatViewController: UIViewController {
         }.catch { err in
             self.showError(title: "上传错误,\(err.localizedDescription)")
         }
-
-
+    }
+    
+    //上传图片 音频 等文件到服务器并发送消息
+    private func uploadFileAndSendMsg(messageId: String, data: Data, fileName: String, type: String) {
+        guard let cId = self.conversation?.id else {
+            return
+        }
+        self.viewModel.uploadFile(conversationId: cId, type: type, fileName: fileName, file: data).then { attachId in
+            DDLogDebug("上传图片成功: \(attachId)")
+            guard let message = self.chatMessageList.first (where: { (info) -> Bool in
+                return info.id == messageId
+            }) else {
+                DDLogDebug("没有找到对应的消息")
+                return
+            }
+            let body = IMMessageBodyInfo.deserialize(from: message.body)
+            body?.fileId = attachId
+            body?.fileTempPath = nil
+            message.body = body?.toJSONString()
+            //发送消息到服务器
+            self.viewModel.sendMsg(msg: message)
+                .then { (result) in
+                    DDLogDebug("图片消息 发送成功 \(result)")
+                    self.viewModel.readConversation(conversationId: self.conversation?.id)
+                }.catch { (error) in
+                    DDLogError(error.localizedDescription)
+                    self.showError(title: "发送消息失败!")
+            }
+        }.catch { err in
+            self.showError(title: "上传错误,\(err.localizedDescription)")
+        }
     }
 
 
@@ -297,6 +349,10 @@ class IMChatViewController: UIViewController {
         self.isShowEmoji.toggle()
         self.view.endEditing(true)
         if self.isShowEmoji {
+            //audio view 先关闭
+            self.isShowAudioView = false
+            self.audioBtnView.removeFromSuperview()
+            //开始添加emojiBar
             self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + self.emojiBarHeight.toCGFloat
             self.emojiBar.delegate = self
             self.emojiBar.translatesAutoresizingMaskIntoConstraints = false
@@ -314,6 +370,25 @@ class IMChatViewController: UIViewController {
 
     @IBAction func micBtnClick(_ sender: UIButton) {
         DDLogDebug("点击了麦克风按钮")
+        self.isShowAudioView.toggle()
+        self.view.endEditing(true)
+        if self.isShowAudioView {
+            //emoji view 先关闭
+            self.isShowEmoji = false
+            self.emojiBar.removeFromSuperview()
+            //开始添加emojiBar
+            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + self.emojiBarHeight.toCGFloat
+            self.audioBtnView.translatesAutoresizingMaskIntoConstraints = false
+            self.bottomBar.addSubview(self.audioBtnView)
+            let top = NSLayoutConstraint(item: self.audioBtnView, attribute: .top, relatedBy: .equal, toItem: self.audioBtnView.superview!, attribute: .top, multiplier: 1, constant: CGFloat(self.bottomBarHeight))
+            let width = NSLayoutConstraint(item: self.audioBtnView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: SCREEN_WIDTH)
+            let height = NSLayoutConstraint(item: self.audioBtnView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.emojiBarHeight.toCGFloat)
+            NSLayoutConstraint.activate([top, width, height])
+        }else {
+            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
+            self.audioBtnView.removeFromSuperview()
+        }
+        self.view.layoutIfNeeded()
     }
 
     @IBAction func imgBtnClick(_ sender: UIButton) {
@@ -331,6 +406,21 @@ class IMChatViewController: UIViewController {
 
 }
 
+// MARK: - 录音delegate
+extension IMChatViewController: IMChatAudioViewDelegate {
+    func sendVoice(path: String, voice: Data, duration: String) {
+        let msg = IMMessageBodyInfo()
+        msg.fileTempPath = path
+        msg.body = o2_im_msg_body_audio
+        msg.type = o2_im_msg_type_audio
+        msg.audioDuration = duration
+        let msgId = self.prepareForSendFileMsg(tempMessage: msg)
+        let fileName = path.split("/").last ?? "MySound.ilbc"
+        DDLogDebug("音频文件:\(fileName)")
+        self.uploadFileAndSendMsg(messageId: msgId, data: voice, fileName: fileName, type: o2_im_msg_type_audio)
+    }
+}
+
 // MARK: - 拍照delegate
 extension IMChatViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
 
@@ -449,12 +539,13 @@ extension IMChatViewController: UITableViewDelegate, UITableViewDataSource {
 extension IMChatViewController: UITextFieldDelegate {
     func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
         DDLogDebug("准备开始输入......")
-        closeEmoji()
+        closeOtherView()
         return true
     }
 
-    private func closeEmoji() {
+    private func closeOtherView() {
         self.isShowEmoji = false
+        self.isShowAudioView = false
         self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
         self.view.layoutIfNeeded()
     }

+ 1 - 0
o2ios/O2Platform/App/IM-聊天/Model/IMConversationInfo.swift

@@ -64,6 +64,7 @@ class IMMessageBodyInfo: NSObject, DataModel {
     @objc var body: String?
     @objc var fileId: String? //文件id
     @objc var fileTempPath: String? //本地临时文件地址
+    @objc var audioDuration: String? // 音频文件时长
     @objc var address: String? //type=location的时候位置信息
     var latitude: Double?//type=location的时候位置信息
     var longitude: Double?//type=location的时候位置信息

+ 23 - 0
o2ios/O2Platform/App/IM-聊天/O2IM.swift

@@ -21,9 +21,11 @@ let o2_im_conversation_type_group = "group"
 let o2_im_msg_type_text = "text"
 let o2_im_msg_type_emoji = "emoji"
 let o2_im_msg_type_image = "image"
+let o2_im_msg_type_audio = "audio"
 
 //消息body
 let o2_im_msg_body_image = "[图片]"
+let o2_im_msg_body_audio = "[语音]"
 let o2_im_msg_body_video = "[视频]"
 
 
@@ -73,5 +75,26 @@ class O2IMFileManager {
     func localFilePath(fileId: String) -> URL {
         return FileUtil.share.cacheDir().appendingPathComponent("\(fileId).png")
     }
+    
+    //音频文件存储地址
+    func getRecorderPath(type: RecordType) -> String {
+        var recorderPath = FileUtil.share.cacheDir()
+        recorderPath.appendPathComponent("o2im")
+        //目录不存在就创建
+        DDLogDebug("开始创建目录\(recorderPath.path)")
+        FileUtil.share.createDirectory(path: recorderPath.path)
+        let now:Date = Date()
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = "yyyy-MM-dd-hh-mm-ss"
+        let fileName = (type == RecordType.Caf) ? "\(dateFormatter.string(from: now))-MySound.caf" : "\(dateFormatter.string(from: now))-MySound.mp3"
+        recorderPath.appendPathComponent(fileName)
+        return recorderPath.path
+    }
+    
+}
+
 
+enum RecordType :String {
+    case Caf = "caf"
+    case MP3 = "mp3"
 }

+ 31 - 0
o2ios/O2Platform/App/IM-聊天/View/IMAudioView.swift

@@ -0,0 +1,31 @@
+//
+//  IMAudioView.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/17.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+
+class IMAudioView: UIView {
+    
+    static let IMAudioView_width: CGFloat = 76
+    static let IMAudioView_height: CGFloat = 36
+    
+    @IBOutlet weak var playImageView: UIImageView!
+    @IBOutlet weak var durationLabel: UILabel!
+    
+    private var playUrl: String? = nil
+    
+    override func awakeFromNib() {
+        
+    }
+    
+    func setPlayUrl(url: String?) {
+        self.playUrl = url
+    }
+    func setDuration(duration: String) {
+        self.durationLabel.text = "\(duration)\""
+    }
+}

+ 57 - 0
o2ios/O2Platform/App/IM-聊天/View/IMAudioView.xib

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.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="IMAudioView" customModule="O2Platform" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="76" height="36"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="chat_audio_play_Normal" translatesAutoresizingMaskIntoConstraints="NO" id="a6h-yn-SMe">
+                    <rect key="frame" x="5" y="4" width="28" height="28"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="28" id="jsR-4F-M7B"/>
+                        <constraint firstAttribute="width" constant="28" id="kKV-bU-3mr"/>
+                    </constraints>
+                </imageView>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="9“" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Pe3-7R-eCP">
+                    <rect key="frame" x="43" y="9.5" width="23" height="17"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+            <constraints>
+                <constraint firstItem="Pe3-7R-eCP" firstAttribute="leading" secondItem="a6h-yn-SMe" secondAttribute="trailing" constant="10" id="6nk-nX-y34"/>
+                <constraint firstItem="a6h-yn-SMe" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="5" id="A7P-7Y-jsE"/>
+                <constraint firstItem="Pe3-7R-eCP" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="f3M-cW-QZa"/>
+                <constraint firstAttribute="trailing" secondItem="Pe3-7R-eCP" secondAttribute="trailing" constant="10" id="oO3-qw-7Xl"/>
+                <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="a6h-yn-SMe" secondAttribute="bottom" constant="4" id="qjF-vI-47S"/>
+                <constraint firstItem="a6h-yn-SMe" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="4" id="r1h-0b-1er"/>
+            </constraints>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <userDefinedRuntimeAttributes>
+                <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                    <real key="value" value="5"/>
+                </userDefinedRuntimeAttribute>
+            </userDefinedRuntimeAttributes>
+            <connections>
+                <outlet property="durationLabel" destination="Pe3-7R-eCP" id="9pj-ZS-n3s"/>
+                <outlet property="playImageView" destination="a6h-yn-SMe" id="6N0-qd-tPk"/>
+            </connections>
+            <point key="canvasLocation" x="131.8840579710145" y="233.70535714285714"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="chat_audio_play_Normal" width="18" height="18"/>
+    </resources>
+</document>

+ 96 - 0
o2ios/O2Platform/App/IM-聊天/View/IMChatAudioView.swift

@@ -0,0 +1,96 @@
+//
+//  IMChatAudioView.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/17.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+import CocoaLumberjack
+
+
+protocol IMChatAudioViewDelegate {
+    func sendVoice(path: String, voice: Data, duration: String)
+}
+class IMChatAudioView: UIView {
+    
+    @IBOutlet weak var audioViewTitle: UILabel!
+    @IBOutlet weak var audioRecordBtn: UIButton!
+    
+    private var isCancel = false
+    
+    private lazy var recordManager: O2RecordVoiceManager = {
+        let rm = O2RecordVoiceManager()
+        rm.delegate = self
+       return rm
+    }()
+    
+    var delegate: IMChatAudioViewDelegate?
+    
+    override func awakeFromNib() {
+        audioRecordBtn.addTarget(self, action: #selector(startRecord), for: .touchDown)
+        audioRecordBtn.addTarget(self, action: #selector(cancelRecord), for: .touchDragExit)
+        audioRecordBtn.addTarget(self, action: #selector(finishRecord), for: .touchUpInside)
+    }
+     
+    
+    @objc private func startRecord() {
+        DDLogError("startRecord record...................")
+        self.isCancel = false
+        self.audioViewTitle.text = "上滑取消发送"
+        //开始录音
+        recordManager.stopRecordCompletion = {
+            DDLogDebug("结束录音!!")
+        }
+        recordManager.cancelledDeleteCompletion = {
+            DDLogDebug("取消录音!")
+        }
+        recordManager.startRecordingWithPath(O2IMFileManager.shared.getRecorderPath(type: .Caf)) {
+            DDLogDebug("开始录音!!!")
+        }
+    }
+    
+    @objc private func cancelRecord() {
+        DDLogError("cancelRecord record...................")
+        self.audioViewTitle.text = "按住说话"
+        self.isCancel = true
+        //取消录音
+        recordManager.cancelledDeleteWithCompletion()
+    }
+    @objc private func finishRecord() {
+        DDLogError("finish record...................")
+        self.audioViewTitle.text = "按住说话"
+        if !self.isCancel {
+            //录音结束
+            recordManager.finishRecordingCompletion()
+            if (recordManager.recordDuration! as NSString).floatValue < 1 {
+                DispatchQueue.main.async {
+                    self.chrysan.show(.error, message: "说话时间太短", hideDelay: 1)
+                }
+                return
+            }
+            let filePath = O2IMFileManager.shared.getRecorderPath(type: .MP3)
+            recordManager.convertCafToMp3(cafPath: recordManager.recordPath!, mp3Path: filePath)
+            let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
+            delegate?.sendVoice(path: filePath, voice: data, duration: recordManager.recordDuration!)
+        }
+    }
+}
+
+extension IMChatAudioView: O2RecordVoiceDelegate {
+    func beyondLimit(_ time: TimeInterval) {
+        //录音结束
+        recordManager.finishRecordingCompletion()
+        if (recordManager.recordDuration! as NSString).floatValue < 1 {
+            DispatchQueue.main.async {
+                self.chrysan.show(.error, message: "说话时间太短", hideDelay: 1)
+            }
+            return
+        }
+        let filePath = O2IMFileManager.shared.getRecorderPath(type: .MP3)
+        recordManager.convertCafToMp3(cafPath: recordManager.recordPath!, mp3Path: filePath)
+        let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
+        delegate?.sendVoice(path: filePath, voice: data, duration: recordManager.recordDuration!)
+    }
+}

+ 57 - 0
o2ios/O2Platform/App/IM-聊天/View/IMChatAudioView.xib

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina6_1" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.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="IMChatAudioView" customModule="O2Platform" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="414" height="196"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wVh-0l-AFu">
+                    <rect key="frame" x="158" y="49" width="98" height="98"/>
+                    <color key="backgroundColor" red="0.95294117649999999" green="0.95294117649999999" blue="0.95294117649999999" alpha="1" colorSpace="calibratedRGB"/>
+                    <constraints>
+                        <constraint firstAttribute="width" constant="98" id="4e9-hO-ccV"/>
+                        <constraint firstAttribute="height" constant="98" id="wuH-tw-j4k"/>
+                    </constraints>
+                    <state key="normal" image="chat_mic"/>
+                    <userDefinedRuntimeAttributes>
+                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
+                            <real key="value" value="49"/>
+                        </userDefinedRuntimeAttribute>
+                    </userDefinedRuntimeAttributes>
+                </button>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="按住说话" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ofc-kF-lNX">
+                    <rect key="frame" x="182" y="14" width="50" height="15"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                    <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+            <constraints>
+                <constraint firstItem="wVh-0l-AFu" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="6cB-Al-ASy"/>
+                <constraint firstItem="Ofc-kF-lNX" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="SHl-b2-znG"/>
+                <constraint firstItem="wVh-0l-AFu" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="tyc-Iy-wZR"/>
+                <constraint firstItem="Ofc-kF-lNX" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="14" id="wVW-4b-Vj0"/>
+            </constraints>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
+            <connections>
+                <outlet property="audioRecordBtn" destination="wVh-0l-AFu" id="85j-KR-mnf"/>
+                <outlet property="audioViewTitle" destination="Ofc-kF-lNX" id="K6i-qG-CmP"/>
+            </connections>
+            <point key="canvasLocation" x="-113" y="-1"/>
+        </view>
+    </objects>
+    <resources>
+        <image name="chat_mic" width="32" height="32"/>
+    </resources>
+</document>

+ 64 - 11
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageSendViewCell.swift

@@ -17,6 +17,12 @@ class IMChatMessageSendViewCell: UITableViewCell {
     @IBOutlet weak var messageBgWidth: NSLayoutConstraint!
     @IBOutlet weak var messageBgHeight: NSLayoutConstraint!
     
+    private lazy var audioView: IMAudioView = {
+        let view = Bundle.main.loadNibNamed("IMAudioView", owner: self, options: nil)?.first as! IMAudioView
+        view.frame = CGRect(x: 0, y: 0, width: IMAudioView.IMAudioView_width, height: IMAudioView.IMAudioView_height)
+        return view
+    }()
+    
     var delegate: IMChatMessageDelegate?
     
     override func awakeFromNib() {
@@ -53,12 +59,68 @@ class IMChatMessageSendViewCell: UITableViewCell {
                 emojiMsgRender(emoji: body.body!)
             }else if o2_im_msg_type_image == body.type {
                 imageMsgRender(info: body)
+            }else if o2_im_msg_type_audio == body.type {
+                audioMsgRender(info: body)
             } else {
                 textMsgRender(msg: body.body!)
             }
         }
     }
     
+    //音频消息
+    private func audioMsgRender(info: IMMessageBodyInfo) {
+        self.messageBgWidth.constant = IMAudioView.IMAudioView_width + 20
+        self.messageBgHeight.constant = IMAudioView.IMAudioView_height + 20
+        self.audioView.translatesAutoresizingMaskIntoConstraints = false
+        self.messageBackgroundView.addSubview(self.audioView)
+        self.audioView.setDuration(duration: info.audioDuration ?? "0")
+        //音频文件
+        if let fileId = info.fileId {
+            let urlStr = AppDelegate.o2Collect.generateURLWithAppContextKey(
+            CommunicateContext.communicateContextKey,
+            query: CommunicateContext.imDownloadFileQuery,
+            parameter: ["##id##": fileId as AnyObject], generateTime: false)
+            self.audioView.setPlayUrl(url: urlStr)
+        } else if let filePath = info.fileTempPath {
+            self.audioView.setPlayUrl(url: filePath)
+        }
+        self.audioView.addTapGesture { (tap) in
+            self.playAudio(info: info)
+        }
+        self.constraintWithContent(contentView: self.audioView)
+    }
+    
+    private func playAudio(info: IMMessageBodyInfo) {
+        if let fileId = info.fileId {
+            O2IMFileManager.shared.getFileLocalUrl(fileId: fileId)
+                .then { (url) in
+                    do {
+                        let data = try Data(contentsOf: url)
+                        AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
+                    } catch {
+                        DDLogError(error.localizedDescription)
+                    }
+            }.catch { (e) in
+                DDLogError(e.localizedDescription)
+            }
+        } else if let filePath = info.fileTempPath {
+            do {
+                let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
+                AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
+            } catch {
+                DDLogError(error.localizedDescription)
+            }
+        }
+    }
+    
+    private func constraintWithContent(contentView: UIView) {
+        let top = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: contentView.superview!, attribute: .top, multiplier: 1, constant: 10)
+        let bottom = NSLayoutConstraint(item: contentView.superview!, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 10)
+        let left = NSLayoutConstraint(item: contentView, attribute: .leading, relatedBy: .equal, toItem: contentView.superview!, attribute: .leading, multiplier: 1, constant: 10)
+        let right = NSLayoutConstraint(item: contentView.superview!, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 10)
+        NSLayoutConstraint.activate([top, bottom, left, right])
+    }
+    
     //图片消息
     private func imageMsgRender(info: IMMessageBodyInfo) {
         let width: CGFloat = 144
@@ -91,12 +153,7 @@ class IMChatMessageSendViewCell: UITableViewCell {
         imageView.addTapGesture { (tap) in
             self.delegate?.clickImageMessage(fileId: info.fileId, tempPath: info.fileTempPath)
         }
-        let top = NSLayoutConstraint(item: imageView, attribute: .top, relatedBy: .equal, toItem: imageView.superview!, attribute: .top, multiplier: 1, constant: 10)
-        let bottom = NSLayoutConstraint(item: imageView.superview!, attribute: .bottom, relatedBy: .equal, toItem: imageView, attribute: .bottom, multiplier: 1, constant: 10)
-        let left = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: imageView.superview!, attribute: .leading, multiplier: 1, constant: 10)
-        let right = NSLayoutConstraint(item: imageView.superview!, attribute: .trailing, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 10)
-        NSLayoutConstraint.activate([top, bottom, left, right])
-
+        self.constraintWithContent(contentView: imageView)
     }
     
     private func emojiMsgRender(emoji: String) {
@@ -119,11 +176,7 @@ class IMChatMessageSendViewCell: UITableViewCell {
         emojiImage.image = UIImage(named: path, in: bundle, compatibleWith: nil)
         emojiImage.translatesAutoresizingMaskIntoConstraints = false
         self.messageBackgroundView.addSubview(emojiImage)
-        let top = NSLayoutConstraint(item: emojiImage, attribute: .top, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .top, multiplier: 1, constant: 10)
-        let bottom = NSLayoutConstraint(item: emojiImage.superview! , attribute: .bottom, relatedBy: .equal, toItem: emojiImage, attribute: .bottom, multiplier: 1, constant: 10)
-        let left = NSLayoutConstraint(item: emojiImage, attribute: .leading, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .leading, multiplier: 1, constant: 10)
-        let right = NSLayoutConstraint(item: emojiImage.superview!, attribute: .trailing, relatedBy: .equal, toItem: emojiImage, attribute: .trailing, multiplier: 1, constant: 10)
-        NSLayoutConstraint.activate([top, bottom, left, right])
+        self.constraintWithContent(contentView: emojiImage)
     }
     
     private func textMsgRender(msg: String) {

+ 64 - 10
o2ios/O2Platform/App/IM-聊天/View/IMChatMessageViewCell.swift

@@ -23,6 +23,12 @@ class IMChatMessageViewCell: UITableViewCell {
     @IBOutlet weak var messageBackgroundHeight: NSLayoutConstraint!
     private let messageWidth = 176
     
+    private lazy var audioView: IMAudioView = {
+        let view = Bundle.main.loadNibNamed("IMAudioView", owner: self, options: nil)?.first as! IMAudioView
+        view.frame = CGRect(x: 0, y: 0, width: IMAudioView.IMAudioView_width, height: IMAudioView.IMAudioView_height)
+        return view
+    }()
+    
     var delegate: IMChatMessageDelegate?
 
     override func awakeFromNib() {
@@ -111,11 +117,67 @@ class IMChatMessageViewCell: UITableViewCell {
                 emojiMsgRender(emoji: body.body!)
             } else if body.type == o2_im_msg_type_image {
                 imageMsgRender(info: body)
+            } else if o2_im_msg_type_audio == body.type {
+                audioMsgRender(info: body)
             } else {
                 textMsgRender(msg: body.body!)
             }
         }
     }
+    
+    //音频消息
+    private func audioMsgRender(info: IMMessageBodyInfo) {
+        self.messageBackgroundWidth.constant = IMAudioView.IMAudioView_width + 20
+        self.messageBackgroundHeight.constant = IMAudioView.IMAudioView_height + 20
+        self.audioView.translatesAutoresizingMaskIntoConstraints = false
+        self.messageBackgroundView.addSubview(self.audioView)
+        self.audioView.setDuration(duration: info.audioDuration ?? "0")
+        //音频文件
+        if let fileId = info.fileId {
+            let urlStr = AppDelegate.o2Collect.generateURLWithAppContextKey(
+            CommunicateContext.communicateContextKey,
+            query: CommunicateContext.imDownloadFileQuery,
+            parameter: ["##id##": fileId as AnyObject], generateTime: false)
+            self.audioView.setPlayUrl(url: urlStr)
+        } else if let filePath = info.fileTempPath {
+            self.audioView.setPlayUrl(url: filePath)
+        }
+        self.audioView.addTapGesture { (tap) in
+            self.playAudio(info: info)
+        }
+        self.constraintWithContent(contentView: self.audioView)
+    }
+    
+    private func playAudio(info: IMMessageBodyInfo) {
+        if let fileId = info.fileId {
+            O2IMFileManager.shared.getFileLocalUrl(fileId: fileId)
+                .then { (url) in
+                    do {
+                        let data = try Data(contentsOf: url)
+                        AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
+                    } catch {
+                        DDLogError(error.localizedDescription)
+                    }
+            }.catch { (e) in
+                DDLogError(e.localizedDescription)
+            }
+        } else if let filePath = info.fileTempPath {
+            do {
+                let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
+                AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
+            } catch {
+                DDLogError(error.localizedDescription)
+            }
+        }
+    }
+    
+    private func constraintWithContent(contentView: UIView) {
+        let top = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: contentView.superview!, attribute: .top, multiplier: 1, constant: 10)
+        let bottom = NSLayoutConstraint(item: contentView.superview!, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 10)
+        let left = NSLayoutConstraint(item: contentView, attribute: .leading, relatedBy: .equal, toItem: contentView.superview!, attribute: .leading, multiplier: 1, constant: 10)
+        let right = NSLayoutConstraint(item: contentView.superview!, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 10)
+        NSLayoutConstraint.activate([top, bottom, left, right])
+    }
 
     //图片消息
     private func imageMsgRender(info: IMMessageBodyInfo) {
@@ -149,11 +211,7 @@ class IMChatMessageViewCell: UITableViewCell {
         imageView.addTapGesture { (tap) in
             self.delegate?.clickImageMessage(fileId: info.fileId, tempPath: info.fileTempPath)
         }
-        let top = NSLayoutConstraint(item: imageView, attribute: .top, relatedBy: .equal, toItem: imageView.superview!, attribute: .top, multiplier: 1, constant: 10)
-        let bottom = NSLayoutConstraint(item: imageView.superview!, attribute: .bottom, relatedBy: .equal, toItem: imageView, attribute: .bottom, multiplier: 1, constant: 10)
-        let left = NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal, toItem: imageView.superview!, attribute: .leading, multiplier: 1, constant: 10)
-        let right = NSLayoutConstraint(item: imageView.superview!, attribute: .trailing, relatedBy: .equal, toItem: imageView, attribute: .trailing, multiplier: 1, constant: 10)
-        NSLayoutConstraint.activate([top, bottom, left, right])
+        self.constraintWithContent(contentView: imageView)
 
     }
 
@@ -177,11 +235,7 @@ class IMChatMessageViewCell: UITableViewCell {
         emojiImage.image = UIImage(named: path, in: bundle, compatibleWith: nil)
         emojiImage.translatesAutoresizingMaskIntoConstraints = false
         self.messageBackgroundView.addSubview(emojiImage)
-        let top = NSLayoutConstraint(item: emojiImage, attribute: .top, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .top, multiplier: 1, constant: 10)
-        let bottom = NSLayoutConstraint(item: emojiImage.superview!, attribute: .bottom, relatedBy: .equal, toItem: emojiImage, attribute: .bottom, multiplier: 1, constant: 10)
-        let left = NSLayoutConstraint(item: emojiImage, attribute: .leading, relatedBy: .equal, toItem: emojiImage.superview!, attribute: .leading, multiplier: 1, constant: 10)
-        let right = NSLayoutConstraint(item: emojiImage.superview!, attribute: .trailing, relatedBy: .equal, toItem: emojiImage, attribute: .trailing, multiplier: 1, constant: 10)
-        NSLayoutConstraint.activate([top, bottom, left, right])
+        self.constraintWithContent(contentView: emojiImage)
     }
 
     private func textMsgRender(msg: String) {

+ 3 - 6
o2ios/O2Platform/App/IM-聊天/View/IMConversationItemCell.swift

@@ -81,20 +81,17 @@ class IMConversationItemCell: UITableViewCell {
         }
         // message
         if let msgBody = conversation.lastMessage?.body, let body = parseJson(msg: msgBody) {
-            if body.type == o2_im_msg_type_text {
+            
+            if body.type == o2_im_msg_type_text || body.type == o2_im_msg_type_image || body.type == o2_im_msg_type_audio {
                 self.messageLabel.text = body.body
                 self.messageLabel.isHidden = false
                 self.emojiImg.isHidden = true
-            }else if body.type == o2_im_msg_type_emoji {
+            } else if body.type == o2_im_msg_type_emoji {
                 self.messageLabel.isHidden = true
                 self.emojiImg.isHidden = false
                 let bundle = Bundle().o2EmojiBundle(anyClass: IMConversationItemCell.self)
                 let path = o2ImEmojiPath(emojiBody: body.body!)
                 self.emojiImg.image = UIImage(named: path, in: bundle, compatibleWith: nil)
-            }else if  body.type == o2_im_msg_type_image {
-                self.messageLabel.text = body.body
-                self.messageLabel.isHidden = false
-                self.emojiImg.isHidden = true
             } else {
                 self.messageLabel.isHidden = true
                 self.emojiImg.isHidden = true

+ 22 - 0
o2ios/O2Platform/Assets.xcassets/im/chat_audio_play_Normal.imageset/Contents.json

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

BIN
o2ios/O2Platform/Assets.xcassets/im/chat_audio_play_Normal.imageset/chat_icon_audio_play_Normal@2x.png


BIN
o2ios/O2Platform/Assets.xcassets/im/chat_audio_play_Normal.imageset/chat_icon_audio_play_Normal@3x.png


+ 2 - 2
o2ios/O2Platform/Framework/Utils/FileUtil.swift

@@ -15,9 +15,9 @@ public class FileUtil {
     private init () {}
     
     public func createDirectory(path: String) {
-        if FileManager.default.fileExists(atPath: path, isDirectory: nil) {
+        if !directoryExist(directoryPath: path) {
             do {
-                try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: false, attributes: nil)
+                try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
             } catch {
                 print("创建目录错误!")
             }

+ 17 - 0
o2ios/O2Platform/Framework/lame/AmrCodec.h

@@ -0,0 +1,17 @@
+//
+//  codec.h
+//  SwiftAmrWaveConvertor
+//
+//  Created by zhenlintie on 2017/3/10.
+//  Copyright © 2017年 sTeven. All rights reserved.
+//
+
+#ifndef codec_h
+#define codec_h
+
+#import "interf_dec.h"
+#import "interf_enc.h"
+#import "dec_if.h"
+#import "enc_if.h"
+
+#endif /* codec_h */

+ 15 - 0
o2ios/O2Platform/Framework/lame/ConvertMp3.h

@@ -0,0 +1,15 @@
+//
+//  ConvertMp3.h
+//  SwiftRecorder
+//
+//  Created by iOS on 2018/9/25.
+//  Copyright © 2018 AidaHe. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface ConvertMp3 : NSObject
+
+- (void) audioPCMtoMP3:(NSString *)audioFileSavePath mp3File:(NSString *)mp3FilePath;
+    
+@end

+ 66 - 0
o2ios/O2Platform/Framework/lame/ConvertMp3.m

@@ -0,0 +1,66 @@
+//
+//  ConvertMp3.m
+//  SwiftRecorder
+//
+//  Created by iOS on 2018/9/25.
+//  Copyright © 2018 AidaHe. All rights reserved.
+//
+
+#import "ConvertMp3.h"
+#import "lame.h"
+
+#define KFILESIZE (1 * 1024 * 1024)
+
+@implementation ConvertMp3
+
+- (void) audioPCMtoMP3:(NSString *)audioFileSavePath mp3File:(NSString *)mp3FilePath{
+    NSLog(@"转码开始---");
+    CFAbsoluteTime startTime =CFAbsoluteTimeGetCurrent();
+
+    @try {
+        int read, write;
+        
+        FILE *pcm = fopen([audioFileSavePath cStringUsingEncoding:1], "rb");  //source 被转换的音频文件位置
+        fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
+        FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb");  //output 输出生成的Mp3文件位置
+        
+        const int PCM_SIZE = 8192;
+        const int MP3_SIZE = 8192;
+        short int pcm_buffer[PCM_SIZE*2];
+        unsigned char mp3_buffer[MP3_SIZE];
+        
+        lame_t lame = lame_init();
+        //设置声道1单声道,2双声道
+        lame_set_num_channels(lame,1);
+        lame_set_in_samplerate(lame, 8000.0);
+        lame_set_VBR(lame, vbr_off);
+        lame_init_params(lame);
+        
+        do {
+            read = (int)fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
+            
+            if (read == 0)
+                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
+            else
+                write = lame_encode_buffer_interleaved(lame,pcm_buffer, read, mp3_buffer, MP3_SIZE);
+            
+            fwrite(mp3_buffer, write, 1, mp3);
+        } while (read != 0);
+        
+        lame_close(lame);
+        fclose(mp3);
+        fclose(pcm);
+    }
+    @catch (NSException *exception) {
+        NSLog(@"%@",[exception description]);
+    }
+    NSLog(@"转码结束----");
+    CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
+    
+    NSLog(@"Linked in %f ms", linkTime *1000.0);
+
+    
+}
+
+
+@end

+ 1323 - 0
o2ios/O2Platform/Framework/lame/lame.h

@@ -0,0 +1,1323 @@
+/*
+ *	Interface to MP3 LAME encoding engine
+ *
+ *	Copyright (c) 1999 Mark Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: lame.h,v 1.189.2.1 2012/01/08 23:49:58 robert Exp $ */
+
+#ifndef LAME_LAME_H
+#define LAME_LAME_H
+
+/* for size_t typedef */
+#include <stddef.h>
+/* for va_list typedef */
+#include <stdarg.h>
+/* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */
+#include <stdio.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef void (*lame_report_function)(const char *format, va_list ap);
+
+#if defined(WIN32) || defined(_WIN32)
+#undef CDECL
+#define CDECL __cdecl
+#else
+#define CDECL
+#endif
+
+#define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1
+
+typedef enum vbr_mode_e {
+  vbr_off=0,
+  vbr_mt,               /* obsolete, same as vbr_mtrh */
+  vbr_rh,
+  vbr_abr,
+  vbr_mtrh,
+  vbr_max_indicator,    /* Don't use this! It's used for sanity checks.       */
+  vbr_default=vbr_mtrh    /* change this to change the default VBR mode of LAME */
+} vbr_mode;
+
+
+/* MPEG modes */
+typedef enum MPEG_mode_e {
+  STEREO = 0,
+  JOINT_STEREO,
+  DUAL_CHANNEL,   /* LAME doesn't supports this! */
+  MONO,
+  NOT_SET,
+  MAX_INDICATOR   /* Don't use this! It's used for sanity checks. */
+} MPEG_mode;
+
+/* Padding types */
+typedef enum Padding_type_e {
+  PAD_NO = 0,
+  PAD_ALL,
+  PAD_ADJUST,
+  PAD_MAX_INDICATOR   /* Don't use this! It's used for sanity checks. */
+} Padding_type;
+
+
+
+/*presets*/
+typedef enum preset_mode_e {
+    /*values from 8 to 320 should be reserved for abr bitrates*/
+    /*for abr I'd suggest to directly use the targeted bitrate as a value*/
+    ABR_8 = 8,
+    ABR_320 = 320,
+
+    V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/
+    VBR_10 = 410,
+    V8 = 420,
+    VBR_20 = 420,
+    V7 = 430,
+    VBR_30 = 430,
+    V6 = 440,
+    VBR_40 = 440,
+    V5 = 450,
+    VBR_50 = 450,
+    V4 = 460,
+    VBR_60 = 460,
+    V3 = 470,
+    VBR_70 = 470,
+    V2 = 480,
+    VBR_80 = 480,
+    V1 = 490,
+    VBR_90 = 490,
+    V0 = 500,
+    VBR_100 = 500,
+
+
+
+    /*still there for compatibility*/
+    R3MIX = 1000,
+    STANDARD = 1001,
+    EXTREME = 1002,
+    INSANE = 1003,
+    STANDARD_FAST = 1004,
+    EXTREME_FAST = 1005,
+    MEDIUM = 1006,
+    MEDIUM_FAST = 1007
+} preset_mode;
+
+
+/*asm optimizations*/
+typedef enum asm_optimizations_e {
+    MMX = 1,
+    AMD_3DNOW = 2,
+    SSE = 3
+} asm_optimizations;
+
+
+/* psychoacoustic model */
+typedef enum Psy_model_e {
+    PSY_GPSYCHO = 1,
+    PSY_NSPSYTUNE = 2
+} Psy_model;
+
+
+/* buffer considerations */
+typedef enum buffer_constraint_e {
+    MDB_DEFAULT=0,
+    MDB_STRICT_ISO=1,
+    MDB_MAXIMUM=2
+} buffer_constraint;
+
+
+struct lame_global_struct;
+typedef struct lame_global_struct lame_global_flags;
+typedef lame_global_flags *lame_t;
+
+
+
+
+/***********************************************************************
+ *
+ *  The LAME API
+ *  These functions should be called, in this order, for each
+ *  MP3 file to be encoded.  See the file "API" for more documentation
+ *
+ ***********************************************************************/
+
+
+/*
+ * REQUIRED:
+ * initialize the encoder.  sets default for all encoder parameters,
+ * returns NULL if some malloc()'s failed
+ * otherwise returns pointer to structure needed for all future
+ * API calls.
+ */
+lame_global_flags * CDECL lame_init(void);
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* obsolete version */
+int CDECL lame_init_old(lame_global_flags *);
+#endif
+
+/*
+ * OPTIONAL:
+ * set as needed to override defaults
+ */
+
+/********************************************************************
+ *  input stream description
+ ***********************************************************************/
+/* number of samples.  default = 2^32-1   */
+int CDECL lame_set_num_samples(lame_global_flags *, unsigned long);
+unsigned long CDECL lame_get_num_samples(const lame_global_flags *);
+
+/* input sample rate in Hz.  default = 44100hz */
+int CDECL lame_set_in_samplerate(lame_global_flags *, int);
+int CDECL lame_get_in_samplerate(const lame_global_flags *);
+
+/* number of channels in input stream. default=2  */
+int CDECL lame_set_num_channels(lame_global_flags *, int);
+int CDECL lame_get_num_channels(const lame_global_flags *);
+
+/*
+  scale the input by this amount before encoding.  default=1
+  (not used by decoding routines)
+*/
+int CDECL lame_set_scale(lame_global_flags *, float);
+float CDECL lame_get_scale(const lame_global_flags *);
+
+/*
+  scale the channel 0 (left) input by this amount before encoding.  default=1
+  (not used by decoding routines)
+*/
+int CDECL lame_set_scale_left(lame_global_flags *, float);
+float CDECL lame_get_scale_left(const lame_global_flags *);
+
+/*
+  scale the channel 1 (right) input by this amount before encoding.  default=1
+  (not used by decoding routines)
+*/
+int CDECL lame_set_scale_right(lame_global_flags *, float);
+float CDECL lame_get_scale_right(const lame_global_flags *);
+
+/*
+  output sample rate in Hz.  default = 0, which means LAME picks best value
+  based on the amount of compression.  MPEG only allows:
+  MPEG1    32, 44.1,   48khz
+  MPEG2    16, 22.05,  24
+  MPEG2.5   8, 11.025, 12
+  (not used by decoding routines)
+*/
+int CDECL lame_set_out_samplerate(lame_global_flags *, int);
+int CDECL lame_get_out_samplerate(const lame_global_flags *);
+
+
+/********************************************************************
+ *  general control parameters
+ ***********************************************************************/
+/* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */
+int CDECL lame_set_analysis(lame_global_flags *, int);
+int CDECL lame_get_analysis(const lame_global_flags *);
+
+/*
+  1 = write a Xing VBR header frame.
+  default = 1
+  this variable must have been added by a Hungarian notation Windows programmer :-)
+*/
+int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int);
+int CDECL lame_get_bWriteVbrTag(const lame_global_flags *);
+
+/* 1=decode only.  use lame/mpglib to convert mp3/ogg to wav.  default=0 */
+int CDECL lame_set_decode_only(lame_global_flags *, int);
+int CDECL lame_get_decode_only(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* 1=encode a Vorbis .ogg file.  default=0 */
+/* DEPRECATED */
+int CDECL lame_set_ogg(lame_global_flags *, int);
+int CDECL lame_get_ogg(const lame_global_flags *);
+#endif
+
+/*
+  internal algorithm selection.  True quality is determined by the bitrate
+  but this variable will effect quality by selecting expensive or cheap algorithms.
+  quality=0..9.  0=best (very slow).  9=worst.
+  recommended:  2     near-best quality, not too slow
+                5     good quality, fast
+                7     ok quality, really fast
+*/
+int CDECL lame_set_quality(lame_global_flags *, int);
+int CDECL lame_get_quality(const lame_global_flags *);
+
+/*
+  mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono
+  default: lame picks based on compression ration and input channels
+*/
+int CDECL lame_set_mode(lame_global_flags *, MPEG_mode);
+MPEG_mode CDECL lame_get_mode(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/*
+  mode_automs.  Use a M/S mode with a switching threshold based on
+  compression ratio
+  DEPRECATED
+*/
+int CDECL lame_set_mode_automs(lame_global_flags *, int);
+int CDECL lame_get_mode_automs(const lame_global_flags *);
+#endif
+
+/*
+  force_ms.  Force M/S for all frames.  For testing only.
+  default = 0 (disabled)
+*/
+int CDECL lame_set_force_ms(lame_global_flags *, int);
+int CDECL lame_get_force_ms(const lame_global_flags *);
+
+/* use free_format?  default = 0 (disabled) */
+int CDECL lame_set_free_format(lame_global_flags *, int);
+int CDECL lame_get_free_format(const lame_global_flags *);
+
+/* perform ReplayGain analysis?  default = 0 (disabled) */
+int CDECL lame_set_findReplayGain(lame_global_flags *, int);
+int CDECL lame_get_findReplayGain(const lame_global_flags *);
+
+/* decode on the fly. Search for the peak sample. If the ReplayGain
+ * analysis is enabled then perform the analysis on the decoded data
+ * stream. default = 0 (disabled)
+ * NOTE: if this option is set the build-in decoder should not be used */
+int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int);
+int CDECL lame_get_decode_on_the_fly(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* DEPRECATED: now does the same as lame_set_findReplayGain()
+   default = 0 (disabled) */
+int CDECL lame_set_ReplayGain_input(lame_global_flags *, int);
+int CDECL lame_get_ReplayGain_input(const lame_global_flags *);
+
+/* DEPRECATED: now does the same as
+   lame_set_decode_on_the_fly() && lame_set_findReplayGain()
+   default = 0 (disabled) */
+int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int);
+int CDECL lame_get_ReplayGain_decode(const lame_global_flags *);
+
+/* DEPRECATED: now does the same as lame_set_decode_on_the_fly()
+   default = 0 (disabled) */
+int CDECL lame_set_findPeakSample(lame_global_flags *, int);
+int CDECL lame_get_findPeakSample(const lame_global_flags *);
+#endif
+
+/* counters for gapless encoding */
+int CDECL lame_set_nogap_total(lame_global_flags*, int);
+int CDECL lame_get_nogap_total(const lame_global_flags*);
+
+int CDECL lame_set_nogap_currentindex(lame_global_flags* , int);
+int CDECL lame_get_nogap_currentindex(const lame_global_flags*);
+
+
+/*
+ * OPTIONAL:
+ * Set printf like error/debug/message reporting functions.
+ * The second argument has to be a pointer to a function which looks like
+ *   void my_debugf(const char *format, va_list ap)
+ *   {
+ *       (void) vfprintf(stdout, format, ap);
+ *   }
+ * If you use NULL as the value of the pointer in the set function, the
+ * lame buildin function will be used (prints to stderr).
+ * To quiet any output you have to replace the body of the example function
+ * with just "return;" and use it in the set function.
+ */
+int CDECL lame_set_errorf(lame_global_flags *, lame_report_function);
+int CDECL lame_set_debugf(lame_global_flags *, lame_report_function);
+int CDECL lame_set_msgf  (lame_global_flags *, lame_report_function);
+
+
+
+/* set one of brate compression ratio.  default is compression ratio of 11.  */
+int CDECL lame_set_brate(lame_global_flags *, int);
+int CDECL lame_get_brate(const lame_global_flags *);
+int CDECL lame_set_compression_ratio(lame_global_flags *, float);
+float CDECL lame_get_compression_ratio(const lame_global_flags *);
+
+
+int CDECL lame_set_preset( lame_global_flags*  gfp, int );
+int CDECL lame_set_asm_optimizations( lame_global_flags*  gfp, int, int );
+
+
+
+/********************************************************************
+ *  frame params
+ ***********************************************************************/
+/* mark as copyright.  default=0 */
+int CDECL lame_set_copyright(lame_global_flags *, int);
+int CDECL lame_get_copyright(const lame_global_flags *);
+
+/* mark as original.  default=1 */
+int CDECL lame_set_original(lame_global_flags *, int);
+int CDECL lame_get_original(const lame_global_flags *);
+
+/* error_protection.  Use 2 bytes from each frame for CRC checksum. default=0 */
+int CDECL lame_set_error_protection(lame_global_flags *, int);
+int CDECL lame_get_error_protection(const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* padding_type. 0=pad no frames  1=pad all frames 2=adjust padding(default) */
+int CDECL lame_set_padding_type(lame_global_flags *, Padding_type);
+Padding_type CDECL lame_get_padding_type(const lame_global_flags *);
+#endif
+
+/* MP3 'private extension' bit  Meaningless.  default=0 */
+int CDECL lame_set_extension(lame_global_flags *, int);
+int CDECL lame_get_extension(const lame_global_flags *);
+
+/* enforce strict ISO compliance.  default=0 */
+int CDECL lame_set_strict_ISO(lame_global_flags *, int);
+int CDECL lame_get_strict_ISO(const lame_global_flags *);
+
+
+/********************************************************************
+ * quantization/noise shaping
+ ***********************************************************************/
+
+/* disable the bit reservoir. For testing only. default=0 */
+int CDECL lame_set_disable_reservoir(lame_global_flags *, int);
+int CDECL lame_get_disable_reservoir(const lame_global_flags *);
+
+/* select a different "best quantization" function. default=0  */
+int CDECL lame_set_quant_comp(lame_global_flags *, int);
+int CDECL lame_get_quant_comp(const lame_global_flags *);
+int CDECL lame_set_quant_comp_short(lame_global_flags *, int);
+int CDECL lame_get_quant_comp_short(const lame_global_flags *);
+
+int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/
+int CDECL lame_get_experimentalX(const lame_global_flags *);
+
+/* another experimental option.  for testing only */
+int CDECL lame_set_experimentalY(lame_global_flags *, int);
+int CDECL lame_get_experimentalY(const lame_global_flags *);
+
+/* another experimental option.  for testing only */
+int CDECL lame_set_experimentalZ(lame_global_flags *, int);
+int CDECL lame_get_experimentalZ(const lame_global_flags *);
+
+/* Naoki's psycho acoustic model.  default=0 */
+int CDECL lame_set_exp_nspsytune(lame_global_flags *, int);
+int CDECL lame_get_exp_nspsytune(const lame_global_flags *);
+
+void CDECL lame_set_msfix(lame_global_flags *, double);
+float CDECL lame_get_msfix(const lame_global_flags *);
+
+
+/********************************************************************
+ * VBR control
+ ***********************************************************************/
+/* Types of VBR.  default = vbr_off = CBR */
+int CDECL lame_set_VBR(lame_global_flags *, vbr_mode);
+vbr_mode CDECL lame_get_VBR(const lame_global_flags *);
+
+/* VBR quality level.  0=highest  9=lowest  */
+int CDECL lame_set_VBR_q(lame_global_flags *, int);
+int CDECL lame_get_VBR_q(const lame_global_flags *);
+
+/* VBR quality level.  0=highest  9=lowest, Range [0,...,10[  */
+int CDECL lame_set_VBR_quality(lame_global_flags *, float);
+float CDECL lame_get_VBR_quality(const lame_global_flags *);
+
+/* Ignored except for VBR=vbr_abr (ABR mode) */
+int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int);
+int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *);
+
+int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int);
+int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *);
+
+int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int);
+int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *);
+
+/*
+  1=strictly enforce VBR_min_bitrate.  Normally it will be violated for
+  analog silence
+*/
+int CDECL lame_set_VBR_hard_min(lame_global_flags *, int);
+int CDECL lame_get_VBR_hard_min(const lame_global_flags *);
+
+/* for preset */
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+int CDECL lame_set_preset_expopts(lame_global_flags *, int);
+#endif
+
+/********************************************************************
+ * Filtering control
+ ***********************************************************************/
+/* freq in Hz to apply lowpass. Default = 0 = lame chooses.  -1 = disabled */
+int CDECL lame_set_lowpassfreq(lame_global_flags *, int);
+int CDECL lame_get_lowpassfreq(const lame_global_flags *);
+/* width of transition band, in Hz.  Default = one polyphase filter band */
+int CDECL lame_set_lowpasswidth(lame_global_flags *, int);
+int CDECL lame_get_lowpasswidth(const lame_global_flags *);
+
+/* freq in Hz to apply highpass. Default = 0 = lame chooses.  -1 = disabled */
+int CDECL lame_set_highpassfreq(lame_global_flags *, int);
+int CDECL lame_get_highpassfreq(const lame_global_flags *);
+/* width of transition band, in Hz.  Default = one polyphase filter band */
+int CDECL lame_set_highpasswidth(lame_global_flags *, int);
+int CDECL lame_get_highpasswidth(const lame_global_flags *);
+
+
+/********************************************************************
+ * psycho acoustics and other arguments which you should not change
+ * unless you know what you are doing
+ ***********************************************************************/
+
+/* only use ATH for masking */
+int CDECL lame_set_ATHonly(lame_global_flags *, int);
+int CDECL lame_get_ATHonly(const lame_global_flags *);
+
+/* only use ATH for short blocks */
+int CDECL lame_set_ATHshort(lame_global_flags *, int);
+int CDECL lame_get_ATHshort(const lame_global_flags *);
+
+/* disable ATH */
+int CDECL lame_set_noATH(lame_global_flags *, int);
+int CDECL lame_get_noATH(const lame_global_flags *);
+
+/* select ATH formula */
+int CDECL lame_set_ATHtype(lame_global_flags *, int);
+int CDECL lame_get_ATHtype(const lame_global_flags *);
+
+/* lower ATH by this many db */
+int CDECL lame_set_ATHlower(lame_global_flags *, float);
+float CDECL lame_get_ATHlower(const lame_global_flags *);
+
+/* select ATH adaptive adjustment type */
+int CDECL lame_set_athaa_type( lame_global_flags *, int);
+int CDECL lame_get_athaa_type( const lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* select the loudness approximation used by the ATH adaptive auto-leveling  */
+int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int);
+int CDECL lame_get_athaa_loudapprox( const lame_global_flags *);
+#endif
+
+/* adjust (in dB) the point below which adaptive ATH level adjustment occurs */
+int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float);
+float CDECL lame_get_athaa_sensitivity( const lame_global_flags* );
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* OBSOLETE: predictability limit (ISO tonality formula) */
+int CDECL lame_set_cwlimit(lame_global_flags *, int);
+int CDECL lame_get_cwlimit(const lame_global_flags *);
+#endif
+
+/*
+  allow blocktypes to differ between channels?
+  default: 0 for jstereo, 1 for stereo
+*/
+int CDECL lame_set_allow_diff_short(lame_global_flags *, int);
+int CDECL lame_get_allow_diff_short(const lame_global_flags *);
+
+/* use temporal masking effect (default = 1) */
+int CDECL lame_set_useTemporal(lame_global_flags *, int);
+int CDECL lame_get_useTemporal(const lame_global_flags *);
+
+/* use temporal masking effect (default = 1) */
+int CDECL lame_set_interChRatio(lame_global_flags *, float);
+float CDECL lame_get_interChRatio(const lame_global_flags *);
+
+/* disable short blocks */
+int CDECL lame_set_no_short_blocks(lame_global_flags *, int);
+int CDECL lame_get_no_short_blocks(const lame_global_flags *);
+
+/* force short blocks */
+int CDECL lame_set_force_short_blocks(lame_global_flags *, int);
+int CDECL lame_get_force_short_blocks(const lame_global_flags *);
+
+/* Input PCM is emphased PCM (for instance from one of the rarely
+   emphased CDs), it is STRONGLY not recommended to use this, because
+   psycho does not take it into account, and last but not least many decoders
+   ignore these bits */
+int CDECL lame_set_emphasis(lame_global_flags *, int);
+int CDECL lame_get_emphasis(const lame_global_flags *);
+
+
+
+/************************************************************************/
+/* internal variables, cannot be set...                                 */
+/* provided because they may be of use to calling application           */
+/************************************************************************/
+/* version  0=MPEG-2  1=MPEG-1  (2=MPEG-2.5)     */
+int CDECL lame_get_version(const lame_global_flags *);
+
+/* encoder delay   */
+int CDECL lame_get_encoder_delay(const lame_global_flags *);
+
+/*
+  padding appended to the input to make sure decoder can fully decode
+  all input.  Note that this value can only be calculated during the
+  call to lame_encoder_flush().  Before lame_encoder_flush() has
+  been called, the value of encoder_padding = 0.
+*/
+int CDECL lame_get_encoder_padding(const lame_global_flags *);
+
+/* size of MPEG frame */
+int CDECL lame_get_framesize(const lame_global_flags *);
+
+/* number of PCM samples buffered, but not yet encoded to mp3 data. */
+int CDECL lame_get_mf_samples_to_encode( const lame_global_flags*  gfp );
+
+/*
+  size (bytes) of mp3 data buffered, but not yet encoded.
+  this is the number of bytes which would be output by a call to
+  lame_encode_flush_nogap.  NOTE: lame_encode_flush() will return
+  more bytes than this because it will encode the reamining buffered
+  PCM samples before flushing the mp3 buffers.
+*/
+int CDECL lame_get_size_mp3buffer( const lame_global_flags*  gfp );
+
+/* number of frames encoded so far */
+int CDECL lame_get_frameNum(const lame_global_flags *);
+
+/*
+  lame's estimate of the total number of frames to be encoded
+   only valid if calling program set num_samples
+*/
+int CDECL lame_get_totalframes(const lame_global_flags *);
+
+/* RadioGain value. Multiplied by 10 and rounded to the nearest. */
+int CDECL lame_get_RadioGain(const lame_global_flags *);
+
+/* AudiophileGain value. Multipled by 10 and rounded to the nearest. */
+int CDECL lame_get_AudiophileGain(const lame_global_flags *);
+
+/* the peak sample */
+float CDECL lame_get_PeakSample(const lame_global_flags *);
+
+/* Gain change required for preventing clipping. The value is correct only if
+   peak sample searching was enabled. If negative then the waveform
+   already does not clip. The value is multiplied by 10 and rounded up. */
+int CDECL lame_get_noclipGainChange(const lame_global_flags *);
+
+/* user-specified scale factor required for preventing clipping. Value is
+   correct only if peak sample searching was enabled and no user-specified
+   scaling was performed. If negative then either the waveform already does
+   not clip or the value cannot be determined */
+float CDECL lame_get_noclipScale(const lame_global_flags *);
+
+
+
+
+
+
+
+/*
+ * REQUIRED:
+ * sets more internal configuration based on data provided above.
+ * returns -1 if something failed.
+ */
+int CDECL lame_init_params(lame_global_flags *);
+
+
+/*
+ * OPTIONAL:
+ * get the version number, in a string. of the form:
+ * "3.63 (beta)" or just "3.63".
+ */
+const char*  CDECL get_lame_version       ( void );
+const char*  CDECL get_lame_short_version ( void );
+const char*  CDECL get_lame_very_short_version ( void );
+const char*  CDECL get_psy_version        ( void );
+const char*  CDECL get_lame_url           ( void );
+const char*  CDECL get_lame_os_bitness    ( void );
+
+/*
+ * OPTIONAL:
+ * get the version numbers in numerical form.
+ */
+typedef struct {
+    /* generic LAME version */
+    int major;
+    int minor;
+    int alpha;               /* 0 if not an alpha version                  */
+    int beta;                /* 0 if not a beta version                    */
+
+    /* version of the psy model */
+    int psy_major;
+    int psy_minor;
+    int psy_alpha;           /* 0 if not an alpha version                  */
+    int psy_beta;            /* 0 if not a beta version                    */
+
+    /* compile time features */
+    const char *features;    /* Don't make assumptions about the contents! */
+} lame_version_t;
+void CDECL get_lame_version_numerical(lame_version_t *);
+
+
+/*
+ * OPTIONAL:
+ * print internal lame configuration to message handler
+ */
+void CDECL lame_print_config(const lame_global_flags*  gfp);
+
+void CDECL lame_print_internals( const lame_global_flags *gfp);
+
+
+/*
+ * input pcm data, output (maybe) mp3 frames.
+ * This routine handles all buffering, resampling and filtering for you.
+ *
+ * return code     number of bytes output in mp3buf. Can be 0
+ *                 -1:  mp3buf was too small
+ *                 -2:  malloc() problem
+ *                 -3:  lame_init_params() not called
+ *                 -4:  psycho acoustic problems
+ *
+ * The required mp3buf_size can be computed from num_samples,
+ * samplerate and encoding rate, but here is a worst case estimate:
+ *
+ * mp3buf_size in bytes = 1.25*num_samples + 7200
+ *
+ * I think a tighter bound could be:  (mt, March 2000)
+ * MPEG1:
+ *    num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
+ * MPEG2:
+ *    num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
+ *
+ * but test first if you use that!
+ *
+ * set mp3buf_size = 0 and LAME will not check if mp3buf_size is
+ * large enough.
+ *
+ * NOTE:
+ * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels
+ * will be averaged into the L channel before encoding only the L channel
+ * This will overwrite the data in buffer_l[] and buffer_r[].
+ *
+*/
+int CDECL lame_encode_buffer (
+        lame_global_flags*  gfp,           /* global context handle         */
+        const short int     buffer_l [],   /* PCM data for left channel     */
+        const short int     buffer_r [],   /* PCM data for right channel    */
+        const int           nsamples,      /* number of samples per channel */
+        unsigned char*      mp3buf,        /* pointer to encoded MP3 stream */
+        const int           mp3buf_size ); /* number of valid octets in this
+                                              stream                        */
+
+/*
+ * as above, but input has L & R channel data interleaved.
+ * NOTE:
+ * num_samples = number of samples in the L (or R)
+ * channel, not the total number of samples in pcm[]
+ */
+int CDECL lame_encode_buffer_interleaved(
+        lame_global_flags*  gfp,           /* global context handlei        */
+        short int           pcm[],         /* PCM data for left and right
+                                              channel, interleaved          */
+        int                 num_samples,   /* number of samples per channel,
+                                              _not_ number of samples in
+                                              pcm[]                         */
+        unsigned char*      mp3buf,        /* pointer to encoded MP3 stream */
+        int                 mp3buf_size ); /* number of valid octets in this
+                                              stream                        */
+
+
+/* as lame_encode_buffer, but for 'float's.
+ * !! NOTE: !! data must still be scaled to be in the same range as
+ * short int, +/- 32768
+ */
+int CDECL lame_encode_buffer_float(
+        lame_global_flags*  gfp,           /* global context handle         */
+        const float         pcm_l [],      /* PCM data for left channel     */
+        const float         pcm_r [],      /* PCM data for right channel    */
+        const int           nsamples,      /* number of samples per channel */
+        unsigned char*      mp3buf,        /* pointer to encoded MP3 stream */
+        const int           mp3buf_size ); /* number of valid octets in this
+                                              stream                        */
+
+/* as lame_encode_buffer, but for 'float's.
+ * !! NOTE: !! data must be scaled to +/- 1 full scale
+ */
+int CDECL lame_encode_buffer_ieee_float(
+        lame_t          gfp,
+        const float     pcm_l [],          /* PCM data for left channel     */
+        const float     pcm_r [],          /* PCM data for right channel    */
+        const int       nsamples,
+        unsigned char * mp3buf,
+        const int       mp3buf_size);
+int CDECL lame_encode_buffer_interleaved_ieee_float(
+        lame_t          gfp,
+        const float     pcm[],             /* PCM data for left and right
+                                              channel, interleaved          */
+        const int       nsamples,
+        unsigned char * mp3buf,
+        const int       mp3buf_size);
+
+/* as lame_encode_buffer, but for 'double's.
+ * !! NOTE: !! data must be scaled to +/- 1 full scale
+ */
+int CDECL lame_encode_buffer_ieee_double(
+        lame_t          gfp,
+        const double    pcm_l [],          /* PCM data for left channel     */
+        const double    pcm_r [],          /* PCM data for right channel    */
+        const int       nsamples,
+        unsigned char * mp3buf,
+        const int       mp3buf_size);
+int CDECL lame_encode_buffer_interleaved_ieee_double(
+        lame_t          gfp,
+        const double    pcm[],             /* PCM data for left and right
+                                              channel, interleaved          */
+        const int       nsamples,
+        unsigned char * mp3buf,
+        const int       mp3buf_size);
+
+/* as lame_encode_buffer, but for long's
+ * !! NOTE: !! data must still be scaled to be in the same range as
+ * short int, +/- 32768
+ *
+ * This scaling was a mistake (doesn't allow one to exploit full
+ * precision of type 'long'.  Use lame_encode_buffer_long2() instead.
+ *
+ */
+int CDECL lame_encode_buffer_long(
+        lame_global_flags*  gfp,           /* global context handle         */
+        const long     buffer_l [],       /* PCM data for left channel     */
+        const long     buffer_r [],       /* PCM data for right channel    */
+        const int           nsamples,      /* number of samples per channel */
+        unsigned char*      mp3buf,        /* pointer to encoded MP3 stream */
+        const int           mp3buf_size ); /* number of valid octets in this
+                                              stream                        */
+
+/* Same as lame_encode_buffer_long(), but with correct scaling.
+ * !! NOTE: !! data must still be scaled to be in the same range as
+ * type 'long'.   Data should be in the range:  +/- 2^(8*size(long)-1)
+ *
+ */
+int CDECL lame_encode_buffer_long2(
+        lame_global_flags*  gfp,           /* global context handle         */
+        const long     buffer_l [],       /* PCM data for left channel     */
+        const long     buffer_r [],       /* PCM data for right channel    */
+        const int           nsamples,      /* number of samples per channel */
+        unsigned char*      mp3buf,        /* pointer to encoded MP3 stream */
+        const int           mp3buf_size ); /* number of valid octets in this
+                                              stream                        */
+
+/* as lame_encode_buffer, but for int's
+ * !! NOTE: !! input should be scaled to the maximum range of 'int'
+ * If int is 4 bytes, then the values should range from
+ * +/- 2147483648.
+ *
+ * This routine does not (and cannot, without loosing precision) use
+ * the same scaling as the rest of the lame_encode_buffer() routines.
+ *
+ */
+int CDECL lame_encode_buffer_int(
+        lame_global_flags*  gfp,           /* global context handle         */
+        const int      buffer_l [],       /* PCM data for left channel     */
+        const int      buffer_r [],       /* PCM data for right channel    */
+        const int           nsamples,      /* number of samples per channel */
+        unsigned char*      mp3buf,        /* pointer to encoded MP3 stream */
+        const int           mp3buf_size ); /* number of valid octets in this
+                                              stream                        */
+
+
+
+
+
+/*
+ * REQUIRED:
+ * lame_encode_flush will flush the intenal PCM buffers, padding with
+ * 0's to make sure the final frame is complete, and then flush
+ * the internal MP3 buffers, and thus may return a
+ * final few mp3 frames.  'mp3buf' should be at least 7200 bytes long
+ * to hold all possible emitted data.
+ *
+ * will also write id3v1 tags (if any) into the bitstream
+ *
+ * return code = number of bytes output to mp3buf. Can be 0
+ */
+int CDECL lame_encode_flush(
+        lame_global_flags *  gfp,    /* global context handle                 */
+        unsigned char*       mp3buf, /* pointer to encoded MP3 stream         */
+        int                  size);  /* number of valid octets in this stream */
+
+/*
+ * OPTIONAL:
+ * lame_encode_flush_nogap will flush the internal mp3 buffers and pad
+ * the last frame with ancillary data so it is a complete mp3 frame.
+ *
+ * 'mp3buf' should be at least 7200 bytes long
+ * to hold all possible emitted data.
+ *
+ * After a call to this routine, the outputed mp3 data is complete, but
+ * you may continue to encode new PCM samples and write future mp3 data
+ * to a different file.  The two mp3 files will play back with no gaps
+ * if they are concatenated together.
+ *
+ * This routine will NOT write id3v1 tags into the bitstream.
+ *
+ * return code = number of bytes output to mp3buf. Can be 0
+ */
+int CDECL lame_encode_flush_nogap(
+        lame_global_flags *  gfp,    /* global context handle                 */
+        unsigned char*       mp3buf, /* pointer to encoded MP3 stream         */
+        int                  size);  /* number of valid octets in this stream */
+
+/*
+ * OPTIONAL:
+ * Normally, this is called by lame_init_params().  It writes id3v2 and
+ * Xing headers into the front of the bitstream, and sets frame counters
+ * and bitrate histogram data to 0.  You can also call this after
+ * lame_encode_flush_nogap().
+ */
+int CDECL lame_init_bitstream(
+        lame_global_flags *  gfp);    /* global context handle                 */
+
+
+
+/*
+ * OPTIONAL:    some simple statistics
+ * a bitrate histogram to visualize the distribution of used frame sizes
+ * a stereo mode histogram to visualize the distribution of used stereo
+ *   modes, useful in joint-stereo mode only
+ *   0: LR    left-right encoded
+ *   1: LR-I  left-right and intensity encoded (currently not supported)
+ *   2: MS    mid-side encoded
+ *   3: MS-I  mid-side and intensity encoded (currently not supported)
+ *
+ * attention: don't call them after lame_encode_finish
+ * suggested: lame_encode_flush -> lame_*_hist -> lame_close
+ */
+
+void CDECL lame_bitrate_hist(
+        const lame_global_flags * gfp,
+        int bitrate_count[14] );
+void CDECL lame_bitrate_kbps(
+        const lame_global_flags * gfp,
+        int bitrate_kbps [14] );
+void CDECL lame_stereo_mode_hist(
+        const lame_global_flags * gfp,
+        int stereo_mode_count[4] );
+
+void CDECL lame_bitrate_stereo_mode_hist (
+        const lame_global_flags * gfp,
+        int bitrate_stmode_count[14][4] );
+
+void CDECL lame_block_type_hist (
+        const lame_global_flags * gfp,
+        int btype_count[6] );
+
+void CDECL lame_bitrate_block_type_hist (
+        const lame_global_flags * gfp,
+        int bitrate_btype_count[14][6] );
+
+#if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0)
+#else
+/*
+ * OPTIONAL:
+ * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file
+ * pointer fid.  These calls perform forward and backwards seeks, so make
+ * sure fid is a real file.  Make sure lame_encode_flush has been called,
+ * and all mp3 data has been written to the file before calling this
+ * function.
+ * NOTE:
+ * if VBR  tags are turned off by the user, or turned off by LAME because
+ * the output is not a regular file, this call does nothing
+ * NOTE:
+ * LAME wants to read from the file to skip an optional ID3v2 tag, so
+ * make sure you opened the file for writing and reading.
+ * NOTE:
+ * You can call lame_get_lametag_frame instead, if you want to insert
+ * the lametag yourself.
+*/
+void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid);
+#endif
+
+/*
+ * OPTIONAL:
+ * lame_get_lametag_frame copies the final LAME-tag into 'buffer'.
+ * The function returns the number of bytes copied into buffer, or
+ * the required buffer size, if the provided buffer is too small.
+ * Function failed, if the return value is larger than 'size'!
+ * Make sure lame_encode flush has been called before calling this function.
+ * NOTE:
+ * if VBR  tags are turned off by the user, or turned off by LAME,
+ * this call does nothing and returns 0.
+ * NOTE:
+ * LAME inserted an empty frame in the beginning of mp3 audio data,
+ * which you have to replace by the final LAME-tag frame after encoding.
+ * In case there is no ID3v2 tag, usually this frame will be the very first
+ * data in your mp3 file. If you put some other leading data into your
+ * file, you'll have to do some bookkeeping about where to write this buffer.
+ */
+size_t CDECL lame_get_lametag_frame(
+        const lame_global_flags *, unsigned char* buffer, size_t size);
+
+/*
+ * REQUIRED:
+ * final call to free all remaining buffers
+ */
+int  CDECL lame_close (lame_global_flags *);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/*
+ * OBSOLETE:
+ * lame_encode_finish combines lame_encode_flush() and lame_close() in
+ * one call.  However, once this call is made, the statistics routines
+ * will no longer work because the data will have been cleared, and
+ * lame_mp3_tags_fid() cannot be called to add data to the VBR header
+ */
+int CDECL lame_encode_finish(
+        lame_global_flags*  gfp,
+        unsigned char*      mp3buf,
+        int                 size );
+#endif
+
+
+
+
+
+
+/*********************************************************************
+ *
+ * decoding
+ *
+ * a simple interface to mpglib, part of mpg123, is also included if
+ * libmp3lame is compiled with HAVE_MPGLIB
+ *
+ *********************************************************************/
+
+struct hip_global_struct;
+typedef struct hip_global_struct hip_global_flags;
+typedef hip_global_flags *hip_t;
+
+
+typedef struct {
+  int header_parsed;   /* 1 if header was parsed and following data was
+                          computed                                       */
+  int stereo;          /* number of channels                             */
+  int samplerate;      /* sample rate                                    */
+  int bitrate;         /* bitrate                                        */
+  int mode;            /* mp3 frame type                                 */
+  int mode_ext;        /* mp3 frame type                                 */
+  int framesize;       /* number of samples per mp3 frame                */
+
+  /* this data is only computed if mpglib detects a Xing VBR header */
+  unsigned long nsamp; /* number of samples in mp3 file.                 */
+  int totalframes;     /* total number of frames in mp3 file             */
+
+  /* this data is not currently computed by the mpglib routines */
+  int framenum;        /* frames decoded counter                         */
+} mp3data_struct;
+
+/* required call to initialize decoder */
+hip_t CDECL hip_decode_init(void);
+
+/* cleanup call to exit decoder  */
+int CDECL hip_decode_exit(hip_t gfp);
+
+/* HIP reporting functions */
+void CDECL hip_set_errorf(hip_t gfp, lame_report_function f);
+void CDECL hip_set_debugf(hip_t gfp, lame_report_function f);
+void CDECL hip_set_msgf  (hip_t gfp, lame_report_function f);
+
+/*********************************************************************
+ * input 1 mp3 frame, output (maybe) pcm data.
+ *
+ *  nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r);
+ *
+ * input:
+ *    len          :  number of bytes of mp3 data in mp3buf
+ *    mp3buf[len]  :  mp3 data to be decoded
+ *
+ * output:
+ *    nout:  -1    : decoding error
+ *            0    : need more data before we can complete the decode
+ *           >0    : returned 'nout' samples worth of data in pcm_l,pcm_r
+ *    pcm_l[nout]  : left channel data
+ *    pcm_r[nout]  : right channel data
+ *
+ *********************************************************************/
+int CDECL hip_decode( hip_t           gfp
+                    , unsigned char * mp3buf
+                    , size_t          len
+                    , short           pcm_l[]
+                    , short           pcm_r[]
+                    );
+
+/* same as hip_decode, and also returns mp3 header data */
+int CDECL hip_decode_headers( hip_t           gfp
+                            , unsigned char*  mp3buf
+                            , size_t          len
+                            , short           pcm_l[]
+                            , short           pcm_r[]
+                            , mp3data_struct* mp3data
+                            );
+
+/* same as hip_decode, but returns at most one frame */
+int CDECL hip_decode1( hip_t          gfp
+                     , unsigned char* mp3buf
+                     , size_t         len
+                     , short          pcm_l[]
+                     , short          pcm_r[]
+                     );
+
+/* same as hip_decode1, but returns at most one frame and mp3 header data */
+int CDECL hip_decode1_headers( hip_t           gfp
+                             , unsigned char*  mp3buf
+                             , size_t          len
+                             , short           pcm_l[]
+                             , short           pcm_r[]
+                             , mp3data_struct* mp3data
+                             );
+
+/* same as hip_decode1_headers, but also returns enc_delay and enc_padding
+   from VBR Info tag, (-1 if no info tag was found) */
+int CDECL hip_decode1_headersB( hip_t gfp
+                              , unsigned char*   mp3buf
+                              , size_t           len
+                              , short            pcm_l[]
+                              , short            pcm_r[]
+                              , mp3data_struct*  mp3data
+                              , int             *enc_delay
+                              , int             *enc_padding
+                              );
+
+
+
+/* OBSOLETE:
+ * lame_decode... functions are there to keep old code working
+ * but it is strongly recommended to replace calls by hip_decode...
+ * function calls, see above.
+ */
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+int CDECL lame_decode_init(void);
+int CDECL lame_decode(
+        unsigned char *  mp3buf,
+        int              len,
+        short            pcm_l[],
+        short            pcm_r[] );
+int CDECL lame_decode_headers(
+        unsigned char*   mp3buf,
+        int              len,
+        short            pcm_l[],
+        short            pcm_r[],
+        mp3data_struct*  mp3data );
+int CDECL lame_decode1(
+        unsigned char*  mp3buf,
+        int             len,
+        short           pcm_l[],
+        short           pcm_r[] );
+int CDECL lame_decode1_headers(
+        unsigned char*   mp3buf,
+        int              len,
+        short            pcm_l[],
+        short            pcm_r[],
+        mp3data_struct*  mp3data );
+int CDECL lame_decode1_headersB(
+        unsigned char*   mp3buf,
+        int              len,
+        short            pcm_l[],
+        short            pcm_r[],
+        mp3data_struct*  mp3data,
+        int              *enc_delay,
+        int              *enc_padding );
+int CDECL lame_decode_exit(void);
+
+#endif /* obsolete lame_decode API calls */
+
+
+/*********************************************************************
+ *
+ * id3tag stuff
+ *
+ *********************************************************************/
+
+/*
+ * id3tag.h -- Interface to write ID3 version 1 and 2 tags.
+ *
+ * Copyright (C) 2000 Don Melton.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* utility to obtain alphabetically sorted list of genre names with numbers */
+void CDECL id3tag_genre_list(
+        void (*handler)(int, const char *, void *),
+        void*  cookie);
+
+void CDECL id3tag_init     (lame_t gfp);
+
+/* force addition of version 2 tag */
+void CDECL id3tag_add_v2   (lame_t gfp);
+
+/* add only a version 1 tag */
+void CDECL id3tag_v1_only  (lame_t gfp);
+
+/* add only a version 2 tag */
+void CDECL id3tag_v2_only  (lame_t gfp);
+
+/* pad version 1 tag with spaces instead of nulls */
+void CDECL id3tag_space_v1 (lame_t gfp);
+
+/* pad version 2 tag with extra 128 bytes */
+void CDECL id3tag_pad_v2   (lame_t gfp);
+
+/* pad version 2 tag with extra n bytes */
+void CDECL id3tag_set_pad  (lame_t gfp, size_t n);
+
+void CDECL id3tag_set_title(lame_t gfp, const char* title);
+void CDECL id3tag_set_artist(lame_t gfp, const char* artist);
+void CDECL id3tag_set_album(lame_t gfp, const char* album);
+void CDECL id3tag_set_year(lame_t gfp, const char* year);
+void CDECL id3tag_set_comment(lame_t gfp, const char* comment);
+            
+/* return -1 result if track number is out of ID3v1 range
+                    and ignored for ID3v1 */
+int CDECL id3tag_set_track(lame_t gfp, const char* track);
+
+/* return non-zero result if genre name or number is invalid
+  result 0: OK
+  result -1: genre number out of range
+  result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other'
+             but taken as-is for ID3v2 genre tag */
+int CDECL id3tag_set_genre(lame_t gfp, const char* genre);
+
+/* return non-zero result if field name is invalid */
+int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue);
+
+/* return non-zero result if image type is invalid */
+int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size);
+
+/* lame_get_id3v1_tag copies ID3v1 tag into buffer.
+ * Function returns number of bytes copied into buffer, or number
+ * of bytes rquired if buffer 'size' is too small.
+ * Function fails, if returned value is larger than 'size'.
+ * NOTE:
+ * This functions does nothing, if user/LAME disabled ID3v1 tag.
+ */
+size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size);
+
+/* lame_get_id3v2_tag copies ID3v2 tag into buffer.
+ * Function returns number of bytes copied into buffer, or number
+ * of bytes rquired if buffer 'size' is too small.
+ * Function fails, if returned value is larger than 'size'.
+ * NOTE:
+ * This functions does nothing, if user/LAME disabled ID3v2 tag.
+ */
+size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size);
+
+/* normaly lame_init_param writes ID3v2 tags into the audio stream
+ * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param
+ * to turn off this behaviour and get ID3v2 tag with above function
+ * write it yourself into your file.
+ */
+void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int);
+int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp);
+
+/* experimental */
+int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text);
+
+/* experimental */
+int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text);
+
+#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED
+#else
+/* experimental */
+int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text);
+
+/* experimental */
+int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang,
+                                  unsigned short const *desc, unsigned short const *text);
+
+/* experimental */
+int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue);
+#endif
+
+/* experimental */
+int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue);
+
+/* experimental */
+int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text);
+
+/* experimental */
+int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text);
+
+
+/***********************************************************************
+*
+*  list of valid bitrates [kbps] & sample frequencies [Hz].
+*  first index: 0: MPEG-2   values  (sample frequencies 16...24 kHz)
+*               1: MPEG-1   values  (sample frequencies 32...48 kHz)
+*               2: MPEG-2.5 values  (sample frequencies  8...12 kHz)
+***********************************************************************/
+
+extern const int     bitrate_table    [3][16];
+extern const int     samplerate_table [3][ 4];
+
+/* access functions for use in DLL, global vars are not exported */
+int CDECL lame_get_bitrate(int mpeg_version, int table_index);
+int CDECL lame_get_samplerate(int mpeg_version, int table_index);
+
+
+/* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER
+   as well since lame_encode_buffer() also returns ID3v2 tag data */
+#define LAME_MAXALBUMART    (128 * 1024)
+
+/* maximum size of mp3buffer needed if you encode at most 1152 samples for
+   each call to lame_encode_buffer.  see lame_encode_buffer() below  
+   (LAME_MAXMP3BUFFER is now obsolete)  */
+#define LAME_MAXMP3BUFFER   (16384 + LAME_MAXALBUMART)
+
+
+typedef enum {
+    LAME_OKAY             =   0,
+    LAME_NOERROR          =   0,
+    LAME_GENERICERROR     =  -1,
+    LAME_NOMEM            = -10,
+    LAME_BADBITRATE       = -11,
+    LAME_BADSAMPFREQ      = -12,
+    LAME_INTERNALERROR    = -13,
+
+    FRONTEND_READERROR    = -80,
+    FRONTEND_WRITEERROR   = -81,
+    FRONTEND_FILETOOLARGE = -82
+
+} lame_errorcodes_t;
+
+#if defined(__cplusplus)
+}
+#endif
+#endif /* LAME_LAME_H */
+

BIN
o2ios/O2Platform/Framework/lame/libmp3lame.a


BIN
o2ios/O2Platform/Framework/lame/opencore-amr/lib/libopencore-amrnb.a


BIN
o2ios/O2Platform/Framework/lame/opencore-amr/lib/libopencore-amrwb.a


+ 34 - 0
o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrnb/interf_dec.h

@@ -0,0 +1,34 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2009 Martin Storsjo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#ifndef OPENCORE_AMRNB_INTERF_DEC_H
+#define OPENCORE_AMRNB_INTERF_DEC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void* Decoder_Interface_init(void);
+void Decoder_Interface_exit(void* state);
+void Decoder_Interface_Decode(void* state, const unsigned char* in, short* out, int bfi);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 50 - 0
o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrnb/interf_enc.h

@@ -0,0 +1,50 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2009 Martin Storsjo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#ifndef OPENCORE_AMRNB_INTERF_ENC_H
+#define OPENCORE_AMRNB_INTERF_ENC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef AMRNB_WRAPPER_INTERNAL
+/* Copied from enc/src/gsmamr_enc.h */
+enum Mode {
+	MR475 = 0,/* 4.75 kbps */
+	MR515,    /* 5.15 kbps */
+	MR59,     /* 5.90 kbps */
+	MR67,     /* 6.70 kbps */
+	MR74,     /* 7.40 kbps */
+	MR795,    /* 7.95 kbps */
+	MR102,    /* 10.2 kbps */
+	MR122,    /* 12.2 kbps */
+	MRDTX,    /* DTX       */
+	N_MODES   /* Not Used  */
+};
+#endif
+
+void* Encoder_Interface_init(int dtx);
+void Encoder_Interface_exit(void* state);
+int Encoder_Interface_Encode(void* state, enum Mode mode, const short* speech, unsigned char* out, int forceSpeech);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 36 - 0
o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrwb/dec_if.h

@@ -0,0 +1,36 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2009 Martin Storsjo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#ifndef OPENCORE_AMRWB_DEC_IF_H
+#define OPENCORE_AMRWB_DEC_IF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _good_frame 0
+
+void* D_IF_init(void);
+void D_IF_decode(void* state, const unsigned char* bits, short* synth, int bfi);
+void D_IF_exit(void* state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 33 - 0
o2ios/O2Platform/Framework/lame/opencore-amr/opencore-amrwb/if_rom.h

@@ -0,0 +1,33 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2009 Martin Storsjo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#ifndef OPENCORE_AMRWB_IF_ROM_H
+#define OPENCORE_AMRWB_IF_ROM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+typedef int16_t Word16;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

BIN
o2ios/O2Platform/Framework/lame/vo-amrwbenc/lib/libvo-amrwbenc.a


+ 34 - 0
o2ios/O2Platform/Framework/lame/vo-amrwbenc/vo-amrwbenc/enc_if.h

@@ -0,0 +1,34 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2010 Martin Storsjo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ * -------------------------------------------------------------------
+ */
+
+#ifndef VO_AMRWBENC_ENC_IF_H
+#define VO_AMRWBENC_ENC_IF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void* E_IF_init(void);
+int E_IF_encode(void* state, int mode, const short* speech, unsigned char* out, int dtx);
+void E_IF_exit(void* state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 78 - 0
o2ios/O2Platform/Manager/AudioPlayerManager.swift

@@ -0,0 +1,78 @@
+//
+//  AudioPlayerManager.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/17.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import AVFoundation
+
+protocol AudioPlayerManagerDelegate {
+    func didAudioPlayerBeginPlay(_ AudioPlayer: AVAudioPlayer)
+    func didAudioPlayerStopPlay(_ AudioPlayer: AVAudioPlayer)
+    func didAudioPlayerPausePlay(_ AudioPlayer: AVAudioPlayer)
+}
+
+class AudioPlayerManager: NSObject {
+    static let  shared: AudioPlayerManager = {
+        return AudioPlayerManager()
+    }()
+    
+    private override init() {super.init()}
+    
+    var delegate: AudioPlayerManagerDelegate?
+    var player: AVAudioPlayer!
+    
+    
+    
+    func managerAudioWithData(_ data:Data, toplay:Bool) {
+        if toplay {
+            playAudioWithData(data)
+        } else {
+            pausePlayingAudio()
+        }
+    }
+    
+    func playAudioWithData(_ voiceData:Data) {
+        do {
+            try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
+        } catch let error as NSError {
+            print("set category fail \(error)")
+        }
+        
+        if player != nil {
+            player.stop()
+            player = nil
+        }
+        
+        do {
+            let pl: AVAudioPlayer = try AVAudioPlayer(data: voiceData)
+            pl.delegate = self
+            pl.play()
+            player = pl
+        } catch let error as NSError {
+            print("alloc AVAudioPlayer with voice data fail with error \(error)")
+        }
+        
+        UIDevice.current.isProximityMonitoringEnabled = true
+    }
+    
+    func pausePlayingAudio() {
+        player?.pause()
+    }
+    
+    func stopAudio() {
+        if player != nil && player.isPlaying {
+            player.stop()
+        }
+        UIDevice.current.isProximityMonitoringEnabled = false
+        delegate?.didAudioPlayerStopPlay(player)
+    }
+}
+
+extension AudioPlayerManager: AVAudioPlayerDelegate {
+    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+        stopAudio()
+    }
+}

+ 235 - 0
o2ios/O2Platform/Manager/O2RecordVoiceManager.swift

@@ -0,0 +1,235 @@
+//
+//  O2RecordVoiceManager.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/6/17.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import UIKit
+import AVFoundation
+
+
+fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+    switch (lhs, rhs) {
+    case let (l?, r?):
+        return l < r
+    case (nil, _?):
+        return true
+    default:
+        return false
+    }
+}
+
+fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
+    switch (lhs, rhs) {
+    case let (l?, r?):
+        return l > r
+    default:
+        return rhs < lhs
+    }
+}
+
+typealias O2RecordCompletionCallBack = () -> Void
+
+protocol O2RecordVoiceDelegate {
+    func beyondLimit(_ time: TimeInterval)
+}
+
+class O2RecordVoiceManager: NSObject {
+    private let maxRecordTime = 60.0
+
+    private var startRecordCompleted: O2RecordCompletionCallBack?
+    private var recorder: AVAudioRecorder?
+    private var recordProgress: Float?
+    private var theTimer: Timer?
+    private var currentTimeInterval: TimeInterval?
+    
+    var recordPath: String?
+    var recordDuration: String?
+    var stopRecordCompletion: O2RecordCompletionCallBack?
+    var cancelledDeleteCompletion: O2RecordCompletionCallBack?
+    var delegate: O2RecordVoiceDelegate?
+    
+    override init() {
+        super.init()
+    }
+    
+    deinit {
+        stopRecord()
+        recordPath = nil
+    }
+    
+    @objc private func updateMeters() {
+        if recorder == nil {
+            return
+        }
+        currentTimeInterval = recorder?.currentTime
+        
+        recordProgress = recorder?.peakPower(forChannel: 0)
+        if currentTimeInterval > maxRecordTime {
+            stopRecord()
+            delegate?.beyondLimit(currentTimeInterval!)
+            if stopRecordCompletion != nil {
+                DispatchQueue.main.async(execute: stopRecordCompletion!)
+                recorder?.updateMeters()
+            }
+        }
+    }
+    
+    private func getVoiceDuration(_ recordPath:String) {
+        do {
+            let player:AVAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: recordPath))
+            player.play()
+            let duration = player.duration
+            self.recordDuration = "\(Int(duration))"
+        } catch let error as NSError {
+            print("get AVAudioPlayer is fail \(error)")
+            self.recordDuration = "0"
+        }
+    }
+    
+    private func resetTimer() {
+        if theTimer == nil {
+            return
+        } else {
+            theTimer!.invalidate()
+            theTimer = nil
+        }
+    }
+    
+    private func cancelRecording() {
+        if recorder == nil {
+            return
+        }
+        if recorder?.isRecording != false {
+            recorder?.stop()
+        }
+        recorder = nil
+    }
+    
+    private func stopRecord() {
+        cancelRecording()
+        resetTimer()
+    }
+    
+    func startRecordingWithPath(_ path:String, startRecordCompleted:@escaping O2RecordCompletionCallBack) {
+        print("Action - startRecordingWithPath:")
+        self.startRecordCompleted = startRecordCompleted
+        self.recordPath = path
+        
+        let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
+        do {
+            try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: .default, options: .defaultToSpeaker)
+        } catch let error as NSError {
+            print("could not set session category")
+            print(error.localizedDescription)
+        }
+        
+        do {
+            try audioSession.setActive(true)
+        } catch let error as NSError {
+            print("could not set session active")
+            print(error.localizedDescription)
+        }
+        
+//        let recordSettings:[String : AnyObject] = [
+//            AVFormatIDKey: NSNumber(value: kAudioFormatAppleIMA4 as UInt32),
+//            AVNumberOfChannelsKey: 1 as AnyObject,
+//            AVSampleRateKey : 16000.0 as AnyObject
+//        ]
+        let recordSetting: [String: Any] = [
+            AVSampleRateKey: NSNumber(value: 16000),//采样率
+            AVEncoderBitRateKey:NSNumber(value: 16000),
+            AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM),//音频格式
+            AVNumberOfChannelsKey: NSNumber(value: 1),//通道数
+            AVLinearPCMBitDepthKey:NSNumber(value: 16),
+            AVEncoderAudioQualityKey: NSNumber(value: AVAudioQuality.high.rawValue)//录音质量
+        ]
+        
+        do {
+            self.recorder = try AVAudioRecorder(url: URL(fileURLWithPath: self.recordPath!), settings: recordSetting)
+            self.recorder!.delegate = self
+            self.recorder!.prepareToRecord()
+            self.recorder?.record(forDuration: 160.0)
+        } catch let error as NSError {
+            recorder = nil
+            print(error.localizedDescription)
+        }
+        
+        if ((self.recorder?.record()) != false) {
+            self.resetTimer()
+            self.theTimer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(updateMeters), userInfo: nil, repeats: true)
+        } else {
+            print("fail record")
+        }
+        
+        if self.startRecordCompleted != nil {
+            DispatchQueue.main.async(execute: self.startRecordCompleted!)
+        }
+    }
+    
+    func finishRecordingCompletion() {
+        stopRecord()
+        getVoiceDuration(recordPath!)
+        
+        if stopRecordCompletion != nil {
+            DispatchQueue.main.async(execute: stopRecordCompletion!)
+        }
+    }
+    
+    func cancelledDeleteWithCompletion() {
+        stopRecord()
+        if recordPath != nil {
+            let fileManager:FileManager = FileManager.default
+            if fileManager.fileExists(atPath: recordPath!) == true {
+                do {
+                    try fileManager.removeItem(atPath: recordPath!)
+                } catch let error as NSError {
+                    print("can no to remove the voice file \(error.localizedDescription)")
+                }
+            } else {
+                if cancelledDeleteCompletion != nil {
+                    DispatchQueue.main.async(execute: cancelledDeleteCompletion!)
+                }
+            }
+            
+        }
+    }
+    // test player
+    func playVoice(_ recordPath:String) {
+        do {
+            print("\(recordPath)")
+            let player:AVAudioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: recordPath))
+            player.volume = 1
+            player.delegate = self
+            player.numberOfLoops = -1
+            player.prepareToPlay()
+            player.play()
+            
+        } catch let error as NSError {
+            print("get AVAudioPlayer is fail \(error)")
+        }
+    }
+    //caf文件转mp3
+    func convertCafToMp3(cafPath: String, mp3Path: String){
+        ConvertMp3().audioPCMtoMP3(cafPath, mp3File: mp3Path)
+        print("caf源文件:\(cafPath)")
+        print("mp3文件:\(mp3Path)")
+    }
+    
+    
+}
+
+extension O2RecordVoiceManager: AVAudioPlayerDelegate, AVAudioRecorderDelegate {
+    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
+        print("finished playing \(flag)")
+        
+    }
+
+    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
+        if let e = error {
+            print("\(e.localizedDescription)")
+        }
+    }
+}

+ 4 - 0
o2ios/O2Platform/O2Platform-Bridging-Header.h

@@ -44,3 +44,7 @@
 // flutter
 #import "GeneratedPluginRegistrant.h"
 
+//lame mp3转码
+#import "ConvertMp3.h"
+#import "AmrCodec.h"
+