Nuked-OPN2 icon indicating copy to clipboard operation
Nuked-OPN2 copied to clipboard

No sound - missing something?

Open astwist opened this issue 2 years ago • 7 comments

Hello! We are trying to run Nuked-OPN2 on a teensy MCU, everything appears to run OK but no sound.

We are first calling OPN2_Reset

then executing OPN2_Clock() inside our audio buffer,

We get noise at the output when manually updating mor and mol with random numbers inside of the PN2_ChOutput() function, but no signal is generated by the emulator when we send the test code below (taken from the datasheet):

`ym3438.write(0x22, 0x00); // LFO off ym3438.write(0x27, 0x00); // Note off (channel 0) ym3438.write(0x28, 0x01); // Note off (channel 1) ym3438.write(0x28, 0x02); // Note off (channel 2) ym3438.write(0x28, 0x04); // Note off (channel 3) ym3438.write(0x28, 0x05); // Note off (channel 4) ym3438.write(0x28, 0x06); // Note off (channel 5) ym3438.write(0x2B, 0x00); // DAC off ym3438.write(0x30, 0x71); // ym3438.write(0x34, 0x0D); // ym3438.write(0x38, 0x33); // ym3438.write(0x3C, 0x01); // DT1/MUL ym3438.write(0x40, 0x23); // ym3438.write(0x44, 0x2D); // ym3438.write(0x48, 0x26); // ym3438.write(0x4C, 0x00); // Total level ym3438.write(0x50, 0x5F); // ym3438.write(0x54, 0x99); // ym3438.write(0x58, 0x5F); // ym3438.write(0x5C, 0x94); // RS/AR ym3438.write(0x60, 0x05); // ym3438.write(0x64, 0x05); // ym3438.write(0x68, 0x05); // ym3438.write(0x6C, 0x07); // AM/D1R ym3438.write(0x70, 0x02); // ym3438.write(0x74, 0x02); // ym3438.write(0x78, 0x02); // ym3438.write(0x7C, 0x02); // D2R ym3438.write(0x80, 0x11); // ym3438.write(0x84, 0x11); // ym3438.write(0x88, 0x11); // ym3438.write(0x8C, 0xA6); // D1L/RR ym3438.write(0x90, 0x00); // ym3438.write(0x94, 0x00); // ym3438.write(0x98, 0x00); // ym3438.write(0x9C, 0x00); // Proprietary ym3438.write(0xB0, 0x32); // Feedback/algorithm ym3438.write(0xB4, 0xC0); // Both speakers on ym3438.write(0x28, 0x00); // Key off ym3438.write(0xA4, 0x22); // ym3438.write(0xA0, 0x69); // Set frequency

while(1){ ym3438.write(0x28, 0xC0); //key on delay(1000); ym3438.write(0x28, 0); //key off delay(1000); }`

I have a doubts about the following functions: OPN2_SetTestPin OPN2_SetChipType Must they be called to enable signal generation? Anything else we are missing? Thanks in advance! Cheers Alex

astwist avatar Nov 12 '21 07:11 astwist

Do you call OPN2_Clock between OPN2_Write calls? YM3438 requires some delays between writes to process data correctly and Nuked OPN2 emulates this behaviour.

nukeykt avatar Nov 12 '21 07:11 nukeykt

Thanks for the fast answer!

I tried to add a delay(100); inside the write()-wrapper:

void write(Bit32u port, Bit8u data)
    {   
        OPN2_Write(&chip, port, data);
        delay(100);
    }

But the output of the buffers is again 0.

I have to note that we are currently have no resampler-code added, but I am looking at the signal sum to see if something happens.

dcoredump avatar Nov 12 '21 08:11 dcoredump

Nope, this won't work, The emulator isn't asynchronous, because you do iterate its work by yourself. You should tick it to make it being iterated.,

Wohlstand avatar Nov 12 '21 08:11 Wohlstand

here is an example of how that worked: https://github.com/Wohlstand/libOPNMIDI/blob/master/src/chips/nuked/ym3438.c#L1473-L1506 also, my fork of an emulator (linked here) has simplified calls that do all jobs for you if you want to have the simple processing with register data writing and PCM output after the process. Otherwise, the emulator needs to work with it using almost the same logic as with the hardware chip.

Wohlstand avatar Nov 12 '21 08:11 Wohlstand

Thanks @Wohlstand!

We tried your code (mentioned inside the other thread, which I will close soon) with buffered write and resampler, but this code has problems with the Teensy audio stack when enabling the audio interrupts. It seems that the calculation of the sound eats up all CPU time. This is strange, because I have a running Dexed (with 2 * 16 voices * 6 OPs each voice = 192 OPs + envelopes) running on the same type of microcontroller.

This happens only when trying the resampler code with buffered write. The original code does not have this problem (apart from our write() problems). I think I have to go deeper into the engine to find the solution of those problems.

Do you have any other hints?

dcoredump avatar Nov 12 '21 09:11 dcoredump

Nuked OPN2 emulator is heavy by itself because it implements the chip very accurately. All internals of WriteBuffered and GenerateRsampled implement the logic to deal with the chip. Without these codes, you need to repeat the almost same work as these calls do. Your write() problems resulted in the chip wasn't iterated properly at all, it was just idling, because you didn't call the OPN2_Clock() that executes the chip's processing step. So, how that should be used actually without buffered and resampled calls:

  • OPN2_Clock() - to process one cycle of the chip
  • OPN2_Write() - to write the data

Also, the data writing here is different than you expected: you need to call it multiple times to send register value first, and then, value itself. So, to send one value into one address, you need to call OPN2_Write() twice.

Wohlstand avatar Nov 12 '21 09:11 Wohlstand

Thanks @nukeykt and @Wohlstand!

I have now managed to successfully write data to the registers. Now I have to see if I can get the downsampling from 53267 Hz to 44100 Hz somehow.

Wouldn't it also be possible to tell the library that it should no longer start from a system clock of 7670454 Hz, but from 44100 / 53267 * 7670454 = 6350405 Hz?

dcoredump avatar Nov 15 '21 07:11 dcoredump