Search code examples
wolfram-mathematicamidi

Superposing music tracks in Mathematica & MIDI pedal events


All of the following refers to music/MIDI (SoundNote objects), not sampled sounds.

Unfortunately Mathematica doesn't seem to be able to import MIDI. I'm trying to make a simple MIDI importer based on MIDI <-> CSV for single-instrument files (focused on piano).

What is the simplest way to superpose two Sound objects in Mathematica?

Show concatenates them, it doesn't superpose.

A not-too simple approach is to disassemble the Sound into SoundNotes, convert each SoundNotes time specification into {Tstart, Tend} format, and assemble these into a new sound. Is there a simple way?

A second question:

Is there a simple way to handle pedal events in Mathematica, while still using the internal sound representation and MIDI player, and not playing the MIDI through some other means?


Solution

  • 1. Superposition

    In order to be able to superimpose two Sound objects, there ought to be a common time origin. A simple SoundNote object has its own time origin and hence using Sound on a list of these will only concatenate them and plays them in serial. You will have to use absolute times for each in order to superimpose them. This way, you can compose MIDI music with multiple instruments playing at the same time.

    Here's a short example (not polished) of superimposing. The bass notes are played by a Piano and the treble notes are played by a Clarinet.

    tempo = 110;
    eighthNoteDuration = 60/tempo/2;
    
    trebleNotes = {"E5", "D#5", "E5", "D#5", "E5", "B", "D5", "C5", "A", 
       None, "C", "E", "A", "B", None, "E", "G#", "B", "C5", None, "E", 
       "E5", "D#5", "E5", "D#5", "E5", "B", "D5", "C5", "A", None, "C", 
       "E", "A", "B", None, "E", "C5", "B", "A"};
    trebleNoteDurations = {1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1,
         1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1,
         1, 4} eighthNoteDuration;
    trebleTimings = 
      Partition[
       Accumulate@Flatten@Transpose@{ConstantArray[0, Length@#], #} &@
        trebleNoteDurations, 2];
    
    bassNotes = {None, None, "A2", "E3", "A3", None, None, "E3", "G#3", 
       "B3", None, None, "A2", "E3", "A3", None, None, None, "A2", "E3", 
       "A3", None, None, "E3", "G#3", "B3", None, None, "A2", "E3", "A3", 
       None};
    bassNoteDurations = {2, 6, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 
        2, 6, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1} eighthNoteDuration;
    bassTimings = 
      Partition[
       Accumulate@Flatten@Transpose@{ConstantArray[0, Length@#], #} &@
        bassNoteDurations, 2];
    
    Sound[Join[
      SoundNote[#1, #2, 
         "Piano"] & @@@ ({bassNotes, bassTimings}\[Transpose]), 
      SoundNote[#1, #2, 
         "Clarinet"] & @@@ ({trebleNotes, trebleTimings}\[Transpose])]]
    

    2. Pedal effects

    As for your second question, I don't think you can reproduce pedal effects using MIDI. The MIDI format is pretty simple and does not actually transmit any sound. All the information it carries is

    1. The note being played (pitch)
    2. The duration of the note (tempo)
    3. Trigger events to start and stop the note

    The instrument you choose to reproduce the sound is entirely dependent on your system and may reproduce differently on different systems. Now if you want to reproduce a pedal effect, you will have to write a function to break it up into individual MIDI events that closely resemble the actual effect of the pedal.

    For example, you could modify SoundNote or create a new function that, when passed an option Sustain -> t, prolongs the note for t seconds. You can make this more realistic and chop up the t seconds into smaller segments, with SoundVolume -> v as an additional option and v decreasing linearly/logarithmically with each segment.