Skip to content

Commit fd63a37

Browse files
committed
Merge branch 'master' into testing
2 parents 0e72ecc + 3111eda commit fd63a37

14 files changed

Lines changed: 232 additions & 76 deletions

File tree

Builds/MacOSX/open-ephys.xcodeproj/xcshareddata/xcschemes/open-ephys (App).xcscheme

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
33
LastUpgradeVersion = "0730"
4-
version = "1.7">
4+
version = "1.8">
55
<BuildAction
66
parallelizeBuildables = "YES"
77
buildImplicitDependencies = "YES">
@@ -65,7 +65,6 @@
6565
buildConfiguration = "Debug"
6666
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
6767
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
68-
language = ""
6968
launchStyle = "0"
7069
useCustomWorkingDirectory = "NO"
7170
ignoresPersistentStateOnLaunch = "NO"

Builds/VisualStudio2013/open-ephys.sln

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ Global
3232
GlobalSection(SolutionProperties) = preSolution
3333
HideSolutionNode = FALSE
3434
EndGlobalSection
35+
GlobalSection(Performance) = preSolution
36+
HasPerformanceSessions = true
37+
EndGlobalSection
3538
EndGlobal

Source/Plugins/EventBroadcaster/EventBroadcaster.cpp

Lines changed: 132 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,85 @@
1111
#include "EventBroadcaster.h"
1212
#include "EventBroadcasterEditor.h"
1313

14-
std::shared_ptr<void> EventBroadcaster::getZMQContext() {
15-
// Note: C++11 guarantees that initialization of static local variables occurs exactly once, even
16-
// if multiple threads attempt to initialize the same static local variable concurrently.
14+
EventBroadcaster::ZMQContext* EventBroadcaster::sharedContext = nullptr;
15+
CriticalSection EventBroadcaster::sharedContextLock{};
16+
17+
EventBroadcaster::ZMQContext::ZMQContext(const ScopedLock& lock)
1718
#ifdef ZEROMQ
18-
static const std::shared_ptr<void> ctx(zmq_ctx_new(), zmq_ctx_destroy);
19-
#else
20-
static const std::shared_ptr<void> ctx;
19+
: context(zmq_ctx_new())
20+
#endif
21+
{
22+
sharedContext = this;
23+
}
24+
25+
// ZMQContext is a ReferenceCountedObject with a pointer in each instance's
26+
// socket pointer, so this only happens when the last instance is destroyed.
27+
EventBroadcaster::ZMQContext::~ZMQContext()
28+
{
29+
ScopedLock lock(sharedContextLock);
30+
sharedContext = nullptr;
31+
#ifdef ZEROMQ
32+
zmq_ctx_destroy(context);
33+
#endif
34+
}
35+
36+
void* EventBroadcaster::ZMQContext::createZMQSocket()
37+
{
38+
#ifdef ZEROMQ
39+
jassert(context != nullptr);
40+
return zmq_socket(context, ZMQ_PUB);
41+
#endif
42+
}
43+
44+
EventBroadcaster::ZMQSocketPtr::ZMQSocketPtr()
45+
: std::unique_ptr<void, decltype(&closeZMQSocket)>(nullptr, &closeZMQSocket)
46+
{
47+
ScopedLock lock(sharedContextLock);
48+
if (sharedContext == nullptr)
49+
{
50+
// first one, create the context
51+
context = new ZMQContext(lock);
52+
}
53+
else
54+
{
55+
// use already-created context
56+
context = sharedContext;
57+
}
58+
59+
#ifdef ZEROMQ
60+
reset(context->createZMQSocket());
61+
#endif
62+
}
63+
64+
EventBroadcaster::ZMQSocketPtr::~ZMQSocketPtr()
65+
{
66+
// close the socket before the context might get destroyed.
67+
reset(nullptr);
68+
}
69+
70+
int EventBroadcaster::unbindZMQSocket()
71+
{
72+
#ifdef ZEROMQ
73+
void* socket = zmqSocket.get();
74+
if (socket != nullptr && listeningPort != 0)
75+
{
76+
return zmq_unbind(socket, getEndpoint(listeningPort).toRawUTF8());
77+
}
2178
#endif
22-
return ctx;
79+
return 0;
2380
}
2481

82+
int EventBroadcaster::rebindZMQSocket()
83+
{
84+
#ifdef ZEROMQ
85+
void* socket = zmqSocket.get();
86+
if (socket != nullptr && listeningPort != 0)
87+
{
88+
return zmq_bind(socket, getEndpoint(listeningPort).toRawUTF8());
89+
}
90+
#endif
91+
return 0;
92+
}
2593

2694
void EventBroadcaster::closeZMQSocket(void* socket)
2795
{
@@ -30,16 +98,33 @@ void EventBroadcaster::closeZMQSocket(void* socket)
3098
#endif
3199
}
32100

101+
String EventBroadcaster::getEndpoint(int port)
102+
{
103+
return String("tcp://*:") + String(port);
104+
}
105+
106+
void EventBroadcaster::reportActualListeningPort(int port)
107+
{
108+
listeningPort = port;
109+
auto editor = static_cast<EventBroadcasterEditor*>(getEditor());
110+
if (editor)
111+
{
112+
editor->setDisplayedPort(port);
113+
}
114+
}
33115

34116
EventBroadcaster::EventBroadcaster()
35117
: GenericProcessor ("Event Broadcaster")
36-
, zmqContext (getZMQContext())
37-
, zmqSocket (nullptr, &closeZMQSocket)
38118
, listeningPort (0)
39119
{
40120
setProcessorType (PROCESSOR_TYPE_SINK);
41121

42-
setListeningPort(5557);
122+
int portToTry = 5557;
123+
while (setListeningPort(portToTry) == EADDRINUSE)
124+
{
125+
// try the next port, looking for one not in use
126+
portToTry++;
127+
}
43128
}
44129

45130

@@ -56,27 +141,53 @@ int EventBroadcaster::getListeningPort() const
56141
}
57142

58143

59-
void EventBroadcaster::setListeningPort(int port, bool forceRestart)
144+
int EventBroadcaster::setListeningPort(int port, bool forceRestart)
60145
{
61146
if ((listeningPort != port) || forceRestart)
62147
{
63148
#ifdef ZEROMQ
64-
zmqSocket.reset(zmq_socket(zmqContext.get(), ZMQ_PUB));
65-
if (!zmqSocket)
149+
// unbind current socket (if any) to free up port
150+
unbindZMQSocket();
151+
ZMQSocketPtr newSocket;
152+
auto editor = static_cast<EventBroadcasterEditor*>(getEditor());
153+
int status = 0;
154+
155+
if (!newSocket.get())
66156
{
67-
std::cout << "Failed to create socket: " << zmq_strerror(zmq_errno()) << std::endl;
68-
return;
157+
status = zmq_errno();
158+
std::cout << "Failed to create socket: " << zmq_strerror(status) << std::endl;
159+
}
160+
else
161+
{
162+
if (0 != zmq_bind(newSocket.get(), getEndpoint(port).toRawUTF8()))
163+
{
164+
status = zmq_errno();
165+
std::cout << "Failed to open socket: " << zmq_strerror(status) << std::endl;
166+
}
167+
else
168+
{
169+
// success
170+
zmqSocket.swap(newSocket);
171+
reportActualListeningPort(port);
172+
return status;
173+
}
69174
}
70175

71-
String url = String("tcp://*:") + String(port);
72-
if (0 != zmq_bind(zmqSocket.get(), url.toRawUTF8()))
176+
// failure, try to rebind current socket to previous port
177+
if (0 == rebindZMQSocket())
73178
{
74-
std::cout << "Failed to open socket: " << zmq_strerror(zmq_errno()) << std::endl;
75-
return;
179+
reportActualListeningPort(listeningPort);
76180
}
77-
#endif
181+
else
182+
{
183+
reportActualListeningPort(0);
184+
}
185+
return status;
78186

79-
listeningPort = port;
187+
#else
188+
reportActualListeningPort(port);
189+
return 0;
190+
#endif
80191
}
81192
}
82193

Source/Plugins/EventBroadcaster/EventBroadcaster.h

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
#include <memory>
2626

27-
2827
class EventBroadcaster : public GenericProcessor
2928
{
3029
public:
@@ -33,7 +32,8 @@ class EventBroadcaster : public GenericProcessor
3332
AudioProcessorEditor* createEditor() override;
3433

3534
int getListeningPort() const;
36-
void setListeningPort (int port, bool forceRestart = false);
35+
// returns 0 on success, else the errno value for the error that occurred.
36+
int setListeningPort (int port, bool forceRestart = false);
3737

3838
void process (AudioSampleBuffer& continuousBuffer) override;
3939
void handleEvent (const EventChannel* channelInfo, const MidiMessage& event, int samplePosition = 0) override;
@@ -44,14 +44,42 @@ class EventBroadcaster : public GenericProcessor
4444

4545

4646
private:
47-
void sendEvent(const MidiMessage& event, float eventSampleRate) const;
48-
static std::shared_ptr<void> getZMQContext();
49-
static void closeZMQSocket (void* socket);
47+
class ZMQContext : public ReferenceCountedObject
48+
{
49+
public:
50+
ZMQContext(const ScopedLock& lock);
51+
~ZMQContext() override;
52+
void* createZMQSocket();
53+
private:
54+
void* context;
55+
};
56+
57+
static void closeZMQSocket(void* socket);
58+
59+
class ZMQSocketPtr : public std::unique_ptr<void, decltype(&closeZMQSocket)>
60+
{
61+
public:
62+
ZMQSocketPtr();
63+
~ZMQSocketPtr();
64+
private:
65+
ReferenceCountedObjectPtr<ZMQContext> context;
66+
};
67+
68+
int unbindZMQSocket();
69+
int rebindZMQSocket();
5070

51-
const std::shared_ptr<void> zmqContext;
52-
std::unique_ptr<void, decltype (&closeZMQSocket)> zmqSocket;
71+
void sendEvent(const MidiMessage& event, float eventSampleRate) const;
72+
static String getEndpoint(int port);
73+
// called from getListeningPort() depending on success/failure of ZMQ operations
74+
void reportActualListeningPort(int port);
75+
76+
// share a "dumb" pointer that doesn't take part in reference counting.
77+
// want the context to be terminated by the time the static members are
78+
// destroyed (see: https://github.com/zeromq/libzmq/issues/1708)
79+
static ZMQContext* sharedContext;
80+
static CriticalSection sharedContextLock;
81+
ZMQSocketPtr zmqSocket;
5382
int listeningPort;
54-
5583
};
5684

5785

Source/Plugins/EventBroadcaster/EventBroadcasterEditor.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ void EventBroadcasterEditor::buttonEvent(Button* button)
4646
if (button == restartConnection)
4747
{
4848
EventBroadcaster* p = (EventBroadcaster*)getProcessor();
49-
p->setListeningPort(p->getListeningPort(), true);
49+
int status = p->setListeningPort(p->getListeningPort(), true);
50+
51+
#ifdef ZEROMQ
52+
if (status != 0)
53+
{
54+
CoreServices::sendStatusMessage(String("Restart failed: ") + zmq_strerror(status));
55+
}
56+
#endif
5057
}
5158
}
5259

@@ -58,6 +65,18 @@ void EventBroadcasterEditor::labelTextChanged(juce::Label* label)
5865
Value val = label->getTextValue();
5966

6067
EventBroadcaster* p = (EventBroadcaster*)getProcessor();
61-
p->setListeningPort(val.getValue());
68+
int status = p->setListeningPort(val.getValue());
69+
70+
#ifdef ZEROMQ
71+
if (status != 0)
72+
{
73+
CoreServices::sendStatusMessage(String("Port change failed: ") + zmq_strerror(status));
74+
}
75+
#endif
6276
}
6377
}
78+
79+
void EventBroadcasterEditor::setDisplayedPort(int port)
80+
{
81+
portLabel->setText(String(port), dontSendNotification);
82+
}

Source/Plugins/EventBroadcaster/EventBroadcasterEditor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class EventBroadcasterEditor : public GenericEditor, public Label::Listener
3030
void buttonEvent(Button* button) override;
3131
void labelTextChanged(juce::Label* label) override;
3232

33+
void setDisplayedPort(int port);
34+
3335
private:
3436
ScopedPointer<UtilityButton> restartConnection;
3537
ScopedPointer<Label> urlLabel;

Source/Plugins/EvntTrigAvg/EvntTrigAvg.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ void EvntTrigAvg::handleEvent(const EventChannel* eventInfo, const MidiMessage&
182182
else if (eventInfo->getChannelType() == EventChannel::TTL && eventInfo == eventChannelArray[triggerEvent])
183183
{// if TTL from right channel
184184
TTLEventPtr ttl = TTLEvent::deserializeFromMessage(event, eventInfo);
185-
if (ttl->getChannel() == triggerChannel)
185+
if (ttl->getChannel() == triggerChannel && ttl->getState())
186186
ttlTimestampBuffer.push_back(Event::getTimestamp(event)); // add timestamp of TTL to buffer
187187
}
188188
}
@@ -290,7 +290,7 @@ AudioProcessorEditor* EvntTrigAvg::createEditor()
290290

291291
float EvntTrigAvg::getSampleRate()
292292
{
293-
return juce::AudioProcessor::getSampleRate();
293+
return CoreServices::getGlobalSampleRate();
294294
}
295295

296296
int EvntTrigAvg::getLastTTLCalculated()

Source/Plugins/EvntTrigAvg/EvntTrigAvgCanvas.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,10 @@ void EvntTrigAvgDisplay::paint(Graphics &g)
234234
GraphUnit* graph;
235235
ScopedLock myScopedLock(*processor->getMutex());
236236
if(histoData[i][1]==0){ // if sortedId == 0
237-
graph = new GraphUnit(processor,canvas,channelColours[(histoData[i][0]+sizeof(channelColours))%(sizeof(channelColours))],labels[histoData[i][0]],&minMaxMean[i][2],&histoData[i][2]); // pass &histoData[i][2] instead of 3 to pass on how many bins are used
237+
graph = new GraphUnit(processor,canvas,channelColours[(histoData[i][0])%16],labels[histoData[i][0]],&minMaxMean[i][2],&histoData[i][2]); // pass &histoData[i][2] instead of 3 to pass on how many bins are used
238238
}
239239
else{
240-
graph = new GraphUnit(processor,canvas,channelColours[(histoData[i][0]+sizeof(channelColours))%(sizeof(channelColours))],"ID "+String(histoData[i][1]),&minMaxMean[i][2],&histoData[i][2]);
240+
graph = new GraphUnit(processor,canvas,channelColours[(histoData[i][0])%16],"ID "+String(histoData[i][1]),&minMaxMean[i][2],&histoData[i][2]);
241241
}
242242
graphs.push_back(graph);
243243
graph->setBounds(0, 40*(graphCount), width-20, 40);

0 commit comments

Comments
 (0)