fbpx
Skip to main content

MIDI Forum

midi code won't com...
 
Notifications
Clear all

midi code won't compile on Xcode

4 Posts
2 Users
0 Reactions
9,868 Views
netanel
Posts: 1
New Member
Topic starter
 

Hello,
I am trying to create a music app in Xcode using MIDI in swift language.
I have encountered a problem that when i activate the code the code is giving me this error:"EXC_BAD-INSTRUCTION(code = EXC_I386_INVOP,subcode=0x0)" (the error is in the bolded line below).
the odd thing is that the same code is running fine in the Xcode's playground.
here is the code:

import UIKit
import AVFoundation

// Creating the sequence

var sequence:MusicSequence? = nil
var musicSequence = NewMusicSequence(&sequence)

// Creating a track

var track:MusicTrack? = nil
var musicTrack = MusicSequenceNewTrack(sequence!, &track) --> the error is given in this line
var musicPlayer:MusicPlayer? = nil
var player = NewMusicPlayer(&musicPlayer)

// Adding notes

var time = MusicTimeStamp(1.0)
var note = MIDINoteMessage(channel: 0,
note: 60,
velocity: 250,
releaseVelocity: 0,
duration: 2 )
var note1 = MIDINoteMessage(channel: 0,
note: 63,
velocity: 250,
releaseVelocity: 0,
duration: 2 )
var note2 = MIDINoteMessage(channel: 0,
note: 67,
velocity: 250,
releaseVelocity: 0,
duration: 2 )

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func chord(_ sender: UIButton) {
musicTrack = MusicTrackNewMIDINoteEvent(track!, time, &note)
musicTrack = MusicTrackNewMIDINoteEvent(track!, time, &note2)
musicTrack = MusicTrackNewMIDINoteEvent(track!, time, &note1)

player = MusicPlayerSetSequence(musicPlayer!, sequence)
player = MusicPlayerStart(musicPlayer!)

}

}

 
Posted : 11/03/2017 4:53 am
Thom Jordan
Posts: 3
New Member
 

The EXC_BAD-INSTRUCTION error is generally one that you will often encounter in various situations when using Swift. It can often be equated or analogous to the common "null pointer" error that appears in various languages. In layman's terms, there's a good chance that somewhere in the line of code that's triggering the error, there's an attempt to access a variable that currently has no memory associated with it. With Swift, this can happen when an optional variable has been defined, but currently holds no value.

See if you can discover the issue now, after reading the above information (Hint: you are using an operator (symbol) here in a way that is greatly discouraged in the Swift guidelines. It's easy to try to "cheat" by using this symbol, but more often than not, it'll cause problems in immediate or future use... hence the need for following the appropriate guidelines when using this operator in Swift.)

Below is a link to a Swift-ready wrapping of the MIDI sequence classes that are part of Apple's Audio Toolbox. I first wrote this in the early days of Swift, then updated it for Swift 3 last summer. It has worked well for me in the past, though I haven't yet used it since upgrading it for Swift 3. Everything compiles though, with no errors, so it should work as expected. There's no secondary means of installing it via CocoaPods or whatever... just download or clone the repository and add it directly to your Xcode project as a framework. You can find numerous examples online of how to add a third-party framework to Xcode. After doing this more than once it becomes much easier to do in practice.

Alternately, just looking at some of the code here might help in figuring out how to work with some of the MIDI classes in the AudioToolbox, using Swift.

MidiToolbox

 
Posted : 18/03/2017 8:29 am
Thom Jordan
Posts: 3
New Member
 

After looking at your code again, I was reminded of the exact same challenging situation I encountered when updating the MidiToolbox code to Swift 3. If you guessed that the problem was using the '!' operator on an optional that is currently nil (the "track" variable defined in the previous line), you are correct !

However, this is a strange case that is not obvious how to fix, because of the intricacies in adapting an API originally written in C to the stricter, more accessibly-powerful world of Swift.

I think the way to get around this restriction here, that almost seems like a "Catch-22", is to create an empty track in the line before the error, instead of assigning it to nil. For an example of this solution, see line 27 of 'MTMusicTrack.swift' in the MidiToolbox link provided in the previous message. After that, then look at the method starting at line 145 of the same file, as an example of how to use the track that was created in line 27.

If this doesn't work directly, then I think the most straightforward solution here is to create an empty "dummy" track within the init() method of your container class, as shown in line 27, after defining the variable NOT AS AN OPTIONAL as shown in line 19. This removes the need for trying to use the newly-adopted "optional" parts of Swift within calls to a toolbox written in C over a decade ago. Since the "track" variable is no longer an optional, an "actually existing" object of its same type must be created and assigned to it within the init() method of the local class, or else it won't compile.

In the new world of Swift, the suggested means of resolving this kind of issue is to adjust the variable's type to make it optional by adding a question mark, then testing for an actual value before trying to access the variable in the subsequent code. However, in the more specialized "lower-level" cases like these where Swift is being used to call into an older C-based library, the trick I believe is to refrain from all uses of optionals within any direct calls to this library's functions, and if the compiler balks at variables that don't yet have a value, the most straightforward solution then is to wrap all the related functions you want to use within one or more classes, then provide the needed values to the variables directly within the class(es) init() method. In order for this to work, each variable and its associated type needs be defined above the init() method, without using a '?' or '!' (i.e. without using ANY optionals at all ! ). By defining the variables before the init() method, they become true "instance properties" of the class, and not local variables defined within a method, whose scope would limited only to the lifetime of the method and not beyond.

In a nutshell:

All instance variables must be first defined before the init() method, directly within the class itself, at the top of the class definition (in the subsequent lines immediately after the class's opening brace '{' is best), without using any optional syntax. Next you must create an init() method, and then within this init() method you must create and assign actual values to each of the instance variables that were defined above and outside of the init() method, the ones that were defined by type alone, that don't yet have actual values.[

 
Posted : 18/03/2017 9:37 am
Thom Jordan
Posts: 3
New Member
 

One more important tidbit that I forgot to add... Once creating a wrapper class or classes that fully encapsulate all calls from Swift to any older library functions originally written in C, you can then treat the wrapper class(es) as a definite abstraction boundary, upon which you can utilize all parts of Swift (including optionals) by dealing directly and exclusively with the wrapper class(es) alone. The wrapper class or classes are written specifically to isolate all calls into some older C-based library whose functions are now accessible from Swift, but that still adhere to C-like constructs (e.g. "pointers") within a function's signature (the list of parameters and their associated types, that describe the ordering and types of values needed to be supplied to the function within any valid call).

You can recognize the older C-based functions by their signature which should resemble the general form of signatures utilized for the AudioToolbox functions.

example:

func MusicTrackSetProperty(_ inTrack: MusicTrack,
_ inPropertyID: UInt32,
_ inData: UnsafeMutableRawPointer,
_ inLength: UInt32) -> OSStatus

A clear giveaway that the library still utilizes the conventions of C, is apparent whenever you see a type like "UnsafeMutableRawPointer" or something similar, as a type directly within a function's signature. It's also obvious that an older C-based implementation is being used still behind the scenes, if there's no surrounding class, only a set of individual functions that are grouped together by file, toolbox, and/or documentation only (i.e. there's NO parent class to host the functions, making them methods instead).

Happy (lower-level) Swifting !

 
Posted : 18/03/2017 10:13 am
Share: