mirror of
https://github.com/holub/mame
synced 2025-07-04 17:38:08 +03:00
sound/rf5c400.cpp: Fixed looping, and implemented position readback. (#7699)
This commit is contained in:
parent
78f6ff34a5
commit
d28aa01821
@ -5,16 +5,6 @@
|
|||||||
|
|
||||||
Written by Ville Linde
|
Written by Ville Linde
|
||||||
Improvements by the hoot development team
|
Improvements by the hoot development team
|
||||||
|
|
||||||
history -
|
|
||||||
2007-02-08 hoot development team
|
|
||||||
looping
|
|
||||||
stereo panning
|
|
||||||
8-bit sample support
|
|
||||||
|
|
||||||
2007-02-16 hoot development team
|
|
||||||
envelope
|
|
||||||
fixed volume table
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
@ -149,15 +139,33 @@ void rf5c400_device::device_start()
|
|||||||
// init channel info
|
// init channel info
|
||||||
for (rf5c400_channel &chan : m_channels)
|
for (rf5c400_channel &chan : m_channels)
|
||||||
{
|
{
|
||||||
|
chan.startH = 0;
|
||||||
|
chan.startL = 0;
|
||||||
|
chan.freq = 0;
|
||||||
|
chan.endL = 0;
|
||||||
|
chan.endHloopH = 0;
|
||||||
|
chan.loopL = 0;
|
||||||
|
chan.pan = 0;
|
||||||
|
chan.effect = 0;
|
||||||
|
chan.volume = 0;
|
||||||
|
chan.attack = 0;
|
||||||
|
chan.decay = 0;
|
||||||
|
chan.release = 0;
|
||||||
|
chan.pos = 0;
|
||||||
|
chan.step = 0;
|
||||||
|
chan.keyon = 0;
|
||||||
chan.env_phase = PHASE_NONE;
|
chan.env_phase = PHASE_NONE;
|
||||||
chan.env_level = 0.0;
|
chan.env_level = 0.0;
|
||||||
chan.env_step = 0.0;
|
chan.env_step = 0.0;
|
||||||
chan.env_scale = 1.0;
|
chan.env_scale = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_req_channel = 0;
|
||||||
|
|
||||||
save_item(NAME(m_rf5c400_status));
|
save_item(NAME(m_rf5c400_status));
|
||||||
save_item(NAME(m_ext_mem_address));
|
save_item(NAME(m_ext_mem_address));
|
||||||
save_item(NAME(m_ext_mem_data));
|
save_item(NAME(m_ext_mem_data));
|
||||||
|
save_item(NAME(m_req_channel));
|
||||||
|
|
||||||
save_item(STRUCT_MEMBER(m_channels, startH));
|
save_item(STRUCT_MEMBER(m_channels, startH));
|
||||||
save_item(STRUCT_MEMBER(m_channels, startL));
|
save_item(STRUCT_MEMBER(m_channels, startL));
|
||||||
@ -201,7 +209,7 @@ void rf5c400_device::device_clock_changed()
|
|||||||
void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||||
{
|
{
|
||||||
int i, ch;
|
int i, ch;
|
||||||
uint64_t end, loop;
|
uint64_t start, end, loop;
|
||||||
uint64_t pos;
|
uint64_t pos;
|
||||||
uint8_t vol, lvol, rvol, type;
|
uint8_t vol, lvol, rvol, type;
|
||||||
uint8_t env_phase;
|
uint8_t env_phase;
|
||||||
@ -216,9 +224,9 @@ void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_
|
|||||||
auto &buf0 = outputs[0];
|
auto &buf0 = outputs[0];
|
||||||
auto &buf1 = outputs[1];
|
auto &buf1 = outputs[1];
|
||||||
|
|
||||||
// start = ((channel->startH & 0xFF00) << 8) | channel->startL;
|
start = ((uint32_t)(channel->startH & 0xFF00) << 8) | channel->startL;
|
||||||
end = ((channel->endHloopH & 0xFF) << 16) | channel->endL;
|
end = ((uint32_t)(channel->endHloopH & 0xFF) << 16) | channel->endL;
|
||||||
loop = ((channel->endHloopH & 0xFF00) << 8) | channel->loopL;
|
loop = ((uint32_t)(channel->endHloopH & 0xFF00) << 8) | channel->loopL;
|
||||||
pos = channel->pos;
|
pos = channel->pos;
|
||||||
vol = channel->volume & 0xFF;
|
vol = channel->volume & 0xFF;
|
||||||
lvol = channel->pan & 0xFF;
|
lvol = channel->pan & 0xFF;
|
||||||
@ -230,6 +238,12 @@ void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_
|
|||||||
env_step = channel->env_step;
|
env_step = channel->env_step;
|
||||||
env_rstep = env_step * channel->env_scale;
|
env_rstep = env_step * channel->env_scale;
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
// This occurs in pop'n music when trying to play a non-existent sample on the sound test menu
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (i=0; i < buf0.samples(); i++)
|
for (i=0; i < buf0.samples(); i++)
|
||||||
{
|
{
|
||||||
int16_t tmp;
|
int16_t tmp;
|
||||||
@ -308,6 +322,15 @@ void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_
|
|||||||
{
|
{
|
||||||
pos -= loop<<16;
|
pos -= loop<<16;
|
||||||
pos &= 0xFFFFFF0000ULL;
|
pos &= 0xFFFFFF0000ULL;
|
||||||
|
|
||||||
|
if (pos < (start<<16))
|
||||||
|
{
|
||||||
|
// This case only shows up in Firebeat games from what I could tell.
|
||||||
|
// The loop value will be higher than the actual buffer size.
|
||||||
|
// This is used when DMAs will be overwriting the current buffer.
|
||||||
|
// It expects the buffer to be looped without any additional commands.
|
||||||
|
pos = start<<16;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -344,6 +367,32 @@ uint16_t rf5c400_device::rf5c400_r(offs_t offset, uint16_t mem_mask)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 0x09: // position read?
|
||||||
|
{
|
||||||
|
// The game will always call rf5c400_w 0x08 with a channel number and some other value before reading this register.
|
||||||
|
// The call to rf5c400_w 0x08 contains additional information, potentially what information it's expecting to be returned here.
|
||||||
|
// This implementation assumes all commands want the same information as command 6.
|
||||||
|
m_stream->update();
|
||||||
|
|
||||||
|
rf5c400_channel* channel = &m_channels[m_req_channel];
|
||||||
|
if (channel->env_phase == PHASE_NONE)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop'n music's SPU program expects to read this register 6 times with the same value between every read before it will send the next DMA request.
|
||||||
|
//
|
||||||
|
// This register is polled while a streaming BGM is being played.
|
||||||
|
// For pop'n music specifically, the game starts off by reading 0x200000 into 0x00780000 - 0x00880000.
|
||||||
|
// When 2xxx is found (pos - start = 0x00080000), it will trigger the next DMA of 0x100000 overwriting 0x00780000 - 0x00800000, and continues polling the register until it reads 1xxx next.
|
||||||
|
// When 1xxx is found (pos - start = 0x00040000), it will trigger the next DMA of 0x100000 overwriting 0x00800000 - 0x00880000, and continues polling the register until it reads 2xxx next.
|
||||||
|
// ... repeat until song is finished, alternating between 2xxx and 1xxx ...
|
||||||
|
// This ends up so that it'll always be buffering new sample data into the sections of memory that aren't being used.
|
||||||
|
auto start = ((uint32_t)(channel->startH & 0xFF00) << 8) | channel->startL;
|
||||||
|
auto ch_offset = (channel->pos >> 16) - start;
|
||||||
|
return ch_offset >> 6;
|
||||||
|
}
|
||||||
|
|
||||||
case 0x13: // memory read
|
case 0x13: // memory read
|
||||||
{
|
{
|
||||||
return read_word(m_ext_mem_address<<1);
|
return read_word(m_ext_mem_address<<1);
|
||||||
@ -391,7 +440,7 @@ void rf5c400_device::rf5c400_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
|||||||
{
|
{
|
||||||
case 0x60:
|
case 0x60:
|
||||||
m_channels[ch].pos =
|
m_channels[ch].pos =
|
||||||
((m_channels[ch].startH & 0xFF00) << 8) | m_channels[ch].startL;
|
((uint32_t)(m_channels[ch].startH & 0xFF00) << 8) | m_channels[ch].startL;
|
||||||
m_channels[ch].pos <<= 16;
|
m_channels[ch].pos <<= 16;
|
||||||
|
|
||||||
m_channels[ch].env_phase = PHASE_ATTACK;
|
m_channels[ch].env_phase = PHASE_ATTACK;
|
||||||
@ -421,8 +470,16 @@ void rf5c400_device::rf5c400_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x08: // relative to env attack (channel no)
|
case 0x08:
|
||||||
case 0x09: // relative to env attack (0x0c00/ 0x1c00)
|
{
|
||||||
|
// There's some other data stuffed in the upper bits beyond the channel: data >> 5
|
||||||
|
// The other data might be some kind of register or command. I've seen 0, 4, 5, and 6.
|
||||||
|
// Firebeat uses 6 when polling rf5c400_r 0x09.
|
||||||
|
m_req_channel = data & 0x1f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x09: // relative to env attack (0x0c00/ 0x1c00/ 0x1e00)
|
||||||
|
|
||||||
case 0x11: // memory r/w address, bits 15 - 0
|
case 0x11: // memory r/w address, bits 15 - 0
|
||||||
{
|
{
|
||||||
|
@ -96,6 +96,7 @@ private:
|
|||||||
uint16_t m_rf5c400_status;
|
uint16_t m_rf5c400_status;
|
||||||
uint32_t m_ext_mem_address;
|
uint32_t m_ext_mem_address;
|
||||||
uint16_t m_ext_mem_data;
|
uint16_t m_ext_mem_data;
|
||||||
|
uint16_t m_req_channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_DEVICE_TYPE(RF5C400, rf5c400_device)
|
DECLARE_DEVICE_TYPE(RF5C400, rf5c400_device)
|
||||||
|
Loading…
Reference in New Issue
Block a user