How to increase the volume / gain of a MIDI synthesizer without distortion in iOS?

I play MIDI files on iPhone using MIKMIDISynthesizerfrom this library . Unfortunately, this level is very low, even when you fully increase the volume of the iPhone system. To increase it, I tried these things:

  • change all MIDI note. The value of the event speed is not more than 127. This is a little, but not enough increase the volume.
  • Add the node's mixer AUGraphas described here . This boosts the volume enough, but instantly distorts the signal so poorly that the quality is too poor.
  • Turn up the volume of the soundfont sample using a soundfont editor like Polyphone. This has no tangible effect.

Now my options are running out. Is there any additional parameter or level (for example, AVAudioSessionor CoreMIDImaybe) that I skipped that provides the ability to adjust the volume / gain? Or MIDI commands that do the same?


Code snippets

1. Using a node mixer

This is the part MIKMIDISynthesizerthat I modified to manage the volume using the node mixer.

- (BOOL)setupAUGraph
{
    AUGraph graph;
    OSStatus err = 0;

    // 1. Create new AUGraph
    if ((err = NewAUGraph(&graph))) {
        NSLog(@"Unable to create AU graph: %@", @(err));
        return NO;
    }

    // 2. Create output node
    AudioComponentDescription outputcd = {0};
    outputcd.componentType = kAudioUnitType_Output;
    outputcd.componentSubType = kAudioUnitSubType_RemoteIO;

    outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;

    AUNode outputNode;
    if ((err = AUGraphAddNode(graph, &outputcd, &outputNode))) {
        NSLog(@"Unable to add ouptput node to graph: %@", @(err));
        return NO;
    }

    // 3. Create the instrument node
    AudioComponentDescription instrumentcd = self.componentDescription;
    AUNode instrumentNode;
    if ((err = AUGraphAddNode(graph, &instrumentcd, &instrumentNode))) {
        NSLog(@"Unable to add instrument node to AU graph: %@", @(err));
        return NO;
    }

    if ((err = AUGraphOpen(graph))) {
        NSLog(@"Unable to open AU graph: %@", @(err));
        return NO;
    }

    AudioUnit instrumentUnit;
    if ((err = AUGraphNodeInfo(graph, instrumentNode, NULL, &instrumentUnit))) {
        NSLog(@"Unable to get instrument AU unit: %@", @(err));
        return NO;
    }

    // 4. Create the mixer node
    AUNode mixerNode;
    AudioComponentDescription cd = {};
    cd.componentManufacturer = kAudioUnitManufacturer_Apple;
    cd.componentType = kAudioUnitType_Mixer;
    cd.componentSubType = kAudioUnitSubType_MultiChannelMixer;

    if ((err = AUGraphAddNode(graph, &cd, &mixerNode))) {
        NSLog(@"Unable to add mixer node to AU graph: %@", @(err));
        return NO;
    }

    if ((err = AUGraphNodeInfo(graph, mixerNode, 0, &_mixerUnit))) {
        NSLog(@"Unable to fetch mixer node reference from AU graph: %@", @(err));
        return NO;
    }

    UInt32 busCount = 2;
    err = AudioUnitSetProperty(_mixerUnit,
                               kAudioUnitProperty_ElementCount,
                               kAudioUnitScope_Input,
                               0,
                               &busCount,
                               sizeof (busCount)
                               );

    if (err) {
        NSLog(@"Unable to set mixer unit properties");
        return NO;
    }

    // 5.1 Connect the instrument node as the mixer node input
    if ((err = AUGraphConnectNodeInput(graph, instrumentNode, 0, mixerNode, 0))) {
        NSLog(@"Unable to connect input node and mixer node: %@", @(err));
        return NO;
    }

    // 5.2 Connect the mixer node as the output node input
    if ((err = AUGraphConnectNodeInput(graph, mixerNode, 0, outputNode, 0))) {
        NSLog(@"Unable to connect input node and mixer node: %@", @(err));
        return NO;
    }

    if ((err = AUGraphInitialize(graph))) {
        NSLog(@"Unable to initialize AU graph: %@", @(err));
        return NO;
    }

    // 6. Finally, start the graph
    if ((err = AUGraphStart(graph))) {
        NSLog(@"Unable to start AU graph: %@", @(err));
        return NO;
    }

    self.graph = graph;
    self.instrumentUnit = instrumentUnit;

    return YES;
}

After setting up the node mixer, I can change the volume like this ( introduces cropping! ):

-(void) setVolume: (float) volume {
    AudioUnitSetParameter(_mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Output, 0, volume, 0);
}

2. Change MIDI events

The second approach sets the property velocityfor each node On event to a maximum of 127. It increases the volume without any noise, but only to a certain level. The code is inside MIKMIDINoteEvent:

+ (MIKMIDINoteOnCommand *)noteOnCommandFromNoteEvent:(MIKMIDINoteEvent *)noteEvent clock:(MIKMIDIClock *)clock
{
    MIKMutableMIDINoteOnCommand *noteOn = [MIKMutableMIDINoteOnCommand commandForCommandType:MIKMIDICommandTypeNoteOn];
    noteOn.midiTimestamp = [clock midiTimeStampForMusicTimeStamp:noteEvent.timeStamp];
    noteOn.channel = noteEvent.channel;
    noteOn.note = noteEvent.note;
    noteOn.velocity = 127; // <------- Here the magic
    return [noteOn copy];
}
+4
3

, , . AVSession ​​ AVAudioSessionCategoryPlayAndRecord, , iPhone . . ,

[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&err];

, , .

+2

, MIDI-. , (, + -32767 16- + -1,0 ), ( ) . , , , .

, ( , ).

: midi-, , ( PAPR, ) - .

+5

AVAudioSession SampleRate, 44100.0 Hertz?

0

Source: https://habr.com/ru/post/1629800/


All Articles