random new devices & soundcores for future use
This commit is contained in:
parent
69da115cd1
commit
74a5481e2b
|
@ -103,14 +103,14 @@ depobj = burn.o burn_bitmap.o burn_gun.o burn_led.o burn_shift.o burn_memory.o
|
|||
\
|
||||
6821pia.o 8255ppi.o 8257dma.o c169.o atariic.o atarijsa.o atarimo.o atarirle.o atarivad.o avgdvg.o bsmt2000.o decobsmt.o ds2404.o earom.o eeprom.o gaelco_crypt.o i4x00.o intelfsh.o \
|
||||
joyprocess.o nb1414m4.o nb1414m4_8bit.o nmk004.o nmk112.o k1ge.o kaneko_tmap.o mathbox.o mb87078.o mermaid.o midcsd.o midsat.o midsg.o midssio.o midtcs.o \
|
||||
namco_c45.o namcoio.o pandora.o poly.o qs1000.o resnet.o seibucop.o seibusnd.o sknsspr.o slapstic.o st0020.o t5182.o timekpr.o tlc34076.o tms34061.o v3021.o vdc.o \
|
||||
namco_c45.o namcoio.o pandora.o poly.o qs1000.o resnet.o rtc9701.o seibucop.o seibusnd.o serflash.o sknsspr.o slapstic.o st0020.o t5182.o timekpr.o tlc34076.o tms34061.o v3021.o vdc.o \
|
||||
tms9928a.o watchdog.o x2212.o \
|
||||
\
|
||||
asteroids.o ay8910.o burn_y8950.o burn_ym2151.o burn_ym2203.o burn_ym2413.o burn_ym2608.o burn_ym2610.o burn_ym2612.o burn_md2612.o \
|
||||
burn_ym3526.o burn_ym3812.o burn_ymf262.o burn_ymf271.o burn_ymf278b.o bzone.o c6280.o dac.o digitalk.o es5506.o es8712.o flower.o flt_rc.o fm.o fmopl.o ym2612.o gaelco.o hc55516.o \
|
||||
i5000.o ics2115.o iremga20.o k005289.o k007232.o k051649.o k053260.o k054539.o llander.o msm5205.o msm5232.o msm6295.o multipcm.o namco_snd.o c140.o c352.o nes_apu.o \
|
||||
i5000.o ics2115.o iremga20.o k005289.o k007232.o k051649.o k053260.o k054539.o llander.o mpeg_audio.o msm5205.o msm5232.o msm6295.o multipcm.o namco_snd.o c140.o c352.o nes_apu.o \
|
||||
t6w28.o tms5110.o tms5220.o tms36xx.o phoenixsound.o pleiadssound.o pokey.o redbaron.o rf5c68.o s14001a.o saa1099.o samples.o segapcm.o sn76477.o sn76496.o \
|
||||
upd7759.o vlm5030.o wiping.o x1010.o ym2151.o ym2413.o ymdeltat.o ymf262.o ymf271.o ymf278b.o ymz280b.o snk6502_sound.o sp0250.o sp0256.o \
|
||||
upd7759.o vlm5030.o wiping.o x1010.o ym2151.o ym2413.o ymdeltat.o ymf262.o ymf271.o ymf278b.o ymz280b.o ymz770.o snk6502_sound.o sp0250.o sp0256.o \
|
||||
\
|
||||
adsp2100.o adsp2100_intf.o arm7_intf.o arm_intf.o f8.o h6280_intf.o hd6309_intf.o konami_intf.o m6502_intf.o m6800_intf.o m6805_intf.o m6809_intf.o \
|
||||
m68000_intf.o mips3_intf.o nec_intf.o pic16c5x_intf.o s2650_intf.o tlcs90_intf.o tms34010.o tms34_intf.o z80_intf.o \
|
||||
|
|
|
@ -0,0 +1,458 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Angelo Salese, David Haywood
|
||||
/***************************************************************************
|
||||
|
||||
rtc9701.c
|
||||
|
||||
Epson RTC-9701-JE
|
||||
|
||||
Serial Real Time Clock + EEPROM
|
||||
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "burnint.h"
|
||||
#include "rtc9701.h"
|
||||
|
||||
enum {
|
||||
CMD_WAIT = 0,
|
||||
RTC_READ,
|
||||
RTC_WRITE,
|
||||
EEPROM_READ,
|
||||
EEPROM_WRITE,
|
||||
AFTER_WRITE_ENABLE
|
||||
};
|
||||
|
||||
struct regs_t
|
||||
{
|
||||
UINT8 sec, min, hour, day, wday, month, year;
|
||||
};
|
||||
|
||||
static int m_latch;
|
||||
static int m_reset_line;
|
||||
static int m_clock_line;
|
||||
|
||||
static UINT8 rtc_state;
|
||||
static int cmd_stream_pos;
|
||||
static int current_cmd;
|
||||
|
||||
static int rtc9701_address_pos;
|
||||
static int rtc9701_current_address;
|
||||
|
||||
static UINT16 rtc9701_current_data;
|
||||
static int rtc9701_data_pos;
|
||||
|
||||
static UINT16 rtc9701_data[0x100];
|
||||
|
||||
static regs_t m_rtc;
|
||||
|
||||
static UINT32 framenum;
|
||||
|
||||
void rtc9701_scan(INT32 nAction, INT32 *pnMin)
|
||||
{
|
||||
if (nAction & ACB_DRIVER_DATA) {
|
||||
SCAN_VAR(m_latch);
|
||||
SCAN_VAR(m_reset_line);
|
||||
SCAN_VAR(m_clock_line);
|
||||
SCAN_VAR(rtc_state);
|
||||
SCAN_VAR(cmd_stream_pos);
|
||||
SCAN_VAR(current_cmd);
|
||||
SCAN_VAR(rtc9701_address_pos);
|
||||
SCAN_VAR(rtc9701_current_address);
|
||||
SCAN_VAR(rtc9701_current_data);
|
||||
SCAN_VAR(rtc9701_data_pos);
|
||||
SCAN_VAR(m_rtc);
|
||||
SCAN_VAR(framenum);
|
||||
}
|
||||
|
||||
if (nAction & ACB_NVRAM) {
|
||||
SCAN_VAR(rtc9701_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rtc9701_once_per_frame()
|
||||
{
|
||||
framenum++;
|
||||
if ((framenum % 60) == 59)
|
||||
{
|
||||
static const UINT8 dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
|
||||
int dpm_count;
|
||||
|
||||
m_rtc.sec++;
|
||||
|
||||
if((m_rtc.sec & 0x0f) >= 0x0a) { m_rtc.sec+=0x10; m_rtc.sec&=0xf0; }
|
||||
if((m_rtc.sec & 0xf0) >= 0x60) { m_rtc.min++; m_rtc.sec = 0; }
|
||||
if((m_rtc.min & 0x0f) >= 0x0a) { m_rtc.min+=0x10; m_rtc.min&=0xf0; }
|
||||
if((m_rtc.min & 0xf0) >= 0x60) { m_rtc.hour++; m_rtc.min = 0; }
|
||||
if((m_rtc.hour & 0x0f) >= 0x0a) { m_rtc.hour+=0x10; m_rtc.hour&=0xf0; }
|
||||
if((m_rtc.hour & 0xff) >= 0x24) { m_rtc.day++; m_rtc.wday<<=1; m_rtc.hour = 0; }
|
||||
if(m_rtc.wday & 0x80) { m_rtc.wday = 1; }
|
||||
if((m_rtc.day & 0x0f) >= 0x0a) { m_rtc.day+=0x10; m_rtc.day&=0xf0; }
|
||||
|
||||
/* TODO: crude leap year support */
|
||||
dpm_count = (m_rtc.month & 0xf) + (((m_rtc.month & 0x10) >> 4)*10)-1;
|
||||
|
||||
if(((m_rtc.year % 4) == 0) && m_rtc.month == 2)
|
||||
{
|
||||
if((m_rtc.day & 0xff) >= dpm[dpm_count]+1+1)
|
||||
{ m_rtc.month++; m_rtc.day = 0x01; }
|
||||
}
|
||||
else if((m_rtc.day & 0xff) >= dpm[dpm_count]+1){ m_rtc.month++; m_rtc.day = 0x01; }
|
||||
if((m_rtc.month & 0x0f) >= 0x0a) { m_rtc.month = 0x10; }
|
||||
if(m_rtc.month >= 0x13) { m_rtc.year++; m_rtc.month = 1; }
|
||||
if((m_rtc.year & 0x0f) >= 0x0a) { m_rtc.year+=0x10; m_rtc.year&=0xf0; }
|
||||
if((m_rtc.year & 0xf0) >= 0xa0) { m_rtc.year = 0; } //2000-2099 possible timeframe
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void rtc9701_init()
|
||||
{
|
||||
tm time;
|
||||
BurnGetLocalTime(&time);
|
||||
|
||||
m_rtc.day = ((time.tm_mday / 10)<<4) | ((time.tm_mday % 10) & 0xf);
|
||||
m_rtc.month = (((time.tm_mon+1) / 10) << 4) | (((time.tm_mon+1) % 10) & 0xf);
|
||||
m_rtc.wday = 1 << time.tm_wday;
|
||||
m_rtc.year = (((time.tm_year % 100)/10)<<4) | ((time.tm_year % 10) & 0xf);
|
||||
m_rtc.hour = ((time.tm_hour / 10)<<4) | ((time.tm_hour % 10) & 0xf);
|
||||
m_rtc.min = ((time.tm_min / 10)<<4) | ((time.tm_min % 10) & 0xf);
|
||||
m_rtc.sec = ((time.tm_sec / 10)<<4) | ((time.tm_sec % 10) & 0xf);
|
||||
|
||||
rtc_state = CMD_WAIT;
|
||||
cmd_stream_pos = 0;
|
||||
current_cmd = 0;
|
||||
|
||||
framenum = 0;
|
||||
|
||||
for (INT32 offs = 0; offs < 0x100; offs++)
|
||||
rtc9701_data[offs] = 0xffff;
|
||||
}
|
||||
|
||||
void rtc9701_exit()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void rtc9701_reset()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rtc_read - used to route RTC reading registers
|
||||
//-------------------------------------------------
|
||||
|
||||
static UINT8 rtc_read(UINT8 offset)
|
||||
{
|
||||
UINT8 res;
|
||||
|
||||
res = 0;
|
||||
|
||||
switch(offset)
|
||||
{
|
||||
case 0: res = m_rtc.sec; break;
|
||||
case 1: res = m_rtc.min; break;
|
||||
case 2: res = m_rtc.hour; break;
|
||||
case 3: res = m_rtc.wday; break; /* untested */
|
||||
case 4: res = m_rtc.day; break;
|
||||
case 5: res = m_rtc.month; break;
|
||||
case 6: res = m_rtc.year & 0xff; break;
|
||||
case 7: res = 0x20; break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void rtc_write(UINT8 offset, UINT8 data)
|
||||
{
|
||||
switch(offset)
|
||||
{
|
||||
case 0: m_rtc.sec = data; break;
|
||||
case 1: m_rtc.min = data; break;
|
||||
case 2: m_rtc.hour = data; break;
|
||||
case 3: m_rtc.wday = data; break; /* untested */
|
||||
case 4: m_rtc.day = data; break;
|
||||
case 5: m_rtc.month = data; break;
|
||||
case 6: m_rtc.year = data; break;
|
||||
case 7: break; // NOP
|
||||
}
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// READ/WRITE HANDLERS
|
||||
//**************************************************************************
|
||||
|
||||
void rtc9701_write_bit(UINT8 data)
|
||||
{
|
||||
m_latch = data & 1;
|
||||
}
|
||||
|
||||
|
||||
UINT8 rtc9701_read_bit()
|
||||
{
|
||||
if (rtc_state == RTC_READ)
|
||||
{
|
||||
//printf("RTC data bits left c9701_data_pos %02x\n", rtc9701_data_pos);
|
||||
return ((rtc9701_current_data) >> (rtc9701_data_pos-1))&1;
|
||||
|
||||
}
|
||||
else if (rtc_state == EEPROM_READ)
|
||||
{
|
||||
//printf("EEPROM data bits left c9701_data_pos %02x\n", rtc9701_data_pos);
|
||||
return ((rtc9701_current_data) >> (rtc9701_data_pos-1))&1;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf("read something else (status?) %02x\n", rtc9701_data_pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void rtc9701_set_cs_line(UINT8 data)
|
||||
{
|
||||
//logerror("set reset line %d\n",data);
|
||||
m_reset_line = data;
|
||||
|
||||
if (m_reset_line != 0)
|
||||
{
|
||||
rtc_state = CMD_WAIT;
|
||||
cmd_stream_pos = 0;
|
||||
current_cmd = 0;
|
||||
rtc9701_address_pos = 0;
|
||||
rtc9701_current_address = 0;
|
||||
rtc9701_current_data = 0;
|
||||
rtc9701_data_pos = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rtc9701_set_clock_line(UINT8 data)
|
||||
{
|
||||
//logerror("set clock line %d\n",data);
|
||||
|
||||
if (m_reset_line == 0)
|
||||
{
|
||||
if (data==1)
|
||||
{
|
||||
//logerror("write latched bit %d\n",m_latch);
|
||||
|
||||
switch (rtc_state)
|
||||
{
|
||||
case CMD_WAIT:
|
||||
|
||||
//logerror("xx\n");
|
||||
current_cmd = (current_cmd << 1) | (m_latch&1);
|
||||
cmd_stream_pos++;
|
||||
|
||||
if (cmd_stream_pos==4)
|
||||
{
|
||||
cmd_stream_pos = 0;
|
||||
//logerror("Comamnd is %02x\n", current_cmd);
|
||||
|
||||
if (current_cmd==0x00) /* 0000 */
|
||||
{
|
||||
//logerror("WRITE RTC MODE\n");
|
||||
rtc_state = RTC_WRITE;
|
||||
cmd_stream_pos = 0;
|
||||
rtc9701_address_pos = 0;
|
||||
rtc9701_current_address = 0;
|
||||
rtc9701_data_pos = 0;
|
||||
rtc9701_current_data = 0;
|
||||
}
|
||||
else if (current_cmd==0x02) /* 0010 */
|
||||
{
|
||||
//logerror("WRITE EEPROM MODE\n");
|
||||
rtc_state = EEPROM_WRITE;
|
||||
cmd_stream_pos = 0;
|
||||
rtc9701_address_pos = 0;
|
||||
rtc9701_current_address = 0;
|
||||
rtc9701_data_pos = 0;
|
||||
rtc9701_current_data = 0;
|
||||
|
||||
}
|
||||
else if (current_cmd==0x06) /* 0110 */
|
||||
{
|
||||
//logerror("WRITE ENABLE\n");
|
||||
rtc_state = AFTER_WRITE_ENABLE;
|
||||
cmd_stream_pos = 0;
|
||||
}
|
||||
else if (current_cmd==0x08) /* 1000 */
|
||||
{
|
||||
//logerror("READ RTC MODE\n");
|
||||
rtc_state = RTC_READ;
|
||||
cmd_stream_pos = 0;
|
||||
rtc9701_address_pos = 0;
|
||||
rtc9701_current_address = 0;
|
||||
rtc9701_data_pos = 0;
|
||||
rtc9701_current_data = 0;
|
||||
}
|
||||
else if (current_cmd==0x0a) /* 1010 */
|
||||
{
|
||||
//logerror("READ EEPROM MODE\n");
|
||||
rtc_state = EEPROM_READ;
|
||||
cmd_stream_pos = 0;
|
||||
rtc9701_address_pos = 0;
|
||||
rtc9701_current_address = 0;
|
||||
rtc9701_data_pos = 0;
|
||||
rtc9701_current_data = 0;
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//logerror("RTC9701 UNKNOWN MODE\n");
|
||||
}
|
||||
|
||||
current_cmd = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case AFTER_WRITE_ENABLE:
|
||||
cmd_stream_pos++;
|
||||
if (cmd_stream_pos==12)
|
||||
{
|
||||
cmd_stream_pos = 0;
|
||||
//logerror("Written 12 bits, going back to WAIT mode\n");
|
||||
rtc_state = CMD_WAIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTC_WRITE:
|
||||
cmd_stream_pos++;
|
||||
if (cmd_stream_pos<=4)
|
||||
{
|
||||
rtc9701_address_pos++;
|
||||
rtc9701_current_address = (rtc9701_current_address << 1) | (m_latch&1);
|
||||
if (cmd_stream_pos==4)
|
||||
{
|
||||
//printf("Set RTC Write Address To %04x\n", rtc9701_current_address );
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_stream_pos>4)
|
||||
{
|
||||
rtc9701_data_pos++;
|
||||
rtc9701_current_data = (rtc9701_current_data << 1) | (m_latch&1);
|
||||
}
|
||||
|
||||
if (cmd_stream_pos==12)
|
||||
{
|
||||
cmd_stream_pos = 0;
|
||||
rtc_write(rtc9701_current_address,rtc9701_current_data);
|
||||
//logerror("Written 12 bits, going back to WAIT mode\n");
|
||||
rtc_state = CMD_WAIT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case EEPROM_READ:
|
||||
cmd_stream_pos++;
|
||||
if (cmd_stream_pos<=12)
|
||||
{
|
||||
rtc9701_address_pos++;
|
||||
rtc9701_current_address = (rtc9701_current_address << 1) | (m_latch&1);
|
||||
if (cmd_stream_pos==12)
|
||||
{
|
||||
//printf("Set EEPROM Read Address To %04x - ", (rtc9701_current_address>>1)&0xff );
|
||||
rtc9701_current_data = rtc9701_data[(rtc9701_current_address>>1)&0xff];
|
||||
//printf("Setting data latch for reading to %04x\n", rtc9701_current_data);
|
||||
rtc9701_data_pos = 16;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_stream_pos>12)
|
||||
{
|
||||
rtc9701_data_pos--;
|
||||
|
||||
}
|
||||
|
||||
if (cmd_stream_pos==28)
|
||||
{
|
||||
cmd_stream_pos = 0;
|
||||
// //logerror("accesed 28 bits, going back to WAIT mode\n");
|
||||
// rtc_state = CMD_WAIT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
case EEPROM_WRITE:
|
||||
cmd_stream_pos++;
|
||||
|
||||
if (cmd_stream_pos<=12)
|
||||
{
|
||||
rtc9701_address_pos++;
|
||||
rtc9701_current_address = (rtc9701_current_address << 1) | (m_latch&1);
|
||||
if (cmd_stream_pos==12)
|
||||
{
|
||||
//printf("Set EEPROM Write Address To %04x\n", rtc9701_current_address );
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_stream_pos>12)
|
||||
{
|
||||
rtc9701_data_pos++;
|
||||
rtc9701_current_data = (rtc9701_current_data << 1) | (m_latch&1);
|
||||
}
|
||||
|
||||
if (cmd_stream_pos==28)
|
||||
{
|
||||
cmd_stream_pos = 0;
|
||||
//printf("written 28 bits - writing data %04x to %04x and going back to WAIT mode\n", rtc9701_current_data, (rtc9701_current_address>>1)&0xff);
|
||||
rtc9701_data[(rtc9701_current_address>>1)&0xff] = rtc9701_current_data;
|
||||
rtc_state = CMD_WAIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTC_READ:
|
||||
cmd_stream_pos++;
|
||||
if (cmd_stream_pos<=4)
|
||||
{
|
||||
rtc9701_address_pos++;
|
||||
rtc9701_current_address = (rtc9701_current_address << 1) | (m_latch&1);
|
||||
if (cmd_stream_pos==4)
|
||||
{
|
||||
//printf("Set RTC Read Address To %04x\n", rtc9701_current_address );
|
||||
rtc9701_current_data = rtc_read(rtc9701_current_address);
|
||||
//printf("Setting data latch for reading to %04x\n", rtc9701_current_data);
|
||||
rtc9701_data_pos = 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_stream_pos>4)
|
||||
{
|
||||
rtc9701_data_pos--;
|
||||
}
|
||||
|
||||
if (cmd_stream_pos==12)
|
||||
{
|
||||
cmd_stream_pos = 0;
|
||||
// //logerror("accessed 12 bits, going back to WAIT mode\n");
|
||||
// rtc_state = CMD_WAIT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Angelo Salese, David Haywood
|
||||
/***************************************************************************
|
||||
|
||||
rtc9701.h
|
||||
|
||||
Serial rtc9701s.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
void rtc9701_init();
|
||||
void rtc9701_exit();
|
||||
void rtc9701_reset();
|
||||
void rtc9701_scan(INT32 nAction, INT32 *pnMin);
|
||||
|
||||
void rtc9701_once_per_frame();
|
||||
|
||||
void rtc9701_write_bit(UINT8 data);
|
||||
UINT8 rtc9701_read_bit();
|
||||
void rtc9701_set_cs_line(UINT8 data);
|
||||
void rtc9701_set_clock_line(UINT8 data);
|
||||
|
|
@ -0,0 +1,489 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Haywood, Luca Elia
|
||||
/* Serial Flash Device */
|
||||
|
||||
/* todo: cleanup, refactor etc. */
|
||||
/* ghosteo.c is similar? */
|
||||
|
||||
// **FBNeo port note**
|
||||
// Flash data saved to the device doesn't get saved or loaded, since what
|
||||
// we're using this for simply uses the flash chips as a ROM source.
|
||||
// - dink april 16, 2022
|
||||
|
||||
#include "burnint.h"
|
||||
#include "serflash.h"
|
||||
|
||||
|
||||
// runtime state
|
||||
enum { IDLE = 0, READ, READ_ID, READ_STATUS, BLOCK_ERASE, PAGE_PROGRAM };
|
||||
|
||||
static INT32 m_length;
|
||||
static UINT8* m_region;
|
||||
|
||||
static UINT32 m_row_num;
|
||||
static UINT16 m_flash_page_size;
|
||||
|
||||
static UINT8 m_flash_state;
|
||||
|
||||
static UINT8 m_flash_enab;
|
||||
|
||||
static UINT8 m_flash_cmd_seq;
|
||||
static UINT32 m_flash_cmd_prev;
|
||||
|
||||
static UINT8 m_flash_addr_seq;
|
||||
static UINT8 m_flash_read_seq;
|
||||
|
||||
static UINT32 m_flash_row;
|
||||
static UINT16 m_flash_col;
|
||||
static int m_flash_page_addr;
|
||||
static UINT32 m_flash_page_index;
|
||||
|
||||
static UINT8 *m_flashwritemap;
|
||||
|
||||
static UINT8 m_last_flash_cmd;
|
||||
|
||||
static UINT32 m_flash_addr;
|
||||
|
||||
static UINT8 *m_flash_page_data;
|
||||
|
||||
static void flash_change_state(UINT8 state); // forward
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void serflash_scan(INT32 nAction, INT32 *pnMin)
|
||||
{
|
||||
if (nAction & ACB_DRIVER_DATA) {
|
||||
SCAN_VAR(m_flash_state);
|
||||
SCAN_VAR(m_flash_enab);
|
||||
SCAN_VAR(m_flash_cmd_seq);
|
||||
SCAN_VAR(m_flash_cmd_prev);
|
||||
SCAN_VAR(m_flash_addr_seq);
|
||||
SCAN_VAR(m_flash_read_seq);
|
||||
SCAN_VAR(m_flash_row);
|
||||
SCAN_VAR(m_flash_col);
|
||||
SCAN_VAR(m_flash_page_addr);
|
||||
SCAN_VAR(m_flash_page_index);
|
||||
|
||||
ScanVar(m_flashwritemap, m_row_num, "FlashWriteMap");
|
||||
|
||||
SCAN_VAR(m_last_flash_cmd);
|
||||
|
||||
SCAN_VAR(m_flash_addr);
|
||||
|
||||
ScanVar(m_flash_page_data, m_flash_page_size, "FlashPageData");
|
||||
}
|
||||
}
|
||||
|
||||
void serflash_init(UINT8 *rom, INT32 length)
|
||||
{
|
||||
m_length = length;
|
||||
m_region = rom;
|
||||
|
||||
m_flash_page_size = 2048+64;
|
||||
m_row_num = m_length / m_flash_page_size;
|
||||
|
||||
m_flashwritemap = (UINT8*)BurnMalloc(m_row_num);
|
||||
memset(m_flashwritemap, 0, m_row_num);
|
||||
|
||||
m_flash_page_data = (UINT8*)BurnMalloc(m_flash_page_size);
|
||||
memset(m_flash_page_data, 0, m_flash_page_size);
|
||||
}
|
||||
|
||||
void serflash_exit()
|
||||
{
|
||||
BurnFree(m_flashwritemap);
|
||||
BurnFree(m_flash_page_data);
|
||||
}
|
||||
|
||||
static void serflash_hard_reset()
|
||||
{
|
||||
// logerror("%08x FLASH: RESET\n", cpuexec_describe_context(machine));
|
||||
|
||||
m_flash_state = READ;
|
||||
|
||||
m_flash_cmd_prev = -1;
|
||||
m_flash_cmd_seq = 0;
|
||||
|
||||
m_flash_addr_seq = 0;
|
||||
m_flash_read_seq = 0;
|
||||
|
||||
m_flash_row = 0;
|
||||
m_flash_col = 0;
|
||||
|
||||
memset(m_flash_page_data, 0, m_flash_page_size);
|
||||
m_flash_page_addr = 0;
|
||||
m_flash_page_index = 0;
|
||||
}
|
||||
|
||||
void serflash_reset()
|
||||
{
|
||||
m_flash_enab = 0;
|
||||
serflash_hard_reset();
|
||||
|
||||
m_last_flash_cmd = 0x00;
|
||||
m_flash_addr_seq = 0;
|
||||
m_flash_addr = 0;
|
||||
|
||||
m_flash_page_addr = 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_read - called to read SERFLASH from the
|
||||
// .nv file
|
||||
//-------------------------------------------------
|
||||
|
||||
void serflash_nvram_read()
|
||||
{
|
||||
#if 0
|
||||
if (m_length % m_flash_page_size) return; // region size must be multiple of flash page size
|
||||
int size = m_length / m_flash_page_size;
|
||||
|
||||
|
||||
if (file.is_open())
|
||||
{
|
||||
UINT32 page;
|
||||
file.read(&page, 4);
|
||||
while (page < size)
|
||||
{
|
||||
m_flashwritemap[page] = 1;
|
||||
file.read(m_region + page * m_flash_page_size, m_flash_page_size);
|
||||
file.read(&page, 4);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram_write - called to write SERFLASH to the
|
||||
// .nv file
|
||||
//-------------------------------------------------
|
||||
|
||||
void serflash_nvram_write()
|
||||
{
|
||||
#if 0
|
||||
if (m_length % m_flash_page_size) return; // region size must be multiple of flash page size
|
||||
int size = m_length / m_flash_page_size;
|
||||
|
||||
UINT32 page = 0;
|
||||
while (page < size)
|
||||
{
|
||||
if (m_flashwritemap[page])
|
||||
{
|
||||
file.write(&page, 4);
|
||||
file.write(m_region + page * m_flash_page_size, m_flash_page_size);
|
||||
}
|
||||
page++;
|
||||
}
|
||||
file.write(&page, 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
void serflash_enab_write(UINT8 data)
|
||||
{
|
||||
//logerror("%08x FLASH: enab = %02X\n", m_maincpu->pc(), data);
|
||||
m_flash_enab = data;
|
||||
}
|
||||
|
||||
static void flash_change_state(UINT8 state)
|
||||
{
|
||||
m_flash_state = state;
|
||||
|
||||
m_flash_cmd_prev = -1;
|
||||
m_flash_cmd_seq = 0;
|
||||
|
||||
m_flash_read_seq = 0;
|
||||
m_flash_addr_seq = 0;
|
||||
|
||||
//logerror("flash_change_state - FLASH: state = %s\n", m_flash_state_name[state]);
|
||||
}
|
||||
|
||||
void serflash_cmd_write(UINT8 data)
|
||||
{
|
||||
if (!m_flash_enab)
|
||||
return;
|
||||
|
||||
//logerror("%08x FLASH: cmd = %02X (prev = %02X)\n", m_maincpu->pc(), data, m_flash_cmd_prev);
|
||||
|
||||
if (m_flash_cmd_prev == -1)
|
||||
{
|
||||
m_flash_cmd_prev = data;
|
||||
|
||||
switch (data)
|
||||
{
|
||||
case 0x00: // READ
|
||||
m_flash_addr_seq = 0;
|
||||
break;
|
||||
|
||||
case 0x60: // BLOCK ERASE
|
||||
m_flash_addr_seq = 2; // row address only
|
||||
break;
|
||||
|
||||
case 0x70: // READ STATUS
|
||||
flash_change_state( READ_STATUS );
|
||||
break;
|
||||
|
||||
case 0x80: // PAGE / CACHE PROGRAM
|
||||
m_flash_addr_seq = 0;
|
||||
// this actually seems to be set with the next 2 writes?
|
||||
m_flash_page_addr = 0;
|
||||
break;
|
||||
|
||||
case 0x90: // READ ID
|
||||
flash_change_state( READ_ID );
|
||||
break;
|
||||
|
||||
case 0xff: // RESET
|
||||
flash_change_state( IDLE );
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
//logerror("%s FLASH: unknown cmd1 = %02X\n", machine().describe_context(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (m_flash_cmd_prev)
|
||||
{
|
||||
case 0x00: // READ
|
||||
if (data == 0x30)
|
||||
{
|
||||
if (m_flash_row < m_row_num)
|
||||
{
|
||||
memcpy(m_flash_page_data, m_region + m_flash_row * m_flash_page_size, m_flash_page_size);
|
||||
m_flash_page_addr = m_flash_col;
|
||||
m_flash_page_index = m_flash_row;
|
||||
}
|
||||
flash_change_state( READ );
|
||||
|
||||
//logerror("%08x FLASH: caching page = %04X\n", m_maincpu->pc(), m_flash_row);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x60: // BLOCK ERASE
|
||||
if (data==0xd0)
|
||||
{
|
||||
flash_change_state( BLOCK_ERASE );
|
||||
if (m_flash_row < m_row_num)
|
||||
{
|
||||
m_flashwritemap[m_flash_row] |= 1;
|
||||
memset(m_region + m_flash_col * m_flash_page_size, 0xff, m_flash_page_size);
|
||||
}
|
||||
//logerror("erased block %04x (%08x - %08x)\n", m_flash_col, m_flash_col * m_flash_page_size, ((m_flash_col+1) * m_flash_page_size)-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//logerror("unexpected 2nd command after BLOCK ERASE\n");
|
||||
}
|
||||
break;
|
||||
case 0x80:
|
||||
if (data==0x10)
|
||||
{
|
||||
flash_change_state( PAGE_PROGRAM );
|
||||
if (m_flash_row < m_row_num)
|
||||
{
|
||||
m_flashwritemap[m_flash_row] |= (memcmp(m_region + m_flash_row * m_flash_page_size, &m_flash_page_data[0], m_flash_page_size) != 0);
|
||||
memcpy(m_region + m_flash_row * m_flash_page_size, m_flash_page_data, m_flash_page_size);
|
||||
}
|
||||
//logerror("re-written block %04x (%08x - %08x)\n", m_flash_row, m_flash_row * m_flash_page_size, ((m_flash_row+1) * m_flash_page_size)-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//logerror("unexpected 2nd command after SPAGE PROGRAM\n");
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
//logerror("%08x FLASH: unknown cmd2 = %02X (cmd1 = %02X)\n", m_maincpu->pc(), data, m_flash_cmd_prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serflash_data_write(UINT8 data)
|
||||
{
|
||||
if (!m_flash_enab)
|
||||
return;
|
||||
|
||||
//logerror("flash data write %04x\n", m_flash_page_addr);
|
||||
if (m_flash_page_addr < m_flash_page_size)
|
||||
{
|
||||
m_flash_page_data[m_flash_page_addr] = data;
|
||||
}
|
||||
m_flash_page_addr++;
|
||||
}
|
||||
|
||||
void serflash_addr_write(UINT8 data)
|
||||
{
|
||||
if (!m_flash_enab)
|
||||
return;
|
||||
|
||||
//logerror("%08x FLASH: addr = %02X (seq = %02X)\n", m_maincpu->pc(), data, m_flash_addr_seq);
|
||||
|
||||
switch( m_flash_addr_seq++ )
|
||||
{
|
||||
case 0:
|
||||
m_flash_col = (m_flash_col & 0xff00) | data;
|
||||
break;
|
||||
case 1:
|
||||
m_flash_col = (m_flash_col & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 2:
|
||||
m_flash_row = (m_flash_row & 0xffff00) | data;
|
||||
if (m_row_num <= 256)
|
||||
{
|
||||
m_flash_addr_seq = 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
m_flash_row = (m_flash_row & 0xff00ff) | (data << 8);
|
||||
if (m_row_num <= 65536)
|
||||
{
|
||||
m_flash_addr_seq = 0;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
m_flash_row = (m_flash_row & 0x00ffff) | (data << 16);
|
||||
m_flash_addr_seq = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UINT8 serflash_io_read()
|
||||
{
|
||||
UINT8 data = 0x00;
|
||||
// UINT32 old;
|
||||
|
||||
if (!m_flash_enab)
|
||||
return 0xff;
|
||||
|
||||
switch (m_flash_state)
|
||||
{
|
||||
case READ_ID:
|
||||
//old = m_flash_read_seq;
|
||||
|
||||
switch( m_flash_read_seq++ )
|
||||
{
|
||||
case 0:
|
||||
data = 0xEC; // Manufacturer
|
||||
break;
|
||||
case 1:
|
||||
data = 0xF1; // Device
|
||||
break;
|
||||
case 2:
|
||||
data = 0x00; // XX
|
||||
break;
|
||||
case 3:
|
||||
data = 0x15; // Flags
|
||||
m_flash_read_seq = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
//logerror("%08x FLASH: read %02X from id(%02X)\n", m_maincpu->pc(), data, old);
|
||||
break;
|
||||
|
||||
case READ:
|
||||
if (m_flash_page_addr > m_flash_page_size-1)
|
||||
m_flash_page_addr = m_flash_page_size-1;
|
||||
|
||||
//old = m_flash_page_addr;
|
||||
|
||||
data = m_flash_page_data[m_flash_page_addr++];
|
||||
|
||||
//logerror("%08x FLASH: read data %02X from addr %03X (page %04X)\n", m_maincpu->pc(), data, old, m_flash_page_index);
|
||||
break;
|
||||
|
||||
case READ_STATUS:
|
||||
// bit 7 = writeable, bit 6 = ready, bit 5 = ready/true ready, bit 1 = fail(N-1), bit 0 = fail
|
||||
data = 0xe0;
|
||||
//logerror("%08x FLASH: read status %02X\n", m_maincpu->pc(), data);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// logerror("%08x FLASH: unknown read in state %s\n",0x00/*m_maincpu->pc()*/, m_flash_state_name[m_flash_state]);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
UINT8 serflash_ready_read()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UINT8 serflash_n3d_flash_read(INT32 offset)
|
||||
{
|
||||
if (m_last_flash_cmd==0x70) return 0xe0;
|
||||
|
||||
if (m_last_flash_cmd==0x00)
|
||||
{
|
||||
UINT8 retdat = m_flash_page_data[m_flash_page_addr];
|
||||
|
||||
//logerror("n3d_flash_r %02x %04x\n", offset, m_flash_page_addr);
|
||||
|
||||
m_flash_page_addr++;
|
||||
return retdat;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
|
||||
void serflash_n3d_flash_cmd_write(INT32 offset, UINT8 data)
|
||||
{
|
||||
m_last_flash_cmd = data;
|
||||
|
||||
if (data==0x00)
|
||||
{
|
||||
if (m_flash_addr < m_row_num)
|
||||
{
|
||||
memcpy(m_flash_page_data, m_region + m_flash_addr * m_flash_page_size, m_flash_page_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serflash_n3d_flash_addr_write(INT32 offset, UINT8 data)
|
||||
{
|
||||
// logerror("n3d_flash_addr_w %02x %02x\n", offset, data);
|
||||
|
||||
m_flash_addr_seq++;
|
||||
|
||||
if (m_flash_addr_seq==3)
|
||||
{
|
||||
m_flash_addr = (m_flash_addr & 0xffff00) | data;
|
||||
if (m_row_num <= 256)
|
||||
{
|
||||
m_flash_addr_seq = 0;
|
||||
m_flash_page_addr = 0;
|
||||
//logerror("set flash block to %08x\n", m_flash_addr);
|
||||
}
|
||||
}
|
||||
if (m_flash_addr_seq==4)
|
||||
{
|
||||
m_flash_addr = (m_flash_addr & 0xff00ff) | data << 8;
|
||||
if (m_row_num <= 65536)
|
||||
{
|
||||
m_flash_addr_seq = 0;
|
||||
m_flash_page_addr = 0;
|
||||
//logerror("set flash block to %08x\n", m_flash_addr);
|
||||
}
|
||||
}
|
||||
if (m_flash_addr_seq==5)
|
||||
{
|
||||
m_flash_addr = (m_flash_addr & 0x00ffff) | data << 16;
|
||||
m_flash_addr_seq = 0;
|
||||
m_flash_page_addr = 0;
|
||||
//logerror("set flash block to %08x\n", m_flash_addr);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Haywood, Luca Elia
|
||||
/* Serial Flash */
|
||||
|
||||
// **FBNeo port note**
|
||||
// Flash data saved to the device doesn't get saved or loaded, since what
|
||||
// we're using this for simply uses the flash chips as a ROM source.
|
||||
// - dink april 16, 2022
|
||||
|
||||
void serflash_init(UINT8 *rom, INT32 length);
|
||||
void serflash_exit();
|
||||
void serflash_reset();
|
||||
|
||||
void serflash_scan(INT32 nAction, INT32 *pnMin);
|
||||
|
||||
void serflash_enab_write(UINT8 data);
|
||||
void serflash_cmd_write(UINT8 data);
|
||||
void serflash_data_write(UINT8 data);
|
||||
void serflash_addr_write(UINT8 data);
|
||||
UINT8 serflash_io_read();
|
||||
UINT8 serflash_ready_read();
|
||||
|
||||
UINT8 serflash_n3d_flash_read(INT32 offset);
|
||||
void serflash_n3d_flash_cmd_write(INT32 offset, UINT8 data);
|
||||
void serflash_n3d_flash_addr_write(INT32 offset, UINT8 data);
|
||||
|
|
@ -0,0 +1,796 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
/***************************************************************************
|
||||
|
||||
MPEG audio support. Only layer2 and variants for now.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include "burnint.h"
|
||||
#include "mpeg_audio.h"
|
||||
|
||||
mpeg_audio::mpeg_audio(const void *_base, unsigned int _accepted, bool lsb_first, int _position_align)
|
||||
{
|
||||
base = (const UINT8 *)_base;
|
||||
accepted = _accepted;
|
||||
do_gb = lsb_first ? do_gb_lsb : do_gb_msb;
|
||||
position_align = _position_align ? _position_align - 1 : 0;
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
for (int j = 0; j < 32; j++)
|
||||
m_cos_cache[i][j] = cos(i*(2 * j + 1)*M_PI / 64);
|
||||
}
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
void mpeg_audio::clear()
|
||||
{
|
||||
memset(audio_buffer, 0, sizeof(audio_buffer));
|
||||
audio_buffer_pos[0] = 16*32;
|
||||
audio_buffer_pos[1] = 16*32;
|
||||
}
|
||||
|
||||
void mpeg_audio::scan()
|
||||
{
|
||||
SCAN_VAR(accepted);
|
||||
SCAN_VAR(position_align);
|
||||
|
||||
SCAN_VAR(sampling_rate);
|
||||
SCAN_VAR(last_frame_number);
|
||||
SCAN_VAR(param_index);
|
||||
|
||||
SCAN_VAR(channel_count);
|
||||
SCAN_VAR(total_bands);
|
||||
SCAN_VAR(joint_bands);
|
||||
|
||||
SCAN_VAR(band_param);
|
||||
SCAN_VAR(scfsi);
|
||||
SCAN_VAR(scf);
|
||||
SCAN_VAR(amp_values);
|
||||
SCAN_VAR(bdata);
|
||||
SCAN_VAR(subbuffer);
|
||||
SCAN_VAR(audio_buffer);
|
||||
SCAN_VAR(audio_buffer_pos);
|
||||
SCAN_VAR(m_cos_cache);
|
||||
|
||||
SCAN_VAR(current_pos);
|
||||
SCAN_VAR(current_limit);
|
||||
}
|
||||
|
||||
bool mpeg_audio::decode_buffer(int &pos, int limit, short *output,
|
||||
int &output_samples, int &sample_rate, int &channels)
|
||||
{
|
||||
if(limit - pos < 16)
|
||||
return false;
|
||||
|
||||
// Scan for the sync mark
|
||||
//
|
||||
// Avoid the exception dance at the point where going out of bound
|
||||
// is the most probable and easily avoidable
|
||||
|
||||
current_pos = pos;
|
||||
current_limit = limit;
|
||||
unsigned short sync = do_gb(base, current_pos, 12);
|
||||
|
||||
retry_sync:
|
||||
while(sync != 0xfff && current_pos < limit)
|
||||
sync = ((sync << 1) | do_gb(base, current_pos, 1)) & 0xfff;
|
||||
|
||||
if(limit - current_pos < 4)
|
||||
return false;
|
||||
|
||||
int layer = 0;
|
||||
int variant = do_gb(base, current_pos, 3);
|
||||
switch(variant) {
|
||||
case 2:
|
||||
if(accepted & L2_5)
|
||||
layer = 2;
|
||||
else if(accepted & AMM)
|
||||
layer = 4;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if(accepted & L3)
|
||||
layer = 3;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if(accepted & (L2|L2_5))
|
||||
layer = 2;
|
||||
else if(accepted & AMM)
|
||||
layer = 4;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
if(accepted & L1)
|
||||
layer = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!layer) {
|
||||
current_pos -= 3;
|
||||
sync = ((sync << 1) | do_gb(base, current_pos, 1)) & 0xfff;
|
||||
goto retry_sync;
|
||||
}
|
||||
|
||||
switch(layer) {
|
||||
case 1:
|
||||
abort();
|
||||
case 2:
|
||||
try {
|
||||
read_header_mpeg2(variant == 2);
|
||||
read_data_mpeg2();
|
||||
decode_mpeg2(output, output_samples);
|
||||
} catch(limit_hit) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
abort();
|
||||
case 4:
|
||||
try {
|
||||
read_header_amm(variant == 2);
|
||||
read_data_mpeg2();
|
||||
if(last_frame_number)
|
||||
decode_mpeg2(output, output_samples);
|
||||
} catch(limit_hit) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(position_align)
|
||||
current_pos = (current_pos + position_align) & ~position_align;
|
||||
|
||||
pos = current_pos;
|
||||
sample_rate = sample_rates[sampling_rate];
|
||||
channels = channel_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
void mpeg_audio::read_header_amm(bool layer25)
|
||||
{
|
||||
gb(1); // unused
|
||||
int full_packets_count = gb(4); // max 12
|
||||
int srate_index = gb(2); // max 2
|
||||
sampling_rate = srate_index + 4 * layer25;
|
||||
int last_packet_frame_id = gb(2); // max 2
|
||||
last_frame_number = 3*full_packets_count + last_packet_frame_id;
|
||||
int stereo_mode = gb(2);
|
||||
int stereo_mode_ext = gb(2);
|
||||
param_index = gb(3);
|
||||
gb(1); // must be zero
|
||||
|
||||
channel_count = stereo_mode != 3 ? 2 : 1;
|
||||
|
||||
total_bands = total_band_counts[param_index];
|
||||
joint_bands = total_bands;
|
||||
if(stereo_mode == 1) // joint stereo
|
||||
joint_bands = joint_band_counts[stereo_mode_ext];
|
||||
if(joint_bands > total_bands )
|
||||
joint_bands = total_bands;
|
||||
}
|
||||
|
||||
void mpeg_audio::read_header_mpeg2(bool layer25)
|
||||
{
|
||||
int prot = gb(1);
|
||||
int bitrate_index = gb(4);
|
||||
sampling_rate = gb(2);
|
||||
gb(1); // padding
|
||||
gb(1);
|
||||
last_frame_number = 36;
|
||||
int stereo_mode = gb(2);
|
||||
int stereo_mode_ext = gb(2);
|
||||
gb(2); // copyright, original
|
||||
gb(2); // emphasis
|
||||
if(!prot)
|
||||
gb(16); // crc
|
||||
|
||||
channel_count = stereo_mode != 3 ? 2 : 1;
|
||||
|
||||
param_index = layer2_param_index[channel_count-1][sampling_rate][bitrate_index];
|
||||
assert(param_index != -1);
|
||||
|
||||
total_bands = total_band_counts[param_index];
|
||||
joint_bands = total_bands;
|
||||
if(stereo_mode == 1) // joint stereo
|
||||
joint_bands = joint_band_counts[stereo_mode_ext];
|
||||
if(joint_bands > total_bands )
|
||||
joint_bands = total_bands;
|
||||
}
|
||||
|
||||
void mpeg_audio::read_data_mpeg2()
|
||||
{
|
||||
read_band_params();
|
||||
read_scfci();
|
||||
read_band_amplitude_params();
|
||||
}
|
||||
|
||||
void mpeg_audio::decode_mpeg2(short *output, int &output_samples)
|
||||
{
|
||||
output_samples = 0;
|
||||
build_amplitudes();
|
||||
|
||||
// Supposed to stop at last_frame_number when it's not 12*3+2 = 38
|
||||
int frame_number = 0;
|
||||
for(int upper_step = 0; upper_step<3; upper_step++)
|
||||
for(int middle_step = 0; middle_step < 4; middle_step++) {
|
||||
build_next_segments(upper_step);
|
||||
for(int lower_step = 0; lower_step < 3; lower_step++) {
|
||||
retrieve_subbuffer(lower_step);
|
||||
|
||||
for(int chan=0; chan<channel_count; chan++) {
|
||||
double resynthesis_buffer[32];
|
||||
idct32(subbuffer[chan], audio_buffer[chan] + audio_buffer_pos[chan]);
|
||||
resynthesis(audio_buffer[chan] + audio_buffer_pos[chan] + 16, resynthesis_buffer);
|
||||
scale_and_clamp(resynthesis_buffer, output + chan, channel_count);
|
||||
audio_buffer_pos[chan] -= 32;
|
||||
if(audio_buffer_pos[chan]<0) {
|
||||
memmove(audio_buffer[chan]+17*32, audio_buffer[chan], 15*32*sizeof(audio_buffer[chan][0]));
|
||||
audio_buffer_pos[chan] = 16*32;
|
||||
}
|
||||
}
|
||||
output += 32*channel_count;
|
||||
output_samples += 32;
|
||||
frame_number++;
|
||||
if(frame_number == last_frame_number)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int mpeg_audio::sample_rates[8] = { 44100, 48000, 32000, 0, 22050, 24000, 16000, 0 };
|
||||
|
||||
const int mpeg_audio::layer2_param_index[2][4][16] = {
|
||||
{
|
||||
{ 1, 2, 2, 0, 0, 0, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 },
|
||||
{ 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1 },
|
||||
{ 1, 3, 3, 0, 0, 0, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 },
|
||||
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
},
|
||||
{
|
||||
{ 1, -1, -1, -1, 2, -1, 2, 0, 0, 0, 1, 1, 1, 1, 1, -1 },
|
||||
{ 0, -1, -1, -1, 2, -1, 2, 0, 0, 0, 0, 0, 0, 0, 0, -1 },
|
||||
{ 1, -1, -1, -1, 3, -1, 3, 0, 0, 0, 1, 1, 1, 1, 1, -1 },
|
||||
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||
}
|
||||
};
|
||||
|
||||
const int mpeg_audio::band_parameter_indexed_values[5][32][17] = {
|
||||
{
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
},
|
||||
{
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
},
|
||||
{
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
},
|
||||
{
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
},
|
||||
{
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
{ 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
|
||||
}
|
||||
};
|
||||
|
||||
const int mpeg_audio::band_parameter_index_bits_count[5][32] = {
|
||||
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 0, 0, 0, 0, 0, },
|
||||
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0, 0, },
|
||||
{ 4, 4, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
|
||||
{ 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, },
|
||||
};
|
||||
|
||||
const int mpeg_audio::total_band_counts[5] = { 27, 30, 8, 12, 30 };
|
||||
|
||||
const int mpeg_audio::joint_band_counts[4] = { 4, 8, 12, 16 };
|
||||
|
||||
const mpeg_audio::band_info mpeg_audio::band_infos[18] = {
|
||||
{ 0x0000, 0.00, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0x0003, 7.00, 2, 5, 3, 9, 1-1.0/ 4, -1.0/ 4, 1/(1-1.0/ 4), 1.0/ 2 },
|
||||
{ 0x0005, 11.00, 3, 7, 5, 25, 1-3.0/ 8, -3.0/ 8, 1/(1-3.0/ 8), 1.0/ 2 },
|
||||
{ 0x0007, 16.00, 3, 9, 0, 0, 1-1.0/ 8, -1.0/ 8, 1/(1-1.0/ 8), 1.0/ 4 },
|
||||
{ 0x0009, 20.84, 4, 10, 9, 81, 1-7.0/ 16, -7.0/ 16, 1/(1-7.0/ 16), 1.0/ 2 },
|
||||
{ 0x000f, 25.28, 4, 12, 0, 0, 1-1.0/ 16, -1.0/ 16, 1/(1-1.0/ 16), 1.0/ 8 },
|
||||
{ 0x001f, 31.59, 5, 15, 0, 0, 1-1.0/ 32, -1.0/ 32, 1/(1-1.0/ 32), 1.0/ 16 },
|
||||
{ 0x003f, 37.75, 6, 18, 0, 0, 1-1.0/ 64, -1.0/ 64, 1/(1-1.0/ 64), 1.0/ 32 },
|
||||
{ 0x007f, 43.84, 7, 21, 0, 0, 1-1.0/ 128, -1.0/ 128, 1/(1-1.0/ 128), 1.0/ 64 },
|
||||
{ 0x00ff, 49.89, 8, 24, 0, 0, 1-1.0/ 256, -1.0/ 256, 1/(1-1.0/ 256), 1.0/ 128 },
|
||||
{ 0x01ff, 55.93, 9, 27, 0, 0, 1-1.0/ 512, -1.0/ 512, 1/(1-1.0/ 512), 1.0/ 256 },
|
||||
{ 0x03ff, 61.96, 10, 30, 0, 0, 1-1.0/ 1024, -1.0/ 1024, 1/(1-1.0/ 1024), 1.0/ 512 },
|
||||
{ 0x07ff, 67.98, 11, 33, 0, 0, 1-1.0/ 2048, -1.0/ 2048, 1/(1-1.0/ 2048), 1.0/ 1024 },
|
||||
{ 0x0fff, 74.01, 12, 36, 0, 0, 1-1.0/ 4096, -1.0/ 4096, 1/(1-1.0/ 4096), 1.0/ 2048 },
|
||||
{ 0x1fff, 80.03, 13, 39, 0, 0, 1-1.0/ 8192, -1.0/ 8192, 1/(1-1.0/ 8192), 1.0/ 4096 },
|
||||
{ 0x3fff, 86.05, 14, 42, 0, 0, 1-1.0/16384, -1.0/16384, 1/(1-1.0/16384), 1.0/ 8192 },
|
||||
{ 0x7fff, 92.01, 15, 45, 0, 0, 1-1.0/32768, -1.0/32768, 1/(1-1.0/32768), 1.0/16384 },
|
||||
{ 0xffff, 98.01, 16, 48, 0, 0, 1-1.0/65536, -1.0/65536, 1/(1-1.0/65536), 1.0/32768 },
|
||||
};
|
||||
|
||||
const double mpeg_audio::scalefactors[64] = {
|
||||
2.00000000000000, 1.58740105196820, 1.25992104989487, 1.00000000000000,
|
||||
0.79370052598410, 0.62996052494744, 0.50000000000000, 0.39685026299205,
|
||||
0.31498026247372, 0.25000000000000, 0.19842513149602, 0.15749013123686,
|
||||
0.12500000000000, 0.09921256574801, 0.07874506561843, 0.06250000000000,
|
||||
0.04960628287401, 0.03937253280921, 0.03125000000000, 0.02480314143700,
|
||||
0.01968626640461, 0.01562500000000, 0.01240157071850, 0.00984313320230,
|
||||
0.00781250000000, 0.00620078535925, 0.00492156660115, 0.00390625000000,
|
||||
0.00310039267963, 0.00246078330058, 0.00195312500000, 0.00155019633981,
|
||||
0.00123039165029, 0.00097656250000, 0.00077509816991, 0.00061519582514,
|
||||
0.00048828125000, 0.00038754908495, 0.00030759791257, 0.00024414062500,
|
||||
0.00019377454248, 0.00015379895629, 0.00012207031250, 0.00009688727124,
|
||||
0.00007689947814, 0.00006103515625, 0.00004844363562, 0.00003844973907,
|
||||
0.00003051757812, 0.00002422181781, 0.00001922486954, 0.00001525878906,
|
||||
0.00001211090890, 0.00000961243477, 0.00000762939453, 0.00000605545445,
|
||||
0.00000480621738, 0.00000381469727, 0.00000302772723, 0.00000240310869,
|
||||
0.00000190734863, 0.00000151386361, 0.00000120155435, 0.00000000000000
|
||||
};
|
||||
|
||||
const double mpeg_audio::synthesis_filter[512] = {
|
||||
+0.000000000, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000015259, -0.000030518,
|
||||
-0.000030518, -0.000030518, -0.000030518, -0.000045776, -0.000045776, -0.000061035, -0.000061035, -0.000076294,
|
||||
-0.000076294, -0.000091553, -0.000106812, -0.000106812, -0.000122070, -0.000137329, -0.000152588, -0.000167847,
|
||||
-0.000198364, -0.000213623, -0.000244141, -0.000259399, -0.000289917, -0.000320435, -0.000366211, -0.000396729,
|
||||
-0.000442505, -0.000473022, -0.000534058, -0.000579834, -0.000625610, -0.000686646, -0.000747681, -0.000808716,
|
||||
-0.000885010, -0.000961304, -0.001037598, -0.001113892, -0.001205444, -0.001296997, -0.001388550, -0.001480103,
|
||||
-0.001586914, -0.001693726, -0.001785278, -0.001907349, -0.002014160, -0.002120972, -0.002243042, -0.002349854,
|
||||
-0.002456665, -0.002578735, -0.002685547, -0.002792358, -0.002899170, -0.002990723, -0.003082275, -0.003173828,
|
||||
+0.003250122, +0.003326416, +0.003387451, +0.003433228, +0.003463745, +0.003479004, +0.003479004, +0.003463745,
|
||||
+0.003417969, +0.003372192, +0.003280640, +0.003173828, +0.003051758, +0.002883911, +0.002700806, +0.002487183,
|
||||
+0.002227783, +0.001937866, +0.001617432, +0.001266479, +0.000869751, +0.000442505, -0.000030518, -0.000549316,
|
||||
-0.001098633, -0.001693726, -0.002334595, -0.003005981, -0.003723145, -0.004486084, -0.005294800, -0.006118774,
|
||||
-0.007003784, -0.007919312, -0.008865356, -0.009841919, -0.010848999, -0.011886597, -0.012939453, -0.014022827,
|
||||
-0.015121460, -0.016235352, -0.017349243, -0.018463135, -0.019577026, -0.020690918, -0.021789550, -0.022857666,
|
||||
-0.023910522, -0.024932861, -0.025909424, -0.026840210, -0.027725220, -0.028533936, -0.029281616, -0.029937744,
|
||||
-0.030532837, -0.031005860, -0.031387330, -0.031661987, -0.031814575, -0.031845093, -0.031738280, -0.031478880,
|
||||
+0.031082153, +0.030517578, +0.029785156, +0.028884888, +0.027801514, +0.026535034, +0.025085450, +0.023422241,
|
||||
+0.021575928, +0.019531250, +0.017257690, +0.014801025, +0.012115479, +0.009231567, +0.006134033, +0.002822876,
|
||||
-0.000686646, -0.004394531, -0.008316040, -0.012420654, -0.016708374, -0.021179200, -0.025817871, -0.030609130,
|
||||
-0.035552980, -0.040634155, -0.045837402, -0.051132202, -0.056533813, -0.061996460, -0.067520140, -0.073059080,
|
||||
-0.078628540, -0.084182740, -0.089706420, -0.095169070, -0.100540160, -0.105819700, -0.110946655, -0.115921020,
|
||||
-0.120697020, -0.125259400, -0.129562380, -0.133590700, -0.137298580, -0.140670780, -0.143676760, -0.146255500,
|
||||
-0.148422240, -0.150115970, -0.151306150, -0.151962280, -0.152069090, -0.151596070, -0.150497440, -0.148773200,
|
||||
-0.146362300, -0.143264770, -0.139450070, -0.134887700, -0.129577640, -0.123474120, -0.116577150, -0.108856200,
|
||||
+0.100311280, +0.090927124, +0.080688480, +0.069595340, +0.057617188, +0.044784546, +0.031082153, +0.016510010,
|
||||
+0.001068115, -0.015228271, -0.032379150, -0.050354004, -0.069168090, -0.088775635, -0.109161380, -0.130310060,
|
||||
-0.152206420, -0.174789430, -0.198059080, -0.221984860, -0.246505740, -0.271591200, -0.297210700, -0.323318480,
|
||||
-0.349868770, -0.376800540, -0.404083250, -0.431655880, -0.459472660, -0.487472530, -0.515609740, -0.543823240,
|
||||
-0.572036740, -0.600219700, -0.628295900, -0.656219500, -0.683914200, -0.711318970, -0.738372800, -0.765029900,
|
||||
-0.791214000, -0.816864000, -0.841949460, -0.866363500, -0.890090940, -0.913055400, -0.935195900, -0.956481930,
|
||||
-0.976852400, -0.996246340, -1.014617900, -1.031936600, -1.048156700, -1.063217200, -1.077117900, -1.089782700,
|
||||
-1.101211500, -1.111373900, -1.120224000, -1.127746600, -1.133926400, -1.138763400, -1.142211900, -1.144287100,
|
||||
+1.144989000, +1.144287100, +1.142211900, +1.138763400, +1.133926400, +1.127746600, +1.120224000, +1.111373900,
|
||||
+1.101211500, +1.089782700, +1.077117900, +1.063217200, +1.048156700, +1.031936600, +1.014617900, +0.996246340,
|
||||
+0.976852400, +0.956481930, +0.935195900, +0.913055400, +0.890090940, +0.866363500, +0.841949460, +0.816864000,
|
||||
+0.791214000, +0.765029900, +0.738372800, +0.711318970, +0.683914200, +0.656219500, +0.628295900, +0.600219700,
|
||||
+0.572036740, +0.543823240, +0.515609740, +0.487472530, +0.459472660, +0.431655880, +0.404083250, +0.376800540,
|
||||
+0.349868770, +0.323318480, +0.297210700, +0.271591200, +0.246505740, +0.221984860, +0.198059080, +0.174789430,
|
||||
+0.152206420, +0.130310060, +0.109161380, +0.088775635, +0.069168090, +0.050354004, +0.032379150, +0.015228271,
|
||||
-0.001068115, -0.016510010, -0.031082153, -0.044784546, -0.057617188, -0.069595340, -0.080688480, -0.090927124,
|
||||
+0.100311280, +0.108856200, +0.116577150, +0.123474120, +0.129577640, +0.134887700, +0.139450070, +0.143264770,
|
||||
+0.146362300, +0.148773200, +0.150497440, +0.151596070, +0.152069090, +0.151962280, +0.151306150, +0.150115970,
|
||||
+0.148422240, +0.146255500, +0.143676760, +0.140670780, +0.137298580, +0.133590700, +0.129562380, +0.125259400,
|
||||
+0.120697020, +0.115921020, +0.110946655, +0.105819700, +0.100540160, +0.095169070, +0.089706420, +0.084182740,
|
||||
+0.078628540, +0.073059080, +0.067520140, +0.061996460, +0.056533813, +0.051132202, +0.045837402, +0.040634155,
|
||||
+0.035552980, +0.030609130, +0.025817871, +0.021179200, +0.016708374, +0.012420654, +0.008316040, +0.004394531,
|
||||
+0.000686646, -0.002822876, -0.006134033, -0.009231567, -0.012115479, -0.014801025, -0.017257690, -0.019531250,
|
||||
-0.021575928, -0.023422241, -0.025085450, -0.026535034, -0.027801514, -0.028884888, -0.029785156, -0.030517578,
|
||||
+0.031082153, +0.031478880, +0.031738280, +0.031845093, +0.031814575, +0.031661987, +0.031387330, +0.031005860,
|
||||
+0.030532837, +0.029937744, +0.029281616, +0.028533936, +0.027725220, +0.026840210, +0.025909424, +0.024932861,
|
||||
+0.023910522, +0.022857666, +0.021789550, +0.020690918, +0.019577026, +0.018463135, +0.017349243, +0.016235352,
|
||||
+0.015121460, +0.014022827, +0.012939453, +0.011886597, +0.010848999, +0.009841919, +0.008865356, +0.007919312,
|
||||
+0.007003784, +0.006118774, +0.005294800, +0.004486084, +0.003723145, +0.003005981, +0.002334595, +0.001693726,
|
||||
+0.001098633, +0.000549316, +0.000030518, -0.000442505, -0.000869751, -0.001266479, -0.001617432, -0.001937866,
|
||||
-0.002227783, -0.002487183, -0.002700806, -0.002883911, -0.003051758, -0.003173828, -0.003280640, -0.003372192,
|
||||
-0.003417969, -0.003463745, -0.003479004, -0.003479004, -0.003463745, -0.003433228, -0.003387451, -0.003326416,
|
||||
+0.003250122, +0.003173828, +0.003082275, +0.002990723, +0.002899170, +0.002792358, +0.002685547, +0.002578735,
|
||||
+0.002456665, +0.002349854, +0.002243042, +0.002120972, +0.002014160, +0.001907349, +0.001785278, +0.001693726,
|
||||
+0.001586914, +0.001480103, +0.001388550, +0.001296997, +0.001205444, +0.001113892, +0.001037598, +0.000961304,
|
||||
+0.000885010, +0.000808716, +0.000747681, +0.000686646, +0.000625610, +0.000579834, +0.000534058, +0.000473022,
|
||||
+0.000442505, +0.000396729, +0.000366211, +0.000320435, +0.000289917, +0.000259399, +0.000244141, +0.000213623,
|
||||
+0.000198364, +0.000167847, +0.000152588, +0.000137329, +0.000122070, +0.000106812, +0.000106812, +0.000091553,
|
||||
+0.000076294, +0.000076294, +0.000061035, +0.000061035, +0.000045776, +0.000045776, +0.000030518, +0.000030518,
|
||||
+0.000030518, +0.000030518, +0.000015259, +0.000015259, +0.000015259, +0.000015259, +0.000015259, +0.000015259,
|
||||
};
|
||||
|
||||
int mpeg_audio::do_gb_msb(const unsigned char *data, int &pos, int count)
|
||||
{
|
||||
int v = 0;
|
||||
for(int i=0; i != count; i++) {
|
||||
v <<= 1;
|
||||
if(data[pos >> 3] & (0x80 >> (pos & 7)))
|
||||
v |= 1;
|
||||
pos++;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int mpeg_audio::do_gb_lsb(const unsigned char *data, int &pos, int count)
|
||||
{
|
||||
int v = 0;
|
||||
for(int i=0; i != count; i++) {
|
||||
v <<= 1;
|
||||
if(data[pos >> 3] & (0x01 << (pos & 7)))
|
||||
v |= 1;
|
||||
pos++;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int mpeg_audio::get_band_param(int band)
|
||||
{
|
||||
int bit_count = band_parameter_index_bits_count[param_index][band];
|
||||
int index = gb(bit_count);
|
||||
return band_parameter_indexed_values[param_index][band][index];
|
||||
}
|
||||
|
||||
void mpeg_audio::read_band_params()
|
||||
{
|
||||
int band = 0;
|
||||
|
||||
while(band < joint_bands) {
|
||||
for(int chan=0; chan < channel_count; chan++)
|
||||
band_param[chan][band] = get_band_param(band);
|
||||
band++;
|
||||
}
|
||||
|
||||
while(band < total_bands) {
|
||||
int val = get_band_param(band);
|
||||
band_param[0][band] = val;
|
||||
band_param[1][band] = val;
|
||||
band++;
|
||||
}
|
||||
|
||||
while(band < 32) {
|
||||
band_param[0][band] = 0;
|
||||
band_param[1][band] = 0;
|
||||
band++;
|
||||
}
|
||||
}
|
||||
|
||||
void mpeg_audio::read_scfci()
|
||||
{
|
||||
memset(scfsi, 0, sizeof(scfsi));
|
||||
for(int band=0; band < total_bands; band++)
|
||||
for(int chan=0; chan < channel_count; chan++)
|
||||
if(band_param[chan][band])
|
||||
scfsi[chan][band] = gb(2);
|
||||
}
|
||||
|
||||
void mpeg_audio::read_band_amplitude_params()
|
||||
{
|
||||
memset(scf, 0, sizeof(scf));
|
||||
for(int band=0; band < total_bands; band++)
|
||||
for(int chan=0; chan<channel_count; chan++)
|
||||
if(band_param[chan][band]) {
|
||||
switch(scfsi[chan][band]) {
|
||||
case 0:
|
||||
scf[chan][0][band] = gb(6);
|
||||
scf[chan][1][band] = gb(6);
|
||||
scf[chan][2][band] = gb(6);
|
||||
break;
|
||||
|
||||
case 1: {
|
||||
int val = gb(6);
|
||||
scf[chan][0][band] = val;
|
||||
scf[chan][1][band] = val;
|
||||
scf[chan][2][band] = gb(6);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
int val = gb(6);
|
||||
scf[chan][0][band] = val;
|
||||
scf[chan][1][band] = val;
|
||||
scf[chan][2][band] = val;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
scf[chan][0][band] = gb(6);
|
||||
int val = gb(6);
|
||||
scf[chan][1][band] = val;
|
||||
scf[chan][2][band] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mpeg_audio::build_amplitudes()
|
||||
{
|
||||
memset(amp_values, 0, sizeof(amp_values));
|
||||
|
||||
for(int band=0; band < total_bands; band++)
|
||||
for(int chan=0; chan<channel_count; chan++)
|
||||
if(band_param[chan][band])
|
||||
for(int step=0; step<3; step++)
|
||||
amp_values[chan][step][band] = scalefactors[scf[chan][step][band]];
|
||||
}
|
||||
|
||||
void mpeg_audio::read_band_value_triplet(int chan, int band)
|
||||
{
|
||||
double buffer[3];
|
||||
|
||||
int band_idx = band_param[chan][band];
|
||||
switch(band_idx) {
|
||||
case 0:
|
||||
bdata[chan][0][band] = 0;
|
||||
bdata[chan][1][band] = 0;
|
||||
bdata[chan][2][band] = 0;
|
||||
return;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 4: {
|
||||
int modulo = band_infos[band_idx].modulo;
|
||||
int val = gb(band_infos[band_idx].cube_bits);
|
||||
buffer[0] = val % modulo;
|
||||
val = val / modulo;
|
||||
buffer[1] = val % modulo;
|
||||
val = val / modulo;
|
||||
buffer[2] = val % modulo;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int bits = band_infos[band_idx].bits;
|
||||
buffer[0] = gb(bits);
|
||||
buffer[1] = gb(bits);
|
||||
buffer[2] = gb(bits);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double scale = 1 << (band_infos[band_idx].bits - 1);
|
||||
|
||||
bdata[chan][0][band] = ((buffer[0] - scale) / scale + band_infos[band_idx].offset) * band_infos[band_idx].scale;
|
||||
bdata[chan][1][band] = ((buffer[1] - scale) / scale + band_infos[band_idx].offset) * band_infos[band_idx].scale;
|
||||
bdata[chan][2][band] = ((buffer[2] - scale) / scale + band_infos[band_idx].offset) * band_infos[band_idx].scale;
|
||||
}
|
||||
|
||||
void mpeg_audio::build_next_segments(int step)
|
||||
{
|
||||
int band = 0;
|
||||
while(band < joint_bands) {
|
||||
for(int chan=0; chan<channel_count; chan++) {
|
||||
read_band_value_triplet(chan, band);
|
||||
double amp = amp_values[chan][step][band];
|
||||
bdata[chan][0][band] *= amp;
|
||||
bdata[chan][1][band] *= amp;
|
||||
bdata[chan][2][band] *= amp;
|
||||
}
|
||||
band++;
|
||||
}
|
||||
|
||||
while(band < joint_bands) {
|
||||
read_band_value_triplet(0, band);
|
||||
bdata[1][0][band] = bdata[0][0][band];
|
||||
bdata[1][1][band] = bdata[0][1][band];
|
||||
bdata[1][2][band] = bdata[0][2][band];
|
||||
|
||||
for(int chan=0; chan<channel_count; chan++) {
|
||||
double amp = amp_values[chan][step][band];
|
||||
bdata[chan][0][band] *= amp;
|
||||
bdata[chan][1][band] *= amp;
|
||||
bdata[chan][2][band] *= amp;
|
||||
}
|
||||
band++;
|
||||
}
|
||||
|
||||
while(band < 32) {
|
||||
bdata[0][0][band] = 0;
|
||||
bdata[0][1][band] = 0;
|
||||
bdata[0][2][band] = 0;
|
||||
bdata[1][0][band] = 0;
|
||||
bdata[1][1][band] = 0;
|
||||
bdata[1][2][band] = 0;
|
||||
band++;
|
||||
}
|
||||
}
|
||||
|
||||
void mpeg_audio::retrieve_subbuffer(int step)
|
||||
{
|
||||
for(int chan=0; chan<channel_count; chan++)
|
||||
memcpy(subbuffer[chan], bdata[chan][step], 32*sizeof(subbuffer[0][0]));
|
||||
}
|
||||
|
||||
void mpeg_audio::idct32(const double *input, double *output)
|
||||
{
|
||||
// Simplest idct32 ever, non-fast at all
|
||||
for (int i = 0; i < 32; i++) {
|
||||
double s = 0;
|
||||
for (int j = 0; j < 32; j++)
|
||||
s += input[j] * m_cos_cache[i][j];
|
||||
output[i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
void mpeg_audio::resynthesis(const double *input, double *output)
|
||||
{
|
||||
memset(output, 0, 32*sizeof(output[0]));
|
||||
for(int j=0; j<64*8; j+=64) {
|
||||
for(int i=0; i<16; i++)
|
||||
output[i] += input[ i+j]*synthesis_filter[i+j] - input[32-i+j]*synthesis_filter[32+i+j];
|
||||
output[16] -= input[16+j]*synthesis_filter[32+16+j];
|
||||
for(int i=17; i<32; i++)
|
||||
output[i] -= input[32-i+j]*synthesis_filter[i+j] + input[ i+j]*synthesis_filter[32+i+j];
|
||||
}
|
||||
}
|
||||
|
||||
void mpeg_audio::scale_and_clamp(const double *input, short *output, int step)
|
||||
{
|
||||
for(int i=0; i<32; i++) {
|
||||
double val = input[i]*32768 + 0.5;
|
||||
short cval;
|
||||
if(val <= -32768)
|
||||
cval = -32768;
|
||||
else if(val >= 32767)
|
||||
cval = 32767;
|
||||
else
|
||||
cval = int(val);
|
||||
*output = cval;
|
||||
output += step;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert
|
||||
/***************************************************************************
|
||||
|
||||
MPEG audio support. Only layer2 and variants for now.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef FBNEO_SOUND_MPEG_AUDIO_H
|
||||
#define FBNEO_SOUND_MPEG_AUDIO_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class mpeg_audio {
|
||||
public:
|
||||
// Accepted layers. Beware that AMM is incompatible with L2 (and
|
||||
// not automatically recognizable) and that 2.5 implies 2.
|
||||
|
||||
enum {
|
||||
L1 = 1,
|
||||
L2 = 2,
|
||||
L2_5 = 4,
|
||||
L3 = 8,
|
||||
AMM = 16
|
||||
};
|
||||
|
||||
// base = Start of the mpeg data block
|
||||
// accepted = Binary or of accepted layers
|
||||
// lsb_first = Read bits out of bytes lsb-first rather than msb first
|
||||
// position_align = Position alignment after reading a block (0 = pure bitstream, must be a power of 2 otherwise)
|
||||
|
||||
mpeg_audio(const void *base, unsigned int accepted, bool lsb_first, int position_align);
|
||||
|
||||
// Decode one mpeg buffer.
|
||||
// pos = position in *bits* relative to base
|
||||
// limit = maximum accepted position in bits
|
||||
// output = output samples, interleaved
|
||||
// output_samples = number of samples written to output per channel
|
||||
// sample_rate = output sample rate
|
||||
// channels = number of channels written to output (total sample count is output_samples*channels)
|
||||
//
|
||||
// returns true if the buffer was complete and the new position in pos, false otherwise
|
||||
//
|
||||
// Sample rate and channels can change every buffer. That's mpeg
|
||||
// for you. Channels rarely changes, sample rate sometimes do,
|
||||
// especially in amm samples (drops to half at the end).
|
||||
//
|
||||
// One call to output buffer will generate 0 or 1 frame, which is
|
||||
// 384 samples per channel in layer I and 1152 otherwise (up to
|
||||
// 1152 in the amm case, <1152 indicating end of stream).
|
||||
|
||||
bool decode_buffer(int &pos, int limit, short *output,
|
||||
int &output_samples, int &sample_rate, int &channels);
|
||||
|
||||
|
||||
// Clear audio buffer
|
||||
void clear();
|
||||
|
||||
// state-scan
|
||||
void scan();
|
||||
|
||||
private:
|
||||
struct limit_hit {};
|
||||
|
||||
struct band_info {
|
||||
int modulo;
|
||||
double s1;
|
||||
int bits, cube_bits;
|
||||
int s4, s5;
|
||||
double range, s7, scale, offset;
|
||||
};
|
||||
|
||||
static const double scalefactors[64];
|
||||
static const int sample_rates[8];
|
||||
static const int layer2_param_index[2][4][16];
|
||||
static const int band_parameter_indexed_values[5][32][17];
|
||||
static const int band_parameter_index_bits_count[5][32];
|
||||
static const int joint_band_counts[4], total_band_counts[5];
|
||||
static const band_info band_infos[18];
|
||||
static const double synthesis_filter[512];
|
||||
|
||||
const UINT8 *base;
|
||||
int accepted, position_align;
|
||||
|
||||
int sampling_rate, last_frame_number;
|
||||
int param_index;
|
||||
|
||||
int channel_count, total_bands, joint_bands;
|
||||
|
||||
int band_param[2][32];
|
||||
int scfsi[2][32];
|
||||
int scf[2][3][32];
|
||||
double amp_values[2][3][32];
|
||||
double bdata[2][3][32];
|
||||
double subbuffer[2][32];
|
||||
double audio_buffer[2][32*32];
|
||||
int audio_buffer_pos[2];
|
||||
double m_cos_cache[32][32];
|
||||
|
||||
int current_pos, current_limit;
|
||||
|
||||
void read_header_amm(bool layer25);
|
||||
void read_header_mpeg2(bool layer25);
|
||||
void read_data_mpeg2();
|
||||
void decode_mpeg2(short *output, int &output_samples);
|
||||
|
||||
int get_band_param(int band);
|
||||
void read_band_params();
|
||||
void read_scfci();
|
||||
void read_band_amplitude_params();
|
||||
void read_band_value_triplet(int chan, int band);
|
||||
void build_amplitudes();
|
||||
void build_next_segments(int step);
|
||||
void retrieve_subbuffer(int step);
|
||||
void idct32(const double *input, double *output);
|
||||
void resynthesis(const double *input, double *output);
|
||||
void scale_and_clamp(const double *input, short *output, int step);
|
||||
|
||||
|
||||
static int do_gb_msb(const unsigned char *data, int &pos, int count);
|
||||
static int do_gb_lsb(const unsigned char *data, int &pos, int count);
|
||||
|
||||
int (*do_gb)(const unsigned char *data, int &pos, int count);
|
||||
|
||||
inline int gb(int count)
|
||||
{
|
||||
if(current_pos + count > current_limit)
|
||||
throw limit_hit();
|
||||
|
||||
return do_gb(base, current_pos, count);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // FBNEO_SOUND_MPEG_AUDIO_H
|
|
@ -0,0 +1,886 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert, R. Belmont, MetalliC
|
||||
/***************************************************************************
|
||||
|
||||
Yamaha YMZ770C "AMMS-A" and YMZ774 "AMMS2C"
|
||||
|
||||
Emulation by R. Belmont and MetalliC
|
||||
AMM decode by Olivier Galibert
|
||||
|
||||
-----
|
||||
TODO:
|
||||
- What does channel ATBL mean?
|
||||
- Simple Access mode. SACs is register / data lists same as SEQ. in 770C, when both /SEL and /CS pins goes low - will be run SAC with number set at data bus.
|
||||
can not be used in CV1K (/SEL pin is NC, internally pulled to VCC), probably not used in PGM2 too.
|
||||
770:
|
||||
- sequencer timers implemented but seems unused, presumably because of design flaws or bugs, likely due to lack of automatic adding of sequencer # to register offset.
|
||||
in result sequences uses very long chains of 32-sample wait commands instead, wasting a lot of ROM space.
|
||||
- sequencer triggers not implemented, not sure how they works (Deathsmiles ending tune starts sequence with TGST = 01h, likely a bug and don't affect tune playback)
|
||||
774:
|
||||
- 4 channel output
|
||||
- Equalizer
|
||||
- pan delayed transition (not used in games)
|
||||
- sequencer off trigger (not used in games)
|
||||
|
||||
known SPUs in this series:
|
||||
YMZ770B AMMSL Capcom medal hardware (alien.cpp), sample format is not AMM, in other parts looks like 770C
|
||||
YMZ770C AMMS-A Cave CV1000
|
||||
YMZ771 SSGS3
|
||||
YMZ773 AMMS2 Cron corp. video slots
|
||||
YMZ775 AMMS2B
|
||||
YMZ774 AMMS2C IGS PGM2
|
||||
YMZ776 AMMS3 uses AM3 sample format (modified Ogg?)
|
||||
YMZ778 AMMS3S
|
||||
YMZ779 AMMS3D
|
||||
YMZ870 AMMS3EX
|
||||
// ported from mame 0.228 -dink april, 2022
|
||||
***************************************************************************/
|
||||
|
||||
#include "burnint.h"
|
||||
#include "ymz770.h"
|
||||
#include "stream.h"
|
||||
#include "mpeg_audio.h"
|
||||
|
||||
#define logerror
|
||||
|
||||
static Stream stream;
|
||||
|
||||
static UINT8 *m_rom;
|
||||
static INT32 m_rom_size;
|
||||
static INT32 m_rom_mask;
|
||||
|
||||
static void (*sequencer)();
|
||||
static void (*internal_reg_write)(UINT8, UINT8);
|
||||
static UINT32 (*get_phrase_offs)(int);
|
||||
static UINT32 (*get_seq_offs)(int);
|
||||
|
||||
static void ymz770_internal_reg_write(UINT8 reg, UINT8 data); // forward
|
||||
static void ymz774_internal_reg_write(UINT8 reg, UINT8 data);
|
||||
static void ymz770_sequencer();
|
||||
static void ymz774_sequencer();
|
||||
static void ymz770_stream_update(INT16 **streams, INT32 samples);
|
||||
|
||||
static UINT32 ymz770_get_phrase_offs(int phrase) { return m_rom[(4 * phrase) + 1] << 16 | m_rom[(4 * phrase) + 2] << 8 | m_rom[(4 * phrase) + 3]; }
|
||||
static UINT32 ymz770_get_seq_offs(int sqn) { return m_rom[(4 * sqn) + 1 + 0x400] << 16 | m_rom[(4 * sqn) + 2 + 0x400] << 8 | m_rom[(4 * sqn) + 3 + 0x400]; }
|
||||
static UINT8 get_rom_byte(UINT32 offset) { return m_rom[offset & m_rom_mask]; }
|
||||
|
||||
static UINT32 ymz774_get_phrase_offs(int phrase) { int ph = phrase * 4; return ((m_rom[ph] & 0x0f) << 24 | m_rom[ph + 1] << 16 | m_rom[ph + 2] << 8 | m_rom[ph + 3]) * 2; }
|
||||
static UINT32 ymz774_get_seq_offs(int sqn) { int sq = sqn * 4 + 0x2000; return ((m_rom[sq] & 0x0f) << 24 | m_rom[sq + 1] << 16 | m_rom[sq + 2] << 8 | m_rom[sq + 3]) * 2; }
|
||||
static UINT32 ymz774_get_sqc_offs(int sqc) { int sq = sqc * 4 + 0x6000; return ((m_rom[sq] & 0x0f) << 24 | m_rom[sq + 1] << 16 | m_rom[sq + 2] << 8 | m_rom[sq + 3]) * 2; }
|
||||
|
||||
// data
|
||||
static UINT8 m_cur_reg;
|
||||
static UINT8 m_mute; // mute chip
|
||||
static UINT8 m_doen; // digital output enable
|
||||
static UINT8 m_vlma; // overall volume L0/R0
|
||||
static UINT8 m_vlma1; // overall volume L1/R1
|
||||
static UINT8 m_bsl; // boost level
|
||||
static UINT8 m_cpl; // clip limiter
|
||||
static int m_bank; // ymz774
|
||||
static UINT32 volinc[256]; // ymz774 (no scan)
|
||||
|
||||
struct ymz_channel
|
||||
{
|
||||
UINT16 phrase;
|
||||
UINT8 pan;
|
||||
UINT8 pan_delay;
|
||||
UINT8 pan1;
|
||||
UINT8 pan1_delay;
|
||||
INT32 volume;
|
||||
UINT8 volume_target;
|
||||
UINT8 volume_delay;
|
||||
UINT8 volume2;
|
||||
UINT8 loop;
|
||||
|
||||
bool is_playing, last_block, is_paused;
|
||||
|
||||
INT16 output_data[0x1000];
|
||||
int output_remaining;
|
||||
int output_ptr;
|
||||
int atbl;
|
||||
int pptr;
|
||||
|
||||
int end_channel_data;
|
||||
};
|
||||
|
||||
static mpeg_audio *mpeg_decoder[16];
|
||||
|
||||
struct ymz_sequence
|
||||
{
|
||||
UINT32 delay;
|
||||
UINT16 sequence;
|
||||
UINT16 timer;
|
||||
UINT16 stopchan;
|
||||
UINT8 loop;
|
||||
UINT32 offset;
|
||||
UINT8 bank;
|
||||
bool is_playing;
|
||||
bool is_paused;
|
||||
};
|
||||
struct ymz_sqc
|
||||
{
|
||||
UINT8 sqc;
|
||||
UINT8 loop;
|
||||
UINT32 offset;
|
||||
bool is_playing;
|
||||
bool is_waiting;
|
||||
};
|
||||
|
||||
static ymz_channel m_channels[16];
|
||||
static ymz_sequence m_sequences[8];
|
||||
static ymz_sqc m_sqcs[8];
|
||||
|
||||
static INT32 ymz_initted = 0;
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
static void init_common()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
m_channels[i].is_playing = false;
|
||||
mpeg_decoder[i] = new mpeg_audio(m_rom, mpeg_audio::AMM, false, 0);
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
m_sequences[i].is_playing = false;
|
||||
for (int i = 0; i < 8; i++)
|
||||
m_sqcs[i].is_playing = false;
|
||||
|
||||
ymz_initted = 1;
|
||||
}
|
||||
|
||||
void ymz770_exit()
|
||||
{
|
||||
if (ymz_initted) {
|
||||
ymz_initted = 0;
|
||||
stream.exit();
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
delete mpeg_decoder[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ymz770_set_buffered(INT32 (*pCPUCyclesCB)(), INT32 nCPUMhz)
|
||||
{
|
||||
stream.set_buffered(pCPUCyclesCB, nCPUMhz);
|
||||
}
|
||||
|
||||
void ymz770_init(UINT8 *rom, INT32 rom_length)
|
||||
{
|
||||
// create the stream
|
||||
stream.init(16000, nBurnSoundRate, 2, 0, ymz770_stream_update);
|
||||
|
||||
m_rom = rom;
|
||||
m_rom_size = rom_length;
|
||||
m_rom_mask = rom_length - 1;
|
||||
|
||||
// setup function pointers
|
||||
sequencer = ymz770_sequencer;
|
||||
internal_reg_write = ymz770_internal_reg_write;
|
||||
get_phrase_offs = ymz770_get_phrase_offs;
|
||||
get_seq_offs = ymz770_get_seq_offs;
|
||||
|
||||
init_common();
|
||||
}
|
||||
|
||||
void ymz774_init(UINT8 *rom, INT32 rom_length)
|
||||
{
|
||||
// create the stream
|
||||
stream.init(44100, nBurnSoundRate, 2, 0, ymz770_stream_update);
|
||||
|
||||
m_rom = rom;
|
||||
m_rom_size = rom_length;
|
||||
m_rom_mask = rom_length - 1;
|
||||
|
||||
// setup function pointers
|
||||
sequencer = ymz774_sequencer;
|
||||
internal_reg_write = ymz774_internal_reg_write;
|
||||
get_phrase_offs = ymz774_get_phrase_offs;
|
||||
get_seq_offs = ymz774_get_seq_offs;
|
||||
|
||||
init_common();
|
||||
|
||||
// calculate volume increments, fixed point values, fractions of 0x20000
|
||||
for (INT32 i = 0; i < 256; i++)
|
||||
{
|
||||
if (i < 0x20)
|
||||
volinc[i] = i;
|
||||
else
|
||||
volinc[i] = (0x20 | (i & 0x1f)) << ((i >> 5) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void ymz770_reset()
|
||||
{
|
||||
memset(&m_channels, 0, sizeof(m_channels));
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
m_channels[i].phrase = 0;
|
||||
m_channels[i].pan = 64;
|
||||
m_channels[i].pan_delay = 0;
|
||||
m_channels[i].pan1 = 64;
|
||||
m_channels[i].pan1_delay = 0;
|
||||
m_channels[i].volume = 0;
|
||||
m_channels[i].volume_target = 0;
|
||||
m_channels[i].volume_delay = 0;
|
||||
m_channels[i].volume2 = 0;
|
||||
m_channels[i].loop = 0;
|
||||
m_channels[i].is_playing = false;
|
||||
m_channels[i].is_paused = false;
|
||||
m_channels[i].output_remaining = 0;
|
||||
|
||||
mpeg_decoder[i]->clear();
|
||||
}
|
||||
memset(&m_sequences, 0, sizeof(m_sequences));
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
m_sequences[i].delay = 0;
|
||||
m_sequences[i].sequence = 0;
|
||||
m_sequences[i].timer = 0;
|
||||
m_sequences[i].stopchan = 0;
|
||||
m_sequences[i].loop = 0;
|
||||
m_sequences[i].bank = 0;
|
||||
m_sequences[i].is_playing = false;
|
||||
m_sequences[i].is_paused = false;
|
||||
}
|
||||
memset(&m_sqcs, 0, sizeof(m_sqcs));
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
m_sqcs[i].is_playing = false;
|
||||
m_sqcs[i].loop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ymz770_update(INT16 *output, INT32 samples_len)
|
||||
{
|
||||
if (samples_len != nBurnSoundLen) {
|
||||
bprintf(0, _T("ymz77x_update(): once per frame, please!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
stream.render(output, samples_len);
|
||||
}
|
||||
|
||||
void ymz770_scan(INT32 nAction, INT32 *pnMin)
|
||||
{
|
||||
if (nAction & ACB_DRIVER_DATA) {
|
||||
SCAN_VAR(m_cur_reg);
|
||||
SCAN_VAR(m_mute);
|
||||
SCAN_VAR(m_doen);
|
||||
SCAN_VAR(m_vlma);
|
||||
SCAN_VAR(m_vlma1);
|
||||
SCAN_VAR(m_bsl);
|
||||
SCAN_VAR(m_cpl);
|
||||
SCAN_VAR(m_bank);
|
||||
SCAN_VAR(m_channels);
|
||||
SCAN_VAR(m_sequences);
|
||||
SCAN_VAR(m_sqcs);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mpeg_decoder[i]->scan(); // mpeg/amm2 decoder state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// sound_stream_update - handle update requests for
|
||||
// our sound stream
|
||||
//-------------------------------------------------
|
||||
|
||||
static void ymz770_stream_update(INT16 **streams, INT32 samples)
|
||||
{
|
||||
INT16 *outL = streams[0];
|
||||
INT16 *outR = streams[1];
|
||||
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
sequencer();
|
||||
|
||||
// process channels
|
||||
INT32 mixl = 0;
|
||||
INT32 mixr = 0;
|
||||
|
||||
for (int ch = 0; ch < 16; ch++)
|
||||
{
|
||||
ymz_channel &channel = m_channels[ch];
|
||||
|
||||
if (channel.output_remaining > 0)
|
||||
{
|
||||
// force finish current block
|
||||
INT32 smpl = ((INT32)channel.output_data[channel.output_ptr++] * (channel.volume >> 17)) >> 7; // volume is linear, 0 - 128 (100%)
|
||||
smpl = (smpl * channel.volume2) >> 7;
|
||||
mixr += (smpl * channel.pan) >> 7; // pan seems linear, 0 - 128, where 0 = 100% left, 128 = 100% right, 64 = 50% left 50% right
|
||||
mixl += (smpl * (128 - channel.pan)) >> 7;
|
||||
channel.output_remaining--;
|
||||
|
||||
if (channel.output_remaining == 0 && !channel.is_playing)
|
||||
mpeg_decoder[ch]->clear();
|
||||
}
|
||||
|
||||
else if (channel.is_playing && !channel.is_paused)
|
||||
{
|
||||
retry:
|
||||
if (channel.last_block)
|
||||
{
|
||||
if (channel.loop)
|
||||
{
|
||||
if (channel.loop != 255)
|
||||
--channel.loop;
|
||||
// loop sample
|
||||
int phrase = channel.phrase;
|
||||
channel.atbl = m_rom[(4*phrase)+0] >> 4 & 7;
|
||||
channel.pptr = 8 * get_phrase_offs(phrase);
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.is_playing = false;
|
||||
channel.output_remaining = 0;
|
||||
mpeg_decoder[ch]->clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (channel.is_playing)
|
||||
{
|
||||
// next block
|
||||
int sample_rate, channel_count;
|
||||
if (!mpeg_decoder[ch]->decode_buffer(channel.pptr, m_rom_size*8, channel.output_data, channel.output_remaining, sample_rate, channel_count) || channel.output_remaining == 0)
|
||||
{
|
||||
channel.is_playing = !channel.last_block; // detect infinite retry loop
|
||||
channel.last_block = true;
|
||||
channel.output_remaining = 0;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
channel.last_block = channel.output_remaining < 1152;
|
||||
channel.output_remaining--;
|
||||
channel.output_ptr = 1;
|
||||
|
||||
INT32 smpl = ((INT32)channel.output_data[0] * (channel.volume >> 17)) >> 7;
|
||||
smpl = (smpl * channel.volume2) >> 7;
|
||||
mixr += (smpl * channel.pan) >> 7;
|
||||
mixl += (smpl * (128 - channel.pan)) >> 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixr *= m_vlma; // main volume is linear, 0 - 255, where 128 = 100%
|
||||
mixl *= m_vlma;
|
||||
mixr >>= 7 - m_bsl;
|
||||
mixl >>= 7 - m_bsl;
|
||||
// Clip limiter: 0 - off, 1 - 6.02 dB (100%), 2 - 4.86 dB (87.5%), 3 - 3.52 dB (75%)
|
||||
const INT32 ClipMax3 = 32768 * 75 / 100;
|
||||
const INT32 ClipMax2 = 32768 * 875 / 1000;
|
||||
switch (m_cpl)
|
||||
{
|
||||
case 3:
|
||||
mixl = (mixl > ClipMax3) ? ClipMax3 : (mixl < -ClipMax3) ? -ClipMax3 : mixl;
|
||||
mixr = (mixr > ClipMax3) ? ClipMax3 : (mixr < -ClipMax3) ? -ClipMax3 : mixr;
|
||||
break;
|
||||
case 2:
|
||||
mixl = (mixl > ClipMax2) ? ClipMax2 : (mixl < -ClipMax2) ? -ClipMax2 : mixl;
|
||||
mixr = (mixr > ClipMax2) ? ClipMax2 : (mixr < -ClipMax2) ? -ClipMax2 : mixr;
|
||||
break;
|
||||
case 1:
|
||||
mixl = (mixl > 32767) ? 32767 : (mixl < -32768) ? -32768 : mixl;
|
||||
mixr = (mixr > 32767) ? 32767 : (mixr < -32768) ? -32768 : mixr;
|
||||
break;
|
||||
}
|
||||
if (m_mute)
|
||||
mixr = mixl = 0;
|
||||
|
||||
outL[i] = mixl;
|
||||
outR[i] = mixr;
|
||||
}
|
||||
}
|
||||
|
||||
static void ymz770_sequencer()
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ymz_sequence &sequence = m_sequences[i];
|
||||
|
||||
if (sequence.is_playing)
|
||||
{
|
||||
if (sequence.delay > 0)
|
||||
sequence.delay--;
|
||||
else
|
||||
{
|
||||
int reg = get_rom_byte(sequence.offset++);
|
||||
UINT8 data = get_rom_byte(sequence.offset++);
|
||||
switch (reg)
|
||||
{
|
||||
case 0x0f:
|
||||
for (int ch = 0; ch < 8; ch++) // might be wrong, ie not needed in case of loop
|
||||
if (sequence.stopchan & (1 << ch))
|
||||
m_channels[ch].is_playing = false;
|
||||
if (sequence.loop)
|
||||
sequence.offset = get_seq_offs(sequence.sequence); // loop sequence
|
||||
else
|
||||
sequence.is_playing = false;
|
||||
break;
|
||||
case 0x0e:
|
||||
sequence.delay = sequence.timer * 32 + 32 - 1;
|
||||
break;
|
||||
default:
|
||||
internal_reg_write(reg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// write - write to the chip's registers
|
||||
//-------------------------------------------------
|
||||
|
||||
void ymz770_write(INT32 offset, UINT8 data)
|
||||
{
|
||||
if (offset & 1)
|
||||
{
|
||||
stream.update();
|
||||
internal_reg_write(m_cur_reg, data);
|
||||
}
|
||||
else
|
||||
m_cur_reg = data;
|
||||
}
|
||||
|
||||
|
||||
static void ymz770_internal_reg_write(UINT8 reg, UINT8 data)
|
||||
{
|
||||
// global registers
|
||||
if (reg < 0x40)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
case 0x00:
|
||||
m_mute = data & 1;
|
||||
m_doen = data >> 1 & 1;
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
m_vlma = data;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
m_bsl = data & 7;
|
||||
m_cpl = data >> 4 & 7;
|
||||
break;
|
||||
|
||||
// unused
|
||||
default:
|
||||
logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// playback registers
|
||||
else if (reg < 0x60)
|
||||
{
|
||||
int ch = reg >> 2 & 0x07;
|
||||
|
||||
switch (reg & 0x03)
|
||||
{
|
||||
case 0:
|
||||
m_channels[ch].phrase = data;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
m_channels[ch].volume2 = data;
|
||||
m_channels[ch].volume = 128 << 17;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_channels[ch].pan = data << 3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if ((data & 6) == 2 || ((data & 6) == 6 && !m_channels[ch].is_playing)) // both KON bits is 1 = "Keep Playing", do not restart channel in this case
|
||||
{
|
||||
UINT8 phrase = m_channels[ch].phrase;
|
||||
m_channels[ch].atbl = m_rom[(4*phrase)+0] >> 4 & 7;
|
||||
m_channels[ch].pptr = 8 * get_phrase_offs(phrase);
|
||||
m_channels[ch].last_block = false;
|
||||
|
||||
m_channels[ch].is_playing = true;
|
||||
}
|
||||
else if ((data & 6) == 0)
|
||||
m_channels[ch].is_playing = false;
|
||||
|
||||
m_channels[ch].loop = (data & 1) ? 255 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// sequencer registers
|
||||
else if (reg >= 0x80)
|
||||
{
|
||||
int ch = reg >> 4 & 0x07;
|
||||
|
||||
switch (reg & 0x0f)
|
||||
{
|
||||
case 0: // SQSN
|
||||
m_sequences[ch].sequence = data;
|
||||
break;
|
||||
case 1: // SQON SQLP
|
||||
if ((data & 6) == 2 || ((data & 6) == 6 && !m_sequences[ch].is_playing)) // both KON bits 1 is "Keep Playing"
|
||||
{
|
||||
m_sequences[ch].offset = get_seq_offs(m_sequences[ch].sequence);
|
||||
m_sequences[ch].delay = 0;
|
||||
m_sequences[ch].is_playing = true;
|
||||
}
|
||||
else if ((data & 6) == 0 && m_sequences[ch].is_playing)
|
||||
{
|
||||
m_sequences[ch].is_playing = false;
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (m_sequences[ch].stopchan & (1 << i))
|
||||
m_channels[i].is_playing = false;
|
||||
}
|
||||
|
||||
m_sequences[ch].loop = data & 1;
|
||||
break;
|
||||
case 2: // TMRH
|
||||
m_sequences[ch].timer = (m_sequences[ch].timer & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 3: // TMRL
|
||||
m_sequences[ch].timer = (m_sequences[ch].timer & 0xff00) | data;
|
||||
break;
|
||||
case 6: // SQOF
|
||||
m_sequences[ch].stopchan = data;
|
||||
break;
|
||||
//case 4: // TGST "ON trigger playback channel selection bitmask"
|
||||
//case 5: // TGEN "OFF trigger playback channel selection bitmask"
|
||||
//case 7: // YMZ770: unused, but CV1K games write 1 here, must be game bugs or YMZ770C datasheet is wrong and have messed 7 and 8 registers ? ; YMZ771: SQOF_SSG
|
||||
//case 8: // YMZ770: docs said "set to 01h" but CV1K games never write it, see above. ; YMZ771: TEMPO (wait timer speed control)
|
||||
default:
|
||||
if (data)
|
||||
logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ymz774_device
|
||||
//-------------------------------------------------
|
||||
|
||||
UINT8 ymz774_read(INT32 offset)
|
||||
{
|
||||
if (offset & 1)
|
||||
{
|
||||
if (m_cur_reg == 0xe3 || m_cur_reg == 0xe4)
|
||||
{
|
||||
stream.update();
|
||||
UINT8 res = 0;
|
||||
int bank = (m_cur_reg == 0xe3) ? 8 : 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (m_channels[i + bank].is_playing)
|
||||
res |= 1 << i;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
logerror("unimplemented read %02X\n", m_cur_reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ymz774_internal_reg_write(UINT8 reg, UINT8 data)
|
||||
{
|
||||
// playback registers
|
||||
if (reg < 0x10) // phrase num H and L
|
||||
{
|
||||
int ch = ((reg >> 1) & 7) + m_bank * 8;
|
||||
if (reg & 1)
|
||||
m_channels[ch].phrase = (m_channels[ch].phrase & 0xff00) | data;
|
||||
else
|
||||
m_channels[ch].phrase = (m_channels[ch].phrase & 0x00ff) | ((data & 7) << 8);
|
||||
}
|
||||
else if (reg < 0x60)
|
||||
{
|
||||
int ch = (reg & 7) + m_bank * 8;
|
||||
switch (reg & 0xf8)
|
||||
{
|
||||
case 0x10: // Volume 1
|
||||
m_channels[ch].volume_target = data;
|
||||
break;
|
||||
case 0x18: // Volume 1 delayed transition
|
||||
m_channels[ch].volume_delay = data;
|
||||
break;
|
||||
case 0x20: // Volume 2
|
||||
m_channels[ch].volume2 = data;
|
||||
break;
|
||||
case 0x28: // Pan L/R
|
||||
m_channels[ch].pan = data;
|
||||
break;
|
||||
case 0x30: // Pan L/R delayed transition
|
||||
if (data) logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
m_channels[ch].pan_delay = data;
|
||||
break;
|
||||
case 0x38: // Pan T/B
|
||||
m_channels[ch].pan1 = data;
|
||||
break;
|
||||
case 0x40: // Pan T/B delayed transition
|
||||
if (data) logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
m_channels[ch].pan1_delay = data;
|
||||
break;
|
||||
case 0x48: // Loop
|
||||
m_channels[ch].loop = data;
|
||||
break;
|
||||
case 0x50: // Start / Stop
|
||||
if (data)
|
||||
{
|
||||
int phrase = m_channels[ch].phrase;
|
||||
m_channels[ch].atbl = m_rom[(4 * phrase) + 0] >> 4 & 7;
|
||||
m_channels[ch].pptr = 8 * get_phrase_offs(phrase);
|
||||
m_channels[ch].last_block = false;
|
||||
|
||||
m_channels[ch].is_playing = true;
|
||||
m_channels[ch].is_paused = false; // checkme, might be not needed
|
||||
}
|
||||
else
|
||||
m_channels[ch].is_playing = false;
|
||||
break;
|
||||
case 0x58: // Pause / Resume
|
||||
m_channels[ch].is_paused = data ? true : false;
|
||||
if (data) logerror("CHECKME: CHAN pause/resume %02X %02X\n", reg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (reg < 0xd0)
|
||||
{
|
||||
if (m_bank == 0)
|
||||
{
|
||||
int sq = reg & 7;
|
||||
switch (reg & 0xf8)
|
||||
{
|
||||
case 0x60: // sequence num H and L
|
||||
case 0x68:
|
||||
sq = (reg >> 1) & 7;
|
||||
if (reg & 1)
|
||||
m_sequences[sq].sequence = (m_sequences[sq].sequence & 0xff00) | data;
|
||||
else
|
||||
m_sequences[sq].sequence = (m_sequences[sq].sequence & 0x00ff) | ((data & 0x07) << 8);
|
||||
break;
|
||||
case 0x70: // Start / Stop
|
||||
if (data)
|
||||
{
|
||||
//logerror("SEQ %d start (%s)\n", sq, m_sequences[sq].is_playing ? "playing":"stopped");
|
||||
m_sequences[sq].offset = get_seq_offs(m_sequences[sq].sequence);
|
||||
m_sequences[sq].delay = 0;
|
||||
m_sequences[sq].is_playing = true;
|
||||
m_sequences[sq].is_paused = false; // checkme, might be not needed
|
||||
}
|
||||
else
|
||||
{
|
||||
//logerror("SEQ %d stop (%s)\n", sq, m_sequences[sq].is_playing ? "playing" : "stopped");
|
||||
if (m_sequences[sq].is_playing)
|
||||
for (int ch = 0; ch < 16; ch++)
|
||||
if (m_sequences[sq].stopchan & (1 << ch))
|
||||
m_channels[ch].is_playing = false;
|
||||
m_sequences[sq].is_playing = false;
|
||||
}
|
||||
break;
|
||||
case 0x78: // Pause / Resume
|
||||
m_sequences[sq].is_paused = data ? true : false;
|
||||
if (data) logerror("CHECKME: SEQ pause/resume %02X %02X\n", reg, data);
|
||||
break;
|
||||
case 0x80: // Loop count, 0 = off, 255 - infinite
|
||||
m_sequences[sq].loop = data;
|
||||
break;
|
||||
case 0x88: // timer H and L
|
||||
case 0x90:
|
||||
sq = (reg - 0x88) >> 1;
|
||||
if (reg & 1)
|
||||
m_sequences[sq].timer = (m_sequences[sq].timer & 0xff00) | data;
|
||||
else
|
||||
m_sequences[sq].timer = (m_sequences[sq].timer & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 0x98: // Off trigger, bit4 = on/off, bits0-3 channel (end sequence when channel playback ends)
|
||||
if (data) logerror("SEQ Off trigger unimplemented %02X %02X\n", reg, data);
|
||||
break;
|
||||
case 0xa0: // stop channel mask H and L (when sequence stopped)
|
||||
case 0xa8:
|
||||
sq = (reg >> 1) & 7;
|
||||
if (reg & 1)
|
||||
m_sequences[sq].stopchan = (m_sequences[sq].stopchan & 0xff00) | data;
|
||||
else
|
||||
m_sequences[sq].stopchan = (m_sequences[sq].stopchan & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 0xb0: // SQC number
|
||||
m_sqcs[sq].sqc = data;
|
||||
break;
|
||||
case 0xb8: // SQC start / stop
|
||||
if (data)
|
||||
{
|
||||
//logerror("SQC %d start (%s)\n", sq, m_sqcs[sq].is_playing ? "playing" : "stopped");
|
||||
m_sqcs[sq].offset = ymz774_get_sqc_offs(m_sqcs[sq].sqc);
|
||||
m_sqcs[sq].is_playing = true;
|
||||
m_sqcs[sq].is_waiting = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//logerror("SQC %d stop (%s)\n", sq, m_sqcs[sq].is_playing ? "playing" : "stopped");
|
||||
m_sqcs[sq].is_playing = false;
|
||||
// stop SEQ too, and stop channels
|
||||
if (m_sequences[sq].is_playing)
|
||||
for (int ch = 0; ch < 16; ch++)
|
||||
if (m_sequences[sq].stopchan & (1 << ch))
|
||||
m_channels[ch].is_playing = false;
|
||||
m_sequences[sq].is_playing = false;
|
||||
}
|
||||
break;
|
||||
case 0xc0: // SQC loop (255 = infinite)
|
||||
m_sqcs[sq].loop = data;
|
||||
break;
|
||||
default:
|
||||
logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// else bank1 - Equalizer control
|
||||
}
|
||||
// global registers
|
||||
else
|
||||
{
|
||||
switch (reg) {
|
||||
case 0xd0: // Total Volume L0/R0
|
||||
m_vlma = data;
|
||||
break;
|
||||
case 0xd1: // Total Volume L1/R1
|
||||
m_vlma1 = data;
|
||||
break;
|
||||
case 0xd2: // Clip limit
|
||||
m_cpl = data;
|
||||
break;
|
||||
//case 0xd3: // Digital/PWM output
|
||||
//case 0xd4: // Digital audio IF/IS L0/R0
|
||||
//case 0xd5: // Digital audio IF/IS L1/R1
|
||||
//case 0xd8: // GPIO A
|
||||
//case 0xdd: // GPIO B
|
||||
//case 0xde: // GPIO C
|
||||
case 0xf0:
|
||||
m_bank = data & 1;
|
||||
if (data > 1) logerror("Set bank %02X!\n", data);
|
||||
break;
|
||||
default:
|
||||
logerror("unimplemented write %02X %02X\n", reg, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ymz774_sequencer()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
ymz_channel &chan = m_channels[i];
|
||||
|
||||
if (chan.is_playing && !chan.is_paused && (chan.volume >> 17) != chan.volume_target)
|
||||
{
|
||||
if (chan.volume_delay)
|
||||
{
|
||||
if ((chan.volume >> 17) < chan.volume_target)
|
||||
chan.volume += volinc[chan.volume_delay];
|
||||
else
|
||||
chan.volume -= volinc[chan.volume_delay];
|
||||
}
|
||||
else
|
||||
chan.volume = chan.volume_target << 17;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ymz_sqc &sqc = m_sqcs[i];
|
||||
ymz_sequence &sequence = m_sequences[i];
|
||||
|
||||
if (sqc.is_playing && !sqc.is_waiting)
|
||||
{
|
||||
// SQC consists of 4 byte records: SEQ num H, SEQ num L, SEQ Loop count, End flag (0xff)
|
||||
sequence.sequence = ((get_rom_byte(sqc.offset) << 8) | get_rom_byte(sqc.offset + 1)) & 0x7ff;
|
||||
sqc.offset += 2;
|
||||
sequence.loop = get_rom_byte(sqc.offset++);
|
||||
sequence.offset = get_seq_offs(sequence.sequence);
|
||||
sequence.delay = 0;
|
||||
sequence.is_playing = true;
|
||||
sequence.is_paused = false; // checkme, might be not needed
|
||||
sqc.is_waiting = true;
|
||||
if (get_rom_byte(sqc.offset++) == 0xff)
|
||||
{
|
||||
if (sqc.loop)
|
||||
{
|
||||
if (sqc.loop != 255)
|
||||
--sqc.loop;
|
||||
sqc.offset = ymz774_get_sqc_offs(sqc.sqc);
|
||||
}
|
||||
else
|
||||
sqc.is_playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sequence.is_playing && !sequence.is_paused)
|
||||
{
|
||||
if (sequence.delay > 0)
|
||||
--sequence.delay;
|
||||
else
|
||||
{
|
||||
int reg = get_rom_byte(sequence.offset++);
|
||||
UINT8 data = get_rom_byte(sequence.offset++);
|
||||
switch (reg)
|
||||
{
|
||||
case 0xff: // end
|
||||
for (int ch = 0; ch < 16; ch++) // might be wrong, ie not needed in case of loop
|
||||
if (sequence.stopchan & (1 << ch))
|
||||
m_channels[ch].is_playing = false;
|
||||
if (sequence.loop)
|
||||
{
|
||||
if (sequence.loop != 255)
|
||||
--sequence.loop;
|
||||
sequence.offset = get_seq_offs(sequence.sequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
sequence.is_playing = false;
|
||||
sqc.is_waiting = false;
|
||||
}
|
||||
break;
|
||||
case 0xfe: // timer delay
|
||||
sequence.delay = sequence.timer * 32 + 32 - 1;
|
||||
break;
|
||||
case 0xf0:
|
||||
sequence.bank = data & 1;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
UINT8 temp = m_bank;
|
||||
m_bank = sequence.bank;
|
||||
if (m_bank == 0 && reg >= 0x60 && reg < 0xb0) // if we hit SEQ registers need to add this sequence offset
|
||||
{
|
||||
int sqn = i;
|
||||
if (reg < 0x70 || (reg >= 0x88 && reg < 0x98) || reg >= 0xa0)
|
||||
sqn = i * 2;
|
||||
internal_reg_write(reg + sqn, data);
|
||||
}
|
||||
else
|
||||
internal_reg_write(reg, data);
|
||||
m_bank = temp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
void ymz770_init(UINT8 *rom, INT32 rom_length);
|
||||
void ymz774_init(UINT8 *rom, INT32 rom_length); // ymz774-specific!
|
||||
void ymz770_exit();
|
||||
void ymz770_set_buffered(INT32 (*pCPUCyclesCB)(), INT32 nCPUMhz);
|
||||
|
||||
void ymz770_write(INT32 offset, UINT8 data);
|
||||
UINT8 ymz774_read(INT32 offset); // ymz774-specific!
|
||||
|
||||
void ymz770_scan(INT32 nAction, INT32 *pnMin);
|
||||
void ymz770_update(INT16 *output, INT32 samples_len);
|
||||
void ymz770_reset();
|
Loading…
Reference in New Issue