~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to libs/midi++2/ipmidi_port.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2012 Paul Davis
 
3
 
 
4
    Using code from Rui Nuno Capela's qmidinet as inspiration.
 
5
    
 
6
    This program 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 2 of the License, or
 
9
    (at your option) any later version.
 
10
 
 
11
    This program 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 this program; if not, write to the Free Software
 
18
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 
 
20
    $Id: port.cc 12065 2012-04-23 16:23:48Z paul $
 
21
*/
 
22
#include <iostream>
 
23
#include <cstdio>
 
24
#include <fcntl.h>
 
25
#include <errno.h>
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <netdb.h>
 
29
 
 
30
#if defined(WIN32)
 
31
static WSADATA g_wsaData;
 
32
typedef int socklen_t;
 
33
#else
 
34
#include <unistd.h>
 
35
#include <sys/ioctl.h>
 
36
inline void closesocket(int s) { ::close(s); }
 
37
#endif
 
38
 
 
39
#include <jack/jack.h>
 
40
#include <jack/midiport.h>
 
41
 
 
42
#include "pbd/xml++.h"
 
43
#include "pbd/error.h"
 
44
#include "pbd/failed_constructor.h"
 
45
#include "pbd/convert.h"
 
46
#include "pbd/compose.h"
 
47
 
 
48
#include "midi++/types.h"
 
49
#include "midi++/ipmidi_port.h"
 
50
#include "midi++/channel.h"
 
51
 
 
52
using namespace MIDI;
 
53
using namespace std;
 
54
using namespace PBD;
 
55
 
 
56
IPMIDIPort::IPMIDIPort (int base_port, const string& iface)
 
57
        : Port (string_compose ("IPmidi@%1", base_port), Port::Flags (Port::IsInput|Port::IsOutput))
 
58
        , sockin (-1)
 
59
        , sockout (-1)
 
60
{
 
61
        if (!open_sockets (base_port, iface)) {
 
62
                throw (failed_constructor ());
 
63
        }
 
64
}
 
65
 
 
66
IPMIDIPort::IPMIDIPort (const XMLNode& node)
 
67
        : Port (node)
 
68
{
 
69
        /* base class does not class set_state() */
 
70
        set_state (node);
 
71
}
 
72
 
 
73
IPMIDIPort::~IPMIDIPort ()
 
74
{
 
75
        close_sockets ();
 
76
}
 
77
 
 
78
int
 
79
IPMIDIPort::selectable () const
 
80
 
81
        return sockin; 
 
82
}
 
83
 
 
84
XMLNode&
 
85
IPMIDIPort::get_state () const
 
86
{
 
87
        return Port::get_state ();
 
88
}
 
89
 
 
90
void
 
91
IPMIDIPort::set_state (const XMLNode& node)
 
92
{
 
93
        Port::set_state (node);
 
94
}
 
95
 
 
96
void
 
97
IPMIDIPort::close_sockets ()
 
98
{
 
99
        if (sockin >= 0) {
 
100
                ::closesocket (sockin);
 
101
                sockin = -1;
 
102
        }
 
103
        
 
104
        if (sockout >= 0) {
 
105
                ::closesocket (sockout);
 
106
                sockout = -1;
 
107
        }
 
108
}
 
109
 
 
110
static bool 
 
111
get_address (int sock, struct in_addr *inaddr, const string& ifname )
 
112
{
 
113
        // Get interface address from supplied name.
 
114
 
 
115
#if !defined(WIN32)
 
116
        struct ifreq ifr;
 
117
        ::strncpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name));
 
118
 
 
119
        if (::ioctl(sock, SIOCGIFFLAGS, (char *) &ifr)) {
 
120
                ::perror("ioctl(SIOCGIFFLAGS)");
 
121
                return false;
 
122
        }
 
123
 
 
124
        if ((ifr.ifr_flags & IFF_UP) == 0) {
 
125
                error << string_compose ("interface %1 is down", ifname) << endmsg;
 
126
                return false;
 
127
        }
 
128
 
 
129
        if (::ioctl(sock, SIOCGIFADDR, (char *) &ifr)) {
 
130
                ::perror("ioctl(SIOCGIFADDR)");
 
131
                return false;
 
132
        }
 
133
 
 
134
        struct sockaddr_in sa;
 
135
        ::memcpy(&sa, &ifr.ifr_addr, sizeof(struct sockaddr_in));
 
136
        inaddr->s_addr = sa.sin_addr.s_addr;
 
137
 
 
138
        return true;
 
139
 
 
140
#else
 
141
 
 
142
        return false;
 
143
 
 
144
#endif  // !WIN32
 
145
}
 
146
 
 
147
bool
 
148
IPMIDIPort::open_sockets (int base_port, const string& ifname)
 
149
{
 
150
        int protonum = 0;
 
151
        struct protoent *proto = ::getprotobyname("IP");
 
152
 
 
153
        if (proto) {
 
154
                protonum = proto->p_proto;
 
155
        }
 
156
 
 
157
        sockin = ::socket (PF_INET, SOCK_DGRAM, protonum);
 
158
        if (sockin < 0) {
 
159
                ::perror("socket(in)");
 
160
                return false;
 
161
        }
 
162
 
 
163
        struct sockaddr_in addrin;
 
164
        ::memset(&addrin, 0, sizeof(addrin));
 
165
        addrin.sin_family = AF_INET;
 
166
        addrin.sin_addr.s_addr = htonl(INADDR_ANY);
 
167
        addrin.sin_port = htons(base_port);
 
168
        
 
169
        if (::bind(sockin, (struct sockaddr *) (&addrin), sizeof(addrin)) < 0) {
 
170
                ::perror("bind");
 
171
                return false;
 
172
        }
 
173
        
 
174
        // Will Hall, 2007
 
175
        // INADDR_ANY will bind to default interface,
 
176
        // specify alternate interface nameon which to bind...
 
177
        struct in_addr if_addr_in;
 
178
        if (!ifname.empty()) {
 
179
                if (!get_address(sockin, &if_addr_in, ifname)) {
 
180
                        error << string_compose ("socket(in): could not find interface address for %1", ifname) << endmsg;
 
181
                        return false;
 
182
                }
 
183
                if (::setsockopt(sockin, IPPROTO_IP, IP_MULTICAST_IF,
 
184
                                 (char *) &if_addr_in, sizeof(if_addr_in))) {
 
185
                        ::perror("setsockopt(IP_MULTICAST_IF)");
 
186
                        return false;
 
187
                }
 
188
        } else {
 
189
                if_addr_in.s_addr = htonl (INADDR_ANY);
 
190
        }
 
191
        
 
192
        struct ip_mreq mreq;
 
193
        mreq.imr_multiaddr.s_addr = ::inet_addr("225.0.0.37");
 
194
        mreq.imr_interface.s_addr = if_addr_in.s_addr;
 
195
        if(::setsockopt (sockin, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)) < 0) {
 
196
                ::perror("setsockopt(IP_ADD_MEMBERSHIP)");
 
197
                fprintf(stderr, "socket(in): your kernel is probably missing multicast support.\n");
 
198
                return false;
 
199
        }
 
200
 
 
201
        // Output socket...
 
202
 
 
203
        sockout = ::socket (AF_INET, SOCK_DGRAM, protonum);
 
204
 
 
205
        if (sockout < 0) {
 
206
                ::perror("socket(out)");
 
207
                return false;
 
208
        }
 
209
        
 
210
        // Will Hall, Oct 2007
 
211
        if (!ifname.empty()) {
 
212
                struct in_addr if_addr_out;
 
213
                if (!get_address(sockout, &if_addr_out, ifname)) {
 
214
                        error << string_compose ("socket(out): could not find interface address for %1", ifname) << endmsg;
 
215
                        return false;
 
216
                }
 
217
                if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_IF, (char *) &if_addr_out, sizeof(if_addr_out))) {
 
218
                        ::perror("setsockopt(IP_MULTICAST_IF)");
 
219
                        return false;
 
220
                }
 
221
        }
 
222
        
 
223
        ::memset(&addrout, 0, sizeof(struct sockaddr_in));
 
224
        addrout.sin_family = AF_INET;
 
225
        addrout.sin_addr.s_addr = ::inet_addr("225.0.0.37");
 
226
        addrout.sin_port = htons (base_port);
 
227
        
 
228
        // Turn off loopback...
 
229
        int loop = 0;
 
230
        if (::setsockopt(sockout, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof (loop)) < 0) {
 
231
                ::perror("setsockopt(IP_MULTICAST_LOOP)");
 
232
                return false;
 
233
        }
 
234
 
 
235
        if (fcntl (sockin, F_SETFL, O_NONBLOCK)) {
 
236
                error << "cannot set non-blocking mode for IP MIDI input socket (" << ::strerror (errno) << ')' << endmsg;
 
237
                return false;
 
238
        }
 
239
 
 
240
        if (fcntl (sockout, F_SETFL, O_NONBLOCK)) {
 
241
                error << "cannot set non-blocking mode for IP MIDI output socket (" << ::strerror (errno) << ')' << endmsg;
 
242
                return false;
 
243
        }
 
244
        
 
245
        return true;
 
246
}
 
247
 
 
248
int
 
249
IPMIDIPort::write (const byte* msg, size_t msglen, timestamp_t /* ignored */) {
 
250
 
 
251
        if (sockout) {
 
252
                Glib::Threads::Mutex::Lock lm (write_lock);
 
253
                if (::sendto (sockout, (const char *) msg, msglen, 0, (struct sockaddr *) &addrout, sizeof(struct sockaddr_in)) < 0) {
 
254
                        ::perror("sendto");
 
255
                        return -1;
 
256
                }
 
257
                return msglen;
 
258
        }
 
259
        return 0;
 
260
}
 
261
 
 
262
int
 
263
IPMIDIPort::read (byte* /*buf*/, size_t /*bufsize*/)
 
264
{
 
265
        /* nothing to do here - all handled by parse() */
 
266
        return 0;
 
267
}
 
268
 
 
269
void
 
270
IPMIDIPort::parse (framecnt_t timestamp)
 
271
{
 
272
        /* input was detected on the socket, so go get it and hand it to the
 
273
         * parser. This will emit appropriate signals that will be handled
 
274
         * by anyone who cares.
 
275
         */
 
276
        
 
277
        unsigned char buf[1024];
 
278
        struct sockaddr_in sender;
 
279
        socklen_t slen = sizeof(sender);
 
280
        int r = ::recvfrom (sockin, (char *) buf, sizeof(buf), 0, (struct sockaddr *) &sender, &slen);
 
281
 
 
282
        if (r >= 0) {
 
283
 
 
284
                _parser->set_timestamp (timestamp);
 
285
                
 
286
                for (int i = 0; i < r; ++i) {
 
287
                        _parser->scanner (buf[i]);
 
288
                }
 
289
        } else {
 
290
                ::perror ("failed to recv from socket");
 
291
        }
 
292
}
 
293