mirror of
https://github.com/libretro/same_cdi.git
synced 2026-11-07 12:37:58 -10:00
Emulate the Digital Video Cartridge (VMPEG)
Add the 22ER9141 Digital Video Cartridge as fitted to the Mono-I, and as built into the CD-i 490: an MCD251 video decoder and a DSP56001 doing MPEG-1 audio in software, driven by the fmvdrv/madriv modules in the cartridge ROM. Titles that need it for full motion video, such as The Firm, Mutant Rampage and Monty Python's Invasion from the Planet Skyron, now play. The register file, interrupt behaviour, program stream demultiplexer and DMA handshake are ported from the CDi_MiSTer VMPEG core by Andre Zeps, which reverse-engineered the cartridge. CDi_MiSTer is GPL-3.0, so cdidvc.cpp/.h and mpeg1demux.cpp/.h carry that license and the README says so. The MPEG-1 video and Layer I/II audio decoders are new code under BSD-3-Clause; their tables follow pl_mpeg, whose MIT notice is reproduced in both. The cartridge is fitted to cdimono1 unconditionally, but its driver ROM, vmpega.rom, is optional. Without it the boot code's cartridge probe finds nothing and the machine behaves exactly as before, so existing BIOS sets keep working. With it, the cartridge decodes d00000-efffff: 1MB of extra system RAM, the register file, the ROM, and 512KB of MPEG working RAM that bus-errors until the driver unlocks it, so that the OS-9 memory crawler does not claim it. IRQ level 4 is shared between the CDIC and the cartridge. A flip-flop on the board gives the line to whichever device raised it first and routes the interrupt acknowledge to it; the driver now arbitrates the same way instead of wiring the CDIC straight to the CPU. The picture reaches the screen through the MCD212's External Video backdrop. The cartridge's display window keys the CD-i planes away where region flags select it, so a title that frames its video with a border drawn in a plane keeps the border. Outside the window, or with no cartridge, the backdrop is the CD-i's 16-level black rather than 0. The software list no longer filters out DVC titles. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
05c6f47837
commit
ec7c838e5a
14 files changed
+5966
-16
No files matched your search
@@ -90,7 +90,11 @@ OBJECTS += $(MESS_OBJS)
|
||||
MAME_MESS_SRC_CXX := \
|
||||
$(CORE_DIR)/src/mame/drivers/cdi.cpp \
|
||||
$(CORE_DIR)/src/mame/machine/cdicdic.cpp \
|
||||
$(CORE_DIR)/src/mame/machine/cdidvc.cpp \
|
||||
$(CORE_DIR)/src/mame/machine/cdislavehle.cpp \
|
||||
$(CORE_DIR)/src/mame/machine/mpeg1audio.cpp \
|
||||
$(CORE_DIR)/src/mame/machine/mpeg1demux.cpp \
|
||||
$(CORE_DIR)/src/mame/machine/mpeg1video.cpp \
|
||||
$(CORE_DIR)/src/mame/video/mcd212.cpp
|
||||
|
||||
MAME_MESS_OBJS := $(patsubst $(CORE_DIR)/%,$(CORE_OBJ_DIR)/%,$(MAME_MESS_SRC_CXX:.cpp=.o))
|
||||
|
||||
@@ -11,6 +11,19 @@ Game files can be either CHD, ISO, or BIN/CUE. Game support/functionality is wh
|
||||
=======
|
||||
BIOS file (cdimono1.zip) is required, and can go either in the same directory as your ```CHD|ISO|CUE``` files or in the ```retroarch_system_dir/same_cdi/bios/``` directory
|
||||
|
||||
*DIGITAL VIDEO CARTRIDGE*
|
||||
=======
|
||||
Titles that need the Digital Video Cartridge for MPEG-1 full motion video,
|
||||
such as *The Firm*, *Mutant Rampage: Bodyslam* and *Monty Python's Invasion
|
||||
from the Planet Skyron*, also need the cartridge's driver ROM. Add
|
||||
`vmpega.rom` (262144 bytes, CRC32 `db264e8b`) to `cdimono1.zip`; the same
|
||||
file is part of `cdi490a.zip`, since the CD-i 490 has the hardware built in.
|
||||
Without it the core runs as a plain Mono-I with no cartridge fitted, exactly
|
||||
as before.
|
||||
|
||||
The cartridge is emulated from the reverse engineering done by the
|
||||
[CDi_MiSTer](https://github.com/MiSTer-devel/CDi_MiSTer) project.
|
||||
|
||||
--------
|
||||
|
||||
# **Libretro notice** #
|
||||
@@ -36,6 +49,15 @@ including core files) are made available under the terms of the
|
||||
would encourage new contributors to make their contributions available under the
|
||||
terms of this license.
|
||||
|
||||
The Digital Video Cartridge emulation (`src/mame/machine/cdidvc.*` and
|
||||
`src/mame/machine/mpeg1demux.*`) is a port of the
|
||||
[CDi_MiSTer](https://github.com/MiSTer-devel/CDi_MiSTer) VMPEG core and is
|
||||
therefore made available under the terms of the
|
||||
[GNU General Public License, version 3](https://opensource.org/licenses/GPL-3.0)
|
||||
(GPL-3.0), like the project it was ported from. Every other license in the
|
||||
tree is compatible with it, and binaries of this core that include those files
|
||||
are distributed under GPL-3.0.
|
||||
|
||||
Please note that MAME is a registered trademark of Gregory Ember, and permission
|
||||
is required to use the "MAME" name, logo, or wordmark.
|
||||
|
||||
|
||||
+191
-5
@@ -99,6 +99,35 @@ void cdi_state::cdimono1_mem(address_map &map)
|
||||
map(0xe80000, 0xefffff).ram(); // DVC RAM block 2
|
||||
}
|
||||
|
||||
// As above, but with a Digital Video Cartridge fitted. The cartridge decodes
|
||||
// its own quarter of the upper address space:
|
||||
//
|
||||
// d00000-dfffff 1MB of extra system RAM contributed by the cartridge
|
||||
// e00000-e3ffff VMPEG register file (address bits 15..1 only, so it mirrors)
|
||||
// e40000-e7ffff 128KB driver ROM, mirrored twice
|
||||
// e80000-efffff 512KB MPEG working RAM, bus-errors until the driver unlocks it
|
||||
void cdi_state::cdimono1_dvc_mem(address_map &map)
|
||||
{
|
||||
map(0x000000, 0xffffff).rw(FUNC(cdi_state::bus_error_r), FUNC(cdi_state::bus_error_w));
|
||||
map(0x000000, 0x07ffff).rw(FUNC(cdi_state::plane_r<0>), FUNC(cdi_state::plane_w<0>)).share("plane0");
|
||||
map(0x200000, 0x27ffff).rw(FUNC(cdi_state::plane_r<1>), FUNC(cdi_state::plane_w<1>)).share("plane1");
|
||||
map(0x300000, 0x303bff).rw(m_cdic, FUNC(cdicdic_device::ram_r), FUNC(cdicdic_device::ram_w));
|
||||
#if ENABLE_UART_PRINTING
|
||||
map(0x301400, 0x301403).r(m_maincpu, FUNC(scc68070_device::uart_loopback_enable));
|
||||
#endif
|
||||
map(0x303c00, 0x303fff).rw(m_cdic, FUNC(cdicdic_device::regs_r), FUNC(cdicdic_device::regs_w));
|
||||
map(0x310000, 0x317fff).rw(m_slave_hle, FUNC(cdislave_hle_device::slave_r), FUNC(cdislave_hle_device::slave_w));
|
||||
map(0x318000, 0x31ffff).noprw();
|
||||
map(0x320000, 0x323fff).rw("mk48t08", FUNC(timekeeper_device::read), FUNC(timekeeper_device::write)).umask16(0xff00);
|
||||
map(0x400000, 0x47ffff).r(FUNC(cdi_state::main_rom_r));
|
||||
map(0x4fffe0, 0x4fffff).m(m_mcd212, FUNC(mcd212_device::map));
|
||||
map(0x500000, 0x57ffff).ram();
|
||||
map(0xd00000, 0xdfffff).ram();
|
||||
map(0xe00000, 0xe3ffff).rw(m_dvc, FUNC(cdidvc_device::regs_r), FUNC(cdidvc_device::regs_w));
|
||||
map(0xe40000, 0xe7ffff).r(FUNC(cdi_state::dvc_rom_r));
|
||||
map(0xe80000, 0xefffff).rw(FUNC(cdi_state::dvc_mpeg_ram_r), FUNC(cdi_state::dvc_mpeg_ram_w));
|
||||
}
|
||||
|
||||
void cdi_state::cdimono2_mem(address_map &map)
|
||||
{
|
||||
map(0x000000, 0x07ffff).ram().share("plane0");
|
||||
@@ -209,6 +238,129 @@ void cdi_state::dvc_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
||||
{
|
||||
}
|
||||
|
||||
// The cartridge ROM is a 128KB device that appears twice across the 256KB
|
||||
// window. It is stored in 68000 byte order, so assemble words explicitly
|
||||
// rather than relying on host endianness.
|
||||
uint16_t cdi_state::dvc_rom_r(offs_t offset)
|
||||
{
|
||||
if (!m_dvc_rom.found())
|
||||
return 0;
|
||||
|
||||
const offs_t addr = (offset & 0xffff) * 2;
|
||||
const uint16_t data = (uint16_t(m_dvc_rom[addr]) << 8) | m_dvc_rom[addr + 1];
|
||||
|
||||
LOGMASKED(LOG_DVC, "%s: DVC ROM read %06x = %04x\n", machine().describe_context(), 0xe40000 + addr, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// The MPEG working RAM is invisible until the driver has unlocked it, so that
|
||||
// the OS-9 memory crawler does not claim it as general-purpose system RAM.
|
||||
uint16_t cdi_state::dvc_mpeg_ram_r(offs_t offset, uint16_t mem_mask)
|
||||
{
|
||||
if (!m_dvc->mpeg_ram_enabled())
|
||||
return bus_error_r(offset + 0xe80000 / 2);
|
||||
|
||||
return m_dvc->mpeg_ram_r(offset, mem_mask);
|
||||
}
|
||||
|
||||
void cdi_state::dvc_mpeg_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
|
||||
{
|
||||
if (!m_dvc->mpeg_ram_enabled())
|
||||
{
|
||||
bus_error_w(offset + 0xe80000 / 2, data);
|
||||
return;
|
||||
}
|
||||
|
||||
m_dvc->mpeg_ram_w(offset, data, mem_mask);
|
||||
}
|
||||
|
||||
uint32_t cdi_state::dvc_video_r(int x, int y)
|
||||
{
|
||||
// The cartridge places its picture in the CD-i's normal 384-pixel-wide
|
||||
// display space, which the driver confirms by programming an active area
|
||||
// of 384x280. The MCD212 renders 768 pixels across a line, so every
|
||||
// cartridge pixel covers two of them.
|
||||
if (!m_dvc.found() || !m_dvc->window_covers(x / 2, y))
|
||||
return 0;
|
||||
|
||||
// Inside the window the cartridge owns the pixel even when it has no
|
||||
// picture to show - between clips, or once playback has been stopped or
|
||||
// skipped - and what it puts there is black. Reporting the window as
|
||||
// unoccupied instead would hand the pixel back to the CD-i plane, and the
|
||||
// artwork the title hides behind the window would show through.
|
||||
//
|
||||
// That black is the CD-i's black, not zero: the cartridge's picture is
|
||||
// carried in the same 16-235 swing as everything else the MCD212 emits (see
|
||||
// display_frame_rgb()), so an empty window has to sit at the same level as
|
||||
// the surrounding planes or it reads as a darker rectangle.
|
||||
uint32_t pixel = mcd212_device::CDI_BLACK;
|
||||
if (!m_dvc->get_pixel(x / 2, y, pixel))
|
||||
pixel = mcd212_device::CDI_BLACK;
|
||||
|
||||
return 0xff000000 | pixel;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* IRQ level 4 arbitration *
|
||||
**************************************/
|
||||
|
||||
void cdi_state::cdic_intreq_w(int state)
|
||||
{
|
||||
m_cdic_intreq = bool(state);
|
||||
update_irq4();
|
||||
}
|
||||
|
||||
void cdi_state::dvc_intreq_w(int state)
|
||||
{
|
||||
m_dvc_intreq = bool(state);
|
||||
update_irq4();
|
||||
}
|
||||
|
||||
void cdi_state::update_irq4()
|
||||
{
|
||||
// Mirrors the 74ACT74 flip-flop on the board: whichever device grabs the
|
||||
// line keeps it until it stops requesting. If both are idle and both
|
||||
// request at once, the cartridge wins.
|
||||
switch (m_irq4_owner)
|
||||
{
|
||||
case IRQ4_CDIC:
|
||||
if (!m_cdic_intreq)
|
||||
m_irq4_owner = IRQ4_IDLE;
|
||||
break;
|
||||
case IRQ4_DVC:
|
||||
if (!m_dvc_intreq)
|
||||
m_irq4_owner = IRQ4_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_irq4_owner == IRQ4_IDLE)
|
||||
{
|
||||
if (m_cdic_intreq)
|
||||
m_irq4_owner = IRQ4_CDIC;
|
||||
if (m_dvc_intreq)
|
||||
m_irq4_owner = IRQ4_DVC;
|
||||
}
|
||||
|
||||
const bool line = (m_irq4_owner == IRQ4_CDIC && m_cdic_intreq)
|
||||
|| (m_irq4_owner == IRQ4_DVC && m_dvc_intreq);
|
||||
|
||||
m_maincpu->in4_w(line ? 1 : 0);
|
||||
}
|
||||
|
||||
uint8_t cdi_state::iack4_r()
|
||||
{
|
||||
if (m_irq4_owner == IRQ4_DVC && m_dvc.found())
|
||||
return m_dvc->intack_r();
|
||||
|
||||
if (m_cdic.found())
|
||||
return m_cdic->intack_r();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* LCD screen *
|
||||
*************************/
|
||||
@@ -278,7 +430,7 @@ void cdi_state::cdimono1_base(machine_config &config)
|
||||
{
|
||||
SCC68070(config, m_maincpu, CLOCK_A);
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &cdi_state::cdimono1_mem);
|
||||
m_maincpu->iack4_callback().set(m_cdic, FUNC(cdicdic_device::intack_r));
|
||||
m_maincpu->iack4_callback().set(FUNC(cdi_state::iack4_r));
|
||||
|
||||
MCD212(config, m_mcd212, CLOCK_A, m_plane_ram[0], m_plane_ram[1]);
|
||||
m_mcd212->set_screen("screen");
|
||||
@@ -304,7 +456,7 @@ void cdi_state::cdimono1_base(machine_config &config)
|
||||
// DSP input clock is 7.5264 MHz
|
||||
CDI_CDIC(config, m_cdic, 45.1584_MHz_XTAL / 2);
|
||||
m_cdic->set_clock2(45.1584_MHz_XTAL * 3 / 7); // generated by PLL circuit incorporating 19.3575 MHz XTAL
|
||||
m_cdic->intreq_callback().set(m_maincpu, FUNC(scc68070_device::in4_w));
|
||||
m_cdic->intreq_callback().set(FUNC(cdi_state::cdic_intreq_w));
|
||||
|
||||
CDI_SLAVE_HLE(config, m_slave_hle, 0);
|
||||
m_slave_hle->int_callback().set(m_maincpu, FUNC(scc68070_device::in2_w));
|
||||
@@ -413,12 +565,35 @@ void cdi_state::cdi910(machine_config &config)
|
||||
}
|
||||
|
||||
// CD-i Mono-I, with CD-ROM image device (MESS) and Software List (MESS)
|
||||
//
|
||||
// The Digital Video Cartridge is fitted unconditionally, but its driver ROM is
|
||||
// optional. Without the ROM the cartridge probe performed by the player's
|
||||
// boot code finds nothing and the machine behaves exactly like a bare Mono-I,
|
||||
// so existing BIOS sets keep working untouched.
|
||||
void cdi_state::cdimono1(machine_config &config)
|
||||
{
|
||||
cdimono1_base(config);
|
||||
|
||||
cdimono1_dvc(config);
|
||||
|
||||
CDROM(config, "cdrom").set_interface("cdi_cdrom");
|
||||
SOFTWARE_LIST(config, "cd_list").set_original("cdi").set_filter("!DVC");
|
||||
SOFTWARE_LIST(config, "cd_list").set_original("cdi");
|
||||
}
|
||||
|
||||
// Digital Video Cartridge hookup, shared by the Mono-I and the CD-i 490
|
||||
void cdi_state::cdimono1_dvc(machine_config &config)
|
||||
{
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &cdi_state::cdimono1_dvc_mem);
|
||||
|
||||
CDI_DVC(config, m_dvc, 0);
|
||||
// The cartridge needs the screen so that it can swap its picture over
|
||||
// between fields rather than in the middle of one.
|
||||
m_dvc->set_screen("screen");
|
||||
m_dvc->intreq_callback().set(FUNC(cdi_state::dvc_intreq_w));
|
||||
m_dvc->add_route(0, "lspeaker", 1.0);
|
||||
m_dvc->add_route(1, "rspeaker", 1.0);
|
||||
|
||||
m_mcd212->set_ext_video_callback(FUNC(cdi_state::dvc_video_r));
|
||||
}
|
||||
|
||||
/*************************
|
||||
@@ -440,6 +615,12 @@ ROM_START( cdimono1 )
|
||||
|
||||
ROM_REGION(0x2000, "slave", 0)
|
||||
ROM_LOAD( "zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206", 0x0000, 0x2000, CRC(688cda63) SHA1(56d0acd7caad51c7de703247cd6d842b36173079) BAD_DUMP )
|
||||
|
||||
// Digital Video Cartridge driver ROM. Optional: without it the machine
|
||||
// simply comes up as a Mono-I with no cartridge attached. The dump is a
|
||||
// 128KB device recorded twice; either half is the whole ROM.
|
||||
ROM_REGION(0x40000, "dvc", ROMREGION_ERASE00)
|
||||
ROM_LOAD_OPTIONAL( "vmpega.rom", 0x000000, 0x40000, CRC(db264e8b) SHA1(be407fbc102f1731a0862554855e963e5a47c17b) )
|
||||
ROM_END
|
||||
|
||||
ROM_START( cdi910 )
|
||||
@@ -476,9 +657,14 @@ ROM_START( cdi490a )
|
||||
ROM_SYSTEM_BIOS( 0, "cdi490", "CD-i 490" )
|
||||
ROMX_LOAD( "cdi490a.rom", 0x000000, 0x80000, CRC(e2f200f6) SHA1(c9bf3c4c7e4fe5cbec3fe3fc993c77a4522ca547), ROM_BIOS(0) | ROM_GROUPWORD | ROM_REVERSE )
|
||||
|
||||
ROM_REGION(0x40000, "mpegs", 0) // keep these somewhere
|
||||
ROM_LOAD( "impega.rom", 0x0000, 0x40000, CRC(84d6f6aa) SHA1(02526482a0851ea2a7b582d8afaa8ef14a8bd914) )
|
||||
// The 490 has the digital video hardware built in rather than on a
|
||||
// cartridge. Only the VMPEG driver ROM is wired up; the IMPEG one is kept
|
||||
// here because the set contains it, but that chipset is not emulated.
|
||||
ROM_REGION(0x40000, "dvc", ROMREGION_ERASE00)
|
||||
ROM_LOAD( "vmpega.rom", 0x0000, 0x40000, CRC(db264e8b) SHA1(be407fbc102f1731a0862554855e963e5a47c17b) )
|
||||
|
||||
ROM_REGION(0x40000, "impeg", 0)
|
||||
ROM_LOAD( "impega.rom", 0x0000, 0x40000, CRC(84d6f6aa) SHA1(02526482a0851ea2a7b582d8afaa8ef14a8bd914) )
|
||||
ROM_END
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "machine/scc68070.h"
|
||||
#include "machine/cdislavehle.h"
|
||||
#include "machine/cdicdic.h"
|
||||
#include "machine/cdidvc.h"
|
||||
#include "sound/dmadac.h"
|
||||
#include "video/mcd212.h"
|
||||
#include "cpu/m6805/m68hc05.h"
|
||||
@@ -28,6 +29,8 @@ public:
|
||||
, m_servo(*this, "servo")
|
||||
, m_slave(*this, "slave")
|
||||
, m_cdic(*this, "cdic")
|
||||
, m_dvc(*this, "dvc")
|
||||
, m_dvc_rom(*this, "dvc")
|
||||
, m_cdrom(*this, "cdrom")
|
||||
, m_mcd212(*this, "mcd212")
|
||||
, m_dmadac(*this, "dac%u", 1U)
|
||||
@@ -35,6 +38,7 @@ public:
|
||||
|
||||
void cdimono1_base(machine_config &config);
|
||||
void cdimono1(machine_config &config);
|
||||
void cdimono1_dvc(machine_config &config);
|
||||
void cdimono2(machine_config &config);
|
||||
void cdi910(machine_config &config);
|
||||
|
||||
@@ -54,6 +58,8 @@ protected:
|
||||
optional_device<m68hc05c8_device> m_servo;
|
||||
optional_device<m68hc05c8_device> m_slave;
|
||||
optional_device<cdicdic_device> m_cdic;
|
||||
optional_device<cdidvc_device> m_dvc;
|
||||
optional_region_ptr<uint8_t> m_dvc_rom;
|
||||
required_device<cdrom_image_device> m_cdrom;
|
||||
required_device<mcd212_device> m_mcd212;
|
||||
|
||||
@@ -63,6 +69,7 @@ protected:
|
||||
virtual void machine_reset() override ATTR_COLD;
|
||||
|
||||
void cdimono1_mem(address_map &map) ATTR_COLD;
|
||||
void cdimono1_dvc_mem(address_map &map) ATTR_COLD;
|
||||
|
||||
void cdi910_mem(address_map &map) ATTR_COLD;
|
||||
void cdimono2_mem(address_map &map) ATTR_COLD;
|
||||
@@ -76,6 +83,31 @@ protected:
|
||||
uint16_t dvc_r(offs_t offset, uint16_t mem_mask = ~0);
|
||||
void dvc_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
|
||||
uint16_t dvc_rom_r(offs_t offset);
|
||||
uint16_t dvc_mpeg_ram_r(offs_t offset, uint16_t mem_mask = ~0);
|
||||
void dvc_mpeg_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
|
||||
uint32_t dvc_video_r(int x, int y);
|
||||
|
||||
// IRQ level 4 is shared between the CDIC and the DVC; a flip-flop on the
|
||||
// board decides which of them owns the line and therefore which one the
|
||||
// CPU's interrupt acknowledge is routed to.
|
||||
enum irq4_owner : uint8_t
|
||||
{
|
||||
IRQ4_IDLE,
|
||||
IRQ4_CDIC,
|
||||
IRQ4_DVC
|
||||
};
|
||||
|
||||
void cdic_intreq_w(int state);
|
||||
void dvc_intreq_w(int state);
|
||||
void update_irq4();
|
||||
uint8_t iack4_r();
|
||||
|
||||
uint8_t m_irq4_owner = IRQ4_IDLE;
|
||||
bool m_cdic_intreq = false;
|
||||
bool m_dvc_intreq = false;
|
||||
|
||||
uint16_t bus_error_r(offs_t offset);
|
||||
void bus_error_w(offs_t offset, uint16_t data);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,401 @@
|
||||
// license:GPL-3.0-only
|
||||
// copyright-holders:Andre Zeps, vibecodekun
|
||||
/******************************************************************************
|
||||
|
||||
Philips CD-i Digital Video Cartridge (VMPEG)
|
||||
--------------------------------------------
|
||||
|
||||
The DVC is an expansion cartridge that adds MPEG-1 audio and video decoding
|
||||
to a CD-i player. Several chipsets exist; this device models the VMPEG
|
||||
variant (as fitted to the 22ER9141 cartridge and built into the CD-i 490),
|
||||
which pairs an MCD251 video decoder with a DSP56001 doing audio in software.
|
||||
|
||||
The register-level behaviour modelled here was derived from the
|
||||
documentation and RTL of the CDi_MiSTer project by Andre Zeps, which
|
||||
reverse-engineered the cartridge in detail:
|
||||
|
||||
https://github.com/MiSTer-devel/CDi_MiSTer (doc/dvc.md, rtl/vmpeg.sv)
|
||||
|
||||
CDi_MiSTer is licensed under the GNU General Public License version 3,
|
||||
and this device is a port of it, so cdidvc.cpp/.h and mpeg1demux.cpp/.h
|
||||
are distributed under the same license.
|
||||
|
||||
Address decode (as seen from the 68070):
|
||||
|
||||
d00000-dfffff 1MB of extra system RAM, always visible
|
||||
e00000-e3ffff VMPEG registers (only address bits 15..1 are decoded,
|
||||
so the register file mirrors every 64KB)
|
||||
e40000-e7ffff 128KB driver ROM (fmvdrv/madriv OS-9 modules), mirrored
|
||||
e80000-efffff 512KB MPEG working RAM, hidden until unlocked
|
||||
|
||||
The MPEG RAM is deliberately not visible after reset. The OS-9 memory
|
||||
crawler would otherwise find it and hand it out as general system RAM,
|
||||
which breaks the decoder. Hardware gates it behind a counter that unlocks
|
||||
once the driver has performed 64 register writes.
|
||||
|
||||
TODO:
|
||||
|
||||
- Picture display is paced off the sequence's own frame rate rather than
|
||||
the decoding time stamps, so a stream whose audio and video drift apart
|
||||
will not be resynchronised.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_CDIDVC_H
|
||||
#define MAME_MACHINE_CDIDVC_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "machine/scc68070.h"
|
||||
#include "machine/mpeg1demux.h"
|
||||
#include "machine/mpeg1video.h"
|
||||
#include "machine/mpeg1audio.h"
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
class cdidvc_device : public device_t, public device_sound_interface, public device_video_interface
|
||||
{
|
||||
public:
|
||||
cdidvc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
auto intreq_callback() { return m_intreq_callback.bind(); }
|
||||
|
||||
// e00000-e3ffff register window
|
||||
uint16_t regs_r(offs_t offset, uint16_t mem_mask = ~0);
|
||||
void regs_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
|
||||
// e80000-efffff MPEG working RAM
|
||||
uint16_t mpeg_ram_r(offs_t offset, uint16_t mem_mask = ~0);
|
||||
void mpeg_ram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
|
||||
bool mpeg_ram_enabled() const { return m_mpeg_ram_enabled; }
|
||||
|
||||
uint8_t intack_r();
|
||||
bool intreq() const { return m_intreq; }
|
||||
|
||||
void set_pal(bool pal) { m_pal = pal; }
|
||||
|
||||
// Video output to the MCD212 external-video (backdrop) path.
|
||||
//
|
||||
// window_covers() reports whether the cartridge's display window is open
|
||||
// over a pixel, which is a separate question from whether there is a
|
||||
// picture to put there: the window stays open across the gap between clips,
|
||||
// and the CD-i plane behind it stays keyed away, so what shows is black
|
||||
// rather than the plane. get_pixel() returns false when there is nothing
|
||||
// to display, and the caller should then fall back to black.
|
||||
//
|
||||
// The window is the decoded picture, taken from the DECOFF corner; DECWIN
|
||||
// does not narrow it, because fmvdrv leaves it holding the *previous*
|
||||
// clip's size. vblank_update() has the detail.
|
||||
bool video_enabled() const;
|
||||
bool window_covers(int x, int y) const;
|
||||
bool get_pixel(int x, int y, uint32_t &result) const;
|
||||
|
||||
protected:
|
||||
virtual void device_resolve_objects() override;
|
||||
virtual void device_start() override ATTR_COLD;
|
||||
virtual void device_reset() override ATTR_COLD;
|
||||
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||
|
||||
virtual void device_pre_save() override;
|
||||
virtual void device_post_load() override;
|
||||
|
||||
TIMER_CALLBACK_MEMBER(dclk_tick);
|
||||
|
||||
private:
|
||||
void vblank_update(screen_device &screen, bool state);
|
||||
|
||||
// FMV interrupt status/enable bits. Bit numbering follows the packed
|
||||
// struct in the MiSTer RTL, where the first-declared field is the MSB.
|
||||
enum : uint16_t
|
||||
{
|
||||
FMV_INT_SEQ = 1 << 0, // sequence header decoded
|
||||
FMV_INT_GOP = 1 << 1, // group-of-pictures decoded
|
||||
FMV_INT_PIC = 1 << 2, // picture started display
|
||||
FMV_INT_EOD = 1 << 3, // end of data
|
||||
FMV_INT_RFB = 1 << 4, // request for bits
|
||||
FMV_INT_NDAT = 1 << 5, // no data / underflow
|
||||
FMV_INT_OVF = 1 << 6, // overflow
|
||||
FMV_INT_DCL = 1 << 7,
|
||||
FMV_INT_TIM = 1 << 8, // timer
|
||||
FMV_INT_ESI = 1 << 9, // end sequence indicator
|
||||
FMV_INT_EII = 1 << 10, // end ISO indicator
|
||||
FMV_INT_VSYNC = 1 << 11,
|
||||
FMV_INT_PAI = 1 << 12, // pause
|
||||
FMV_INT_VCUP = 1 << 13, // video clip update
|
||||
FMV_INT_ERDD = 1 << 14,
|
||||
FMV_INT_ERDV = 1 << 15
|
||||
};
|
||||
|
||||
// FMA interrupt status/enable bits
|
||||
enum : uint16_t
|
||||
{
|
||||
FMA_INT_EOI = 1 << 0, // ISO end detected
|
||||
FMA_INT_CSU = 1 << 1, // audio stream changed
|
||||
FMA_INT_UPD = 1 << 2, // frame header updated
|
||||
FMA_INT_UNF = 1 << 3, // underflow
|
||||
FMA_INT_DEC = 1 << 4, // decoding started
|
||||
FMA_INT_ERR = 1 << 5,
|
||||
FMA_INT_POLL = 1 << 8
|
||||
};
|
||||
|
||||
// FMV SYSCMD (e040c0) command bits
|
||||
enum : uint16_t
|
||||
{
|
||||
SYSCMD_PLAY = 1 << 3,
|
||||
SYSCMD_PAUSE = 1 << 4,
|
||||
SYSCMD_CONTINUE = 1 << 5,
|
||||
SYSCMD_STEP = 1 << 6,
|
||||
SYSCMD_STOP = 1 << 7,
|
||||
SYSCMD_CLEAR_FIFO = 1 << 8,
|
||||
SYSCMD_SEARCH_GOP = 1 << 10,
|
||||
SYSCMD_DEC_ON = 1 << 12,
|
||||
SYSCMD_DEC_OFF = 1 << 13,
|
||||
SYSCMD_DMA = 1 << 15
|
||||
};
|
||||
|
||||
void update_interrupts();
|
||||
uint32_t audio_ring_used() const;
|
||||
uint32_t audio_ring_free() const;
|
||||
void request_dma(bool for_fma);
|
||||
void try_dma();
|
||||
void feed_byte(uint8_t data, bool for_fma);
|
||||
bool advance_video();
|
||||
void advance_audio();
|
||||
void write_syscmd(uint16_t data);
|
||||
void write_vidcmd(uint16_t data);
|
||||
void write_fma_cmd(uint16_t data);
|
||||
|
||||
devcb_write_line m_intreq_callback;
|
||||
|
||||
required_address_space m_memory_space;
|
||||
required_device<scc68070_device> m_scc;
|
||||
|
||||
emu_timer *m_dclk_timer = nullptr;
|
||||
|
||||
bool m_pal = true;
|
||||
|
||||
// ---- FMA (audio) registers ----
|
||||
uint16_t m_fma_command = 0;
|
||||
uint8_t m_fma_status = 0;
|
||||
uint16_t m_fma_isr = 0;
|
||||
uint16_t m_fma_ier = 0;
|
||||
uint16_t m_fma_ivec = 0;
|
||||
uint32_t m_fma_dclk = 0;
|
||||
uint16_t m_fma_dclkl_latch = 0;
|
||||
uint8_t m_fma_stream = 0;
|
||||
uint8_t m_fma_dspa = 0;
|
||||
bool m_fma_dsp_enable = false;
|
||||
bool m_fma_stream_change_pending = false;
|
||||
bool m_fma_status_decoding = false;
|
||||
// The audio subsystem starts decoding when the stream's own clock reaches
|
||||
// the first presentation time stamp, rather than on an explicit command.
|
||||
int64_t m_fma_playback_start_dclk = 0;
|
||||
bool m_fma_playback_start_valid = false;
|
||||
uint32_t m_fma_audio_header = 0;
|
||||
|
||||
// ---- FMV (video) registers ----
|
||||
uint16_t m_fmv_isr = 0;
|
||||
uint16_t m_fmv_ier = 0;
|
||||
uint16_t m_fmv_ivec = 0;
|
||||
uint32_t m_fmv_dclk = 0;
|
||||
uint16_t m_fmv_syscmd = 0;
|
||||
uint16_t m_fmv_vidcmd = 0;
|
||||
uint16_t m_fmv_sysscr = 0;
|
||||
uint16_t m_fmv_dec_cmd = 0;
|
||||
uint16_t m_fmv_vdi_cmd = 0;
|
||||
uint16_t m_fmv_frame_rate = 0;
|
||||
uint16_t m_fmv_timer_compare = 56 - 1;
|
||||
uint32_t m_timer_count = 0;
|
||||
uint8_t m_fmv_stream = 0;
|
||||
bool m_fmv_dsp_enable = false;
|
||||
bool m_fmv_playback_active = false;
|
||||
bool m_fmv_decoder_active = false;
|
||||
bool m_fmv_show = false;
|
||||
bool m_fmv_single_step_pending = false;
|
||||
uint8_t m_fmv_slow_motion = 0;
|
||||
|
||||
uint32_t m_fmv_dclk_start_video = 0;
|
||||
bool m_fmv_dclk_start_video_latched = false;
|
||||
uint32_t m_fmv_dclk_pause_video = 0;
|
||||
bool m_fmv_dclk_pause_video_latched = false;
|
||||
|
||||
bool m_regs_update_latch = false;
|
||||
bool m_regs_update_scroll = false;
|
||||
|
||||
// mv_org()/mv_pos()/mv_window() geometry, written by the driver
|
||||
uint16_t m_ctrl_x_offset = 0, m_ctrl_y_offset = 0;
|
||||
uint16_t m_ctrl_x_active = 0, m_ctrl_y_active = 0;
|
||||
uint16_t m_ctrl_x_display = 0, m_ctrl_y_display = 0;
|
||||
uint16_t m_ctrl_window_width = 0, m_ctrl_window_height = 0;
|
||||
uint16_t m_ctrl_decoder_offset_x = 0, m_ctrl_decoder_offset_y = 0;
|
||||
|
||||
// Latched at register-update time, these are what the display uses.
|
||||
// m_latched_window_w/h are the DECWIN size; the display path does not use
|
||||
// it - see vblank_update() for why - but it is latched all the same, since
|
||||
// the register still has to read back the way the hardware's does.
|
||||
uint16_t m_latched_display_x = 0, m_latched_display_y = 0;
|
||||
uint16_t m_latched_window_x = 0, m_latched_window_y = 0;
|
||||
uint16_t m_latched_window_w = 0, m_latched_window_h = 0;
|
||||
|
||||
uint16_t m_image_width = 0, m_image_height = 0, m_image_rt = 0;
|
||||
|
||||
// ---- unlock counter for the MPEG working RAM ----
|
||||
uint8_t m_mpeg_ram_unlock_count = 0;
|
||||
bool m_mpeg_ram_enabled = false;
|
||||
std::unique_ptr<uint16_t[]> m_mpeg_ram;
|
||||
|
||||
// ---- stream plumbing ----
|
||||
mpeg1_demuxer m_video_demux{ 0xe0 };
|
||||
mpeg1_demuxer m_audio_demux{ 0xc0 };
|
||||
bool m_dma_pending = false;
|
||||
bool m_dma_for_fma = false;
|
||||
bool m_vcd_pixel_clock = false;
|
||||
|
||||
bool m_intreq = false;
|
||||
|
||||
// Counts of elementary-stream bytes handed to each decoder; useful when
|
||||
// tracing whether the driver is actually streaming.
|
||||
uint64_t m_video_es_bytes = 0;
|
||||
uint64_t m_audio_es_bytes = 0;
|
||||
|
||||
// --- decoders ---
|
||||
//
|
||||
// The decoders are kept free of MAME, so they hand their state over as
|
||||
// plain structs that this device owns, registers and copies across in
|
||||
// device_pre_save()/device_post_load(). Between them they add rather more
|
||||
// than a megabyte to the save state, most of it the two reference pictures
|
||||
// and the input FIFOs.
|
||||
mpeg1_video_decoder::sequence_state m_video_sequence;
|
||||
mpeg1_video_decoder::decoder_state m_video_state;
|
||||
mpeg1_audio_decoder::decoder_state m_audio_state;
|
||||
mpeg1_demuxer::demux_state m_video_demux_state;
|
||||
mpeg1_demuxer::demux_state m_audio_demux_state;
|
||||
mpeg1_video_decoder m_video;
|
||||
mpeg1_audio_decoder m_audio;
|
||||
|
||||
// Pictures are pulled out of the decoder on the stream's own frame period.
|
||||
//
|
||||
// The deadline is measured against m_display_ticks, a plain count of 45 kHz
|
||||
// ticks, and deliberately not against m_fmv_dclk. GEN_SYSCR lets the driver
|
||||
// write the stream's system clock reference straight into m_fmv_dclk, so
|
||||
// that register jumps backwards and forwards with the stream; pacing off it
|
||||
// leaves the deadline unreachable and the picture never changes again.
|
||||
uint32_t m_field_count = 0;
|
||||
uint32_t m_display_ticks = 0;
|
||||
uint32_t m_next_picture_tick = 0;
|
||||
bool m_picture_pending = false;
|
||||
bool m_seen_first_picture = false;
|
||||
|
||||
// Latch behind FMV_INT_NDAT, so that the video buffer running dry is
|
||||
// reported once per occurrence rather than on every tick it stays empty.
|
||||
bool m_video_underflow = false;
|
||||
|
||||
// Display geometry reported back to the driver.
|
||||
uint16_t m_display_width = 0, m_display_height = 0;
|
||||
uint32_t m_decoder_timecode = 0, m_display_timecode = 0;
|
||||
uint8_t m_display_video_status = 0;
|
||||
|
||||
// Decoded picture, already converted to RGB for the MCD212 to pick up.
|
||||
//
|
||||
// The MCD212 is configured for VIDEO_UPDATE_SCANLINE, so it asks for the
|
||||
// cartridge's pixels one scanline at a time as the field is scanned out,
|
||||
// while the decoder replaces the picture whenever the 45 kHz clock says the
|
||||
// next one is due. Handing get_pixel() the buffer the decoder writes into
|
||||
// therefore splices two different pictures - and two different sets of
|
||||
// geometry - into one displayed field, which is what shows up as tearing.
|
||||
//
|
||||
// So the decoder writes into m_picture and everything the display reads
|
||||
// lives in the m_shown_* copies, swapped over once per field in
|
||||
// vblank_update().
|
||||
std::vector<uint32_t> m_picture;
|
||||
int m_picture_width = 0, m_picture_height = 0;
|
||||
bool m_picture_updated = false;
|
||||
|
||||
std::vector<uint32_t> m_shown_picture;
|
||||
int m_shown_width = 0, m_shown_height = 0;
|
||||
|
||||
// Whether the picture on the display side is one this clip put there.
|
||||
//
|
||||
// This is the frameplayer's latched_frame_valid: the cartridge shows a
|
||||
// picture only once one has been latched for display, and switching the
|
||||
// decoder off invalidates whatever was latched. Without it the previous
|
||||
// clip's last frame stays in the display buffer and is put back on screen
|
||||
// the moment the driver re-enables the window for the *next* clip, a couple
|
||||
// of fields before that clip's first picture is ready - which is seen as
|
||||
// the outgoing clip's final frame, often at the wrong position, flashing up
|
||||
// again just before the new one starts.
|
||||
//
|
||||
// It has to survive a save state, or rewind and run-ahead - which load one
|
||||
// every frame - put the stale frame back on screen as fast as this clears
|
||||
// it. See device_pre_save() for how it travels.
|
||||
bool m_shown_picture_valid = false;
|
||||
|
||||
// The shown picture has to ride along in the save state. It is derived from
|
||||
// the decoder's frame store, but not reconstructably so: by the time a
|
||||
// state is written the decoder has usually moved on to a newer picture and
|
||||
// the one on screen may already have been recycled.
|
||||
//
|
||||
// The pixel count doubles as m_shown_picture_valid, which has no save item
|
||||
// of its own: an invalidated picture is written out as no picture at all.
|
||||
//
|
||||
// The back buffer m_picture needs no such copy. It only ever matters while
|
||||
// m_picture_updated is set, and it then holds exactly the decoder's current
|
||||
// display frame, so device_post_load() rebuilds it instead.
|
||||
static constexpr size_t MAX_SHOWN_PIXELS = 384 * 288;
|
||||
uint32_t m_shown_picture_save[MAX_SHOWN_PIXELS] = { 0 };
|
||||
uint32_t m_shown_picture_pixels = 0;
|
||||
bool m_shown_enabled = false;
|
||||
// The display window outlives the picture: it is open whenever the decoder
|
||||
// is powered up and the driver has given it a size, whether or not a clip
|
||||
// is currently running through it.
|
||||
bool m_shown_window_active = false;
|
||||
uint16_t m_shown_display_x = 0, m_shown_display_y = 0;
|
||||
uint16_t m_shown_window_x = 0, m_shown_window_y = 0;
|
||||
uint16_t m_shown_window_w = 0, m_shown_window_h = 0;
|
||||
|
||||
// Audio output ring, drained by the sound stream.
|
||||
//
|
||||
// Playback holds off until the ring has k_audio_prime_samples in it. The
|
||||
// driver hands the cartridge audio just in time - it keeps on the order of
|
||||
// 120ms of it outstanding - while the sound stream drains a whole update at
|
||||
// a time, so with no cushion the ring comes up a few hundred samples short
|
||||
// on the trough of every delivery cycle. The stream then has to insert
|
||||
// silence, and those short gaps are what is heard as crackling. The cushion
|
||||
// is rebuilt whenever the cartridge stops and drains, so it costs its own
|
||||
// depth in latency once per clip rather than accumulating.
|
||||
//
|
||||
// The cushion has to be deeper than one delivery burst, or the first burst
|
||||
// satisfies it and it buys nothing. How deep that has to be depends on the
|
||||
// title: Mutant Rampage hands over about 95ms at a time, but Monty Python's
|
||||
// Invasion from the Planet Skyron opens a clip by delivering 185ms every
|
||||
// 280ms - fully caught up on average, but a third of each cycle spent dry -
|
||||
// and keeps that up for the first four seconds. A 150ms cushion is emptied
|
||||
// by the trough of every one of those cycles, which is heard as the clicking
|
||||
// and stuttering that clears up once the driver settles into a tighter
|
||||
// rhythm. Measured against that title's delivery, 250ms is the point where
|
||||
// the ring stops reaching zero at all; 300ms leaves some margin and still
|
||||
// costs well under half the ring's 743ms depth.
|
||||
//
|
||||
// A title that keeps less audio outstanding than the cushion would never
|
||||
// reach it and would play nothing at all, so once audio is actually arriving
|
||||
// the wait is given up on after this long and playback starts with whatever
|
||||
// has accumulated. The timer only runs while the ring has something in it:
|
||||
// before that there is no audio yet and nothing is lost by waiting.
|
||||
static constexpr uint32_t k_audio_prime_samples = (44100 * 300 / 1000) * 2; // 300ms, stereo
|
||||
static constexpr uint32_t k_audio_prime_timeout = 44100 / 2; // 500ms
|
||||
sound_stream *m_stream = nullptr;
|
||||
std::vector<int16_t> m_audio_ring;
|
||||
uint32_t m_audio_write = 0, m_audio_read = 0;
|
||||
bool m_audio_primed = false;
|
||||
uint32_t m_audio_prime_wait = 0;
|
||||
|
||||
// Last time the audio path held anything at all to play, used to tell a
|
||||
// real buffer underflow from the gaps between the driver's transfers.
|
||||
uint32_t m_fma_last_audio_dclk = 0;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(CDI_DVC, cdidvc_device)
|
||||
|
||||
#endif // MAME_MACHINE_CDIDVC_H
|
||||
@@ -0,0 +1,872 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:vibecodekun
|
||||
/******************************************************************************
|
||||
|
||||
ISO/IEC 11172-3 (MPEG-1) Layer I/II audio decoder
|
||||
|
||||
See mpeg1audio.h for the interface.
|
||||
|
||||
Portions of this file are derived from PL_MPEG by Dominic Szablewski
|
||||
(https://github.com/phoboslab/pl_mpeg), whose MP2 decoder is in turn
|
||||
based on kjmp2 by Martin J. Fiedler, used under the following license:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Dominic Szablewski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "mpeg1audio.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int LAYER_III = 1;
|
||||
constexpr int LAYER_II = 2;
|
||||
constexpr int LAYER_I = 3;
|
||||
|
||||
constexpr int MODE_STEREO = 0;
|
||||
constexpr int MODE_JOINT_STEREO = 1;
|
||||
constexpr int MODE_DUAL_CHANNEL = 2;
|
||||
constexpr int MODE_MONO = 3;
|
||||
|
||||
const uint16_t SAMPLE_RATE[8] =
|
||||
{
|
||||
44100, 48000, 32000, 0, // MPEG-1
|
||||
22050, 24000, 16000, 0 // MPEG-2
|
||||
};
|
||||
|
||||
const uint16_t BIT_RATE_LAYER_II[28] =
|
||||
{
|
||||
32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1
|
||||
8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 // MPEG-2
|
||||
};
|
||||
|
||||
const uint16_t BIT_RATE_LAYER_I[28] =
|
||||
{
|
||||
32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, // MPEG-1
|
||||
32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 // MPEG-2
|
||||
};
|
||||
|
||||
// Scale factors are 2 * 2^(-index/3); the three bases cover the residue of the
|
||||
// index modulo three, and the shift covers the rest.
|
||||
const int32_t SCALEFACTOR_BASE[3] = { 0x02000000, 0x01965fea, 0x01428a30 };
|
||||
|
||||
//**************************************************************************
|
||||
// Layer II bit allocation lookup (ISO tables 3-B.2a through 3-B.2d)
|
||||
//**************************************************************************
|
||||
|
||||
// Table selection is by bit rate per channel and then by sample rate. The
|
||||
// encoded value carries the subband limit in its low six bits and the table
|
||||
// row set above that.
|
||||
constexpr uint8_t QUANT_TAB_A = 27 | 64; // 3-B.2a, high rate, sblimit 27
|
||||
constexpr uint8_t QUANT_TAB_B = 30 | 64; // 3-B.2b, high rate, sblimit 30
|
||||
constexpr uint8_t QUANT_TAB_C = 8; // 3-B.2c, low rate, sblimit 8
|
||||
constexpr uint8_t QUANT_TAB_D = 12; // 3-B.2d, low rate, sblimit 12
|
||||
|
||||
const uint8_t QUANT_LUT_STEP_1[2][16] =
|
||||
{
|
||||
// 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 kbit/s
|
||||
{ 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // mono
|
||||
// the same, per channel, for two-channel modes
|
||||
{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 } // stereo
|
||||
};
|
||||
|
||||
const uint8_t QUANT_LUT_STEP_2[3][3] =
|
||||
{
|
||||
// 44.1 kHz 48 kHz 32 kHz
|
||||
{ QUANT_TAB_C, QUANT_TAB_C, QUANT_TAB_D }, // 32 - 48 kbit/s/ch
|
||||
{ QUANT_TAB_A, QUANT_TAB_A, QUANT_TAB_A }, // 56 - 80 kbit/s/ch
|
||||
{ QUANT_TAB_B, QUANT_TAB_A, QUANT_TAB_B } // 96 + kbit/s/ch
|
||||
};
|
||||
|
||||
// Per subband: number of allocation bits in the high nibble, the row of the
|
||||
// step 4 table in the low nibble.
|
||||
const uint8_t QUANT_LUT_STEP_3[3][32] =
|
||||
{
|
||||
// low rate (3-B.2c and 3-B.2d)
|
||||
{
|
||||
0x44, 0x44,
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34
|
||||
},
|
||||
// high rate (3-B.2a and 3-B.2b)
|
||||
{
|
||||
0x43, 0x43, 0x43,
|
||||
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
||||
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
|
||||
},
|
||||
// MPEG-2 low sample rate (ISO 13818-3 B.2)
|
||||
{
|
||||
0x45, 0x45, 0x45, 0x45,
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
|
||||
0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24
|
||||
}
|
||||
};
|
||||
|
||||
// Row of the step 3 table plus the allocation code gives a quantiser index.
|
||||
const uint8_t QUANT_LUT_STEP_4[6][16] =
|
||||
{
|
||||
{ 0, 1, 2, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// Quantiser specifications, indexed by the value the tables above produce.
|
||||
// Index zero means the subband carries no data.
|
||||
const mpeg1_detail::quantizer_spec QUANT_TAB[17] =
|
||||
{
|
||||
{ 3, 1, 5 }, { 5, 1, 7 }, { 7, 0, 3 }, { 9, 1, 10 },
|
||||
{ 15, 0, 4 }, { 31, 0, 5 }, { 63, 0, 6 }, { 127, 0, 7 },
|
||||
{ 255, 0, 8 }, { 511, 0, 9 }, { 1023, 0, 10 }, { 2047, 0, 11 },
|
||||
{ 4095, 0, 12 }, { 8191, 0, 13 }, { 16383, 0, 14 }, { 32767, 0, 15 },
|
||||
{ 65535, 0, 16 }
|
||||
};
|
||||
|
||||
const mpeg1_detail::quantizer_spec QUANT_TAB_LAYER_I[14] =
|
||||
{
|
||||
{ 3, 0, 2 }, { 7, 0, 3 }, { 15, 0, 4 }, { 31, 0, 5 },
|
||||
{ 63, 0, 6 }, { 127, 0, 7 }, { 255, 0, 8 }, { 511, 0, 9 },
|
||||
{ 1023, 0, 10 }, { 2047, 0, 11 }, { 4095, 0, 12 }, { 8191, 0, 13 },
|
||||
{ 16383, 0, 14 }, { 32767, 0, 15 }
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
// ISO/IEC 11172-3 Table B.3, the polyphase synthesis window D[i],
|
||||
// scaled by 65536 so that every entry is an exact integer.
|
||||
const int32_t SYNTHESIS_WINDOW[512] =
|
||||
{
|
||||
0, -1, -1, -1, -1, -1, -1, -2,
|
||||
-2, -2, -2, -3, -3, -4, -4, -5,
|
||||
-5, -6, -7, -7, -8, -9, -10, -11,
|
||||
-13, -14, -16, -17, -19, -21, -24, -26,
|
||||
-29, -31, -35, -38, -41, -45, -49, -53,
|
||||
-58, -63, -68, -73, -79, -85, -91, -97,
|
||||
-104, -111, -117, -125, -132, -139, -147, -154,
|
||||
-161, -169, -176, -183, -190, -196, -202, -208,
|
||||
213, 218, 222, 225, 227, 228, 228, 227,
|
||||
224, 221, 215, 208, 200, 189, 177, 163,
|
||||
146, 127, 106, 83, 57, 29, -2, -36,
|
||||
-72, -111, -153, -197, -244, -294, -347, -401,
|
||||
-459, -519, -581, -645, -711, -779, -848, -919,
|
||||
-991, -1064, -1137, -1210, -1283, -1356, -1428, -1498,
|
||||
-1567, -1634, -1698, -1759, -1817, -1870, -1919, -1962,
|
||||
-2001, -2032, -2057, -2075, -2085, -2087, -2080, -2063,
|
||||
2037, 2000, 1952, 1893, 1822, 1739, 1644, 1535,
|
||||
1414, 1280, 1131, 970, 794, 605, 402, 185,
|
||||
-45, -288, -545, -814, -1095, -1388, -1692, -2006,
|
||||
-2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788,
|
||||
-5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597,
|
||||
-7910, -8209, -8491, -8755, -8998, -9219, -9416, -9585,
|
||||
-9727, -9838, -9916, -9959, -9966, -9935, -9863, -9750,
|
||||
-9592, -9389, -9139, -8840, -8492, -8092, -7640, -7134,
|
||||
6574, 5959, 5288, 4561, 3776, 2935, 2037, 1082,
|
||||
70, -998, -2122, -3300, -4533, -5818, -7154, -8540,
|
||||
-9975, -11455, -12980, -14548, -16155, -17799, -19478, -21189,
|
||||
-22929, -24694, -26482, -28289, -30112, -31947, -33791, -35640,
|
||||
-37489, -39336, -41176, -43006, -44821, -46617, -48390, -50137,
|
||||
-51853, -53534, -55178, -56778, -58333, -59838, -61289, -62684,
|
||||
-64019, -65290, -66494, -67629, -68692, -69679, -70590, -71420,
|
||||
-72169, -72835, -73415, -73908, -74313, -74630, -74856, -74992,
|
||||
75038, 74992, 74856, 74630, 74313, 73908, 73415, 72835,
|
||||
72169, 71420, 70590, 69679, 68692, 67629, 66494, 65290,
|
||||
64019, 62684, 61289, 59838, 58333, 56778, 55178, 53534,
|
||||
51853, 50137, 48390, 46617, 44821, 43006, 41176, 39336,
|
||||
37489, 35640, 33791, 31947, 30112, 28289, 26482, 24694,
|
||||
22929, 21189, 19478, 17799, 16155, 14548, 12980, 11455,
|
||||
9975, 8540, 7154, 5818, 4533, 3300, 2122, 998,
|
||||
-70, -1082, -2037, -2935, -3776, -4561, -5288, -5959,
|
||||
6574, 7134, 7640, 8092, 8492, 8840, 9139, 9389,
|
||||
9592, 9750, 9863, 9935, 9966, 9959, 9916, 9838,
|
||||
9727, 9585, 9416, 9219, 8998, 8755, 8491, 8209,
|
||||
7910, 7597, 7271, 6935, 6589, 6237, 5879, 5517,
|
||||
5153, 4788, 4425, 4063, 3705, 3351, 3004, 2663,
|
||||
2330, 2006, 1692, 1388, 1095, 814, 545, 288,
|
||||
45, -185, -402, -605, -794, -970, -1131, -1280,
|
||||
-1414, -1535, -1644, -1739, -1822, -1893, -1952, -2000,
|
||||
2037, 2063, 2080, 2087, 2085, 2075, 2057, 2032,
|
||||
2001, 1962, 1919, 1870, 1817, 1759, 1698, 1634,
|
||||
1567, 1498, 1428, 1356, 1283, 1210, 1137, 1064,
|
||||
991, 919, 848, 779, 711, 645, 581, 519,
|
||||
459, 401, 347, 294, 244, 197, 153, 111,
|
||||
72, 36, 2, -29, -57, -83, -106, -127,
|
||||
-146, -163, -177, -189, -200, -208, -215, -221,
|
||||
-224, -227, -228, -228, -227, -225, -222, -218,
|
||||
213, 208, 202, 196, 190, 183, 176, 169,
|
||||
161, 154, 147, 139, 132, 125, 117, 111,
|
||||
104, 97, 91, 85, 79, 73, 68, 63,
|
||||
58, 53, 49, 45, 41, 38, 35, 31,
|
||||
29, 26, 24, 21, 19, 17, 16, 14,
|
||||
13, 11, 10, 9, 8, 7, 7, 6,
|
||||
5, 5, 4, 4, 3, 3, 2, 2,
|
||||
2, 2, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
|
||||
// Matrixing coefficients N[i][k] = cos((16 + i) * (2k + 1) * pi / 64),
|
||||
// built once on first use.
|
||||
struct matrix_table
|
||||
{
|
||||
double n[64][32];
|
||||
|
||||
matrix_table()
|
||||
{
|
||||
for (int i = 0; i < 64; i++)
|
||||
for (int k = 0; k < 32; k++)
|
||||
n[i][k] = std::cos((16 + i) * (2 * k + 1) * 3.14159265358979323846 / 64.0);
|
||||
}
|
||||
};
|
||||
|
||||
const matrix_table &matrix()
|
||||
{
|
||||
static const matrix_table table;
|
||||
return table;
|
||||
}
|
||||
|
||||
inline int16_t clamp_sample(double value)
|
||||
{
|
||||
const int v = int(value);
|
||||
return int16_t((v < -32768) ? -32768 : ((v > 32767) ? 32767 : v));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
//**************************************************************************
|
||||
// Construction
|
||||
//**************************************************************************
|
||||
|
||||
mpeg1_audio_decoder::mpeg1_audio_decoder()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::reset()
|
||||
{
|
||||
m_buffer.clear();
|
||||
m_read_pos = 0;
|
||||
m_bit_pos = 0;
|
||||
m_underflow = false;
|
||||
m_frame_decoded = false;
|
||||
|
||||
m_frame_header = 0;
|
||||
m_version = 0;
|
||||
m_layer = 0;
|
||||
m_bitrate_index = 0;
|
||||
m_samplerate_index = 3;
|
||||
m_mode = 0;
|
||||
m_bound = 0;
|
||||
m_sample_rate = 0;
|
||||
m_bit_rate = 0;
|
||||
m_channels = 2;
|
||||
m_samples_per_frame = 1152;
|
||||
m_next_frame_size = 0;
|
||||
m_has_header = false;
|
||||
|
||||
std::memset(m_allocation, 0, sizeof(m_allocation));
|
||||
std::memset(m_scale_factor_info, 0, sizeof(m_scale_factor_info));
|
||||
std::memset(m_scale_factor, 0, sizeof(m_scale_factor));
|
||||
std::memset(m_sample, 0, sizeof(m_sample));
|
||||
std::memset(m_v, 0, sizeof(m_v));
|
||||
m_v_pos = 0;
|
||||
|
||||
m_output.assign(1152 * 2, 0);
|
||||
m_sample_count = 0;
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::get_decoder_state(decoder_state &state) const
|
||||
{
|
||||
// Rebase so the read position becomes byte 0; nothing behind it is read
|
||||
// again. Only a driver that ignored the FMA status entirely could push the
|
||||
// buffer past the cap, and the clamp keeps that from overrunning the copy.
|
||||
const size_t available = m_buffer.size() - m_read_pos;
|
||||
const size_t size = std::min<size_t>(available, decoder_state::max_buffer);
|
||||
state.buffer_size = uint32_t(size);
|
||||
state.bit_pos = uint32_t(std::min<size_t>(m_bit_pos - m_read_pos * 8, size * 8));
|
||||
if (size != 0)
|
||||
std::memcpy(state.buffer, m_buffer.data() + m_read_pos, size);
|
||||
|
||||
state.underflow = m_underflow ? 1 : 0;
|
||||
state.frame_decoded = m_frame_decoded ? 1 : 0;
|
||||
state.has_header = m_has_header ? 1 : 0;
|
||||
|
||||
state.frame_header = m_frame_header;
|
||||
state.version = m_version;
|
||||
state.layer = m_layer;
|
||||
state.bitrate_index = m_bitrate_index;
|
||||
state.samplerate_index = m_samplerate_index;
|
||||
state.mode = m_mode;
|
||||
state.bound = m_bound;
|
||||
state.sample_rate = m_sample_rate;
|
||||
state.bit_rate = m_bit_rate;
|
||||
state.channels = m_channels;
|
||||
state.samples_per_frame = m_samples_per_frame;
|
||||
state.next_frame_size = m_next_frame_size;
|
||||
|
||||
std::memcpy(state.v, m_v, sizeof(state.v));
|
||||
state.v_pos = m_v_pos;
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::set_decoder_state(const decoder_state &state)
|
||||
{
|
||||
const size_t size = std::min<size_t>(state.buffer_size, decoder_state::max_buffer);
|
||||
m_buffer.assign(state.buffer, state.buffer + size);
|
||||
m_read_pos = 0;
|
||||
m_bit_pos = std::min<size_t>(state.bit_pos, size * 8);
|
||||
|
||||
m_underflow = state.underflow != 0;
|
||||
m_frame_decoded = state.frame_decoded != 0;
|
||||
m_has_header = state.has_header != 0;
|
||||
|
||||
m_frame_header = state.frame_header;
|
||||
m_version = state.version;
|
||||
m_layer = state.layer;
|
||||
m_bitrate_index = state.bitrate_index;
|
||||
m_samplerate_index = state.samplerate_index;
|
||||
m_mode = state.mode;
|
||||
m_bound = state.bound;
|
||||
m_sample_rate = state.sample_rate;
|
||||
m_bit_rate = state.bit_rate;
|
||||
m_channels = state.channels;
|
||||
m_samples_per_frame = state.samples_per_frame;
|
||||
m_next_frame_size = state.next_frame_size;
|
||||
|
||||
std::memcpy(m_v, state.v, sizeof(m_v));
|
||||
m_v_pos = state.v_pos;
|
||||
|
||||
// Per-frame working state is rebuilt by the next decode(); clear the
|
||||
// pointer table so a stale entry can never be followed if that assumption
|
||||
// is ever broken.
|
||||
std::memset(m_allocation, 0, sizeof(m_allocation));
|
||||
m_sample_count = 0;
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Bit reading
|
||||
//**************************************************************************
|
||||
|
||||
void mpeg1_audio_decoder::write(const uint8_t *data, size_t length)
|
||||
{
|
||||
if (m_buffer.size() - m_read_pos + length > m_buffer_limit)
|
||||
{
|
||||
const size_t drop = std::min(m_read_pos, m_buffer.size());
|
||||
if (drop)
|
||||
{
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + drop);
|
||||
m_bit_pos -= drop * 8;
|
||||
m_read_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.insert(m_buffer.end(), data, data + length);
|
||||
}
|
||||
|
||||
bool mpeg1_audio_decoder::have_bits(size_t count) const
|
||||
{
|
||||
return m_bit_pos + count <= m_buffer.size() * 8;
|
||||
}
|
||||
|
||||
uint32_t mpeg1_audio_decoder::read_bits(int count)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
while (count--)
|
||||
{
|
||||
if (m_bit_pos >= m_buffer.size() * 8)
|
||||
{
|
||||
value <<= 1;
|
||||
m_bit_pos++;
|
||||
continue;
|
||||
}
|
||||
value = (value << 1) | ((m_buffer[m_bit_pos >> 3] >> (7 - (m_bit_pos & 7))) & 1);
|
||||
m_bit_pos++;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::align_to_byte()
|
||||
{
|
||||
m_bit_pos = (m_bit_pos + 7) & ~size_t(7);
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Frame header
|
||||
//**************************************************************************
|
||||
|
||||
bool mpeg1_audio_decoder::find_frame_sync()
|
||||
{
|
||||
// A frame begins with eleven set bits; scan byte-aligned since that is
|
||||
// where a valid frame always starts.
|
||||
for (size_t i = m_bit_pos >> 3; i + 1 < m_buffer.size(); i++)
|
||||
{
|
||||
if (m_buffer[i] == 0xff && (m_buffer[i + 1] & 0xe0) == 0xe0)
|
||||
{
|
||||
m_bit_pos = i * 8;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found; drop what has been scanned so the buffer does not grow.
|
||||
m_bit_pos = (m_buffer.size() > 1) ? (m_buffer.size() - 1) * 8 : 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mpeg1_audio_decoder::decode_header()
|
||||
{
|
||||
if (!have_bits(32))
|
||||
return false;
|
||||
|
||||
const size_t start = m_bit_pos;
|
||||
const uint32_t header = read_bits(32);
|
||||
|
||||
if ((header & 0xffe00000) != 0xffe00000)
|
||||
{
|
||||
m_bit_pos = start + 8;
|
||||
return false;
|
||||
}
|
||||
|
||||
const int version = (header >> 19) & 3;
|
||||
const int layer = (header >> 17) & 3;
|
||||
const int bitrate_index = (header >> 12) & 0x0f;
|
||||
const int samplerate_index = (header >> 10) & 3;
|
||||
const int padding = (header >> 9) & 1;
|
||||
const int mode = (header >> 6) & 3;
|
||||
|
||||
// Only MPEG-1 (version 3) and MPEG-2 (version 2) Layer I/II are handled.
|
||||
if (version == 1 || layer == 0 || layer == LAYER_III ||
|
||||
bitrate_index == 0 || bitrate_index == 0x0f || samplerate_index == 3)
|
||||
{
|
||||
m_bit_pos = start + 8;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_frame_header = header;
|
||||
m_version = version;
|
||||
m_layer = layer;
|
||||
m_bitrate_index = bitrate_index - 1;
|
||||
m_samplerate_index = samplerate_index;
|
||||
m_mode = mode;
|
||||
|
||||
const bool mpeg2 = (version == 2);
|
||||
m_sample_rate = SAMPLE_RATE[samplerate_index + (mpeg2 ? 4 : 0)];
|
||||
m_bit_rate = (layer == LAYER_I)
|
||||
? BIT_RATE_LAYER_I[m_bitrate_index + (mpeg2 ? 14 : 0)]
|
||||
: BIT_RATE_LAYER_II[m_bitrate_index + (mpeg2 ? 14 : 0)];
|
||||
|
||||
m_channels = (mode == MODE_MONO) ? 1 : 2;
|
||||
|
||||
// In joint stereo the mode extension gives the number of subbands that are
|
||||
// still coded separately.
|
||||
m_bound = (mode == MODE_JOINT_STEREO) ? ((((header >> 4) & 3) + 1) << 2) : 32;
|
||||
if (mode == MODE_MONO)
|
||||
m_bound = 0;
|
||||
|
||||
if (layer == LAYER_I)
|
||||
{
|
||||
m_samples_per_frame = 384;
|
||||
m_next_frame_size = (12000 * m_bit_rate / m_sample_rate + padding) * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_samples_per_frame = 1152;
|
||||
m_next_frame_size = 144000 * m_bit_rate / m_sample_rate + padding;
|
||||
}
|
||||
|
||||
m_has_header = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Frame decoding
|
||||
//**************************************************************************
|
||||
|
||||
int mpeg1_audio_decoder::decode()
|
||||
{
|
||||
m_sample_count = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const size_t frame_start = m_bit_pos;
|
||||
|
||||
if (!find_frame_sync())
|
||||
{
|
||||
m_underflow = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!decode_header())
|
||||
{
|
||||
if (m_bit_pos <= frame_start)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The whole frame has to be present before it can be decoded.
|
||||
const size_t frame_bits = size_t(m_next_frame_size) * 8;
|
||||
const size_t header_start = m_bit_pos - 32;
|
||||
if (header_start + frame_bits > m_buffer.size() * 8)
|
||||
{
|
||||
m_bit_pos = header_start;
|
||||
m_underflow = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_underflow = false;
|
||||
|
||||
// Skip the CRC when protection is enabled.
|
||||
if (((m_frame_header >> 16) & 1) == 0)
|
||||
read_bits(16);
|
||||
|
||||
if (m_layer == LAYER_I)
|
||||
decode_layer_i();
|
||||
else
|
||||
decode_layer_ii();
|
||||
|
||||
// Move to the start of the next frame regardless of how many bits the
|
||||
// payload actually used.
|
||||
m_bit_pos = header_start + frame_bits;
|
||||
m_read_pos = m_bit_pos >> 3;
|
||||
|
||||
if (m_read_pos >= 16384)
|
||||
{
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + m_read_pos);
|
||||
m_bit_pos -= m_read_pos * 8;
|
||||
m_read_pos = 0;
|
||||
}
|
||||
|
||||
m_frame_decoded = true;
|
||||
return m_sample_count;
|
||||
}
|
||||
}
|
||||
|
||||
const mpeg1_audio_decoder::quantizer_spec *mpeg1_audio_decoder::read_allocation(int sb, int table)
|
||||
{
|
||||
const int step3 = QUANT_LUT_STEP_3[table][sb];
|
||||
const int bits = step3 >> 4;
|
||||
const int row = step3 & 15;
|
||||
|
||||
const int code = int(read_bits(bits));
|
||||
const int index = QUANT_LUT_STEP_4[row][code];
|
||||
|
||||
return index ? &QUANT_TAB[index - 1] : nullptr;
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::read_samples(int channel, int sb, int part)
|
||||
{
|
||||
const quantizer_spec *const q = m_allocation[channel][sb];
|
||||
double *const out = m_sample[channel][sb];
|
||||
|
||||
if (!q)
|
||||
{
|
||||
out[0] = out[1] = out[2] = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve the scale factor: 2 * 2^(-index/3), with 63 meaning silence.
|
||||
const int sf_index = m_scale_factor[channel][sb][part];
|
||||
double scale;
|
||||
if (sf_index == 63)
|
||||
{
|
||||
scale = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int shift = sf_index / 3;
|
||||
scale = double(SCALEFACTOR_BASE[sf_index % 3]) / double(1 << 24) / double(1 << shift);
|
||||
}
|
||||
|
||||
int code[3];
|
||||
if (q->group)
|
||||
{
|
||||
// Three samples share one codeword.
|
||||
uint32_t value = read_bits(q->bits);
|
||||
code[0] = int(value % q->levels);
|
||||
value /= q->levels;
|
||||
code[1] = int(value % q->levels);
|
||||
code[2] = int(value / q->levels);
|
||||
}
|
||||
else
|
||||
{
|
||||
code[0] = int(read_bits(q->bits));
|
||||
code[1] = int(read_bits(q->bits));
|
||||
code[2] = int(read_bits(q->bits));
|
||||
}
|
||||
|
||||
// The code is a two's complement fraction with its most significant bit
|
||||
// inverted, so subtracting from the midpoint recovers the signed value.
|
||||
const int adjust = ((q->levels + 1) >> 1) - 1;
|
||||
const double norm = 1.0 / double(q->levels + 1);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
out[i] = double(adjust - code[i]) * norm * scale;
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::decode_layer_ii()
|
||||
{
|
||||
const int table1 = (m_mode == MODE_MONO) ? 0 : 1;
|
||||
const int table2 = QUANT_LUT_STEP_1[table1][m_bitrate_index];
|
||||
const int encoded = QUANT_LUT_STEP_2[table2][m_samplerate_index];
|
||||
|
||||
int sblimit = encoded & 63;
|
||||
const int table = encoded >> 6;
|
||||
|
||||
int bound = (m_mode == MODE_JOINT_STEREO) ? m_bound : ((m_mode == MODE_MONO) ? 0 : 32);
|
||||
if (bound > sblimit)
|
||||
bound = sblimit;
|
||||
|
||||
// Bit allocation
|
||||
std::memset(m_allocation, 0, sizeof(m_allocation));
|
||||
for (int sb = 0; sb < bound; sb++)
|
||||
{
|
||||
m_allocation[0][sb] = read_allocation(sb, table);
|
||||
m_allocation[1][sb] = read_allocation(sb, table);
|
||||
}
|
||||
for (int sb = bound; sb < sblimit; sb++)
|
||||
m_allocation[0][sb] = m_allocation[1][sb] = read_allocation(sb, table);
|
||||
|
||||
// Scale factor selection information
|
||||
for (int sb = 0; sb < sblimit; sb++)
|
||||
{
|
||||
for (int ch = 0; ch < m_channels; ch++)
|
||||
{
|
||||
if (m_allocation[ch][sb])
|
||||
m_scale_factor_info[ch][sb] = uint8_t(read_bits(2));
|
||||
}
|
||||
if (m_mode == MODE_MONO)
|
||||
m_scale_factor_info[1][sb] = m_scale_factor_info[0][sb];
|
||||
}
|
||||
|
||||
// Scale factors
|
||||
for (int sb = 0; sb < sblimit; sb++)
|
||||
{
|
||||
for (int ch = 0; ch < m_channels; ch++)
|
||||
{
|
||||
if (!m_allocation[ch][sb])
|
||||
continue;
|
||||
|
||||
int *const sf = m_scale_factor[ch][sb];
|
||||
switch (m_scale_factor_info[ch][sb])
|
||||
{
|
||||
case 0:
|
||||
sf[0] = int(read_bits(6));
|
||||
sf[1] = int(read_bits(6));
|
||||
sf[2] = int(read_bits(6));
|
||||
break;
|
||||
case 1:
|
||||
sf[0] = sf[1] = int(read_bits(6));
|
||||
sf[2] = int(read_bits(6));
|
||||
break;
|
||||
case 2:
|
||||
sf[0] = sf[1] = sf[2] = int(read_bits(6));
|
||||
break;
|
||||
default:
|
||||
sf[0] = int(read_bits(6));
|
||||
sf[1] = sf[2] = int(read_bits(6));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_mode == MODE_MONO)
|
||||
{
|
||||
m_scale_factor[1][sb][0] = m_scale_factor[0][sb][0];
|
||||
m_scale_factor[1][sb][1] = m_scale_factor[0][sb][1];
|
||||
m_scale_factor[1][sb][2] = m_scale_factor[0][sb][2];
|
||||
}
|
||||
}
|
||||
|
||||
// Twelve granules, each producing three sets of 32 subband samples.
|
||||
int out_pos = 0;
|
||||
for (int granule = 0; granule < 12; granule++)
|
||||
{
|
||||
const int part = granule >> 2;
|
||||
|
||||
for (int sb = 0; sb < bound; sb++)
|
||||
{
|
||||
read_samples(0, sb, part);
|
||||
read_samples(1, sb, part);
|
||||
}
|
||||
|
||||
for (int sb = bound; sb < sblimit; sb++)
|
||||
{
|
||||
read_samples(0, sb, part);
|
||||
// Above the bound both channels share the coded samples.
|
||||
for (int i = 0; i < 3; i++)
|
||||
m_sample[1][sb][i] = m_sample[0][sb][i];
|
||||
}
|
||||
|
||||
for (int sb = sblimit; sb < 32; sb++)
|
||||
{
|
||||
for (int ch = 0; ch < 2; ch++)
|
||||
m_sample[ch][sb][0] = m_sample[ch][sb][1] = m_sample[ch][sb][2] = 0.0;
|
||||
}
|
||||
|
||||
for (int part_index = 0; part_index < 3; part_index++)
|
||||
synthesise(part_index, out_pos);
|
||||
}
|
||||
|
||||
m_sample_count = out_pos / 2;
|
||||
}
|
||||
|
||||
void mpeg1_audio_decoder::decode_layer_i()
|
||||
{
|
||||
int bound = (m_mode == MODE_JOINT_STEREO) ? m_bound : ((m_mode == MODE_MONO) ? 0 : 32);
|
||||
|
||||
std::memset(m_allocation, 0, sizeof(m_allocation));
|
||||
|
||||
for (int sb = 0; sb < bound; sb++)
|
||||
{
|
||||
for (int ch = 0; ch < 2; ch++)
|
||||
{
|
||||
const int code = int(read_bits(4));
|
||||
m_allocation[ch][sb] = code ? &QUANT_TAB_LAYER_I[code - 1] : nullptr;
|
||||
}
|
||||
}
|
||||
for (int sb = bound; sb < 32; sb++)
|
||||
{
|
||||
const int code = int(read_bits(4));
|
||||
m_allocation[0][sb] = m_allocation[1][sb] = code ? &QUANT_TAB_LAYER_I[code - 1] : nullptr;
|
||||
}
|
||||
|
||||
for (int sb = 0; sb < 32; sb++)
|
||||
{
|
||||
for (int ch = 0; ch < m_channels; ch++)
|
||||
{
|
||||
if (m_allocation[ch][sb])
|
||||
m_scale_factor[ch][sb][0] = int(read_bits(6));
|
||||
}
|
||||
if (m_mode == MODE_MONO)
|
||||
m_scale_factor[1][sb][0] = m_scale_factor[0][sb][0];
|
||||
}
|
||||
|
||||
int out_pos = 0;
|
||||
for (int granule = 0; granule < 12; granule++)
|
||||
{
|
||||
for (int sb = 0; sb < bound; sb++)
|
||||
{
|
||||
for (int ch = 0; ch < 2; ch++)
|
||||
{
|
||||
const quantizer_spec *const q = m_allocation[ch][sb];
|
||||
if (!q)
|
||||
{
|
||||
m_sample[ch][sb][0] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int sf_index = m_scale_factor[ch][sb][0];
|
||||
const int shift = sf_index / 3;
|
||||
const double scale = (sf_index == 63) ? 0.0
|
||||
: double(SCALEFACTOR_BASE[sf_index % 3]) / double(1 << 24) / double(1 << shift);
|
||||
|
||||
const int code = int(read_bits(q->bits));
|
||||
const int adjust = ((q->levels + 1) >> 1) - 1;
|
||||
m_sample[ch][sb][0] = double(adjust - code) / double(q->levels + 1) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
for (int sb = bound; sb < 32; sb++)
|
||||
{
|
||||
const quantizer_spec *const q = m_allocation[0][sb];
|
||||
if (!q)
|
||||
{
|
||||
m_sample[0][sb][0] = m_sample[1][sb][0] = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int sf_index = m_scale_factor[0][sb][0];
|
||||
const int shift = sf_index / 3;
|
||||
const double scale = (sf_index == 63) ? 0.0
|
||||
: double(SCALEFACTOR_BASE[sf_index % 3]) / double(1 << 24) / double(1 << shift);
|
||||
|
||||
const int code = int(read_bits(q->bits));
|
||||
const int adjust = ((q->levels + 1) >> 1) - 1;
|
||||
m_sample[0][sb][0] = m_sample[1][sb][0] = double(adjust - code) / double(q->levels + 1) * scale;
|
||||
}
|
||||
|
||||
synthesise(0, out_pos);
|
||||
}
|
||||
|
||||
m_sample_count = out_pos / 2;
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Polyphase synthesis filter (ISO/IEC 11172-3 clause 2.4.3.2)
|
||||
//**************************************************************************
|
||||
|
||||
void mpeg1_audio_decoder::synthesise(int part, int &out_pos)
|
||||
{
|
||||
m_v_pos = (m_v_pos - 64) & 1023;
|
||||
|
||||
const matrix_table &n = matrix();
|
||||
|
||||
for (int ch = 0; ch < 2; ch++)
|
||||
{
|
||||
// Matrixing: 32 subband samples become 64 values in the history.
|
||||
double *const v = &m_v[ch][0];
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int k = 0; k < 32; k++)
|
||||
sum += n.n[i][k] * m_sample[ch][k][part];
|
||||
v[(m_v_pos + i) & 1023] = sum;
|
||||
}
|
||||
|
||||
// Build the 512-entry window input and apply the window.
|
||||
double u[512];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
for (int j = 0; j < 32; j++)
|
||||
{
|
||||
u[i * 64 + j] = v[(m_v_pos + i * 128 + j) & 1023];
|
||||
u[i * 64 + 32 + j] = v[(m_v_pos + i * 128 + 96 + j) & 1023];
|
||||
}
|
||||
}
|
||||
|
||||
// The window table is stored scaled by 65536.
|
||||
double out[32];
|
||||
for (int j = 0; j < 32; j++)
|
||||
{
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < 16; i++)
|
||||
sum += u[j + 32 * i] * (double(SYNTHESIS_WINDOW[j + 32 * i]) / 65536.0);
|
||||
out[j] = sum;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 32; j++)
|
||||
m_output[out_pos + j * 2 + ch] = clamp_sample(out[j] * 32767.0);
|
||||
}
|
||||
|
||||
// A mono stream fills only channel zero, so mirror it.
|
||||
if (m_mode == MODE_MONO)
|
||||
{
|
||||
for (int j = 0; j < 32; j++)
|
||||
m_output[out_pos + j * 2 + 1] = m_output[out_pos + j * 2];
|
||||
}
|
||||
|
||||
out_pos += 64;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:vibecodekun
|
||||
/******************************************************************************
|
||||
|
||||
ISO/IEC 11172-3 (MPEG-1) Layer I/II audio decoder
|
||||
|
||||
Companion to mpeg1video.h for the CD-i Digital Video Cartridge, and like it
|
||||
deliberately independent of MAME so it can be tested on its own. The
|
||||
quantiser tables follow pl_mpeg and kjmp2; see mpeg1audio.cpp for pl_mpeg's
|
||||
MIT license notice.
|
||||
|
||||
Green Book streams are Layer II, 44.1 kHz, 224 kbit/s stereo, but Layer I
|
||||
and the other sample rates are handled as well since they cost almost
|
||||
nothing to support.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_MPEG1AUDIO_H
|
||||
#define MAME_MACHINE_MPEG1AUDIO_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace mpeg1_detail {
|
||||
|
||||
// One entry of the quantiser tables: how many levels the subband is coded
|
||||
// with, whether three samples share a codeword, and the codeword width.
|
||||
struct quantizer_spec
|
||||
{
|
||||
uint16_t levels;
|
||||
uint8_t group;
|
||||
uint8_t bits;
|
||||
};
|
||||
|
||||
} // namespace mpeg1_detail
|
||||
|
||||
class mpeg1_audio_decoder
|
||||
{
|
||||
public:
|
||||
// Layer II carries 1152 samples per channel per frame, which is the most
|
||||
// any one call to decode() can produce.
|
||||
static constexpr int max_samples_per_frame = 1152;
|
||||
|
||||
mpeg1_audio_decoder();
|
||||
|
||||
void reset();
|
||||
|
||||
// Feed demultiplexed elementary stream bytes.
|
||||
void write(const uint8_t *data, size_t length);
|
||||
|
||||
// Decode one frame. Returns the number of sample pairs produced, or zero
|
||||
// if more data is needed.
|
||||
int decode();
|
||||
|
||||
// Interleaved stereo output of the last decoded frame. Mono streams are
|
||||
// duplicated to both channels so the consumer always sees stereo.
|
||||
const int16_t *samples() const { return m_output.data(); }
|
||||
int sample_count() const { return m_sample_count; }
|
||||
|
||||
int sample_rate() const { return m_sample_rate; }
|
||||
int channels() const { return m_channels; }
|
||||
int bit_rate() const { return m_bit_rate; }
|
||||
|
||||
// The 32 bits of the most recent frame header, which the cartridge exposes
|
||||
// to the driver verbatim.
|
||||
uint32_t frame_header() const { return m_frame_header; }
|
||||
|
||||
// Set when a frame has been decoded since the last call to this.
|
||||
bool take_frame_decoded() { const bool v = m_frame_decoded; m_frame_decoded = false; return v; }
|
||||
|
||||
size_t buffered_bytes() const { return m_buffer.size() - m_read_pos; }
|
||||
void set_buffer_limit(size_t bytes) { m_buffer_limit = bytes; }
|
||||
bool underflowed() const { return m_underflow; }
|
||||
void clear_underflow() { m_underflow = false; }
|
||||
|
||||
// Everything that has to survive a save state.
|
||||
//
|
||||
// The per-frame working set - the allocation table, scale factors, the
|
||||
// dequantised samples and the output buffer - is deliberately absent.
|
||||
// decode() fills and consumes all of it within the one call, and the
|
||||
// allocation table is an array of pointers into static tables that would
|
||||
// have to be turned back into indices for no benefit.
|
||||
//
|
||||
// What does have to come back is the polyphase synthesis history. It is a
|
||||
// rolling window that spans frames, and starting it from zero puts a click
|
||||
// on the first frame after every load.
|
||||
struct decoder_state
|
||||
{
|
||||
// write() compacts but does not refuse data once the limit is reached,
|
||||
// so unlike the video decoder the buffer can run past its limit if the
|
||||
// driver keeps pushing. Four times the cartridge's 64KB gives that
|
||||
// plenty of room; a buffer somehow larger still is truncated, costing
|
||||
// the tail of the stream and resyncing at the next frame header.
|
||||
static constexpr size_t max_buffer = 256 * 1024;
|
||||
|
||||
uint32_t buffer_size = 0;
|
||||
uint32_t bit_pos = 0; // bit offset into buffer[]
|
||||
uint8_t buffer[max_buffer] = { 0 };
|
||||
|
||||
uint8_t underflow = 0;
|
||||
uint8_t frame_decoded = 0;
|
||||
uint8_t has_header = 0;
|
||||
|
||||
uint32_t frame_header = 0;
|
||||
int32_t version = 0;
|
||||
int32_t layer = 0;
|
||||
int32_t bitrate_index = 0;
|
||||
int32_t samplerate_index = 3;
|
||||
int32_t mode = 0;
|
||||
int32_t bound = 0;
|
||||
int32_t sample_rate = 0;
|
||||
int32_t bit_rate = 0;
|
||||
int32_t channels = 2;
|
||||
int32_t samples_per_frame = 1152;
|
||||
int32_t next_frame_size = 0;
|
||||
|
||||
double v[2][1024] = { { 0.0 } };
|
||||
int32_t v_pos = 0;
|
||||
};
|
||||
|
||||
void get_decoder_state(decoder_state &state) const;
|
||||
void set_decoder_state(const decoder_state &state);
|
||||
|
||||
private:
|
||||
using quantizer_spec = mpeg1_detail::quantizer_spec;
|
||||
|
||||
bool have_bits(size_t count) const;
|
||||
uint32_t read_bits(int count);
|
||||
void align_to_byte();
|
||||
|
||||
bool find_frame_sync();
|
||||
bool decode_header();
|
||||
void decode_layer_ii();
|
||||
void decode_layer_i();
|
||||
const quantizer_spec *read_allocation(int sb, int table);
|
||||
void read_samples(int channel, int sb, int part);
|
||||
void synthesise(int part, int &out_pos);
|
||||
|
||||
std::vector<uint8_t> m_buffer;
|
||||
size_t m_read_pos = 0;
|
||||
size_t m_bit_pos = 0;
|
||||
size_t m_buffer_limit = 128 * 1024;
|
||||
bool m_underflow = false;
|
||||
bool m_frame_decoded = false;
|
||||
|
||||
// header fields
|
||||
uint32_t m_frame_header = 0;
|
||||
int m_version = 0;
|
||||
int m_layer = 0;
|
||||
int m_bitrate_index = 0;
|
||||
int m_samplerate_index = 3;
|
||||
int m_mode = 0;
|
||||
int m_bound = 0;
|
||||
int m_sample_rate = 0;
|
||||
int m_bit_rate = 0;
|
||||
int m_channels = 2;
|
||||
int m_samples_per_frame = 1152;
|
||||
int m_next_frame_size = 0;
|
||||
bool m_has_header = false;
|
||||
|
||||
const quantizer_spec *m_allocation[2][32];
|
||||
uint8_t m_scale_factor_info[2][32];
|
||||
int m_scale_factor[2][32][3];
|
||||
double m_sample[2][32][3];
|
||||
|
||||
// polyphase synthesis history
|
||||
double m_v[2][1024];
|
||||
int m_v_pos = 0;
|
||||
|
||||
std::vector<int16_t> m_output;
|
||||
int m_sample_count = 0;
|
||||
};
|
||||
|
||||
#endif // MAME_MACHINE_MPEG1AUDIO_H
|
||||
@@ -0,0 +1,297 @@
|
||||
// license:GPL-3.0-only
|
||||
// copyright-holders:Andre Zeps, vibecodekun
|
||||
/******************************************************************************
|
||||
|
||||
ISO/IEC 11172-1 (MPEG-1) program stream demultiplexer
|
||||
|
||||
See mpeg1demux.h. This is a direct port of mpeg_demuxer.sv from the
|
||||
CDi_MiSTer VMPEG core, including its habit of parking the state machine in
|
||||
MAGIC_MATCH when a stream number does not match.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "mpeg1demux.h"
|
||||
|
||||
void mpeg1_demuxer::reset()
|
||||
{
|
||||
m_state = IDLE;
|
||||
m_packet_body = false;
|
||||
m_length_decreasing = false;
|
||||
m_packet_length = 0;
|
||||
m_dts_present = false;
|
||||
m_scr = m_scr_temp = 0;
|
||||
m_pts = m_pts_temp = 0;
|
||||
m_dts = m_dts_temp = 0;
|
||||
m_scr_updated = m_pts_updated = m_dts_updated = false;
|
||||
m_program_end = false;
|
||||
}
|
||||
|
||||
void mpeg1_demuxer::get_state(demux_state &state) const
|
||||
{
|
||||
state.state = m_state;
|
||||
state.packet_body = m_packet_body ? 1 : 0;
|
||||
state.length_decreasing = m_length_decreasing ? 1 : 0;
|
||||
state.dts_present = m_dts_present ? 1 : 0;
|
||||
state.packet_length = m_packet_length;
|
||||
state.scr = m_scr;
|
||||
state.scr_temp = m_scr_temp;
|
||||
state.pts = m_pts;
|
||||
state.pts_temp = m_pts_temp;
|
||||
state.dts = m_dts;
|
||||
state.dts_temp = m_dts_temp;
|
||||
state.scr_updated = m_scr_updated ? 1 : 0;
|
||||
state.pts_updated = m_pts_updated ? 1 : 0;
|
||||
state.dts_updated = m_dts_updated ? 1 : 0;
|
||||
state.program_end = m_program_end ? 1 : 0;
|
||||
}
|
||||
|
||||
void mpeg1_demuxer::set_state(const demux_state &state)
|
||||
{
|
||||
m_state = state.state;
|
||||
m_packet_body = state.packet_body != 0;
|
||||
m_length_decreasing = state.length_decreasing != 0;
|
||||
m_dts_present = state.dts_present != 0;
|
||||
m_packet_length = state.packet_length;
|
||||
m_scr = state.scr;
|
||||
m_scr_temp = state.scr_temp;
|
||||
m_pts = state.pts;
|
||||
m_pts_temp = state.pts_temp;
|
||||
m_dts = state.dts;
|
||||
m_dts_temp = state.dts_temp;
|
||||
m_scr_updated = state.scr_updated != 0;
|
||||
m_pts_updated = state.pts_updated != 0;
|
||||
m_dts_updated = state.dts_updated != 0;
|
||||
m_program_end = state.program_end != 0;
|
||||
}
|
||||
|
||||
// Helper for the 33-bit timestamp fields, which arrive as marker-separated
|
||||
// 3/15/15 bit groups spread over five bytes.
|
||||
static inline void put_bits(int64_t &value, int hi, int lo, uint8_t bits)
|
||||
{
|
||||
const int64_t mask = ((int64_t(1) << (hi - lo + 1)) - 1) << lo;
|
||||
value = (value & ~mask) | ((int64_t(bits) << lo) & mask);
|
||||
}
|
||||
|
||||
bool mpeg1_demuxer::feed(uint8_t data, uint8_t stream_filter)
|
||||
{
|
||||
// The decoders are gated by the packet-body flag as it stands *before*
|
||||
// this byte is processed, matching the registered output in the RTL.
|
||||
const bool body = m_packet_body;
|
||||
|
||||
m_scr_updated = m_pts_updated = m_dts_updated = m_program_end = false;
|
||||
|
||||
if (m_length_decreasing)
|
||||
{
|
||||
if (m_packet_length == 1)
|
||||
{
|
||||
m_length_decreasing = false;
|
||||
m_packet_body = false;
|
||||
}
|
||||
m_packet_length--;
|
||||
}
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case PACK5:
|
||||
m_state = IDLE;
|
||||
m_scr = m_scr_temp;
|
||||
m_scr_updated = true;
|
||||
break;
|
||||
|
||||
case PACK4: m_state = PACK5; put_bits(m_scr_temp, 6, 0, (data >> 1) & 0x7f); break;
|
||||
case PACK3: m_state = PACK4; put_bits(m_scr_temp, 14, 7, data); break;
|
||||
case PACK2: m_state = PACK3; put_bits(m_scr_temp, 21, 15, (data >> 1) & 0x7f); break;
|
||||
case PACK1: m_state = PACK2; put_bits(m_scr_temp, 29, 22, data); break;
|
||||
case PACK0: m_state = PACK1; put_bits(m_scr_temp, 32, 30, (data >> 1) & 0x07); break;
|
||||
|
||||
case PES8:
|
||||
// Only reached when a PTS was present; a DTS is optional but can
|
||||
// never occur without one.
|
||||
m_state = IDLE;
|
||||
m_pts = m_pts_temp;
|
||||
m_pts_updated = true;
|
||||
// Without a DTS the VMPEG uses the PTS in its place.
|
||||
m_dts = m_dts_present ? m_dts_temp : m_pts_temp;
|
||||
m_dts_updated = true;
|
||||
break;
|
||||
|
||||
case PES_DTS4:
|
||||
if ((data & 1))
|
||||
{
|
||||
put_bits(m_dts_temp, 6, 0, (data >> 1) & 0x7f);
|
||||
m_packet_body = true;
|
||||
m_state = PES8;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PES_DTS3: m_state = PES_DTS4; put_bits(m_dts_temp, 14, 7, data); break;
|
||||
|
||||
case PES_DTS2:
|
||||
if ((data & 1))
|
||||
{
|
||||
put_bits(m_dts_temp, 21, 15, (data >> 1) & 0x7f);
|
||||
m_state = PES_DTS3;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PES_DTS1: m_state = PES_DTS2; put_bits(m_dts_temp, 29, 22, data); break;
|
||||
|
||||
case PES_DTS0:
|
||||
if ((data & 0xf1) == 0x11)
|
||||
{
|
||||
put_bits(m_dts_temp, 32, 30, (data >> 1) & 0x07);
|
||||
m_state = PES_DTS1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PES7:
|
||||
if ((data & 1))
|
||||
{
|
||||
put_bits(m_pts_temp, 6, 0, (data >> 1) & 0x7f);
|
||||
if (m_dts_present)
|
||||
{
|
||||
m_state = PES_DTS0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_packet_body = true;
|
||||
m_state = PES8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PES6: m_state = PES7; put_bits(m_pts_temp, 14, 7, data); break;
|
||||
|
||||
case PES5:
|
||||
if ((data & 1))
|
||||
{
|
||||
put_bits(m_pts_temp, 21, 15, (data >> 1) & 0x7f);
|
||||
m_state = PES6;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PES4: m_state = PES5; put_bits(m_pts_temp, 29, 22, data); break;
|
||||
|
||||
case PES2:
|
||||
if ((data & 0xf1) == 0x21) // PTS only
|
||||
{
|
||||
put_bits(m_pts_temp, 32, 30, (data >> 1) & 0x07);
|
||||
m_dts_present = false;
|
||||
m_state = PES4;
|
||||
}
|
||||
else if ((data & 0xf1) == 0x31) // PTS and DTS
|
||||
{
|
||||
put_bits(m_pts_temp, 32, 30, (data >> 1) & 0x07);
|
||||
m_dts_present = true;
|
||||
m_state = PES4;
|
||||
}
|
||||
else if (data == 0x0f) // neither
|
||||
{
|
||||
m_state = IDLE;
|
||||
m_packet_body = true;
|
||||
}
|
||||
else if ((data & 0xc0) == 0x40) // STD buffer size, two bytes
|
||||
{
|
||||
m_state = PES3;
|
||||
}
|
||||
else if (data == 0xff) // stuffing
|
||||
{
|
||||
m_state = PES2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PES3:
|
||||
// ignore the second STD buffer size byte
|
||||
m_state = PES2;
|
||||
break;
|
||||
|
||||
case PES1:
|
||||
m_state = PES2;
|
||||
m_packet_length = (m_packet_length & 0xff00) | data;
|
||||
m_length_decreasing = true;
|
||||
break;
|
||||
|
||||
case PES0:
|
||||
m_state = PES1;
|
||||
m_packet_length = (m_packet_length & 0x00ff) | (uint16_t(data) << 8);
|
||||
break;
|
||||
|
||||
case MAGIC_MATCH:
|
||||
if (data == 0xba)
|
||||
{
|
||||
m_state = PACK0;
|
||||
}
|
||||
else if (data == 0xb9)
|
||||
{
|
||||
m_program_end = true;
|
||||
m_state = IDLE;
|
||||
}
|
||||
else if ((data & 0xf0) == m_stream_class)
|
||||
{
|
||||
// An elementary stream of the class this demuxer is filtering:
|
||||
// 0xCn for audio, 0xEn for video. The MiSTer RTL accepts both
|
||||
// classes in both instances, which with the usual stream number
|
||||
// of zero lets video packets fall into the audio path; each
|
||||
// instance only looks at its own class here.
|
||||
//
|
||||
// On a stream number mismatch the state machine stays parked in
|
||||
// MAGIC_MATCH rather than returning to IDLE, as in the RTL.
|
||||
if ((data & 0x0f) == stream_filter)
|
||||
m_state = PES0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case MAGIC2:
|
||||
if (data == 0x01)
|
||||
m_state = MAGIC_MATCH;
|
||||
else if (data == 0x00)
|
||||
m_state = MAGIC2;
|
||||
else
|
||||
m_state = IDLE;
|
||||
break;
|
||||
|
||||
case MAGIC0:
|
||||
m_state = (data == 0x00) ? MAGIC2 : IDLE;
|
||||
break;
|
||||
|
||||
case IDLE:
|
||||
default:
|
||||
// A start code prefix is only looked for outside of a packet body.
|
||||
if (data == 0x00 && !m_packet_body)
|
||||
m_state = MAGIC0;
|
||||
else
|
||||
m_state = IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// license:GPL-3.0-only
|
||||
// copyright-holders:Andre Zeps, vibecodekun
|
||||
/******************************************************************************
|
||||
|
||||
ISO/IEC 11172-1 (MPEG-1) program stream demultiplexer
|
||||
|
||||
A byte-at-a-time state machine, ported from the CDi_MiSTer VMPEG core by
|
||||
Andre Zeps (https://github.com/MiSTer-devel/CDi_MiSTer,
|
||||
rtl/mpeg/mpeg_demuxer.sv), which is licensed under the GNU General Public
|
||||
License version 3. The Digital Video Cartridge runs one instance
|
||||
for audio and one for video, each fed its own copy of the program stream by
|
||||
the driver, and each picking out the elementary stream it cares about.
|
||||
|
||||
Kept free of MAME dependencies so it can be tested against captured discs.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_MPEG1DEMUX_H
|
||||
#define MAME_MACHINE_MPEG1DEMUX_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class mpeg1_demuxer
|
||||
{
|
||||
public:
|
||||
// stream_class is 0xc0 for audio or 0xe0 for video.
|
||||
explicit mpeg1_demuxer(uint8_t stream_class = 0xe0) : m_stream_class(stream_class) { }
|
||||
|
||||
void set_stream_class(uint8_t stream_class) { m_stream_class = stream_class; }
|
||||
void reset();
|
||||
|
||||
// Feeds one byte. Returns true when that byte is part of the body of a
|
||||
// packet belonging to the selected stream, i.e. elementary stream data.
|
||||
bool feed(uint8_t data, uint8_t stream_filter);
|
||||
|
||||
// Timestamps carried by the stream, in 90 kHz ticks. The *_updated flags
|
||||
// refer to the most recent call to feed().
|
||||
int64_t system_clock_reference() const { return m_scr; }
|
||||
int64_t presentation_timestamp() const { return m_pts; }
|
||||
int64_t decoding_timestamp() const { return m_dts; }
|
||||
|
||||
bool scr_updated() const { return m_scr_updated; }
|
||||
bool pts_updated() const { return m_pts_updated; }
|
||||
bool dts_updated() const { return m_dts_updated; }
|
||||
bool program_end() const { return m_program_end; }
|
||||
|
||||
// The whole of the state machine, as plain data so the owning device can
|
||||
// save it. A demuxer restored mid-packet has to come back knowing how much
|
||||
// of the packet body is still to come, or it will hand the rest of the
|
||||
// packet to the decoder as though it were start codes.
|
||||
//
|
||||
// The stream class is configuration rather than state and is deliberately
|
||||
// left out; the owner sets it when it constructs the demuxer.
|
||||
struct demux_state
|
||||
{
|
||||
uint8_t state = IDLE;
|
||||
uint8_t packet_body = 0;
|
||||
uint8_t length_decreasing = 0;
|
||||
uint8_t dts_present = 0;
|
||||
uint16_t packet_length = 0;
|
||||
int64_t scr = 0, scr_temp = 0;
|
||||
int64_t pts = 0, pts_temp = 0;
|
||||
int64_t dts = 0, dts_temp = 0;
|
||||
uint8_t scr_updated = 0;
|
||||
uint8_t pts_updated = 0;
|
||||
uint8_t dts_updated = 0;
|
||||
uint8_t program_end = 0;
|
||||
};
|
||||
|
||||
void get_state(demux_state &state) const;
|
||||
void set_state(const demux_state &state);
|
||||
|
||||
private:
|
||||
enum state : uint8_t
|
||||
{
|
||||
IDLE, MAGIC0, MAGIC2, MAGIC_MATCH,
|
||||
PACK0, PACK1, PACK2, PACK3, PACK4, PACK5,
|
||||
PES0, PES1, PES2, PES3, PES4, PES5, PES6, PES7, PES8,
|
||||
PES_DTS0, PES_DTS1, PES_DTS2, PES_DTS3, PES_DTS4
|
||||
};
|
||||
|
||||
uint8_t m_stream_class;
|
||||
uint8_t m_state = IDLE;
|
||||
bool m_packet_body = false;
|
||||
bool m_length_decreasing = false;
|
||||
uint16_t m_packet_length = 0;
|
||||
bool m_dts_present = false;
|
||||
|
||||
int64_t m_scr = 0, m_scr_temp = 0;
|
||||
int64_t m_pts = 0, m_pts_temp = 0;
|
||||
int64_t m_dts = 0, m_dts_temp = 0;
|
||||
|
||||
bool m_scr_updated = false;
|
||||
bool m_pts_updated = false;
|
||||
bool m_dts_updated = false;
|
||||
bool m_program_end = false;
|
||||
};
|
||||
|
||||
#endif // MAME_MACHINE_MPEG1DEMUX_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,327 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:vibecodekun
|
||||
/******************************************************************************
|
||||
|
||||
ISO/IEC 11172-2 (MPEG-1) video decoder
|
||||
|
||||
Written for the CD-i Digital Video Cartridge, but deliberately free of any
|
||||
dependency on MAME so that it can be exercised standalone against captured
|
||||
streams.
|
||||
|
||||
The decoder is fed a demultiplexed video elementary stream through write()
|
||||
and decodes one picture per call to decode(). Pictures are only decoded
|
||||
once the whole picture is buffered, so the caller never has to deal with
|
||||
partially consumed input.
|
||||
|
||||
The variable-length code tables, the zig-zag order, the default quantiser
|
||||
matrices and the coefficient escape rules are all defined by the standard;
|
||||
they are transcribed here in the binary-tree form used by pl_mpeg, which
|
||||
the CDi_MiSTer project also uses for its decoder firmware. pl_mpeg's MIT
|
||||
license notice is reproduced in mpeg1video.cpp.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_MPEG1VIDEO_H
|
||||
#define MAME_MACHINE_MPEG1VIDEO_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace mpeg1_detail {
|
||||
|
||||
// A node of a canonical VLC tree: on each bit, either descend to another node
|
||||
// or emit a value. An index of 0 means the value is final, -1 that the code
|
||||
// cannot occur.
|
||||
struct vlc_node
|
||||
{
|
||||
int16_t index;
|
||||
int16_t value;
|
||||
};
|
||||
|
||||
struct vlc_node_u
|
||||
{
|
||||
int16_t index;
|
||||
uint16_t value;
|
||||
};
|
||||
|
||||
} // namespace mpeg1_detail
|
||||
|
||||
class mpeg1_video_decoder
|
||||
{
|
||||
public:
|
||||
mpeg1_video_decoder();
|
||||
|
||||
void reset();
|
||||
// Discard input and reference pictures while retaining sequence parameters
|
||||
// for a seek whose new position may not contain another sequence header.
|
||||
void clear_fifo();
|
||||
|
||||
// Feed demultiplexed elementary stream bytes.
|
||||
void write(const uint8_t *data, size_t length);
|
||||
|
||||
// Decode until one picture has been produced. Returns true if a picture
|
||||
// was decoded, false if more data is needed.
|
||||
bool decode();
|
||||
|
||||
bool has_sequence() const { return m_has_sequence; }
|
||||
int width() const { return m_width; }
|
||||
int height() const { return m_height; }
|
||||
|
||||
// Frame period of the coded sequence, in 90 kHz ticks, and the raw frame
|
||||
// rate index from the sequence header.
|
||||
uint16_t frame_period_90khz() const;
|
||||
uint8_t frame_rate_index() const { return m_frame_rate_index; }
|
||||
|
||||
// Picture type of the most recently decoded picture (1=I, 2=P, 3=B) and
|
||||
// its temporal reference.
|
||||
int picture_type() const { return m_picture_type; }
|
||||
int temporal_reference() const { return m_temporal_reference; }
|
||||
// VMPEG packs GOP seconds/pictures into the high word and hours/minutes
|
||||
// into the low word. Keep this metadata with each reordered picture.
|
||||
uint32_t decoded_timecode() const { return m_gop_timecode; }
|
||||
uint32_t display_timecode() const { return m_display_frame ? m_display_frame->timecode : 0; }
|
||||
|
||||
// True once a picture has been decoded that is ready to be shown. Frames
|
||||
// come out in display order, so an I or P picture is held back until the
|
||||
// following anchor picture arrives.
|
||||
bool has_display_frame() const { return m_display_frame != nullptr; }
|
||||
|
||||
// Single-picture/scan mode displays the decoded picture immediately,
|
||||
// without waiting for the next anchor to reorder an I or P picture.
|
||||
void display_decoded_frame();
|
||||
|
||||
// Picture type and temporal reference of the frame currently being shown,
|
||||
// which because of reordering is not the one most recently decoded.
|
||||
int display_picture_type() const { return m_display_frame ? m_display_frame->picture_type : 0; }
|
||||
int display_temporal_reference() const { return m_display_frame ? m_display_frame->temporal_reference : 0; }
|
||||
|
||||
// Converts the current display frame to packed 0x00RRGGBB. dst must have
|
||||
// room for width() * height() entries; stride is in pixels.
|
||||
void display_frame_rgb(uint32_t *dst, int stride) const;
|
||||
|
||||
// Raw planar access to the current display frame, for testing.
|
||||
const uint8_t *display_y() const;
|
||||
const uint8_t *display_cb() const;
|
||||
const uint8_t *display_cr() const;
|
||||
int luma_stride() const { return m_luma_width; }
|
||||
int chroma_stride() const { return m_chroma_width; }
|
||||
|
||||
// Number of whole pictures currently sitting in the input buffer, which
|
||||
// is what the cartridge reports to the driver.
|
||||
int pictures_buffered() const;
|
||||
|
||||
size_t buffered_bytes() const { return m_buffer.size() - m_read_pos; }
|
||||
|
||||
// Clamped so that the save state's fixed-size copy of the buffer can never
|
||||
// be too small for it. See decoder_state.
|
||||
void set_buffer_limit(size_t bytes)
|
||||
{
|
||||
m_buffer_limit = (bytes > decoder_state::max_buffer) ? decoder_state::max_buffer : bytes;
|
||||
}
|
||||
|
||||
// True while the buffer holds too much undecoded data to keep accepting
|
||||
// more, which is what the cartridge reports to the driver through SYS_STS.
|
||||
//
|
||||
// This is the live state of the buffer rather than a latch: the hardware
|
||||
// compares the FIFO level against a high-water mark continuously, so the
|
||||
// condition goes away again on its own once the decoder has drained enough.
|
||||
// The mark sits below the limit so that the driver is told to stop while
|
||||
// there is still room, and nothing has to be dropped. The reference FIFO
|
||||
// holds 32768 bytes and reports full above 28000 of them; the same fraction
|
||||
// is used here so that the headroom scales with the buffer.
|
||||
bool buffer_full() const { return buffered_bytes() > uint64_t(m_buffer_limit) * 28000 / 32768; }
|
||||
|
||||
// Set when a sequence end code has been seen.
|
||||
bool sequence_ended() const { return m_sequence_ended; }
|
||||
void clear_sequence_ended() { m_sequence_ended = false; }
|
||||
|
||||
// Everything a sequence header establishes. A stream only carries one
|
||||
// every so often, so a decoder that is started part way through a clip
|
||||
// (after a save state is restored, say) has to be told these separately or
|
||||
// it will discard pictures until the next header comes round, which may be
|
||||
// never. Kept as plain data so the owning device can save it.
|
||||
struct sequence_state
|
||||
{
|
||||
uint8_t valid = 0;
|
||||
uint16_t width = 0;
|
||||
uint16_t height = 0;
|
||||
uint8_t frame_rate_index = 0;
|
||||
uint8_t intra_quant[64] = { 0 };
|
||||
uint8_t non_intra_quant[64] = { 0 };
|
||||
};
|
||||
|
||||
void get_sequence_state(sequence_state &state) const;
|
||||
void set_sequence_state(const sequence_state &state);
|
||||
|
||||
// Everything else a save state has to carry.
|
||||
//
|
||||
// The bit reader, picture, slice and macroblock fields are deliberately
|
||||
// absent. decode() only ever returns with the bit position parked on a
|
||||
// start code - every early exit rewinds to one - so a picture is never half
|
||||
// decoded between calls and none of that state is live across a save.
|
||||
//
|
||||
// What cannot be reconstructed is the pair of reference pictures. Without
|
||||
// them the first P and B pictures after a load predict from whatever the
|
||||
// buffers happened to hold, which shows as a few frames of smeared blocks,
|
||||
// so they ride along in full.
|
||||
struct decoder_state
|
||||
{
|
||||
// The input buffer is capped at the largest the decoder will accept
|
||||
// (set_buffer_limit() clamps to this), so the copy can never truncate.
|
||||
// The frame cap covers the biggest picture a Green Book stream carries,
|
||||
// 384x280 rounded up to whole macroblocks. A larger picture still saves
|
||||
// and loads correctly, just without its reference pictures: frames_saved
|
||||
// comes back 0 and the stream resyncs on the next intra picture, which
|
||||
// is what the hardware does across a seek anyway.
|
||||
static constexpr size_t max_buffer = 512 * 1024;
|
||||
static constexpr size_t max_luma = 384 * 288;
|
||||
static constexpr size_t max_chroma = 192 * 144;
|
||||
|
||||
// Undecoded input, rebased so that the read position is byte 0.
|
||||
uint32_t buffer_size = 0;
|
||||
uint32_t bit_pos = 0; // bit offset into buffer[]
|
||||
uint8_t buffer[max_buffer] = { 0 };
|
||||
|
||||
uint8_t sequence_ended = 0;
|
||||
// Low three bits are picture type; the remaining bits carry the GOP
|
||||
// timecode shifted left by three. Its highest bit is bit 27, so this
|
||||
// fits a positive int32_t. Legacy states have zero in the unused bits
|
||||
// and recover timecodes when the next GOP header arrives. This retains
|
||||
// the existing save layout and all input-buffer capacity.
|
||||
int32_t picture_type = 0;
|
||||
int32_t temporal_reference = 0;
|
||||
|
||||
// Frame store. The four working pointers are saved as indices into
|
||||
// frames[], with -1 standing in for a null display frame.
|
||||
uint8_t frames_saved = 0;
|
||||
int32_t past_index = 1;
|
||||
int32_t future_index = 2;
|
||||
int32_t work_index = 0;
|
||||
int32_t display_index = -1;
|
||||
uint8_t frame_valid[3] = { 0, 0, 0 };
|
||||
int32_t frame_picture_type[3] = { 0, 0, 0 }; // same packed metadata
|
||||
int32_t frame_temporal_reference[3] = { 0, 0, 0 };
|
||||
uint8_t frame_y[3][max_luma] = { { 0 } };
|
||||
uint8_t frame_cb[3][max_chroma] = { { 0 } };
|
||||
uint8_t frame_cr[3][max_chroma] = { { 0 } };
|
||||
};
|
||||
|
||||
// Must be called after set_sequence_state(), which reallocates and clears
|
||||
// the frame store whenever the picture size changes.
|
||||
void get_decoder_state(decoder_state &state) const;
|
||||
void set_decoder_state(const decoder_state &state);
|
||||
|
||||
private:
|
||||
struct frame
|
||||
{
|
||||
std::vector<uint8_t> y, cb, cr;
|
||||
int temporal_reference = 0;
|
||||
uint32_t timecode = 0;
|
||||
int picture_type = 0;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
using vlc_node = mpeg1_detail::vlc_node;
|
||||
using vlc_node_u = mpeg1_detail::vlc_node_u;
|
||||
|
||||
// --- bit reading over the input buffer ---
|
||||
bool have_bits(size_t count) const;
|
||||
uint32_t read_bits(int count);
|
||||
int read_bit();
|
||||
void align_to_byte();
|
||||
int read_vlc(const vlc_node *table);
|
||||
uint16_t read_vlc_uint(const vlc_node_u *table);
|
||||
|
||||
// Finds the byte offset of the next 00 00 01 start code prefix at or after
|
||||
// `from`, or SIZE_MAX. Only searches whole bytes.
|
||||
size_t find_start_code(size_t from) const;
|
||||
// True when the next 23 bits are zero, i.e. a start code follows.
|
||||
bool at_start_code() const;
|
||||
|
||||
void discard_consumed();
|
||||
|
||||
bool decode_sequence_header();
|
||||
void decode_gop_header();
|
||||
bool decode_picture();
|
||||
void decode_slice(int vertical_position);
|
||||
void decode_macroblock();
|
||||
void decode_block(int block);
|
||||
void decode_motion_vector(int &dst, int motion, int r_size, int f);
|
||||
|
||||
// Stands in every macroblock from the one after the last that was written
|
||||
// up to and including `last`, by predicting it from the reference picture.
|
||||
void conceal_through(int last);
|
||||
|
||||
void predict_macroblock();
|
||||
void copy_macroblock(const frame &src, int motion_h, int motion_v, bool full_pel);
|
||||
void interpolate_macroblock(const frame &fwd, int fwd_h, int fwd_v, const frame &bwd, int bwd_h, int bwd_v);
|
||||
|
||||
void allocate_frames();
|
||||
void idct(int *block);
|
||||
|
||||
// --- input buffer ---
|
||||
std::vector<uint8_t> m_buffer;
|
||||
size_t m_read_pos = 0; // byte offset of the next unconsumed byte
|
||||
size_t m_bit_pos = 0; // absolute bit offset into m_buffer
|
||||
size_t m_buffer_limit = 512 * 1024;
|
||||
|
||||
// --- sequence state ---
|
||||
bool m_has_sequence = false;
|
||||
bool m_sequence_ended = false;
|
||||
int m_width = 0, m_height = 0;
|
||||
int m_mb_width = 0, m_mb_height = 0;
|
||||
int m_luma_width = 0, m_luma_height = 0;
|
||||
int m_chroma_width = 0, m_chroma_height = 0;
|
||||
uint8_t m_frame_rate_index = 0;
|
||||
uint8_t m_intra_quant[64];
|
||||
uint8_t m_non_intra_quant[64];
|
||||
|
||||
// --- picture state ---
|
||||
int m_picture_type = 0;
|
||||
int m_temporal_reference = 0;
|
||||
uint32_t m_gop_timecode = 0;
|
||||
int m_forward_f_code = 0, m_backward_f_code = 0;
|
||||
int m_forward_r_size = 0, m_backward_r_size = 0;
|
||||
int m_forward_f = 0, m_backward_f = 0;
|
||||
bool m_full_pel_forward = false, m_full_pel_backward = false;
|
||||
|
||||
// --- slice / macroblock state ---
|
||||
int m_quantizer_scale = 0;
|
||||
int m_macroblock_address = 0;
|
||||
// Set while the next macroblock is the first of a slice, whose address
|
||||
// increment is measured from the slice's vertical position rather than from
|
||||
// the macroblock before it. See decode_macroblock().
|
||||
bool m_slice_begin = false;
|
||||
// Set when a symbol turns up that cannot occur in a well-formed stream,
|
||||
// which means the bit reader has lost the slice. The rest of the slice is
|
||||
// abandoned and its macroblocks are concealed instead of being decoded from
|
||||
// what is no longer the right bit position. Purely per-slice, so it is not
|
||||
// part of decoder_state and does not affect the save state size.
|
||||
bool m_slice_error = false;
|
||||
int m_mb_row = 0, m_mb_col = 0;
|
||||
int m_macroblock_type = 0;
|
||||
bool m_macroblock_intra = false;
|
||||
int m_dc_predictor[3] = { 0, 0, 0 };
|
||||
int m_motion_fw_h = 0, m_motion_fw_v = 0;
|
||||
int m_motion_bw_h = 0, m_motion_bw_v = 0;
|
||||
bool m_has_forward = false, m_has_backward = false;
|
||||
int m_block_data[64];
|
||||
|
||||
// --- frames ---
|
||||
//
|
||||
// Pictures arrive in decode order but must be shown in display order, so
|
||||
// the two anchor pictures either side of any B pictures are both kept.
|
||||
// m_past is the older anchor, m_future the newer one, and m_work is the
|
||||
// picture currently being decoded. An anchor only becomes displayable once
|
||||
// the next anchor has been decoded.
|
||||
frame m_frames[3];
|
||||
frame *m_past = nullptr;
|
||||
frame *m_future = nullptr;
|
||||
frame *m_work = nullptr;
|
||||
frame *m_display_frame = nullptr;
|
||||
};
|
||||
|
||||
#endif // MAME_MACHINE_MPEG1VIDEO_H
|
||||
+58
-10
@@ -331,10 +331,37 @@ int mcd212_device::get_border_width()
|
||||
return width;
|
||||
}
|
||||
|
||||
uint32_t mcd212_device::get_backdrop_plane()
|
||||
// Samples the Digital Video Cartridge's picture across the line about to be
|
||||
// drawn. It is read up-front rather than from get_backdrop_plane() because the
|
||||
// plane decoders need to know where the cartridge's picture falls: see the
|
||||
// region-flag handling in process_vsr().
|
||||
void mcd212_device::update_ext_video_line()
|
||||
{
|
||||
m_ext_active = BIT(m_image_coding_method, ICM_EV_BIT) && !m_ext_video_cb.isnull();
|
||||
if (!m_ext_active)
|
||||
return;
|
||||
|
||||
const int width = get_screen_width();
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
const uint32_t pixel = m_ext_video_cb(x, m_backdrop_scanline);
|
||||
m_ext_present[x] = (pixel & 0xff000000) != 0;
|
||||
m_ext_pixel[x] = pixel & 0x00ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mcd212_device::get_backdrop_plane(int x)
|
||||
{
|
||||
if (BIT(m_image_coding_method, ICM_EV_BIT))
|
||||
return 0; // External Video Background. Default to Black since there is no DVC.
|
||||
{
|
||||
// External Video Background: this is where a Digital Video Cartridge
|
||||
// shows through. With no cartridge, or outside of its display window,
|
||||
// the backdrop is black - the CD-i's black, the same level the border
|
||||
// and a black plane pixel come out at, not zero.
|
||||
if (m_ext_active && m_ext_present[x])
|
||||
return m_ext_pixel[x];
|
||||
return CDI_BLACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only the low nibble of the register is the colour - intensity, R, G,
|
||||
@@ -571,6 +598,22 @@ void mcd212_device::process_vsr(uint32_t *pixels, bool *transparent)
|
||||
const bool is_dyuv_rgb = (icm == ICM_DYUV) || ((icm == ICM_RGB555) && (Path == 1)); // DYUV and RGB do not have access to color key.
|
||||
const bool use_color_key = !is_dyuv_rgb && ((tp_ctrl_type == TCR_KEY) || (tp_ctrl_type == TCR_MF0_KEY1) || (tp_ctrl_type == TCR_MF1_KEY1));
|
||||
|
||||
// With External Video selected, a region flag marks out where a Digital
|
||||
// Video Cartridge is keyed in, rather than keying the plane away outright.
|
||||
// A title that hands the whole line to the cartridge and then relies on the
|
||||
// cartridge only driving its own display window - as Mutant Rampage does for
|
||||
// the border it draws around its MPEG window - needs the plane back outside
|
||||
// that window, or the border disappears and the video is left floating on
|
||||
// black. Note the test is on the window being open, not on a picture being
|
||||
// available: the window keys the plane away just the same between clips,
|
||||
// which is why a stopped or skipped clip leaves a black void rather than
|
||||
// revealing the artwork the title parks behind it.
|
||||
const bool region_keys_ext = m_ext_active && use_matte_flag;
|
||||
const auto matte_transparent = [&](uint32_t px)
|
||||
{
|
||||
return (matte_flags[px] == tp_check_parity) && (!region_keys_ext || m_ext_present[px]);
|
||||
};
|
||||
|
||||
// A plane whose coding method is OFF is still a plane: it drives black into
|
||||
// the mixer, and whether that black is keyed away is decided by the plane's
|
||||
// own transparency control, exactly as it would be for a real picture. Only
|
||||
@@ -584,7 +627,7 @@ void mcd212_device::process_vsr(uint32_t *pixels, bool *transparent)
|
||||
{
|
||||
std::fill_n(pixels, width, s_4bpp_color[0]);
|
||||
for (int x = 0; x < width; x++)
|
||||
transparent[x] = tp_always || (use_matte_flag && (matte_flags[x] == tp_check_parity));
|
||||
transparent[x] = tp_always || (use_matte_flag && matte_transparent(x));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -633,10 +676,10 @@ void mcd212_device::process_vsr(uint32_t *pixels, bool *transparent)
|
||||
pixels[x + 1] = color0;
|
||||
pixels[x + 2] = color1;
|
||||
pixels[x + 3] = color1;
|
||||
transparent[x ] = tp_always || (use_matte_flag && (matte_flags[x ] == tp_check_parity));
|
||||
transparent[x + 1] = tp_always || (use_matte_flag && (matte_flags[x + 1] == tp_check_parity));
|
||||
transparent[x + 2] = tp_always || (use_matte_flag && (matte_flags[x + 2] == tp_check_parity));
|
||||
transparent[x + 3] = tp_always || (use_matte_flag && (matte_flags[x + 3] == tp_check_parity));
|
||||
transparent[x ] = tp_always || (use_matte_flag && matte_transparent(x ));
|
||||
transparent[x + 1] = tp_always || (use_matte_flag && matte_transparent(x + 1));
|
||||
transparent[x + 2] = tp_always || (use_matte_flag && matte_transparent(x + 2));
|
||||
transparent[x + 3] = tp_always || (use_matte_flag && matte_transparent(x + 3));
|
||||
x += 4;
|
||||
}
|
||||
else
|
||||
@@ -676,8 +719,8 @@ void mcd212_device::process_vsr(uint32_t *pixels, bool *transparent)
|
||||
{
|
||||
pixels[rl_index ] = color0;
|
||||
pixels[rl_index + 1] = color1;
|
||||
transparent[rl_index ] = tp_always || rgb_tp_bit || (use_color_key && color_match0) || (use_matte_flag && (matte_flags[rl_index ] == tp_check_parity));
|
||||
transparent[rl_index + 1] = tp_always || rgb_tp_bit || (use_color_key && color_match1) || (use_matte_flag && (matte_flags[rl_index + 1] == tp_check_parity));
|
||||
transparent[rl_index ] = tp_always || rgb_tp_bit || (use_color_key && color_match0) || (use_matte_flag && matte_transparent(rl_index ));
|
||||
transparent[rl_index + 1] = tp_always || rgb_tp_bit || (use_color_key && color_match1) || (use_matte_flag && matte_transparent(rl_index + 1));
|
||||
}
|
||||
x = end;
|
||||
}
|
||||
@@ -715,7 +758,7 @@ void mcd212_device::mix_lines(uint32_t *plane_a, bool *transparent_a, uint32_t *
|
||||
{
|
||||
if (transparent_a[x] && transparent_b[x])
|
||||
{
|
||||
out[x] = get_backdrop_plane();
|
||||
out[x] = get_backdrop_plane(x);
|
||||
continue;
|
||||
}
|
||||
uint32_t plane_a_cur = MosaicA ? plane_a[x - (x % mosaic_count_a)] : plane_a[x];
|
||||
@@ -1036,6 +1079,7 @@ uint32_t mcd212_device::screen_update(screen_device &screen, bitmap_rgb32 &bitma
|
||||
bool transparent_b[768];
|
||||
|
||||
int scanline = screen.vpos();
|
||||
m_backdrop_scanline = scanline - m_ica_height;
|
||||
|
||||
// Process VSR and mix if we're in the visible region
|
||||
if (scanline >= m_ica_height)
|
||||
@@ -1064,6 +1108,8 @@ uint32_t mcd212_device::screen_update(screen_device &screen, bitmap_rgb32 &bitma
|
||||
out += 24;
|
||||
}
|
||||
|
||||
update_ext_video_line();
|
||||
|
||||
process_vsr<0>(plane_a, transparent_a);
|
||||
process_vsr<1>(plane_b, transparent_b);
|
||||
|
||||
@@ -1241,6 +1287,7 @@ mcd212_device::mcd212_device(const machine_config &mconfig, const char *tag, dev
|
||||
: device_t(mconfig, MCD212, tag, owner, clock)
|
||||
, device_video_interface(mconfig, *this)
|
||||
, m_int_callback(*this)
|
||||
, m_ext_video_cb(*this)
|
||||
, m_planea(*this, finder_base::DUMMY_TAG)
|
||||
, m_planeb(*this, finder_base::DUMMY_TAG)
|
||||
{
|
||||
@@ -1255,6 +1302,7 @@ mcd212_device::mcd212_device(const machine_config &mconfig, const char *tag, dev
|
||||
void mcd212_device::device_resolve_objects()
|
||||
{
|
||||
m_int_callback.resolve_safe();
|
||||
m_ext_video_cb.resolve();
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
+32
-1
@@ -47,6 +47,23 @@ public:
|
||||
|
||||
auto int_callback() { return m_int_callback.bind(); }
|
||||
|
||||
// The CD-i's black. Everything this chip emits carries the 16-235 studio
|
||||
// swing: the 4bpp constants below run 0x10 to 0xe6, and mix_lines() takes
|
||||
// the pedestal off each plane before weighting it and puts it back
|
||||
// afterwards. Anything feeding pixels in from outside - the External Video
|
||||
// backdrop - has to speak the same units, or its black reads as a darker
|
||||
// hole in the picture.
|
||||
static constexpr uint32_t CDI_BLACK = 0x00101010;
|
||||
|
||||
// Supplies the External Video backdrop, i.e. the picture coming from a
|
||||
// Digital Video Cartridge. Called with the active-area pixel position;
|
||||
// returns 0 when the cartridge's display window does not cover that pixel,
|
||||
// or the colour with the alpha byte set when it does. A covered pixel with
|
||||
// no picture behind it comes back CDI_BLACK rather than unoccupied, so that
|
||||
// the window still keys the CD-i plane away between clips.
|
||||
typedef device_delegate<uint32_t (int, int)> ext_video_delegate;
|
||||
template <typename... T> void set_ext_video_callback(T &&... args) { m_ext_video_cb.set(std::forward<T>(args)...); }
|
||||
|
||||
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
|
||||
void map(address_map &map) ATTR_COLD;
|
||||
@@ -231,6 +248,9 @@ protected:
|
||||
// interrupt callbacks
|
||||
devcb_write_line m_int_callback;
|
||||
|
||||
// External Video source (Digital Video Cartridge)
|
||||
ext_video_delegate m_ext_video_cb;
|
||||
|
||||
required_shared_ptr<uint16_t> m_planea;
|
||||
required_shared_ptr<uint16_t> m_planeb;
|
||||
|
||||
@@ -259,7 +279,10 @@ protected:
|
||||
|
||||
int get_screen_width();
|
||||
int get_border_width();
|
||||
uint32_t get_backdrop_plane();
|
||||
uint32_t get_backdrop_plane(int x);
|
||||
void update_ext_video_line();
|
||||
|
||||
int m_backdrop_scanline = 0;
|
||||
|
||||
// The address the display is currently fetching each plane from.
|
||||
//
|
||||
@@ -277,6 +300,14 @@ protected:
|
||||
// leaving it out keeps existing save states loadable.
|
||||
uint32_t m_vsr_pos[2] = { 0, 0 };
|
||||
|
||||
// The external-video picture for the scanline being drawn, sampled once
|
||||
// before the planes are decoded. m_ext_present marks where the cartridge's
|
||||
// display window is open, not where it happens to have a picture. Scratch
|
||||
// for one line, so deliberately not part of the save state.
|
||||
uint32_t m_ext_pixel[768]{};
|
||||
bool m_ext_present[768]{};
|
||||
bool m_ext_active = false;
|
||||
|
||||
template <int Path> void set_vsr(uint32_t value);
|
||||
template <int Path> uint32_t get_vsr();
|
||||
|
||||
|
||||
Reference in new issue
Block a user