1

(11 replies, posted in Other Platforms)

So I tried 4-bit PCM:

4-bit PCM

I guess even on a real stock MZ-700, the rate is too low for the frequency carrier to be filtered out without adding a low-pass RC at the loudspeaker.

I also tried the mode 2 which allows to output a high cycle pulse (1,180MHz!) by playing with the counter. I believe it is that you call PFM. But I can only get negative energy because I cannot invert the output to get the positive energy.

PFM

 
For now, let's forget the 4-bit PCM and be back to 1-bit PCM.

Currently here is what I get in "pure" 1-bit PCM->PWM:
1x1-bit PCM
This consists in making a duty at 0% or 100%. So, the maximum frequency is 1.9 KHz.

But I hope to double the resolution by a set of duty: 0%, 50% and 100%.

Indeed, I can use the mode and its counter to recreate a square signal with a 0%, 50% or 100% duty within an ISR.
1x1-bit PCM
I haven't tested it yet because it involves a change in the player or in the data to be read and transformed.

2

(4 replies, posted in Other Platforms)

So far, that #20 is for format:

#00    0 0    — x —    Latch counter value. Next read of counter will read snapshot of value.
#10    0 1    mode    Read/Write low byte of counter value only
#20    1 0    mode    Read/Write high byte of counter value only
#30    1 1    mode    2×Read/2xWrite low byte then high byte of counter value

We can use #10/#20/#30 and #18/#28/#38 *BECAUSE* we never set the counter value after setting the MODE. Unhopefully, #00 and #08 cannot work *because* they are not setting the MODE which is the condition to set a default value to OUT0 immediately.

3

(11 replies, posted in Other Platforms)

Regarding nick change, I had a look on profile and see nowhere how to do so.

I will re-read the rest tomorrow. smile

4

(11 replies, posted in Other Platforms)

Hi @utz !

Thanks for your advices.

First I must explain I'm not a musician (I even wear hearing aids with a disability of almost 90%). So this is not an easy task for me to add sound and that's why I joined here. I recently read a lot of information about PCM, PWM, PDM, and so on. I'm quite aware that having such low rate may not give all the expectations. 

1) "Beware of the Nyquist limit". I wasn't sure when and what kind of filter to pass. So I must apply a *LOW-PASS* filter with a cutoff of ~1902Hz *BEFORE* resampling it to ~3903Hz. Copy that. I recreate the CSND wave file from "CantSlowDown" through Beepola using Savage and apply those transformations to get a 1-bit PCM turned into compressed PWM bytes to "show" you the effect of 4-bit PCM when I set the H signal globally with a fix $10 (~6%), $040, $80, $C0 or $F0 (100%) and $00 for a L signal.

2) yeah, pcm2pwm is truncating crudely if I don't extremely saturate the wave file into 1-bit PCM. And we get a flat volume.

3) You can hear the result of a global "intersective PWM" for each wave file I captured (the ones ending with "<n>over15" (the same duty is applied globally on each file). The less "volume" is, the more we can hear the 3903Hz.

4) Interesting, I wish there is a way to port one engine in such a way it can fill the ring buffer and be adapted to the rate limit.

At worst case, I will use the in-game "engine" to make sound and noise effects through events.

I put several wav files into the archive:

- original musique done through Beepola using Savage
- audicity transformations before pcm2pwm
- different sound captures for different global duty.

Be sure to lower the volume.

5

(11 replies, posted in Other Platforms)

Oh that! I used an emulator (my stock MZ-700 needs to be repaired). Due to the fact I need to use Audacity to amplify a wave stream extremely and to resample it to 3902.8314Hz to transform into a "1-bit PCM" wav file, It is not satisfying as it is very saturated.

I used that MP3 as source: Learn Soundation - Chip Music - Square Wave.mp3.

I made an horrible resampling of it: Sampler.wav.

And it sounds as such when playing SpaceRallyTest.mp4.

Be sure to lower the volume.

6

(11 replies, posted in Other Platforms)

I'm not confident to build a tracker to generate those bytes so for now I used the pcm2pmw v1 tool (https://github.com/JeffAlyanak/pcm2pwm) in the 1-bit PCM case. I need to write a tool to encode a wave file into a 4-bit PCM using intersective PWM encoding.

Obviously WAV or MP3 bitstreams are too big to encode into bytes playable by the game. I don't know if I will use that player for music background or just for game sound effects.

7

(11 replies, posted in Other Platforms)

I am currently experimenting with an approach to create a volume effect. Instead of emitting a high or low signal with 100% duty every ISR, I have introduced (partially for the moment) the notion of 4-bit PCM instead of 1-bit PCM. Well, I realized that the mode 0 allowed me to create a low and then a high signal with a "programmable" duty. Indeed, as soon as the counter reaches the threshold, it switches to the high signal and stays there until the counter is reprogrammed.

So I thought I could have fun creating a 4-bit PCM with duties like this : 0/15, 1/15, 2/15, ..., 14/15, 15/15. Indeed with 1/15, the volume seems much lower.

Now the technique (and why only 4-bit PCM):

Instead of a 256-byte page containing the mode programming bytes of counter 0 (used for the production of its square to 1.108404688 MHz), I determine two contiguous tables. The first one continues to play the same role but indirectly because it contains indexes on the second page which contains code every 16 bytes to produce a square signal with a duty related to the index of the period of an ISR (one every 3.902825 KHz)

Here is the definition of the two pages:

    align 256
sfx_buffer:
        DS        256,$00
sfx_play_pwm_0:
        LD        HL,$E007 ; duty 0% !
        LD        (HL),$28
        JP        sfx_isr_ret
    align 16
sfx_play_pwm_1_15:
        LD        HL,$E007 ; 16-bit counter, duty 6,6...%
        LD        (HL),$30
        LD        L,$04
        LD        (HL),SFX_DUTY_1_15>>0
        LD        (HL),SFX_DUTY_1_15>>8
        JP        sfx_isr_ret
    align 16
...
sfx_play_pwm_14_15:
        LD        HL,$E007 ; 16-bit counter, duty 93,3...%
        LD        (HL),$30
        LD        L,$04
        LD        (HL),SFX_DUTY_14_15>>0
        LD        (HL),SFX_DUTY_14_15>>8
        JP        sfx_isr_ret
    align 16
sfx_play_pwm_15:
        LD        HL,$E007 ; duty 100% !
        LD        (HL),$20
        JP        sfx_isr_ret

I took 4-bit index because I only have 2 bytes left for the 1/15 to 14/15. By switching to 5-bit, I wouldn't be able to fit all the duties in one page of code.

Now I have to adapt the ISR :

sfx_isr:    PUSH    HL
        PUSH    AF
        
        LD        HL,$E006 ; rearm counter to get the next ISR
        LD        (HL),PIT_CNT2
        
        LD        HL,sfx_buffer ; current index stored in the first page
sfx_isr_buffer_index equ $-2
        LD        L,(HL) ; read a byte $i0 where i is the index
        INC        H ; go to the next page
        JP        (HL) ; jump to the code emitting a square signal with the right duty 

sfx_isr_ret:        
        LD        HL,sfx_isr_buffer_index ; next index for the next ISR

sfx_isr_on_off:
        NOP ; either $34 (INC [HL]) when playing is ON or $00 (NOP) if OFF

        POP        AF
        POP        HL

        EI
        RET

The ISR initially had 114 execution cycles. Here we have 115 cycles to which we must add either 30 cycles or 57 cycles depending on the desired duty: 145 or 172 cycles. The game always seems to run at 50 fps.

The reason I wanted to do this is twofold:

- to see if by this set of duty, we could create a volume effect and thus have the possibility to mix several sources.
- to test the possibility to make "intersective" PWM : https://en.wikipedia.org/wiki/Pulse-width_modulation.

8

(11 replies, posted in Other Platforms)

Hi,

I'm the owner of an MZ-700 and coding some games on it.

First, let me introduce to its sound hardware:

- Loudpseaker 8Ohm/1W
- Notes are usually played through a square signal from 16Hz to 1.108404688MHz using i8253 counter 0 in mode 3.

PWM can be done by alternating mode 0 and mode 4 without setting counter 0.

In fact, we can also use mode 0 with setting count 0 to create a one-shot square signal with a variable duty: intersective PWM.

To be able to output sound in-game, I use an interrupt. Stock MZ-700 mostly uses /INT to toggle AM.PM flag and to readjust time through counters 1 and 2. Since my games are standalone, I usually shut down this interrupt to allow "fast" screen drawing using stack trick (POP/PUSH) because you can access VRAM only when horizontal blanking is on.

So I need another trick to be able use interrupts without disturbing the drawing code. It happens the clock of counter 1 is the same signal which freezes CPU when attempting to access VRAM when BLNK is 0. Interrupt is raised at falling edge of BLNK. So I use HALT instruction to wait for an ISR to toggle the sound output or not then transfer N VRAM blocks through stack then restore a valid stack and do that for 25 lines (in total, we have 2 x 28 x 25 characters/attributes to draw in 100 horizontal blanks - 312 for a frame). I won't dig in the details so let say what is possible:

- for a full 50 FPS, I need an ISR every 4 horizontal blanks to avoid screen corruption. PWM rate is 3902.8314Hz.
- for a full 25 FPS, I need an ISR every 3 horizontal blanks to avoid screen corruption. PWM rate is 5203.7752Hz.

I used IM2 a la Spectrum to set my ISR and here is the ISR:

sfx_isr:
        PUSH    HL
        PUSH    AF

    if DEBUG
        LD      HL,isr_count
        INC     (HL)
    endif

        LD      HL,$E006 ; rearm counter to get the next ISR
        LD      (HL),PIT_CNT2

        LD      A,(sfx_buffer) ; get $20 or $28 to toggle the output sound (PWM 0 or 100%)
sfx_isr_buffer_index equ $-2
        INC     L
        LD      (HL),A

        LD      HL,sfx_isr_buffer_index ; advance in buffer (256-byte ring page)
sfx_isr_on_off:
        NOP ; opcodes $34 (INC [HL]) for ON or $00 (NOP) for OFF

        POP     AF
        POP     HL

        EI
        RET

$20 is mode 0 (final sound output is 0) and $28 is mode 4 (final sound output is 1).

I then have a code to run in the game loop to fill the ring page on the fly from the data generated by pcm2pwm v1.

For 50 FPS, I need 78 bytes in the ring page to play per frame.
For 25 FPS, I need 104 bytes in the ring page to play per frame.