├── .gitignore ├── Arduino └── SoftModemFSK │ └── SoftModemFSK.ino ├── FSKModem ├── FSKModem.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── FSKModem │ ├── JMAudioInputStream.h │ ├── JMAudioInputStream.m │ ├── JMAudioOutputStream.h │ ├── JMAudioOutputStream.m │ ├── JMAudioSource.h │ ├── JMAudioStream.h │ ├── JMAudioStream.m │ ├── JMFSKModem.h │ ├── JMFSKModem.m │ ├── JMFSKModemConfiguration.h │ ├── JMFSKModemConfiguration.m │ ├── JMFSKModemDelegate.h │ ├── JMFSKRecognizer.h │ ├── JMFSKRecognizer.m │ ├── JMFSKRecognizerDelegate.h │ ├── JMFSKSerialGenerator.h │ ├── JMFSKSerialGenerator.m │ ├── JMPatternRecognizer.h │ ├── JMPrefix.pch │ ├── JMProtocolDecoder.h │ ├── JMProtocolDecoder.m │ ├── JMProtocolDecoderDelegate.h │ ├── JMProtocolEncoder.h │ ├── JMProtocolEncoder.m │ ├── JMQueue.h │ ├── JMQueue.m │ ├── JMQueueNode.h │ └── JMQueueNode.m ├── OSX │ ├── FSKModem.h │ └── Info.plist └── iOS │ ├── FSKModem.h │ └── Info.plist ├── LICENSE ├── README.md └── examples └── Terminal ├── Terminal.xcodeproj └── project.pbxproj └── Terminal ├── AppDelegate.h ├── AppDelegate.m ├── Images.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon_120.png │ ├── icon_152.png │ └── icon_76.png └── Contents.json ├── Info.plist ├── JMTerminalView.h ├── JMTerminalView.m ├── JMTerminalViewController.h ├── JMTerminalViewController.m ├── JMTerminalViewModel.h ├── JMTerminalViewModel.m ├── LaunchScreen.storyboard └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | Carthage 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | profile 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | 23 | # Bundler 24 | .bundle 25 | 26 | Carthage 27 | # We recommend against adding the Pods directory to your .gitignore. However 28 | # you should judge for yourself, the pros and cons are mentioned at: 29 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 30 | # 31 | # Note: if you ignore the Pods directory, make sure to uncomment 32 | # `pod install` in .travis.yml 33 | # 34 | Pods/ 35 | *.swp 36 | -------------------------------------------------------------------------------- /Arduino/SoftModemFSK/SoftModemFSK.ino: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | SoftModem modem; 26 | 27 | static const byte START_BYTE = 0xFF; 28 | static const byte ESCAPE_BYTE = 0x33; 29 | static const byte END_BYTE = 0x77; 30 | 31 | static const unsigned int BAUD_RATE = 57600; 32 | 33 | void setup() 34 | { 35 | Serial.begin(BAUD_RATE); 36 | delay(1000); 37 | modem.begin(); 38 | } 39 | 40 | void decodeByte() 41 | { 42 | static boolean escaped = false; 43 | 44 | while(modem.available()) 45 | { 46 | byte data = modem.read(); 47 | 48 | if(escaped) 49 | { 50 | Serial.print((char)data); 51 | escaped = false; 52 | 53 | continue; 54 | } 55 | 56 | if(data == ESCAPE_BYTE) 57 | { 58 | escaped = true; 59 | 60 | continue; 61 | } 62 | 63 | if(data == START_BYTE) 64 | { 65 | continue; 66 | } 67 | 68 | if(data == END_BYTE) 69 | { 70 | Serial.print('\n'); 71 | break; 72 | } 73 | 74 | Serial.print((char)data); 75 | } 76 | } 77 | 78 | void encodeByte() 79 | { 80 | if(Serial.available()) 81 | { 82 | modem.write(START_BYTE); 83 | while(Serial.available()) 84 | { 85 | byte data = Serial.read(); 86 | if(data == START_BYTE || data == END_BYTE || data == ESCAPE_BYTE) 87 | { 88 | modem.write(ESCAPE_BYTE); 89 | } 90 | modem.write(data); 91 | } 92 | modem.write(END_BYTE); 93 | } 94 | } 95 | 96 | void loop() 97 | { 98 | decodeByte(); 99 | encodeByte(); 100 | } 101 | -------------------------------------------------------------------------------- /FSKModem/FSKModem.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B4A39425196B1037002C7324 /* FSKModem.h in Headers */ = {isa = PBXBuildFile; fileRef = B4A39424196B1037002C7324 /* FSKModem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | B4A3943B196B1078002C7324 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A39439196B1078002C7324 /* AudioToolbox.framework */; }; 12 | B4A3943C196B1078002C7324 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4A3943A196B1078002C7324 /* AVFoundation.framework */; }; 13 | B4A3943D196B108E002C7324 /* JMProtocolDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E858D1968AA5B00148E42 /* JMProtocolDecoder.m */; }; 14 | B4A3943E196B108E002C7324 /* JMProtocolEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85901968AA5B00148E42 /* JMProtocolEncoder.m */; }; 15 | B4A3943F196B108E002C7324 /* JMQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85921968AA5B00148E42 /* JMQueue.m */; }; 16 | B4A39440196B108E002C7324 /* JMQueueNode.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85941968AA5B00148E42 /* JMQueueNode.m */; }; 17 | B4A39441196B108E002C7324 /* JMAudioInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E859B1968AA6E00148E42 /* JMAudioInputStream.m */; }; 18 | B4A39442196B108E002C7324 /* JMAudioOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E859D1968AA6E00148E42 /* JMAudioOutputStream.m */; }; 19 | B4A39443196B108E002C7324 /* JMAudioStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E859F1968AA6E00148E42 /* JMAudioStream.m */; }; 20 | B4A39444196B108E002C7324 /* JMFSKRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85A11968AA6E00148E42 /* JMFSKRecognizer.m */; }; 21 | B4A39445196B108E002C7324 /* JMFSKSerialGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85A31968AA6E00148E42 /* JMFSKSerialGenerator.m */; }; 22 | B4A39446196B108E002C7324 /* JMFSKModem.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E858A1968AA5B00148E42 /* JMFSKModem.m */; }; 23 | B4A39447196B108E002C7324 /* JMFSKModemConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = B41254721969BE15009D9228 /* JMFSKModemConfiguration.m */; }; 24 | B4A39448196B10A2002C7324 /* JMProtocolDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858C1968AA5B00148E42 /* JMProtocolDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | B4A39449196B10A2002C7324 /* JMProtocolDecoderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858E1968AA5B00148E42 /* JMProtocolDecoderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 26 | B4A3944A196B10A2002C7324 /* JMProtocolEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858F1968AA5B00148E42 /* JMProtocolEncoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 27 | B4A3944B196B10A2002C7324 /* JMQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85911968AA5B00148E42 /* JMQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 28 | B4A3944C196B10A2002C7324 /* JMQueueNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85931968AA5B00148E42 /* JMQueueNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | B4A3944D196B10A2002C7324 /* JMAudioInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E859A1968AA6E00148E42 /* JMAudioInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30 | B4A3944E196B10A2002C7324 /* JMAudioOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E859C1968AA6E00148E42 /* JMAudioOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 31 | B4A3944F196B10A2002C7324 /* JMAudioStream.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E859E1968AA6E00148E42 /* JMAudioStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32 | B4A39450196B10A2002C7324 /* JMFSKRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85A01968AA6E00148E42 /* JMFSKRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33 | B4A39451196B10A2002C7324 /* JMPatternRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85A51968AA6E00148E42 /* JMPatternRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34 | B4A39452196B10A2002C7324 /* JMFSKRecognizerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858B1968AA5B00148E42 /* JMFSKRecognizerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35 | B4A39453196B10A2002C7324 /* JMFSKSerialGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85A21968AA6E00148E42 /* JMFSKSerialGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 36 | B4A39454196B10A2002C7324 /* JMAudioSource.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85881968AA5B00148E42 /* JMAudioSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 37 | B4A39455196B10A2002C7324 /* JMFSKModem.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85891968AA5B00148E42 /* JMFSKModem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 38 | B4A39456196B10A2002C7324 /* JMFSKModemConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = B41254711969BE15009D9228 /* JMFSKModemConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 39 | B4A39457196B10A2002C7324 /* JMFSKModemDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B4A393B5196AE1E3002C7324 /* JMFSKModemDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 40 | B4B5D5901ED0525900AF8BDB /* FSKModem.h in Headers */ = {isa = PBXBuildFile; fileRef = B4B5D58E1ED0525900AF8BDB /* FSKModem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 41 | B4B5D5941ED0527C00AF8BDB /* JMProtocolDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E858D1968AA5B00148E42 /* JMProtocolDecoder.m */; }; 42 | B4B5D5951ED0527C00AF8BDB /* JMProtocolEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85901968AA5B00148E42 /* JMProtocolEncoder.m */; }; 43 | B4B5D5961ED0527C00AF8BDB /* JMQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85921968AA5B00148E42 /* JMQueue.m */; }; 44 | B4B5D5971ED0527C00AF8BDB /* JMQueueNode.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85941968AA5B00148E42 /* JMQueueNode.m */; }; 45 | B4B5D5981ED0527C00AF8BDB /* JMAudioInputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E859B1968AA6E00148E42 /* JMAudioInputStream.m */; }; 46 | B4B5D5991ED0527C00AF8BDB /* JMAudioOutputStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E859D1968AA6E00148E42 /* JMAudioOutputStream.m */; }; 47 | B4B5D59A1ED0527C00AF8BDB /* JMAudioStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E859F1968AA6E00148E42 /* JMAudioStream.m */; }; 48 | B4B5D59B1ED0527C00AF8BDB /* JMFSKRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85A11968AA6E00148E42 /* JMFSKRecognizer.m */; }; 49 | B4B5D59C1ED0527C00AF8BDB /* JMFSKSerialGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E85A31968AA6E00148E42 /* JMFSKSerialGenerator.m */; }; 50 | B4B5D59D1ED0527C00AF8BDB /* JMFSKModem.m in Sources */ = {isa = PBXBuildFile; fileRef = B44E858A1968AA5B00148E42 /* JMFSKModem.m */; }; 51 | B4B5D59E1ED0527C00AF8BDB /* JMFSKModemConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = B41254721969BE15009D9228 /* JMFSKModemConfiguration.m */; }; 52 | B4B5D59F1ED0529B00AF8BDB /* JMProtocolDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858C1968AA5B00148E42 /* JMProtocolDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53 | B4B5D5A01ED0529B00AF8BDB /* JMProtocolDecoderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858E1968AA5B00148E42 /* JMProtocolDecoderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 54 | B4B5D5A11ED0529B00AF8BDB /* JMProtocolEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858F1968AA5B00148E42 /* JMProtocolEncoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55 | B4B5D5A21ED0529B00AF8BDB /* JMQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85911968AA5B00148E42 /* JMQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 56 | B4B5D5A31ED0529B00AF8BDB /* JMQueueNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85931968AA5B00148E42 /* JMQueueNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 57 | B4B5D5A41ED0529B00AF8BDB /* JMAudioInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E859A1968AA6E00148E42 /* JMAudioInputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 58 | B4B5D5A51ED0529B00AF8BDB /* JMAudioOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E859C1968AA6E00148E42 /* JMAudioOutputStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59 | B4B5D5A61ED0529B00AF8BDB /* JMAudioStream.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E859E1968AA6E00148E42 /* JMAudioStream.h */; settings = {ATTRIBUTES = (Public, ); }; }; 60 | B4B5D5A71ED0529B00AF8BDB /* JMFSKRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85A01968AA6E00148E42 /* JMFSKRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 61 | B4B5D5A81ED0529B00AF8BDB /* JMPatternRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85A51968AA6E00148E42 /* JMPatternRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62 | B4B5D5A91ED0529B00AF8BDB /* JMFSKRecognizerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E858B1968AA5B00148E42 /* JMFSKRecognizerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 63 | B4B5D5AA1ED0529B00AF8BDB /* JMFSKSerialGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85A21968AA6E00148E42 /* JMFSKSerialGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64 | B4B5D5AB1ED0529B00AF8BDB /* JMAudioSource.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85881968AA5B00148E42 /* JMAudioSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 65 | B4B5D5AC1ED0529B00AF8BDB /* JMFSKModem.h in Headers */ = {isa = PBXBuildFile; fileRef = B44E85891968AA5B00148E42 /* JMFSKModem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66 | B4B5D5AD1ED0529B00AF8BDB /* JMFSKModemConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = B41254711969BE15009D9228 /* JMFSKModemConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 67 | B4B5D5AE1ED0529B00AF8BDB /* JMFSKModemDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B4A393B5196AE1E3002C7324 /* JMFSKModemDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 68 | B4B5D5AF1ED0542C00AF8BDB /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4145CB3196971B900C61775 /* AudioToolbox.framework */; }; 69 | B4B5D5B01ED0543700AF8BDB /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4145CB4196971B900C61775 /* AVFoundation.framework */; }; 70 | /* End PBXBuildFile section */ 71 | 72 | /* Begin PBXFileReference section */ 73 | B41254711969BE15009D9228 /* JMFSKModemConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMFSKModemConfiguration.h; sourceTree = ""; }; 74 | B41254721969BE15009D9228 /* JMFSKModemConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMFSKModemConfiguration.m; sourceTree = ""; }; 75 | B4145CB3196971B900C61775 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 76 | B4145CB4196971B900C61775 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 77 | B44E85881968AA5B00148E42 /* JMAudioSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMAudioSource.h; sourceTree = ""; }; 78 | B44E85891968AA5B00148E42 /* JMFSKModem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMFSKModem.h; sourceTree = ""; }; 79 | B44E858A1968AA5B00148E42 /* JMFSKModem.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = JMFSKModem.m; sourceTree = ""; }; 80 | B44E858B1968AA5B00148E42 /* JMFSKRecognizerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMFSKRecognizerDelegate.h; sourceTree = ""; }; 81 | B44E858C1968AA5B00148E42 /* JMProtocolDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMProtocolDecoder.h; sourceTree = ""; }; 82 | B44E858D1968AA5B00148E42 /* JMProtocolDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMProtocolDecoder.m; sourceTree = ""; }; 83 | B44E858E1968AA5B00148E42 /* JMProtocolDecoderDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMProtocolDecoderDelegate.h; sourceTree = ""; }; 84 | B44E858F1968AA5B00148E42 /* JMProtocolEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMProtocolEncoder.h; sourceTree = ""; }; 85 | B44E85901968AA5B00148E42 /* JMProtocolEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMProtocolEncoder.m; sourceTree = ""; }; 86 | B44E85911968AA5B00148E42 /* JMQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMQueue.h; sourceTree = ""; }; 87 | B44E85921968AA5B00148E42 /* JMQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMQueue.m; sourceTree = ""; }; 88 | B44E85931968AA5B00148E42 /* JMQueueNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMQueueNode.h; sourceTree = ""; }; 89 | B44E85941968AA5B00148E42 /* JMQueueNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMQueueNode.m; sourceTree = ""; }; 90 | B44E859A1968AA6E00148E42 /* JMAudioInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMAudioInputStream.h; sourceTree = ""; }; 91 | B44E859B1968AA6E00148E42 /* JMAudioInputStream.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = JMAudioInputStream.m; sourceTree = ""; }; 92 | B44E859C1968AA6E00148E42 /* JMAudioOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMAudioOutputStream.h; sourceTree = ""; }; 93 | B44E859D1968AA6E00148E42 /* JMAudioOutputStream.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = JMAudioOutputStream.m; sourceTree = ""; }; 94 | B44E859E1968AA6E00148E42 /* JMAudioStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMAudioStream.h; sourceTree = ""; }; 95 | B44E859F1968AA6E00148E42 /* JMAudioStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMAudioStream.m; sourceTree = ""; }; 96 | B44E85A01968AA6E00148E42 /* JMFSKRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMFSKRecognizer.h; sourceTree = ""; }; 97 | B44E85A11968AA6E00148E42 /* JMFSKRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMFSKRecognizer.m; sourceTree = ""; }; 98 | B44E85A21968AA6E00148E42 /* JMFSKSerialGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMFSKSerialGenerator.h; sourceTree = ""; }; 99 | B44E85A31968AA6E00148E42 /* JMFSKSerialGenerator.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = JMFSKSerialGenerator.m; sourceTree = ""; }; 100 | B44E85A51968AA6E00148E42 /* JMPatternRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMPatternRecognizer.h; sourceTree = ""; }; 101 | B44E85AF1968AD2200148E42 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 102 | B44E85B11968ADAB00148E42 /* JMPrefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JMPrefix.pch; sourceTree = ""; }; 103 | B4A393B5196AE1E3002C7324 /* JMFSKModemDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JMFSKModemDelegate.h; sourceTree = ""; }; 104 | B4A39420196B1037002C7324 /* FSKModem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FSKModem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 105 | B4A39423196B1037002C7324 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 106 | B4A39424196B1037002C7324 /* FSKModem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSKModem.h; sourceTree = ""; }; 107 | B4A39439196B1078002C7324 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/AudioToolbox.framework; sourceTree = DEVELOPER_DIR; }; 108 | B4A3943A196B1078002C7324 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; }; 109 | B4B5D58C1ED0525900AF8BDB /* FSKModem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FSKModem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | B4B5D58E1ED0525900AF8BDB /* FSKModem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSKModem.h; sourceTree = ""; }; 111 | B4B5D58F1ED0525900AF8BDB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 112 | /* End PBXFileReference section */ 113 | 114 | /* Begin PBXFrameworksBuildPhase section */ 115 | B4A3941C196B1037002C7324 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | B4A3943B196B1078002C7324 /* AudioToolbox.framework in Frameworks */, 120 | B4A3943C196B1078002C7324 /* AVFoundation.framework in Frameworks */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | B4B5D5881ED0525900AF8BDB /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | B4B5D5B01ED0543700AF8BDB /* AVFoundation.framework in Frameworks */, 129 | B4B5D5AF1ED0542C00AF8BDB /* AudioToolbox.framework in Frameworks */, 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXFrameworksBuildPhase section */ 134 | 135 | /* Begin PBXGroup section */ 136 | B44E85651968A9F900148E42 = { 137 | isa = PBXGroup; 138 | children = ( 139 | B4A39439196B1078002C7324 /* AudioToolbox.framework */, 140 | B4A3943A196B1078002C7324 /* AVFoundation.framework */, 141 | B4145CB3196971B900C61775 /* AudioToolbox.framework */, 142 | B4145CB4196971B900C61775 /* AVFoundation.framework */, 143 | B44E85AF1968AD2200148E42 /* Foundation.framework */, 144 | B44E85701968A9F900148E42 /* FSKModem */, 145 | B4A39421196B1037002C7324 /* OSX */, 146 | B4B5D58D1ED0525900AF8BDB /* iOS */, 147 | B44E856F1968A9F900148E42 /* Products */, 148 | ); 149 | sourceTree = ""; 150 | }; 151 | B44E856F1968A9F900148E42 /* Products */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | B4A39420196B1037002C7324 /* FSKModem.framework */, 155 | B4B5D58C1ED0525900AF8BDB /* FSKModem.framework */, 156 | ); 157 | name = Products; 158 | sourceTree = ""; 159 | }; 160 | B44E85701968A9F900148E42 /* FSKModem */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | B489B41C196AD1A6002100DC /* ProtocolCoding */, 164 | B489B419196AD163002100DC /* Queue */, 165 | B489B41A196AD173002100DC /* AudioStreams */, 166 | B489B41B196AD18E002100DC /* Recognizer */, 167 | B489B41D196AD1C5002100DC /* AudioSource */, 168 | B44E85891968AA5B00148E42 /* JMFSKModem.h */, 169 | B44E858A1968AA5B00148E42 /* JMFSKModem.m */, 170 | B44E85B11968ADAB00148E42 /* JMPrefix.pch */, 171 | B41254711969BE15009D9228 /* JMFSKModemConfiguration.h */, 172 | B41254721969BE15009D9228 /* JMFSKModemConfiguration.m */, 173 | B4A393B5196AE1E3002C7324 /* JMFSKModemDelegate.h */, 174 | ); 175 | path = FSKModem; 176 | sourceTree = ""; 177 | }; 178 | B489B419196AD163002100DC /* Queue */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | B44E85911968AA5B00148E42 /* JMQueue.h */, 182 | B44E85921968AA5B00148E42 /* JMQueue.m */, 183 | B44E85931968AA5B00148E42 /* JMQueueNode.h */, 184 | B44E85941968AA5B00148E42 /* JMQueueNode.m */, 185 | ); 186 | name = Queue; 187 | sourceTree = ""; 188 | }; 189 | B489B41A196AD173002100DC /* AudioStreams */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | B44E859A1968AA6E00148E42 /* JMAudioInputStream.h */, 193 | B44E859B1968AA6E00148E42 /* JMAudioInputStream.m */, 194 | B44E859C1968AA6E00148E42 /* JMAudioOutputStream.h */, 195 | B44E859D1968AA6E00148E42 /* JMAudioOutputStream.m */, 196 | B44E859E1968AA6E00148E42 /* JMAudioStream.h */, 197 | B44E859F1968AA6E00148E42 /* JMAudioStream.m */, 198 | ); 199 | name = AudioStreams; 200 | sourceTree = ""; 201 | }; 202 | B489B41B196AD18E002100DC /* Recognizer */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | B44E85A01968AA6E00148E42 /* JMFSKRecognizer.h */, 206 | B44E85A11968AA6E00148E42 /* JMFSKRecognizer.m */, 207 | B44E85A51968AA6E00148E42 /* JMPatternRecognizer.h */, 208 | B44E858B1968AA5B00148E42 /* JMFSKRecognizerDelegate.h */, 209 | ); 210 | name = Recognizer; 211 | sourceTree = ""; 212 | }; 213 | B489B41C196AD1A6002100DC /* ProtocolCoding */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | B44E858C1968AA5B00148E42 /* JMProtocolDecoder.h */, 217 | B44E858D1968AA5B00148E42 /* JMProtocolDecoder.m */, 218 | B44E858E1968AA5B00148E42 /* JMProtocolDecoderDelegate.h */, 219 | B44E858F1968AA5B00148E42 /* JMProtocolEncoder.h */, 220 | B44E85901968AA5B00148E42 /* JMProtocolEncoder.m */, 221 | ); 222 | name = ProtocolCoding; 223 | sourceTree = ""; 224 | }; 225 | B489B41D196AD1C5002100DC /* AudioSource */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | B44E85A21968AA6E00148E42 /* JMFSKSerialGenerator.h */, 229 | B44E85A31968AA6E00148E42 /* JMFSKSerialGenerator.m */, 230 | B44E85881968AA5B00148E42 /* JMAudioSource.h */, 231 | ); 232 | name = AudioSource; 233 | sourceTree = ""; 234 | }; 235 | B4A39421196B1037002C7324 /* OSX */ = { 236 | isa = PBXGroup; 237 | children = ( 238 | B4A39424196B1037002C7324 /* FSKModem.h */, 239 | B4A39422196B1037002C7324 /* Supporting Files */, 240 | ); 241 | path = OSX; 242 | sourceTree = ""; 243 | }; 244 | B4A39422196B1037002C7324 /* Supporting Files */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | B4A39423196B1037002C7324 /* Info.plist */, 248 | ); 249 | name = "Supporting Files"; 250 | sourceTree = ""; 251 | }; 252 | B4B5D58D1ED0525900AF8BDB /* iOS */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | B4B5D58E1ED0525900AF8BDB /* FSKModem.h */, 256 | B4B5D58F1ED0525900AF8BDB /* Info.plist */, 257 | ); 258 | path = iOS; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXGroup section */ 262 | 263 | /* Begin PBXHeadersBuildPhase section */ 264 | B4A3941D196B1037002C7324 /* Headers */ = { 265 | isa = PBXHeadersBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | B4A39425196B1037002C7324 /* FSKModem.h in Headers */, 269 | B4A39448196B10A2002C7324 /* JMProtocolDecoder.h in Headers */, 270 | B4A39449196B10A2002C7324 /* JMProtocolDecoderDelegate.h in Headers */, 271 | B4A3944A196B10A2002C7324 /* JMProtocolEncoder.h in Headers */, 272 | B4A3944B196B10A2002C7324 /* JMQueue.h in Headers */, 273 | B4A3944C196B10A2002C7324 /* JMQueueNode.h in Headers */, 274 | B4A3944D196B10A2002C7324 /* JMAudioInputStream.h in Headers */, 275 | B4A3944E196B10A2002C7324 /* JMAudioOutputStream.h in Headers */, 276 | B4A3944F196B10A2002C7324 /* JMAudioStream.h in Headers */, 277 | B4A39450196B10A2002C7324 /* JMFSKRecognizer.h in Headers */, 278 | B4A39451196B10A2002C7324 /* JMPatternRecognizer.h in Headers */, 279 | B4A39452196B10A2002C7324 /* JMFSKRecognizerDelegate.h in Headers */, 280 | B4A39453196B10A2002C7324 /* JMFSKSerialGenerator.h in Headers */, 281 | B4A39454196B10A2002C7324 /* JMAudioSource.h in Headers */, 282 | B4A39455196B10A2002C7324 /* JMFSKModem.h in Headers */, 283 | B4A39456196B10A2002C7324 /* JMFSKModemConfiguration.h in Headers */, 284 | B4A39457196B10A2002C7324 /* JMFSKModemDelegate.h in Headers */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | B4B5D5891ED0525900AF8BDB /* Headers */ = { 289 | isa = PBXHeadersBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | B4B5D5901ED0525900AF8BDB /* FSKModem.h in Headers */, 293 | B4B5D59F1ED0529B00AF8BDB /* JMProtocolDecoder.h in Headers */, 294 | B4B5D5A01ED0529B00AF8BDB /* JMProtocolDecoderDelegate.h in Headers */, 295 | B4B5D5A11ED0529B00AF8BDB /* JMProtocolEncoder.h in Headers */, 296 | B4B5D5A21ED0529B00AF8BDB /* JMQueue.h in Headers */, 297 | B4B5D5A31ED0529B00AF8BDB /* JMQueueNode.h in Headers */, 298 | B4B5D5A41ED0529B00AF8BDB /* JMAudioInputStream.h in Headers */, 299 | B4B5D5A51ED0529B00AF8BDB /* JMAudioOutputStream.h in Headers */, 300 | B4B5D5A61ED0529B00AF8BDB /* JMAudioStream.h in Headers */, 301 | B4B5D5A71ED0529B00AF8BDB /* JMFSKRecognizer.h in Headers */, 302 | B4B5D5A81ED0529B00AF8BDB /* JMPatternRecognizer.h in Headers */, 303 | B4B5D5A91ED0529B00AF8BDB /* JMFSKRecognizerDelegate.h in Headers */, 304 | B4B5D5AA1ED0529B00AF8BDB /* JMFSKSerialGenerator.h in Headers */, 305 | B4B5D5AB1ED0529B00AF8BDB /* JMAudioSource.h in Headers */, 306 | B4B5D5AC1ED0529B00AF8BDB /* JMFSKModem.h in Headers */, 307 | B4B5D5AD1ED0529B00AF8BDB /* JMFSKModemConfiguration.h in Headers */, 308 | B4B5D5AE1ED0529B00AF8BDB /* JMFSKModemDelegate.h in Headers */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXHeadersBuildPhase section */ 313 | 314 | /* Begin PBXNativeTarget section */ 315 | B4A3941F196B1037002C7324 /* OSX */ = { 316 | isa = PBXNativeTarget; 317 | buildConfigurationList = B4A39433196B1037002C7324 /* Build configuration list for PBXNativeTarget "OSX" */; 318 | buildPhases = ( 319 | B4A3941B196B1037002C7324 /* Sources */, 320 | B4A3941C196B1037002C7324 /* Frameworks */, 321 | B4A3941D196B1037002C7324 /* Headers */, 322 | ); 323 | buildRules = ( 324 | ); 325 | dependencies = ( 326 | ); 327 | name = OSX; 328 | productName = FSKModem; 329 | productReference = B4A39420196B1037002C7324 /* FSKModem.framework */; 330 | productType = "com.apple.product-type.framework"; 331 | }; 332 | B4B5D58B1ED0525900AF8BDB /* iOS */ = { 333 | isa = PBXNativeTarget; 334 | buildConfigurationList = B4B5D5931ED0525900AF8BDB /* Build configuration list for PBXNativeTarget "iOS" */; 335 | buildPhases = ( 336 | B4B5D5871ED0525900AF8BDB /* Sources */, 337 | B4B5D5881ED0525900AF8BDB /* Frameworks */, 338 | B4B5D5891ED0525900AF8BDB /* Headers */, 339 | B4B5D58A1ED0525900AF8BDB /* Resources */, 340 | ); 341 | buildRules = ( 342 | ); 343 | dependencies = ( 344 | ); 345 | name = iOS; 346 | productName = iOS; 347 | productReference = B4B5D58C1ED0525900AF8BDB /* FSKModem.framework */; 348 | productType = "com.apple.product-type.framework"; 349 | }; 350 | /* End PBXNativeTarget section */ 351 | 352 | /* Begin PBXProject section */ 353 | B44E85661968A9F900148E42 /* Project object */ = { 354 | isa = PBXProject; 355 | attributes = { 356 | CLASSPREFIX = JM; 357 | LastUpgradeCheck = 0820; 358 | ORGANIZATIONNAME = "Jens Meder"; 359 | TargetAttributes = { 360 | B4A3941F196B1037002C7324 = { 361 | CreatedOnToolsVersion = 6.0; 362 | }; 363 | B4B5D58B1ED0525900AF8BDB = { 364 | CreatedOnToolsVersion = 8.2.1; 365 | ProvisioningStyle = Automatic; 366 | }; 367 | }; 368 | }; 369 | buildConfigurationList = B44E85691968A9F900148E42 /* Build configuration list for PBXProject "FSKModem" */; 370 | compatibilityVersion = "Xcode 3.2"; 371 | developmentRegion = English; 372 | hasScannedForEncodings = 0; 373 | knownRegions = ( 374 | en, 375 | ); 376 | mainGroup = B44E85651968A9F900148E42; 377 | productRefGroup = B44E856F1968A9F900148E42 /* Products */; 378 | projectDirPath = ""; 379 | projectRoot = ""; 380 | targets = ( 381 | B4A3941F196B1037002C7324 /* OSX */, 382 | B4B5D58B1ED0525900AF8BDB /* iOS */, 383 | ); 384 | }; 385 | /* End PBXProject section */ 386 | 387 | /* Begin PBXResourcesBuildPhase section */ 388 | B4B5D58A1ED0525900AF8BDB /* Resources */ = { 389 | isa = PBXResourcesBuildPhase; 390 | buildActionMask = 2147483647; 391 | files = ( 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | /* End PBXResourcesBuildPhase section */ 396 | 397 | /* Begin PBXSourcesBuildPhase section */ 398 | B4A3941B196B1037002C7324 /* Sources */ = { 399 | isa = PBXSourcesBuildPhase; 400 | buildActionMask = 2147483647; 401 | files = ( 402 | B4A3943D196B108E002C7324 /* JMProtocolDecoder.m in Sources */, 403 | B4A3943E196B108E002C7324 /* JMProtocolEncoder.m in Sources */, 404 | B4A3943F196B108E002C7324 /* JMQueue.m in Sources */, 405 | B4A39440196B108E002C7324 /* JMQueueNode.m in Sources */, 406 | B4A39441196B108E002C7324 /* JMAudioInputStream.m in Sources */, 407 | B4A39442196B108E002C7324 /* JMAudioOutputStream.m in Sources */, 408 | B4A39443196B108E002C7324 /* JMAudioStream.m in Sources */, 409 | B4A39444196B108E002C7324 /* JMFSKRecognizer.m in Sources */, 410 | B4A39445196B108E002C7324 /* JMFSKSerialGenerator.m in Sources */, 411 | B4A39446196B108E002C7324 /* JMFSKModem.m in Sources */, 412 | B4A39447196B108E002C7324 /* JMFSKModemConfiguration.m in Sources */, 413 | ); 414 | runOnlyForDeploymentPostprocessing = 0; 415 | }; 416 | B4B5D5871ED0525900AF8BDB /* Sources */ = { 417 | isa = PBXSourcesBuildPhase; 418 | buildActionMask = 2147483647; 419 | files = ( 420 | B4B5D5941ED0527C00AF8BDB /* JMProtocolDecoder.m in Sources */, 421 | B4B5D5951ED0527C00AF8BDB /* JMProtocolEncoder.m in Sources */, 422 | B4B5D5961ED0527C00AF8BDB /* JMQueue.m in Sources */, 423 | B4B5D5971ED0527C00AF8BDB /* JMQueueNode.m in Sources */, 424 | B4B5D5981ED0527C00AF8BDB /* JMAudioInputStream.m in Sources */, 425 | B4B5D5991ED0527C00AF8BDB /* JMAudioOutputStream.m in Sources */, 426 | B4B5D59A1ED0527C00AF8BDB /* JMAudioStream.m in Sources */, 427 | B4B5D59B1ED0527C00AF8BDB /* JMFSKRecognizer.m in Sources */, 428 | B4B5D59C1ED0527C00AF8BDB /* JMFSKSerialGenerator.m in Sources */, 429 | B4B5D59D1ED0527C00AF8BDB /* JMFSKModem.m in Sources */, 430 | B4B5D59E1ED0527C00AF8BDB /* JMFSKModemConfiguration.m in Sources */, 431 | ); 432 | runOnlyForDeploymentPostprocessing = 0; 433 | }; 434 | /* End PBXSourcesBuildPhase section */ 435 | 436 | /* Begin XCBuildConfiguration section */ 437 | B44E85801968A9F900148E42 /* Debug */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | ALWAYS_SEARCH_USER_PATHS = NO; 441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 442 | CLANG_CXX_LIBRARY = "libc++"; 443 | CLANG_ENABLE_MODULES = YES; 444 | CLANG_ENABLE_OBJC_ARC = YES; 445 | CLANG_WARN_BOOL_CONVERSION = YES; 446 | CLANG_WARN_CONSTANT_CONVERSION = YES; 447 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 448 | CLANG_WARN_EMPTY_BODY = YES; 449 | CLANG_WARN_ENUM_CONVERSION = YES; 450 | CLANG_WARN_INFINITE_RECURSION = YES; 451 | CLANG_WARN_INT_CONVERSION = YES; 452 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CLANG_WARN_UNREACHABLE_CODE = YES; 455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 456 | COPY_PHASE_STRIP = NO; 457 | ENABLE_STRICT_OBJC_MSGSEND = YES; 458 | ENABLE_TESTABILITY = YES; 459 | GCC_C_LANGUAGE_STANDARD = gnu99; 460 | GCC_DYNAMIC_NO_PIC = NO; 461 | GCC_NO_COMMON_BLOCKS = YES; 462 | GCC_OPTIMIZATION_LEVEL = 0; 463 | GCC_PREPROCESSOR_DEFINITIONS = ( 464 | "DEBUG=1", 465 | "$(inherited)", 466 | ); 467 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 468 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 469 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 470 | GCC_WARN_UNDECLARED_SELECTOR = YES; 471 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 472 | GCC_WARN_UNUSED_FUNCTION = YES; 473 | GCC_WARN_UNUSED_VARIABLE = YES; 474 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 475 | METAL_ENABLE_DEBUG_INFO = YES; 476 | ONLY_ACTIVE_ARCH = YES; 477 | SDKROOT = iphoneos; 478 | }; 479 | name = Debug; 480 | }; 481 | B44E85811968A9F900148E42 /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ALWAYS_SEARCH_USER_PATHS = NO; 485 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 486 | CLANG_CXX_LIBRARY = "libc++"; 487 | CLANG_ENABLE_MODULES = YES; 488 | CLANG_ENABLE_OBJC_ARC = YES; 489 | CLANG_WARN_BOOL_CONVERSION = YES; 490 | CLANG_WARN_CONSTANT_CONVERSION = YES; 491 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 492 | CLANG_WARN_EMPTY_BODY = YES; 493 | CLANG_WARN_ENUM_CONVERSION = YES; 494 | CLANG_WARN_INFINITE_RECURSION = YES; 495 | CLANG_WARN_INT_CONVERSION = YES; 496 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 497 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 498 | CLANG_WARN_UNREACHABLE_CODE = YES; 499 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 500 | COPY_PHASE_STRIP = YES; 501 | ENABLE_NS_ASSERTIONS = NO; 502 | ENABLE_STRICT_OBJC_MSGSEND = YES; 503 | GCC_C_LANGUAGE_STANDARD = gnu99; 504 | GCC_NO_COMMON_BLOCKS = YES; 505 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 506 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 507 | GCC_WARN_UNDECLARED_SELECTOR = YES; 508 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 509 | GCC_WARN_UNUSED_FUNCTION = YES; 510 | GCC_WARN_UNUSED_VARIABLE = YES; 511 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 512 | METAL_ENABLE_DEBUG_INFO = NO; 513 | SDKROOT = iphoneos; 514 | VALIDATE_PRODUCT = YES; 515 | }; 516 | name = Release; 517 | }; 518 | B4A39434196B1037002C7324 /* Debug */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | COMBINE_HIDPI_IMAGES = YES; 522 | CURRENT_PROJECT_VERSION = 1; 523 | DEFINES_MODULE = YES; 524 | DYLIB_COMPATIBILITY_VERSION = 1; 525 | DYLIB_CURRENT_VERSION = 1; 526 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 527 | FRAMEWORK_VERSION = A; 528 | GCC_PREFIX_HEADER = FSKModem/JMPrefix.pch; 529 | GCC_PREPROCESSOR_DEFINITIONS = ( 530 | "DEBUG=1", 531 | "$(inherited)", 532 | ); 533 | INFOPLIST_FILE = "$(SRCROOT)/OSX/Info.plist"; 534 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 535 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 536 | MACOSX_DEPLOYMENT_TARGET = 10.7; 537 | METAL_ENABLE_DEBUG_INFO = YES; 538 | PRODUCT_BUNDLE_IDENTIFIER = "de.jensmeder.${PRODUCT_NAME:rfc1034identifier}"; 539 | PRODUCT_NAME = FSKModem; 540 | SDKROOT = macosx; 541 | SKIP_INSTALL = YES; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | VERSION_INFO_PREFIX = ""; 544 | }; 545 | name = Debug; 546 | }; 547 | B4A39435196B1037002C7324 /* Release */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | COMBINE_HIDPI_IMAGES = YES; 551 | CURRENT_PROJECT_VERSION = 1; 552 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 553 | DEFINES_MODULE = YES; 554 | DYLIB_COMPATIBILITY_VERSION = 1; 555 | DYLIB_CURRENT_VERSION = 1; 556 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 557 | FRAMEWORK_VERSION = A; 558 | GCC_PREFIX_HEADER = FSKModem/JMPrefix.pch; 559 | INFOPLIST_FILE = "$(SRCROOT)/OSX/Info.plist"; 560 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 562 | MACOSX_DEPLOYMENT_TARGET = 10.7; 563 | METAL_ENABLE_DEBUG_INFO = NO; 564 | PRODUCT_BUNDLE_IDENTIFIER = "de.jensmeder.${PRODUCT_NAME:rfc1034identifier}"; 565 | PRODUCT_NAME = FSKModem; 566 | SDKROOT = macosx; 567 | SKIP_INSTALL = YES; 568 | VERSIONING_SYSTEM = "apple-generic"; 569 | VERSION_INFO_PREFIX = ""; 570 | }; 571 | name = Release; 572 | }; 573 | B4B5D5911ED0525900AF8BDB /* Debug */ = { 574 | isa = XCBuildConfiguration; 575 | buildSettings = { 576 | CLANG_ANALYZER_NONNULL = YES; 577 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 578 | CLANG_WARN_INFINITE_RECURSION = YES; 579 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 580 | CODE_SIGN_IDENTITY = ""; 581 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 582 | CURRENT_PROJECT_VERSION = 1; 583 | DEBUG_INFORMATION_FORMAT = dwarf; 584 | DEFINES_MODULE = YES; 585 | DYLIB_COMPATIBILITY_VERSION = 1; 586 | DYLIB_CURRENT_VERSION = 1; 587 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 588 | ENABLE_TESTABILITY = YES; 589 | GCC_NO_COMMON_BLOCKS = YES; 590 | INFOPLIST_FILE = iOS/Info.plist; 591 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 592 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 593 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 594 | MTL_ENABLE_DEBUG_INFO = YES; 595 | PRODUCT_BUNDLE_IDENTIFIER = de.jensmeder.iOS; 596 | PRODUCT_NAME = FSKModem; 597 | SKIP_INSTALL = YES; 598 | TARGETED_DEVICE_FAMILY = "1,2"; 599 | VERSIONING_SYSTEM = "apple-generic"; 600 | VERSION_INFO_PREFIX = ""; 601 | }; 602 | name = Debug; 603 | }; 604 | B4B5D5921ED0525900AF8BDB /* Release */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | CLANG_ANALYZER_NONNULL = YES; 608 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 609 | CLANG_WARN_INFINITE_RECURSION = YES; 610 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 611 | CODE_SIGN_IDENTITY = ""; 612 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 613 | COPY_PHASE_STRIP = NO; 614 | CURRENT_PROJECT_VERSION = 1; 615 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 616 | DEFINES_MODULE = YES; 617 | DYLIB_COMPATIBILITY_VERSION = 1; 618 | DYLIB_CURRENT_VERSION = 1; 619 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 620 | GCC_NO_COMMON_BLOCKS = YES; 621 | INFOPLIST_FILE = iOS/Info.plist; 622 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 623 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 624 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 625 | MTL_ENABLE_DEBUG_INFO = NO; 626 | PRODUCT_BUNDLE_IDENTIFIER = de.jensmeder.iOS; 627 | PRODUCT_NAME = FSKModem; 628 | SKIP_INSTALL = YES; 629 | TARGETED_DEVICE_FAMILY = "1,2"; 630 | VERSIONING_SYSTEM = "apple-generic"; 631 | VERSION_INFO_PREFIX = ""; 632 | }; 633 | name = Release; 634 | }; 635 | /* End XCBuildConfiguration section */ 636 | 637 | /* Begin XCConfigurationList section */ 638 | B44E85691968A9F900148E42 /* Build configuration list for PBXProject "FSKModem" */ = { 639 | isa = XCConfigurationList; 640 | buildConfigurations = ( 641 | B44E85801968A9F900148E42 /* Debug */, 642 | B44E85811968A9F900148E42 /* Release */, 643 | ); 644 | defaultConfigurationIsVisible = 0; 645 | defaultConfigurationName = Release; 646 | }; 647 | B4A39433196B1037002C7324 /* Build configuration list for PBXNativeTarget "OSX" */ = { 648 | isa = XCConfigurationList; 649 | buildConfigurations = ( 650 | B4A39434196B1037002C7324 /* Debug */, 651 | B4A39435196B1037002C7324 /* Release */, 652 | ); 653 | defaultConfigurationIsVisible = 0; 654 | defaultConfigurationName = Release; 655 | }; 656 | B4B5D5931ED0525900AF8BDB /* Build configuration list for PBXNativeTarget "iOS" */ = { 657 | isa = XCConfigurationList; 658 | buildConfigurations = ( 659 | B4B5D5911ED0525900AF8BDB /* Debug */, 660 | B4B5D5921ED0525900AF8BDB /* Release */, 661 | ); 662 | defaultConfigurationIsVisible = 0; 663 | defaultConfigurationName = Release; 664 | }; 665 | /* End XCConfigurationList section */ 666 | }; 667 | rootObject = B44E85661968A9F900148E42 /* Project object */; 668 | } 669 | -------------------------------------------------------------------------------- /FSKModem/FSKModem.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioInputStream.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMAudioStream.h" 24 | #import "JMPatternRecognizer.h" 25 | 26 | @interface JMAudioInputStream : JMAudioStream 27 | 28 | - (void) addRecognizer: (id)recognizer; 29 | - (void) removeRecognizer:(id) recognizer; 30 | 31 | - (void) record; 32 | - (void) stop; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioInputStream.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMAudioInputStream.h" 24 | 25 | static const int EDGE_DIFF_THRESHOLD = 16384; 26 | static const int EDGE_SLOPE_THRESHOLD = 256; 27 | static const int EDGE_MAX_WIDTH = 8; 28 | 29 | static const int BUFFER_BYTE_SIZE = 4096; 30 | 31 | static const int NUMBER_OF_AUDIO_BUFFERS = 20; 32 | 33 | typedef struct 34 | { 35 | int lastFrame; 36 | int lastEdgeSign; 37 | unsigned int lastEdgeWidth; 38 | int edgeSign; 39 | int edgeDiff; 40 | unsigned int edgeWidth; 41 | unsigned int plateauWidth; 42 | } 43 | JMAnalyzerData; 44 | 45 | @interface JMAudioInputStream () 46 | 47 | @property (readonly) JMAnalyzerData* pulseData; 48 | @property (readonly) AudioStreamBasicDescription* audioFormat; 49 | 50 | - (void) edge: (int)height width:(unsigned)width interval:(unsigned)interval; 51 | - (void) idle: (unsigned)samples; 52 | - (void) reset; 53 | 54 | @end 55 | 56 | static int analyze( SInt16 *inputBuffer, unsigned long framesPerBuffer, JMAudioInputStream* inputStream) 57 | { 58 | JMAnalyzerData *data = inputStream.pulseData; 59 | int lastFrame = data->lastFrame; 60 | 61 | unsigned idleInterval = data->plateauWidth + data->lastEdgeWidth + data->edgeWidth; 62 | 63 | for (int i = 0; i < framesPerBuffer; i++) 64 | { 65 | int thisFrame = inputBuffer[i]; 66 | int diff = thisFrame - lastFrame; 67 | 68 | int sign = 0; 69 | if (diff > EDGE_SLOPE_THRESHOLD) 70 | { 71 | // Signal is rising 72 | sign = 1; 73 | } 74 | else if(-diff > EDGE_SLOPE_THRESHOLD) 75 | { 76 | // Signal is falling 77 | sign = -1; 78 | } 79 | 80 | // If the signal has changed direction or the edge detector has gone on for too long, 81 | // then close out the current edge detection phase 82 | if(data->edgeSign != sign || (data->edgeSign && data->edgeWidth + 1 > EDGE_MAX_WIDTH)) 83 | { 84 | if(abs(data->edgeDiff) > EDGE_DIFF_THRESHOLD && data->lastEdgeSign != data->edgeSign) 85 | { 86 | // The edge is significant 87 | [inputStream edge:data->edgeDiff width:data->edgeWidth interval:data->plateauWidth + data->edgeWidth]; 88 | 89 | // Save the edge 90 | data->lastEdgeSign = data->edgeSign; 91 | data->lastEdgeWidth = data->edgeWidth; 92 | 93 | // Reset the plateau 94 | data->plateauWidth = 0; 95 | idleInterval = data->edgeWidth; 96 | } 97 | else 98 | { 99 | // The edge is rejected; add the edge data to the plateau 100 | data->plateauWidth += data->edgeWidth; 101 | } 102 | 103 | data->edgeSign = sign; 104 | data->edgeWidth = 0; 105 | data->edgeDiff = 0; 106 | } 107 | 108 | if(data->edgeSign) 109 | { 110 | // Sample may be part of an edge 111 | data->edgeWidth++; 112 | data->edgeDiff += diff; 113 | } 114 | else 115 | { 116 | // Sample is part of a plateau 117 | data->plateauWidth++; 118 | } 119 | idleInterval++; 120 | 121 | data->lastFrame = lastFrame = thisFrame; 122 | 123 | int idleCheckPeriod = inputStream.audioFormat->mSampleRate / 100; 124 | 125 | if ( (idleInterval % idleCheckPeriod) == 0 ) 126 | { 127 | [inputStream idle:idleInterval]; 128 | } 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | 135 | static void recordingCallback (void* inUserData, AudioQueueRef inAQ,AudioQueueBufferRef inBuffer, const AudioTimeStamp* inStartTime, UInt32 inNumberPacketDescriptions, const AudioStreamPacketDescription *inPacketDescs) 136 | { 137 | JMAudioInputStream *inputStream = (__bridge JMAudioInputStream*) inUserData; 138 | 139 | // if there is audio data, analyze it 140 | if (inNumberPacketDescriptions > 0) 141 | { 142 | analyze((SInt16*)inBuffer->mAudioData, inBuffer->mAudioDataByteSize / inputStream.audioFormat->mBytesPerFrame, inputStream); 143 | } 144 | 145 | // if not stopping, re-enqueue the buffer so that it can be filled again 146 | if (inputStream.running) 147 | { 148 | AudioQueueEnqueueBuffer (inAQ, inBuffer, 0, NULL); 149 | } 150 | } 151 | 152 | 153 | @implementation JMAudioInputStream 154 | { 155 | @private 156 | 157 | JMAnalyzerData _pulseData; 158 | NSMutableArray* _recognizers; 159 | } 160 | 161 | - (JMAnalyzerData*) pulseData 162 | { 163 | return &_pulseData; 164 | } 165 | 166 | -(AudioStreamBasicDescription *)audioFormat 167 | { 168 | return &_audioFormat; 169 | } 170 | 171 | - (instancetype) initWithAudioFormat:(AudioStreamBasicDescription)format 172 | { 173 | self = [super initWithAudioFormat:format]; 174 | 175 | if (self) 176 | { 177 | _recognizers = [[NSMutableArray alloc] init]; 178 | 179 | AudioQueueNewInput (&_audioFormat, recordingCallback, (__bridge void *)(self), NULL, NULL, 0, &_queueObject); 180 | } 181 | 182 | return self; 183 | } 184 | 185 | - (void) addRecognizer: (id)recognizer 186 | { 187 | [_recognizers addObject:recognizer]; 188 | } 189 | 190 | -(void)removeRecognizer:(id)recognizer 191 | { 192 | [_recognizers removeObject:recognizer]; 193 | } 194 | 195 | - (void) record 196 | { 197 | [self setupRecording]; 198 | 199 | [self reset]; 200 | 201 | AudioQueueStart (_queueObject, NULL); 202 | } 203 | 204 | 205 | - (void) stop 206 | { 207 | AudioQueueStop (_queueObject, TRUE); 208 | 209 | [self reset]; 210 | } 211 | 212 | 213 | - (void) setupRecording 214 | { 215 | for (int bufferIndex = 0; bufferIndex < NUMBER_OF_AUDIO_BUFFERS; ++bufferIndex) 216 | { 217 | AudioQueueBufferRef bufferRef; 218 | 219 | AudioQueueAllocateBuffer (_queueObject, BUFFER_BYTE_SIZE, &bufferRef); 220 | 221 | AudioQueueEnqueueBuffer (_queueObject, bufferRef, 0, NULL); 222 | } 223 | } 224 | 225 | - (void) idle: (unsigned)samples 226 | { 227 | UInt64 nsInterval = [self convertToNanoSeconds:samples]; 228 | for (id recognizer in _recognizers) 229 | { 230 | [recognizer idle:nsInterval]; 231 | } 232 | } 233 | 234 | -(UInt64) convertToNanoSeconds:(UInt64) interval 235 | { 236 | return interval * NSEC_PER_SEC / _audioFormat.mSampleRate; 237 | } 238 | 239 | - (void) edge: (int)height width:(unsigned)width interval:(unsigned)interval 240 | { 241 | UInt64 nsInterval = [self convertToNanoSeconds:interval]; 242 | UInt64 nsWidth = [self convertToNanoSeconds:width]; 243 | 244 | for (id recognizer in _recognizers) 245 | { 246 | [recognizer edge:height width:nsWidth interval:nsInterval]; 247 | } 248 | } 249 | 250 | - (void) reset 251 | { 252 | [_recognizers makeObjectsPerformSelector:@selector(reset)]; 253 | 254 | memset(&_pulseData, 0, sizeof(_pulseData)); 255 | } 256 | 257 | - (void) dealloc 258 | { 259 | AudioQueueDispose (_queueObject, TRUE); 260 | } 261 | 262 | @end 263 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioOutputStream.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMAudioStream.h" 24 | #import "JMAudioSource.h" 25 | 26 | 27 | @interface JMAudioOutputStream : JMAudioStream 28 | 29 | @property (nonatomic, strong) id audioSource; 30 | 31 | - (void) play; 32 | - (void) stop; 33 | - (void) pause; 34 | - (void) resume; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioOutputStream.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | #import "JMAudioOutputStream.h" 25 | 26 | static const int NUMBER_AUDIO_DATA_BUFFERS = 3; 27 | static const int BUFFER_BYTE_SIZE = 1024; 28 | 29 | @interface JMAudioOutputStream () 30 | 31 | @property (readonly) AudioStreamPacketDescription* packetDescriptions; 32 | @property (readonly) BOOL stopped; 33 | @property (readonly) BOOL audioPlayerShouldStopImmediately; 34 | @property (readonly) UInt32 bufferByteSize; 35 | @property (readonly) UInt32 bufferPacketCount; 36 | 37 | @end 38 | 39 | 40 | static void playbackCallback (void* inUserData, AudioQueueRef inAudioQueue, AudioQueueBufferRef bufferReference) 41 | { 42 | JMAudioOutputStream *player = (__bridge JMAudioOutputStream*) inUserData; 43 | if (player.stopped) 44 | { 45 | return; 46 | } 47 | [player.audioSource outputStream:player fillBuffer:bufferReference->mAudioData bufferSize:player.bufferByteSize]; 48 | 49 | bufferReference->mAudioDataByteSize = player.bufferByteSize; 50 | 51 | AudioQueueEnqueueBuffer (inAudioQueue, bufferReference, player.bufferPacketCount, player.packetDescriptions); 52 | } 53 | 54 | @implementation JMAudioOutputStream 55 | { 56 | @private 57 | 58 | AudioQueueBufferRef buffers[NUMBER_AUDIO_DATA_BUFFERS]; 59 | } 60 | 61 | - (instancetype) initWithAudioFormat:(AudioStreamBasicDescription)format 62 | { 63 | self = [super initWithAudioFormat:format]; 64 | 65 | if (self) 66 | { 67 | [self setupPlaybackAudioQueueObject]; 68 | 69 | _stopped = NO; 70 | _audioPlayerShouldStopImmediately = NO; 71 | _bufferByteSize = BUFFER_BYTE_SIZE; 72 | } 73 | 74 | return self; 75 | } 76 | 77 | - (void) setupPlaybackAudioQueueObject 78 | { 79 | AudioQueueNewOutput (&_audioFormat, playbackCallback, (__bridge void *)(self), nil, nil, 0, &_queueObject); 80 | 81 | AudioQueueSetParameter (_queueObject, kAudioQueueParam_Volume, 1.0f); 82 | } 83 | 84 | - (void) setupAudioQueueBuffers 85 | { 86 | // prime the queue with some data before starting 87 | // allocate and enqueue buffers 88 | for (int bufferIndex = 0; bufferIndex < NUMBER_AUDIO_DATA_BUFFERS; ++bufferIndex) 89 | { 90 | AudioQueueAllocateBuffer (_queueObject, _bufferByteSize, &buffers[bufferIndex]); 91 | 92 | playbackCallback ((__bridge void *)(self), _queueObject, buffers[bufferIndex]); 93 | 94 | if (_stopped) 95 | { 96 | break; 97 | } 98 | } 99 | } 100 | 101 | - (void) play 102 | { 103 | [self setupAudioQueueBuffers]; 104 | 105 | AudioQueueStart (_queueObject, NULL); 106 | } 107 | 108 | - (void) stop 109 | { 110 | AudioQueueStop (_queueObject, self.audioPlayerShouldStopImmediately); 111 | } 112 | 113 | 114 | - (void) pause 115 | { 116 | AudioQueuePause (_queueObject); 117 | } 118 | 119 | 120 | - (void) resume 121 | { 122 | AudioQueueStart (_queueObject, NULL); 123 | } 124 | 125 | 126 | - (void) dealloc 127 | { 128 | AudioQueueDispose (_queueObject,YES); 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioSource.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @class JMAudioOutputStream; 26 | 27 | @protocol JMAudioSource 28 | 29 | -(void) outputStream:(JMAudioOutputStream*)stream fillBuffer:(void*)buffer bufferSize:(NSUInteger)bufferSize; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioStream.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | @interface JMAudioStream : NSObject 26 | { 27 | @protected 28 | 29 | AudioQueueRef _queueObject; 30 | AudioStreamBasicDescription _audioFormat; 31 | } 32 | 33 | @property (NS_NONATOMIC_IOSONLY, getter=isRunning, readonly) BOOL running; 34 | 35 | -(instancetype)initWithAudioFormat:(AudioStreamBasicDescription)format; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMAudioStream.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMAudioStream.h" 24 | 25 | 26 | @implementation JMAudioStream 27 | 28 | -(instancetype)initWithAudioFormat:(AudioStreamBasicDescription)format 29 | { 30 | self = [super init]; 31 | 32 | if (self) 33 | { 34 | _audioFormat = format; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | - (BOOL) isRunning 41 | { 42 | UInt32 outData; 43 | UInt32 propertySize = sizeof (UInt32); 44 | OSStatus result; 45 | 46 | result = AudioQueueGetProperty (_queueObject, kAudioQueueProperty_IsRunning, &outData, &propertySize); 47 | 48 | if (result != noErr) 49 | { 50 | return NO; 51 | } 52 | 53 | return outData; 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKModem.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | #import 25 | #import "JMFSKModemConfiguration.h" 26 | #import "JMFSKModemDelegate.h" 27 | 28 | @interface JMFSKModem : NSObject 29 | 30 | @property (nonatomic, weak) id delegate; 31 | @property (readonly) BOOL connected; 32 | 33 | -(instancetype)initWithConfiguration:(JMFSKModemConfiguration*)configuration; 34 | 35 | -(void) connect; 36 | -(void) connect:(void (^)(BOOL error))completion; 37 | 38 | -(void) disconnect; 39 | -(void) disconnect:(void (^)(BOOL error))completion; 40 | 41 | -(void) sendData:(NSData*)data; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKModem.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMFSKModem.h" 24 | #import "JMAudioInputStream.h" 25 | #import "JMFSKSerialGenerator.h" 26 | #import "JMAudioOutputStream.h" 27 | #import "JMAudioInputStream.h" 28 | #import "JMFSKRecognizer.h" 29 | #import 30 | #import "JMProtocolDecoder.h" 31 | #import "JMProtocolDecoderDelegate.h" 32 | #import "JMProtocolEncoder.h" 33 | 34 | static const int SAMPLE_RATE = 44100; 35 | 36 | static const int NUM_CHANNELS = 1; 37 | static const int BITS_PER_CHANNEL = 16; 38 | static const int BYTES_PER_FRAME = (NUM_CHANNELS * (BITS_PER_CHANNEL / 8)); 39 | 40 | @interface JMFSKModem () 41 | 42 | @end 43 | 44 | @implementation JMFSKModem 45 | { 46 | @private 47 | 48 | JMFSKModemConfiguration* _configuration; 49 | AudioStreamBasicDescription* _audioFormat; 50 | 51 | JMAudioInputStream* _inputStream; 52 | JMAudioOutputStream* _outputStream; 53 | JMFSKSerialGenerator* _generator; 54 | JMProtocolDecoder* _decoder; 55 | JMProtocolEncoder* _encoder; 56 | 57 | dispatch_once_t _setupToken; 58 | } 59 | 60 | -(instancetype)initWithConfiguration:(JMFSKModemConfiguration *)configuration 61 | { 62 | self = [super init]; 63 | 64 | if (self) 65 | { 66 | _configuration = configuration; 67 | } 68 | 69 | return self; 70 | } 71 | 72 | -(void)dealloc 73 | { 74 | [self disconnect:NULL]; 75 | 76 | if (_audioFormat) 77 | { 78 | delete _audioFormat; 79 | } 80 | } 81 | 82 | -(void) setupAudioFormat 83 | { 84 | _audioFormat = new AudioStreamBasicDescription(); 85 | 86 | _audioFormat->mSampleRate = SAMPLE_RATE; 87 | _audioFormat->mFormatID = kAudioFormatLinearPCM; 88 | _audioFormat->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 89 | _audioFormat->mFramesPerPacket = 1; 90 | _audioFormat->mChannelsPerFrame = NUM_CHANNELS; 91 | _audioFormat->mBitsPerChannel = BITS_PER_CHANNEL; 92 | _audioFormat->mBytesPerPacket = BYTES_PER_FRAME; 93 | _audioFormat->mBytesPerFrame = BYTES_PER_FRAME; 94 | } 95 | 96 | -(void) setup 97 | { 98 | __weak typeof(self) weakSelf = self; 99 | 100 | dispatch_once(&_setupToken, 101 | ^{ 102 | __strong typeof(weakSelf) strongSelf = weakSelf; 103 | 104 | [strongSelf setupAudioFormat]; 105 | 106 | strongSelf->_encoder = [[JMProtocolEncoder alloc]init]; 107 | 108 | strongSelf->_outputStream = [[JMAudioOutputStream alloc]initWithAudioFormat:*_audioFormat]; 109 | 110 | strongSelf->_inputStream = [[JMAudioInputStream alloc]initWithAudioFormat:*_audioFormat]; 111 | strongSelf->_generator = [[JMFSKSerialGenerator alloc]initWithAudioFormat:strongSelf->_audioFormat configuration:strongSelf->_configuration]; 112 | strongSelf->_outputStream.audioSource = _generator; 113 | 114 | strongSelf->_decoder = [[JMProtocolDecoder alloc]init]; 115 | strongSelf->_decoder.delegate = self; 116 | 117 | JMFSKRecognizer* recognizer = [[JMFSKRecognizer alloc]initWithConfiguration:strongSelf->_configuration]; 118 | recognizer.delegate = _decoder; 119 | 120 | [strongSelf->_inputStream addRecognizer:recognizer]; 121 | }); 122 | } 123 | 124 | -(void)connect 125 | { 126 | [self connect:NULL]; 127 | } 128 | 129 | -(void)connect:(void (^)(BOOL error))completion 130 | { 131 | if (!_connected) 132 | { 133 | __weak typeof(self) weakSelf = self; 134 | 135 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), 136 | ^{ 137 | __strong typeof(weakSelf) strongSelf = weakSelf; 138 | 139 | [strongSelf setup]; 140 | 141 | #if TARGET_OS_IPHONE 142 | 143 | if([AVAudioSession sharedInstance].availableInputs.count > 0) 144 | { 145 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; 146 | [strongSelf->_inputStream record]; 147 | } 148 | else 149 | { 150 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; 151 | } 152 | 153 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil]; 154 | 155 | NSError* error = nil; 156 | [[AVAudioSession sharedInstance] setActive:YES error:&error]; 157 | 158 | if (error) 159 | { 160 | if (completion) 161 | { 162 | completion(YES); 163 | } 164 | dispatch_async(dispatch_get_main_queue(), 165 | ^{ 166 | [strongSelf->_delegate modemDidDisconnect:strongSelf]; 167 | }); 168 | 169 | return; 170 | } 171 | #endif 172 | 173 | [strongSelf->_outputStream play]; 174 | 175 | strongSelf->_connected = YES; 176 | 177 | if (completion) 178 | { 179 | completion(NO); 180 | } 181 | dispatch_async(dispatch_get_main_queue(), 182 | ^{ 183 | [strongSelf->_delegate modemDidConnect:strongSelf]; 184 | }); 185 | }); 186 | } 187 | } 188 | 189 | -(void)disconnect 190 | { 191 | [self disconnect:NULL]; 192 | } 193 | 194 | -(void)disconnect:(void (^)(BOOL error))completion 195 | { 196 | if (_connected) 197 | { 198 | __weak typeof(self) weakSelf = self; 199 | 200 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), 201 | ^{ 202 | __strong typeof(weakSelf) strongSelf = weakSelf; 203 | 204 | [strongSelf->_inputStream stop]; 205 | [strongSelf->_outputStream stop]; 206 | 207 | #if TARGET_OS_IPHONE 208 | 209 | [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; 210 | 211 | NSError* error = nil; 212 | [[AVAudioSession sharedInstance] setActive:NO error:&error]; 213 | 214 | if (error) 215 | { 216 | if (completion) 217 | { 218 | completion(YES); 219 | } 220 | 221 | dispatch_async(dispatch_get_main_queue(), 222 | ^{ 223 | [strongSelf->_delegate modemDidConnect:strongSelf]; 224 | }); 225 | 226 | return; 227 | } 228 | #endif 229 | 230 | strongSelf->_connected = NO; 231 | 232 | if (completion) 233 | { 234 | completion(NO); 235 | } 236 | 237 | dispatch_async(dispatch_get_main_queue(), 238 | ^{ 239 | [strongSelf->_delegate modemDidDisconnect:strongSelf]; 240 | }); 241 | }); 242 | } 243 | } 244 | 245 | -(void)sendData:(NSData *)data 246 | { 247 | if (_connected) 248 | { 249 | [_generator writeData:[_encoder encodeData:data]]; 250 | } 251 | } 252 | 253 | #pragma mark - Protocol decoder delegate 254 | 255 | -(void)decoder:(JMProtocolDecoder *)decoder didDecodeData:(NSData *)data 256 | { 257 | [_delegate modem:self didReceiveData:data]; 258 | } 259 | 260 | #pragma mark - Notifications 261 | 262 | - (void)routeChanged:(NSNotification*)notification 263 | { 264 | if (_connected) 265 | { 266 | [self disconnect:NULL]; 267 | 268 | [self connect:NULL]; 269 | } 270 | } 271 | 272 | @end 273 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKModemConfiguration.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface JMFSKModemConfiguration : NSObject 26 | 27 | @property (readonly) UInt16 highFrequency; 28 | @property (readonly) UInt16 lowFrequency; 29 | @property (readonly) UInt16 baudRate; 30 | 31 | @property (readonly) NSTimeInterval highFrequencyWaveDuration; 32 | @property (readonly) NSTimeInterval lowFrequencyWaveDuration; 33 | @property (readonly) NSTimeInterval bitDuration; 34 | 35 | +(JMFSKModemConfiguration*)lowSpeedConfiguration; 36 | +(JMFSKModemConfiguration*)mediumSpeedConfiguration; 37 | +(JMFSKModemConfiguration*)highSpeedConfiguration; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKModemConfiguration.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMFSKModemConfiguration.h" 24 | 25 | @implementation JMFSKModemConfiguration 26 | 27 | -(instancetype)initWithBaudRate:(UInt16)baudRate lowFrequency:(UInt16)lowFrequency highFrequency:(UInt16)highFrequency 28 | { 29 | self = [super init]; 30 | 31 | if (self) 32 | { 33 | _baudRate = baudRate; 34 | _highFrequency = highFrequency; 35 | _lowFrequency = lowFrequency; 36 | 37 | _highFrequencyWaveDuration = (double) NSEC_PER_SEC / (double) _highFrequency; 38 | _lowFrequencyWaveDuration = (double) NSEC_PER_SEC / (double) _lowFrequency; 39 | _bitDuration = (double) NSEC_PER_SEC / _baudRate; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | +(JMFSKModemConfiguration *)lowSpeedConfiguration 46 | { 47 | return [[JMFSKModemConfiguration alloc]initWithBaudRate:100 lowFrequency:800 highFrequency:1600]; 48 | } 49 | 50 | +(JMFSKModemConfiguration *)mediumSpeedConfiguration 51 | { 52 | return [[JMFSKModemConfiguration alloc]initWithBaudRate:600 lowFrequency:2666 highFrequency:4000]; 53 | } 54 | 55 | +(JMFSKModemConfiguration *)highSpeedConfiguration 56 | { 57 | return [[JMFSKModemConfiguration alloc]initWithBaudRate:1225 lowFrequency:4900 highFrequency:7350]; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKModemDelegate.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | @class JMFSKModem; 24 | 25 | @protocol JMFSKModemDelegate 26 | 27 | -(void) modemDidConnect:(JMFSKModem*)modem; 28 | -(void) modemDidDisconnect:(JMFSKModem*)modem; 29 | -(void) modem:(JMFSKModem*)modem didReceiveData:(NSData*)data; 30 | 31 | @end -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKRecognizer.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMPatternRecognizer.h" 24 | #import "JMFSKRecognizerDelegate.h" 25 | #import "JMFSKModemConfiguration.h" 26 | 27 | @interface JMFSKRecognizer : NSObject 28 | 29 | @property (nonatomic, weak) NSObject* delegate; 30 | 31 | -(instancetype)initWithConfiguration:(JMFSKModemConfiguration*)configuration; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKRecognizer.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMFSKRecognizer.h" 24 | #import "JMQueue.h" 25 | 26 | typedef NS_ENUM(NSInteger, FSKRecState) 27 | { 28 | FSKStart, 29 | FSKBits, 30 | FSKSuccess, 31 | FSKFail 32 | } ; 33 | 34 | static const int FSK_SMOOTH = 3; 35 | static const int SMOOTHER_COUNT = FSK_SMOOTH * (FSK_SMOOTH + 1) / 2; 36 | 37 | @implementation JMFSKRecognizer 38 | { 39 | @private 40 | 41 | unsigned _recentLows; 42 | unsigned _recentHighs; 43 | unsigned _halfWaveHistory[FSK_SMOOTH]; 44 | unsigned _bitPosition; 45 | unsigned _recentWidth; 46 | unsigned _recentAvrWidth; 47 | UInt8 _bits; 48 | FSKRecState _state; 49 | JMQueue* _queue; 50 | 51 | JMFSKModemConfiguration* _configuration; 52 | } 53 | 54 | -(instancetype)initWithConfiguration:(JMFSKModemConfiguration *)configuration 55 | { 56 | self = [super init]; 57 | 58 | if(self) 59 | { 60 | _configuration = configuration; 61 | 62 | _queue = [[JMQueue alloc]init]; 63 | [self reset]; 64 | } 65 | 66 | return self; 67 | } 68 | 69 | - (void) commitBytes 70 | { 71 | while (_queue.count) 72 | { 73 | NSNumber* value = [_queue dequeueQbject]; 74 | UInt8 input = value.unsignedIntegerValue; 75 | [_delegate recognizer:self didReceiveByte:input]; 76 | } 77 | } 78 | 79 | - (void) dataBit:(BOOL)one 80 | { 81 | if(one) 82 | { 83 | _bits |= (1 << _bitPosition); 84 | } 85 | 86 | _bitPosition++; 87 | } 88 | 89 | - (void) determineStateForBit:(BOOL)isHigh 90 | { 91 | FSKRecState newState = FSKFail; 92 | switch (_state) 93 | { 94 | case FSKStart: 95 | { 96 | if(!isHigh) // Start Bit 97 | { 98 | newState = FSKBits; 99 | _bits = 0; 100 | _bitPosition = 0; 101 | } 102 | else 103 | { 104 | newState = FSKStart; 105 | } 106 | break; 107 | } 108 | case FSKBits: 109 | { 110 | if((_bitPosition <= 7)) 111 | { 112 | newState = FSKBits; 113 | [self dataBit:isHigh]; 114 | } 115 | else if(_bitPosition == 8) 116 | { 117 | newState = FSKStart; 118 | [_queue enqueueObject:[NSNumber numberWithChar:_bits]]; 119 | [self performSelectorOnMainThread:@selector(commitBytes) withObject:nil waitUntilDone:NO]; 120 | _bits = 0; 121 | _bitPosition = 0; 122 | } 123 | break; 124 | } 125 | default: 126 | { 127 | } 128 | } 129 | _state = newState; 130 | } 131 | 132 | - (void) processHalfWave:(unsigned)width 133 | { 134 | // Calculate necessary values 135 | 136 | int discriminator = SMOOTHER_COUNT * (_configuration.highFrequencyWaveDuration + _configuration.lowFrequencyWaveDuration) / 4; 137 | 138 | // Shift historic values to the next index 139 | 140 | for (int i = FSK_SMOOTH - 2; i >= 0; i--) 141 | { 142 | _halfWaveHistory[i+1] = _halfWaveHistory[i]; 143 | } 144 | _halfWaveHistory[0] = width; 145 | 146 | // Smooth input 147 | 148 | unsigned waveSum = 0; 149 | for(int i = 0; i < FSK_SMOOTH; ++i) 150 | { 151 | waveSum += _halfWaveHistory[i] * (FSK_SMOOTH - i); 152 | } 153 | 154 | // Determine frequency 155 | 156 | BOOL isHighFrequency = waveSum < discriminator; 157 | unsigned avgWidth = waveSum / SMOOTHER_COUNT; 158 | 159 | _recentWidth += width; 160 | _recentAvrWidth += avgWidth; 161 | 162 | if (_state == FSKStart) 163 | { 164 | if(!isHighFrequency) 165 | { 166 | _recentLows += avgWidth; 167 | } 168 | else if(_recentLows) 169 | { 170 | _recentHighs += avgWidth; 171 | 172 | // High bit -> error -> reset 173 | 174 | if(_recentHighs > _recentLows) 175 | { 176 | _recentLows = _recentHighs = 0; 177 | } 178 | } 179 | 180 | if(_recentLows + _recentHighs >= _configuration.bitDuration) 181 | { 182 | // We have received the low bit that indicates the beginning of a byte 183 | 184 | [self determineStateForBit:NO]; 185 | _recentWidth = _recentAvrWidth = 0; 186 | 187 | if(_recentLows < _configuration.bitDuration) 188 | { 189 | _recentLows = 0; 190 | } 191 | else 192 | { 193 | _recentLows -= _configuration.bitDuration; 194 | } 195 | 196 | if(!isHighFrequency) 197 | { 198 | _recentHighs = 0; 199 | } 200 | } 201 | } 202 | else 203 | { 204 | if(isHighFrequency) 205 | { 206 | _recentHighs += avgWidth; 207 | } 208 | else 209 | { 210 | _recentLows += avgWidth; 211 | } 212 | 213 | if(_recentLows + _recentHighs >= _configuration.bitDuration) 214 | { 215 | BOOL isHighFrequencyRegion = _recentHighs > _recentLows; 216 | [self determineStateForBit:isHighFrequencyRegion]; 217 | 218 | _recentWidth -= _configuration.bitDuration; 219 | _recentAvrWidth -= _configuration.bitDuration; 220 | 221 | if(_state == FSKStart) 222 | { 223 | // The byte ended, reset the accumulators 224 | _recentLows = _recentHighs = 0; 225 | return; 226 | } 227 | 228 | unsigned* matched = isHighFrequencyRegion?&_recentHighs:&_recentLows; 229 | unsigned* unmatched = isHighFrequencyRegion?&_recentLows:&_recentHighs; 230 | 231 | if(*matched < _configuration.bitDuration) 232 | { 233 | *matched = 0; 234 | } 235 | else 236 | { 237 | *matched -= _configuration.bitDuration; 238 | } 239 | 240 | if(isHighFrequency == isHighFrequencyRegion) 241 | { 242 | *unmatched = 0; 243 | } 244 | } 245 | } 246 | } 247 | 248 | - (void) edge:(int)height width:(UInt64)nsWidth interval:(UInt64)nsInterval 249 | { 250 | if(nsInterval <= _configuration.lowFrequencyWaveDuration / 2 + _configuration.highFrequencyWaveDuration / 2) 251 | { 252 | [self processHalfWave:(unsigned)nsInterval]; 253 | } 254 | } 255 | 256 | - (void) idle: (UInt64)nsInterval 257 | { 258 | [self reset]; 259 | } 260 | 261 | - (void) reset 262 | { 263 | _bits = 0; 264 | _bitPosition = 0; 265 | _state = FSKStart; 266 | for (int i = 0; i < FSK_SMOOTH; i++) 267 | { 268 | _halfWaveHistory[i] = (_configuration.highFrequencyWaveDuration + _configuration.lowFrequencyWaveDuration) / 4; 269 | } 270 | _recentLows = _recentHighs = 0; 271 | } 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKRecognizerDelegate.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | @class JMFSKRecognizer; 24 | 25 | @protocol JMFSKRecognizerDelegate 26 | 27 | - (void) recognizer:(JMFSKRecognizer*)recognizer didReceiveByte:(UInt8)input; 28 | 29 | @end -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKSerialGenerator.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMAudioSource.h" 24 | #import 25 | #import "JMFSKModemConfiguration.h" 26 | 27 | @interface JMFSKSerialGenerator : NSObject 28 | 29 | - (instancetype) initWithAudioFormat:(AudioStreamBasicDescription*)audioFormat configuration:(JMFSKModemConfiguration*)configuration; 30 | 31 | - (void) writeData:(NSData*)data; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMFSKSerialGenerator.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMFSKSerialGenerator.h" 24 | #import "JMQueue.h" 25 | #import "JMFSKModemConfiguration.h" 26 | 27 | static const int SAMPLE_LIMIT_FACTOR = 100; 28 | 29 | static const int NUMBER_OF_DATA_BITS = 8; 30 | static const int NUMBER_OF_START_BITS = 1; 31 | static const int NUMBER_OF_STOP_BITS = 1; 32 | 33 | @implementation JMFSKSerialGenerator 34 | { 35 | @private 36 | 37 | int _sineTableLength; 38 | SInt16* _sineTable; 39 | 40 | float _nsBitProgress; 41 | unsigned _sineTableIndex; 42 | 43 | unsigned _bitCount; 44 | UInt16 _bits; 45 | 46 | BOOL _idle; 47 | BOOL _sendCarrier; 48 | 49 | JMQueue* _queue; 50 | AudioStreamBasicDescription _audioFormat; 51 | JMFSKModemConfiguration* _configuration; 52 | } 53 | 54 | - (instancetype) initWithAudioFormat:(AudioStreamBasicDescription*)audioFormat configuration:(JMFSKModemConfiguration*)configuration 55 | { 56 | self = [super init]; 57 | 58 | if (self) 59 | { 60 | _configuration = configuration; 61 | _audioFormat = *audioFormat; 62 | _queue = [[JMQueue alloc]init]; 63 | _idle = YES; 64 | _sineTableLength = _audioFormat.mSampleRate / SAMPLE_LIMIT_FACTOR; 65 | _sineTable = new SInt16[_sineTableLength]; 66 | 67 | int maxValuePerChannel = (1 << (_audioFormat.mBitsPerChannel - 1)) - 1; 68 | 69 | for(int i = 0; i < _sineTableLength; i++) 70 | { 71 | // Transfer values between -1.0 and 1.0 to integer values between -sample max and sample max 72 | 73 | _sineTable[i] = (SInt16)(sin(i * 2 * M_PI / _sineTableLength) * maxValuePerChannel); 74 | } 75 | } 76 | 77 | return self; 78 | } 79 | 80 | -(void)dealloc 81 | { 82 | delete _sineTable; 83 | } 84 | 85 | - (BOOL) hasNextByte 86 | { 87 | // Set the output bit HIGH to indicate that there is no data transmission 88 | 89 | _bits = 1; 90 | 91 | if(_idle) 92 | { 93 | if(_queue.count > 0) 94 | { 95 | int preCarrierBitsCount = _configuration.baudRate / 25 + 1; 96 | 97 | _bitCount = preCarrierBitsCount; 98 | _sendCarrier = YES; 99 | _idle = NO; 100 | 101 | return YES; 102 | } 103 | } 104 | else 105 | { 106 | if(_queue.count > 0) 107 | { 108 | NSNumber* value = [_queue dequeueQbject]; 109 | UInt8 byte = value.unsignedIntValue; 110 | _bits = byte; 111 | _bits <<= NUMBER_OF_START_BITS; // Set start bits to LOW 112 | _bits |= UINT16_MAX << (NUMBER_OF_START_BITS + NUMBER_OF_DATA_BITS); // Set stop bits to HIGH 113 | 114 | _bitCount = NUMBER_OF_DATA_BITS + NUMBER_OF_START_BITS + NUMBER_OF_STOP_BITS; 115 | _sendCarrier = NO; 116 | } 117 | else 118 | { 119 | int postCarrierBitsCount = _configuration.baudRate / 200 + 1; 120 | 121 | _bitCount = postCarrierBitsCount; 122 | _sendCarrier = YES; 123 | _idle = YES; 124 | } 125 | 126 | return YES; 127 | } 128 | 129 | return NO; 130 | } 131 | 132 | - (void) outputStream:(JMAudioOutputStream *)stream fillBuffer:(void *)buffer bufferSize:(NSUInteger)bufferSize 133 | { 134 | SInt16* sample = (SInt16*)buffer; 135 | BOOL underflow = NO; 136 | 137 | if(!_bitCount) 138 | { 139 | underflow = ![self hasNextByte]; 140 | } 141 | 142 | for(int i = 0; i < bufferSize; i += _audioFormat.mBytesPerFrame, sample++) 143 | { 144 | // Send next bit 145 | 146 | if(_nsBitProgress >= _configuration.bitDuration) 147 | { 148 | if(_bitCount) 149 | { 150 | --_bitCount; 151 | if(!_sendCarrier) 152 | { 153 | _bits >>= 1; 154 | } 155 | } 156 | _nsBitProgress -= _configuration.bitDuration; 157 | if(!_bitCount) 158 | { 159 | underflow = ![self hasNextByte]; 160 | } 161 | } 162 | 163 | *sample = [self modulate:underflow]; 164 | 165 | if(_bitCount) 166 | { 167 | float sampleDuration = NSEC_PER_SEC / _audioFormat.mSampleRate; 168 | _nsBitProgress += sampleDuration; 169 | } 170 | } 171 | } 172 | 173 | -(SInt16) modulate:(BOOL)underflow 174 | { 175 | if(underflow) 176 | { 177 | // No more bits to send 178 | 179 | return 0; 180 | } 181 | 182 | // Modulate bits to high and low frequencies 183 | 184 | int highFrequencyThreshold = _configuration.highFrequency / SAMPLE_LIMIT_FACTOR; 185 | int lowFrequencyThreshold = _configuration.lowFrequency / SAMPLE_LIMIT_FACTOR; 186 | 187 | _sineTableIndex += (_bits & 1) ? highFrequencyThreshold:lowFrequencyThreshold; 188 | _sineTableIndex %= _sineTableLength; 189 | 190 | return _sineTable[_sineTableIndex]; 191 | } 192 | 193 | - (void) writeData:(NSData *)data 194 | { 195 | const char* bytes = (const char*)[data bytes]; 196 | 197 | for (int i = 0; i < data.length; i++) 198 | { 199 | [_queue enqueueObject:[NSNumber numberWithChar:bytes[i]]]; 200 | } 201 | } 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMPatternRecognizer.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | 26 | @protocol JMPatternRecognizer 27 | 28 | - (void) edge: (int)height width:(UInt64)nsWidth interval:(UInt64)nsInterval; 29 | - (void) idle: (UInt64)nsInterval; 30 | - (void) reset; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMPrefix.pch: -------------------------------------------------------------------------------- 1 | #import -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMProtocolDecoder.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | #import "JMFSKRecognizerDelegate.h" 25 | #import "JMProtocolDecoderDelegate.h" 26 | 27 | @interface JMProtocolDecoder : NSObject 28 | 29 | @property (nonatomic, weak) id delegate; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMProtocolDecoder.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMProtocolDecoder.h" 24 | 25 | static const UInt8 START_BYTE = 0xFF; 26 | static const UInt8 ESCAPE_BYTE = 0x33; 27 | static const UInt8 END_BYTE = 0x77; 28 | 29 | @implementation JMProtocolDecoder 30 | { 31 | @private 32 | 33 | NSMutableData* _data; 34 | BOOL _escaped; 35 | } 36 | 37 | -(void)recognizer:(JMFSKRecognizer *)recognizer didReceiveByte:(UInt8)input 38 | { 39 | if(_escaped) 40 | { 41 | [_data appendBytes:&input length:1]; 42 | _escaped = NO; 43 | 44 | return; 45 | } 46 | 47 | if(input == ESCAPE_BYTE) 48 | { 49 | _escaped = YES; 50 | 51 | return; 52 | } 53 | 54 | if(input == START_BYTE) 55 | { 56 | _data = [NSMutableData data]; 57 | 58 | return; 59 | } 60 | 61 | if(input == END_BYTE) 62 | { 63 | if ([_delegate respondsToSelector:@selector(decoder:didDecodeData:)]) 64 | { 65 | [_delegate decoder:self didDecodeData:_data]; 66 | } 67 | _data = nil; 68 | 69 | return; 70 | } 71 | 72 | [_data appendBytes:&input length:1]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMProtocolDecoderDelegate.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | @class JMProtocolDecoder; 24 | 25 | @protocol JMProtocolDecoderDelegate 26 | 27 | -(void) decoder:(JMProtocolDecoder*)decoder didDecodeData:(NSData*)data; 28 | 29 | @end -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMProtocolEncoder.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface JMProtocolEncoder : NSObject 26 | 27 | -(NSData*)encodeData:(NSData*)data; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMProtocolEncoder.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMProtocolEncoder.h" 24 | 25 | static const UInt8 START_BYTE = 0xFF; 26 | static const UInt8 ESCAPE_BYTE = 0x33; 27 | static const UInt8 END_BYTE = 0x77; 28 | 29 | @implementation JMProtocolEncoder 30 | 31 | -(NSData *)encodeData:(NSData *)data 32 | { 33 | NSMutableData* encodedData = [NSMutableData dataWithCapacity:data.length]; 34 | 35 | // Append start byte 36 | 37 | [encodedData appendBytes:&START_BYTE length:1]; 38 | 39 | // Escape bytes 40 | 41 | const UInt8* dataBytes = data.bytes; 42 | 43 | for (int i = 0; i < data.length; i++) 44 | { 45 | UInt8 dataByte = dataBytes[i]; 46 | 47 | if (dataByte == START_BYTE || dataByte == END_BYTE || dataByte == ESCAPE_BYTE) 48 | { 49 | [encodedData appendBytes:&ESCAPE_BYTE length:1]; 50 | } 51 | 52 | [encodedData appendBytes:&dataByte length:1]; 53 | } 54 | 55 | // Append end byte 56 | 57 | [encodedData appendBytes:&END_BYTE length:1]; 58 | 59 | return encodedData; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMQueue.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface JMQueue : NSObject 26 | 27 | @property (readonly) NSUInteger count; 28 | 29 | -(void) enqueueObject:(NSObject*)obj; 30 | -(id) dequeueQbject; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMQueue.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMQueue.h" 24 | #import "JMQueueNode.h" 25 | 26 | @implementation JMQueue 27 | { 28 | @private 29 | 30 | JMQueueNode* _firstNode; 31 | JMQueueNode* _lastNode; 32 | 33 | dispatch_queue_t _queue; 34 | } 35 | 36 | -(instancetype)init 37 | { 38 | self = [super init]; 39 | 40 | if (self) 41 | { 42 | _queue = dispatch_queue_create("de.jensmeder.concurrencyQueue", DISPATCH_QUEUE_CONCURRENT); 43 | } 44 | 45 | return self; 46 | } 47 | 48 | -(void)enqueueObject:(NSObject *)obj 49 | { 50 | dispatch_barrier_async(_queue, 51 | ^{ 52 | JMQueueNode* node = [[JMQueueNode alloc]initWithObject:obj]; 53 | 54 | if (_count == 0) 55 | { 56 | _firstNode = node; 57 | } 58 | else 59 | { 60 | _lastNode.next = node; 61 | } 62 | 63 | _lastNode = node; 64 | _count++; 65 | }); 66 | } 67 | 68 | -(id)dequeueQbject 69 | { 70 | if (_count == 0) 71 | { 72 | return nil; 73 | } 74 | 75 | __block JMQueueNode* node = nil; 76 | dispatch_sync(_queue, 77 | ^{ 78 | node = _firstNode; 79 | 80 | if(_count == 1) 81 | { 82 | _firstNode = nil; 83 | _lastNode = nil; 84 | } 85 | else if (_count == 2) 86 | { 87 | _firstNode = _lastNode; 88 | } 89 | else 90 | { 91 | _firstNode = node.next; 92 | } 93 | 94 | _count--; 95 | }); 96 | 97 | return node.object; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMQueueNode.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface JMQueueNode : NSObject 26 | 27 | @property (nonatomic, strong) NSObject* object; 28 | @property (nonatomic, strong) JMQueueNode* next; 29 | 30 | -(instancetype)initWithObject:(NSObject*)object; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /FSKModem/FSKModem/JMQueueNode.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMQueueNode.h" 24 | 25 | @implementation JMQueueNode 26 | { 27 | @private 28 | 29 | NSObject* _object; 30 | } 31 | 32 | -(instancetype)initWithObject:(NSObject *)object 33 | { 34 | self = [super init]; 35 | 36 | if (self) 37 | { 38 | _object = object; 39 | } 40 | 41 | return self; 42 | } 43 | 44 | -(NSObject *)object 45 | { 46 | return _object; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /FSKModem/OSX/FSKModem.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | //! Project version number for FSKModem. 26 | FOUNDATION_EXPORT double FSKModemVersionNumber; 27 | 28 | //! Project version string for FSKModem. 29 | FOUNDATION_EXPORT const unsigned char FSKModemVersionString[]; 30 | 31 | // In this header, you should import all the public headers of your framework using statements like #import 32 | 33 | 34 | -------------------------------------------------------------------------------- /FSKModem/OSX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSHumanReadableCopyright 24 | Copyright © 2014 Jens Meder. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /FSKModem/iOS/FSKModem.h: -------------------------------------------------------------------------------- 1 | // 2 | // FSKModem.h 3 | // FSKModem 4 | // 5 | // Created by Jens Meder on 20/05/17. 6 | // Copyright © 2017 Jens Meder. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for iOS. 12 | FOUNDATION_EXPORT double iOSVersionNumber; 13 | 14 | //! Project version string for iOS. 15 | FOUNDATION_EXPORT const unsigned char iOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | #import 34 | #import 35 | -------------------------------------------------------------------------------- /FSKModem/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jens Meder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FSKModem 2 | ============= 3 | 4 | The FSKModem framework allows sending and receiving data from any iOS or OS X device via the head phone jack. It uses [frequency shift keying (FSK)](http://en.wikipedia.org/wiki/Frequency-shift_keying) to modulate a sine curve carrier signal to transmit bits. On top of that it uses a serial protocol to transmit single bytes and a simple packet protocol to cluster bytes. 5 | 6 | ## Overview 7 | 8 | 1. [System requirements](README.md#system-requirements) 9 | 2. [Project setup](README.md#project-setup) 10 | 3. [Usage](README.md#usage) 11 | 4. [Talking to Arduino](README.md#talking-to-arduino) 12 | 5. [Known Issues](README.md#known-issues) 13 | 6. [Credits / Acknowledgements](README.md#credits--acknowledgements) 14 | 7. [License](README.md#license) 15 | 16 | ## System requirements 17 | 18 | iOS 8.0+ or Mac OS X 10.7+ 19 | 20 | ## Project setup 21 | 22 | If you want to use FSKModem you need to add the following frameworks to your _Link Binary with Library_ build phase of your project: 23 | 24 | * AudioToolbox.framework 25 | * AVFoundation.framework 26 | 27 | ## Usage 28 | 29 | ```objc 30 | JMFSKModemConfiguration* configuration = [JMModemConfiguration highSpeedConfiguration]; 31 | JMFSKModem* modem = [[JMFSKModem alloc]initWithConfiguration:configuration]; 32 | 33 | [modem connect]; 34 | ``` 35 | 36 | ### Sending data 37 | 38 | ```objc 39 | NSString* textToSend = @"Hello World"; 40 | NSData* dataToSend = [textToSend dataUsingEncoding:NSASCIIStringEncoding]; 41 | 42 | [modem sendData:dataToSend]; 43 | ``` 44 | 45 | ### Receiving data 46 | 47 | Register a delegate on your `JMFSKModem` instance and implement the `JMFSKModemDelegate` protocol to be notified whenever data arrives. 48 | 49 | #### Setting the delegate object 50 | 51 | ```objc 52 | modem.delegate = myModemDelegate; 53 | ``` 54 | 55 | #### Delegate implementation 56 | 57 | ```objc 58 | -(void)modem:(JMFSKModem *)modem didReceiveData:(NSData *)data 59 | { 60 | NSString* receivedText = [[NSString alloc]initWithData:data encoding:NSASCIIStringEncoding]; 61 | NSLog(@"%@", receivedText); 62 | } 63 | ``` 64 | ## Talking to Arduino 65 | 66 | The FSKModem allows you to talk to Arduino microcontrollers by using a simple circuit. 67 | 68 | ### Requirements 69 | 70 | * 4-pole audio cable with 3.5mm male connectors 71 | * Audio Jack Circuit / [Breakout](http://www.switch-science.com/catalog/600/) 72 | * [SoftModem Arduino library](https://code.google.com/p/arms22/downloads/detail?name=SoftModem-005.zip) 73 | 74 | _Note_: Switch Science offers a breakout board that saves you the hassle of building the circuit yourself. You can obtain one from [Tinkersoup](https://www.tinkersoup.de/a-569/). 75 | 76 | ### Sample sketch 77 | 78 | ```c++ 79 | #include 80 | 81 | SoftModem modem; 82 | 83 | static const byte START_BYTE = 0xFF; 84 | static const byte ESCAPE_BYTE = 0x33; 85 | static const byte END_BYTE = 0x77; 86 | 87 | static const unsigned int BAUD_RATE = 57600; 88 | 89 | void setup() 90 | { 91 | Serial.begin(BAUD_RATE); 92 | delay(1000); 93 | modem.begin(); 94 | } 95 | 96 | void decodeByte() 97 | { 98 | static boolean escaped = false; 99 | 100 | while(modem.available()) 101 | { 102 | byte c = modem.read(); 103 | 104 | if(escaped) 105 | { 106 | Serial.print((char)c); 107 | escaped = false; 108 | 109 | continue; 110 | } 111 | 112 | if(c == ESCAPE_BYTE) 113 | { 114 | escaped = true; 115 | 116 | continue; 117 | } 118 | 119 | if(c == START_BYTE) 120 | { 121 | continue; 122 | } 123 | 124 | if(c == END_BYTE) 125 | { 126 | Serial.print('\n'); 127 | break; 128 | } 129 | 130 | Serial.print((char)c); 131 | } 132 | } 133 | 134 | void encodeByte() 135 | { 136 | if(Serial.available()) 137 | { 138 | modem.write(START_BYTE); 139 | while(Serial.available()) 140 | { 141 | byte c = Serial.read(); 142 | if(c == START_BYTE || c == END_BYTE || c == ESCAPE_BYTE) 143 | { 144 | modem.write(ESCAPE_BYTE); 145 | } 146 | modem.write(c); 147 | } 148 | modem.write(END_BYTE); 149 | } 150 | } 151 | 152 | void loop() 153 | { 154 | decodeByte(); 155 | encodeByte(); 156 | } 157 | ``` 158 | ## Known Issues 159 | 160 | iOS determines if plugged-in headphones also offer microphone capabilities. Unfortunately, iOS does not detect the Switch Science breakout board to include a microphone. If you run your app in the iOS simulator on Mac OS X everything works fine as Mac OS X detects the breakout as headphones with microphone. Consequently, you can send data from your iOS devices to Arduino but not the other way around. I assume that this issue is due to a difference in resistance of the microphone pole of the breakout board and the Apple Earpods. A custom circuit might resolve this issue. 161 | 162 | ## Credits / Acknowledgements 163 | 164 | This project uses code from arm22's [SoftModemTerminal](https://code.google.com/p/arms22/wiki/SoftModemBreakoutBoard 165 | ) application. 166 | 167 | The guys from Perceptive Development provide a great read on their usage of [FSK in their Tin Can iOS App](http://labs.perceptdev.com/how-to-talk-to-tin-can/). 168 | 169 | ## License 170 | 171 | The MIT License (MIT) 172 | 173 | Copyright (c) 2014 Jens Meder 174 | 175 | Permission is hereby granted, free of charge, to any person obtaining a copy 176 | of this software and associated documentation files (the "Software"), to deal 177 | in the Software without restriction, including without limitation the rights 178 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 179 | copies of the Software, and to permit persons to whom the Software is 180 | furnished to do so, subject to the following conditions: 181 | 182 | The above copyright notice and this permission notice shall be included in all 183 | copies or substantial portions of the Software. 184 | 185 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 186 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 187 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 188 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 189 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 190 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 191 | SOFTWARE. 192 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B4118464196DD86B004EB8F4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B4118463196DD86B004EB8F4 /* main.m */; }; 11 | B4118467196DD86B004EB8F4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B4118466196DD86B004EB8F4 /* AppDelegate.m */; }; 12 | B411846F196DD86B004EB8F4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B411846E196DD86B004EB8F4 /* Images.xcassets */; }; 13 | B4118492196DD8CD004EB8F4 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4118490196DD8CD004EB8F4 /* AudioToolbox.framework */; }; 14 | B4118493196DD8CD004EB8F4 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4118491196DD8CD004EB8F4 /* AVFoundation.framework */; }; 15 | B411849F196DD9BF004EB8F4 /* JMTerminalViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = B411849E196DD9BF004EB8F4 /* JMTerminalViewModel.m */; }; 16 | B41184A2196DD9D7004EB8F4 /* JMTerminalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B41184A1196DD9D7004EB8F4 /* JMTerminalViewController.m */; }; 17 | B41184A5196DD9E4004EB8F4 /* JMTerminalView.m in Sources */ = {isa = PBXBuildFile; fileRef = B41184A4196DD9E4004EB8F4 /* JMTerminalView.m */; }; 18 | B4B5D5B81ED0549E00AF8BDB /* FSKModem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4B5D5B71ED0549200AF8BDB /* FSKModem.framework */; }; 19 | B4B5D5B91ED0549E00AF8BDB /* FSKModem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B4B5D5B71ED0549200AF8BDB /* FSKModem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 20 | B4B5D5BE1ED0552000AF8BDB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4B5D5BD1ED0552000AF8BDB /* LaunchScreen.storyboard */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | B411848B196DD8A3004EB8F4 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */; 27 | proxyType = 2; 28 | remoteGlobalIDString = B4A39420196B1037002C7324; 29 | remoteInfo = OSXFSKModem; 30 | }; 31 | B4B5D5B21ED0549200AF8BDB /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */; 34 | proxyType = 1; 35 | remoteGlobalIDString = B4B5D58B1ED0525900AF8BDB; 36 | remoteInfo = iOS; 37 | }; 38 | B4B5D5B61ED0549200AF8BDB /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */; 41 | proxyType = 2; 42 | remoteGlobalIDString = B4B5D58C1ED0525900AF8BDB; 43 | remoteInfo = iOS; 44 | }; 45 | B4B5D5BA1ED0549E00AF8BDB /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */; 48 | proxyType = 1; 49 | remoteGlobalIDString = B4B5D58B1ED0525900AF8BDB; 50 | remoteInfo = iOS; 51 | }; 52 | /* End PBXContainerItemProxy section */ 53 | 54 | /* Begin PBXCopyFilesBuildPhase section */ 55 | B4B5D5BC1ED0549E00AF8BDB /* Embed Frameworks */ = { 56 | isa = PBXCopyFilesBuildPhase; 57 | buildActionMask = 2147483647; 58 | dstPath = ""; 59 | dstSubfolderSpec = 10; 60 | files = ( 61 | B4B5D5B91ED0549E00AF8BDB /* FSKModem.framework in Embed Frameworks */, 62 | ); 63 | name = "Embed Frameworks"; 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXCopyFilesBuildPhase section */ 67 | 68 | /* Begin PBXFileReference section */ 69 | B411845E196DD86B004EB8F4 /* Terminal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Terminal.app; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | B4118462196DD86B004EB8F4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | B4118463196DD86B004EB8F4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 72 | B4118465196DD86B004EB8F4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 73 | B4118466196DD86B004EB8F4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 74 | B411846E196DD86B004EB8F4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 75 | B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FSKModem.xcodeproj; path = ../../FSKModem/FSKModem.xcodeproj; sourceTree = ""; }; 76 | B4118490196DD8CD004EB8F4 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 77 | B4118491196DD8CD004EB8F4 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 78 | B411849D196DD9BF004EB8F4 /* JMTerminalViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTerminalViewModel.h; sourceTree = ""; }; 79 | B411849E196DD9BF004EB8F4 /* JMTerminalViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMTerminalViewModel.m; sourceTree = ""; }; 80 | B41184A0196DD9D7004EB8F4 /* JMTerminalViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTerminalViewController.h; sourceTree = ""; }; 81 | B41184A1196DD9D7004EB8F4 /* JMTerminalViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMTerminalViewController.m; sourceTree = ""; }; 82 | B41184A3196DD9E4004EB8F4 /* JMTerminalView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JMTerminalView.h; sourceTree = ""; }; 83 | B41184A4196DD9E4004EB8F4 /* JMTerminalView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMTerminalView.m; sourceTree = ""; }; 84 | B4B5D5BD1ED0552000AF8BDB /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | B411845B196DD86B004EB8F4 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | B4118492196DD8CD004EB8F4 /* AudioToolbox.framework in Frameworks */, 93 | B4B5D5B81ED0549E00AF8BDB /* FSKModem.framework in Frameworks */, 94 | B4118493196DD8CD004EB8F4 /* AVFoundation.framework in Frameworks */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | /* End PBXFrameworksBuildPhase section */ 99 | 100 | /* Begin PBXGroup section */ 101 | B4118455196DD86B004EB8F4 = { 102 | isa = PBXGroup; 103 | children = ( 104 | B4118490196DD8CD004EB8F4 /* AudioToolbox.framework */, 105 | B4118491196DD8CD004EB8F4 /* AVFoundation.framework */, 106 | B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */, 107 | B4118460196DD86B004EB8F4 /* Terminal */, 108 | B411845F196DD86B004EB8F4 /* Products */, 109 | ); 110 | sourceTree = ""; 111 | }; 112 | B411845F196DD86B004EB8F4 /* Products */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | B411845E196DD86B004EB8F4 /* Terminal.app */, 116 | ); 117 | name = Products; 118 | sourceTree = ""; 119 | }; 120 | B4118460196DD86B004EB8F4 /* Terminal */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | B4118465196DD86B004EB8F4 /* AppDelegate.h */, 124 | B4118466196DD86B004EB8F4 /* AppDelegate.m */, 125 | B411846E196DD86B004EB8F4 /* Images.xcassets */, 126 | B4118461196DD86B004EB8F4 /* Supporting Files */, 127 | B411849D196DD9BF004EB8F4 /* JMTerminalViewModel.h */, 128 | B411849E196DD9BF004EB8F4 /* JMTerminalViewModel.m */, 129 | B41184A0196DD9D7004EB8F4 /* JMTerminalViewController.h */, 130 | B41184A1196DD9D7004EB8F4 /* JMTerminalViewController.m */, 131 | B41184A3196DD9E4004EB8F4 /* JMTerminalView.h */, 132 | B41184A4196DD9E4004EB8F4 /* JMTerminalView.m */, 133 | ); 134 | path = Terminal; 135 | sourceTree = ""; 136 | }; 137 | B4118461196DD86B004EB8F4 /* Supporting Files */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | B4118462196DD86B004EB8F4 /* Info.plist */, 141 | B4118463196DD86B004EB8F4 /* main.m */, 142 | B4B5D5BD1ED0552000AF8BDB /* LaunchScreen.storyboard */, 143 | ); 144 | name = "Supporting Files"; 145 | sourceTree = ""; 146 | }; 147 | B4118485196DD8A2004EB8F4 /* Products */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | B411848C196DD8A3004EB8F4 /* FSKModem.framework */, 151 | B4B5D5B71ED0549200AF8BDB /* FSKModem.framework */, 152 | ); 153 | name = Products; 154 | sourceTree = ""; 155 | }; 156 | /* End PBXGroup section */ 157 | 158 | /* Begin PBXNativeTarget section */ 159 | B411845D196DD86B004EB8F4 /* Terminal */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = B411847E196DD86B004EB8F4 /* Build configuration list for PBXNativeTarget "Terminal" */; 162 | buildPhases = ( 163 | B411845A196DD86B004EB8F4 /* Sources */, 164 | B411845B196DD86B004EB8F4 /* Frameworks */, 165 | B411845C196DD86B004EB8F4 /* Resources */, 166 | B4B5D5BC1ED0549E00AF8BDB /* Embed Frameworks */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | B4B5D5B31ED0549200AF8BDB /* PBXTargetDependency */, 172 | B4B5D5BB1ED0549E00AF8BDB /* PBXTargetDependency */, 173 | ); 174 | name = Terminal; 175 | productName = Terminal; 176 | productReference = B411845E196DD86B004EB8F4 /* Terminal.app */; 177 | productType = "com.apple.product-type.application"; 178 | }; 179 | /* End PBXNativeTarget section */ 180 | 181 | /* Begin PBXProject section */ 182 | B4118456196DD86B004EB8F4 /* Project object */ = { 183 | isa = PBXProject; 184 | attributes = { 185 | CLASSPREFIX = JM; 186 | LastUpgradeCheck = 0820; 187 | ORGANIZATIONNAME = "Jens Meder"; 188 | TargetAttributes = { 189 | B411845D196DD86B004EB8F4 = { 190 | CreatedOnToolsVersion = 6.0; 191 | }; 192 | }; 193 | }; 194 | buildConfigurationList = B4118459196DD86B004EB8F4 /* Build configuration list for PBXProject "Terminal" */; 195 | compatibilityVersion = "Xcode 3.2"; 196 | developmentRegion = English; 197 | hasScannedForEncodings = 0; 198 | knownRegions = ( 199 | en, 200 | Base, 201 | ); 202 | mainGroup = B4118455196DD86B004EB8F4; 203 | productRefGroup = B411845F196DD86B004EB8F4 /* Products */; 204 | projectDirPath = ""; 205 | projectReferences = ( 206 | { 207 | ProductGroup = B4118485196DD8A2004EB8F4 /* Products */; 208 | ProjectRef = B4118484196DD8A2004EB8F4 /* FSKModem.xcodeproj */; 209 | }, 210 | ); 211 | projectRoot = ""; 212 | targets = ( 213 | B411845D196DD86B004EB8F4 /* Terminal */, 214 | ); 215 | }; 216 | /* End PBXProject section */ 217 | 218 | /* Begin PBXReferenceProxy section */ 219 | B411848C196DD8A3004EB8F4 /* FSKModem.framework */ = { 220 | isa = PBXReferenceProxy; 221 | fileType = wrapper.framework; 222 | path = FSKModem.framework; 223 | remoteRef = B411848B196DD8A3004EB8F4 /* PBXContainerItemProxy */; 224 | sourceTree = BUILT_PRODUCTS_DIR; 225 | }; 226 | B4B5D5B71ED0549200AF8BDB /* FSKModem.framework */ = { 227 | isa = PBXReferenceProxy; 228 | fileType = wrapper.framework; 229 | path = FSKModem.framework; 230 | remoteRef = B4B5D5B61ED0549200AF8BDB /* PBXContainerItemProxy */; 231 | sourceTree = BUILT_PRODUCTS_DIR; 232 | }; 233 | /* End PBXReferenceProxy section */ 234 | 235 | /* Begin PBXResourcesBuildPhase section */ 236 | B411845C196DD86B004EB8F4 /* Resources */ = { 237 | isa = PBXResourcesBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | B411846F196DD86B004EB8F4 /* Images.xcassets in Resources */, 241 | B4B5D5BE1ED0552000AF8BDB /* LaunchScreen.storyboard in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXResourcesBuildPhase section */ 246 | 247 | /* Begin PBXSourcesBuildPhase section */ 248 | B411845A196DD86B004EB8F4 /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | B4118467196DD86B004EB8F4 /* AppDelegate.m in Sources */, 253 | B41184A5196DD9E4004EB8F4 /* JMTerminalView.m in Sources */, 254 | B411849F196DD9BF004EB8F4 /* JMTerminalViewModel.m in Sources */, 255 | B4118464196DD86B004EB8F4 /* main.m in Sources */, 256 | B41184A2196DD9D7004EB8F4 /* JMTerminalViewController.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXTargetDependency section */ 263 | B4B5D5B31ED0549200AF8BDB /* PBXTargetDependency */ = { 264 | isa = PBXTargetDependency; 265 | name = iOS; 266 | targetProxy = B4B5D5B21ED0549200AF8BDB /* PBXContainerItemProxy */; 267 | }; 268 | B4B5D5BB1ED0549E00AF8BDB /* PBXTargetDependency */ = { 269 | isa = PBXTargetDependency; 270 | name = iOS; 271 | targetProxy = B4B5D5BA1ED0549E00AF8BDB /* PBXContainerItemProxy */; 272 | }; 273 | /* End PBXTargetDependency section */ 274 | 275 | /* Begin XCBuildConfiguration section */ 276 | B411847C196DD86B004EB8F4 /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | ALWAYS_SEARCH_USER_PATHS = NO; 280 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 281 | CLANG_CXX_LIBRARY = "libc++"; 282 | CLANG_ENABLE_MODULES = YES; 283 | CLANG_ENABLE_OBJC_ARC = YES; 284 | CLANG_WARN_BOOL_CONVERSION = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 287 | CLANG_WARN_EMPTY_BODY = YES; 288 | CLANG_WARN_ENUM_CONVERSION = YES; 289 | CLANG_WARN_INFINITE_RECURSION = YES; 290 | CLANG_WARN_INT_CONVERSION = YES; 291 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 292 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 293 | CLANG_WARN_UNREACHABLE_CODE = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | ENABLE_STRICT_OBJC_MSGSEND = YES; 298 | ENABLE_TESTABILITY = YES; 299 | GCC_C_LANGUAGE_STANDARD = gnu99; 300 | GCC_DYNAMIC_NO_PIC = NO; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_OPTIMIZATION_LEVEL = 0; 303 | GCC_PREPROCESSOR_DEFINITIONS = ( 304 | "DEBUG=1", 305 | "$(inherited)", 306 | ); 307 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 315 | MTL_ENABLE_DEBUG_INFO = YES; 316 | ONLY_ACTIVE_ARCH = YES; 317 | SDKROOT = iphoneos; 318 | TARGETED_DEVICE_FAMILY = "1,2"; 319 | }; 320 | name = Debug; 321 | }; 322 | B411847D196DD86B004EB8F4 /* Release */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 339 | CLANG_WARN_UNREACHABLE_CODE = YES; 340 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 341 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 342 | COPY_PHASE_STRIP = YES; 343 | ENABLE_NS_ASSERTIONS = NO; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | GCC_C_LANGUAGE_STANDARD = gnu99; 346 | GCC_NO_COMMON_BLOCKS = YES; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 354 | MTL_ENABLE_DEBUG_INFO = NO; 355 | SDKROOT = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Release; 360 | }; 361 | B411847F196DD86B004EB8F4 /* Debug */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | INFOPLIST_FILE = Terminal/Info.plist; 366 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 368 | OTHER_LDFLAGS = ( 369 | "-lstdc++", 370 | "-ObjC", 371 | "-all_load", 372 | ); 373 | PRODUCT_BUNDLE_IDENTIFIER = "de.jensmeder.FSK${PRODUCT_NAME:rfc1034identifier}"; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | USER_HEADER_SEARCH_PATHS = "$(TARGET_BUILD_DIR)/**"; 376 | }; 377 | name = Debug; 378 | }; 379 | B4118480196DD86B004EB8F4 /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 383 | INFOPLIST_FILE = Terminal/Info.plist; 384 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 386 | OTHER_LDFLAGS = ( 387 | "-lstdc++", 388 | "-ObjC", 389 | "-all_load", 390 | ); 391 | PRODUCT_BUNDLE_IDENTIFIER = "de.jensmeder.FSK${PRODUCT_NAME:rfc1034identifier}"; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | USER_HEADER_SEARCH_PATHS = "$(TARGET_BUILD_DIR)/**"; 394 | }; 395 | name = Release; 396 | }; 397 | /* End XCBuildConfiguration section */ 398 | 399 | /* Begin XCConfigurationList section */ 400 | B4118459196DD86B004EB8F4 /* Build configuration list for PBXProject "Terminal" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | B411847C196DD86B004EB8F4 /* Debug */, 404 | B411847D196DD86B004EB8F4 /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | B411847E196DD86B004EB8F4 /* Build configuration list for PBXNativeTarget "Terminal" */ = { 410 | isa = XCConfigurationList; 411 | buildConfigurations = ( 412 | B411847F196DD86B004EB8F4 /* Debug */, 413 | B4118480196DD86B004EB8F4 /* Release */, 414 | ); 415 | defaultConfigurationIsVisible = 0; 416 | defaultConfigurationName = Release; 417 | }; 418 | /* End XCConfigurationList section */ 419 | }; 420 | rootObject = B4118456196DD86B004EB8F4 /* Project object */; 421 | } 422 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface AppDelegate : UIResponder 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "AppDelegate.h" 24 | #import "JMTerminalViewController.h" 25 | #import "JMTerminalViewModel.h" 26 | @import FSKModem; 27 | 28 | @implementation AppDelegate 29 | { 30 | @private 31 | 32 | UIWindow* _mainWindow; 33 | JMTerminalViewController* _terminalViewController; 34 | JMFSKModem* _modem; 35 | } 36 | 37 | 38 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 39 | { 40 | _modem = [[JMFSKModem alloc]initWithConfiguration:[JMFSKModemConfiguration highSpeedConfiguration]]; 41 | JMTerminalViewModel* terminalViewModel = [[JMTerminalViewModel alloc]initWithModem:_modem]; 42 | _terminalViewController = [[JMTerminalViewController alloc]initWithViewModel:terminalViewModel]; 43 | 44 | UINavigationController* navigationController = [[UINavigationController alloc]initWithRootViewController:_terminalViewController]; 45 | _mainWindow = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; 46 | _mainWindow.rootViewController = navigationController; 47 | [_mainWindow makeKeyAndVisible]; 48 | 49 | return YES; 50 | } 51 | 52 | - (void)applicationWillResignActive:(UIApplication *)application 53 | { 54 | [_modem disconnect]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon_120.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "idiom" : "iphone", 41 | "size" : "60x60", 42 | "scale" : "3x" 43 | }, 44 | { 45 | "idiom" : "ipad", 46 | "size" : "20x20", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "2x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "29x29", 57 | "scale" : "1x" 58 | }, 59 | { 60 | "idiom" : "ipad", 61 | "size" : "29x29", 62 | "scale" : "2x" 63 | }, 64 | { 65 | "idiom" : "ipad", 66 | "size" : "40x40", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "idiom" : "ipad", 71 | "size" : "40x40", 72 | "scale" : "2x" 73 | }, 74 | { 75 | "size" : "76x76", 76 | "idiom" : "ipad", 77 | "filename" : "icon_76.png", 78 | "scale" : "1x" 79 | }, 80 | { 81 | "size" : "76x76", 82 | "idiom" : "ipad", 83 | "filename" : "icon_152.png", 84 | "scale" : "2x" 85 | }, 86 | { 87 | "idiom" : "ipad", 88 | "size" : "83.5x83.5", 89 | "scale" : "2x" 90 | } 91 | ], 92 | "info" : { 93 | "version" : 1, 94 | "author" : "xcode" 95 | } 96 | } -------------------------------------------------------------------------------- /examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jensmeder/FSKModem/e20630e6c2e01311a57de9703ca06bd5d1836b65/examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/icon_152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jensmeder/FSKModem/e20630e6c2e01311a57de9703ca06bd5d1836b65/examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/icon_152.png -------------------------------------------------------------------------------- /examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/icon_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jensmeder/FSKModem/e20630e6c2e01311a57de9703ca06bd5d1836b65/examples/Terminal/Terminal/Images.xcassets/AppIcon.appiconset/icon_76.png -------------------------------------------------------------------------------- /examples/Terminal/Terminal/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /examples/Terminal/Terminal/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSMicrophoneUsageDescription 26 | The microphone is needed for this app to function. 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/JMTerminalView.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface JMTerminalView : UIView 26 | 27 | @property (nonatomic, strong, readonly) UITextField* inputTextField; 28 | @property (nonatomic, strong, readonly) UITextView* receivingTextView; 29 | 30 | @property CGFloat bottomOffset; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/JMTerminalView.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMTerminalView.h" 24 | 25 | @implementation JMTerminalView 26 | { 27 | @private 28 | 29 | NSLayoutConstraint* _bottomConstraint; 30 | } 31 | 32 | - (instancetype)init 33 | { 34 | self = [super init]; 35 | 36 | if (self) 37 | { 38 | self.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0]; 39 | _inputTextField = [[UITextField alloc]init]; 40 | _inputTextField.borderStyle = UITextBorderStyleRoundedRect; 41 | _inputTextField.translatesAutoresizingMaskIntoConstraints = NO; 42 | [_inputTextField setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; 43 | 44 | _receivingTextView = [[UITextView alloc]init]; 45 | _receivingTextView.backgroundColor = [UIColor whiteColor]; 46 | _receivingTextView.translatesAutoresizingMaskIntoConstraints = NO; 47 | _receivingTextView.editable = NO; 48 | _receivingTextView.font = [UIFont systemFontOfSize:18.0]; 49 | _receivingTextView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0); 50 | 51 | [self addSubview:_receivingTextView]; 52 | [self addSubview:_inputTextField]; 53 | 54 | _bottomConstraint = [NSLayoutConstraint constraintWithItem:_inputTextField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-5]; 55 | 56 | [self addConstraint:_bottomConstraint]; 57 | 58 | [self addConstraint:[NSLayoutConstraint constraintWithItem:_inputTextField attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:0.0 constant:35]]; 59 | 60 | [self addConstraint:[NSLayoutConstraint constraintWithItem:_receivingTextView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]]; 61 | 62 | [self addConstraint:[NSLayoutConstraint constraintWithItem:_receivingTextView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_inputTextField attribute:NSLayoutAttributeTop multiplier:1.0 constant:-5]]; 63 | 64 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_receivingTextView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_receivingTextView)]]; 65 | 66 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-5-[_inputTextField]-5-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_inputTextField)]]; 67 | } 68 | 69 | return self; 70 | } 71 | 72 | -(void)setBottomOffset:(CGFloat)bottomOffset 73 | { 74 | _bottomConstraint.constant = -bottomOffset - 5; 75 | } 76 | 77 | -(CGFloat)bottomOffset 78 | { 79 | return -(_bottomConstraint.constant + 5); 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/JMTerminalViewController.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @class JMTerminalViewModel; 26 | 27 | @interface JMTerminalViewController : UIViewController 28 | 29 | -(instancetype)initWithViewModel:(JMTerminalViewModel*)viewModel; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/JMTerminalViewController.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMTerminalViewController.h" 24 | #import "JMTerminalView.h" 25 | #import "JMTerminalViewModel.h" 26 | 27 | @interface JMTerminalViewController () 28 | 29 | @end 30 | 31 | @implementation JMTerminalViewController 32 | { 33 | @private 34 | 35 | JMTerminalViewModel* _viewModel; 36 | UIBarButtonItem* _connectBarButtonItem; 37 | UIBarButtonItem* _disconnectBarButtonItem; 38 | } 39 | 40 | -(instancetype)initWithViewModel:(JMTerminalViewModel *)viewModel 41 | { 42 | self = [super init]; 43 | 44 | if (self) 45 | { 46 | _viewModel = viewModel; 47 | } 48 | 49 | return self; 50 | } 51 | 52 | -(void)loadView 53 | { 54 | self.view = [[JMTerminalView alloc]init]; 55 | } 56 | 57 | -(void)viewDidLoad 58 | { 59 | _connectBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Connect" style:UIBarButtonItemStylePlain target:self action:@selector(connect)]; 60 | _disconnectBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Disconnect" style:UIBarButtonItemStylePlain target:self action:@selector(disconnect)]; 61 | } 62 | 63 | -(void) connect 64 | { 65 | _connectBarButtonItem.enabled = NO; 66 | [_viewModel connect]; 67 | } 68 | 69 | -(void) disconnect 70 | { 71 | _disconnectBarButtonItem.enabled = NO; 72 | [_viewModel disconnect]; 73 | } 74 | 75 | - (void)viewWillAppear:(BOOL)animated 76 | { 77 | [super viewWillAppear:animated]; 78 | 79 | self.navigationItem.rightBarButtonItem = _connectBarButtonItem; 80 | 81 | JMTerminalView* terminalView = (JMTerminalView*)self.view; 82 | 83 | terminalView.inputTextField.delegate = self; 84 | 85 | [_viewModel addObserver:self forKeyPath:@"receivedText" options:NSKeyValueObservingOptionNew context:NULL]; 86 | [_viewModel addObserver:self forKeyPath:@"connected" options:NSKeyValueObservingOptionNew context:NULL]; 87 | 88 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameWillChange:) name:UIKeyboardWillShowNotification object:nil]; 89 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameWillChange:) name:UIKeyboardWillHideNotification object:nil]; 90 | } 91 | 92 | -(void)viewWillDisappear:(BOOL)animated 93 | { 94 | [super viewWillDisappear:animated]; 95 | 96 | JMTerminalView* terminalView = (JMTerminalView*)self.view; 97 | 98 | terminalView.inputTextField.delegate = nil; 99 | 100 | [_viewModel removeObserver:self forKeyPath:@"receivedText"]; 101 | [_viewModel removeObserver:self forKeyPath:@"connected"]; 102 | 103 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 104 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 105 | } 106 | 107 | -(void) keyboardFrameWillChange:(NSNotification*)notification 108 | { 109 | UIViewAnimationCurve curve = [((NSNumber*)[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey]) integerValue]; 110 | float duration = [((NSNumber*)[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey]) floatValue]; 111 | CGRect endFrame = [((NSValue*)[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]) CGRectValue]; 112 | 113 | JMTerminalView* terminalView = (JMTerminalView*)self.view; 114 | 115 | [UIView beginAnimations:@"" context:NULL]; 116 | [UIView setAnimationCurve:curve]; 117 | [UIView setAnimationDuration:duration]; 118 | terminalView.bottomOffset = MAX(0, self.view.frame.size.height - endFrame.origin.y); 119 | [self.view layoutIfNeeded]; 120 | [UIView commitAnimations]; 121 | } 122 | 123 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 124 | { 125 | if ([keyPath isEqualToString:@"receivedText"]) 126 | { 127 | JMTerminalView* terminalView = (JMTerminalView*)self.view; 128 | 129 | terminalView.receivingTextView.text = _viewModel.receivedText; 130 | } 131 | else 132 | { 133 | if (_viewModel.connected) 134 | { 135 | _disconnectBarButtonItem.enabled = YES; 136 | [self.navigationItem setRightBarButtonItem:_disconnectBarButtonItem animated:YES]; 137 | } 138 | else 139 | { 140 | _connectBarButtonItem.enabled = YES; 141 | [self.navigationItem setRightBarButtonItem:_connectBarButtonItem animated:YES]; 142 | } 143 | } 144 | } 145 | 146 | #pragma mark - Text field delegate 147 | 148 | -(BOOL)textFieldShouldReturn:(UITextField *)textField 149 | { 150 | [_viewModel sendMessage:textField.text]; 151 | textField.text = nil; 152 | 153 | return NO; 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/JMTerminalViewModel.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | @import FSKModem; 25 | 26 | @interface JMTerminalViewModel : NSObject 27 | 28 | @property (readonly) BOOL connected; 29 | @property (nonatomic, strong, readonly) NSString* receivedText; 30 | 31 | -(instancetype)initWithModem:(JMFSKModem*)modem; 32 | 33 | -(void) sendMessage:(NSString*)message; 34 | -(void) connect; 35 | -(void) disconnect; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/JMTerminalViewModel.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "JMTerminalViewModel.h" 24 | 25 | @interface JMTerminalViewModel () 26 | 27 | @end 28 | 29 | @implementation JMTerminalViewModel 30 | { 31 | @private 32 | 33 | JMFSKModem* _modem; 34 | } 35 | 36 | -(instancetype)initWithModem:(JMFSKModem *)modem 37 | { 38 | self = [super init]; 39 | 40 | if (self) 41 | { 42 | _modem = modem; 43 | _modem.delegate = self; 44 | _receivedText = @""; 45 | } 46 | 47 | return self; 48 | } 49 | 50 | -(void)sendMessage:(NSString *)message 51 | { 52 | NSData* data = [message dataUsingEncoding:NSASCIIStringEncoding]; 53 | 54 | [_modem sendData:data]; 55 | } 56 | 57 | -(void)connect 58 | { 59 | [_modem connect]; 60 | } 61 | 62 | -(void)disconnect 63 | { 64 | [_modem disconnect]; 65 | } 66 | 67 | -(void)setConnected:(BOOL)connected 68 | { 69 | [self willChangeValueForKey:@"connected"]; 70 | 71 | _connected = connected; 72 | 73 | [self didChangeValueForKey:@"connected"]; 74 | } 75 | 76 | #pragma mark - Delegate 77 | 78 | -(void)modem:(JMFSKModem *)modem didReceiveData:(NSData *)data 79 | { 80 | NSString* text = [[NSString alloc]initWithData:data encoding:NSASCIIStringEncoding]; 81 | 82 | [self willChangeValueForKey:@"receivedText"]; 83 | 84 | _receivedText = [_receivedText stringByAppendingFormat:@"%@\n",text]; 85 | 86 | [self didChangeValueForKey:@"receivedText"]; 87 | } 88 | 89 | -(void)modemDidDisconnect:(JMFSKModem *)modem 90 | { 91 | [self setConnected:NO]; 92 | } 93 | 94 | -(void)modemDidConnect:(JMFSKModem *)modem 95 | { 96 | [self setConnected:YES]; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/Terminal/Terminal/main.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jens Meder 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | #import "AppDelegate.h" 25 | 26 | int main(int argc, char * argv[]) 27 | { 28 | @autoreleasepool 29 | { 30 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 31 | } 32 | } 33 | --------------------------------------------------------------------------------