chipweinberger / dart_melty_soundfont

A port of Melty Synth by Nobuaki Tanaka (C#) to Dart
Other
33 stars 8 forks source link

SampleEndLoop error comes up #9

Closed nathanblanchard12 closed 2 years ago

nathanblanchard12 commented 2 years ago

I am getting a flutter: 'sampleEndLoop' is out of range. 'STRING error. My code

static byteToPlayer() async { try{ // Create the synthesizer. ByteData bytes = await rootBundle.load(defaultURL);

  Synthesizer synth = Synthesizer.loadByteData(bytes);

  // Turn on some notes
  synth.noteOn(channel: 0, key: noteToPlay, velocity: 120);

  // Render the waveform (3 seconds)
  ArrayInt16 buf16 = ArrayInt16.zeros(numShorts: 44100 * 3);

  synth.renderMonoInt16(buf16);

  return {"bytes" : buf16.bytes.buffer.asUint8List(), "isSuccessful": true};
}catch(e){
  print('byteToPlayer');
  print(e);
  return {"bytes" : 0, "isSuccessful": false};
}

}

chipweinberger commented 2 years ago

please share a copy of your SF2 file.

nathanblanchard12 commented 2 years ago

Brass.sf2.zip

chipweinberger commented 2 years ago

@sinshu, any chance you want to try this file in your C# version?

sinshu commented 2 years ago

The C# version works with the following code. The output waveform seems OK as well.

// Create the synthesizer.
var sampleRate = 44100;
var synthesizer = new Synthesizer("Brass.sf2", sampleRate);

// Play some notes (middle C, E, G).
synthesizer.NoteOn(0, 60, 100);
synthesizer.NoteOn(0, 64, 100);
synthesizer.NoteOn(0, 67, 100);

// The output buffer (3 seconds).
var left = new float[3 * sampleRate];
var right = new float[3 * sampleRate];

// Render the waveform.
synthesizer.Render(left, right);
chipweinberger commented 2 years ago

Thanks for trying. oh no! a bug in my port! =(

chipweinberger commented 2 years ago

@nathanblanchard12, you may have to debug this further yourself. I wont be able to look at it for a few weeks.

nathanblanchard12 commented 2 years ago

Ok let me know. :)

chipweinberger commented 2 years ago

I'm not reproducing this issue:

I added this print statement:

        print("endloop: ${region.sampleEndLoop()} sampleCount: $sampleCount");

        if (!(0 < region.sampleEndLoop() &&
            region.sampleEndLoop() <= sampleCount)) {
          throw "'sampleEndLoop' is out of range. '${region.sample.name}'.'${instrument.name}'.";
        }

And I get these results:

flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
flutter: endloop: 40250 sampleCount: 112664
flutter: endloop: 56319 sampleCount: 112664
chipweinberger commented 2 years ago

Interestingly, it still does not work though. I get an error here.

Screen Shot 2022-04-30 at 5 20 23 AM

I take it you are on web? I know dart handles numbers differently on web. I don't have code that loads SF2 files on web yet (did you write your own?).

chipweinberger commented 2 years ago

So debugging the above error a bit, I see a crazy value for fineTine, which is causing us to do crazy pitchRatios and calculate bad array bounds.

gs.[GeneratorType.fineTune] = 65497

Which is out of range since fine tuning should be [-8192,8191]

@sinshu, I dont see how your code deals with signed/vs unsigned for GeneratorType.fineTune. Edit: It looks liked you read from the file as ushort, but implicitly convert to short in InstrumentRegion.cs. I'm not sure how C# handles implicit conversions.

Dart does not have shorts (only ints).

I wrote this code to work around the int only limitation, but I think it's causing problems because I dont handle signed/unsigned?

int castToShort(int v) {
  return v & 0xFFFF;
}

@sinshu, does your c# version actually render correctly?

class Generator
{
    final GeneratorType type;
    final int value; // ushort

    Generator(this.type, this.value);

    factory Generator.fromReader(BinaryReader reader)
    {
        GeneratorType type = generatorTypeFromInt(reader.readUInt16());
        int value = reader.readUInt16(); // I later call castToShort on this

        return Generator(type, value);
    }
chipweinberger commented 2 years ago

Think i just need to do this:

int castToByte(int v) {
  int m = v & 0xFF;
  if (m >= 128){
    // handle two's compliment
    m = -128 + (m - 128);
  }
  return m;
}

int castToShort(int v) {
  int m = v & 0xFFFF;
  if (m >= 32768){
    // handle two's compliment
    m = -32768 + (m - 32768);
  }
  return m;
}
sinshu commented 2 years ago

Most of the fine tuning values were -39 in the C# version. Since the value 65497 = 65536 - 39, your fix above should work.

chipweinberger commented 2 years ago

Yes that fixed it! Beautiful brass music!

chipweinberger commented 2 years ago

Pushed the fix to pub.dev.

chipweinberger commented 2 years ago

thanks @sinshu!

chipweinberger commented 2 years ago

Brass.sf2.zip works for me with the above change, closing.