-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question on the FFI wrapper #1
Comments
Hello. Thank you for your report. This wrapper to ymfm allows write commands to be queued in order to accommodate high sampling YM registers dump files (e.g., in VGM format). libymfm.wasm/src/cpp/ymfmffi.cpp Lines 114 to 135 in 10fb80a
The queued commands are executed in units of the sound chip's sampling rate in the generate function. Therefore, if you want to set a register in one cycle (one generate), you may not get the expected wave. Removing the |
In the next part, registers 0 through 13 are set in order, one tick at a time. while( i < 14) : ( i += 1) {
ymfm.ymfm_write(ym2149, 0, i, dump_b[counter]);
counter += 1;
} I thought that the order in which the registers are set might cause the sound chip to not sound properly. It may be a good practice to write only the changes between sampled registers as a sound log. |
Hello, So I removed the // handle a register write: just queue for now
virtual void write(uint32_t reg, uint8_t data) override
{
// m_queue.push_back(std::make_pair(reg, data));
m_chip.write(reg, data);
} And I commented the Did I do something wrong? For the registers order, I am using YM dump files (http://leonard.oxg.free.fr/ymformat.html) so this is hard to know in which order they have to be written. I tried using another library (emu2149 and the same loop (same way to write the 14 registers) and it works. My test code and YM dumps are here: https://github.com/shazz/ZigYmFm/blob/main/src/main.zig (without those modifications) |
Thank you very much. I understand about the dump file format. If the queue is not used, It may be necessary to include the following address translation process in the libymfm.wasm/src/cpp/ymfmffi.cpp Lines 126 to 144 in 10fb80a
|
Hi, This is also something I tried (even if I don't really understand this address translation for the YM2149): full source: ymfmffi.cpp virtual void write(uint32_t reg, uint8_t data) override
{
// m_queue.push_back(std::make_pair(reg, data));
uint32_t addr1 = 0xffff, addr2 = 0xffff;
uint8_t data1 = 0, data2 = 0;
addr1 = 0 + 2 * ((reg >> 8) & 3);
data1 = reg & 0xff;
addr2 = addr1 + ((m_type == CHIP_YM2149) ? 2 : 1);
data2 = data;
// write to the chip
if (addr1 != 0xffff)
{
m_chip.write(addr1, data1);
m_chip.write(addr2, data2);
}
} The result is even worse, just noise. This is really weird.... could it be an issue with the YM2149 implementation? |
Hello. Sorry for the precious time. Since ymfm is a clock architecture, writing registers at the same time (tick) at the same time may not work well. ymfm operates by emulating the state of each clock. To avoid this, ymfmffi.cpp includes a queue mechanism for write and geneate. (The process here is different from the actual YM clock, as the transmission is in units of the sound chip's native sampling rate.) How about reverting the queue mechanism and keeping the last written register value in an array, and writing only the register that has changed? while( i < 14) : ( i += 1) {
ymfm.ymfm_write(ym2149, 0, i, dump_b[counter]);
counter += 1;
} Similar to the VGM format, it is simulated as a sound log sent from the host to the YM by tick. You may also need to pay attention to the order in which the registers are written. |
Hello, So as you proposed I reverted the queue and now I'm writing the registers only if they have changed from the last frame but the result is not better.
The new loop is: // counters for the YM dump file
var counter: u64 = 0;
var dump_loop: u32 = 0;
var audio_buffer: [31250 / 50]i32 = undefined;
var last_frame: [14]u8 = std.mem.zeroes([14]u8);
// the YM dump file is composed of 50Hz frames capturing the first 14 YM2149 registers
// loop on those frames
while(dump_loop < dump_ym2149_b.len / 14) : (dump_loop += 1) {
// write the 14 registers
var i: u32 = 0;
while( i < 14) : ( i += 1) {
const reg_val:u8 = dump_ym2149_b[counter];
if(reg_val != last_frame[i])
ymfm.ymfm_write(ym2149, 0, i, reg_val);
counter += 1;
last_frame[i] = reg_val;
}
// generate some sound for 50 Hz, so tick the YM (sampling_rate / 50) times
// write every sample output to the file
var tick: u32 = 0;
var buffer: [2]i32 = undefined;
while (tick < sampling_rate / 50) : ( tick += 1) {
ymfm.ymfm_generate(ym2149, 0, &buffer);
// store only left channel
audio_buffer[tick] = buffer[0];
}
try writer.writeAll(std.mem.asBytes(&audio_buffer));
if(dump_loop % 1000 == 0) {
std.debug.print("{} frames done\n", .{ dump_loop } );
}
}
try buf_writer.flush(); https://github.com/shazz/ZigYmFm/blob/main/src/ymfm2149_player.zig#L91 Is it what you have in mind? Would you have time to write a very simple cpp example loading the same ym dump and check to be sure it is not some dumb zig issues? Thanks for your help! |
Oh, something I just noticed in the ffi: else if (m_type == CHIP_YM2149)
{
int32_t out0 = m_output.data[0];
int32_t out1 = m_output.data[1 % ChipType::OUTPUTS];
int32_t out2 = m_output.data[2 % ChipType::OUTPUTS];
*buffer++ += (out0 + out1 + out2) / 2;
*buffer++ += (out0 + out1 + out2) / 2;
} Why do you add the new sample to the previous one ( += ) ? But the fact you are adding the sample to the previous one made me think I did not understand something, can you explain? Is it to mix outputs of different chips ? in addition, why at each clock cycle are there 2 writes to the chip? // write to the chip
if (addr1 != 0xffff)
{
// if (LOG_WRITES)
// printf("%10.5f: %s %03X=%02X\n", double(m_clocks) / double(m_chip.sample_rate(m_clock)), m_name.c_str(), data1, data2);
m_chip.write(addr1, data1);
m_chip.write(addr2, data2);
} and also, this specific translation for ym2149, is it due to the chip itself or the VGM data format? in my case I simply have reg address and reg value. auto front = m_queue.front();
addr1 = 0 + 2 * ((front.first >> 8) & 3);
data1 = front.first & 0xff;
addr2 = addr1 + ((m_type == CHIP_YM2149) ? 2 : 1);
data2 = front.second;
m_queue.erase(m_queue.begin()); |
Ah ok I understood the address translation and the 2 writes, this is not a direct write to the YM2149 register, this is set address then set data. Ok. void ym2149::write(uint32_t offset, uint8_t data)
{
switch (offset & 3) // BC2,BC1
{
case 0: // address
write_address(data);
break;
case 1: // inactive
break;
case 2: // write
write_data(data);
break;
case 3: // address
write_address(data);
break;
}
} So I understood this part but still n ot why the sound is corrupted... |
Hello.
oh.. You are correct in pointing this out. The process was for mixing with other chips. I missed it. Sorry about that...
I too could not find the problem in the source code.. |
Hi Hiromasa,
I'm trying to use your FFI wrapper to simply replay a YM registers dump file (captured every 50Hz) but the result is not good at all. I guess I don't use your wrapper correctly, can you tell me what is wrong ? I guess this is due to the frequency I call
ymfm_generate
but I don't undertstand why.Here is the principle (in Zig but not really different from Rust):
(if not clear in the comments)
ymfm_add_chip
to set up a YM2149 at2000000
Hz and get the sampling rate:31250
14
YM2149 registers contained in my YM dump file14
registers usingymfm_write
31250
/50
ticks withymfm_generate
i32
of the buffer (mono channel) in a file for each tickSampling Rate: 31250 for master clock: 2000000 Dump length: 8833 frames = 176 seconds Tick per frame: 625
Any idea what I did wrong ?
The text was updated successfully, but these errors were encountered: