• Please review our updated Terms and Rules here

MOD player Porta to Note effect

With what you listed above, you increment it too much.
it is 5 Times per tick, not 5 then 6
Ah, right. So that is what I was going back and forth with Trixter about earlier. Using your suggestion of multiplying the porta parameter by 2 and not sliding on tick #0 (including not sliding on tick #0 of subsequent rows) sounds a whole lot better. :)

I was wondering why your note values are half of what mine are? I.e. 320 to 160 instead of 640 to 320. We read from the same source and the period value for that note is 640 for me. 🤔

Other mods that sounds bad may be because of the way you manage finetune
Yeah, I need to look into this. I don't see those other MODs changing the finetune value and I'm literally using the periods table I linked to earlier (the one from Brett Paterson's docs). And I am following the finetune for the channel, default is finetune #0. This is how I get my Amiga period value, pretty straight forward:
Code:
channel->period_value = _mod_tunings[channel->finetune][channel->period_index];
When I load the patterns I scan through all the notes and do a look up in finetune table #0 and if a match is found I then store the index to that period on the note data structure. I think this is a pretty common technique if I am not mistaken since it allows you to switch between fine tunings easily.

Oh, just saw that your target is assembly, for 8088/8086

May I ask you why ? :) Because it is not easy at all.
That is the eventual objective. Like Kennedy said "We do these things not because they are easy but because they are hard - Moon Landing Speech". I guess this MOD player is my moon landing :)

Everyone needs a hobby right? Some people collect stamps and others do crossword puzzles. Its more about solving a tricky problem than anything else I guess.

it took me years to have Mod Master at its current level of performance.
I have no illusions. I actually wrote the barebones of this MOD player for Windows, minus many of the effects, about 20 years ago. I recently dug out the source code and was wondering if I could port it to 16bit DOS as a xmas break project. The initial port took me about a day, it even ran on my 286/16 @8Khz (using the fixed point implementation).

I expect many more hours of screwdriver work ahead before going into the Assembler part, first I'd like to get a decent reference implementation working in C and it seems that the effects are the hardest part.

Its nice though to be able to ask a question to others, I was stuck on this porta thing for about a week before I actually decided to reach out to the community. Sometimes you just go down the wrong path and just "rubber ducking" with other often gives you new avenues or insights you otherwise would not have been able to get to.

So, many thanks for engaging, its much appreciated! :)
 
So you decided to do it more or less like me.
I did port the mod player I wrote from 93 to 96 to XT computer after discovering GLX player and i just wanted to see if I can give it another chance because it was not popular

I.asked because you May have a project to write a game or a demo.

I strongly suggest you to not write everything in asdembly directly.
Focus on the software mixing first.

I wrote a detailed description of my mixing code to explain to trixter how I did it 😀
 
Oh, I am not planning to write a general purpose player. It is actually meant to be a player for some "fake cracktros" I want to make, that is, if I ever get there. :)
 
Reponse from here: https://forum.vcfed.org/index.php?threads/dma-addressing.1241044/post-1294008

Ended up with something similar. At the moment I am using a 50ms buffer that is split in two (i.e. the SB auto-initialize is triggered at half the size of that buffer, so, middle and end). The buffer starts off empty (silence) so when playing starts and the first half is reached, an IRQ is triggered that will start mixing into that first half. Playback continues and another IRQ is triggered at the end of the buffer and I now mix into the second half of the buffer. I.e. mixing is always half a buffer behind. :)

My mod_mixer function takes 2 parameters, first the buffer to mix into and the second to indicate the size of that buffer. This is to create some decoupling between the mod player logic and the sound device. This way I could technically stream the mod output into a wav file or something.

Edit: Oh, forgot to mention. I need to remove the mixing from the mod player in its entirety. It should just prepare the stream data for each individual channel so that the mixing is actually done outside the mod player, should be done by the audio device driver instead.

This is my basic main function. Removed a bunch of stuff for brevity like command line parameter parsing:
Code:
#define AUDIO_MODE       MOD_MODE_MONO
#define AUDIO_BITS       8

int main(int argc, char *argv[])
{
  buffer_size = (((frequency / 1000) * 50) * AUDIO_MODE * (AUDIO_BITS / 8));
  buffer_size_half = buffer_size >> 1;

  dma_block = dma_block_alloc(buffer_size);

  mod_load(filename)

  mod_init(frequency, AUDIO_BITS, AUDIO_MODE);

  sb_detect(&version, &sb_base_io, &sb_irq, &sb_dma);

  sb_irq_capture(sb_irq);

  sb_play(sb_base_io, sb_dma, dma_block, frequency);

  buffer_page = 0; // Indicates which half of the buffer we want to target and the first time we mix its in the first half.

  buffer_offsets[0] = 0;
  buffer_offsets[1] = buffer_size_half;

  while(1) {
    if (sb_get_irq_status()) {
      sb_reset_irq_status();

      buffer = (unsigned char*)dma_block->buffer + buffer_offsets[buffer_page];

      mod_mixer(buffer, buffer_size_half);

      buffer_page ^= 1; // Flip it.
    }

    if (kbhit()) {
      int key = getch();

      if (key == 27) {
        break;
      }
    }
  }

  sb_stop(sb_base_io);

  sb_irq_release(sb_irq);

  mod_free();

  dma_block_free(dma_block);
}

This is what happens in my mod_mixer function. For each number of samples to mix (buffer size) I do:
Code:
mix_sample = 0;

for (i = 0; i < MOD_MAX_CHANNELS; i++) {
  MOD_CHANNEL* channel = channels[i];

  uint16_t position = (uint16_t)channel->sample_position;

  if (position < channel->sample_length) {
    mix_sample += instruments[channel->instrument_index]->data[position] * channel->volume;

    channel->sample_position += channel->delta;
  }
  else {
    MOD_INSTRUMENT* instrument = instruments[channel->instrument_index];

    if (instrument->repeat_length > 0) {
      channel->sample_position = (float)instrument->repeat_point;
      channel->sample_length = instrument->repeat_point + instrument->repeat_length;
    }
  }
}

// Rather than SHR 6'ing each channel on volume multiply, just do one SHR 8 at the end.
mix_sample = (mix_sample >> 8);

mix_sample = mix_sample * master_volume;
mix_sample = mix_sample >> 6;

*mix_buffer++ = (int8_t)mix_sample + 127; // +127 for SB weirdness...
 
Back
Top