Network MIDI on iOS - Part 1

 
This is an app I wrote to try out some ideas for networked MIDI on iPhone and iPad. It connects to a host computer running OS X Tiger or later (see the documentation for Apple's Audio MIDI Setup application), or any compatible RTP MIDI host such as rtpMIDI for Windows.
 
I will discuss the implementation details in further blog posts, as it introduces some useful concepts such as:
  • Composing outgoing MIDI data in response to user input
  • Processing incoming MIDI data in real time (with semaphores and lock free buffers)
  • Finding network services with Bonjour
For now, here's the source code.
Updated project for iOS 5.1 - 6 using ARC:

XCode 4.51 Project (MediaFire download)

Updated project for iOS 7 (should work back to iOS 5.1):


Please note you need to run this on a device rather than the simulator.

(Now seems to work OK on the iOS 7 simulator at least).

Network MIDI on iOS - Part 2

 

In this part, I will discuss finding and publishing network MIDI services using Bonjour, and creating the MIDI client using the CoreMIDI API. The source code for this project is available for download in
Part 1 of this article.

The network MIDI services in OS X and iOS can be discovered in the same way as any other Bonjour service. In the initialiser of the MIDIController class (see MIDIController.m) an instance of
NSNetServiceBrowser is created, and instructed to search for services of the type MIDINetworkBonjourServiceType i.e. @"_apple-midi._udp". By implementing the delegate protocol for this browser, a MIDIController instance can monitor available services of
this type on the network.

These services are exposed to the remainder of the application as a dictionary. In addition, various operations are provided to allow connection, disconnection etc. (see the section marked "Connection Management" in the implementation for details). The sample
application uses these methods to provide a simple UI to connect to any detected services. The settings page is displayed by flipping the main view. From here, we can select one or more of the remote MIDI services available on the current LAN.

 

Note that connections can also be made from the other end. By opening Audio MIDI Setup on a Mac on the same LAN, we can browse to the iPhone and initiate a connection from the computer. In this instance, the MIDIController instance will inform the UI that a
connection has been made via an NSNotification. If multiple connections are made to or from the same device, they are bridged in that:

  • All incoming data from the network is merged as if it was coming from a single device
  • Outgoing data from the device is sent to all network services
The settings view also allows the user to set the MIDI channel on which messages will be sent in response to actions from the main UI views. This is similarly the channel on which the app will listen for incoming MIDI commands.

The MIDIController initialiser also sets up the shared MIDINetworkSession instance, which acts as a bridge between the network services and the CoreMIDI API. A MIDI client, input port and output port are then created. The input port is connected to the source endpoint of the MIDINetworkSession instance. Note
that these endpoints are named from the perspective of the iOS application; the "source" endpoint is where data will be received from the network, and the "destination" endpoint is where the application will send data to the network.

To send data to the output port once a connection has been made, the app invokes the methods in the section marked Sending in the implementation file, namely:

-(void) allNotesOffOnChannel:(NSUInteger)channel;
-(void) sendChangeForController:(NSUInteger)controller onChannel:(NSUInteger)channel withValue:(NSUInteger)value;
-(void) sendNote:(NSUInteger)note on:(BOOL)on onChannel:(NSUInteger)channel withVelocity:(NSUInteger)velocity;
-(void) sendMMCCommand:(NSUInteger)command toDevice:(NSUInteger)device;

Internally, these use the writeMIDIPacket methods to construct a MIDIPacketList and send it via the output port created above to the MIDINetworkSession's destinationEndpoint. Care should be taken to construct the packet lists correctly, as sending garbage
data (for instance where the packet count is set too high) will typically result in disconnection by the remote service.
The sendChangeForController:onChannel:withValue: method is invoked in response to user input from the controller tab:

The sendNote:on:onChannel:withVelocity: method is invoked when the user presses the "keys" on the Notes tab:

The sendMMCCommand:toDevice: method is invoked in response to button presses on the MMC tab:

Please note that the MMC command output hasn't been tested as I don't have any devices that respond to this part of the MIDI protocol. Let me know via the comments if you have any problems.

The operation of the input port is described in Part 3 of this article.

Network MIDI on iOS - Part 3

 

In this part I will discuss how incoming MIDI is received into the application and subsequently processed. The source code for this project is available for download in
Part 1 of this article.

The MIDI controller class (see MIDIController.h) provides a formal protocol, MIDIReceivedDelegate, and a corresponding delegate property. This defines the following methods:

- (void) midiControllerUpdated:(Byte)controller onChannel:(Byte)channel toValue:(Byte)value;
- (void) midiNoteOnOff:(Byte)note onChannel:(Byte)channel withVelocity:(Byte)velocity on:(BOOL)on;

These methods encapsulate the complexity of receiving MIDI data - but how is this accomplished behind the scenes?

When we created the MIDI client in part 2 of this article, we also created a MIDI input port. This was passed a pointer to a callback function, MIDIInputReadProc. This callback function is called by the operating system when MIDI data is received at the port.
As a real time callback function which may be re-entrant when the system is under load, certain restrictions should be adhered to. In particular, this function should avoid:

  • Allocating and deallocating memory
  • Acquiring locks
  • Performing lengthy operations

The function walks the list of MIDI packets received as follows:

For each MIDI packet received:

  • The packet's length and data are written into a structured circular buffer (see MIDIPacketBuffer.h)
  • A Mach semaphore is incremented to signal the rest of the application that another packet is available for processing.

Note that the structured buffer in this example disregards the timestamp of the MIDI packet. This will be covered in a later example.

Another thread (see midiInputThreadProc) waits on this semaphore. If the semaphore is signalled, the length of the next MIDI packet is retrieved from the circular buffer, and then that amount of data is copied into a regular buffer.

This data is then parsed from this buffer. The following MIDI commands are identified in this example:

  • Control changes
  • Note on/off
Other commands are discarded. When a recognised command is received, Grand Central Dispatch is used to enqueue a block that will invoke the controller's delegate asynchronously on the main queue. The delegate will then respond to the invocation and perform any necessary UI updates etc.

By these means, the application decouples the processing of MIDI data from its receipt, and conforms with the requirements placed on it by the CoreMIDI port model

The Stereo Action Dimension的更多相关文章

  1. 强化学习_PolicyGradient(策略梯度)_代码解析

    使用策略梯度解决离散action space问题. 一.导入包,定义hyper parameter import gym import tensorflow as tf import numpy as ...

  2. Java基础之处理事件——使用动作Action(Sketcher 6 using Action objects)

    控制台程序. 动作Action是任何实现了javax.swing.Action接口的类的对象.这个接口声明了操作Action对象的方法,例如,存储与动作相关的属性.启用和禁用动作.Action接口扩展 ...

  3. 微软BI 之SSIS 系列 - 数据仓库中实现 Slowly Changing Dimension 缓慢渐变维度的三种方式

    开篇介绍 关于 Slowly Changing Dimension 缓慢渐变维度的理论概念请参看 数据仓库系列 - 缓慢渐变维度 (Slowly Changing Dimension) 常见的三种类型 ...

  4. 【论文笔记】Spatial Temporal Graph Convolutional Networks for Skeleton-Based Action Recognition

    Spatial Temporal Graph Convolutional Networks for Skeleton-Based Action Recognition 2018-01-28  15:4 ...

  5. [libGDX游戏开发教程]使用libGDX进行游戏开发(12)-Action动画

    前文章节列表:  使用libGDX进行游戏开发(11)-高级编程技巧   使用libGDX进行游戏开发(10)-音乐音效不求人,程序员也可以DIY   使用libGDX进行游戏开发(9)-场景过渡   ...

  6. redux-amrc:用更少的代码发起异步 action

    很多人说 Redux 代码多,开发效率低.其实 Redux 是可以灵活使用以及拓展的,经过充分定制的 Redux 其实写不了几行代码.今天先介绍一个很好用的 Redux 拓展-- redux-amrc ...

  7. 尝试asp.net mvc 基于controller action 方式权限控制方案可行性

    微软在推出mvc框架不久,短短几年里,版本更新之快,真是大快人心,微软在这种优秀的框架上做了大量的精力投入,是值得赞同的,毕竟程序员驾驭在这种框架上,能够强力的精化代码,代码层次也更加优雅,扩展较为方 ...

  8. ASP.NET Core 中文文档 第四章 MVC(4.1)Controllers, Actions 和 Action Results

    原文:Controllers, Actions, and Action Results 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:许登洋(Seay) Action 和 acti ...

  9. java中Action层、Service层和Dao层的功能区分

    Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...

随机推荐

  1. git add 之后因为没提交正确文件需要撤销

    之后因为没提交正确文件需要撤销时,采用如下方法:git log //查看提交记录MT6592_L_2SIM_GIT$ git reset --hard 717af7c9664be098939cb266 ...

  2. 坏账,断供,四大国有资产管理公司(AMC):东方、长城、信达和华融

    在高房价大幅度下降以后,银行会认为你在贷款的时候的抵押物,已经不值钱了,比如已经下跌百分之五十了,那么,银行就会给贷款者一个通知——你的抵押物--房子,已经不值钱了,所以说,你必须立刻缴纳这一部分贬值 ...

  3. 配置spring上下文

    <context-param> <param-name>contextConfigLocation</param-name> <param-value> ...

  4. Codeforces 500A - New Year Transportation【DFS】

    题意:给出n个数,终点t 从第i点能够跳到i+a[i],问能否到达终点 #include<iostream> #include<cstdio> #include<cstr ...

  5. UVa 136 Ugly Numbers【优先队列】

    题意:给出丑数的定义,不能被除2,3,5以外的素数整除的的数称为丑数. 和杭电的那一题丑数一样--这里学的紫书上的用优先队列来做. 用已知的丑数去生成新的丑数,利用优先队列的能够每次取出当前最小的丑数 ...

  6. o4.数组指针和指针数组的区别

    ------- android培训.iOS培训.期待与您交流! ---------- 我们看一下数组指针和指针数组: 数组指针(也称行指针)定义 int (*p)[n];()优先级高,首先说明p是一个 ...

  7. python 包管理

    如果导入的模块和主程序在同个目录下,直接import就行了 2.如果导入的模块是在主程序所在目录的子目录下,可以在子目录中增加一个空白的__init__.py文件,该文件使得python解释器将子目录 ...

  8. c++ vector 释放内存

    1.释放单个指针 关于Vector中存放指针的问题,在进行清空的时候比较安全的一种做法是:    std::vector<ClassName *> ClassNameVec;    ... ...

  9. 【转】Eclipse和PyDev搭建完美Python开发环境(Ubuntu篇)

    原文网址:http://www.cnblogs.com/Realh/archive/2010/10/10/1847251.html 前两天在Windows下成功地搭好了一个Python开发环境,这次转 ...

  10. jQuery autoComplete 样式

    前提:使用了jQuery-ui 官网:http://jqueryui.com/autocomplete/ /*** autocomplete ***/ .ui-widget-content { bac ...