Skip to content

Commit 44e7e9b

Browse files
sound/tms320av110: Add preliminary MPEG audio decoder
Implement the host data interface, active-low reset and request signals, and MPEG-1 Audio Layer II playback using MAME's existing MPEG audio decoder.
1 parent 5f4a88b commit 44e7e9b

3 files changed

Lines changed: 294 additions & 0 deletions

File tree

scripts/src/sound.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,18 @@ if SOUNDS["MPEG_AUDIO"] then
15341534
}
15351535
end
15361536

1537+
---------------------------------------------------
1538+
-- Texas Instruments TMS320AV110 MPEG audio decoder
1539+
--@src/devices/sound/tms320av110.h,SOUNDS["TMS320AV110"] = true
1540+
---------------------------------------------------
1541+
1542+
if SOUNDS["TMS320AV110"] then
1543+
files {
1544+
MAME_DIR .. "src/devices/sound/tms320av110.cpp",
1545+
MAME_DIR .. "src/devices/sound/tms320av110.h",
1546+
}
1547+
end
1548+
15371549
---------------------------------------------------
15381550
-- ZOOM ZSG-2
15391551
--@src/devices/sound/zsg2.h,SOUNDS["ZSG2"] = true

src/devices/sound/tms320av110.cpp

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// license:BSD-3-Clause
2+
// copyright-holders:MagikalUnicorn
3+
4+
/*
5+
* Texas Instruments TMS320AV110 MPEG-1 audio decoder (preliminary)
6+
*
7+
* The implemented subset consists of the host register window, memory-mapped
8+
* compressed-data input, reset-controlled REQ signalling, and MPEG-1 Audio
9+
* Layer II reconstruction. Input-buffer-full backpressure and other
10+
* control/status register behaviour are not yet fully implemented.
11+
*
12+
* Reference: Texas Instruments data sheet SCSS013C, revised August 1995.
13+
*/
14+
15+
#include "emu.h"
16+
#include "tms320av110.h"
17+
18+
#include "mpeg_audio.h"
19+
20+
namespace {
21+
22+
// The host interface has seven address pins, SADDR6 through SADDR0.
23+
constexpr unsigned HOST_ADDRESS_BITS = 7;
24+
constexpr offs_t HOST_ADDRESS_MASK = (1U << HOST_ADDRESS_BITS) - 1;
25+
26+
// DATAIN is the data sheet's memory-mapped compressed-audio input register.
27+
constexpr offs_t REG_DATAIN = 0x18;
28+
29+
// MPEG audio is presented as left and right PCM output slots, including mono streams.
30+
constexpr unsigned LEFT_CHANNEL = 0;
31+
constexpr unsigned RIGHT_CHANNEL = 1;
32+
constexpr unsigned OUTPUT_CHANNELS = 2;
33+
34+
// Used for sound scheduling until an MPEG header supplies the stream rate.
35+
constexpr u32 INITIAL_SAMPLE_RATE = 44'100;
36+
37+
// MPEG-1 Layer II produces 1,152 samples per channel for each decoded frame.
38+
constexpr unsigned MPEG_LAYER_II_SAMPLES_PER_FRAME = 1'152;
39+
40+
// A Layer II frame is largest at the maximum bit rate and minimum sample rate.
41+
constexpr unsigned MAX_MPEG1_LAYER_II_BIT_RATE = 384'000;
42+
constexpr unsigned MIN_MPEG1_SAMPLE_RATE = 32'000;
43+
constexpr unsigned MPEG1_LAYER_II_FRAME_SCALE = 144;
44+
constexpr unsigned MPEG_FRAME_PADDING_BYTES = 1;
45+
constexpr unsigned MAX_MPEG_AUDIO_FRAME_BYTES =
46+
(MPEG1_LAYER_II_FRAME_SCALE * MAX_MPEG1_LAYER_II_BIT_RATE / MIN_MPEG1_SAMPLE_RATE) + MPEG_FRAME_PADDING_BYTES;
47+
48+
// Retain two maximum-sized frames so the frame-based decoder can accept streaming input.
49+
constexpr unsigned INPUT_BUFFER_BYTES = 2 * MAX_MPEG_AUDIO_FRAME_BYTES;
50+
51+
} // anonymous namespace
52+
53+
DEFINE_DEVICE_TYPE(TMS320AV110, tms320av110_device, "tms320av110", "Texas Instruments TMS320AV110 MPEG Audio Decoder")
54+
55+
struct tms320av110_device::decoder_state
56+
{
57+
std::array<u8, INPUT_BUFFER_BYTES> input{};
58+
std::array<s16, MPEG_LAYER_II_SAMPLES_PER_FRAME * OUTPUT_CHANNELS> pcm{};
59+
std::unique_ptr<mpeg_audio> decoder;
60+
unsigned input_bytes = 0;
61+
unsigned input_bit_position = 0;
62+
unsigned pcm_position = 0;
63+
unsigned pcm_count = 0;
64+
unsigned pcm_channels = 0;
65+
};
66+
67+
tms320av110_device::tms320av110_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
68+
device_t(mconfig, TMS320AV110, tag, owner, clock),
69+
device_sound_interface(mconfig, *this),
70+
m_stream(nullptr),
71+
m_req_cb(*this),
72+
m_decoder(std::make_unique<decoder_state>()),
73+
m_reset_asserted(true)
74+
{
75+
}
76+
77+
tms320av110_device::~tms320av110_device() = default;
78+
79+
void tms320av110_device::device_start()
80+
{
81+
m_stream = stream_alloc(0, OUTPUT_CHANNELS, INITIAL_SAMPLE_RATE);
82+
decoder_create();
83+
84+
save_item(NAME(m_decoder->input));
85+
save_item(NAME(m_decoder->pcm));
86+
save_item(NAME(m_decoder->input_bytes));
87+
save_item(NAME(m_decoder->input_bit_position));
88+
save_item(NAME(m_decoder->pcm_position));
89+
save_item(NAME(m_decoder->pcm_count));
90+
save_item(NAME(m_decoder->pcm_channels));
91+
save_item(NAME(m_reset_asserted));
92+
}
93+
94+
void tms320av110_device::device_reset()
95+
{
96+
decoder_reset();
97+
m_reset_asserted = true;
98+
update_req();
99+
}
100+
101+
void tms320av110_device::decoder_create()
102+
{
103+
m_decoder->decoder = std::make_unique<mpeg_audio>(m_decoder->input.data(), mpeg_audio::L2, false, 0);
104+
}
105+
106+
void tms320av110_device::decoder_reset()
107+
{
108+
if (m_stream)
109+
{
110+
m_stream->update();
111+
m_stream->set_sample_rate(INITIAL_SAMPLE_RATE);
112+
}
113+
114+
m_decoder->input.fill(0);
115+
m_decoder->pcm.fill(0);
116+
m_decoder->input_bytes = 0;
117+
m_decoder->input_bit_position = 0;
118+
m_decoder->pcm_position = 0;
119+
m_decoder->pcm_count = 0;
120+
m_decoder->pcm_channels = 0;
121+
decoder_create();
122+
}
123+
124+
bool tms320av110_device::decode_frame()
125+
{
126+
int position = m_decoder->input_bit_position;
127+
int sample_count = 0;
128+
int sample_rate = 0;
129+
int channels = 0;
130+
if (!m_decoder->decoder->decode_buffer(
131+
position,
132+
m_decoder->input_bytes * 8,
133+
m_decoder->pcm.data(),
134+
sample_count,
135+
sample_rate,
136+
channels))
137+
{
138+
return false;
139+
}
140+
141+
assert(sample_count <= MPEG_LAYER_II_SAMPLES_PER_FRAME);
142+
assert((channels == 1) || (channels == OUTPUT_CHANNELS));
143+
m_decoder->pcm_position = 0;
144+
m_decoder->pcm_count = sample_count;
145+
m_decoder->pcm_channels = channels;
146+
147+
const unsigned bytes_consumed = position / 8;
148+
if (bytes_consumed)
149+
{
150+
std::move(
151+
m_decoder->input.begin() + bytes_consumed,
152+
m_decoder->input.begin() + m_decoder->input_bytes,
153+
m_decoder->input.begin());
154+
m_decoder->input_bytes -= bytes_consumed;
155+
}
156+
m_decoder->input_bit_position = position & 7;
157+
update_req();
158+
159+
if (sample_rate && (sample_rate != m_stream->sample_rate()))
160+
m_stream->set_sample_rate(sample_rate);
161+
162+
return true;
163+
}
164+
165+
void tms320av110_device::fifo_w(u8 data)
166+
{
167+
if (m_decoder->input_bytes == m_decoder->input.size())
168+
{
169+
logerror("%s: compressed-audio input overflow\n", machine().describe_context());
170+
return;
171+
}
172+
173+
m_decoder->input[m_decoder->input_bytes++] = data;
174+
update_req();
175+
}
176+
177+
void tms320av110_device::update_req()
178+
{
179+
// REQ is active low and is held inactive during reset or while the input buffer is full.
180+
m_req_cb((m_reset_asserted || (m_decoder->input_bytes == m_decoder->input.size())) ? ASSERT_LINE : CLEAR_LINE);
181+
}
182+
183+
void tms320av110_device::reset_w(int state)
184+
{
185+
const bool asserted = !state;
186+
if (asserted && !m_reset_asserted)
187+
decoder_reset();
188+
189+
m_reset_asserted = asserted;
190+
update_req();
191+
}
192+
193+
u8 tms320av110_device::read(offs_t offset)
194+
{
195+
if (m_reset_asserted)
196+
return 0; // The data sheet specifies that RESET low disables host accesses.
197+
198+
offset &= HOST_ADDRESS_MASK;
199+
if (!machine().side_effects_disabled())
200+
logerror("%s: unimplemented register read %02x\n", machine().describe_context(), offset);
201+
return 0;
202+
}
203+
204+
void tms320av110_device::write(offs_t offset, u8 data)
205+
{
206+
if (m_reset_asserted)
207+
return; // The data sheet specifies that RESET low disables host accesses.
208+
209+
offset &= HOST_ADDRESS_MASK;
210+
if (offset == REG_DATAIN)
211+
{
212+
fifo_w(data);
213+
return;
214+
}
215+
216+
logerror("%s: unimplemented register write %02x = %02x\n", machine().describe_context(), offset, data);
217+
}
218+
219+
void tms320av110_device::sound_stream_update(sound_stream &stream)
220+
{
221+
for (int sample = 0; sample < stream.samples(); sample++)
222+
{
223+
if ((m_decoder->pcm_position == m_decoder->pcm_count) && !decode_frame())
224+
{
225+
stream.put(LEFT_CHANNEL, sample, 0.0F);
226+
stream.put(RIGHT_CHANNEL, sample, 0.0F);
227+
continue;
228+
}
229+
230+
const unsigned position = m_decoder->pcm_position * m_decoder->pcm_channels;
231+
const s16 left = m_decoder->pcm[position];
232+
const s16 right = (m_decoder->pcm_channels == OUTPUT_CHANNELS)
233+
? m_decoder->pcm[position + RIGHT_CHANNEL]
234+
: left;
235+
stream.put_int(LEFT_CHANNEL, sample, left, 32'768);
236+
stream.put_int(RIGHT_CHANNEL, sample, right, 32'768);
237+
m_decoder->pcm_position++;
238+
}
239+
}

src/devices/sound/tms320av110.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// license:BSD-3-Clause
2+
// copyright-holders:MagikalUnicorn
3+
4+
#ifndef MAME_SOUND_TMS320AV110_H
5+
#define MAME_SOUND_TMS320AV110_H
6+
7+
#pragma once
8+
9+
class tms320av110_device : public device_t, public device_sound_interface
10+
{
11+
public:
12+
tms320av110_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
13+
virtual ~tms320av110_device();
14+
15+
auto req() { return m_req_cb.bind(); } // active-low compressed-data request output
16+
17+
void reset_w(int state); // active-low RESET input
18+
u8 read(offs_t offset);
19+
void write(offs_t offset, u8 data);
20+
21+
protected:
22+
virtual void device_start() override ATTR_COLD;
23+
virtual void device_reset() override ATTR_COLD;
24+
virtual void sound_stream_update(sound_stream &stream) override;
25+
26+
private:
27+
struct decoder_state;
28+
29+
void decoder_create();
30+
void decoder_reset();
31+
bool decode_frame();
32+
void fifo_w(u8 data);
33+
void update_req();
34+
35+
sound_stream *m_stream;
36+
devcb_write_line m_req_cb;
37+
std::unique_ptr<decoder_state> m_decoder;
38+
bool m_reset_asserted;
39+
};
40+
41+
DECLARE_DEVICE_TYPE(TMS320AV110, tms320av110_device)
42+
43+
#endif // MAME_SOUND_TMS320AV110_H

0 commit comments

Comments
 (0)