/** * @brief Receive an AppleMIDI command. * Receive a datagram and decompose the message into the message structure. * @private @memberof MIDIDriverAppleMIDI * @param driver The driver. * @param fd The file descriptor to use for communication. * @param command The command. * @retval 0 On success. * @retval >0 If the packet could not be sent. */ static int _applemidi_recv_command( struct MIDIDriverAppleMIDI * driver, int fd, struct AppleMIDICommand * command ) { unsigned int ssrc; unsigned int msg[16]; int len; command->size = sizeof(command->addr); len = recvfrom( fd, &msg[0], sizeof(msg), 0, (struct sockaddr *) &(command->addr), &(command->size) ); if( command->addr.ss_family == AF_INET ) { struct sockaddr_in * a = (struct sockaddr_in *) &(command->addr); MIDILog( DEBUG, "recv %i bytes from %s:%i on s(%i)\n", len, inet_ntoa( a->sin_addr ), ntohs( a->sin_port ), fd ); } else { MIDILog( DEBUG, "recv %i bytes from <unknown addr family> on s(%i)\n", len, fd ); } command->type = ntohl( msg[0] ) & 0xffff; switch( command->type ) { case APPLEMIDI_COMMAND_INVITATION: case APPLEMIDI_COMMAND_INVITATION_ACCEPTED: case APPLEMIDI_COMMAND_INVITATION_REJECTED: case APPLEMIDI_COMMAND_ENDSESSION: if( len < 16 ) return 1; command->data.session.version = ntohl( msg[1] ); command->data.session.token = ntohl( msg[2] ); command->data.session.ssrc = ntohl( msg[3] ); len -= 16; if( len > 0 ) { if( len > sizeof( command->data.session.name ) - 1 ) { len = sizeof( command->data.session.name ) - 1; } memcpy( &(command->data.session.name[0]), &msg[4], len ); command->data.session.name[len] = '\0'; } ssrc = command->data.session.ssrc; break; case APPLEMIDI_COMMAND_SYNCHRONIZATION: if( len != 36 ) return 1; command->data.sync.ssrc = ntohl( msg[1] ); command->data.sync.count = ntohl( msg[2] ) >> 24; command->data.sync.timestamp1 = (unsigned long long) ntohl( msg[3] ) << 32; command->data.sync.timestamp1 += ntohl( msg[4] ); command->data.sync.timestamp2 = (unsigned long long) ntohl( msg[5] ) << 32; command->data.sync.timestamp2 += ntohl( msg[6] ); command->data.sync.timestamp3 = (unsigned long long) ntohl( msg[7] ) << 32; command->data.sync.timestamp3 += ntohl( msg[8] ); ssrc = command->data.sync.ssrc; break; case APPLEMIDI_COMMAND_RECEIVER_FEEDBACK: if( len != 12 ) return 1; command->data.feedback.ssrc = ntohl( msg[1] ); command->data.feedback.seqnum = ntohl( msg[2] ); ssrc = command->data.feedback.ssrc; break; default: return 1; } return 0; }
/** * @brief Send the given AppleMIDI command. * Compose a message buffer and send the datagram to the given peer. * @private @memberof MIDIDriverAppleMIDI * @param driver The driver. * @param fd The file descriptor to use for communication. * @param command The command. * @retval 0 On success. * @retval >0 If the packet could not be sent. */ static int _applemidi_send_command( struct MIDIDriverAppleMIDI * driver, int fd, struct AppleMIDICommand * command ) { unsigned int ssrc; unsigned int msg[16]; int len; msg[0] = htonl( ( APPLEMIDI_PROTOCOL_SIGNATURE << 16 ) | command->type ); switch( command->type ) { case APPLEMIDI_COMMAND_INVITATION: case APPLEMIDI_COMMAND_INVITATION_ACCEPTED: case APPLEMIDI_COMMAND_INVITATION_REJECTED: case APPLEMIDI_COMMAND_ENDSESSION: ssrc = command->data.session.ssrc; msg[1] = htonl( command->data.session.version ); msg[2] = htonl( command->data.session.token ); msg[3] = htonl( command->data.session.ssrc ); if( command->data.session.name[0] != '\0' ) { len = strlen( command->data.session.name ); memcpy( &(msg[4]), command->data.session.name, len ); len += 16; } else { len = 16; } break; case APPLEMIDI_COMMAND_SYNCHRONIZATION: ssrc = command->data.sync.ssrc; msg[1] = htonl( command->data.sync.ssrc ); msg[2] = htonl( command->data.sync.count << 24 ); msg[3] = htonl( command->data.sync.timestamp1 >> 32 ); msg[4] = htonl( command->data.sync.timestamp1 & 0xffffffff ); msg[5] = htonl( command->data.sync.timestamp2 >> 32 ); msg[6] = htonl( command->data.sync.timestamp2 & 0xffffffff ); msg[7] = htonl( command->data.sync.timestamp3 >> 32 ); msg[8] = htonl( command->data.sync.timestamp3 & 0xffffffff ); len = 36; break; case APPLEMIDI_COMMAND_RECEIVER_FEEDBACK: ssrc = command->data.feedback.ssrc; msg[1] = htonl( command->data.feedback.ssrc ); msg[2] = htonl( command->data.feedback.seqnum ); len = 12; break; default: return 1; } if( command->addr.ss_family == AF_INET ) { struct sockaddr_in * a = (struct sockaddr_in *) &(command->addr); MIDILog( DEBUG, "send %i bytes to %s:%i on s(%i)\n", len, inet_ntoa( a->sin_addr ), ntohs( a->sin_port ), fd ); } else { MIDILog( DEBUG, "send %i bytes to <unknown addr family> on s(%i)\n", len, fd ); } if( sendto( fd, &msg[0], len, 0, (struct sockaddr *) &(command->addr), command->size ) != len ) { return 1; } else { return 0; } }
/** * @brief Create a MIDIDriverAppleMIDI instance. * Allocate space and initialize an MIDIDriverAppleMIDI instance. * @public @memberof MIDIDriverAppleMIDI * @return a pointer to the created driver structure on success. * @return a @c NULL pointer if the driver could not created. */ struct MIDIDriverAppleMIDI * MIDIDriverAppleMIDICreate( char * name, unsigned short port ) { struct MIDIDriverAppleMIDI * driver; MIDITimestamp timestamp; int ret = 0; driver = malloc( sizeof( struct MIDIDriverAppleMIDI ) ); MIDIPrecondReturn( driver != NULL, ENOMEM, NULL ); MIDIDriverInit( &(driver->base), name, APPLEMIDI_CLOCK_RATE ); driver->control_socket = 0; driver->rtp_socket = 0; driver->port = port; driver->accept = 0; driver->sync = 0; strncpy( &(driver->name[0]), name, sizeof(driver->name) ); driver->in_queue = MIDIMessageQueueCreate(); driver->out_queue = MIDIMessageQueueCreate(); driver->base.send = &_driver_send; driver->base.destroy = &_driver_destroy; ret = _applemidi_connect( driver ); if (ret==-1) MIDILog( ERROR, "Bind failed for port: %hu\n", port); driver->peer = NULL; driver->rtp_session = RTPSessionCreate( driver->rtp_socket ); driver->rtpmidi_session = RTPMIDISessionCreate( driver->rtp_session ); MIDIClockGetNow( driver->base.clock, ×tamp ); MIDILog( DEBUG, "initial timestamp: %lli\n", timestamp ); driver->token = timestamp; memset( &(driver->command), 0, sizeof(driver->command) ); driver->command.peer = NULL; _applemidi_init_runloop_source( driver ); return driver; }