Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

#639 lua sound.get() frequency and midikey

General
open
nobody
None
5
2014-02-20
2014-01-19
Bavi_H
No

In the patch below, I propose the following changes to lua sound.get():

  1. For each sound channel, frequency and midikey are set appropriately for NTSC or PAL.

  2. For short mode noise, frequency and midikey are set to the pitch (periodic waves per second).

  3. For long mode noise and DPCM, frequency and midikey are set to the rate (level changes per second).

1 Attachments

Discussion

  • Bavi_H
    Bavi_H
    2014-02-20

    More Detailed Description

    In the lua sound.get() function, right now frequency and midikey have the following problems.

    Pulse and triangle frequency

    Problem: For the pulse and triangle channels, the frequency calculations assume the NTSC clock rate. The calculated values are incorrect during PAL emulation.

    current code for pulse channels:

    freq = (39375000.0/352.0) / (curfreq[0] + 1);
    freq = (39375000.0/352.0) / (curfreq[1] + 1);
    

    current code for triangle channel:

    freq = (39375000.0/704.0) / (freqReg + 1);
    

    Proposed solution: In the patch I propose, the calculations use the NTSC or PAL clock rate constant, depending on which machine type is in use.

    proposed code for pulse channels:

    freq = ((PAL?PAL_CPU:NTSC_CPU)/16.0) / (curfreq[0] + 1);
    freq = ((PAL?PAL_CPU:NTSC_CPU)/16.0) / (curfreq[1] + 1);
    

    proposed code for triangle channel:

    freq = ((PAL?PAL_CPU:NTSC_CPU)/32.0) / (freqReg + 1);
    

    References: Nesdev Wiki APU Pulse, APU Triangle

    Noise and DPCM frequency

    Problem: For the noise and DPCM channels, I'm not sure what the currently calculated frequency values are intended to represent. Also, the noise period lookup table currently used assumes the NTSC clock rate.

    current code for noise channel:

    freq = (39375000.0/44.0) / NoiseFreqTable[freqReg]; // probably wrong
    

    current code for DPCM channel:

    freq = (39375000.0/2.0) / DMCPeriod;
    

    Proposed solution: For long mode noise and DPCM, I propose the frequency value should be the rate (level changes per second). I propose calculations that use correct clock constants and lookup tables for NTSC or PAL.

    proposed code for noise channel:

    freq = PAL? PAL_CPU/NoiseFreqTablePAL[freqReg] : NTSC_CPU/NoiseFreqTableNTSC[freqReg] ;  // rate
    

    proposed code for DPCM channel:

    freq = (PAL?PAL_CPU:NTSC_CPU) / DMCPeriod;  // rate
    

    For short mode noise, I propose the frequency value should be the pitch (periodic waves per second).

    proposed code for noise channel:
    use the noise rate calculation above, then if short mode is in use, further calculate the pitch:

    freq /= 93.0;  // pitch
    

    Reasoning: For short mode noise, the 93-step waveform is short enough to be heard as a pitch. Setting frequency to the pitch (periodic waves per second) will end up producing a midikey value that represents the pitch of the sound.

    Long mode noise doesn't have a definite pitch. (There is no audible periodic repetition to the sound wave.) DPCM sounds can possibly have a definite pitch if the sample is constructed to have a wave shape with a periodic repition, but I think it might be too much work for sound.get() to try to tell if that's the case. I think setting frequency to the rate (level changes per second) is a reasonable alternative for these non-pitched or unable-to-determine-pitch sounds.

    midikey

    In the current code and in the proposed patch, midikey is always calculated the same way from frequency.

    code remains the same and unchanged for all channels:

    lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69);
    lua_setfield(L, -2, "midikey");
    

    For long mode noise and DPCM the midikey value shouldn't be interpreted as a correct pitch value. However, since existing scripts may display the midikey value, I thought it might be reasonable to return a "midikey based on the rate" instead of some kind of error value. This way existing scripts will get a changing midikey value and know the frequency of the sound is changing, instead of getting an error value that obscures any frequency changes occurring in the sound.

    DPCM pitch

    If a script author knows a particular DPCM sound is periodic, they can adjust the frequency or midikey to a pitch value themselves:

    frequency = frequency / period
    
    downshift = 12 * (math.log(period)/math.log(2))
    midikey = midikey - downshift
    

    where period is the number of deltas in the repeating periodic waveform pattern.