├── internal.md ├── Default-568h@2x.png ├── EspGridOSX ├── en.lproj │ └── InfoPlist.strings ├── Esp Grid Icon V2.icns ├── Help │ ├── Help.helpindex │ ├── chat.html │ ├── code.html │ ├── preferences.html │ ├── peerlist.html │ ├── Info.plist │ ├── getting-started.html │ ├── main.html │ ├── beats.html │ ├── index.html │ ├── architecture.html │ ├── osc.html │ └── history.html ├── EspGrid-Prefix.pch ├── EspDetailedPeerListController.h ├── main.m ├── EspDetailedPeerListController.m ├── EspGrid-Info.plist ├── EspAppDelegate.h └── EspAppDelegate.m ├── EspGridUnitTests ├── en.lproj │ └── InfoPlist.strings ├── EspGridUnitTests-Prefix.pch ├── EspGridUnitTests-Info.plist ├── EspCodeShareItemTests.h ├── EspOscSubscribersTest.h ├── EspCodeShareTests.h ├── EspOpcodeReceiveTests.h ├── EspClockTests.h ├── EspOscTests.h ├── EspKeyValueControllerTests.h ├── EspOscSubscribersTest.m ├── EspOpcodeReceiveTests.m ├── EspKeyValueControllerTests.m ├── EspOscTests.m └── EspClockTests.m ├── .gitignore ├── espgridd ├── espgridd-Prefix.pch └── espgridd.1 ├── EspGrid ├── EspHandleOsc.h ├── EspMovingAverage.h ├── Makefile ├── EspMovingAverage.m ├── MockEspInternalProtocol.h ├── EspChannel.h ├── EspChatTests.h ├── EspChat.h ├── EspMessage.h ├── EspQueue.h ├── EspOscSubscribers.h ├── MockEspInternalProtocol.m ├── EspCodeShare.h ├── EspPeerList.h ├── EspBeat.h ├── EspGrid.h ├── EspClock.h ├── EspOsc.h ├── EspKeyValueController.h ├── EspNetwork.h ├── main.m ├── EspOscSocket.h ├── EspSocket.h ├── EspCodeShareItem.h ├── EspPeer.h ├── EspChatTests.m ├── EspQueue.m ├── EspChat.m ├── EspChannel.m ├── EspOpcode.h ├── EspOscSubscribers.m ├── EspGridDefs.h ├── EspClock.m ├── EspPeerList.m ├── EspOscSocket.m ├── EspCodeShare.m ├── EspMessage.m └── EspCodeShareItem.m ├── Max └── MaxEspGrid.js ├── README.md └── INSTALLING.md /internal.md: -------------------------------------------------------------------------------- 1 | # EspGrid's internal protocol 2 | 3 | In progress. 4 | -------------------------------------------------------------------------------- /Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dktr0/EspGrid/HEAD/Default-568h@2x.png -------------------------------------------------------------------------------- /EspGridOSX/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /EspGridUnitTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /EspGridOSX/Esp Grid Icon V2.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dktr0/EspGrid/HEAD/EspGridOSX/Esp Grid Icon V2.icns -------------------------------------------------------------------------------- /EspGridOSX/Help/Help.helpindex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dktr0/EspGrid/HEAD/EspGridOSX/Help/Help.helpindex -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | espgridd.exe 4 | espgridd 5 | .DS_Store 6 | EspGrid.xcodeproj/project.xcworkspace/ 7 | EspGrid.xcodeproj/xcuserdata/ 8 | -------------------------------------------------------------------------------- /EspGridOSX/EspGrid-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'EspGrid' target in the 'EspGrid' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /espgridd/espgridd-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'espgridd' target in the 'espgridd' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspGridUnitTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'EspGridUnitTests' target in the 'EspGridUnitTests' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /EspGrid/EspHandleOsc.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspHandleOsc.h 3 | // EspGrid 4 | // 5 | // Created by David Ogborn on 2015-10-24. 6 | // 7 | // 8 | 9 | #ifndef EspGrid_EspHandleOsc_h 10 | #define EspGrid_EspHandleOsc_h 11 | 12 | @protocol EspHandleOsc 13 | -(BOOL) handleOsc:(NSString*)address withParameters:(NSArray*)d fromHost:(NSString*)h port:(int)p; 14 | @end 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /EspGrid/EspMovingAverage.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspMovingAverage.h 3 | // EspGrid 4 | // 5 | // Created by David Ogborn on 1/27/2014. 6 | // 7 | // 8 | 9 | #import 10 | #import "EspGridDefs.h" 11 | 12 | @interface EspMovingAverage : NSObject 13 | { 14 | int length; // length of moving average filter 15 | int count; // current number of values in storage 16 | int index; // an index for a ring buffer 17 | EspTimeType* array; // memory for a ring buffer 18 | EspTimeType accumulator; // efficient update of sum 19 | } 20 | 21 | -(id) initWithLength:(int)l; 22 | -(EspTimeType) push:(EspTimeType)x; 23 | @end 24 | -------------------------------------------------------------------------------- /EspGrid/Makefile: -------------------------------------------------------------------------------- 1 | COMPILE = gcc 2 | FLAGS = -c -std=gnu99 $(shell gnustep-config --objc-flags) 3 | LDFLAGS = $(shell gnustep-config --base-libs) 4 | OBJECTS = EspBeat.o EspChat.o EspChannel.o EspClock.o EspCodeShareItem.o EspCodeShare.o EspOscSubscribers.o EspOscSocket.o EspKeyValueController.o EspMessage.o EspMovingAverage.o EspPeer.o EspPeerList.o EspQueue.o EspSocket.o EspNetwork.o EspOsc.o EspGrid.o main.o 5 | 6 | espgridd: $(OBJECTS) 7 | $(COMPILE) $(OBJECTS) $(LDFLAGS) -o $@ 8 | 9 | %.o: %.m EspGridDefs.h EspOpcode.h 10 | $(COMPILE) $(FLAGS) $< -o $@ 11 | 12 | all: espgridd 13 | 14 | clean: 15 | rm -f espgridd espgridd.exe *.o *.d 16 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspGridUnitTests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /EspGridOSX/Help/chat.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 |

Chat

27 | 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspCodeShareItemTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspCodeShareItemTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | 21 | @interface EspCodeShareItemTests : SenTestCase 22 | @end 23 | -------------------------------------------------------------------------------- /EspGrid/EspMovingAverage.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspMovingAverage.m 3 | // EspGrid 4 | // 5 | // Created by David Ogborn on 1/27/2014. 6 | // 7 | // 8 | 9 | #import "EspMovingAverage.h" 10 | 11 | @implementation EspMovingAverage 12 | 13 | -(id) initWithLength:(int)l 14 | { 15 | self = [super init]; 16 | length = l; 17 | count = 0; 18 | index = 0; 19 | array = malloc(sizeof(EspTimeType)*l); 20 | memset(array, 0, sizeof(EspTimeType)*l); 21 | accumulator = 0; 22 | return self; 23 | } 24 | 25 | -(void) dealloc 26 | { 27 | free(array); 28 | [super dealloc]; 29 | } 30 | 31 | -(EspTimeType) push:(EspTimeType)x 32 | { 33 | accumulator += x; // add the new value 34 | accumulator -= array[index]; // subtract the oldest value 35 | if(count. 18 | 19 | #import 20 | 21 | @interface EspDetailedPeerListController : NSWindowController 22 | -(id) initWithOwner:(id)owner; 23 | @end 24 | -------------------------------------------------------------------------------- /EspGridOSX/Help/code.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 | 27 |

Sharing Code

28 | 29 |
30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspOscSubscribersTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscSubscribersTest.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2015 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | 21 | #import "EspOscSubscribers.h" 22 | 23 | @interface EspOscSubscribersTest : SenTestCase 24 | { 25 | EspOscSubscribers* subscribers; 26 | } 27 | @end 28 | -------------------------------------------------------------------------------- /EspGridOSX/Help/preferences.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 | 27 |

Preferences

28 | 29 |
30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /EspGrid/MockEspInternalProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockEspNetwork.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspNetwork.h" 20 | 21 | @interface MockEspNetwork : EspNetwork 22 | @property (atomic,copy) NSData* transmittedData; 23 | @property (atomic,assign) NSUInteger burst; 24 | @property (atomic,assign) BOOL transmitted; 25 | @end 26 | -------------------------------------------------------------------------------- /EspGrid/EspChannel.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspChannel.h 3 | // EspGrid 4 | // 5 | // Created by David Ogborn on 2014-03-30. 6 | // 7 | // 8 | 9 | #import 10 | #import "EspOpcode.h" 11 | #import "EspSocket.h" 12 | 13 | @class EspChannel; 14 | 15 | @protocol EspChannelDelegate 16 | -(void) packetReceived:(NSDictionary*)packet fromChannel:(EspChannel*)channel; // old-style, still necessary for now 17 | -(void) opcodeReceived:(EspOpcode*)opcode fromChannel:(EspChannel*)channel; // new-style 18 | @end 19 | 20 | @interface EspChannel : NSObject 21 | { 22 | int port; 23 | NSString* host; 24 | EspSocket* socket; 25 | id delegate; 26 | } 27 | @property (nonatomic,assign) int port; 28 | @property (nonatomic,copy) NSString* host; 29 | @property (nonatomic,assign) id delegate; 30 | 31 | -(void) sendOldOpcode:(int)n withDictionary:(NSDictionary*)d; // old method 32 | -(void) sendOpcode:(EspOpcode*)opcode; // new method 33 | -(void) afterDataReceived:(NSDictionary*)plist; // old method 34 | -(void) afterOpcodeReceived:(EspOpcode*)opcode; // new method 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspCodeShareTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspCodeShareTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspCodeShare.h" 21 | #import "EspClock.h" 22 | 23 | @interface EspCodeShareTests : SenTestCase 24 | { 25 | EspClock* clock; 26 | EspCodeShare* codeShare; 27 | NSDictionary* validAnnounce; 28 | } 29 | @end 30 | -------------------------------------------------------------------------------- /EspGrid/EspChatTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspChatTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | 21 | #import "MockEspNetwork.h" 22 | #import "EspOsc.h" 23 | #import "EspChat.h" 24 | 25 | @interface EspChatTests : SenTestCase 26 | { 27 | MockEspNetwork* udp; 28 | EspOsc* osc; 29 | EspChat* chat; 30 | NSMutableDictionary* chatopc; 31 | } 32 | @end 33 | -------------------------------------------------------------------------------- /EspGridOSX/Help/peerlist.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | The EspGrid PeerList 23 | 24 | 25 | 26 | 27 |

List of Peers

28 | 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /EspGrid/EspChat.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspChat.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspNetwork.h" 21 | #import "EspOsc.h" 22 | 23 | @interface EspChat : NSObject 24 | { 25 | EspNetwork* network; 26 | EspOsc* osc; 27 | EspChatOpcode chat; 28 | } 29 | 30 | -(void) sendMessage:(NSString*)msg; 31 | +(EspChat*) chat; 32 | @end 33 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspOpcodeReceiveTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspOpcodeReceiveTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspNetwork.h" 21 | 22 | @interface MockOpcodeHandler : NSObject 23 | @property (atomic,assign) BOOL wasHandled; 24 | @end 25 | 26 | @interface EspOpcodeReceiveTests : SenTestCase 27 | { 28 | EspNetwork* opcodeReceiver; 29 | MockOpcodeHandler* opcodeHandler; 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspClockTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspClockTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "MockEspNetwork.h" 21 | #import "EspOsc.h" 22 | #import "EspClock.h" 23 | #import "EspPeerList.h" 24 | 25 | @interface EspClockTests : SenTestCase 26 | { 27 | MockEspNetwork* udp; 28 | EspOsc* osc; 29 | EspPeerList* peerList; 30 | EspClock* clock; 31 | NSMutableDictionary* beacon; 32 | NSMutableDictionary* ack; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspOscTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOsc.h" 21 | 22 | @interface MockEspOscHandler : NSObject 23 | @property (atomic,assign) NSString* address; 24 | @property (atomic,assign) NSArray* parameters; 25 | @property (atomic,assign) BOOL handled; 26 | @end 27 | 28 | @interface EspOscTests : SenTestCase 29 | { 30 | EspOsc* oscReceive; 31 | MockEspOscHandler* handler; 32 | } 33 | @end 34 | -------------------------------------------------------------------------------- /EspGridOSX/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspGrid.h" 21 | 22 | int main(int argc, char *argv[]) 23 | { 24 | @try { 25 | return NSApplicationMain(argc, (const char **)argv); 26 | } 27 | @catch (NSException* e) { 28 | NSString* l = [NSString stringWithFormat:@"main: Caught %@: %@",[e name],[e reason]]; 29 | postProblem(l,nil); 30 | // NSLog(@"main: Caught %@: %@", [e name], [e reason]); 31 | } 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspKeyValueControllerTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspKeyValueControllerTests.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "MockEspNetwork.h" 21 | #import "EspKeyValueController.h" 22 | #import "EspPeerList.h" 23 | 24 | @interface EspKeyValueControllerTests : SenTestCase 25 | { 26 | MockEspNetwork* udp; 27 | EspOsc* osc; 28 | EspPeerList* peerList; 29 | EspClock* clock; 30 | NSMutableDictionary* model; 31 | EspKeyValueController* kvc; 32 | NSMutableDictionary* kvcop; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /EspGrid/EspMessage.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspMessage.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspClock.h" 21 | #import "EspNetwork.h" 22 | #import "EspOsc.h" 23 | #import "EspQueue.h" 24 | #import "EspPeerList.h" 25 | 26 | @interface EspMessage : NSObject 27 | { 28 | EspClock* clock; 29 | EspNetwork* network; 30 | EspOsc* osc; 31 | EspPeerList* peerList; 32 | EspQueue* queue; 33 | } 34 | 35 | -(void) respondToQueuedItem:(id)item; 36 | +(EspMessage*) message; 37 | @end 38 | -------------------------------------------------------------------------------- /EspGrid/EspQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspQueue.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspClock.h" 21 | 22 | @protocol EspQueueDelegate 23 | -(void) respondToQueuedItem:(id)item; 24 | @end 25 | 26 | @interface EspQueue : NSObject 27 | 28 | { 29 | NSThread* queueThread; 30 | NSMutableArray* items; 31 | NSTimer* queueTimer; 32 | NSObject* delegate; 33 | } 34 | @property (assign) NSObject* delegate; 35 | 36 | -(void) addItem:(id)item atTime:(EspTimeType)t; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /EspGridOSX/Help/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleInfoDictionaryVersion 6 | 6.0 7 | CFBundleDevelopmentRegion 8 | en-us 9 | CFBundleIdentifier 10 | ogborn.david.EspGrid.help 11 | CFBundleName 12 | EspGrid 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1 17 | CFBundleSignature 18 | hbwr 19 | CFBundleVersion 20 | 32 21 | HPDBookAccessPath 22 | index.html 23 | HPDBookIconPath 24 | 25 | HPDBookIndexPath 26 | Help/Help.helpindex 27 | HPDBookKBProduct 28 | EspGrid1 29 | HPDBookKBURL 30 | 31 | HPDBookRemoteURL 32 | 33 | HPDBookTitle 34 | EspGrid Help 35 | HPDBookType 36 | 3 37 | HPDBookTopicListCSSPath 38 | 39 | HPDBookTopicListTemplatePath 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /EspGrid/EspOscSubscribers.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscSubscribers.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2015 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOscSocket.h" 21 | #import "EspHandleOsc.h" 22 | 23 | @interface EspOscSubscribers : NSObject 24 | { 25 | NSMutableArray* hosts; 26 | NSMutableArray* ports; 27 | EspOscSocket* socket; 28 | NSMutableArray* subscribers; 29 | } 30 | @property (nonatomic,assign) EspOscSocket* socket; 31 | 32 | -(void) subscribeHost:(NSString*)host port:(int)port; 33 | -(void) unsubscribeHost:(NSString*)host port:(int)port; 34 | -(int) count; 35 | -(void) sendData:(NSData*)data; // send data to all subscribers 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /EspGrid/MockEspInternalProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockEspNetwork.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "MockEspNetwork.h" 20 | 21 | @implementation MockEspNetwork 22 | @synthesize transmittedData; 23 | @synthesize burst; 24 | @synthesize transmitted; 25 | 26 | -(id) init 27 | { 28 | self = [super init]; 29 | transmitted = NO; 30 | return self; 31 | } 32 | 33 | -(void) transmitData: (NSData*)data 34 | { 35 | [self setTransmitted:YES]; 36 | [self setTransmittedData:data]; 37 | [self setBurst:1]; 38 | } 39 | 40 | -(void) transmitData: (NSData*)data burst:(int)n 41 | { 42 | [self setTransmitted:YES]; 43 | [self setTransmittedData:data]; 44 | [self setBurst:n]; 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /EspGridOSX/EspDetailedPeerListController.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspDetailedPeerListController.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspDetailedPeerListController.h" 20 | 21 | @interface EspDetailedPeerListController () 22 | 23 | @end 24 | 25 | @implementation EspDetailedPeerListController 26 | 27 | -(id) initWithOwner:(id)owner 28 | { 29 | NSLog(@"in custom init"); 30 | self = [super initWithWindowNibName:@"EspDetailedPeerList" owner:owner]; 31 | NSLog(@"finished custom init"); 32 | return self; 33 | } 34 | 35 | - (void)windowDidLoad 36 | { 37 | [super windowDidLoad]; 38 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 39 | 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /EspGrid/EspCodeShare.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspCodeShare.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOsc.h" 21 | #import "EspNetwork.h" 22 | #import "EspClock.h" 23 | #import "EspCodeShareItem.h" 24 | 25 | 26 | @interface EspCodeShare : NSObject 27 | { 28 | EspOsc* osc; 29 | EspNetwork* network; 30 | EspClock* clock; 31 | NSMutableArray* items; 32 | } 33 | @property (readonly) NSMutableArray* items; 34 | @property (nonatomic,assign) EspCodeShareItem* requestedShare; 35 | 36 | -(void) shareCode:(NSString*)code withTitle:(NSString*)title; 37 | -(NSString*) getOrRequestItem:(EspCodeShareItem*)item; 38 | -(NSUInteger) countOfShares; 39 | +(EspCodeShare*) codeShare; 40 | @end 41 | 42 | 43 | -------------------------------------------------------------------------------- /EspGridOSX/EspGrid-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleHelpBookFolder 10 | Help 11 | CFBundleHelpBookName 12 | ogborn.david.${PRODUCT_NAME:rfc1034identifier}.help 13 | CFBundleIconFile 14 | Esp Grid Icon V2.icns 15 | CFBundleIdentifier 16 | $(PRODUCT_BUNDLE_IDENTIFIER) 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundleName 20 | ${PRODUCT_NAME} 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | 0.58.7 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | 1 29 | LSApplicationCategoryType 30 | public.app-category.music 31 | LSMinimumSystemVersion 32 | ${MACOSX_DEPLOYMENT_TARGET} 33 | NSHumanReadableCopyright 34 | Copyright © 2012 David Ogborn. All rights reserved. 35 | NSMainNibFile 36 | MainMenu 37 | NSPrincipalClass 38 | NSApplication 39 | 40 | 41 | -------------------------------------------------------------------------------- /EspGrid/EspPeerList.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspPeerList.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOpcode.h" 21 | #import "EspGridDefs.h" 22 | #import "EspPeer.h" 23 | 24 | @interface EspPeerList : NSObject 25 | { 26 | NSMutableArray* peers; 27 | NSString* status; 28 | EspPeer* selfInPeerList; 29 | } 30 | @property (nonatomic,assign) NSString* status; 31 | @property (nonatomic,assign) EspPeer* selfInPeerList; 32 | 33 | +(EspPeerList*) peerList; 34 | -(EspPeer*) receivedBeacon:(EspBeaconOpcode*)opcode; 35 | -(EspPeer*) receivedAck:(EspAckOpcode*)opcode; 36 | -(void) receivedPeerInfo:(EspPeerInfoOpcode*)opcode; 37 | -(EspPeer*) findPeerWithName:(NSString*)name; 38 | -(EspPeer*) addNewPeer:(EspBeaconOpcode*)opcode; 39 | -(void) addSelfToPeerList; 40 | -(void) updateStatus; 41 | 42 | -(void) personChanged; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /EspGridOSX/Help/getting-started.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 |

Getting Started

27 | 28 |
    29 |
  1. Launch the EspGrid application. If this is the first time the application has been launched on a particular machine, go to the application's Preferences and enter your name, a name for the machine you are using, and select the local applications (for example, Max, ChucK, PD, SuperCollider) that you would like to receive messages.
  2. 30 | 31 |
  3. Get your friend to launch the EspGrid application. If you are both connected to the same network, you should see your friend in the list of peers, and they should see you. Type a chat message in the chat entry field in the middle of the "Main" tab - your friend should see your message. For more details, see the section on "Chat".
  4. 32 |
      33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /EspGrid/EspBeat.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspBeat.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspKeyValueController.h" 21 | 22 | @interface EspBeat : NSObject 23 | { 24 | EspKeyValueController* kvc; 25 | NSNumber* on; 26 | NSNumber* tempo; 27 | NSNumber* time; 28 | NSNumber* beat; 29 | bool tempoHasBeenSet; 30 | NSDictionary* params; 31 | unsigned long beatsIssued; 32 | } 33 | @property (retain) NSDictionary* params; 34 | @property (retain) NSNumber* on; 35 | @property (retain) NSNumber* tempo; 36 | @property (retain) NSNumber* time; 37 | @property (retain) NSNumber* beat; 38 | 39 | -(void) turnBeatOn; 40 | -(void) turnBeatOff; 41 | -(void) changeTempo:(double)newBpm; 42 | -(EspTimeType) adjustedDownbeatTime; 43 | +(EspBeat*) beat; 44 | 45 | -(bool) startTicking; // for testing, returns YES if successful or already ticking 46 | -(void) stopTicking; // for testing 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Max/MaxEspGrid.js: -------------------------------------------------------------------------------- 1 | inlets = 3; 2 | outlets = 2; 3 | 4 | var refOn; 5 | var refTempo; 6 | var refSeconds; 7 | var refNanos; 8 | var refBeat; 9 | var adjustSeconds; 10 | var adjustNanos; 11 | 12 | function list(a) { 13 | if(inlet == 2) { 14 | if(arguments.length != 2) { 15 | post("third inlet expects list of 2 items (seconds/nanoseconds)"); 16 | return; 17 | } 18 | adjustSeconds = arguments[0]; 19 | adjustNanos = arguments[1]; 20 | } 21 | if(inlet == 1) { 22 | if(arguments.length != 5) { 23 | post("second inlet excepts list of 5 items from /esp/tempoCPU/r"); 24 | post(); 25 | return; 26 | } 27 | refOn = arguments[0]; 28 | refTempo = arguments[1]; 29 | refSeconds = arguments[2]; 30 | refNanos = arguments[3]; 31 | refBeat = arguments[4]; 32 | } 33 | } 34 | 35 | function msg_float(a) { 36 | if(adjustSeconds == null) { 37 | post("haven't got adjustSeconds yet..."); 38 | post(); 39 | return; 40 | } 41 | if(refOn == null) { 42 | post("haven't got /esp/tempoCPU/r values yet..."); 43 | post(); 44 | return; 45 | } 46 | var seconds = Math.floor(a/1000); 47 | var nanos = Math.floor((a % 1000.0) * 1000000) 48 | seconds = seconds + adjustSeconds; 49 | nanos = nanos + adjustNanos; 50 | 51 | var secondsSince = seconds - refSeconds; 52 | var nanosSince = nanos - refNanos; 53 | if(nanosSince<0) { 54 | nanosSince = nanosSince + 1000000000; 55 | secondsSince = secondsSince - 1; 56 | } 57 | 58 | var beatsSince = secondsSince * (refTempo/60.0); 59 | beatsSince = beatsSince + (nanosSince/1000000000.0*(refTempo/60)); 60 | beatsNow = refBeat + beatsSince; 61 | unitsNow = Math.floor(beatsNow*480); 62 | 63 | outlet(1,unitsNow); 64 | outlet(0,["tempo",refTempo]); 65 | outlet(0,refOn); 66 | } 67 | -------------------------------------------------------------------------------- /EspGrid/EspGrid.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspGrid.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspNetwork.h" 21 | #import "EspOsc.h" 22 | #import "EspClock.h" 23 | #import "EspBeat.h" 24 | #import "EspPeerList.h" 25 | #import "EspChat.h" 26 | #import "EspKeyValueController.h" 27 | #import "EspCodeShare.h" 28 | #import "EspMessage.h" 29 | #import "EspQueue.h" 30 | 31 | @interface EspGrid: NSObject 32 | { 33 | // NSString* versionString; 34 | // NSString* title; 35 | // BOOL highVolumePosts; 36 | } 37 | //@property (readonly) NSString* versionString; 38 | //@property (readonly) NSString* title; 39 | //@property (assign) BOOL highVolumePosts; 40 | 41 | +(EspGrid*) grid; 42 | +(void) postChat:(NSString*)m; 43 | +(void) postLog:(NSString*)m; 44 | -(EspBeat*) beat; 45 | -(EspCodeShare*) codeShare; 46 | -(EspPeerList*) peerList; 47 | -(EspChannel*) bridge; 48 | -(EspClock*) clock; 49 | -(EspChat*) chat; 50 | 51 | -(void) personChanged; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /EspGrid/EspClock.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspClock.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOpcode.h" 21 | #import "EspNetwork.h" 22 | #import "EspOsc.h" 23 | #import "EspPeerList.h" 24 | 25 | @interface EspClock : NSObject 26 | { 27 | int countOfBeaconsIssued; 28 | int syncMode; 29 | EspTimeType fluxTimes[1024]; 30 | EspTimeType fluxValues[1024]; 31 | int fluxIndex; 32 | EspPeerList* peerList; 33 | EspNetwork* network; 34 | EspOsc* osc; 35 | EspTimeType flux; 36 | NSString* fluxStatus; 37 | EspBeaconOpcode beacon; 38 | EspAckOpcode ack; 39 | } 40 | @property (assign) int syncMode; 41 | @property (assign) EspTimeType flux; 42 | @property (retain) NSString* fluxStatus; 43 | 44 | +(EspClock*) clock; 45 | -(void) changeSyncMode:(int)mode; 46 | -(EspTimeType) adjustmentForPeer:(EspPeer*)peer; 47 | -(void) updateflux:(EspTimeType)adjToAdj; 48 | -(void) sendBeacon:(NSTimer*)t; 49 | -(void) personChanged; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /EspGrid/EspOsc.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspOsc.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOscSocket.h" 21 | #import "EspHandleOsc.h" 22 | #import "EspOscSubscribers.h" 23 | 24 | @interface EspOsc : NSObject 25 | { 26 | EspOscSocket* udp; 27 | NSMutableArray* handlers; 28 | EspOscSubscribers* subscribers; 29 | BOOL echoToLog; 30 | } 31 | @property (nonatomic) BOOL echoToLog; 32 | 33 | +(EspOsc*) osc; 34 | 35 | -(void) addHandler:(id)handler forAddress:(NSString*)address; 36 | 37 | // stuff for sending OSC 38 | +(NSMutableData*) createOscMessage:(NSArray*)msg log:(BOOL)log; 39 | -(void) transmit:(NSArray*)msg log:(BOOL)log; // transmit OSC to all destinations named in prefs 40 | -(void) transmit:(NSArray*)msg toHost:(NSString*)h port:(int)p log:(BOOL)log; // transmit OSC a specific host and port 41 | -(void) transmitData:(NSData*)d; 42 | 43 | -(void) response:(NSString*)address value:(NSObject*)v toQuery:(NSArray*)d fromHost:(NSString*)h port:(int)p; // helper for responding to standard ../q messages with ../r messages 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /EspGrid/EspKeyValueController.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspKeyValueController.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOsc.h" 21 | #import "EspNetwork.h" 22 | #import "EspClock.h" 23 | #import "EspPeerList.h" 24 | 25 | @interface EspKeyValueController : NSObject 26 | { 27 | NSMutableArray* keyPaths; 28 | NSMutableDictionary* authorityNames; 29 | NSMutableDictionary* authorities; 30 | NSMutableDictionary* timeStamps; 31 | NSMutableDictionary* values; 32 | NSMutableDictionary* types; 33 | NSMutableDictionary* scopes; 34 | int broadcastIndex; 35 | NSObject* model; 36 | EspOsc* osc; 37 | EspNetwork* network; 38 | EspClock* clock; 39 | EspPeerList* peerList; 40 | 41 | EspIntOpcode intOpcode; 42 | EspFloatOpcode floatOpcode; 43 | EspTimeOpcode timeOpcode; 44 | EspStringOpcode stringOpcode; 45 | EspMetreOpcode metreOpcode; 46 | } 47 | @property (nonatomic,assign) NSObject* model; 48 | -(void) addKeyPath:(NSString*)keyPath type:(int)t scope:(int)s; 49 | -(void) broadcastKeyPath:(NSString*)keyPath; 50 | -(EspTimeType) clockAdjustmentForAuthority:(NSString*)keyPath; 51 | +(EspKeyValueController*) keyValueController; 52 | @end 53 | -------------------------------------------------------------------------------- /EspGrid/EspNetwork.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspNetwork.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | // 19 | 20 | #import 21 | #import "EspOpcode.h" 22 | #import "EspPeerList.h" 23 | #import "EspChannel.h" 24 | 25 | #define ESPUDP_MAX_HANDLERS 16 26 | 27 | @protocol EspNetworkDelegate 28 | -(void) handleOpcode:(EspOpcode*)opcode; 29 | -(void) handleOldOpcode:(NSDictionary*)d; 30 | @end 31 | 32 | @interface EspNetwork : NSObject 33 | { 34 | id handlers[ESPUDP_MAX_HANDLERS]; 35 | NSMutableArray* channels; 36 | EspChannel* broadcast; 37 | EspChannel* bridge; 38 | char name[ESP_MAXNAMELENGTH]; 39 | } 40 | @property (nonatomic,assign) EspChannel* broadcast; 41 | @property (nonatomic,assign) EspChannel* bridge; 42 | 43 | +(EspNetwork*) network; 44 | -(void) sendOldOpcode:(unsigned int)opcode withDictionary:(NSDictionary*)d; // old method 45 | -(void) sendOpcode:(EspOpcode*)opcode; // new method 46 | -(void) handleOpcode:(EspOpcode*)opcode; // new 47 | -(void) handleOldOpcode:(NSDictionary*)d; // old 48 | -(void) setHandler:(id)h forOpcode:(unsigned int)o; 49 | -(void) broadcastAddressChanged; // signal that the broadcast address may have been changed 50 | -(void) nameChanged; // signal that unique name for this instance may have changed 51 | 52 | @end 53 | 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EspGrid 2 | 3 | EspGrid is a software system to streamline the sharing of timing, beats and code in electronic ensembles, from duos and trios through larger laptop orchestras to globally distributed ensembles. 4 | 5 | Here's how it works: Each member of the ensemble runs the EspGrid software on any computers they are using. Behind the scenes, all of the individual copies of EspGrid talk to each other and run various algorithms to estimate timing differences between them, as well as to share things like musical definitions and events. Then, other software can "ask" EspGrid about the situation (using a simple OSC protocol), receiving an accurate answer while being shielded from much of the complexity of the question. 6 | 7 | EspGrid's development began in the busy rehearsal and performance environment of McMaster University’s Cybernetic Orchestra, originally as part of the project "Scalable, Collective Traditions of Electronic Sound Performance" supported by Canada’s Social Sciences and Humanities Research Council (SSHRC), and the Arts Research Board of McMaster University. Aspects of its design and use have been discussed in contributions to the Audio Engineering Society and Computer Music Journal. 8 | 9 | A number of features distinguish EspGrid from most other synchronization and sharing systems for electronic music: 10 | 11 | - a collection of different algorithms to estimate time differences are included, and can be selected on the fly 12 | - it is a freestanding application, not built on top of or dependent upon, common audio programming environments 13 | - a rudimentary "bridge" system allows the construction of ensembles spanning local-area/wide-area networks 14 | 15 | # Where to next? 16 | 17 | To learn more about installing or building EspGrid, and getting started, see the document INSTALLING.md. 18 | 19 | To learn more about the simple OSC protocol used to receive information from EspGrid, and to control it, see the document OSC.md. 20 | 21 | To learn more about the internal protocol used for communication between EspGrid instances, see the document internal.md. 22 | -------------------------------------------------------------------------------- /EspGrid/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspGrid.h" 21 | 22 | int main(int argc, const char * argv[]) 23 | { 24 | NSLog(@"espgridd (-h for help)"); 25 | if(argc == 2 && (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help"))) 26 | { 27 | NSLog(@" --help (gets help, run without this in order to launch grid)"); 28 | NSLog(@" -person [name] (sets performer name on grid, only needed when changing)"); 29 | NSLog(@" -broadcast [address] [(sets LAN broadcast address, only needed when changing)"); 30 | NSLog(@" -clockMode [value] (sets clock mode, possible values: 0 1 2)"); 31 | NSLog(@" --logOSC (log info about OSC messages sent and received)"); 32 | } 33 | else 34 | { 35 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 36 | EspGrid* grid = [[EspGrid alloc] init]; 37 | BOOL echoToLog = false; 38 | for(int x=1; x. 18 | 19 | #import 20 | #import "EspGridDefs.h" 21 | #ifdef _WIN32 22 | #import 23 | #else 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #endif 31 | 32 | @protocol EspOscSocketDelegate 33 | -(void)packetReceived:(NSDictionary*)packet; 34 | @end 35 | 36 | #define ESP_OSC_SOCKET_BUFFER_SIZE 2048 37 | 38 | @interface EspOscSocket : NSObject 39 | { 40 | int socketRef, port; 41 | struct sockaddr_in us; 42 | NSThread* thread; 43 | struct sockaddr_in them; 44 | NSObject *delegate; 45 | void* transmitBuffer; 46 | NSMutableData* transmitData; 47 | void* receiveBuffer; 48 | NSMutableData* receiveData; 49 | } 50 | @property (nonatomic,assign) NSObject* delegate; 51 | 52 | -(id) initWithPort:(int)p andDelegate:(id)delegate; 53 | -(BOOL) bindToPort:(unsigned int)p; 54 | -(void) sendData:(NSData*)data toHost:(NSString*)host; // send to whatever port we listen on 55 | -(void) sendData:(NSData*)data toHost:(NSString*)host port:(int)p; // send to an arbitrary port 56 | 57 | @end 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /EspGrid/EspSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspSocket.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2015 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspGridDefs.h" 21 | #import "EspOpcode.h" 22 | #ifdef _WIN32 23 | #import 24 | #else 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #endif 32 | 33 | @protocol EspSocketDelegate 34 | -(void)opcodeReceived:(NSData*)data; 35 | @end 36 | 37 | #define ESP_SOCKET_BUFFER_SIZE (sizeof(EspOldOpcode)) 38 | 39 | @interface EspSocket : NSObject 40 | { 41 | int socketRef, port; 42 | struct sockaddr_in us; 43 | NSThread* thread; 44 | struct sockaddr_in them; 45 | NSObject *delegate; 46 | void* transmitBuffer; 47 | NSMutableData* transmitData; 48 | void* receiveBuffer; 49 | NSMutableData* receiveData; 50 | } 51 | @property (nonatomic,assign) NSObject* delegate; 52 | 53 | -(id) initWithPort:(int)p andDelegate:(id)delegate; 54 | -(BOOL) bindToPort:(unsigned int)p; 55 | -(void) closeSocket; 56 | -(void) sendOpcode:(EspOpcode*)opcode toHost:(NSString*)host; 57 | -(void) sendOldOpcode:(int)n withDictionary:(NSDictionary*)d toHost:(NSString*)host; 58 | 59 | @end 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /EspGrid/EspCodeShareItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspCodeShareItem.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspNetwork.h" 21 | 22 | #define ESPGRID_CODESHARE_FRAGMENTSIZE 128 23 | 24 | @interface EspCodeShareItem : NSObject 25 | { 26 | NSMutableArray* fragments; 27 | BOOL complete; 28 | NSString* title; 29 | NSString* content; 30 | NSString* sourceName; 31 | NSString* sourceMachine; 32 | EspTimeType timeStamp; 33 | unsigned long contentLength; 34 | unsigned long nFragments; 35 | } 36 | @property (assign) BOOL complete; 37 | @property (copy) NSString* title; 38 | @property (copy) NSString* content; 39 | @property (copy) NSString* sourceName; 40 | @property (copy) NSString* sourceMachine; 41 | @property (assign) EspTimeType timeStamp; 42 | @property (assign) unsigned long contentLength; 43 | @property (assign) unsigned long nFragments; 44 | 45 | 46 | +(id)createWithLocalContent:(NSString*)c title:(NSString*)t timeStamp:(EspTimeType)ts; 47 | +(id)createWithGridSource:(NSString*)n machine:(NSString*)m title:(NSString*)t timeStamp:(EspTimeType)ts length:(unsigned long)l; 48 | -(BOOL) isEqualToName:(NSString*)n machine:(NSString*)m timeStamp:(NSNumber*)ts; 49 | -(void) addFragment:(NSString*)fragment index:(unsigned long)i; 50 | -(void) announceOnUdp:(EspNetwork*)udp; 51 | -(void) requestAllOnUdp:(EspNetwork*)udp; 52 | -(void) deliverAllOnUdp:(EspNetwork*)udp; 53 | -(void) deliverFragment:(unsigned long)i onUdp:(EspNetwork*)udp; 54 | -(NSString*) getOrRequestContentOnUdp:(EspNetwork*)udp; 55 | -(id)initWithLocalContent:(NSString*)c title:(NSString*)t timeStamp:(EspTimeType)ts; 56 | -(id)initWithGridSource:(NSString*)n machine:(NSString*)m title:(NSString*)t timeStamp:(EspTimeType)ts length:(unsigned long)l; 57 | @end 58 | -------------------------------------------------------------------------------- /EspGridOSX/Help/main.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 | 27 |

      EspGrid Main tab

      28 | 29 |
      On the Main tab of the EspGrid application you will find controls that pertain to the shared beat and chat systems - in addition to some reports of the status of the grid.
      30 | 31 |

      Beat Controls

      32 |

      Tempo

      This sets the tempo in beats per minute (BPM) of the shared, synchronized beat system.
      33 |

      cycleLength

      This sets the number of beats in each bar. The synchronized beats will be generated with identifiers ranging from 1 up to this number. For example, for 4/4 - set cycleLength to 4. The generated beats will be 1, then 2, then 3, then 4, then back to 1, etc. Each generated beat is an OSC message issued to a list of local applications - these local applications can use information from the beat to place events between beats (each beat message includes the number of beats in a bar as well as the duration, in seconds, of a current beat).
      34 |

      On/Off

      This turns the generation of synchronized beats on/off. Each time the beat system is turned on, the first beat to be generated will be beat 1.
      35 | 36 |

      Chat Controls

      37 |

      EspChat

      The first long box is for the entry of chat messages. Type a message here and press enter - it will be sent to all of your co-performers on the grid.
      38 |
      Below the long box is a large area where received chat messages are displayed.
      39 | 40 |
      (Note: If you are using EspGrid for the first time, you may wish to peruse the "Getting Started" section of this help book.
      41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /EspGridOSX/Help/beats.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 |

      Sharing Beats

      27 | 28 |
      EspGrid includes a simple (but flexible and controllable) system for generating synchronized sequences of numbered beats. A key feature of the system (in keeping with the "decentralization" design principle) is that control of the system is available equally to all users running EspGrid on a given network. If one user turns the beat on, it will be turned on for all. Another user can then turn it off, and a third user can change the tempo or the number of beats in a bar.
      29 | 30 |
      Control of the beat system takes two forms. On the main tab of the application there are controls for tempo (beats per minut), cycleLength (number of beats in a bar) and the on/off status of the beat system. This method of control is often used in improvisation sessions. A second method of control involves changing the settings of the beat system via OSC messages sent to a local EspGrid application (see OSC). This method of control is more commonly used in fixed compositions.
      31 | 32 |
      When a beat occurs, the following OSC message will be sent locally (i.e. to selected local applications as well as any other addresses/ports specified in Preferences):
      33 | /esp/beat [n] [l] [d]
      34 | where:
      35 | n = the number of the beat, i.e. 1 for the first beat of a bar [type: int32]
      36 | l = the number of beats in a bar [type: int32]
      37 | d = the duration of a beat in seconds [type: float32] 38 |
      39 | 40 |
      However, in practice, performers rarely deal with these OSC messages directly. Instead, they use helper layers that bring the beat (and other shared) information into a convenient form. For example, the Esp class (in esp.ck) uses the above OSC messages to provide ChucK events that "match" the desired position within the bar.
      41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspOscSubscribersTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscSubscribersTest.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2015 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspOscSubscribersTest.h" 20 | 21 | @implementation EspOscSubscribers 22 | 23 | -(void) setUp 24 | { 25 | subscribers = [[EspOscSubscribers alloc] init]; 26 | } 27 | 28 | -(void) tearDown 29 | { 30 | [subscribers release]; 31 | } 32 | 33 | -(void) testSubscriberCountStartsAtZero 34 | { 35 | STAssertEquals(0,[subscribers count],@"new OscSubscribers object should have count 0"); 36 | } 37 | 38 | -(void) testSubscribeIncreasesCount 39 | { 40 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 41 | STAssertEquals(1,[subscribers count],@"after subscribeHost count should be 1"); 42 | } 43 | 44 | -(void) testAdditionalSubscribeIncreasesCount 45 | { 46 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 47 | [subscribers subscribeHost:@"10.0.0.1" port:5560]; 48 | STAssertEquals(2,[subscribers count],@"after two x subscribeHost count should be 2"); 49 | } 50 | 51 | -(void) testDuplicateSubscribeDoesntIncreaseCount 52 | { 53 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 54 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 55 | STAssertEquals(1,[subscribers count],@"after duplicate subscriptions count should be 1"); 56 | } 57 | 58 | -(void) testUnsubscribingReturnsCountToZero 59 | { 60 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 61 | [subscribers unsubscribeHost:@"127.0.0.1" port:5511]; 62 | STAssertEquals(0,[subscribers count],@"unsubscribing should return count to 0"); 63 | } 64 | 65 | -(void) testUnsubscribingUnmatchedHostRetainsCount 66 | { 67 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 68 | [subscribers unsubscribeHost:@"10.0.0.4" port:5511]; 69 | STAssertEquals(1,[subscribers count],@"unsubscribing unmatched host should retain count"); 70 | } 71 | 72 | -(void) testUnsubscribingUnmatchedPortRetainsCount 73 | { 74 | [subscribers subscribeHost:@"127.0.0.1" port:5511]; 75 | [subscribers unsubscribeHost:@"127.0.0.1" port:5560]; 76 | STAssertEquals(1,[subscribers count],@"unsubscribing unmatched port should retain count"); 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /EspGrid/EspPeer.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspPeer.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2014 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspOpcode.h" 21 | #import "EspGridDefs.h" 22 | #import "EspMovingAverage.h" 23 | 24 | @interface EspPeer : NSObject 25 | { 26 | // properties 27 | NSString* name; 28 | NSString* ip; 29 | char majorVersion; 30 | char minorVersion; 31 | char subVersion; 32 | NSString* version; 33 | char syncMode; 34 | long beaconCount; 35 | EspTimeType lastBeacon; 36 | NSString* lastBeaconStatus; 37 | EspTimeType recentLatency; 38 | EspTimeType lowestLatency; 39 | EspTimeType averageLatency; 40 | EspTimeType refBeacon,refBeaconAverage; 41 | 42 | EspPeerInfoOpcode peerinfo; 43 | 44 | // instance variables 45 | EspTimeType* adjustments; 46 | EspMovingAverage* averageLatencyObj; 47 | EspMovingAverage* refBeaconAverageObj; 48 | 49 | bool validBeaconReceived; 50 | bool validAckForSelfReceived; 51 | bool validAckForOtherReceived; 52 | } 53 | // these are set/updated from BEACON opcode 54 | @property (copy) NSString* name; 55 | @property (copy) NSString* ip; 56 | @property (assign) char majorVersion; 57 | @property (assign) char minorVersion; 58 | @property (assign) char subVersion; 59 | @property (copy) NSString* version; 60 | @property (assign) char syncMode; 61 | @property (assign) long beaconCount; 62 | @property (assign) EspTimeType lastBeacon; 63 | @property (copy) NSString* lastBeaconStatus; 64 | 65 | // these are set/updated from ACK opcode 66 | @property (assign) EspTimeType recentLatency; 67 | @property (assign) EspTimeType lowestLatency; 68 | @property (assign) EspTimeType averageLatency; 69 | @property (assign) EspTimeType refBeacon,refBeaconAverage; 70 | 71 | @property (assign) bool validBeaconReceived; 72 | @property (assign) bool validAckForSelfReceived; 73 | @property (assign) bool validAckForOtherReceived; 74 | 75 | -(void) processBeacon:(EspBeaconOpcode*)opcode; 76 | -(void) processAckForSelf:(EspAckOpcode*)opcode peerCount:(int)count; 77 | -(void) processAck:(EspAckOpcode*)opcode forOther:(EspPeer*)other; 78 | -(void) updateLastBeaconStatus; 79 | -(void) dumpAdjustments; 80 | -(EspTimeType) adjustmentForSyncMode:(int)mode; 81 | -(void) issuePeerInfoOpcode; 82 | 83 | -(void) personChanged; 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /EspGrid/EspChatTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspChatTests.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspChatTests.h" 20 | #import "EspChat.h" 21 | #import "EspGridDefs.h" 22 | 23 | @implementation EspChatTests 24 | 25 | -(void) setUp 26 | { 27 | udp = [[MockEspNetwork alloc] init]; 28 | osc = [[EspOsc alloc] init]; 29 | chat = [[EspChat alloc] init]; 30 | [chat setOsc:osc]; 31 | [chat setUdp:udp]; 32 | chatopc = [NSMutableDictionary dictionary]; 33 | [chatopc setObject:[NSNumber numberWithInt:ESP_OPCODE_CHATSEND] forKey:@"opcode"]; 34 | [chatopc setObject:@"aName" forKey:@"name"]; 35 | [chatopc setObject:@"this is a chat message" forKey:@"msg"]; 36 | } 37 | 38 | -(void) tearDown 39 | { 40 | [chat release]; 41 | [udp release]; 42 | [osc release]; 43 | } 44 | 45 | -(void) invalidateString:(NSString*)key onOpcode:(NSMutableDictionary*)opcode withMsg:(NSString*)msg 46 | { 47 | [opcode removeObjectForKey:key]; 48 | STAssertNoThrow([chat handleOpcode:opcode],msg); 49 | STAssertFalse([chat handleOpcode:opcode],msg); 50 | [opcode setObject:[NSNumber numberWithInt:666] forKey:key]; 51 | STAssertNoThrow([chat handleOpcode:opcode], msg); 52 | STAssertFalse([chat handleOpcode:opcode], msg); 53 | [opcode setObject:@"" forKey:key]; 54 | STAssertNoThrow([chat handleOpcode:opcode], msg); 55 | STAssertFalse([chat handleOpcode:opcode], msg); 56 | } 57 | 58 | -(void) invalidateNumber:(NSString*)key onOpcode:(NSMutableDictionary*)opcode withMsg:(NSString*)msg 59 | { 60 | [opcode removeObjectForKey:key]; 61 | STAssertNoThrow([chat handleOpcode:opcode], msg); 62 | STAssertFalse([chat handleOpcode:opcode],msg); 63 | [opcode setObject:@"oops" forKey:key]; 64 | STAssertNoThrow([chat handleOpcode:opcode],msg); 65 | STAssertFalse([chat handleOpcode:opcode],msg); 66 | } 67 | 68 | -(void) testCompleteChatOpcodeDoesntThrow 69 | { 70 | STAssertNoThrow([chat handleOpcode:chatopc], @"complete chat opcode shouldn't throw exception"); 71 | } 72 | 73 | -(void) testInvalidOpcode 74 | { 75 | [self invalidateNumber:@"opcode" onOpcode:chatopc withMsg:@"opcode missing opcode field shouldn't throw or be handled"]; 76 | } 77 | 78 | -(void) testInvalidName 79 | { 80 | [self invalidateString:@"name" onOpcode:chatopc withMsg:@"CHAT with invalid name shouldn't throw or be handled"]; 81 | } 82 | 83 | -(void) testInvalidMsg 84 | { 85 | [self invalidateString:@"msg" onOpcode:chatopc withMsg:@"CHAT with invalid msg shouldn't throw or be handled"]; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /espgridd/espgridd.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 13-09-11 \" DATE 7 | .Dt espgridd 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm espgridd, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /EspGridOSX/EspAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspAppDelegate.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import 20 | #import "EspGrid.h" 21 | #import "EspDetailedPeerListController.h" 22 | 23 | @interface EspAppDelegate : NSObject 24 | { 25 | IBOutlet EspGrid* esp; 26 | 27 | IBOutlet NSTabView* tabView; 28 | 29 | // Main tab 30 | IBOutlet NSTextField* espClockAdjustment; 31 | IBOutlet NSTextField* espClockFlux; 32 | IBOutlet NSButton* beatOn; 33 | IBOutlet NSTextField* beatTempo; 34 | IBOutlet NSTextField* espChatMsg; 35 | IBOutlet NSTextView* espChatOutput; 36 | 37 | // Peers tab 38 | 39 | // Shared Code tab 40 | IBOutlet NSArrayController* codeShareController; 41 | 42 | // Log tab 43 | IBOutlet NSTextView* espLogOutput; 44 | IBOutlet NSButton* espLogOSC; 45 | 46 | // Bridge tab 47 | IBOutlet NSTextField* espBridgeLocalGroup; 48 | IBOutlet NSTextField* espBridgeLocalAddress; 49 | IBOutlet NSTextField* espBridgeLocalPort; 50 | IBOutlet NSTextField* espBridgeRemoteAddress; 51 | IBOutlet NSTextField* espBridgeRemotePort; 52 | IBOutlet NSTextField* espBridgeRemoteClaimedAddress; 53 | IBOutlet NSTextField* espBridgeRemoteClaimedPort; 54 | IBOutlet NSTextField* espBridgeRemoteGroup; 55 | IBOutlet NSTextField* espBridgeRemotePackets; 56 | 57 | // other (not a part of tabbed interface) 58 | IBOutlet NSUserDefaultsController* preferencesController; 59 | NSWindowController* preferencesPanel; 60 | EspDetailedPeerListController* detailedPeerList; 61 | 62 | IBOutlet NSMenuItem* tickOnBeats; 63 | 64 | } 65 | @property (assign) IBOutlet NSWindow *window; 66 | 67 | // Main tab 68 | -(IBAction)beatOn:(id)sender; 69 | -(IBAction)beatTempo:(id)sender; 70 | 71 | // Peers tab 72 | -(IBAction)showDetailedPeerList:(id)sender; 73 | 74 | // Shared Code tab 75 | -(IBAction)grabShare:(id)sender; 76 | -(IBAction)shareClipboard:(id)sender; 77 | 78 | // Log tab 79 | -(IBAction)logOSCChanged:(id)sender; 80 | 81 | // Bridge tab 82 | -(IBAction)bridgeRemoteAdddress:(id)sender; 83 | -(IBAction)bridgeRemotePort:(id)sender; 84 | 85 | -(IBAction)sendChatMessage:(id)sender; 86 | -(IBAction)showPreferences:(id)sender; 87 | -(IBAction)copyLogToClipboard:(id)sender; 88 | 89 | // Help buttons 90 | -(IBAction)helpPreferences:(id)sender; 91 | -(IBAction)helpPeerList:(id)sender; 92 | -(IBAction)helpMain:(id)sender; 93 | -(IBAction)helpCode:(id)sender; 94 | 95 | // other (not a part of tabbed interface) 96 | -(void)postChatNotification:(NSNotification*)n; 97 | -(void)postLogNotification:(NSNotification*)n; 98 | 99 | -(IBAction)tickOnBeatsChanged:(id)sender; 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /EspGridOSX/Help/index.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | 23 | EspGrid 24 | 25 | 26 | 27 | 28 | 29 |

      EspGrid

      30 | 31 |

      by David Ogborn

      32 | 33 |
      EspGrid is a protocol/application developed to streamline the sharing of timing, beats and code in participatory electronic ensembles, such as laptop orchestras. EspGrid has been developed and tested in the busy rehearsal and performance environment of McMaster University's Cybernetic Orchestra, during the project "Scalable, Collective Traditions of Electronic Sound Performance" supported by Canada's Social Sciences and Humanities Research Council (SSHRC), and the Arts Research Board of McMaster University. The design of EspGrid has been heavily influenced by the following 5 principles:
      34 |
      35 |
        36 |
      1. Immediacy: Simply launching EspGrid on a network makes shared resources immediately available, with new resources becoming available as soon as they exist
      2. 37 |
      3. Neutrality: EspGrid works alongside diverse performance/environments languages (ChucK, Max, PD, SuperCollider, etc)
      4. 38 |
      5. Hybridity: EspGrid facilitates the use of multiple languages/environments within the same ensemble or even the same machine
      6. 39 |
      7. Decentralization: All machines run the same EspGrid software, and all performers have equal access to the resources and controls of the grid - there is no "server".
      8. 40 |
      9. Extensibility: EspGrid can be used to synchronize and schedule arbitrary OSC messages across an ensemble
      10. 41 |
      42 | 43 |
      EspGrid is still very much in development - so the fine details of many of the features documented in this help system can be expected to change rapidly and without (much) warning in the future. The basic architecture and approach, however, is likely to be much more stable. So fire up the grid, launch the electronic music tools of your choice, and let's all make music together!
      44 | 45 |
      46 |

      Table of Contents

      47 |
        48 |
      1. Getting Started
      2. 49 |
      3. Preferences
      4. 50 |
      5. Sharing Beats
      6. 51 |
      7. Sharing Code
      8. 52 |
      9. Sharing Chat
      10. 53 |
      11. EspGrid Architecture
      12. 54 |
      13. OSC output and input
      14. 55 |
      15. Version History
      16. 56 |
      57 |
      58 | 59 | 60 | -------------------------------------------------------------------------------- /EspGridOSX/Help/architecture.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid 23 | 24 | 25 | 26 |

      Architecture

      27 | 28 |
      Every machine in an ensemble runs the EspGrid application. The instances of EspGrid all communicate with each other, automatically and behind the scenes. Performers (for example, live coding performers) don't send information directly to other performers/computers. Instead, they send information to their local EspGrid application - and it works out the details of how to get that information to everyone else "on the grid". This approach (no direct communication with other performers/computers) has several advantages:
      29 | 30 |
      31 |
        32 |
      • The behind the scenes protocol can employ sophisticated techniques to improve reliability, timing, etc
      • 33 |
      • The behind the scenes protocol can change and improve with out disturbance to ongoing performance and composition practices
      • 34 |
      • The focus of creators and performers can stay on the content of shared information rather than the mode of transmission
      • 35 |
      • The behind the scenes protocol can evolve to span multiple networks (and the Internet) with little to no change to the interface used by creators/performers for everyday ensemble work
      • 36 |
      37 |
      38 | 39 |
      When events happen that need to be shared with "local" applications (for example, ChucK or Max running on an individual performer's machine) the grid sends an OSC message to a designated port on that machine. By convention, the ports are as follows: 40 |
        41 |
      • Max - port 5511
      • 42 |
      • ChucK - port 5512
      • 43 |
      • SuperCollider - port 57120
      • 44 |
      • PD - port 5513
      • 45 |
      46 |
      47 | 48 |
      This allows different applications to be used by different members of the ensemble, or even multiple applications to be used simultaneously on a given machine. Both situations are quite common in laptop orchestras! For complete flexibility, additional ports (and also addresses other than the local address) can be set as the target for messages from the grid. Of course, if a particular environment is in heavy use by EspGrid users, we should add that as a standard option (let us know!)!
      49 | 50 |
      In practice, performers don't work directly with the OSC messages that the grid sends to the applications. Instead, they use one of a series of "thin helper objects" to connect the shared data to convenient practices/affordances in the performance environment of their choice. For example, the esp.ck file receives the OSC beat messages sent by the grid, and makes that available to ChucK coders in a way that respects ChucK programming practices/conventions.
      51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /EspGrid/EspQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspQueue.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspQueue.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspQueue 23 | @synthesize delegate; 24 | 25 | 26 | -(id) init 27 | { 28 | self = [super init]; 29 | items = [[NSMutableArray alloc] init]; 30 | queueThread = [[NSThread alloc] initWithTarget:self selector:@selector(queueThreadMainMethod) object:nil]; 31 | [queueThread start]; 32 | return self; 33 | } 34 | 35 | -(void) queueThreadMainMethod 36 | { 37 | [NSThread setThreadPriority: 0.99]; 38 | [NSTimer scheduledTimerWithTimeInterval:0.010 39 | target:self 40 | selector:@selector(queueThreadLoop:) 41 | userInfo:nil 42 | repeats:NO]; 43 | [[NSRunLoop currentRunLoop] run]; 44 | } 45 | 46 | 47 | -(void) queueThreadLoop:(NSTimer*)timer 48 | { 49 | if(![items count]) 50 | { 51 | [NSTimer scheduledTimerWithTimeInterval:0.010 52 | target:self 53 | selector:@selector(queueThreadLoop:) 54 | userInfo:nil 55 | repeats:NO]; 56 | return; 57 | } 58 | EspTimeType now = monotonicTime(); 59 | for(NSArray* a in items) 60 | { 61 | EspTimeType t = [[a objectAtIndex:0] longLongValue]; 62 | if(t<=now) 63 | { 64 | [delegate performSelectorOnMainThread:@selector(respondToQueuedItem:) 65 | withObject:[a objectAtIndex:1] 66 | waitUntilDone:YES]; 67 | [items removeObject:a]; 68 | } 69 | } 70 | 71 | if([items count]) 72 | { 73 | [NSTimer scheduledTimerWithTimeInterval:0.001 74 | target:self 75 | selector:@selector(queueThreadLoop:) 76 | userInfo:nil 77 | repeats:NO]; 78 | } 79 | else 80 | { 81 | [NSTimer scheduledTimerWithTimeInterval:0.010 82 | target:self 83 | selector:@selector(queueThreadLoop:) 84 | userInfo:nil 85 | repeats:NO]; 86 | } 87 | } 88 | 89 | 90 | -(void) addItem:(id)item atTime:(EspTimeType)t 91 | { 92 | // right now we are doing this with brute force 93 | // in the future, we should sort as we insert objects so that queueThreadLoop doesn't have to traverse the whole array! 94 | NSArray* a = [NSArray arrayWithObjects:[NSNumber numberWithDouble:t],item,nil]; 95 | [items performSelector:@selector(addObject:) onThread:queueThread withObject:a waitUntilDone:NO]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspOpcodeReceiveTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspOpcodeReceiveTests.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspOpcodeReceiveTests.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation MockOpcodeHandler 23 | @synthesize wasHandled; 24 | 25 | -(BOOL) handleOpcode:(NSDictionary*)d 26 | { 27 | [self setWasHandled:YES]; 28 | return YES; 29 | } 30 | @end 31 | 32 | @implementation EspOpcodeReceiveTests 33 | 34 | -(void) setUp 35 | { 36 | opcodeReceiver = [[EspNetwork alloc] init]; 37 | opcodeHandler = [[MockOpcodeHandler alloc] init]; 38 | [opcodeReceiver setHandler:opcodeHandler forOpcode:12]; 39 | } 40 | 41 | -(void) tearDown 42 | { 43 | [opcodeReceiver release]; 44 | [opcodeHandler release]; 45 | } 46 | 47 | -(void) testNilDataThrows 48 | { 49 | STAssertThrows([opcodeReceiver dataReceived:nil fromHost:@"192.168.0.1" atTime:EspGridTime()], 50 | @"nil data to dataReceived should throw exception"); 51 | } 52 | 53 | -(void) testNilHostThrows 54 | { 55 | NSData* data = [[NSData alloc] init]; 56 | STAssertThrows([opcodeReceiver dataReceived:data fromHost:nil atTime:EspGridTime()], 57 | @"nil host to dataReceived should throw exception"); 58 | } 59 | 60 | -(void) testSettingHandlerBeyondMaximumThrows 61 | { 62 | EspNetwork* udp = [[EspNetwork alloc] init]; 63 | MockOpcodeHandler* h = [[MockOpcodeHandler alloc] init]; 64 | STAssertThrows([udp setHandler:h forOpcode:300],@"adding opcode handler beyond maximum should throw exception"); 65 | } 66 | 67 | -(void) testDataWithMatchingOpcodeIsHandled 68 | { 69 | NSDictionary* dictionary = [NSDictionary dictionaryWithObjectsAndKeys: 70 | [NSNumber numberWithInt:12],@"opcode",nil]; 71 | NSError* err; 72 | NSData* data = [NSPropertyListSerialization dataWithPropertyList:dictionary 73 | format:NSPropertyListBinaryFormat_v1_0 options:0 74 | error:&err]; 75 | [opcodeReceiver dataReceived:data fromHost:@"192.168.0.1" atTime:EspGridTime()]; 76 | STAssertTrue([opcodeHandler wasHandled],@"data with matching opcode should be handled"); 77 | } 78 | 79 | 80 | -(void) testDataWithMismatchedOpcodeIsNotHandled 81 | { 82 | NSDictionary* dictionary = [NSDictionary dictionaryWithObjectsAndKeys: 83 | [NSNumber numberWithInt:13],@"opcode",nil]; 84 | NSError* err; 85 | NSData* data = [NSPropertyListSerialization dataWithPropertyList:dictionary 86 | format:NSPropertyListBinaryFormat_v1_0 options:0 87 | error:&err]; 88 | [opcodeReceiver dataReceived:data fromHost:@"192.168.0.1" atTime:EspGridTime()]; 89 | STAssertFalse([opcodeHandler wasHandled],@"data with mismatching opcode should be handled"); 90 | } 91 | 92 | 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /EspGrid/EspChat.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspChat.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspChat.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspChat 23 | 24 | +(EspChat*) chat 25 | { 26 | static EspChat* sharedObject = nil; 27 | if(!sharedObject)sharedObject = [[EspChat alloc] init]; 28 | return sharedObject; 29 | } 30 | 31 | -(id) init 32 | { 33 | self = [super init]; 34 | network = [EspNetwork network]; 35 | osc = [EspOsc osc]; 36 | 37 | // setup CHAT opcode 38 | chat.header.opcode = ESP_OPCODE_CHATSEND; 39 | chat.header.length = sizeof(EspChatOpcode); 40 | copyPersonIntoOpcode((EspOpcode*)&chat); 41 | 42 | return self; 43 | } 44 | 45 | 46 | -(void) sendMessage:(NSString*)msg 47 | { 48 | // transmit opcode in internal protocol 49 | const char* msgCString = [msg cStringUsingEncoding:NSUTF8StringEncoding]; 50 | strncpy(chat.text, msgCString, ESP_CHAT_MAXLENGTH); 51 | chat.text[ESP_CHAT_MAXLENGTH-1] = 0; 52 | copyPersonIntoOpcode((EspOpcode*)&chat); // to fix: really should only be when defaults change... 53 | [network sendOpcode:(EspOpcode*)&chat]; 54 | 55 | // send to subscribers to external protocol 56 | NSString* from = [[NSUserDefaults standardUserDefaults] stringForKey:@"person"]; 57 | NSArray* a = [NSArray arrayWithObjects:@"/esp/chat/receive",from,msg,nil]; 58 | [osc transmit:a log:YES]; 59 | 60 | // post chat message in this process 61 | NSString* m = [NSString stringWithFormat:@"%@: %@",from,msg]; 62 | postChat(m); 63 | } 64 | 65 | -(void) handleOpcode:(EspOpcode *)opcode 66 | { 67 | NSAssert(opcode->opcode == ESP_OPCODE_CHATSEND,@"EspChat sent unrecognized opcode"); 68 | EspChatOpcode* msgRcvd = (EspChatOpcode*)opcode; 69 | 70 | // sanitize strings 71 | msgRcvd->text[ESP_CHAT_MAXLENGTH-1] = 0; 72 | opcode->name[15] = 0; 73 | 74 | // post message in this process 75 | postChat([NSString stringWithFormat:@"%s: %s",opcode->name,msgRcvd->text]); 76 | 77 | // and send it to all external protocol subscribers 78 | NSString* x = [NSString stringWithCString:opcode->name encoding:NSUTF8StringEncoding]; 79 | NSString* y = [NSString stringWithCString:msgRcvd->text encoding:NSUTF8StringEncoding]; 80 | NSArray* a = [NSArray arrayWithObjects:@"/esp/chat/receive",x,y,nil]; 81 | [osc transmit:a log:YES]; 82 | } 83 | 84 | -(void) handleOldOpcode:(NSDictionary*)d; 85 | { 86 | NSAssert(false,@"empty old opcode handler in EspChat called"); 87 | } 88 | 89 | -(BOOL) handleOsc:(NSString*)address withParameters:(NSArray*)d fromHost:(NSString*)h port:(int)p 90 | { 91 | if([address isEqual:@"/esp/chat/send"]) 92 | { 93 | if([d count]<1){postCritical(@"received /esp/chat/send with no parameters",self); return NO;} 94 | NSMutableString* msg = [[NSMutableString alloc] init]; 95 | for(int i=0;i<[d count];i++) 96 | { 97 | [msg appendFormat:@"%@ ",[d objectAtIndex:i]]; 98 | } 99 | [self sendMessage:msg]; 100 | return YES; 101 | } 102 | return NO; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /EspGrid/EspChannel.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspChannel.m 3 | // EspGrid 4 | // 5 | // Created by David Ogborn on 2014-03-30. 6 | // 7 | // 8 | 9 | #import "EspChannel.h" 10 | 11 | @implementation EspChannel 12 | @synthesize host, delegate; 13 | 14 | -(id) init 15 | { 16 | self = [super init]; 17 | port = 0; 18 | socket = NULL; 19 | return self; 20 | } 21 | 22 | -(int) port 23 | { 24 | return port; 25 | } 26 | 27 | -(void) setPort:(int)p 28 | { 29 | if(p != port) 30 | { 31 | port = p; 32 | EspSocket* oldSocket = socket; 33 | socket = [[EspSocket alloc] initWithPort:p andDelegate:self]; 34 | [oldSocket closeSocket]; 35 | [oldSocket release]; 36 | } 37 | } 38 | 39 | 40 | -(void) sendOldOpcode:(int)n withDictionary:(NSDictionary *)d 41 | { 42 | [socket sendOldOpcode:n withDictionary:d toHost:host]; 43 | } 44 | 45 | -(void) sendOpcode:(EspOpcode*)opcode 46 | { 47 | [socket sendOpcode:opcode toHost:host]; 48 | } 49 | 50 | // note: don't override this in subclasses - override handleOpcode: (new style) and afterDataReceived: (old) instead 51 | -(void)opcodeReceived:(NSData*)data 52 | { 53 | NSAssert([[NSThread currentThread] isMainThread],@"attempt to process opcode outside main thread"); 54 | EspOpcode* opcode = (EspOpcode*)[data bytes]; 55 | if(opcode->opcode == ESP_OPCODE_BEACON || 56 | opcode->opcode == ESP_OPCODE_ACK || 57 | opcode->opcode == ESP_OPCODE_PEERINFO || 58 | opcode->opcode == ESP_OPCODE_CHATSEND || 59 | opcode->opcode == ESP_OPCODE_INT || 60 | opcode->opcode == ESP_OPCODE_FLOAT || 61 | opcode->opcode == ESP_OPCODE_STRING || 62 | opcode->opcode == ESP_OPCODE_TIME || 63 | opcode->opcode == ESP_OPCODE_METRE) 64 | { 65 | // received opcode is a new-style opcode 66 | [self afterOpcodeReceived:opcode]; 67 | } 68 | else 69 | { 70 | // received opcode is an old-style, NSDictionary based opcode, so dictionary needs to be built... 71 | char* data = ((char*)opcode) + sizeof(EspOpcode); 72 | NSData* temp = [NSData dataWithBytesNoCopy:data length:(opcode->length-sizeof(EspOpcode)) freeWhenDone:NO]; 73 | NSError* err = nil; 74 | NSMutableDictionary* plist = (NSMutableDictionary*)[NSPropertyListSerialization 75 | propertyListWithData:temp options:NSPropertyListMutableContainers 76 | format:NULL error:&err]; 77 | if(plist != nil) 78 | { 79 | [plist setValue:[NSNumber numberWithChar:opcode->opcode] forKey:@"opcode"]; 80 | [plist setValue:[NSString stringWithCString:opcode->ip encoding:NSUTF8StringEncoding] forKey:@"ip"]; 81 | [plist setValue:[NSNumber numberWithInt:opcode->port] forKey:@"port"]; 82 | [plist setValue:[NSNumber numberWithLongLong:opcode->sendTime] forKey:@"packetSendTime"]; 83 | [plist setValue:[NSNumber numberWithLongLong:opcode->receiveTime] forKey:@"packetReceiveTime"]; 84 | [self afterDataReceived:plist]; 85 | } 86 | else 87 | { 88 | NSString* s = [NSString stringWithFormat:@"unable to deserialize dictionary from %s:%d (%u bytes)", 89 | opcode->ip,opcode->port,opcode->length]; 90 | postProblem(s, self); 91 | } 92 | } 93 | } 94 | 95 | // override these in subclasses in order to add custom behaviours upon receipt of a timestamped packet 96 | // overridden methods can call [super afterDataReceived] to ensure packet is processed by EspNetwork 97 | -(void) afterDataReceived:(NSDictionary*)plist 98 | { 99 | [delegate packetReceived:plist fromChannel:self]; 100 | } 101 | 102 | -(void) afterOpcodeReceived:(EspOpcode*)opcode 103 | { 104 | [delegate opcodeReceived:opcode fromChannel:self]; 105 | } 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /INSTALLING.md: -------------------------------------------------------------------------------- 1 | # Installing/Using/Building EspGrid 2 | 3 | ## Installing EspGrid from prebuilt binaries 4 | 5 | The quickest way to get started with EspGrid is to download a prebuilt binary. Recent binary builds of EspGrid for OS X and Windows are available here: 6 | 7 | ## Additional installation steps on Windows 8 | 9 | On Windows, EspGrid is distributed as "command-line" application called espgridd (for "EspGrid Daemon"). Don't worry if the command line isn't your thing - you won't need to work with it at the command line. For the espgridd binary to work on Windows, however, you'll need to install two basic packages from the free/open-source GNUstep project. Install the "GNUstep MSYS System" and "GNUstep Core". 10 | 11 | In some cases, if you've installed the GNUstep packages, you can run espgridd simply by clicking on the espgridd.exe file wherever you have unzipped it to. In other cases, restarting the system can help the first time. In some others cases still, it may help to move the espgridd.exe file to C:\GNUstep\bin\. The provided binaries work for Windows XP and later. 12 | 13 | ## Linux 14 | 15 | On Linux, you'll need to build espgridd from source. See the section below on Building from Source. 16 | 17 | ## Using EspGrid 18 | 19 | Once you have EspGrid (or espgridd) running, you'll want some way of talking to it from your performance environment. Currently this is most convenient from SuperCollider. The Esp.sc project provides a SuperCollider quark that can be installed with a single line of SuperCollider code, and then used in a way that should be quite intuitive to SuperCollider artists. Evaluating the following line in SuperCollider will install the Esp quark: 20 | ``` 21 | Quarks.install("https://github.com/d0kt0r0/Esp.sc.git"); 22 | ``` 23 | 24 | Some examples of how to use the Esp.sc SuperCollider quark are visible in the comments at the top of the quark itself, which you can view online with the following link: 25 | https://github.com/d0kt0r0/Esp.sc/blob/master/Esp.sc 26 | 27 | In the absence of a "helper" like the SuperCollider quark, you can send and receive OSC messages to EspGrid in any way that makes sense. EspGrid normally listens on UDP port 5510 for incoming OSC messages. You can read more about the protocol for talking to EspGrid in the document OSC.md. As time passes, hopefully more helpers like the SuperCollider quark will be created for other languages/environments. 28 | 29 | ## Building EspGrid from source 30 | 31 | ### Building EspGrid on OS X 32 | 33 | If you want to build EspGrid from source code on OS X, start by cloning the current source tree from github. Enter the following at the Terminal: git clone https://github.com/d0kt0r0/EspGrid.git 34 | 35 | From there, building should simply be a matter of opening the Xcode project file contained therein with Xcode and selecting Build. You can use "Archive" in Xcode to build a freestanding binary like those downloadable from the esp.mcmaster.ca site. 36 | 37 | ### Building espgridd on Windows 38 | 39 | If you want to build EspGrid from source code on Windows, you'll need to download and install the free/open-source GNUstep development environment first. In addition to the "GNUstep MSYS System" and "GNUstep Core" you'll require the "GNUstep Devel" package. It is also recommended to install a Windows version of the git version management software. 40 | 41 | After installing the GNUstep packages, open the GNUstep shell and clone the EspGrid source tree from github: 42 | ``` 43 | git clone https://github.com/d0kt0r0/EspGrid.git 44 | ``` 45 | 46 | Change into the directory containing the EspGrid source as follows: 47 | ``` 48 | cd EspGrid/EspGrid 49 | ``` 50 | 51 | From there, building espgridd.exe should simply be a matter of invoking make: 52 | ``` 53 | make 54 | ``` 55 | 56 | ### Building espgridd on Linux 57 | 58 | Building espgridd on Linux will, in general, be similar to building it on Windows. Make sure you have both the basic and development GNUstep packages on your system. Clone the EspGrid source tree using git. Change into the EspGrid subfolder o the EspGrid source tree and invoke "make". 59 | -------------------------------------------------------------------------------- /EspGrid/EspOpcode.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspOpcode.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2016 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #ifndef EspOpcode_h 20 | #define EspOpcode_h 21 | 22 | #include "EspGridDefs.h" 23 | 24 | #define ESP_NUMBER_OF_OPCODES 15 25 | 26 | // new-style opcodes 27 | #define ESP_OPCODE_BEACON 0 28 | #define ESP_OPCODE_ACK 1 29 | #define ESP_OPCODE_CHATSEND 2 30 | #define ESP_OPCODE_PEERINFO 4 31 | #define ESP_OPCODE_INT 10 32 | #define ESP_OPCODE_FLOAT 11 33 | #define ESP_OPCODE_TIME 12 34 | #define ESP_OPCODE_STRING 13 35 | #define ESP_OPCODE_METRE 14 36 | 37 | // old-style opcodes 38 | #define ESP_OPCODE_ANNOUNCESHARE 5 39 | #define ESP_OPCODE_REQUESTSHARE 6 40 | #define ESP_OPCODE_DELIVERSHARE 7 41 | #define ESP_OPCODE_OSCNOW 8 42 | #define ESP_OPCODE_OSCFUTURE 9 43 | 44 | #define ESP_MAXNAMELENGTH 16 45 | 46 | typedef struct { 47 | EspTimeType sendTime; 48 | EspTimeType receiveTime; 49 | char name[ESP_MAXNAMELENGTH]; 50 | char ip[ESP_MAXNAMELENGTH]; 51 | uint16_t port; 52 | uint16_t length; 53 | uint16_t opcode; 54 | } EspOpcode __attribute__((aligned(8))); 55 | 56 | void copyPersonIntoOpcode(EspOpcode* opcode); // defined in EspNetwork.m 57 | 58 | typedef struct { 59 | EspOpcode header; 60 | char data[2048]; 61 | } EspOldOpcode; 62 | 63 | typedef struct { 64 | EspOpcode header; 65 | int32_t beaconCount; 66 | unsigned char majorVersion; 67 | unsigned char minorVersion; 68 | unsigned char subVersion; 69 | unsigned char syncMode; 70 | } EspBeaconOpcode; 71 | 72 | typedef struct { 73 | EspOpcode header; 74 | char nameRcvd[ESP_MAXNAMELENGTH]; 75 | char ipRcvd[ESP_MAXNAMELENGTH]; 76 | EspTimeType beaconSend; 77 | EspTimeType beaconReceive; 78 | int32_t beaconCount; 79 | } EspAckOpcode; 80 | 81 | typedef struct { 82 | EspOpcode header; 83 | char peerName[ESP_MAXNAMELENGTH]; 84 | char peerIp[ESP_MAXNAMELENGTH]; 85 | EspTimeType recentLatency; 86 | EspTimeType lowestLatency; 87 | EspTimeType averageLatency; 88 | EspTimeType refBeacon; 89 | EspTimeType refBeaconAverage; 90 | } EspPeerInfoOpcode; 91 | 92 | #define ESP_CHAT_MAXLENGTH 256 93 | 94 | typedef struct { 95 | EspOpcode header; 96 | char text[ESP_CHAT_MAXLENGTH]; 97 | } EspChatOpcode; 98 | 99 | #define ESP_SCOPE_SYSTEM 0 100 | #define ESP_SCOPE_GLOBAL 1 101 | #define ESP_SCOPE_LOCAL 2 102 | 103 | typedef struct { 104 | char path[ESP_MAXNAMELENGTH]; 105 | char authority[ESP_MAXNAMELENGTH]; 106 | EspTimeType timeStamp; 107 | char scope; 108 | } EspVariableInfo; 109 | 110 | typedef struct { 111 | EspOpcode header; 112 | EspVariableInfo info; 113 | int32_t value; 114 | } EspIntOpcode; 115 | 116 | typedef struct { 117 | EspOpcode header; 118 | EspVariableInfo info; 119 | Float32 value; 120 | } EspFloatOpcode; 121 | 122 | typedef struct { 123 | EspOpcode header; 124 | EspVariableInfo info; 125 | EspTimeType value; 126 | } EspTimeOpcode; 127 | 128 | #define ESP_MAX_STRINGOPCODELENGTH 1024 129 | 130 | typedef struct { 131 | EspOpcode header; 132 | EspVariableInfo info; 133 | char value[ESP_MAX_STRINGOPCODELENGTH]; 134 | } EspStringOpcode; 135 | 136 | typedef struct { 137 | EspTimeType time; 138 | int32_t on; 139 | int32_t beat; 140 | Float32 tempo; 141 | } EspMetre; 142 | 143 | typedef struct { 144 | EspOpcode header; 145 | EspVariableInfo info; 146 | EspMetre metre; 147 | } EspMetreOpcode; 148 | 149 | #endif /* EspOpcode_h */ 150 | -------------------------------------------------------------------------------- /EspGrid/EspOscSubscribers.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscSubscribers.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2015 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspOscSubscribers.h" 20 | 21 | @implementation EspOscSubscribers 22 | @synthesize socket; 23 | 24 | -(id) init 25 | { 26 | self = [super init]; 27 | subscribers = [[NSMutableArray alloc] init]; 28 | return self; 29 | } 30 | 31 | -(void) dealloc 32 | { 33 | [subscribers release]; 34 | [super dealloc]; 35 | } 36 | 37 | -(BOOL) handleOsc:(NSString*)address withParameters:(NSArray*)d fromHost:(NSString*)h port:(int)p 38 | { 39 | if([address isEqual:@"/esp/subscribe"]) 40 | { 41 | if([d count] == 0) 42 | { 43 | [self subscribeHost:h port:p]; 44 | } 45 | else if([d count] == 1) 46 | { 47 | [self subscribeHost:h port:[[d objectAtIndex:0] intValue]]; 48 | } 49 | else if([d count] == 2) 50 | { 51 | [self subscribeHost:[d objectAtIndex:1] port:[[d objectAtIndex:0] intValue]]; 52 | } 53 | else 54 | { 55 | postProblem(@"received /esp/subscribe with too many parameters", self); 56 | } 57 | return YES; 58 | } 59 | if([address isEqual:@"/esp/unsubscribe"]) 60 | { 61 | if([d count] == 0) 62 | { 63 | [self unsubscribeHost:h port:p]; 64 | } 65 | else if([d count] == 1) 66 | { 67 | [self unsubscribeHost:h port:[[d objectAtIndex:0] intValue]]; 68 | } 69 | else if([d count] == 2) 70 | { 71 | [self unsubscribeHost:[d objectAtIndex:1] port:[[d objectAtIndex:0] intValue]]; 72 | } 73 | else 74 | { 75 | postProblem(@"received /esp/unsubscribe with too many parameters", self); 76 | } 77 | return YES; 78 | } 79 | return NO; 80 | } 81 | 82 | -(void) subscribeHost:(NSString*)host port:(int)port 83 | { 84 | for(NSArray *x in subscribers) 85 | { 86 | NSString* h = [x objectAtIndex:0]; 87 | int p = [[x objectAtIndex:1] intValue]; 88 | if( [h isEqualToString:host] && port==p) return; 89 | } 90 | // if we get here, match was not found, so add entry 91 | NSArray* n = [NSArray arrayWithObjects:host,[NSNumber numberWithInt:port],nil]; 92 | [subscribers addObject:n]; 93 | NSString* msg = [NSString stringWithFormat:@"subscribed %@:%d",host,port]; 94 | postProtocolLow(msg,self); 95 | } 96 | 97 | -(void) unsubscribeHost:(NSString*)host port:(int)port 98 | { 99 | NSArray* found = nil; 100 | for(NSArray *x in subscribers) 101 | { 102 | NSString* h = [x objectAtIndex:0]; 103 | int p = [[x objectAtIndex:1] intValue]; 104 | if( [h isEqualToString:host] && port==p) { 105 | found = x; 106 | break; 107 | } 108 | } 109 | if(found != nil) // a match was found so remove it 110 | { 111 | [subscribers removeObject:found]; 112 | NSString* msg = [NSString stringWithFormat:@"unsubscribed %@:%d",host,port]; 113 | postProtocolLow(msg,self); 114 | } 115 | } 116 | 117 | -(int) count 118 | { 119 | return (int)[subscribers count]; 120 | } 121 | 122 | -(void) sendData:(NSData *)data 123 | { 124 | for(NSArray *x in subscribers) 125 | { 126 | NSString* host = [x objectAtIndex:0]; 127 | int port = [[x objectAtIndex:1] intValue]; 128 | NSAssert(socket!=nil,@"socket is nil in EspOscSubscribers sendData"); 129 | [socket sendData:data toHost:host port:port]; 130 | } 131 | } 132 | 133 | @end 134 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspKeyValueControllerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspKeyValueControllerTests.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspKeyValueControllerTests.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspKeyValueControllerTests 23 | 24 | -(void) setUp 25 | { 26 | udp = [[MockEspNetwork alloc] init]; 27 | osc = [[EspOsc alloc] init]; 28 | peerList = [[EspPeerList alloc] init]; 29 | clock = [[EspClock alloc] init]; 30 | [clock setPeerList:peerList]; 31 | [clock setUdp:udp]; 32 | [clock setOsc:osc]; 33 | model = [NSMutableDictionary dictionary]; 34 | kvc = [[EspKeyValueController alloc] init]; 35 | [kvc setUdp:udp]; 36 | [kvc setOsc:osc]; 37 | [kvc setClock:clock]; 38 | [kvc setModel:model]; 39 | [kvc addKeyPath:@"myKeyPath"]; 40 | 41 | kvcop = [NSMutableDictionary dictionary]; 42 | [kvcop setObject:[NSNumber numberWithInt:ESP_OPCODE_KVC] forKey:@"opcode"]; 43 | [kvcop setObject:@"aName" forKey:@"name"]; 44 | [kvcop setObject:@"aMachine" forKey:@"machine"]; 45 | [kvcop setObject:@"192.168.2.1" forKey:@"ip"]; 46 | 47 | [kvcop setObject:@"myKeyPath" forKey:@"keyPath"]; 48 | [kvcop setObject:@"this is a value" forKey:@"value"]; 49 | [kvcop setObject:[NSNumber numberWithDouble:123456.0] forKey:@"time"]; 50 | } 51 | 52 | -(void) tearDown 53 | { 54 | [kvc release]; 55 | [clock release]; 56 | [peerList release]; 57 | [osc release]; 58 | [udp release]; 59 | } 60 | 61 | -(void) invalidateString:(NSString*)key onOpcode:(NSMutableDictionary*)opcode withMsg:(NSString*)msg 62 | { 63 | [opcode removeObjectForKey:key]; 64 | STAssertNoThrow([kvc handleOpcode:opcode],msg); 65 | STAssertFalse([kvc handleOpcode:opcode],msg); 66 | [opcode setObject:[NSNumber numberWithInt:666] forKey:key]; 67 | STAssertNoThrow([kvc handleOpcode:opcode], msg); 68 | STAssertFalse([kvc handleOpcode:opcode], msg); 69 | [opcode setObject:@"" forKey:key]; 70 | STAssertNoThrow([kvc handleOpcode:opcode], msg); 71 | STAssertFalse([kvc handleOpcode:opcode], msg); 72 | } 73 | 74 | -(void) invalidateNumber:(NSString*)key onOpcode:(NSMutableDictionary*)opcode withMsg:(NSString*)msg 75 | { 76 | [opcode removeObjectForKey:key]; 77 | STAssertNoThrow([kvc handleOpcode:opcode], msg); 78 | STAssertFalse([kvc handleOpcode:opcode],msg); 79 | [opcode setObject:@"oops" forKey:key]; 80 | STAssertNoThrow([kvc handleOpcode:opcode],msg); 81 | STAssertFalse([kvc handleOpcode:opcode],msg); 82 | } 83 | 84 | -(void) testCompleteKvcOpcodeWorks 85 | { 86 | STAssertNoThrow([kvc handleOpcode:kvcop], @"complete KVC opcode shouldn't throw exception"); 87 | STAssertTrue([kvc handleOpcode:kvcop],@"complete KVC opcode should be handled (return TRUE)"); 88 | STAssertTrue([[model objectForKey:@"myKeyPath"] isEqualToString:@"this is a value"],@"complete KVC opcode should change model value"); 89 | } 90 | 91 | -(void) testInvalidOpcode 92 | { 93 | [self invalidateNumber:@"opcode" onOpcode:kvcop withMsg:@"opcode with invalid opcode field should not throw or be handled"]; 94 | } 95 | 96 | -(void) testInvalidKeyPath 97 | { 98 | [self invalidateString:@"keyPath" onOpcode:kvcop withMsg:@"KVC with invalid keyPath field should not throw or be handled"]; 99 | } 100 | 101 | -(void) testInvalidValue 102 | { 103 | [kvcop removeObjectForKey:@"value"]; 104 | STAssertNoThrow([kvc handleOpcode:kvcop], @"KVC with missing 'value' should not throw"); 105 | STAssertFalse([kvc handleOpcode:kvcop], @"KVC with missing 'value' should not be handled"); 106 | } 107 | 108 | -(void) testInvalidTime 109 | { 110 | [self invalidateNumber:@"time" onOpcode:kvcop withMsg:@"KVC with invalid time should not throw or be handled"]; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /EspGrid/EspGridDefs.h: -------------------------------------------------------------------------------- 1 | // 2 | // EspGridDefs.h 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2015 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #ifndef EspGrid_EspGridDefs_h 20 | #define EspGrid_EspGridDefs_h 21 | 22 | #define ESPGRID_MAJORVERSION 0 23 | #define ESPGRID_MINORVERSION 58 // changes to external/internal protocol MUST increment MINORVERSION 24 | #define ESPGRID_SUBVERSION 7 25 | 26 | #define ESP_POST_CHAT 1 27 | #define ESP_POST_LOG 2 28 | 29 | #define VALIDATE_OPCODE_NSSTRING(vx) \ 30 | do { \ 31 | if(vx == nil) { postWarning(@"opcode with no " #vx,self); return; } \ 32 | if(![vx isKindOfClass:[NSString class]]) { postWarning(@"opcode with " #vx " not NSString",self); return; } \ 33 | if([vx length]==0){ postWarning(@"opcode with zero length " #vx,self); return; } \ 34 | } while(0) 35 | 36 | #define VALIDATE_OPCODE_NSNUMBER(vx) \ 37 | do { \ 38 | if(vx == nil) { postWarning(@"opcode with no " #vx,self); return; } \ 39 | if(![vx isKindOfClass:[NSNumber class]]) { postWarning(@"opcode with " #vx " not NSString",self); return; } \ 40 | } while(0) 41 | 42 | 43 | void postChat(NSString* s); 44 | void postWarning(NSString* s,id sender); 45 | void postProblem(NSString* s,id sender); 46 | void postLog(NSString* s,id sender); 47 | void postLogHighVolume(NSString* s,id sender); 48 | 49 | #import 50 | 51 | #ifdef _WIN32 52 | #include 53 | #endif 54 | 55 | #ifndef GNUSTEP 56 | #import 57 | typedef SInt64 EspTimeType; 58 | #else 59 | typedef int64_t EspTimeType; 60 | #endif 61 | 62 | inline static EspTimeType systemTime(void) { 63 | #ifndef _WIN32 64 | // OS X and Linux (MINGW/GNUSTEP) 65 | struct timeval t; 66 | gettimeofday(&t, NULL); 67 | return ((EspTimeType)t.tv_sec*(EspTimeType)1000000000)+((EspTimeType)t.tv_usec*(EspTimeType)1000); 68 | #else 69 | // Windows (MINGW/GNUSTEP) 70 | SYSTEMTIME s; 71 | FILETIME f; 72 | GetSystemTime(&s); 73 | SystemTimeToFileTime(&s,&f); 74 | EspTimeType r; 75 | r = f.dwLowDateTime; 76 | r += ((uint64_t)f.dwHighDateTime) <<32; 77 | r -= 116444736000000000ULL; // epoch adjustment (Windows to UNIX) 78 | r *= 100; // one hundred nanoseconds per Windows tick 79 | return r; 80 | #endif 81 | } 82 | 83 | inline static EspTimeType monotonicTime(void) { 84 | #ifndef GNUSTEP 85 | // OS X 86 | return mach_absolute_time(); 87 | #else 88 | #ifdef _WIN32 89 | // Windows (MINGW/GNUSTEP) 90 | extern LARGE_INTEGER performanceFrequency; 91 | LARGE_INTEGER t; 92 | QueryPerformanceCounter(&t); 93 | EspTimeType x = t.QuadPart / performanceFrequency.QuadPart * 1000000000L; // whole seconds in nanoseconds 94 | EspTimeType y = t.QuadPart % performanceFrequency.QuadPart * 1000000000L / performanceFrequency.QuadPart; // remainder 95 | return x+y; 96 | #else 97 | // Linux (GNUSTEP) 98 | struct timespec t; 99 | clock_gettime(CLOCK_MONOTONIC,&t); 100 | return (EspTimeType)t.tv_sec*(EspTimeType)1000000000+(EspTimeType)t.tv_nsec; 101 | #endif 102 | #endif 103 | } 104 | 105 | #ifdef GNUSTEP 106 | 107 | #ifdef _WIN32 108 | // GNUSTEP/MINGW (Windows) 109 | #include 110 | typedef uint32_t UInt32; 111 | typedef float Float32; 112 | typedef double Float64; 113 | inline static UInt32 EspSwapInt32(UInt32 x) { return htonl(x); } 114 | inline static Float32 EspSwapFloat32(const Float32 x) { 115 | unsigned char* c = (unsigned char*)&x; 116 | uint32_t y = c[3] + (c[2] << 8) + (c[1] << 16) + (c[0] << 24); 117 | float z = *((float*)&y); 118 | return z; 119 | } 120 | inline static Float64 EspSwapFloat64(double x) { return __builtin_bswap64(x); } 121 | #endif 122 | 123 | #ifndef _WIN32 124 | // GNUSTEP/Linux 125 | #include 126 | typedef uint32_t UInt32; 127 | typedef float Float32; 128 | typedef double Float64; 129 | inline static UInt32 EspSwapInt32(UInt32 x) { return htobe32(x); } 130 | // inline static Float32 EspSwapFloat32(Float32 x) { return htobe32(x); } 131 | inline static Float32 EspSwapFloat32(const Float32 x) { 132 | unsigned char* c = (unsigned char*)&x; 133 | uint32_t y = c[3] + (c[2] << 8) + (c[1] << 16) + (c[0] << 24); 134 | float z = *((float*)&y); 135 | return z; 136 | } 137 | inline static Float64 EspSwapFloat64(double x) { return htobe64(x); } 138 | #endif 139 | 140 | #endif 141 | 142 | #ifndef GNUSTEP 143 | // Cocoa/OSX 144 | inline static UInt32 EspSwapInt32(UInt32 x) {return CFSwapInt32(x); } 145 | inline static Float32 EspSwapFloat32(Float32 x) { 146 | CFSwappedFloat32 y = CFConvertFloatHostToSwapped(x); 147 | return *((Float32*)(&y)); 148 | } 149 | inline static Float64 EspSwapFloat64(double x) { return CFSwapInt64(*((UInt64*)&x)); } 150 | #endif 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /EspGrid/EspClock.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspClock.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspClock.h" 20 | #import "EspGridDefs.h" 21 | #import 22 | 23 | @implementation EspClock 24 | @synthesize syncMode; 25 | @synthesize flux; 26 | @synthesize fluxStatus; 27 | 28 | +(EspClock*) clock 29 | { 30 | static EspClock* sharedClock = nil; 31 | if(!sharedClock)sharedClock = [[EspClock alloc] init]; 32 | return sharedClock; 33 | } 34 | 35 | -(id) init 36 | { 37 | peerList = [EspPeerList peerList]; 38 | network = [EspNetwork network]; 39 | osc = [EspOsc osc]; 40 | for(int x=0;x<1024;x++) 41 | { 42 | fluxTimes[x] = 0; 43 | fluxValues[x] = 0; 44 | } 45 | fluxIndex = 0; 46 | [self setFluxStatus:@"---"]; 47 | self = [super init]; 48 | 49 | // setup BEACON opcode 50 | beacon.header.opcode = ESP_OPCODE_BEACON; 51 | beacon.header.length = sizeof(EspBeaconOpcode); 52 | beacon.majorVersion = ESPGRID_MAJORVERSION; 53 | beacon.minorVersion = ESPGRID_MINORVERSION; 54 | beacon.subVersion = ESPGRID_SUBVERSION; 55 | copyPersonIntoOpcode((EspOpcode*)&beacon); 56 | 57 | // setup ACK opcode 58 | ack.header.opcode = ESP_OPCODE_ACK; 59 | ack.header.length = sizeof(EspAckOpcode); 60 | copyPersonIntoOpcode((EspOpcode*)&ack); 61 | 62 | countOfBeaconsIssued = 0; 63 | [self sendBeacon:nil]; // issue initial beacon 64 | 65 | return self; 66 | } 67 | 68 | -(void) personChanged 69 | { 70 | copyPersonIntoOpcode((EspOpcode*)&beacon); 71 | copyPersonIntoOpcode((EspOpcode*)&ack); 72 | } 73 | 74 | 75 | -(void) changeSyncMode:(int)mode 76 | { 77 | postEvent([NSString stringWithFormat:@"changing clock mode to %d",mode],self); 78 | [self setSyncMode:mode]; 79 | [[peerList selfInPeerList] setSyncMode:mode]; 80 | } 81 | 82 | -(void) issueBeacon 83 | { 84 | countOfBeaconsIssued++; 85 | beacon.beaconCount = countOfBeaconsIssued; 86 | beacon.syncMode = syncMode; 87 | [network sendOpcode:(EspOpcode*)&beacon]; 88 | [peerList updateStatus]; 89 | } 90 | 91 | -(void) issueAck:(EspBeaconOpcode*)b 92 | { 93 | strncpy(ack.nameRcvd,b->header.name,16); 94 | ack.nameRcvd[15] = 0; // i.e. make sure strings have only 15 readable characters in them 95 | strncpy(ack.ipRcvd,b->header.ip,16); 96 | ack.ipRcvd[15] = 0; 97 | ack.beaconCount = b->beaconCount; 98 | ack.beaconSend = b->header.sendTime; 99 | ack.beaconReceive = b->header.receiveTime; 100 | [network sendOpcode:(EspOpcode*)&ack]; 101 | } 102 | 103 | -(void) sendBeacon:(NSTimer*)t 104 | { 105 | [self issueBeacon]; 106 | NSTimeInterval nextBeacon = 1.0 + (4.0*((double)rand()/RAND_MAX)); // beacons 1 - 5 seconds apart 107 | [NSTimer scheduledTimerWithTimeInterval:nextBeacon 108 | target:self 109 | selector:@selector(sendBeacon:) 110 | userInfo:nil 111 | repeats:NO]; 112 | } 113 | 114 | -(void) handleOpcode:(EspOpcode*)opcode; 115 | { 116 | NSAssert(opcode->opcode == ESP_OPCODE_BEACON || opcode->opcode == ESP_OPCODE_ACK || opcode->opcode == ESP_OPCODE_PEERINFO,@"EspClock sent unrecognized opcode"); 117 | 118 | if(opcode->opcode==ESP_OPCODE_BEACON) { 119 | postProtocolLow([NSString stringWithFormat:@"BEACON from %s at %s",opcode->name,opcode->ip],self); 120 | [self issueAck:(EspBeaconOpcode*)opcode]; 121 | [peerList receivedBeacon:(EspBeaconOpcode*)opcode]; 122 | } 123 | 124 | if(opcode->opcode==ESP_OPCODE_ACK) { 125 | EspPeer* peer = [peerList receivedAck:(EspAckOpcode*)opcode]; // harvest data into peerlist 126 | [peer issuePeerInfoOpcode]; 127 | } 128 | 129 | if(opcode->opcode==ESP_OPCODE_PEERINFO) { 130 | [peerList receivedPeerInfo:(EspPeerInfoOpcode*)opcode]; 131 | } 132 | } 133 | 134 | -(void) handleOldOpcode:(NSDictionary*)opcode 135 | { 136 | NSAssert(false,@"empty old opcode handler called"); 137 | } 138 | 139 | -(EspTimeType) adjustmentForPeer:(EspPeer*)peer 140 | { 141 | if(peer) return [peer adjustmentForSyncMode:[self syncMode]]; 142 | else 143 | { 144 | postCritical(@"nil peer in [EspClock adjustmentForPeer]",self); 145 | return 0; 146 | } 147 | } 148 | 149 | -(void)updateflux:(EspTimeType)adjToAdj 150 | { 151 | EspTimeType now = monotonicTime(); 152 | fluxTimes[fluxIndex] = now; 153 | if(adjToAdj<0)adjToAdj*=-1; 154 | fluxIndex++; 155 | if(fluxIndex>=1024)fluxIndex=0; 156 | EspTimeType total = 0; 157 | EspTimeType threshold = now - 5000000000; 158 | for(int x=0;x<1024;x++)if(fluxTimes[x]>threshold)total = total + fluxValues[x]; 159 | [self setValue:[NSNumber numberWithLongLong:total] forKey:@"flux"]; 160 | NSLog(@"flux = %llu",flux); 161 | if(flux==0) [self setFluxStatus:@"stable"]; 162 | else if(flux<1000) [self setFluxStatus:[NSString stringWithFormat:@"%lld nanos",flux]]; 163 | else if(flux<1000000) [self setFluxStatus:[NSString stringWithFormat:@"%lld micros",flux/1000]]; 164 | else if(flux<1000000000) [self setFluxStatus:[NSString stringWithFormat:@"%lld millis",flux/1000000]]; 165 | else [self setFluxStatus:@">1second"]; 166 | } 167 | 168 | @end 169 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspOscTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscTests.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspOscTests.h" 20 | 21 | @implementation MockEspOscHandler 22 | @synthesize address; 23 | @synthesize parameters; 24 | @synthesize handled; 25 | -(id)init 26 | { 27 | self = [super init]; 28 | handled = FALSE; 29 | return self; 30 | } 31 | 32 | -(BOOL) handleOsc:(NSString*)a withParameters:(NSArray*)d 33 | { 34 | [self setAddress:a]; 35 | [self setParameters:d]; 36 | [self setHandled:TRUE]; 37 | return YES; 38 | } 39 | 40 | @end 41 | 42 | @implementation EspOscTests 43 | 44 | - (void)setUp 45 | { 46 | [super setUp]; 47 | oscReceive = [[EspOsc alloc] init]; 48 | handler = [[MockEspOscHandler alloc] init]; 49 | [oscReceive addHandler:handler forAddress:@"/esp/osc/test"]; 50 | } 51 | 52 | - (void)tearDown 53 | { 54 | [oscReceive release]; 55 | [handler release]; 56 | [super tearDown]; 57 | } 58 | 59 | -(void)testMatchedAddressIsHandled 60 | { 61 | char* msg = "/esp/osc/test\0\0\0,\0\0\0"; 62 | NSData* d = [NSData dataWithBytesNoCopy:msg length:20 freeWhenDone:NO]; 63 | [oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()]; 64 | STAssertTrue([handler handled],@"Matched OSC address should be handled."); 65 | } 66 | 67 | -(void)testMismatchedAddressIsNotHandled 68 | { 69 | char* msg = "/esp/osc/no!!\0\0\0"; 70 | NSData* d = [NSData dataWithBytesNoCopy:msg length:16 freeWhenDone:NO]; 71 | [oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()]; 72 | STAssertFalse([handler handled],@"Mismatched OSC address should not be handled."); 73 | } 74 | 75 | -(void)testNilDataThrowsException 76 | { 77 | STAssertThrows([oscReceive dataReceived:nil fromHost:@"127.0.0.1" atTime:EspGridTime()], 78 | @"nil data to oscReceive should throw exception."); 79 | } 80 | 81 | -(void)testZeroLengthDataThrowsException 82 | { 83 | char* msg = "\0"; 84 | NSData* d = [NSData dataWithBytesNoCopy:msg length:0 freeWhenDone:NO]; 85 | STAssertThrows([oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()], 86 | @"zero length data to oscReceive should throw exception."); 87 | } 88 | 89 | -(void)testMalformedAddressIsNotHandledWithoutException 90 | { 91 | char* msg = "\0\0"; 92 | NSData* d = [NSData dataWithBytesNoCopy:msg length:2 freeWhenDone:NO]; 93 | STAssertNoThrow([oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()], 94 | @"malformed address should not throw exception"); 95 | STAssertFalse([handler handled],@"malformed address should not be handled"); 96 | } 97 | 98 | -(void)testMissingIntParameterIsNotHandledWithoutException 99 | { 100 | char* msg = "/esp/osc/test\0\0\0,i\0\0"; 101 | NSData* d = [NSData dataWithBytesNoCopy:msg length:20 freeWhenDone:NO]; 102 | STAssertNoThrow([oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()], 103 | @"message missing int parameter should not throw exception"); 104 | STAssertFalse([handler handled],@"message missing int parameter should not be handled"); 105 | } 106 | 107 | -(void)testSingleIntParameterIsReceived 108 | { 109 | char* msg = "/esp/osc/test\0\0\0,i\0\0\0\0\0\1"; 110 | NSData* d = [NSData dataWithBytesNoCopy:msg length:24 freeWhenDone:NO]; 111 | [oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()]; 112 | STAssertTrue([[handler parameters] count]==1,@"receiving a single int by OSC should lead to 1 parameter"); 113 | STAssertTrue([[[handler parameters] objectAtIndex:0] isKindOfClass:[NSNumber class]], 114 | @"single int from OSC should be parsed as NSNumber"); 115 | STAssertTrue([[[handler parameters] objectAtIndex:0] isEqualTo:[NSNumber numberWithInt:1]], 116 | @"single int from OSC should have been equal to 1"); 117 | } 118 | 119 | -(void)testMissingFloatParameterIsNotHandledWithoutException 120 | { 121 | char* msg = "/esp/osc/test\0\0\0,f\0\0"; 122 | NSData* d = [NSData dataWithBytesNoCopy:msg length:20 freeWhenDone:NO]; 123 | STAssertNoThrow([oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()], 124 | @"message missing float parameter should not throw exception"); 125 | STAssertFalse([handler handled],@"message missing float parameter should not be handled"); 126 | } 127 | 128 | -(void)testSingleFloatParameterIsReceived 129 | { 130 | char* msg = "/esp/osc/test\0\0\0,f\0\0\0\0\0\1"; 131 | NSData* d = [NSData dataWithBytesNoCopy:msg length:24 freeWhenDone:NO]; 132 | [oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()]; 133 | STAssertTrue([[handler parameters] count]==1,@"receiving a single float by OSC should lead to 1 parameter"); 134 | STAssertTrue([[[handler parameters] objectAtIndex:0] isKindOfClass:[NSNumber class]], 135 | @"single float from OSC should be parsed as NSNumber"); 136 | } 137 | 138 | -(void)testMissingStringParameterIsNotHandledWithoutException 139 | { 140 | char* msg = "/esp/osc/test\0\0\0,s\0\0"; 141 | NSData* d = [NSData dataWithBytesNoCopy:msg length:20 freeWhenDone:NO]; 142 | STAssertNoThrow([oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()], 143 | @"message missing string parameter should not throw exception"); 144 | STAssertFalse([handler handled],@"message missing string parameter should not be handled"); 145 | } 146 | 147 | -(void)testSingleStringParameterIsReceived 148 | { 149 | char* msg = "/esp/osc/test\0\0\0,s\0\0test\0\0\0\0"; 150 | NSData* d = [NSData dataWithBytesNoCopy:msg length:28 freeWhenDone:NO]; 151 | [oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()]; 152 | STAssertTrue([[handler parameters] count]==1,@"receiving a single string by OSC should lead to 1 parameter"); 153 | STAssertTrue([[[handler parameters] objectAtIndex:0] isKindOfClass:[NSString class]], 154 | @"single string from OSC should be parsed as NSString"); 155 | STAssertTrue([[[handler parameters] objectAtIndex:0] isEqualTo:@"test"], 156 | @"single string from OSC should have been equal to 'test'"); 157 | } 158 | 159 | -(void)testUnterminatedSingleStringParameterIsNotHandled 160 | { 161 | char* msg = "/esp/osc/test\0\0\0,s\0\0test!!!!"; // !!!! added to "unterminate" string 162 | NSData* d = [NSData dataWithBytesNoCopy:msg length:24 freeWhenDone:NO]; 163 | [oscReceive dataReceived:d fromHost:@"127.0.0.1" atTime:EspGridTime()]; 164 | STAssertFalse([handler handled],@"receiving unterminated string by OSC should be unhandled"); 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /EspGrid/EspPeerList.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspPeerList.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspPeerList.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspPeerList 23 | @synthesize status; 24 | @synthesize selfInPeerList; 25 | 26 | +(EspPeerList*) peerList 27 | { 28 | static EspPeerList* sharedPeerList = nil; 29 | if(!sharedPeerList) sharedPeerList = [[EspPeerList alloc] init]; 30 | return sharedPeerList; 31 | } 32 | 33 | -(id) init { 34 | self = [super init]; 35 | peers = [[NSMutableArray alloc] init]; 36 | [self addSelfToPeerList]; 37 | [self updateStatus]; 38 | return self; 39 | } 40 | 41 | -(void) dealloc 42 | { 43 | [peers release]; 44 | [super dealloc]; 45 | } 46 | 47 | -(void)addSelfToPeerList 48 | { 49 | NSUserDefaults* x = [NSUserDefaults standardUserDefaults]; 50 | EspPeer* d = [[EspPeer alloc] init]; 51 | [d setName:[x stringForKey:@"person"]]; 52 | [d setIp:@"unknown"]; 53 | [d setMajorVersion:ESPGRID_MAJORVERSION]; 54 | [d setMinorVersion:ESPGRID_MINORVERSION]; 55 | [d setSubVersion:ESPGRID_SUBVERSION]; 56 | [d setVersion:[NSString stringWithFormat:@"%d.%d.%d", 57 | ESPGRID_MAJORVERSION, 58 | ESPGRID_MINORVERSION, 59 | ESPGRID_SUBVERSION]]; 60 | [peers addObject:d]; 61 | selfInPeerList = d; 62 | } 63 | 64 | -(EspPeer*) receivedBeacon:(EspBeaconOpcode*)opcode 65 | { 66 | // find or add the peer from whom the beacon has come 67 | NSString* name = [NSString stringWithCString:opcode->header.name encoding:NSUTF8StringEncoding]; 68 | EspPeer* peer = [self findPeerWithName:name]; 69 | if(peer == nil) peer = [self addNewPeer:opcode]; // note: only a BEACON can add a new peer 70 | [self willChangeValueForKey:@"peers"]; 71 | [peer processBeacon:opcode]; 72 | [self didChangeValueForKey:@"peers"]; 73 | return peer; 74 | } 75 | 76 | -(EspPeer*) receivedAck:(EspAckOpcode*)opcode 77 | { 78 | // find or add the peer from whom the ack has come 79 | NSString* name = [NSString stringWithCString:opcode->header.name encoding:NSUTF8StringEncoding]; 80 | EspPeer* peer = [self findPeerWithName:name]; 81 | if(peer == nil) return nil; // note: we don't do anything with a given peer unless we have received a prior BEACON 82 | 83 | // who is the ack for? 84 | NSString* ackForName = [NSString stringWithCString:opcode->nameRcvd encoding:NSUTF8StringEncoding]; 85 | EspPeer* ackFor = [self findPeerWithName:ackForName]; 86 | if(ackFor == nil) 87 | { 88 | // note: we don't do anything with a given peer unless we have received a prior BEACON 89 | NSString* m = [NSString stringWithFormat:@"ACK for unknown peer %@",ackForName]; 90 | postProtocolLow(m,self); 91 | return nil; 92 | } 93 | // process the ACK within the pertinent EspPeer instance... 94 | [self willChangeValueForKey:@"peers"]; 95 | if(ackFor == selfInPeerList) [peer processAckForSelf:opcode peerCount:(int)[peers count]]; 96 | else [peer processAck:opcode forOther:ackFor]; 97 | [self didChangeValueForKey:@"peers"]; 98 | [peer dumpAdjustments]; 99 | return peer; 100 | } 101 | 102 | -(void) receivedPeerInfo:(EspPeerInfoOpcode*)opcode 103 | { 104 | NSString* name1 = [NSString stringWithCString:opcode->header.name encoding:NSUTF8StringEncoding]; 105 | EspPeer* peer1 = [self findPeerWithName:name1]; 106 | if(peer1 == nil) { // note: we don't do anything with a given peer unless we have received a prior BEACON 107 | postProtocolLow(@"received PEERINFO from peer before receiving BEACON from that peer",self); 108 | return; 109 | } 110 | NSString* name2 = [NSString stringWithCString:opcode->peerName encoding:NSUTF8StringEncoding]; 111 | EspPeer* peer2 = [self findPeerWithName:name2]; 112 | if(peer2 == nil) { // note: we don't do anything with a given peer unless we have received a prior BEACON 113 | postProtocolLow(@"received PEERINFO about a peer before receiving BEACON from that peer",self); 114 | return; 115 | } 116 | postProtocolLow([NSString stringWithFormat:@"PEERINFO from %s-%s re %s-%s", 117 | opcode->header.name,opcode->header.ip,opcode->peerName,opcode->peerIp,nil],self); 118 | postProtocolLow([NSString stringWithFormat:@" recentLatency=%lld",opcode->recentLatency,nil],self); 119 | postProtocolLow([NSString stringWithFormat:@" lowestLatency=%lld",opcode->lowestLatency,nil],self); 120 | postProtocolLow([NSString stringWithFormat:@" averageLatency=%lld",opcode->averageLatency,nil],self); 121 | postProtocolLow([NSString stringWithFormat:@" refBeacon=%lld",opcode->refBeacon,nil],self); 122 | postProtocolLow([NSString stringWithFormat:@" refBeaconAverage=%lld",opcode->refBeaconAverage,nil],self); 123 | [peer1 dumpAdjustments]; 124 | [peer2 dumpAdjustments]; 125 | } 126 | 127 | 128 | -(EspPeer*) addNewPeer:(EspBeaconOpcode*)opcode 129 | { 130 | // extract parameters from dictionary passed from opcode 131 | NSString* name = [NSString stringWithCString:opcode->header.name encoding:NSUTF8StringEncoding]; 132 | NSString* ip = [NSString stringWithCString:opcode->header.ip encoding:NSUTF8StringEncoding]; 133 | char theirMajorVersion = opcode->majorVersion; 134 | char theirMinorVersion = opcode->minorVersion; 135 | 136 | // check EspGrid version of peer/sender and warn in cases of mismatch 137 | if(theirMajorVersion < ESPGRID_MAJORVERSION || 138 | (theirMajorVersion==ESPGRID_MAJORVERSION && theirMinorVersion < ESPGRID_MINORVERSION)) 139 | { 140 | NSString* s = [NSString stringWithFormat:@"%@ is running old EspGrid %hhu.%2hhu",name,theirMajorVersion,theirMinorVersion]; 141 | postCritical(s,self); 142 | } 143 | else if(theirMajorVersion > ESPGRID_MAJORVERSION || 144 | (theirMajorVersion==ESPGRID_MAJORVERSION && theirMinorVersion > ESPGRID_MINORVERSION)) 145 | { 146 | NSString* s = [NSString stringWithFormat:@"%@ is running newer EspGrid %hhu.%2hhu",name,theirMajorVersion,theirMinorVersion]; 147 | postCritical(s,self); 148 | } 149 | 150 | // add new peer to peerlist 151 | [self willChangeValueForKey:@"peers"]; 152 | EspPeer* x = [[EspPeer alloc] init]; 153 | [peers addObject:x]; 154 | [self didChangeValueForKey:@"peers"]; 155 | postEvent([NSString stringWithFormat:@"adding %@ at %@",name,ip], self); 156 | [self updateStatus]; 157 | return x; 158 | } 159 | 160 | -(void) updateStatus 161 | { 162 | long c = [peers count]; 163 | for(EspPeer* x in peers) [x updateLastBeaconStatus]; 164 | if(c>1) [self setStatus:[NSString stringWithFormat:@"%ld peers on grid",c]]; 165 | else [self setStatus:@"no peers found yet"]; 166 | } 167 | 168 | -(void) personChanged 169 | { 170 | NSUserDefaults* defs = [NSUserDefaults standardUserDefaults]; 171 | [selfInPeerList setName:[defs stringForKey:@"person"]]; 172 | for(EspPeer* x in peers) [x personChanged]; 173 | } 174 | 175 | -(EspPeer*) findPeerWithName:(NSString*)name 176 | { 177 | for(EspPeer* x in peers) if([[x name] isEqualToString:name]) return x; 178 | return nil; 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /EspGrid/EspOscSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspOscSocket.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012-2016 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspOscSocket.h" 20 | #import "EspGridDefs.h" 21 | #import 22 | 23 | #ifdef _WIN32 24 | #import 25 | #endif 26 | 27 | @implementation EspOscSocket 28 | @synthesize delegate; 29 | 30 | -(id) initWithPort: (int)p andDelegate:(id)d 31 | { 32 | self = [super init]; 33 | transmitData = [[NSMutableData alloc] initWithLength:ESP_OSC_SOCKET_BUFFER_SIZE]; 34 | if(!transmitData) { postProblem(@"unable to allocate transmitData", self); } 35 | transmitBuffer = (void*)[transmitData bytes]; 36 | receiveData = [[NSMutableData alloc] initWithLength:ESP_OSC_SOCKET_BUFFER_SIZE]; 37 | if(!receiveData) { postProblem(@"unable to allocate receiveData", self); } 38 | receiveBuffer = (void*)[receiveData bytes]; 39 | if(!receiveData) { postProblem(@"unable to allocate receiveData", self); } 40 | socketRef = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 41 | if(socketRef == -1) { postProblem(@"unable to create socket",self); } 42 | #ifndef _WIN32 43 | int reuseOn = 1; 44 | #else 45 | const char reuseOn = 1; 46 | #endif 47 | setsockopt(socketRef, SOL_SOCKET, SO_BROADCAST, &reuseOn, sizeof(reuseOn)); 48 | [self setDelegate:d]; 49 | port = p; 50 | if([self bindToPort:port] == YES) 51 | { 52 | thread = [[NSThread alloc] initWithTarget:self selector:@selector(udpReceiveLoop) object:nil]; 53 | [thread start]; 54 | } 55 | return self; 56 | } 57 | 58 | -(void) dealloc 59 | { 60 | close(socketRef); 61 | [receiveData release]; 62 | free(receiveBuffer); 63 | [transmitData release]; 64 | free(transmitBuffer); 65 | [super dealloc]; 66 | } 67 | 68 | -(BOOL) bindToPort:(unsigned int)p 69 | { 70 | #ifndef _WIN32 71 | int reuseOn = 1; 72 | #else 73 | const char reuseOn = 1; 74 | #endif 75 | setsockopt(socketRef, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); // maybe this isn't necessary? 76 | 77 | #ifndef _WIN32 78 | int timestamp = 1; 79 | int r = setsockopt(socketRef, SOL_SOCKET, SO_TIMESTAMP, ×tamp, sizeof(timestamp)); 80 | if(r!=0) postProblem(@"unable to set socket option", self); 81 | #endif 82 | 83 | // us.sin_len = sizeof(struct sockaddr_in); // probably needs to be commented out on Linux, seems to ok without on Cocoa too though 84 | us.sin_family = AF_INET; 85 | us.sin_port = htons(p); 86 | us.sin_addr.s_addr = htonl(INADDR_ANY); 87 | memset(&(us.sin_zero), 0, sizeof(us.sin_zero)); 88 | if (bind(socketRef, (const struct sockaddr*)&us, sizeof(us)) < 0) { 89 | NSString* s = [NSString stringWithFormat:@"unable to bind to port %d",p]; 90 | postProblem(s,self); 91 | close(socketRef); 92 | return NO; 93 | } 94 | NSString* l = [NSString stringWithFormat:@"bound to UDP port %d",p]; 95 | postLog(l,self); 96 | return YES; 97 | } 98 | 99 | -(void) udpReceiveLoop 100 | { 101 | [NSThread setThreadPriority:0.5]; 102 | for(;;) 103 | { 104 | #ifdef GNUSTEP 105 | NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // was this necessary for GNUstep,or...? it produces a segfault on OSX sometimes 106 | #endif 107 | #ifndef _WIN32 108 | struct msghdr msg; 109 | struct iovec entry; 110 | struct { struct cmsghdr cm; char control[512]; } control; 111 | memset(&msg, 0, sizeof(msg)); 112 | msg.msg_iov = &entry; 113 | msg.msg_iovlen = 1; 114 | entry.iov_base = receiveBuffer; 115 | entry.iov_len = ESP_OSC_SOCKET_BUFFER_SIZE; 116 | msg.msg_name = (caddr_t)&them; 117 | msg.msg_namelen = sizeof(them); 118 | msg.msg_control = &control; 119 | msg.msg_controllen = sizeof(control); 120 | long n = recvmsg(socketRef, &msg, 0); 121 | #else 122 | int s = sizeof(them); 123 | long n = recvfrom(socketRef, receiveBuffer, ESP_OSC_SOCKET_BUFFER_SIZE, 0, (struct sockaddr*)&them, &s); 124 | #endif 125 | EspTimeType monotonic = monotonicTime(); 126 | if(n>ESP_OSC_SOCKET_BUFFER_SIZE) 127 | { 128 | postProblem(@"received more data than buffer can handle",self); 129 | continue; 130 | } 131 | if(n>0) { 132 | 133 | (*(char*)(receiveBuffer+n)) = 0; 134 | NSString* h = [NSString stringWithCString:inet_ntoa(them.sin_addr) encoding:NSASCIIStringEncoding]; 135 | NSData* d = [[NSData alloc] initWithBytesNoCopy:receiveBuffer length:n freeWhenDone:NO]; 136 | them.sin_port = ntohs(them.sin_port); 137 | @try { 138 | NSDictionary* packet = [NSDictionary dictionaryWithObjectsAndKeys: 139 | d,@"data",h,@"host",[NSNumber numberWithInt:them.sin_port],@"port", 140 | [NSNumber numberWithUnsignedLongLong:monotonic],@"monotonicTime",nil]; 141 | [delegate performSelectorOnMainThread:@selector(packetReceived:) withObject:packet waitUntilDone:YES]; 142 | } 143 | @catch (NSException* exception) { 144 | NSString* msg = [NSString stringWithFormat:@"EXCEPTION in udpReceiveLoop: %@: %@",[exception name],[exception reason]]; 145 | postProblem(msg, nil); 146 | @throw; 147 | } 148 | [d release]; 149 | #ifdef GNUSTEP 150 | [pool drain]; 151 | #endif 152 | } 153 | else if (n==-1) 154 | { 155 | NSLog(@"***udpReceiveLoop error: %s",strerror(errno)); 156 | } 157 | } 158 | } 159 | 160 | static void sendOscData(int socketRef,const void* data,size_t length,NSString* host,int port) 161 | { 162 | assert([[NSThread currentThread] isMainThread]); // don't allow transmission other than from main thread 163 | if(host == nil) { postProblem(@"can't send when host==nil",nil); return; } 164 | if(port == 0) { postProblem(@"can't send when port==0",nil); return; } 165 | struct sockaddr_in address; 166 | // address.sin_len = sizeof(struct sockaddr_in); // this line needs to be commented out for GNUstep, apparently ok without on cocoa 167 | address.sin_family = AF_INET; 168 | address.sin_port = htons(port); 169 | inet_pton(AF_INET, [host UTF8String], &(address.sin_addr.s_addr)); 170 | memset(&(address.sin_zero), 0, sizeof(address.sin_zero)); 171 | ssize_t r = sendto(socketRef, data, length, 0, (struct sockaddr*)&address, (socklen_t)sizeof(address)); 172 | if(r != length) NSLog(@"*** sendto unable to send all %ld bytes to %@, bytes sent = %ld",length,host,r); 173 | } 174 | 175 | -(void)sendData: (NSData*)data toHost:(NSString*)host port:(int)p 176 | { 177 | sendOscData(socketRef, [data bytes], [data length], host, p); // use a specific port indicated by argument p 178 | } 179 | 180 | -(void)sendData: (NSData*)data toHost:(NSString*)host 181 | { 182 | sendOscData(socketRef, [data bytes], [data length], host, port); // use the port on which we listen 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /EspGrid/EspCodeShare.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspCodeShare.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspCodeShare.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspCodeShare 23 | @synthesize items; 24 | 25 | +(EspCodeShare*) codeShare 26 | { 27 | static EspCodeShare* sharedObject = nil; 28 | if(!sharedObject)sharedObject = [[EspCodeShare alloc] init]; 29 | return sharedObject; 30 | } 31 | 32 | -(id) init 33 | { 34 | self = [super init]; 35 | items = [[NSMutableArray alloc] init]; 36 | network = [EspNetwork network]; 37 | osc = [EspOsc osc]; 38 | clock = [EspClock clock]; 39 | return self; 40 | } 41 | 42 | -(NSUInteger) countOfShares 43 | { 44 | return [items count]; 45 | } 46 | 47 | -(void) shareCode:(NSString*)code withTitle:(NSString*)title 48 | { 49 | EspCodeShareItem* item = [EspCodeShareItem createWithLocalContent:code title:title timeStamp:monotonicTime()]; 50 | // *** NOTE: we have stamped codeshare items with local monotonic time 51 | // but we have not reworked codeshare system to adjust local times based on measured differences 52 | [self willChangeValueForKey:@"items"]; 53 | [items addObject:item]; 54 | [self didChangeValueForKey:@"items"]; 55 | [item announceOnUdp:network]; 56 | } 57 | 58 | -(NSString*) getOrRequestItem:(EspCodeShareItem*)item 59 | { 60 | return [item getOrRequestContentOnUdp:network]; 61 | } 62 | 63 | -(void) handleAnnounceShare:(NSDictionary*)d 64 | { 65 | // 1. validate info in received opcode - if anything is missing or awry, abort with a warning 66 | NSString* name = [d objectForKey:@"sourceName"]; 67 | if(name == nil) { postWarning(@"received ANNOUNCE_SHARE with no name",self); return; } 68 | if(![name isKindOfClass:[NSString class]]) { postWarning(@"received ANNOUNCE_SHARE with name not NSString",self); return; } 69 | if([name length]==0){ postWarning(@"received ANNOUNCE_SHARE with zero length name",self); return; } 70 | 71 | NSNumber* timeStamp = [d objectForKey:@"timeStamp"]; 72 | if(timeStamp == nil) { postWarning(@"received ANNOUNCE_SHARE with no timeStamp",self); return; } 73 | if(![timeStamp isKindOfClass:[NSNumber class]]) { postWarning(@"received ANNOUNCE_SHARE with timeStamp not NSNumber",self); return; } 74 | 75 | NSString* title = [d objectForKey:@"title"]; 76 | if(title == nil) { postWarning(@"received ANNOUNCE_SHARE with no title",self); return; } 77 | if(![title isKindOfClass:[NSString class]]) { postWarning(@"received ANNOUNCE_SHARE with title not NSString",self); return; } 78 | if([title length]==0){ postWarning(@"received ANNOUNCE_SHARE with zero length title",self); return; } 79 | 80 | NSNumber* length = [d objectForKey:@"length"]; 81 | if(length == nil) { postWarning(@"received ANNOUNCE_SHARE with no length",self); return; } 82 | if(![length isKindOfClass:[NSNumber class]]) { postWarning(@"received ANNOUNCE_SHARE with length not NSNumber",self); return; } 83 | if([length longValue]<=0){ postWarning(@"received ANNOUNCE_SHARE with length <= 0",self); return; } 84 | 85 | // 2. see if this item is already in local array - if it is, ignore... 86 | EspCodeShareItem* item = nil; 87 | for(EspCodeShareItem* x in items) 88 | { 89 | if([x isEqualToName:name timeStamp:timeStamp]) 90 | { 91 | item = x; 92 | break; 93 | } 94 | } 95 | 96 | // 3. if item is not already present, then create a new EspCodeShareItem and add it to local array 97 | if(item == nil) 98 | { 99 | item = [EspCodeShareItem createWithGridSource:name 100 | title:title 101 | timeStamp:[timeStamp doubleValue] 102 | length:[length longValue]]; 103 | [self willChangeValueForKey:@"items"]; 104 | [items addObject:item]; 105 | [self didChangeValueForKey:@"items"]; 106 | NSString *log = [NSString stringWithFormat:@"received ANNOUNCE_SHARE from %@ for timeStamp %@",name,timeStamp]; 107 | postLog(log, self); 108 | } 109 | } 110 | 111 | 112 | -(void) handleRequestShare:(NSDictionary*)d 113 | { 114 | NSString* sourceName = [d objectForKey:@"sourceName"]; 115 | EspTimeType timeStamp = [[d objectForKey:@"timeStamp"] doubleValue]; 116 | for(EspCodeShareItem* x in items) 117 | { 118 | if([[x sourceName] isEqualToString:sourceName] && 119 | [x timeStamp] == timeStamp) 120 | { 121 | [x deliverAllOnUdp:network]; 122 | return; 123 | } 124 | } 125 | } 126 | 127 | 128 | -(void) handleDeliverShare:(NSDictionary*)d 129 | { 130 | NSString* sourceName = [d objectForKey:@"sourceName"]; 131 | EspTimeType timeStamp = [[d objectForKey:@"timeStamp"] doubleValue]; 132 | 133 | EspCodeShareItem* item; 134 | for(EspCodeShareItem* x in items) 135 | { 136 | if([[x sourceName] isEqualToString:sourceName] && 137 | [x timeStamp] == timeStamp) 138 | { 139 | item = x; 140 | break; 141 | } 142 | } 143 | if(item == nil) return; // ? for now... later, receiving delivery of unknown items should start an entry 144 | 145 | NSString* fragment = [d objectForKey:@"fragment"]; 146 | unsigned long index = [[d objectForKey:@"index"] longValue]; 147 | [item addFragment:fragment index:index]; 148 | // [self copyShareToClipboardIfRequested:item]; // factoring this out for cross-platform dvpmt 149 | NSLog(@"receiving DELIVER_SHARE for %@ with timeStamp %lld (%ld of %ld)", 150 | sourceName,timeStamp,index+1,[item nFragments]); 151 | } 152 | 153 | -(void) handleOpcode:(EspOpcode *)opcode 154 | { 155 | NSAssert(false,@"empty new opcode handler called"); 156 | } 157 | 158 | -(void) handleOldOpcode:(NSDictionary*)d 159 | { 160 | int opcode = [[d objectForKey:@"opcode"] intValue]; 161 | 162 | if(opcode == ESP_OPCODE_ANNOUNCESHARE) // receiving ANNOUNCE_SHARE 163 | { 164 | [self handleAnnounceShare:d]; 165 | } 166 | else if(opcode == ESP_OPCODE_REQUESTSHARE) // receiving REQUEST_SHARE 167 | { 168 | NSString* l = [NSString stringWithFormat:@"receiving REQUEST_SHARE for %@ on %@", 169 | [d valueForKey:@"timeStamp"],[d valueForKey:@"sourceName"]]; 170 | postLog(l,self); 171 | // we should change this so that any grid instance can respond to a request if it has the goods... 172 | NSString* ourName = [[NSUserDefaults standardUserDefaults] stringForKey:@"person"]; 173 | if([[d valueForKey:@"sourceName"] isEqual:ourName]) 174 | { 175 | [self handleRequestShare:d]; 176 | } 177 | } 178 | else if(opcode == ESP_OPCODE_DELIVERSHARE) // receiving DELIVER_SHARE 179 | { 180 | [self handleDeliverShare:d]; 181 | } 182 | } 183 | 184 | -(BOOL) handleOsc:(NSString*)address withParameters:(NSArray*)d fromHost:(NSString*)h port:(int)p 185 | { 186 | if([address isEqualToString:@"/esp/codeShare/post"]) 187 | { 188 | if([d count]!=2){postProblem(@"received /esp/codeShare/post with wrong number of parameters",self); return NO;} 189 | [self shareCode:[d objectAtIndex:1] withTitle:[d objectAtIndex:0]]; 190 | return YES; 191 | } 192 | return NO; 193 | } 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /EspGridOSX/Help/osc.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid OSC specification 23 | 24 | 25 | 26 |

      OSC issued by EspGrid

      27 | 28 |
        29 | 30 |
      • /clock/offset/r [offset]
        31 | where:
        32 | offset = (EspGrid synchronized time) - (local system clock relative to UNIX epoch starting Jan 1 1970) [float64]
        33 | This message is part of the emerging TOPLAP clock protocol, and is only issued in response to receiving /clock/offset/q. The response message is sent to a specific host and port. If /clock/offset/q has no arguments, then the response is sent to the IP address and port deduced from the incoming message. If /clock/offset/q has one argument, that is interpreted as a port, and the response is sent to that port on whichever host is deduced from the incoming message. If /clock/offset/q has two arguments, then they are assumed to be the port and host (in that order) to which the response should be sent.
        34 |
      • 35 | 36 |
      • /esp/tempo/r [on] [bpm] [time] [n] [length]
        37 | where:
        38 | on = tempo is on or paused [0 or 1, int32]
        39 | bpm = beats per minute [float64]
        40 | time = the logical time (EspGrid sync time) at which a specific beat from this metre occurs [float64]
        41 | n = the number of this beat (total accumulated beats before that beat) [int32]
        42 | length = the number of beats in a bar
        43 | The specification of this message is expected to evolve pending the TOPLAP clock protocol discussion. Like /clock/offset/r this message is request-driven, and is issued only in reponse to /esp/tempo/r. Like /clock/offset/q, no arguments to /esp/tempo/q results in a response to the port and host deduced from the source of the request, one argument explicitly sets a port (with the host deduced), and two arguments explicitly sets the port and host (in that order) for the response.
      • 44 | 45 |
      • /esp/beat [n] [l] [d]
        46 | where:
        47 | n = the number of the beat, i.e. 1 for the first beat of a bar [int32]
        48 | l = the number of beats in a bar [int32]
        49 | d = the duration of a beat in seconds [float32]
        50 |
      • 51 | 52 |
      • /esp/chat/receive [remainder of items are text of message]
      • 53 | 54 |
      • plus any user-specified messages created with the /esp/msg/* operations described below (in OSC control)
      • 55 | 56 |
      57 | 58 | 59 |

      OSC Control

      60 | 61 |
      In some cases, it is advantageous to control the grid software from other software. For example, a Max patch might be created to turn beat generation on, then change the tempo in a specific way at specific times. Additionally, EspGrid can be used to broadcast arbitrary OSC messages at specific synchronized times (ranging from immediately to in the distant future). Here is a complete list of the OSC messages to which the EspGrid application responds. These should be sent from a local application to port 5510:
      62 | 63 |
        64 |
      • /esp/beat/on [0 or 1 to turn beat off or on respectively, int32]
      • 65 |
      • /esp/beat/tempo [tempo in beats per minute, float32/64]
      • 66 |
      • /esp/chat/send [remainder of items are text of message to send]
      • 67 |
      • /esp/codeShare/post [title] [remainder of items are text of code to be shared]
      • 68 |
      • /esp/msg/now [/some/other/oscAddress] [rest of parameters to OSC message] (the specified OSC message is sent immediately to everyone on the grid)
      • 69 |
      • /esp/msg/soon [/some/other/oscAddress] [rest of parameters to OSC message] (the specified OSC message is schedule on every computer a very short moment from now - and thus can be synchronized)
      • 70 |
      • /esp/msg/future [timeIncrement in seconds, type: float32/64] [/some/other/oscAddress] [rest of parameters to message] (the specified OSC message is sceheduled [timeIncrement] seconds into the future, synchronized on all machines running EspGrid
      • 71 |
      • /esp/msg/nowStamp... (same as /esp/msg/now except that a timestamp (from the issuing computer) is added to msg received by all machines)
      • 72 |
      • /esp/msg/soonStamp... (same as /esp/msg/soon except that the scheduled time of the message is included in the msg received as first parameter)
      • 73 |
      • /esp/msg/futureStamp... (same as /esp/msg/future except that the scheduled time of the message is included in the msg received as first parameter)
      • 74 | 75 |
      • /esp/bridge/localGroup [string] (set the name of the local group when making a WAN bridge)
      • 76 |
      • /esp/bridge/localAddress [string] (set the address of this computer on the WAN for receiving bridge connections)
      • 77 |
      • /esp/bridge/localPort [string] (set the port on which this computer will receive WAN bridge connections - 5508 recommended)
      • 78 |
      • /esp/bridge/remoteAddress [string] (set the IP address of the remote group when making a bridge)
      • 79 |
      • /esp/bridge/remotePort [string] (set the port which the remote group has opened for a bridge - 5508 recommended)
      • 80 | 81 |
      82 | 83 |
      In addition, the user defaults/preferences of the application can be changed via OSC, by sending messages as follows:
      84 | 85 |
        86 |
      • /esp/name [string] (change your name on the grid)
      • 87 |
      • /esp/machine [string] (change this machine's name on the grid)
      • 88 |
      • /esp/broadcast [string] (change the local network broadcast address)
      • 89 |
      • /esp/clockMode [0-2] (change the clock mode)
      • 90 |
      • /esp/connectToMax [0 or 1] (connect to Max)
      • 91 |
      • /esp/connectToChuck [0 or 1] (connect to Chuck)
      • 92 |
      • /esp/connectToPD [0 or 1] (connect to PD)
      • 93 |
      • /esp/connectToSupercollider [0 or 1] (connect to Supercollider)
      • 94 |
      • /esp/customAddress1 [string] (custom connection to an application via UDP)
      • 95 |
      • /esp/customPort1 [int] (the port for the above custom connection via UDP)
      • 96 |
      • /esp/customAddress2 [string] (custom connection to an application via UDP)
      • 97 |
      • /esp/customPort2 [int] (the port for the above custom connection via UDP)
      • 98 |
      • /esp/customAddress3 [string] (custom connection to an application via UDP)
      • 99 |
      • /esp/customPort3 [int] (the port for the above custom connection via UDP)
      • 100 |
      • /esp/customAddress4 [string] (custom connection to an application via UDP)
      • 101 |
      • /esp/customPort4 [int] (the port for the above custom connection via UDP)
      • 102 |
      103 | 104 |
      EspGrid is gradually adopting the emerging TOPLAP clock protocols. In order for a language-side client to receive a /clock/offset/r message (see above) with the offset value that, when added to the system clock time, synchronizes with EspGrid logical time, the language-side client sends EspGrid the message /clock/offset/q. The /clock/offset/r message is not sent on any automatic schedule by EspGrid - it is only sent in response to a /clock/offset/q message. Similarly, synchronized tempo information (that can be used to generate highly accurate tempo streams within a language client) is accessed by issuing the /esp/tempo/q query, and receiving an /esp/tempo/r response.
      105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /EspGridOSX/Help/history.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 | EspGrid Version History 23 | 24 | 25 | 26 | 27 | 28 |

      EspGrid Version History

      29 | 30 |
      v0.53.8 refactoring EspBeat so beat parameters are a single key-value controller entry
      31 |
      v0.53.7 bug fix (for OS X): querying clockmode on OS X threw exception
      32 |
      v0.53.6 bug fix (for WIN32): hopefully final byte-order problem on Windows
      33 |
      v0.53.5 fixed a WIN32 byte-order bug affecting OSC floats in EspOsc
      34 |
      v0.53.4 OSC preference changes on GNUSTEP/WIN32 take effect immediately
      35 |
      v0.53.3 command-line preference changes on Windows are immediately persistent
      36 |
      v0.53.2 various small fixes
      37 |
      v0.53.1 can query person-machine-broadcast-clockMode and version via OSC
      38 |
      v0.52.1 subscription system for OSC events replaces user preferences approach
      39 |
      v0.51.4 small fixes to enable building on Windows (GNUSTEP/MinGW)
      40 |
      v0.51.3 Esp.sc using experimental SuperCollider Main.monotonicClock method
      41 |
      v0.51.2 EspMessage was not setting self as delegate for EspQueue; beginning update of /esp/message protocol to times as two 32-bit ints
      42 |
      v0.51.0 return to (basically) single-threaded architecture; new EspChannel and EspNetwork class in preparation for more complex network topologies
      43 |
      v0.50.5 more inter-thread fixes; reformatting of OSC echo of EspChat messages
      44 |
      v0.50.4 more inter-thread fixes
      45 |
      v0.50.3 fixed inter-thread problem in EspCodeShare by adding a lock; various tiny fixes/changes
      46 |
      v0.50.2 EspKeyValueController now broadcasts only when a node is the authority for a value, OR the authority for a value hasn't been heard from (time since beacon) in more than 10 seconds
      47 |
      v0.50.1 some pruning of log statements; beacons (internal protocol) now broadcast in a random pattern (1-5s)
      48 |
      v0.50.0 two major changes to synchronization: (a) using monotonic, epochless machine clock instead of gettimeofday and (b) each node translates monotonic, epochless machine time of each other peer into its own frame, instead of maintaining a shared, synchronized clock.
      49 |
      v0.49 Linux/GNUstep port; request-driven messages to flexible host/ports; all UDP sent from meaningful ports; cross-thread notification mechanism for chat/log posting; general refactoring
      50 |
      v0.48 critical bug fix in mode 1 clock synchronization
      51 |
      v0.47 request-driven /clock/offset/* /esp/tempo/* messages (cf. TOPLAP discussions), EspClock for SuperCollider
      52 |
      v0.46 local and remote ports for EspBridge are configurable (in GUI and via OSC)
      53 |
      v0.45 changes to public protocol /esp/clock and /esp/beat messages, command line help, complete OSC controls
      54 |
      v0.44 basic WAN bridge in private protocol, initial command line edition "espgridd"
      55 |
      v0.43 addition of continuous (slow) "/esp/clock [adjustedTime] [adjustment]" messages to public protocol
      56 |
      v0.42 reorganization of EspClock algorithms, flux measurement, plus other bug fixes
      57 |
      v0.41 various bug fixes - improved stability relative to 0.40
      58 |
      v0.40 reference broadcast synchronization algorithm added, plus other optimizations
      59 |
      v0.39 addition of system for switching synchronization algorithms, race-only algorithm added
      60 |
      v0.37-38 experiments with different EspClock algorithms
      61 |
      v0.36 using moving average of specific latency in EspClock/EspPeerList, lock mechanism in EspClock (constrains sync adjustments to 1ms or less), more protection against corrupt transmissions, expanded unit testing
      62 |
      v0.35 fixed nasty bug in EspClock, reduced CPU load from EspQueue, reduced bandwidth of EspKeyValueController
      63 |
      v0.34 reorganization of code sharing to support long (>MTU) code fragments; established unit testing for some classes
      64 |
      v0.33 various protections against malformed messages in EspOsc
      65 |
      v0.32 added integrated help system and app icon (the latter contributed by Kearon Roy Taylor)
      66 |
      v0.31 broadcast of immediate and scheduled arbitrary OSC messages (with or without timestamps included in the messages) (EspMessage/EspQueue)
      67 |
      v0.30 various small changes: clock system only warns of mismatched versions once, peers are deleted when no beacon for 2 mins, OSC control of EspBeat propagates over network properly
      68 |
      v0.29 fixed oversight in 0.28 that broke code sharing system
      69 |
      v0.28 refinements to code in EspBeat.m (including smooth on-the-fly tempo changes)
      70 |
      v0.27 designated port for PD; protect against divide by 0 in tempo; fixed bug where incomplete "custom application connection" in Preferences crashes app
      71 |
      v0.26 shift to tab view in Mac OS X GUI, implementation of log window separate from chat window
      72 |
      v0.25 EspCodeShare: all entries requestable/copyable, system to autocopy requested deliveries
      73 |
      v0.24 EspPeerList keeps track of how long since last communication with each peer, updates status
      74 |
      v0.23 ability to pick out specific pieces of shared code and grab them in EspCodeShare
      75 |
      v0.22 major overhaul of EspKeyValueController; first iPad version; begun to abandon Cocoa bindings
      76 |
      v0.21 EspPost system added for better handling of different types of "log" messages
      77 |
      v0.20 clock algorithm adjusts for peer-specific latency; other small changes
      78 |
      v0.19 name change to EspGrid, peer list includes self info
      79 |
      v0.18 OSC backdoor for hacked miniAudicle (add/replace shred in miniAudicle shares code)
      80 |
      v0.17 OSC control of EspBeat and OSC "back door" for EspChat
      81 |
      v0.16 basic EspCodeShare mechanisms via clipboard; shared control of EspBeat cyclelength
      82 |
      v0.15 crude shared control of EspBeat on the basis of EspKeyValueController
      83 |
      v0.14 addition of EspKeyValueController (network sharing of variables, KVC/KVO compliant)
      84 |
      v0.13 basic EspBeat completed without user/net control
      85 |
      v0.12 removed Automatic Reference Counting (ARC) and fixed compatibility with OS X 10.6
      86 |
      v0.11 beacon is transmitted 19 times in first 5 seconds (every 250ms) before reducing to 5s period
      87 |
      v0.10 replaced public domain AsyncUdpSocket with new EspSocket and UdpSend classes
      88 |
      v0.09 added first version of UDP burst system
      89 |
      v0.08 added Preferences panel
      90 |
      v0.07 basic EspChat working in GUI app
      91 |
      v0.06 move to app architecture - basic EspClock working in GUI app
      92 |
      v0.05 Improvements to espBeat [formerly espPulse]
      93 |
      v0.04 Improvements to espPulse
      94 |
      v0.03 Added espPulse
      95 |
      v0.02 Added espColourOrgan
      96 |
      v0.01 First version of espChat
      97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /EspGridUnitTests/EspClockTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspClockTests.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspClockTests.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspClockTests 23 | 24 | -(void) setUp 25 | { 26 | udp = [[MockEspNetwork alloc] init]; 27 | osc = [[EspOsc alloc] init]; 28 | peerList = [[EspPeerList alloc] init]; 29 | clock = [[EspClock alloc] init]; 30 | [clock setOsc:osc]; 31 | [clock setUdp:udp]; 32 | [clock setPeerList:peerList]; 33 | 34 | beacon = [NSMutableDictionary dictionary]; 35 | [beacon setObject:[NSNumber numberWithInt:ESP_OPCODE_BEACON] forKey:@"opcode"]; 36 | [beacon setObject:@"aName" forKey:@"name"]; 37 | [beacon setObject:@"aMachine" forKey:@"machine"]; 38 | [beacon setObject:@"192.168.2.1" forKey:@"ip"]; 39 | [beacon setObject:[NSNumber numberWithLong:123456] forKey:@"beaconCount"]; 40 | [beacon setObject:[NSNumber numberWithDouble:(EspGridTime())] forKey:@"timeReceived"]; 41 | [beacon setObject:[NSNumber numberWithDouble:(EspGridTime()-0.5)] forKey:@"beaconClock"]; 42 | [beacon setObject:[NSNumber numberWithInt:ESPGRID_MAJORVERSION] forKey:@"majorVersion"]; 43 | [beacon setObject:[NSNumber numberWithInt:ESPGRID_MINORVERSION] forKey:@"minorVersion"]; 44 | [beacon setObject:[NSNumber numberWithInt:2] forKey:@"syncMode"]; 45 | [beacon setObject:[NSNumber numberWithDouble:(EspGridTime()-120.0)] forKey:@"gridClockStart"]; 46 | [beacon setObject:[NSNumber numberWithBool:NO] forKey:@"urgent"]; 47 | 48 | ack = [NSMutableDictionary dictionary]; 49 | [ack setObject:[NSNumber numberWithInt:ESP_OPCODE_ACK] forKey:@"opcode"]; 50 | [ack setObject:@"bName" forKey:@"name"]; 51 | [ack setObject:@"bMachine" forKey:@"machine"]; 52 | [ack setObject:@"192.168.2.2" forKey:@"ip"]; 53 | [ack setObject:[[NSUserDefaults standardUserDefaults] stringForKey:@"name"] forKey:@"nameRcvd"]; 54 | [ack setObject:[[NSUserDefaults standardUserDefaults] stringForKey:@"machine"] forKey:@"machineRcvd"]; 55 | [ack setObject:@"192.168.2.1" forKey:@"ipRcvd"]; 56 | [ack setObject:[NSNumber numberWithLong:123456] forKey:@"beaconCount"]; 57 | [ack setObject:[NSNumber numberWithDouble:(EspGridTime())] forKey:@"timeReceived"]; 58 | [ack setObject:[NSNumber numberWithDouble:(EspGridTime()-1.0)] forKey:@"beaconReceived"]; 59 | [ack setObject:[NSNumber numberWithDouble:(EspGridTime()-2.0)] forKey:@"beaconClock"]; 60 | [ack setObject:[NSNumber numberWithDouble:(EspGridTime()-3.0)] forKey:@"ackClock"]; 61 | [ack setObject:[NSNumber numberWithDouble:(EspGridTime()-4.0)] forKey:@"clock"]; 62 | [ack setObject:[NSNumber numberWithInt:ESPGRID_MAJORVERSION] forKey:@"majorVersion"]; 63 | [ack setObject:[NSNumber numberWithInt:ESPGRID_MINORVERSION] forKey:@"minorVersion"]; 64 | 65 | } 66 | 67 | -(void) tearDown 68 | { 69 | [clock release]; 70 | [udp release]; 71 | [osc release]; 72 | [peerList release]; 73 | } 74 | 75 | -(void) invalidateString:(NSString*)key onOpcode:(NSMutableDictionary*)opcode withMsg:(NSString*)msg 76 | { 77 | [opcode removeObjectForKey:key]; 78 | STAssertNoThrow([clock handleOpcode:opcode],msg); 79 | STAssertFalse([clock handleOpcode:opcode],msg); 80 | [opcode setObject:[NSNumber numberWithInt:666] forKey:key]; 81 | STAssertNoThrow([clock handleOpcode:opcode], msg); 82 | STAssertFalse([clock handleOpcode:opcode], msg); 83 | [opcode setObject:@"" forKey:key]; 84 | STAssertNoThrow([clock handleOpcode:opcode], msg); 85 | STAssertFalse([clock handleOpcode:opcode], msg); 86 | } 87 | 88 | -(void) invalidateNumber:(NSString*)key onOpcode:(NSMutableDictionary*)opcode withMsg:(NSString*)msg 89 | { 90 | [opcode removeObjectForKey:key]; 91 | STAssertNoThrow([clock handleOpcode:opcode], msg); 92 | STAssertFalse([clock handleOpcode:opcode],msg); 93 | [opcode setObject:@"oops" forKey:key]; 94 | STAssertNoThrow([clock handleOpcode:opcode],msg); 95 | STAssertFalse([clock handleOpcode:opcode],msg); 96 | } 97 | 98 | -(void) testCompleteBeaconOpcodeIsHandledNoException 99 | { 100 | STAssertNoThrow([clock handleOpcode:beacon], @"complete BEACON opcode shouldn't throw exception"); 101 | STAssertTrue([clock handleOpcode:beacon],@"complete BEACON opcode should be handled (return TRUE)"); 102 | } 103 | 104 | -(void) testCompleteAckOpcodeIsHandledNoException 105 | { 106 | STAssertNoThrow([clock handleOpcode:ack], @"complete ACK opcode shouldn't throw exception"); 107 | STAssertTrue([clock handleOpcode:ack],@"complete ACK opcode should be handled (return TRUE)"); 108 | } 109 | 110 | -(void) testOpcodeWithInvalidOpcode 111 | { 112 | [self invalidateNumber:@"opcode" onOpcode:beacon withMsg:@"opcode with invalid opcode shouldn't throw or be handled"]; 113 | } 114 | 115 | -(void) testBeaconWithInvalidName 116 | { 117 | [self invalidateString:@"name" onOpcode:beacon withMsg:@"BEACON with invalid name shouldn't throw or be handled"]; 118 | } 119 | 120 | -(void) testBeaconWithInvalidMachine 121 | { 122 | [self invalidateString:@"machine" onOpcode:beacon withMsg:@"BEACON with invalid machine shouldn't throw or be handled"]; 123 | } 124 | 125 | -(void) testBeaconWithInvalidIP 126 | { 127 | [self invalidateString:@"ip" onOpcode:beacon withMsg:@"BEACON with invalid ip shouldn't throw or be handled"]; 128 | } 129 | 130 | -(void) testBeaconWithInvalidMajorVersion 131 | { 132 | [self invalidateNumber:@"majorVersion" onOpcode:beacon withMsg:@"BEACON with invalid majorVersion shouldn't throw or be handled"]; 133 | } 134 | 135 | -(void) testBeaconWithInvalidMinorVersion 136 | { 137 | [self invalidateNumber:@"minorVersion" onOpcode:beacon withMsg:@"BEACON with invalid minorVersion shouldn't throw or be handled"]; 138 | } 139 | 140 | -(void) testAckWithInvalidNameNoHandleNoThrow 141 | { 142 | [self invalidateString:@"name" onOpcode:ack withMsg:@"ACK opcode with invalid name shouldn't throw or be handled"]; 143 | } 144 | 145 | -(void) testAckWithInvalidMachineNoHandleNoThrow 146 | { 147 | [self invalidateString:@"machine" onOpcode:ack withMsg:@"ACK opcode with invalid name shouldn't throw or be handled"]; 148 | } 149 | 150 | -(void) testAckWithInvalidIPNoHandleNoThrow 151 | { 152 | [self invalidateString:@"ip" onOpcode:ack withMsg:@"ACK opcode with invalid ip shouldn't throw or be handled"]; 153 | } 154 | 155 | -(void) testAckWithInvalidNameRcvdNoHandleNoThrow 156 | { 157 | [self invalidateString:@"nameRcvd" onOpcode:ack withMsg:@"ACK opcode with invalid nameRcvd shouldn't throw or be handled"]; 158 | } 159 | 160 | -(void) testAckWithInvalidMachineRcvdNoHandleNoThrow 161 | { 162 | [self invalidateString:@"machineRcvd" onOpcode:ack withMsg:@"ACK opcode with invalid machineRcvd shouldn't throw or be handled"]; 163 | } 164 | 165 | -(void) testAckWithInvalidIPRcvdNoHandleNoThrow 166 | { 167 | [self invalidateString:@"ipRcvd" onOpcode:ack withMsg:@"ACK opcode with invalid ipRcvd shouldn't throw or be handled"]; 168 | } 169 | 170 | -(void) testAckWithInvalidBeaconCount 171 | { 172 | [self invalidateNumber:@"beaconCount" onOpcode:ack withMsg:@"ACK with invalid beaconCount shouldn't throw or be handled"]; 173 | } 174 | 175 | -(void) testAckWithInvalidMajorVersion 176 | { 177 | [self invalidateNumber:@"majorVersion" onOpcode:ack withMsg:@"ACK with invalid majorVersion shouldn't throw or be handled"]; 178 | } 179 | 180 | -(void) testAckWithInvalidMinorVersion 181 | { 182 | [self invalidateNumber:@"minorVersion" onOpcode:ack withMsg:@"ACK with invalid minorVersion shouldn't throw or be handled"]; 183 | } 184 | 185 | -(void) testAckWithInvalidClock 186 | { 187 | [self invalidateNumber:@"clock" onOpcode:ack withMsg:@"ACK with invalid clock shouldn't throw or be handled"]; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /EspGrid/EspMessage.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspMessage.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspMessage.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspMessage 23 | 24 | +(EspMessage*) message 25 | { 26 | static EspMessage* sharedObject = nil; 27 | if(!sharedObject)sharedObject = [[EspMessage alloc] init]; 28 | return sharedObject; 29 | } 30 | 31 | -(id) init 32 | { 33 | self = [super init]; 34 | network = [EspNetwork network]; 35 | osc = [EspOsc osc]; 36 | clock = [EspClock clock]; 37 | peerList = [EspPeerList peerList]; 38 | queue = [[EspQueue alloc] init]; 39 | [queue setDelegate:self]; 40 | return self; 41 | } 42 | 43 | -(void) respondToQueuedItem:(id)item 44 | { 45 | [osc transmit:(NSArray*)item log:NO]; 46 | } 47 | 48 | -(void) sendMessageNow:(NSArray*)params 49 | { 50 | EspTimeType t = monotonicTime(); 51 | NSMutableDictionary* d = [[NSMutableDictionary alloc] init]; 52 | [d setObject:params forKey:@"params"]; 53 | [d setObject:[NSNumber numberWithLongLong:t] forKey:@"time"]; 54 | [network sendOldOpcode:ESP_OPCODE_OSCNOW withDictionary:d]; 55 | [osc transmit:params log:NO]; 56 | } 57 | 58 | -(void) sendMessageNowStamped:(NSArray*)params 59 | { 60 | EspTimeType t = monotonicTime(); 61 | NSNumber* nSecs = [NSNumber numberWithLongLong:t/1000000000]; 62 | NSNumber* nNanos = [NSNumber numberWithDouble:t%1000000000]; 63 | NSMutableArray* a = [NSMutableArray arrayWithArray:params]; 64 | [a insertObject:nSecs atIndex:1]; // insert time stamp (seconds) into parameters // NOT RIGHT: needs to be translated 65 | [a insertObject:nNanos atIndex:2]; // insert time stamp (nanoseconds) into parameters 66 | NSMutableDictionary* d = [[NSMutableDictionary alloc] init]; 67 | [d setObject:a forKey:@"params"]; 68 | [d setObject:[NSNumber numberWithLongLong:t] forKey:@"time"]; 69 | [network sendOldOpcode:ESP_OPCODE_OSCNOW withDictionary:d]; 70 | [osc transmit:a log:NO]; 71 | } 72 | 73 | -(void) sendMessageSoon:(NSArray*)params 74 | { 75 | EspTimeType t = monotonicTime(); 76 | t += 100000000; // fixed latency for now (100ms), change this later (should be maximum real latency from peerlist) 77 | NSMutableDictionary* d = [[NSMutableDictionary alloc] init]; 78 | [d setObject:params forKey:@"params"]; 79 | [d setObject:[NSNumber numberWithLongLong:t] forKey:@"time"]; 80 | [network sendOldOpcode:ESP_OPCODE_OSCFUTURE withDictionary:d]; 81 | [queue addItem:params atTime:t]; 82 | } 83 | 84 | 85 | -(void) sendMessageSoonStamped:(NSArray*)params 86 | { 87 | EspTimeType t = monotonicTime(); 88 | t += 100000000; // fixed latency for now (100ms), change this later (should be maximum real latency from peerlist) 89 | NSNumber* n = [NSNumber numberWithLongLong:t]; 90 | NSMutableArray* a = [NSMutableArray arrayWithArray:params]; 91 | [a insertObject:n atIndex:1]; // insert time stamp into parameters 92 | NSMutableDictionary* d = [[NSMutableDictionary alloc] init]; 93 | [d setObject:a forKey:@"params"]; 94 | [d setObject:[NSNumber numberWithLongLong:t] forKey:@"time"]; 95 | [network sendOldOpcode:ESP_OPCODE_OSCFUTURE withDictionary:d]; 96 | [queue addItem:a atTime:t]; 97 | } 98 | 99 | -(void) sendMessageFuture:(NSArray*)params 100 | { 101 | EspTimeType t = monotonicTime(); 102 | EspTimeType iSecs = [[params objectAtIndex:0] longLongValue]; 103 | EspTimeType iNanos = [[params objectAtIndex:1] longLongValue]; 104 | EspTimeType i = iSecs*1000000000+iNanos; 105 | NSLog(@"%lld",i+t); 106 | NSMutableDictionary* d = [[NSMutableDictionary alloc] init]; 107 | NSMutableArray* a = [NSMutableArray arrayWithArray:params]; 108 | [a removeObjectAtIndex:0]; // remove time increment parameter (seconds) 109 | [a removeObjectAtIndex:0]; // remove time increment parameter (nanoseconds) 110 | [d setObject:a forKey:@"params"]; 111 | [d setObject:[NSNumber numberWithLongLong:t+i] forKey:@"time"]; 112 | [network sendOldOpcode:ESP_OPCODE_OSCFUTURE withDictionary:d]; 113 | [queue addItem:a atTime:t+i]; 114 | } 115 | 116 | -(void) sendMessageFutureStamped:(NSArray*)params 117 | { 118 | EspTimeType t = monotonicTime(); 119 | EspTimeType iSecs = [[params objectAtIndex:0] floatValue]; 120 | EspTimeType iNanos = [[params objectAtIndex:1] longLongValue]; 121 | EspTimeType i = iSecs*1000000000+iNanos; 122 | NSNumber* nSecs = [NSNumber numberWithLongLong:(t+i)/1000000000]; 123 | NSNumber* nNanos = [NSNumber numberWithLongLong:(t+i)%1000000000]; 124 | NSMutableArray* a = [NSMutableArray arrayWithArray:params]; 125 | [a removeObjectAtIndex:0]; // remove time increment parameter (seconds) 126 | [a removeObjectAtIndex:0]; // remove time increment parameter (nanoseconds) 127 | [a insertObject:nSecs atIndex:1]; // insert time stamp (seconds) into parameters 128 | [a insertObject:nNanos atIndex:2]; // insert time stamp (nanoseconds) into parameters 129 | NSMutableDictionary* d = [[NSMutableDictionary alloc] init]; 130 | [d setObject:a forKey:@"params"]; 131 | [d setObject:[NSNumber numberWithLongLong:t+i] forKey:@"time"]; 132 | [network sendOldOpcode:ESP_OPCODE_OSCFUTURE withDictionary:d]; 133 | [queue addItem:a atTime:t+i]; 134 | } 135 | 136 | -(void) handleOpcode:(EspOpcode *)opcode 137 | { 138 | NSAssert(false,@"empty new opcode handler called"); 139 | } 140 | 141 | -(void) handleOldOpcode:(NSDictionary*)d; 142 | { 143 | int opcode = [[d objectForKey:@"opcode"] intValue]; 144 | 145 | if(opcode==ESP_OPCODE_OSCNOW) { 146 | NSMutableArray* params = [NSMutableArray arrayWithArray:[d objectForKey:@"params"]]; 147 | [osc transmit:params log:NO]; 148 | return; 149 | } 150 | if(opcode==ESP_OPCODE_OSCFUTURE) { 151 | EspTimeType t = [[d objectForKey:@"time"] longLongValue]; // trigger time in other's terms 152 | NSString* name = [d objectForKey:@"name"]; 153 | EspPeer* peer = [peerList findPeerWithName:name]; 154 | if(peer == nil) 155 | { 156 | NSString* m = [NSString stringWithFormat:@"dropping OSCFUTURE from unknown peer %@",name]; 157 | postProtocolLow(m,self); 158 | return; 159 | } 160 | EspTimeType adjustment = [clock adjustmentForPeer:peer]; 161 | if(adjustment == 0) 162 | { 163 | NSString* m = [NSString stringWithFormat:@"dropping OSCFUTURE because clock adjustment 0 for peer %@",name]; 164 | postProtocolLow(m,self); 165 | return; 166 | } 167 | EspTimeType t2 = t + adjustment; 168 | NSMutableArray* params = [NSMutableArray arrayWithArray:[d objectForKey:@"params"]]; 169 | // [params addObject:[d objectForKey:@"time"]]; // append timestamp as final parameter 170 | // NSLog(@" description of params=%@",[params description]); 171 | [queue addItem:params atTime:t2]; 172 | } 173 | } 174 | 175 | -(BOOL) handleOsc:(NSString*)address withParameters:(NSArray*)params fromHost:(NSString*)h port:(int)p 176 | { 177 | if([address isEqualToString:@"/esp/msg/now"]) 178 | { 179 | [self sendMessageNow:params]; 180 | return YES; 181 | } 182 | else if([address isEqualToString:@"/esp/msg/nowStamp"]) 183 | { 184 | [self sendMessageNowStamped:params]; 185 | return YES; 186 | } 187 | else if([address isEqualToString:@"/esp/msg/soon"]) 188 | { 189 | [self sendMessageSoon:params]; 190 | return YES; 191 | } 192 | else if([address isEqualToString:@"/esp/msg/soonStamp"]) 193 | { 194 | [self sendMessageSoonStamped:params]; 195 | return YES; 196 | } 197 | else if([address isEqualToString:@"/esp/msg/future"]) 198 | { 199 | [self sendMessageFuture:params]; 200 | return YES; 201 | } 202 | else if([address isEqualToString:@"/esp/msg/futureStamp"]) 203 | { 204 | [self sendMessageFutureStamped:params]; 205 | return YES; 206 | } 207 | return NO; 208 | } 209 | 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /EspGrid/EspCodeShareItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // EspCodeShareItem.m 3 | // 4 | // This file is part of EspGrid. EspGrid is (c) 2012,2013 by David Ogborn. 5 | // 6 | // EspGrid is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // EspGrid is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with EspGrid. If not, see . 18 | 19 | #import "EspCodeShareItem.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspCodeShareItem 23 | @synthesize complete; 24 | @synthesize title; 25 | @synthesize content; 26 | @synthesize timeStamp; 27 | @synthesize contentLength; 28 | @synthesize sourceName; 29 | @synthesize sourceMachine; 30 | @synthesize nFragments; 31 | 32 | 33 | +(id)createWithLocalContent:(NSString*)c title:(NSString*)t timeStamp:(EspTimeType)ts 34 | { 35 | id r = [EspCodeShareItem alloc]; 36 | return [r initWithLocalContent:c title:t timeStamp:ts]; 37 | } 38 | 39 | -(id)initWithLocalContent:(NSString*)c title:(NSString*)t timeStamp:(EspTimeType)ts 40 | { 41 | self = [super init]; 42 | 43 | NSAssert(c != nil, @"can't create local EspCodeShareItem with nil content"); 44 | NSAssert([c length] > 0, @"can't create local EspCodeShareItem with no content"); 45 | [self setContent:c]; 46 | [self setContentLength:[c length]]; 47 | nFragments = (contentLength / ESPGRID_CODESHARE_FRAGMENTSIZE)+1; 48 | 49 | NSAssert(t != nil, @"can't create local EspCodeShareItem with nil title"); 50 | NSAssert([t length] > 0, @"can't create local EspCodeShareItem with no title"); 51 | [self setTitle:t]; 52 | 53 | NSAssert(ts > 0.0, @"can't create local EspCodeShareItem with timeStamp < 0.0"); 54 | [self setTimeStamp:ts]; 55 | 56 | [self setSourceName:[[NSUserDefaults standardUserDefaults] objectForKey:@"person"]]; 57 | [self setSourceMachine:[[NSUserDefaults standardUserDefaults] objectForKey:@"machine"]]; 58 | [self setComplete:YES]; 59 | 60 | return self; 61 | } 62 | 63 | +(id)createWithGridSource:(NSString*)n machine:(NSString*)m title:(NSString*)t timeStamp:(EspTimeType)ts length:(unsigned long)l; 64 | { 65 | id r = [EspCodeShareItem alloc]; 66 | return [r initWithGridSource:n machine:m title:t timeStamp:ts length:l]; 67 | } 68 | 69 | -(id)initWithGridSource:(NSString*)n machine:(NSString*)m title:(NSString*)t timeStamp:(EspTimeType)ts length:(unsigned long)l 70 | { 71 | self = [super init]; 72 | 73 | NSAssert(t != nil, @"can't create grid EspCodeShareItem with nil title"); 74 | NSAssert([t length] > 0, @"can't create grid EspCodeShareItem with no title"); 75 | [self setTitle:t]; 76 | 77 | NSAssert(n != nil, @"can't create grid EspCodeShareItem with no source name"); 78 | NSAssert([n length] > 0, @"can't create grid EspCodeShareItem with no source name"); 79 | [self setSourceName:n]; 80 | 81 | NSAssert(m != nil, @"can't create grid EspCodeShareItem with no source machine"); 82 | NSAssert([m length] > 0, @"can't create grid EspCodeShareItem with no source machine"); 83 | [self setSourceMachine:m]; 84 | 85 | NSAssert(ts > 0.0, @"can't create grid EspCodeShareItem with 0 timestamp"); 86 | [self setTimeStamp:ts]; 87 | 88 | NSAssert(l > 0, @"can't create grid EspCodeShareItem with size <= 0"); 89 | [self setContentLength:l]; 90 | 91 | nFragments = (l / ESPGRID_CODESHARE_FRAGMENTSIZE)+1; 92 | fragments = [[NSMutableArray alloc] initWithCapacity:nFragments]; 93 | for(int x=0;x. 18 | 19 | #import "EspAppDelegate.h" 20 | #import "EspGridDefs.h" 21 | 22 | @implementation EspAppDelegate 23 | 24 | @synthesize window = _window; 25 | 26 | -(IBAction)bridgeRemoteAdddress:(id)sender 27 | { 28 | [[esp bridge] setHost:[espBridgeRemoteAddress stringValue]]; 29 | } 30 | 31 | -(IBAction)bridgeRemotePort:(id)sender 32 | { 33 | [[esp bridge] setPort:[espBridgeRemotePort intValue]]; 34 | } 35 | 36 | -(IBAction)logOSCChanged:(id)sender 37 | { 38 | if([espLogOSC state]) [[EspOsc osc] setEchoToLog:YES]; 39 | else [[EspOsc osc] setEchoToLog:NO]; 40 | } 41 | 42 | -(IBAction)beatOn:(id)sender 43 | { 44 | if([beatOn state]) [[esp beat] turnBeatOn]; 45 | else [[esp beat] turnBeatOff]; 46 | } 47 | 48 | -(IBAction)beatTempo:(id)sender 49 | { 50 | [[esp beat] changeTempo:beatTempo.doubleValue]; 51 | } 52 | 53 | -(IBAction)sendChatMessage:(id)sender 54 | { 55 | NSString* msg = [sender stringValue]; 56 | if([msg length]>0) 57 | { 58 | [[esp chat] sendMessage:[sender stringValue]]; 59 | [espChatMsg setStringValue:@""]; 60 | } 61 | } 62 | 63 | -(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 64 | { 65 | id nv = [change objectForKey:NSKeyValueChangeNewKey]; 66 | if([keyPath isEqualToString:@"beat.on"]) [beatOn setState:[nv boolValue]]; 67 | else if([keyPath isEqualToString:@"beat.tempo"])[beatTempo setStringValue:nv]; 68 | else if([keyPath isEqualToString:@"bridge.localGroup"]) [espBridgeLocalGroup setStringValue:nv]; 69 | else if([keyPath isEqualToString:@"bridge.remoteAddress"]) [espBridgeRemoteAddress setStringValue:nv]; 70 | else if([keyPath isEqualToString:@"bridge.remoteGroup"]) [espBridgeRemoteGroup setStringValue:nv]; 71 | else if([keyPath isEqualToString:@"bridge.remoteClaimedAddress"]) [espBridgeRemoteClaimedAddress setStringValue:nv]; 72 | else if([keyPath isEqualToString:@"bridge.remoteClaimedPort"]) [espBridgeRemoteClaimedPort setStringValue:nv]; 73 | else if([keyPath isEqualToString:@"bridge.remotePackets"]) [espBridgeRemotePackets setStringValue:nv]; 74 | else NSLog(@"PROBLEM: received KVO notification for unexpected keyPath %@",keyPath); 75 | } 76 | 77 | 78 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 79 | { 80 | [esp addObserver:self forKeyPath:@"beat.on" options:NSKeyValueObservingOptionNew context:nil]; 81 | [esp addObserver:self forKeyPath:@"beat.tempo" options:NSKeyValueObservingOptionNew context:nil]; 82 | [esp addObserver:self forKeyPath:@"bridge.localGroup" options:NSKeyValueObservingOptionNew context:nil]; 83 | [esp addObserver:self forKeyPath:@"bridge.remoteAddress" options:NSKeyValueObservingOptionNew context:nil]; 84 | [esp addObserver:self forKeyPath:@"bridge.remoteGroup" options:NSKeyValueObservingOptionNew context:nil]; 85 | [esp addObserver:self forKeyPath:@"bridge.remoteClaimedAddress" options:NSKeyValueObservingOptionNew context:nil]; 86 | [esp addObserver:self forKeyPath:@"bridge.remoteClaimedPort" options:NSKeyValueObservingOptionNew context:nil]; 87 | [esp addObserver:self forKeyPath:@"bridge.remotePackets" options:NSKeyValueObservingOptionNew context:nil]; 88 | 89 | [esp setValue:[esp valueForKeyPath:@"beat.on"] forKeyPath:@"beat.on"]; 90 | [esp setValue:[esp valueForKeyPath:@"beat.tempo"] forKeyPath:@"beat.tempo"]; 91 | 92 | NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; 93 | [nc addObserver:self selector:@selector(postChatNotification:) name:@"chat" object:nil]; 94 | [nc addObserver:self selector:@selector(postLogNotification:) name:@"log" object:nil]; 95 | 96 | [NSTimer scheduledTimerWithTimeInterval:0.25 97 | target:self 98 | selector:@selector(updateClock:) 99 | userInfo:nil 100 | repeats:YES]; 101 | } 102 | 103 | -(void) updateClock:(NSTimer*)x 104 | { 105 | // EspTimeType t = [[esp clock] adjustment]; 106 | EspTimeType t = -1; // *** not applicable anymore! 107 | NSString* s = [NSString stringWithFormat:@"%lld",t]; 108 | [espClockAdjustment setStringValue:s]; 109 | [espClockFlux setStringValue:[[[esp clock] fluxStatus] copy]]; 110 | } 111 | 112 | -(void)postChatNotification:(NSNotification*)n 113 | { 114 | [espChatOutput insertText:[NSString stringWithFormat:@"%@\n",[[n userInfo] objectForKey:@"text"]]]; 115 | } 116 | 117 | -(void)postLogNotification:(NSNotification*)n 118 | { 119 | [espLogOutput insertText:[NSString stringWithFormat:@"%@\n",[[n userInfo] objectForKey:@"text"]]]; 120 | } 121 | 122 | -(IBAction)showPreferences:(id)sender 123 | { 124 | if(!preferencesPanel)preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"EspPreferencesPanel"]; 125 | [preferencesPanel showWindow:self]; 126 | } 127 | 128 | -(IBAction)showDetailedPeerList:(id)sender 129 | { 130 | if(!detailedPeerList) detailedPeerList = [[EspDetailedPeerListController alloc] initWithOwner:self]; 131 | [detailedPeerList showWindow:self]; 132 | } 133 | 134 | -(IBAction)shareClipboard:(id)sender 135 | { 136 | NSPasteboard* pasteBoard = [NSPasteboard generalPasteboard]; 137 | NSArray* types = [pasteBoard types]; 138 | if([types containsObject:NSStringPboardType]) { 139 | NSString* text = [pasteBoard stringForType:NSStringPboardType]; 140 | [[esp codeShare] shareCode:text withTitle:@"clipboard"]; 141 | } else postWarning(@"empty clipboard!",self); 142 | } 143 | 144 | -(IBAction)grabShare:(id)sender 145 | { 146 | long c = [[[NSApplication sharedApplication] currentEvent] clickCount]; 147 | long i = [sender selectedRow]; 148 | if(c==2 && i>=0) 149 | { 150 | [[[esp codeShare] items] sortUsingDescriptors:[codeShareController sortDescriptors]]; 151 | EspCodeShareItem* item = [[[esp codeShare] items] objectAtIndex:i]; // not crazy about this reaching in... 152 | NSString* content = [[esp codeShare] getOrRequestItem:item]; 153 | if(content != nil) 154 | { 155 | NSPasteboard* pasteBoard = [NSPasteboard generalPasteboard]; 156 | [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; 157 | [pasteBoard setString:content forType:NSStringPboardType]; 158 | } 159 | } 160 | } 161 | 162 | -(IBAction)copyLogToClipboard:(id)sender 163 | { 164 | NSPasteboard* pasteBoard = [NSPasteboard generalPasteboard]; 165 | [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; 166 | NSString* logText = [[[espLogOutput textStorage] string] copy]; 167 | [pasteBoard setString:logText forType:NSStringPboardType]; 168 | } 169 | 170 | -(IBAction)helpPreferences:(id)sender 171 | { 172 | NSString *locBookName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]; 173 | [[NSHelpManager sharedHelpManager] openHelpAnchor:@"preferences" inBook:locBookName]; 174 | } 175 | 176 | -(IBAction)helpPeerList:(id)sender 177 | { 178 | NSString *locBookName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]; 179 | [[NSHelpManager sharedHelpManager] openHelpAnchor:@"peerlist" inBook:locBookName]; 180 | } 181 | 182 | -(IBAction)helpMain:(id)sender 183 | { 184 | NSString *locBookName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]; 185 | [[NSHelpManager sharedHelpManager] openHelpAnchor:@"main" inBook:locBookName]; 186 | } 187 | 188 | -(IBAction)helpCode:(id)sender 189 | { 190 | NSString *locBookName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]; 191 | [[NSHelpManager sharedHelpManager] openHelpAnchor:@"code" inBook:locBookName]; 192 | } 193 | 194 | -(IBAction)tickOnBeatsChanged:(id)sender 195 | { 196 | NSMenuItem* mi = (NSMenuItem*)sender; 197 | if([mi state]) 198 | { 199 | NSLog(@"turning ticking off!!!"); 200 | [[esp beat] stopTicking]; 201 | [mi setState:NO]; 202 | } 203 | else 204 | { 205 | NSLog(@"turning ticking on!!!"); 206 | [mi setState:[[esp beat] startTicking]]; 207 | } 208 | } 209 | 210 | @end 211 | --------------------------------------------------------------------------------