Code Monkey home page Code Monkey logo

midiparser's Introduction

MidiParser

MidiParser is a wrapper library of AudioToolbox.framework about SMF(Standard Midi Files) that makes it easy to read/write midi files.

AudioToolbox framework provides many useful APIs for MIDI, but they are not Swifty styles (bridging with C pointers, explicit deallocation, etc). MidiParser wraps them and make more Swifty to access properties.

  • no need to care about deallocating
  • no need UnsafePointers, CFtypes
  • use Enum for constants

Usage

Read

Import *.mid file

import MidiParser

let midi = MidiData()
let data: Data = ... // load .mid file as `Data` type
midi.load(data: data)

print(midi.noteTracks.count) // 5
print(midi.noteTracks[0].trackName) // "Bass"
print(midi.noteTracks[1].trackName) // "Piano"
print(midi.noteTracks[2].trackName) // "Hi-hat only"
print(midi.noteTracks[3].trackName) // "Drums"
print(midi.noteTracks[4].trackName) // "Jazz Guitar"

print(midi.tempoTrack.timeSignatures) 
// [MidiParser.MidiTimeSignature(timeStamp: 0.0, numerator: 4, denominator: 2, cc: 24, bb: 8)]
print(midi.tempoTrack.extendedTempos) 
// [MidiParser.MidiExtendedTempo(timeStamp: 0.0, bpm: 120.0)]

print(midi.infoDictionary[.tempo] as! Int) // 120
print(midi.infoDictionary[.timeSignature] as! String) // 4/4

Midi Events

let track = midi.noteTracks[0]
print(track.count) // number of note events in the track
print(track[0]) // get note event from index with subscript
// MidiNote(timeStamp: 0.0, duration: 0.533333361, note: 45, velocity: 78, channel: 0, releaseVelocity: 64)

Write

// init empty midi
let midi = MidiData()
// add track
let track1 = midi.addTrack()

// add note events to track1
track1.add(note: MidiNote(timeStamp: 0, duration: 10, note: 50, velocity: 100, channel: 0))
track1.add(notes: [
    MidiNote(timeStamp: 5, duration: 10, note: 40, velocity: 100, channel: 0),
    MidiNote(timeStamp: 10, duration: 10, note: 40, velocity: 100, channel: 0),
    MidiNote(timeStamp: 20, duration: 10, note: 40, velocity: 100, channel: 0)
])

// set meta events
track1.keySignatures = [MidiKeySignature(timeStamp: 0, key: .minor(.A))]
track1.patch = MidiPatch(channel: 0, patch: .cello)
track1.trackName = "track1"

// write data to url
try! midi.writeData(to: tmp)
// generate midi file as Data
let output = midi.createData()

open with GarageBand... screen shot 2018-07-17 at 10 50 44

Installation

Carthage

To install using Carthage, add the following line to Cartfile:
github "matsune/MidiParser"

midiparser's People

Contributors

matsune avatar nbelfast avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

midiparser's Issues

The time stamp is not correct for some midi file

For example, I convert my midi in https://tonejs.github.io/Midi/ to this:

{
  "header": {
    "keySignatures": [],
    "meta": [
      {
        "ticks": 0,
        "type": "sequencerSpecific"
      },
      {
        "ticks": 0,
        "type": "sequencerSpecific"
      },
      {
        "ticks": 0,
        "type": "sequencerSpecific"
      }
    ],
    "name": "",
    "ppq": 960,
    "tempos": [
      {
        "bpm": 109.99990833340972,
        "ticks": 0
      }
    ],
    "timeSignatures": [
      {
        "ticks": 0,
        "timeSignature": [
          4,
          4
        ],
        "measures": 0
      }
    ]
  },
  "tracks": [
    {
      "channel": 0,
      "controlChanges": {},
      "pitchBends": [],
      "instrument": {
        "family": "piano",
        "name": "acoustic grand piano",
        "number": 0
      },
      "name": "Track 1",
      "notes": [
        {
          "duration": 0.27272750000000023,
          "durationTicks": 480,
          "midi": 74,
          "name": "D5",
          "ticks": 2640,
          "time": 1.50000125,
          "velocity": 0.5039370078740157
        },
        {
          "duration": 0.2727274999999998,
          "durationTicks": 480,
          "midi": 70,
          "name": "A#4",
          "ticks": 4080,
          "time": 2.31818375,
          "velocity": 0.5039370078740157
        },
        {
          "duration": 0.27272750000000023,
          "durationTicks": 480,
          "midi": 77,
          "name": "F5",
          "ticks": 5280,
          "time": 3.0000025,
          "velocity": 0.5039370078740157
        },
        {
          "duration": 0.2727274999999998,
          "durationTicks": 480,
          "midi": 67,
          "name": "G4",
          "ticks": 6960,
          "time": 3.9545487500000003,
          "velocity": 0.5039370078740157
        },
        {
          "duration": 0.545455,
          "durationTicks": 960,
          "midi": 71,
          "name": "B4",
          "ticks": 6960,
          "time": 3.9545487500000003,
          "velocity": 0.5039370078740157
        },
        {
          "duration": 0.27272750000000023,
          "durationTicks": 480,
          "midi": 75,
          "name": "D#5",
          "ticks": 8160,
          "time": 4.6363675,
          "velocity": 0.5039370078740157
        }
      ]
    }
  ]
}

But the time is not correct after parsing with MidiParser. Here is the result:

Name time duration
--------------------
D6      2.75     0.5
A#5   4.25      0.5
F6      5.5        0.5
G5     7.25      0.5
B5     7.25      1.0
D#6  8.5        0.5

Here is my code:

    let midi = MidiData()
    if let data = try? Data(contentsOf: URL(fileURLWithPath: file)) {
            
      midi.load(data: data)

      for track in midi.noteTracks {
        if track.notes.count == 0 {
          continue
        }

        for note in track.notes {
          print(converToNoteName(note.pitch),note.timeStamp,note.duration)
        }
      }
    }

What could be wrong? Thanks.

I get two tracks instead of one. The first has the notes, the second the track name.

Hi! I really like this project. Is it still being updated? Is there a newer version somewhere?

Here's my situation
Random16s.mid.zip

That MIDI file has only one track, but MidiParser makes two tracks out of it, putting the notes on the first track and the name on the second track. That causes problems for apps that try to find notes based on the name of the track.

Oddly, if you load the same file into the current version of Logic Pro, it splits it into two files as well. However, both the notes and the track name are on the second track in the display. However, it's event list puts the name on the upper track, even though it displays on the bottom.

I wasn't trying to create a difficult track and I've spent a day or so just trying to figure out what is going on (and not really successful at that). I created the track by simply cutting two measures of a drum pattern sample track and pasting it to a new track in Logic Pro at the beginning. Then I erased everything else and wound up with this.

So possibly it is a very odd case and unimportant. In any event, it would be great to hear the the project is still active. I'm using it for an iPad music app that I am designing and building now. Thanks.

(later).After looking at the source code of this repository, I see that I think it relies on Apple's Midi Sequence classes in AudioToolbox to do the actual parsing. If so, the problem must be within there. That also may explain why I saw some similar problems with Logic Pro handling the same type of file.

I need to kludge my way through this for my own app somehow. So currently I am trying an awful write around where, if I look up the name and find it on track 1, but track 1 has no note events, I use track 0 instead. It actually seems to work so far, but it feels like opening a can of worms.

Discrepancy in duration of the Notes

Greetings @matsune

I've used your great package to create note-beams in my project.
Untitled

These I call note beams :)

However, I've noticed that the duration of a parsed midi file varies a bit from other MIDISequencers (like Apple AVMIDIPlayer API) and thus the duration of each MidiNote inSeconds does not match the duration of notes that are played by the sequencer.

The difference is not usually that big, it's mostly around 0.1% - 1%.

So I wondered, why there's a slight difference while both data is derived from the same set of APIs?

Create meta events

I’m wanting to use your library to create midi files with markers (no other data). Do you have any examples of this?

Thanks

XCode 11.3 creates warning on Compile: withUnsafeBytes is depreciated

XCode 11.3 (11C29) generates this warning on compile:

/Users/.../MidiParser/Source/MidiData/MidiSequence.swift:40:27: 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead

I am new to Swift and can'T help myself, sorry.

Regards

Gerd.

Add package in playground gives error

the given error is

Could not resolve package graph. Cannot continue.

the package manifest at '/Package.swift' cannot be accessed
(noEntry(PlaygroundProjectModel.FilePath(path: "/Package.swift")))

can someone help me? thanks

Playback speed problem

Hello.
Thanks for the nice library, but there is a problem with it.

I generated a .mid file according to your instructions. Then I try to play the recording using AVMIDIPlayer.
For some reason the recorded midi is played at double speed.
If I set AVMIDIPlayer.rate = 0.5, then it sounds like normal.
Could you help me with problem? Maybe I'm generating the midi incorrectly?

Occasional malloc error when adding notes

I'm noticing a fairly frequent crash when calling add(note:):

SpliqsTool(50382,0x70000ee42000) malloc: *** error for object 0x600000ce9620: pointer being freed was not allocated
SpliqsTool(50382,0x70000eec5000) malloc: Heap corruption detected, free list is damaged at 0x600000cfaed0
*** Incorrect guard value: 1
SpliqsTool(50382,0x70000ee42000) malloc: *** set a breakpoint in malloc_error_break to debug
SpliqsTool(50382,0x70000eec5000) malloc: *** set a breakpoint in malloc_error_break to debug
SpliqsTool(50382,0x70000f4e9000) malloc: Heap corruption detected, free list is damaged at 0x600000ce7fd0
*** Incorrect guard value: 105553129677296
SpliqsTool(50382,0x70000f4e9000) malloc: *** set a breakpoint in malloc_error_break to debug

I'm calling it from an extension I wrote for merging events onto a given channel:

extension MidiNoteTrack {
    func add(notes: [MidiNote], mergeToChannel ch: Int) {
        for note in notes {
            let n = MidiNote(timeStamp: note.timeStamp, duration: note.duration, note: note.note, velocity: note.velocity, channel: UInt8(ch))
            self.add(note: n)
        }
    }
}

I don't see how my function could be causing it (though I obviously won't rule out the possibility) so I thought I'd report it, just in case. I am calling my function from a DispatchQueue.concurrentPerform, which contains a midiData.writeData(to:) call—could that be the problem?

I'm on macOS 10.15.3, Xcode 11.3.1, Swift 5.1.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.