Quantcast

Results 1 to 6 of 6

Thread: 32x sound programming

  1. #1
    Take it apart! WCPO Agent MEGADRIVE Jeroi's Avatar
    Join Date
    Nov 2009
    Location
    Turku, Finland
    Posts
    986
    Rep Power
    15

    Default 32x sound programming

    Hello, I have taken look into 32X sound architecture and started to wonder things.

    Here is interesting Chilly willys interrupt-driven code for 32X audio:
    This is how you do interrupt driven DMA PWM audio on the 32X. The first thing to ask is, why? If you wish to do more than just audio on the slave sh2, you need to make your audio code interrupt driven. That way when you do something that takes a long time, the audio will interrupt the task as needed to generate audio. Otherwise you are stuck trying to break the tasks into small enough pieces that it doesn't interfere with your polled audio. With DMA'd buffers, that is a decent amount of time, but not enough for some things.

    This method works on real hardware and on Fusion 3.64 (remember that 3.63 sounds like crap). If you use Gens/GS release 7 with my DMA PWM modifications, you need to make one more change to pwm.c to use int-driven dma pwm:

    Change this
    if (PWM_Mode & 0x0080)
    {
    // RPT => generate DREQ1 as well as INT
    SH2_DMA1_Request(&M_SH2, 1);
    SH2_DMA1_Request(&S_SH2, 1);
    }



    to this
    if (PWM_Mode & 0x0080)
    {
    // RPT => generate DREQ1 as well as INT
    SH2_DMA1_Request(&M_SH2, 1);
    SH2_DMA1_Request(&S_SH2, 1);

    if ((SH2_Read_Long(&S_SH2, 0xFFFFFF9C) & 7) == 7)
    SH2_Interrupt_Internal(&S_SH2, (SH2_Read_Long(&S_SH2, 0xFFFFFFA8)<<8) | ((SH2_Read_Word(&S_SH2, 0xFFFFFEE2) >> 8) & 0x000F));
    }




    So how do we do interrupt driven dma pwm audio? First, assign the dma an exception entry in the exception table... I suggest the exception right after the autovectors since it's the first free entry not used by anything else, and easy to find. So your table looks like this at the end:

    .long slave_pwm /* PWM interupt (Level 6 & 7) */
    .long slave_cmd /* Command interupt (Level 8 & 9) */
    .long slave_hbi /* H Blank interupt (Level 10 & 11 */
    .long slave_vbi /* V Blank interupt (Level 12 & 13) */
    .long slave_rst /* Reset Button (Level 14 & 15) */
    .long slave_dma1 /* DMA1 TE INT */



    Make sure you're using the slave table, not the master. Now you need the code for that exception.

    !-----------------------------------------------------------------------
    ! Slave DMA 1 TE INT handler
    !-----------------------------------------------------------------------

    slave_dma1:
    ! save registers
    sts.l pr,@-r15
    mov.l r0,@-r15
    mov.l r1,@-r15
    mov.l r2,@-r15
    mov.l r3,@-r15
    mov.l r4,@-r15
    mov.l r5,@-r15
    mov.l r6,@-r15
    mov.l r7,@-r15

    mov.l sd1_handler,r0
    jsr @r0
    nop

    ! restore registers
    mov.l @r15+,r7
    mov.l @r15+,r6
    mov.l @r15+,r5
    mov.l @r15+,r4
    mov.l @r15+,r3
    mov.l @r15+,r2
    mov.l @r15+,r1
    mov.l @r15+,r0
    lds.l @r15+,pr
    rte
    nop

    .align 2
    sd1_handler:
    .long _slave_dma1_handler



    We push the registers that aren't saved by C, then call the C function, slave_dma1_handler(). Before I talk about that function, we need to see how to set up the dma in the slave code.

    void slave(void)
    {
    uint16_t sample, ix;

    // init DMA
    SH2_DMA_SAR0 = 0;
    SH2_DMA_DAR0 = 0;
    SH2_DMA_TCR0 = 0;
    SH2_DMA_CHCR0 = 0;
    SH2_DMA_DRCR0 = 0;
    SH2_DMA_SAR1 = 0;
    SH2_DMA_DAR1 = 0x20004034; // storing a long here will set left and right
    SH2_DMA_TCR1 = 0;
    SH2_DMA_CHCR1 = 0;
    SH2_DMA_DRCR1 = 0;
    SH2_DMA_DMAOR = 1; // enable DMA

    SH2_DMA_VCR1 = 72; // set exception vector for DMA channel 1
    SH2_INT_IPRA = (SH2_INT_IPRA & 0xF0FF) | 0x0F00; // set DMA INT to priority 15

    // init the sound hardware
    MARS_PWM_MONO = 1;
    MARS_PWM_MONO = 1;
    MARS_PWM_MONO = 1;
    if (MARS_VDP_DISPMODE & MARS_NTSC_FORMAT)
    MARS_PWM_CYCLE = (((23011361 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for NTSC clock
    else
    MARS_PWM_CYCLE = (((22801467 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for PAL clock
    MARS_PWM_CTRL = 0x0185; // TM = 1, RTP, RMD = right, LMD = left

    sample = SAMPLE_MIN;
    /* ramp up to SAMPLE_CENTER to avoid click in audio (real 32X) */
    while (sample < SAMPLE_CENTER)
    {
    for (ix=0; ix<(SAMPLE_RATE*2)/(SAMPLE_CENTER - SAMPLE_MIN); ix++)
    {
    while (MARS_PWM_MONO & 0x8000) ; // wait while full
    MARS_PWM_MONO = sample;
    }
    sample++;
    }

    // initialize mixer
    MARS_SYS_COMM6 = MIXER_UNLOCKED; // sound subsystem running
    fill_buffer(&snd_buffer[0]); // fill first buffer
    slave_dma1_handler(); // start DMA

    SetSH2SR(2);
    while (1)
    {
    if (MARS_SYS_COMM4 == SSH2_WAITING)
    continue; // wait for command

    // do command in COMM4

    // done
    MARS_SYS_COMM4 = SSH2_WAITING;
    }
    }



    Notice that the VCR for DMA1 is set to 72. That's that exception vector we added to the slave table. Notice that IPRA bits 12 to 8 are set to 0xF (15). That's the exception priority. Tailor that to your needs knowing that CMD is 8, HBlank is 10, VBlank is 12, and the reset button is 14. Other than that, we do a "standard" set up of the audio. We fill the first buffer and call the handler to start off the dma. After that, the transfer-end dma interrupt will continue the process. We then fall in a loop where we look at COMM4 for a command from the Master SH2 or 68K. That's where you stick any tasks that take a long time for the slave to do. Those tasks may/will be interrupted by the dma interrupt as needed to fill buffers and start the next dma operation.

    So how about that exception function?

    void slave_dma1_handler(void)
    {
    static int32_t which = 0;

    while (MARS_SYS_COMM6 == MIXER_LOCK_MSH2) ; // locked by MSH2

    SH2_DMA_CHCR1; // read TE
    SH2_DMA_CHCR1 = 0; // clear TE

    if (which)
    {
    // start DMA on first buffer and fill second
    SH2_DMA_SAR1 = ((uint32_t)&snd_buffer[0]) | 0x20000000;
    SH2_DMA_TCR1 = num_samples; // number longs
    SH2_DMA_CHCR1 = 0x18E5; // dest fixed, src incr, size long, ext req, dack mem to dev, dack hi, dack edge, dreq rising edge, cycle-steal, dual addr, intr enabled, clear TE, dma enabled

    fill_buffer(&snd_buffer[MAX_NUM_SAMPLES * 2]);
    }
    else
    {
    // start DMA on second buffer and fill first
    SH2_DMA_SAR1 = ((uint32_t)&snd_buffer[MAX_NUM_SAMPLES * 2]) | 0x20000000;
    SH2_DMA_TCR1 = num_samples; // number longs
    SH2_DMA_CHCR1 = 0x18E5; // dest fixed, src incr, size long, ext req, dack mem to dev, dack hi, dack edge, dreq rising edge, cycle-steal, dual addr, intr enabled, clear TE, dma enabled

    fill_buffer(&snd_buffer[0]);
    }

    which ^= 1; // flip audio buffer
    }


    Note that read TE/clear TE set of lines - those are REQUIRED for dma interrupts to occur properly. Took a while to figure that out. Then it's merely a matter of starting the next DMA on the proper buffer, then calling fill_buffer on the other buffer. We're then done until the next interrupt.

    See how easy that is?

    Here is the full source code. There is an arc with my current linker scripts for the compiler, an arc with the xm player library and converter tool, and an arc with the 32X code:

    ldscripts-32X.7z
    libxmp-v1.2.7z
    xmplayer-v1.1

    The resultant binaries:
    XMPlayer-Doom-Wolf3D-RSO.7z

    UP/DOWN immediately goes to the previous/next songs, LEFT/RIGHT changes the volume down/up (it starts at max volume), and START pauses/resumes.
    So if I understand right, you can use 16bit Audio and scale it by code into 10bit audio on 4 mono channels?

    What doeas that mean? Does 32x have digital to analog FM converter that has 4 mono 10bit inputs and converts them into Analog FM for Gensis?

    Would the 32x perform rather good on audio wise? 10bit is quite okayish resolution for audio still...



    Any toughts?
    Last edited by MEGADRIVE Jeroi; 10-24-2013 at 12:43 PM.
    #MEGADRIVEJeroi @ Quakenet irc server.
    Be true to yourself. GFX doesn't matter, the game does. If you are intrested to donate one NTSC Genesis for hardware testing purposes, please pm me.

  2. #2
    Take it apart! WCPO Agent MEGADRIVE Jeroi's Avatar
    Join Date
    Nov 2009
    Location
    Turku, Finland
    Posts
    986
    Rep Power
    15

    Default

    Here is more info by Chilly willy:

    You have one rate control - samples play at the master clock rate divided by rate value + 1. The master clock in the 32X is three times the 68000 clock, which is different for NTSC and PAL. Here's how I set the rate:

    if (MARS_VDP_DISPMODE & MARS_NTSC_FORMAT)
    MARS_PWM_CYCLE = (((23011361 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for NTSC clock
    else
    MARS_PWM_CYCLE = (((22801467 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for PAL clock



    Think of that rate as the mixer rate. You can play samples of any other rate, but you have to resample them to the mixer rate on the fly in software (I do that with the slave sh2). The number of bits in the PWM sample depends on the rate. That rate value you calculate above? That's a terminal COUNTER VALUE used for the pulse width modulation - it's the maximum width in sh2 clocks. So if the value was 1040, that the maximum value a sample can have. You also can't use the value 0 as a sample. You need to add half the cycle width to samples to set the center value, and limit samples to +/- one half the (cycle width - 2).

    1048 is about 22kHz sample rate, which is VERY popular in 32X games. Most samples the time are 8 bit, which is -128 to 127 for signed samples. Four of those added together gives -512 to 508. The center value for that rate would be 524, so PWM samples would range from 524 - 512 = 12, up to 524 + 508 = 1032. So your four 8-bit samples added together fit nicely in the cycle width for 22kHz.

    You have three sample registers that are emptied at the cycle width rate: you have a left channel, a right channel, and a mono channel; HOWEVER! the mono channel isn't a real channel - it merely writes both the left and right channel registers with the same value. There's a control register that let's you do things like turn off the channels, or flip the channels, but most folks just route the left channel to left out, and the right channel to right out. You have to store PWM samples to the channels, NOT PCM! The values must be 1 to (cycle width) to create any output. The channels have a FIFO so that you can store samples to the channel until the FIFO fills, and wait for it to empty. The FIFO empties at the rate specified, so unless you don't keep the FIFO at least partly full, the samples play at a steady rate (unlike the bare DAC in the YM2612). When you read the channel register, bit 15 is set is the FIFO is full, and bit 14 is set if the FIFO is empty.

    The 32X can interrupt either SH2 after X samples have been played (X being 1 to 15). SEGA's 32X sound sample code all used interrupt driven sample playing, supposedly because there were some bugs using the DMA for sound in the first developer units. Consumer 32Xs don't have these bugs, so I prefer to use DMA driven audio. In that case, we don't need to worry about interrupts or FIFOs. Instead, we initialize the DMA channel in the sh2 and fill the PWM FIFOs:

    // init DMA
    SH2_DMA_SAR0 = 0;
    SH2_DMA_DAR0 = 0;
    SH2_DMA_TCR0 = 0;
    SH2_DMA_CHCR0 = 0;
    SH2_DMA_DRCR0 = 0;
    SH2_DMA_SAR1 = 0;
    SH2_DMA_DAR1 = 0x20004034; // storing a long here will set left and right
    SH2_DMA_TCR1 = 0;
    SH2_DMA_CHCR1 = 0;
    SH2_DMA_DRCR1 = 0;
    SH2_DMA_DMAOR = 1; // enable DMA

    // init the sound hardware
    MARS_PWM_MONO = 1;
    MARS_PWM_MONO = 1;
    MARS_PWM_MONO = 1;
    if (MARS_VDP_DISPMODE & MARS_NTSC_FORMAT)
    MARS_PWM_CYCLE = (((23011361 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for NTSC clock
    else
    MARS_PWM_CYCLE = (((22801467 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for PAL clock
    MARS_PWM_CTRL = 0x0185; // TM = 1, RTP, RMD = right, LMD = left



    Fill the first sound buffer, and enter your loop with starting to play the first buffer, then immediately fill the second buffer, wait on the dma, start the second buffer playing, fill the first buffer, wait for the dma, and loop:

    if (MARS_SYS_COMM6 == 0)
    {
    MARS_SYS_COMM6 = 1; // sound subsystem running
    // fill first buffer
    fill_buffer((unsigned long)&snd_buffer);
    }

    // only do sound when sound subsytem initialized and unlocked
    while (MARS_SYS_COMM6 == 1)
    {
    // start DMA on first buffer and fill second
    SH2_DMA_SAR1 = ((unsigned long)&snd_buffer) | 0x20000000;
    SH2_DMA_TCR1 = NUM_SAMPLES; // number longs
    SH2_DMA_CHCR1 = 0x18E1; // dest fixed, src incr, size long, ext req, dack mem to dev, dack hi, dack edge, dreq rising edge, cycle-steal, dual addr, intr disabled, clear TE, dma enabled

    fill_buffer((unsigned long)&snd_buffer + MAX_NUM_SAMPLES * 4);

    // wait on DMA
    while (!(SH2_DMA_CHCR1 & 2)) // wait on TE
    {
    if (MARS_SYS_COMM4)
    task_handler();
    }

    // start DMA on second buffer and fill first
    SH2_DMA_SAR1 = ((unsigned long)&snd_buffer + MAX_NUM_SAMPLES * 4) | 0x20000000;
    SH2_DMA_TCR1 = NUM_SAMPLES; // number longs
    SH2_DMA_CHCR1 = 0x18E1; // dest fixed, src incr, size long, ext req, dack mem to dev, dack hi, dack edge, dreq rising edge, cycle-steal, dual addr, intr disabled, clear TE, dma enabled

    fill_buffer((unsigned long)&snd_buffer);

    // wait on DMA
    while (!(SH2_DMA_CHCR1 & 2)) // wait on TE
    {
    if (MARS_SYS_COMM4)
    task_handler();
    }
    }


    You can find all this code in my MOD player example, or the Yeti3D examples.

    Notice in the code how we are DMAing longs (32 bits). We DMA them to the left channel. A long is two words, the first word gets DMAed to the left channel, and the second word to the right channel. So we are doing stereo samples from the buffer, stored LEFT then RIGHT. You could set the DMA to words, and then set the destination to the mono channel for mono audio playback. The the buffer would be nothing but mono PWM samples.

    Notice in the code above that while waiting on the DMA done, the code checks the comm4 register to see if the other sh2 is signalling another task for the slave to handle. You don't need that if all you are using the slave for is mixing/playing the audio.
    #MEGADRIVEJeroi @ Quakenet irc server.
    Be true to yourself. GFX doesn't matter, the game does. If you are intrested to donate one NTSC Genesis for hardware testing purposes, please pm me.

  3. #3
    Take it apart! WCPO Agent MEGADRIVE Jeroi's Avatar
    Join Date
    Nov 2009
    Location
    Turku, Finland
    Posts
    986
    Rep Power
    15

    Default

    And here again more by Chilly Willy:
    In my drivers, I convert 8-bit samples to 16 bit, scale them for pan (left/right), and add them together as a 16-bit stream. At the very end, I convert the 16-bit stream to what the 32X uses (10-bit). It sounds really good. You should be able to add 8 to 16 channels without significant noise, depending on your software volume... I really think people should leave it at full volume and use the volume on the TV/stereo to control how loud it is. That gives the best quality out from the 32X as it doesn't have it's own analog volume control.

    A bit more precise detail:

    Look at the code I use to add a sample voice to the output stream:

    ! Uses: r0 = scratch
    ! r1 = scratch
    ! r2 = voices
    ! r3 = current buffer pointer
    ! r4 = buffer pointer (passed in from _fill_buffer)
    ! r5 = voice count
    ! r6 = sample count
    ! r7 = wave
    ! r8 = index
    ! r9 = step
    ! r10 = loop
    ! r11 = length
    ! r12 = left volume
    ! r13 = right volume
    ! r14 = sample

    process_svc:
    mov.l ps_svc_pause,r1
    mov.w @r1,r0
    cmp/pl r0
    bt proc_snd_exit /* paused - just exit */

    sts.l pr,@-r15 /* save return address */

    mov r4,r3
    bsr _SVC_Lock
    mov #3,r4 /* locked by SSH2 */
    mov r3,r4

    mov.l ps_svc_purge,r1
    mov.w @r1,r0
    cmp/eq #0,r0
    bt do_svc_voices
    /* flush svc voices */
    mov #NUM_VOICES,r2
    mov.l ps_flush_voices,r3
    mov #0,r0
    1:
    mov.l r0,@(0x00,r3)
    mov.l r0,@(0x10,r3)
    dt r2
    bf/s 1b
    add #vc_sizeof,r3
    mov.w r0,@r1

    do_svc_voices:
    mov.l ps_voices,r2
    mov #NUM_VOICES,r5
    0:
    /* check if voice playing */
    mov.w @(vc_flags,r2),r0
    tst #VF_PLAYING,r0
    bt 4f /* not playing - next voice */
    /* playing, check if belongs to module and paused */
    shlr8 r0
    tst #VF_MODULE,r0
    bt 1f /* not a module voice - play */
    mov.l ps_mod_pause,r1
    mov.w @r1,r0
    cmp/pl r0
    bt 4f /* module paused - next voice */
    1:
    /* process samples */
    mov r4,r3 /* start of sample buffer */
    mov.l ps_num_samples,r1
    mov.w @r1,r6
    mov.l @(vc_wave,r2),r7
    mov.l @(vc_index,r2),r8
    mov.l @(vc_step,r2),r9
    mov.l @(vc_loop,r2),r10
    mov.l @(vc_length,r2),r11
    mov.w @(vc_left,r2),r0
    shlr2 r0
    mov r0,r12
    mov.w @(vc_right,r2),r0
    shlr2 r0
    mov r0,r13
    2:
    add r9,r8 /* index += step */
    cmp/hs r11,r8
    bt 5f /* index >= length */
    3:
    /* process one sample */
    mov r8,r0
    shlr8 r0
    shll2 r0
    shlr8 r0
    mov.b @(r0,r7),r14
    /* scale sample for left output */
    muls.w r14,r12
    mov.w @r3,r1
    sts macl,r0
    shlr8 r0 /* now 13-bit sample - no overflow with 8 voices */
    exts.w r0,r0
    add r0,r1
    mov.w r1,@r3
    add #2,r3
    /* scale sample for right output */
    muls.w r14,r13
    mov.w @r3,r1
    sts macl,r0
    shlr8 r0
    exts.w r0,r0
    add r0,r1
    mov.w r1,@r3
    add #2,r3
    /* next sample */
    dt r6
    bf 2b
    mov.l r8,@(vc_index,r2)
    4:
    /* next voice */
    dt r5
    bf/s 0b
    add #vc_sizeof,r2

    bsr _SVC_Unlock
    nop
    lds.l @r15+,pr /* restore return address */
    rts
    nop
    5:
    /* check if loop sample */
    mov.w @(vc_flags,r2),r0
    shlr8 r0
    tst #VF_LOOPS,r0
    bt/s 6f /* no looping */
    sub r11,r8
    bra 3b
    add r10,r8 /* index = index - length + loop */
    6:
    /* check if one-shot sample */
    tst #VF_ONESHOT,r0
    bt 7f /* not oneshot - leave enabled */
    mov #0,r0 /* voice disabled */
    7:
    shll8 r0
    mov.w r0,@(vc_flags,r2) /* voice not playing (and possibly disabled) */

    /* next voice */
    dt r5
    bf/s 0b
    add #vc_sizeof,r2

    bsr _SVC_Unlock
    nop
    lds.l @r15+,pr /* restore return address */

    proc_snd_exit:
    rts
    nop


    Okay, let's do a little discussion on bits of it... the left and right volume values are 0 to $7FFF, or 15 bits. I shift the volume right 2 bits, making it 13 bits. I then multiply the 8 bit sample by the volume and shift the result 8 bits. That leaves the final sample as 13 bits with no loss of info. That allows me to add eight channels together before it has a chance of overflowing on a 16-bit stream. So I have a 16-bit stream with no noise for up to 8 channels. Now let's look at how the stream is converted for output:

    /* handle sample processing */
    handle_voices:
    /* clear sound buffer */
    mov r14,r3
    mov.l fb_num_samples,r1
    mov.w @r1,r2
    mov #0,r0
    1:
    mov.l r0,@r3
    dt r2
    bf/s 1b
    add #4,r3

    /* process sample voices */
    bsr process_svc
    mov r14,r4

    ! convert buffer from signed 15-bit samples (4 signed 13-bit samples)
    ! to unsigned PWM samples (4 to 516 @44kHz or 8 to 1032 @22kHz)
    ! only assumes eqv of 4 full-range samples for better volume, this
    ! may result in clipping

    mov r4,r3
    mov.l fb_num_samples,r1
    mov.w @r1,r2
    mov.w fb_sample_center,r1
    mov.w fb_sample_max,r6
    mov.w fb_sample_min,r5
    2:
    /* process left sample */
    mov.w @r3+,r0
    /* shift sample */
    .ifndef CD_SAMPLE_RATE
    shlr2 r0 /* 13 bits */
    shlr2 r0 /* 11 bits */
    shlr r0 /* 10 bits */
    exts.w r0,r0
    .else
    shll2 r0 /* 17 bits */
    shlr8 r0 /* 9 bits */
    exts.w r0,r0
    .endif
    /* clamp sample */
    cmp/ge r5,r0
    bf 5f /* clamp to minimum sample */
    cmp/gt r6,r0
    bt 6f /* clamp to maximum sample */
    3:
    add r1,r0
    mov.w r0,@r4

    /* process right sample */
    mov.w @r3+,r0
    /* shift sample */
    .ifndef CD_SAMPLE_RATE
    shlr2 r0 /* 13 bits */
    shlr2 r0 /* 11 bits */
    shlr r0 /* 10 bits */
    exts.w r0,r0
    .else
    shll2 r0 /* 17 bits */
    shlr8 r0 /* 9 bits */
    exts.w r0,r0
    .endif
    /* clamp sample */
    cmp/ge r5,r0
    bf 7f /* clamp to minimum sample */
    cmp/gt r6,r0
    bt 8f /* clamp to maximum sample */
    4:
    add r1,r0
    mov.w r0,@(2,r4)

    dt r2
    bf/s 2b
    add #4,r4



    Notice that I assume four channels at max volume for the scaling - I was adding eight channels in the mixing, so IF I had more than four voices loud enough to overflow, the scaling won't be enough. But, if you notice, I clamp the samples to 10 bits. A single voice was 13 bits after volume scaling (if you remember), and I scale the output stream by 5 bits. That gives me perfect quality for 8-bit samples... as long as we don't get too many voices at full volume. Then you get a clamped signal.

    This is actually fine as it's pretty rare that ANY channel is at full volume, much less more than four of them. Eight channels at half volume won't cause a sample to clamp. So most of the time, all eight channels mixed will sound just fine. On the rare occasions the stream overflows 10 bits, the clamped sample adds less noise than scaling the samples lower to avoid the clamped sample. Anywho, you get more noise from aliasing from low sample rate instruments than from scaling the samples. I'm not saying this is perfect, but it sounds really good under most conditions, and the conditions under which it might not sound as good naturally masks the noise.

    Think about it - when would you have more than four channels at full volume? When the music is at full volume AND your sound effects are at full volume AND you are using ALL the channels. So tell me, are you going to notice a clamped sample when you have the music playing full blast AND four sound effects going off at the same time at full volume? Hell no! The ear cannot handle that. That's partly how modern audio compression works - loud sounds drown out soft ones, so they discard the soft sounds to make it compress better, and give the loud sound less bit since the ear has trouble handling loud sounds.
    #MEGADRIVEJeroi @ Quakenet irc server.
    Be true to yourself. GFX doesn't matter, the game does. If you are intrested to donate one NTSC Genesis for hardware testing purposes, please pm me.

  4. #4
    Hero of Algol kool kitty89's Avatar
    Join Date
    Mar 2009
    Location
    San Jose, CA
    Age
    30
    Posts
    9,725
    Rep Power
    63

    Default

    Do note that it's only 10 bit resolution up to 22 kHz (specifically, 22,470 Hz NTSC, 22,274 kHz PAL). The PWM DACs operate with a 23.01 MHz base frequency for NTSC (22.8 MHz PAL), and the ouput resolution is divided from that.

    The way the 32x's PWM sound format is set-up allows for variable resolution and sample rate . . . so 23.01 MHz divided by 1024 (number amplitude levels for 10-bit linear sound, ie 2^10) gives 22.47 kHz. Or, 23.01 mHz divided by 22.05 kHz gives 1043 usable output levels. (slightly higher than 10-bit)
    Dropping to 9-bit (512 levels) doubles the peak usable sample rate.

    You can also do something in-between with resolution not strictly tied to binary multiples . . . say you want 32 kHz output, and that would allow (in NTSC) 719 output levels, so significantly more than 9-bit, but also less than 10-bit . . . sort of 9 1/2 bits in that sense.
    Though to work with PAL and NTSC at the same exact sample rate, you'd actually want to limit that to 712 levels.
    6 days older than SEGA Genesis
    -------------
    Quote Originally Posted by evilevoix View Post
    Dude it’s the bios that marries the 16 bit and the 8 bit that makes it 24 bit. If SNK released their double speed bios revision SNK would have had the world’s first 48 bit machine, IDK how you keep ignoring this.
    Quote Originally Posted by evilevoix View Post
    the PCE, that system has no extra silicone for music, how many resources are used to make music and it has less sprites than the MD on screen at once but a larger sprite area?

  5. #5
    Take it apart! WCPO Agent MEGADRIVE Jeroi's Avatar
    Join Date
    Nov 2009
    Location
    Turku, Finland
    Posts
    986
    Rep Power
    15

    Default

    22khz is not problem because Genesis has 16khx high filter anyways so more higher notes gets less volume and so on. Have to remember also that most people cannot hear 22khz tones anymore.
    #MEGADRIVEJeroi @ Quakenet irc server.
    Be true to yourself. GFX doesn't matter, the game does. If you are intrested to donate one NTSC Genesis for hardware testing purposes, please pm me.

  6. #6
    Mastering your Systems Shining Hero TmEE's Avatar
    Join Date
    Oct 2007
    Location
    Estonia, Rapla City
    Age
    30
    Posts
    10,092
    Rep Power
    110

    Default

    22KHz sample rate only gives at most 11KHz frequency range. The closer to nyquist rate the more shit sound gets.
    Death To MP3, :3
    Mida sa loed ? Nagunii aru ei saa "Gnirts test is a shit" New and growing website of total jawusumness !
    If any of my images in my posts no longer work you can find them in "FileDen Dump" on my site ^

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

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