xm3smps hates me

Discussion in 'Technical Discussion' started by wazkatango, Jul 28, 2011.

  1. Recently, I wanted to try using the PSG channels for a remix I'm making (all three channels, not including the noise), and I can't seem to get the rests working. Normally, if I had no rests or notes being playing for a very long time on one channel, the channel would grab the nearest note and start playing too early. I understand why this is the case, because the smps format has a limit to how long a note or rest can be played and all that. Well, normally I can just put a note cut (==) every so often in that channel, and it will stay silent for the correct amount of time. For the PSG channels however, this rule gets thrown out the window. For some bizarre reason, if I use more than one note cut, the channel will all of the sudden start to play the last note it was playing before it rested the first time. What.

    Any ideas?

    By the way, if I have any more problems with xmps, I'm necro-ing this thread, if you don't mind
     
  2. amphobius

    amphobius

    doing more important things with my life Member
    2,121
    0
    16
    life
    SMPS itself has a limit for how long there can be silence in a channel, 7F if I recall correctly.

    I just used to plonk down a note rest every pattern, but I can't remember if this works since it's been a long time since I've used these tools.
     
  3. Doesn't this bug only apply to speeds higher than 02xx? Or something? I can't remember for the life of me.

    However if this bug appears, put a note rest every 16 rows.
     
  4. nineko

    nineko

    I am the Holy Cat Tech Member
    The bug explained:

    SMPS note durations, as you know, can be in the $01-$7F range, because $80 to $DF are notes, and $E0 to $FF are flags.
    When the driver processes a note, it puts its duration in RAM, and then it decreases that by 1 every time it is meant to (according to the second byte of the tempo, but that's another story). For example, if a note is supposed to last for 20 time units, the driver will load 20 when that note is played, and then it will decrease it to 19, 18, 17, ...; and when it reaches 0, that note ends and the driver processes the next one.
    Now, the problem arises because note durations are stored as bytes in RAM. You might wonder why this is a problem, since the maximum duration, $7F, is less than 255. The issue lies within the first byte of the tempo, usually mistakingly called "dividing timer" because it "divides" the tempo (e.g. if it's 02xx it goes half as fast as a 01xx), but in reality it actually MULTIPLIES note durations when they are loaded in RAM, so a note which is 10 time units long will be actually 20 time units long if the tempo is 02xx instead of 01xx, with the driver running at the same actual speed.

    To get the maximum allowed duration for a given "dividing timer" you have to divide 255 by it:
    01xx -> 255 / 1 -> 255 -> $7F (no bug)
    02xx -> 255 / 2 -> 127.5 -> $7F (no bug)
    03xx -> 255 / 3 -> 85 -> notes longer than $55 won't be processed correctly
    04xx -> 255 / 4 -> 63.75 -> notes longer than $3F won't be processed correctly
    etc.

    Note that this problem arises with both rests AND notes; while it's easy to fix it for rests (just put more of them in a row), it's harder to fix it for notes, because it requires additional $E7 flags to make notes longer without cutting them. I can't speak for xm3smps/oerg and xm4smps, but my original xm3smps code is hardcoded to put an $E7 only if the note duration is longer than $7F, so if you use a > 03xx tempo you're fucked.

    Last but not least, $E7 flags are ignored on the DAC channel (iirc) so you can have to actually put rests there.
     
  5. Well the problem isn't that the notes aren't long enough, it's that they play instead of resting whenever I call a rest. For example, if I do this:
    C#4 (plays)
    --
    --
    == (rests)
    --
    --
    == (starts playing C#4 again. When playing this note, it sounds like it's quieter than before and is fading, like it's been playing the note the whole time and was just muted for a second)
    --
    == (same deal as above)

    And so on. Again, this doesn't apply to any of the FM channels, they rest correctly as many times as I put down rests, this only occurs with the PSGs.

    I've also tried adjusting the tempo in different ways, I.e. 02xx and 04xx to no avail.
     
  6. Hodgy

    Hodgy

    Member
    797
    0
    16
    UK
    Games programming :)
    I've personally found that the preview feature likes to turn the PSG channels back on but when I play the song in an emulator or on hardware this does not happen.
     
  7. nineko

    nineko

    I am the Holy Cat Tech Member
    Such a scenario is literally impossible, as xm3smps processes FM and PSG notes in the same block of code (only DAC and noise are processed separately). Again, I can't know if Oerg altered the conversion code, but I'm fairly confident that he didn't, so you're actually encountering a bizzarre issue. Feel free to send me the XM to investigate it, along with a list of the channel assignments.

    For the record,
    Code (Text):
    1.     If nota = 97 Then
    2.         gnota = 128
    3.     Else
    4.         Select Case usage(I)
    5.         Case Is = 4
    6.             gnota = 209
    7.         Case Is = 2
    8.             If Form4.Option1(1).Value Then gnota = Asc(Mid$(Form4.dorri$, nota, 1))
    9.             If Form4.Option1(0).Value Then gnota = Asc(Mid$(Form4.dorri$, cins, 1))
    10.         Case Else
    11.             gnota = nota + 128
    12.         End Select
    13.     End If
    Excuse me while I used the Italian "nota" instead of the English "note", but that's the code which assigns an smps note number (stored in the "gnota" variable, I completely forgot what the "g" stands for though) given an XM note number (stored in the "nota" variable). As you can see, if the XM note number is 97 (the "==" thing), then the smps note number is 128 (the rest) in all cases; separate cases are made for XM note numbers other than 97 (e.g. any valid note), but even then, only noise (case 4) and DAC (case 2) get processed separately, with FM and PSG both being in the "case else" section (smps note number = XM note number + 128), so it is really impossible that the same note or the same rest gets different results on FM and PSG channels.
    The only difference between FM and PSG channels in xm3smps is how instrument changes are processed, but even then, the difference is minimal, as it's only a different flag ($EF and $F5 respectively).

    Trivia: there is actually an XM note number which can't be correctly converted to smps, since the XM range is 96 notes wide, and the smps range is 95 notes wide. The highest XM note, B-7, has a note number of 96, which added to 128 results into 224, or $E0, which is the first coordination flag. If there is a B-7 in your XM you should consider transposing it down and compensate for that in the smps header.
     
  8. Well actually Hodgy's right. It only happens when I use the "preview" program to hear everything before I convert and build in the song, so yeah, that's where the confusion is. It works just fine when converted and all that, thanks though.
     
  9. nineko

    nineko

    I am the Holy Cat Tech Member
    Oh, I had no way to know that, since I still use my own vanilla xm3smps, which doesn't have the previewer :v:

    Either way, this thread has been useful because I actually documented in a clear way a couple of xm-to-smps-related bugs which were known only to a selected few people before, so it's all good.