/** * @brief Start or continue a synchronization session. * Continue a synchronization session identified by a given command. * The command must contain a pointer to a valid peer. * @param driver The driver. * @param fd The file descriptor to be used for communication. * @param command The previous sync command. * @retval 0 On success. * @retval >0 If the synchronization failed. */ static int _applemidi_sync( struct MIDIDriverAppleMIDI * driver, int fd, struct AppleMIDICommand * command ) { unsigned long ssrc; MIDITimestamp timestamp, diff; RTPSessionGetSSRC( driver->rtp_session, &ssrc ); MIDIClockGetNow( driver->base.clock, ×tamp ); if( command->type != APPLEMIDI_COMMAND_SYNCHRONIZATION || command->data.sync.ssrc == ssrc || command->data.sync.count > 2 ) { command->type = APPLEMIDI_COMMAND_SYNCHRONIZATION; command->data.sync.ssrc = ssrc; command->data.sync.count = 0; command->data.sync.timestamp1 = timestamp; driver->sync = 1; return _applemidi_send_command( driver, fd, command ); } else { RTPSessionFindPeerBySSRC( driver->rtp_session, &(driver->peer), command->data.sync.ssrc ); /* received packet from other peer */ if( command->data.sync.count == 2 ) { /* compute media delay */ diff = ( command->data.sync.timestamp3 - command->data.sync.timestamp1 ) / 2; /* approximate time difference between peer and self */ diff = command->data.sync.timestamp3 + diff - timestamp; /* RTPPeerSetTimestampDiff( command->peer, diff ) */ /* finished sync */ command->data.sync.ssrc = ssrc; command->data.sync.count = 3; driver->sync = 0; return 0; } if( command->data.sync.count == 1 ) { /* compute media delay */ diff = ( command->data.sync.timestamp3 - command->data.sync.timestamp1 ) / 2; /* approximate time difference between peer and self */ diff = command->data.sync.timestamp2 + diff - timestamp; /* RTPPeerSetTimestampDiff( command->peer, diff ) */ command->data.sync.ssrc = ssrc; command->data.sync.count = 2; command->data.sync.timestamp3 = timestamp; driver->sync = 0; return _applemidi_send_command( driver, fd, command ); } if( command->data.sync.count == 0 ) { command->data.sync.ssrc = ssrc; command->data.sync.count = 1; command->data.sync.timestamp2 = timestamp; driver->sync = 2; return _applemidi_send_command( driver, fd, command ); } } return 1; }
/** * @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; driver = kmalloc(sizeof(struct MIDIDriverAppleMIDI), GFP_KERNEL); if (driver == NULL) { return NULL; } pr_debug("placed driver at %p\n", driver); spin_lock_init(&(driver->lock)); pr_debug("allocated driver structure\n"); MIDIDriverInit(&(driver->base), name, APPLEMIDI_CLOCK_RATE, (void *)driver); driver->control_socket = NULL; driver->rtp_socket = NULL; driver->port = port; driver->accept = 0xff; driver->sync = 0; strncpy(&(driver->name[0]), name, sizeof(driver->name)); driver->name[31] = 0; // driver->in_queue = MIDIMessageQueueCreate(); // driver->out_queue = MIDIMessageQueueCreate(); // driver->base.send = &_driver_send; // driver->base.destroy = &_driver_destroy; _applemidi_connect(driver); pr_debug("connected sockets\n"); driver->peer = NULL; driver->rtp_session = RTPSessionCreate(driver->rtp_socket); driver->rtpmidi_session = RTPMIDISessionCreate(driver->rtp_session); MIDIClockGetNow(driver->base.clock, ×tamp); pr_debug("init timestamp with %lld\n", timestamp); driver->token = timestamp; memset(&(driver->command), 0, sizeof(driver->command)); driver->command.peer = NULL; setup_timer(&driver->timer, _applemidi_idle_timeout, (unsigned long)driver); mod_timer(&driver->timer, jiffies + msecs_to_jiffies(5000)); return driver; }
/** * @brief Process outgoing MIDI messages. * This is called by the generic driver interface to pass messages to this driver implementation. * The driver may queue outgoing messages to reduce package overhead, trading of latency for throughput. * @public @memberof MIDIDriverAppleMIDI * @param driver The driver. * @param message The message that should be sent. * @retval 0 on success. * @retval >0 if the message could not be processed. */ int MIDIDriverAppleMIDISendMessage( struct MIDIDriverAppleMIDI * driver, struct MIDIMessage * message ) { /** * @todo: when midi messages get timestamped by global clock, do something different: * - if we use the global clock (driver->clock == global_clock) do nothing * - otherwise: convert timestamp between clocks */ MIDITimestamp timestamp; MIDIClockGetNow( driver->base.clock, ×tamp ); MIDIMessageSetTimestamp( message, timestamp ); MIDIMessageQueuePush( driver->out_queue, message ); return MIDIDriverAppleMIDISend( driver ); }
/** * @brief Process outgoing MIDI messages. * This is called by the generic driver interface to pass messages to this driver implementation. * The driver may queue outgoing messages to reduce package overhead, trading of latency for throughput. * @public @memberof MIDIDriverAppleMIDI * @param driver The driver. * @param message The message that should be sent. * @retval 0 on success. * @retval >0 if the message could not be processed. */ int MIDIDriverAppleMIDISendMessage( struct MIDIDriverAppleMIDI * driver, struct MIDIMessage * message ) { /** * @todo: when midi messages get timestamped by global clock, do something different: * - if we use the global clock (driver->clock == global_clock) do nothing * - otherwise: convert timestamp between clocks */ MIDITimestamp timestamp; MIDIClockGetNow( driver->base.clock, ×tamp ); MIDIMessageSetTimestamp( message, timestamp ); MIDIMessageQueuePush( driver->out_queue, message ); /** * @todo: instead of sending directly, set a minimal timeout, just long enough so that * multiple messages that are queued together will be sent together. */ return MIDIDriverAppleMIDISend( driver ); }
/** * @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; }