summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author 987123879113 <63495610+987123879113@users.noreply.github.com>2021-01-24 14:05:20 +0900
committer GitHub <noreply@github.com>2021-01-24 16:05:20 +1100
commitd28aa01821b749e6128244e1f0b0a0bbab6227c9 (patch)
treedeed040daecaf11051cd09efbc750a0f57b5a237
parent78f6ff34a5d8839a88c8cf36703cf8afc67139a4 (diff)
sound/rf5c400.cpp: Fixed looping, and implemented position readback. (#7699)
-rw-r--r--src/devices/sound/rf5c400.cpp91
-rw-r--r--src/devices/sound/rf5c400.h1
2 files changed, 75 insertions, 17 deletions
diff --git a/src/devices/sound/rf5c400.cpp b/src/devices/sound/rf5c400.cpp
index 7495e484540..9c650275aa1 100644
--- a/src/devices/sound/rf5c400.cpp
+++ b/src/devices/sound/rf5c400.cpp
@@ -5,16 +5,6 @@
Written by Ville Linde
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"
@@ -149,15 +139,33 @@ void rf5c400_device::device_start()
// init channel info
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_level = 0.0;
chan.env_step = 0.0;
chan.env_scale = 1.0;
}
+ m_req_channel = 0;
+
save_item(NAME(m_rf5c400_status));
save_item(NAME(m_ext_mem_address));
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, 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)
{
int i, ch;
- uint64_t end, loop;
+ uint64_t start, end, loop;
uint64_t pos;
uint8_t vol, lvol, rvol, type;
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 &buf1 = outputs[1];
-// start = ((channel->startH & 0xFF00) << 8) | channel->startL;
- end = ((channel->endHloopH & 0xFF) << 16) | channel->endL;
- loop = ((channel->endHloopH & 0xFF00) << 8) | channel->loopL;
+ start = ((uint32_t)(channel->startH & 0xFF00) << 8) | channel->startL;
+ end = ((uint32_t)(channel->endHloopH & 0xFF) << 16) | channel->endL;
+ loop = ((uint32_t)(channel->endHloopH & 0xFF00) << 8) | channel->loopL;
pos = channel->pos;
vol = channel->volume & 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_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++)
{
int16_t tmp;
@@ -308,6 +322,15 @@ void rf5c400_device::sound_stream_update(sound_stream &stream, std::vector<read_
{
pos -= loop<<16;
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;
}
+ 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
{
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:
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].env_phase = PHASE_ATTACK;
@@ -421,8 +470,16 @@ void rf5c400_device::rf5c400_w(offs_t offset, uint16_t data, uint16_t mem_mask)
break;
}
- case 0x08: // relative to env attack (channel no)
- case 0x09: // relative to env attack (0x0c00/ 0x1c00)
+ case 0x08:
+ {
+ // 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
{
diff --git a/src/devices/sound/rf5c400.h b/src/devices/sound/rf5c400.h
index 6e729daad0a..8f54d2bc8b3 100644
--- a/src/devices/sound/rf5c400.h
+++ b/src/devices/sound/rf5c400.h
@@ -96,6 +96,7 @@ private:
uint16_t m_rf5c400_status;
uint32_t m_ext_mem_address;
uint16_t m_ext_mem_data;
+ uint16_t m_req_channel;
};
DECLARE_DEVICE_TYPE(RF5C400, rf5c400_device)