Image Map Image Map
Page 1 of 2 12 LastLast
Results 1 to 10 of 17

Thread: Hardware synthesis of CMF files?

  1. #1
    Join Date
    Sep 2011
    Location
    Toronto, Canada
    Posts
    150

    Default Hardware synthesis of CMF files?

    I was working on writing an adlib driver for my adventure game engine and I came across a reference on wikipedia about cmf files that "in the early 1990s when the format first came out this allowed songs to be synthesised entirely in hardware, meaning the performance impact of using CMF music was very low." The article continues to mention that games like Jill of the Jungle used cmf files in the way mentioned but I cannot seem to find any reference to how this was actually implemented. Normally I expect to have my driver on a timed interrupt to send commands to the card, this obviously can have a significant performance hit, something which I would like to avoid. Does anyone have any more insight into how this trick was done? Most resources seem to be sparse on information about early 90s dos programming.
    Currently working on new DOS game, Chuck Jones: Space Cop of the Future, Check out my Dev Blog

    Vintage Computers:
    Unitron Apple II clone, 2x Commodore Vic-20, Commodore 64, Commodore 128, Amiga 500, Macintosh Plus, Macintosh SE, 3 386sx PCs, Atari TT030

  2. #2

    Default

    That's a poorly-phrased explanation of it. The hardware (the OPL2 chip) handles all the actual sound synthesis, but the software still needs to handle things like timekeeping and writing the appropriate register values to the chip.
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/HS-80/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/M1, Ensoniq SQ-80, E-mu Emax HD/Proteus-2, Casio CZ-5000, Moog Satellite, Sequential Circuits Prophet-600
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

  3. #3
    Join Date
    Sep 2011
    Location
    Toronto, Canada
    Posts
    150

    Default

    I figured that might be the case, So my original idea of just writing register values in a timed interrupt is the way to go then?
    Currently working on new DOS game, Chuck Jones: Space Cop of the Future, Check out my Dev Blog

    Vintage Computers:
    Unitron Apple II clone, 2x Commodore Vic-20, Commodore 64, Commodore 128, Amiga 500, Macintosh Plus, Macintosh SE, 3 386sx PCs, Atari TT030

  4. #4
    Join Date
    Aug 2006
    Location
    Chicagoland, Illinois, USA
    Posts
    4,026
    Blog Entries
    1

    Default

    Seconded. Whoever wrote that about CMF files was incorrect. You have to write register values to define and sound the notes on an Adlib or compatible just like any other simple sound device.

    Interrupt overhead for a music engine running at 60Hz is not terrible. What is terrible is that the first-generation FM on sound cards (adlib, sound blaster) requires a delay when you send it data. For an engine that fires 60 times a second, writing all the sound card values can take up to half the available raster time! That's if you fire all 9 channels every frame; most music doesn't do this, so it's not terrible. The later FM chips like the OPL3 don't require this delay, which helps.
    Offering a bounty for:
    - Music Construction Set, IBM Music Feature edition (has red sticker on front stating IBM Music Feature)
    - Any very old/ugly IBM joystick (such as the Franklin JS-123)

  5. #5
    Join Date
    Sep 2011
    Location
    Toronto, Canada
    Posts
    150

    Default

    Hmm yes the delays might be a bit problematic, how would you produce the delays given that the suggested method of reading the register port several times is not reliable on fast machines and wasteful on slow machines?
    Currently working on new DOS game, Chuck Jones: Space Cop of the Future, Check out my Dev Blog

    Vintage Computers:
    Unitron Apple II clone, 2x Commodore Vic-20, Commodore 64, Commodore 128, Amiga 500, Macintosh Plus, Macintosh SE, 3 386sx PCs, Atari TT030

  6. #6

    Default

    The delay for the OPL2 is significant enough that I've pondered just setting an interrupt timer to fire when it's time to write the next byte, but I haven't checked into PC interrupt latencies enough to know whether that's not just as wasteful.
    Computers: Amiga 1200, DEC VAXStation 4000/60, DEC MicroPDP-11/73
    Synthesizers: Roland JX-10/SH-09/HS-80/MT-32/D-50, Yamaha DX7-II/V50/TX7/TG33/FB-01, Korg MS-20 Mini/ARP Odyssey/DW-8000/M1, Ensoniq SQ-80, E-mu Emax HD/Proteus-2, Casio CZ-5000, Moog Satellite, Sequential Circuits Prophet-600
    "'Legacy code' often differs from its suggested alternative by actually working and scaling." - Bjarne Stroustrup

  7. #7
    Join Date
    Aug 2006
    Location
    Chicagoland, Illinois, USA
    Posts
    4,026
    Blog Entries
    1

    Default

    Quote Originally Posted by PgrAm View Post
    Hmm yes the delays might be a bit problematic, how would you produce the delays given that the suggested method of reading the register port several times is not reliable on fast machines and wasteful on slow machines?
    The Adlib SDK says you are supposed to perform 6 dummy IN delays after writing a control byte and 35 dummy IN delays after writing a data byte. If you do this, it will work correctly on every single system, although as you noted it can be wasteful on the very slowest systems. Pseudocode:

    Code:
      asm
        mov bl,value
        mov dx,AdlibIndexReg
        mov al,register
        out dx,al
        {delay}
        in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx
        inc dx
        mov al,bl
        out dx,al
        {delay}
        in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx;
        in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx;
        in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx;
        in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx; in al,dx;
        in al,dx
      end;
    Quote Originally Posted by commodorejohn View Post
    The delay for the OPL2 is significant enough that I've pondered just setting an interrupt timer to fire when it's time to write the next byte, but I haven't checked into PC interrupt latencies enough to know whether that's not just as wasteful.
    CALL overhead would be more significant if you were planning on firing an interrupt *every* time it is "safe" to write an OPL control/data combo. The only reason you would do this is if you had a latency- or jitter-sensitive need in your program, and unless you're writing a demo I don't think it's worth the effort.

    My suggestion: Just fire off your music routine at the base BPM rate and simply do nothing if there are no notes to play or update.
    Offering a bounty for:
    - Music Construction Set, IBM Music Feature edition (has red sticker on front stating IBM Music Feature)
    - Any very old/ugly IBM joystick (such as the Franklin JS-123)

  8. #8
    Join Date
    Dec 2014
    Location
    The Netherlands
    Posts
    1,457

    Default

    Quote Originally Posted by Trixter View Post
    The Adlib SDK says you are supposed to perform 6 dummy IN delays after writing a control byte and 35 dummy IN delays after writing a data byte. If you do this, it will work correctly on every single system, although as you noted it can be wasteful on the very slowest systems.
    Does that even make a difference though?
    I mean, if all you're doing is 'in al, dx', that is only single-byte instructions. So the overhead there should be quite limited.
    I guess the performance is mostly governed by how quickly the OPL2 chip actually responds to the read, correct? I mean, as mentioned in the Tandy audio card thread, writing to a port when the SN76489 is present takes about 10 times as long as when there is no chip listening. I would expect reads to take relatively long as well? Perhaps making the additional overhead on a slower CPU marginal?

    If that is not the case however, then I suppose we could 'finetune' the number of reads you would have to do on a given system. Namely, if the 6 and 35 dummy reads are enough delay even on the fastest system (say a Pentium II with ISA slots?), then we can measure how long these take in absolute time.
    Once we know the absolute time (eg in PIT ticks), we could create a small routine of self-modifying code that will dynamically build up a series of 'in al, dx' instructions until it exceeds the desired delay time.

  9. #9
    Join Date
    Aug 2006
    Location
    Chicagoland, Illinois, USA
    Posts
    4,026
    Blog Entries
    1

    Default

    Quote Originally Posted by Scali View Post
    Does that even make a difference though?
    I mean, if all you're doing is 'in al, dx', that is only single-byte instructions. So the overhead there should be quite limited.
    Reads or writes to the bus always take a certain amount of time, much longer than the actual CPU execution of an IN or OUT. The delay is measured in "35 INs" because of how the hardware responds. Some people used other forms of delay (see below) but would use NOPs or something but that doesn't produce as accurate enough a delay.

    If that is not the case however, then I suppose we could 'finetune' the number of reads you would have to do on a given system. Namely, if the 6 and 35 dummy reads are enough delay even on the fastest system (say a Pentium II with ISA slots?), then we can measure how long these take in absolute time.
    Once we know the absolute time (eg in PIT ticks), we could create a small routine of self-modifying code that will dynamically build up a series of 'in al, dx' instructions until it exceeds the desired delay time.
    While this is the most CPU-friendly way of writing to the card, I think the actual time spent writing to the card is more of a worry than people think it is. If you are writing a background music driver, and you aren't writing demo effects or other 60Hz screen stuff, in practice things will work out fine without being a massive drain on the CPU. This is because you're not changing every channel every single frame, as traditional music has pauses. (Tracker music is different.)

    Or, you can just throw caution to the wind and do what some people did as shortcuts: Perform a local jump, which performs a single bus settle and clears the prefetch queue. Indianapolis 500 does this whenever it needs to delay between writes:

    Code:
    mov dx,0388
    mov al,A0
    add al,bl
    out dx,al
    call pwait
    
    ...
    
    pwait:
      mov al,0c
      dec al
      nop
      jne pwait
      ret
    Since the official published method from Adlib was the IN 6 or 35 times method, that's what I've stuck to.
    Offering a bounty for:
    - Music Construction Set, IBM Music Feature edition (has red sticker on front stating IBM Music Feature)
    - Any very old/ugly IBM joystick (such as the Franklin JS-123)

  10. #10
    Join Date
    Dec 2014
    Location
    The Netherlands
    Posts
    1,457

    Default

    Quote Originally Posted by Trixter View Post
    Reads or writes to the bus always take a certain amount of time, much longer than the actual CPU execution of an IN or OUT. The delay is measured in "35 INs" because of how the hardware responds.
    Yea, but that was my question: is there much of a difference between '35 ins' on a 8088 vs a Pentium II? If you're spending 99% of the time waiting on the OPL2 to put the actual data on the bus, then the answer would be 'no'.

    Quote Originally Posted by Trixter View Post
    This is because you're not changing every channel every single frame, as traditional music has pauses. (Tracker music is different.)
    I suppose what you're saying is that OPL2 is meant to use its own built-in ADSR to just play notes as 'fire-and-forget'?
    I don't know how AdLib trackers are designed, but if they're modeled after Amiga trackers, then they wouldn't do this. The Amiga hardware could only play a sample and loop it automatically. Any kind of pitch bending, vibrato, tremolo, volume enveloping or whatever would require constant updates to the registers.
    If you were to implement a music routine on AdLib that way, then you'd be looking at a lot of (slow) port writes.
    However, the OPL2 can perform vibrato, tremolo, ADSR and such by itself, so you don't need to 'babysit' the channels every frame. You'd just need to design your music tools around this. It's much more similar to MIDI commands... send a note on/off, or modify some parameters on-the-fly for more advanced pitch bending or sound shaping.

    Quote Originally Posted by Trixter View Post
    Indianapolis 500 does this whenever it needs to delay between writes:
    Something tells me this goes horribly wrong on faster systems.

Page 1 of 2 12 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •