diff options
Diffstat (limited to 'src/devices/sound/mas3507d.cpp')
-rw-r--r-- | src/devices/sound/mas3507d.cpp | 181 |
1 files changed, 87 insertions, 94 deletions
diff --git a/src/devices/sound/mas3507d.cpp b/src/devices/sound/mas3507d.cpp index c2b50088d11..25f9d2ab1af 100644 --- a/src/devices/sound/mas3507d.cpp +++ b/src/devices/sound/mas3507d.cpp @@ -1,25 +1,25 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert -// -// MAS 3507D MPEG audio decoder -// +/* + Micronas MAS 3507D MPEG audio decoder + + Datasheet: https://www.mas-player.de/mp3/download/mas3507d.pdf + + TODO: + - Datasheet says it has DSP and internal program ROM, + but these are not dumped and hooked up + - Support Broadcast mode, MPEG Layer 2 +*/ #include "emu.h" #include "mas3507d.h" +#include "mp3_audio.h" -#define MINIMP3_ONLY_MP3 -#define MINIMP3_NO_STDIO -#define MINIMP3_IMPLEMENTATION -#define MAX_FRAME_SYNC_MATCHES 1 -#include "minimp3/minimp3.h" -#include "minimp3/minimp3_ex.h" - -#define LOG_GENERAL (1 << 0) -#define LOG_READ (1 << 1) -#define LOG_WRITE (1 << 2) -#define LOG_REGISTER (1 << 3) -#define LOG_CONFIG (1 << 4) -#define LOG_OTHER (1 << 5) +#define LOG_READ (1U << 1) +#define LOG_WRITE (1U << 2) +#define LOG_REGISTER (1U << 3) +#define LOG_CONFIG (1U << 4) +#define LOG_OTHER (1U << 5) // #define VERBOSE (LOG_GENERAL | LOG_READ | LOG_WRITE | LOG_REGISTER | LOG_CONFIG | LOG_OTHER) // #define LOG_OUTPUT_STREAM std::cout @@ -37,12 +37,12 @@ ALLOW_SAVE_TYPE(mas3507d_device::i2c_subdest_t) ALLOW_SAVE_TYPE(mas3507d_device::i2c_command_t) // device type definition -DEFINE_DEVICE_TYPE(MAS3507D, mas3507d_device, "mas3507d", "MAS 3507D MPEG decoder") +DEFINE_DEVICE_TYPE(MAS3507D, mas3507d_device, "mas3507d", "Micronas MAS 3507D MPEG decoder") mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, MAS3507D, tag, owner, clock) , device_sound_interface(mconfig, *this) - , cb_sample(*this) + , cb_mpeg_frame_sync(*this), cb_demand(*this) , i2c_bus_state(IDLE), i2c_bus_address(UNKNOWN), i2c_subdest(UNDEFINED), i2c_command(CMD_BAD) , i2c_scli(false), i2c_sclo(false), i2c_sdai(false), i2c_sdao(false) , i2c_bus_curbit(0), i2c_bus_curval(0), i2c_bytecount(0), i2c_io_bank(0), i2c_io_adr(0), i2c_io_count(0), i2c_io_val(0) @@ -51,9 +51,8 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, void mas3507d_device::device_start() { - current_rate = 44100; - stream = stream_alloc(0, 2, current_rate); - cb_sample.resolve(); + stream = stream_alloc(0, 2, 44100); + mp3dec = std::make_unique<mp3_audio>(reinterpret_cast<const uint8_t *>(&mp3data[0])); save_item(NAME(mp3data)); save_item(NAME(samples)); @@ -67,10 +66,9 @@ void mas3507d_device::device_start() save_item(NAME(i2c_sdao)); save_item(NAME(i2c_bus_curbit)); save_item(NAME(i2c_bus_curval)); + save_item(NAME(mp3data_count)); - save_item(NAME(current_rate)); save_item(NAME(decoded_frame_count)); - save_item(NAME(decoded_samples)); save_item(NAME(sample_count)); save_item(NAME(samples_idx)); save_item(NAME(is_muted)); @@ -82,22 +80,10 @@ void mas3507d_device::device_start() save_item(NAME(i2c_io_count)); save_item(NAME(i2c_io_val)); save_item(NAME(i2c_sdao_data)); - save_item(NAME(playback_status)); - - // This should be removed in the future if/when native MP3 decoding is implemented in MAME - save_item(NAME(mp3_dec.mdct_overlap)); - save_item(NAME(mp3_dec.qmf_state)); - save_item(NAME(mp3_dec.reserv)); - save_item(NAME(mp3_dec.free_format_bytes)); - save_item(NAME(mp3_dec.header)); - save_item(NAME(mp3_dec.reserv_buf)); - - save_item(NAME(mp3_info.frame_bytes)); - save_item(NAME(mp3_info.frame_offset)); - save_item(NAME(mp3_info.channels)); - save_item(NAME(mp3_info.hz)); - save_item(NAME(mp3_info.layer)); - save_item(NAME(mp3_info.bitrate_kbps)); + + save_item(NAME(frame_channels)); + + mp3dec->register_save(*this); } void mas3507d_device::device_reset() @@ -108,9 +94,19 @@ void mas3507d_device::device_reset() i2c_bus_address = UNKNOWN; i2c_bus_curbit = -1; i2c_bus_curval = 0; + i2c_bytecount = 0; + i2c_io_bank = 0; + i2c_io_adr = 0; + i2c_io_count = 0; + i2c_io_val = 0; + i2c_sdao_data = 0; is_muted = false; - gain_ll = gain_rr = 0; + gain_ll = gain_rr = 1.0; + + frame_channels = 2; + + stream->set_sample_rate(44100); reset_playback(); } @@ -120,7 +116,8 @@ void mas3507d_device::i2c_scl_w(bool line) if(line == i2c_scli) return; i2c_scli = line; - + // FIXME: sda output should only change state when clock is low. + // On wrong device-id ensure sda is left high (Think it's OK but not certain) if(i2c_scli) { if(i2c_bus_state == STARTED) { if(i2c_sdai) @@ -348,11 +345,12 @@ void mas3507d_device::i2c_device_got_stop() LOGOTHER("MAS I2C: got stop\n"); } -int gain_to_db(double val) { +int mas3507d_device::gain_to_db(double val) { return round(20 * log10((0x100000 - val) / 0x80000)); } -float gain_to_percentage(int val) { +float mas3507d_device::gain_to_percentage(int val) +{ if(val == 0) return 0; // Special case for muting it seems @@ -370,10 +368,6 @@ void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val) case 0x107f8: gain_ll = gain_to_percentage(val); LOGCONFIG("MAS3507D: left->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_ll); - - if(!is_muted) { - set_output_gain(0, gain_ll); - } break; case 0x107f9: LOGCONFIG("MAS3507D: left->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val)); @@ -384,10 +378,6 @@ void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val) case 0x107fb: gain_rr = gain_to_percentage(val); LOGCONFIG("MAS3507D: right->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_rr); - - if(!is_muted) { - set_output_gain(1, gain_rr); - } break; default: LOGCONFIG("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break; } @@ -399,8 +389,7 @@ void mas3507d_device::reg_write(uint32_t adr, uint32_t val) case 0x8e: LOGCONFIG("MAS3507D: DCCF = %05x\n", val); break; case 0xaa: LOGCONFIG("MAS3507D: Mute/bypass = %05x\n", val); - set_output_gain(0, val == 1 ? 0 : gain_ll); - set_output_gain(1, val == 1 ? 0 : gain_rr); + is_muted = val == 1; break; case 0xe6: LOGCONFIG("MAS3507D: StartupConfig = %05x\n", val); break; case 0xe7: LOGCONFIG("MAS3507D: Kprescale = %05x\n", val); break; @@ -418,48 +407,63 @@ void mas3507d_device::run_program(uint32_t adr) } } -void mas3507d_device::fill_buffer() +void mas3507d_device::sid_w(uint8_t byte) { - while(mp3data_count + 2 < mp3data.size()) { - uint16_t v = cb_sample(); - mp3data[mp3data_count++] = v >> 8; - mp3data[mp3data_count++] = v; + if (mp3data_count >= mp3data.size()) { + std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin()); + mp3data_count--; } - sample_count = mp3dec_decode_frame(&mp3_dec, static_cast<const uint8_t *>(&mp3data[0]), mp3data_count, static_cast<mp3d_sample_t *>(&samples[0]), &mp3_info); + mp3data[mp3data_count++] = byte; + + cb_demand(mp3data_count < mp3data.size()); +} + +void mas3507d_device::fill_buffer() +{ + cb_mpeg_frame_sync(0); + + int pos = 0, frame_sample_rate = 0; + bool decoded_frame = mp3dec->decode_buffer(pos, mp3data_count, &samples[0], sample_count, frame_sample_rate, frame_channels); samples_idx = 0; - playback_status = PLAYBACK_STATE_BUFFER_FULL; - if(sample_count == 0) + if (!decoded_frame || sample_count == 0) { + // Frame decode failed + if (mp3data_count >= mp3data.size()) { + std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin()); + mp3data_count--; + } + + cb_demand(1); // always request more data when nothing could be decoded to force potentially stale data out of the buffer return; + } - std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin()); - mp3data_count -= mp3_info.frame_bytes; + std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin()); + mp3data_count -= pos; - if(mp3_info.hz != current_rate) { - current_rate = mp3_info.hz; - stream->set_sample_rate(current_rate); - } + stream->set_sample_rate(frame_sample_rate); decoded_frame_count++; + cb_mpeg_frame_sync(1); + + cb_demand(mp3data_count < mp3data.size()); } void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount) { - int s1 = scount - pos; - int bytes_per_sample = mp3_info.channels > 2 ? 2 : mp3_info.channels; // More than 2 channels is unsupported here - - if(s1 > sample_count) - s1 = sample_count; - - playback_status = PLAYBACK_STATE_DEMAND_BUFFER; + const int bytes_per_sample = std::min(frame_channels, 2); // More than 2 channels is unsupported here + const int s1 = std::min(scount - pos, sample_count); + const stream_buffer::sample_t sample_scale = 1.0 / 32768.0; + const stream_buffer::sample_t mute_scale = is_muted ? 0.0 : 1.0; for(int i = 0; i < s1; i++) { - outputs[0].put_int(pos, samples[samples_idx * bytes_per_sample], 32768); - outputs[1].put_int(pos, samples[samples_idx * bytes_per_sample + (bytes_per_sample >> 1)], 32768); + const stream_buffer::sample_t lsamp_mixed = stream_buffer::sample_t(samples[samples_idx * bytes_per_sample]) * sample_scale * mute_scale * gain_ll; + const stream_buffer::sample_t rsamp_mixed = stream_buffer::sample_t(samples[samples_idx * bytes_per_sample + (bytes_per_sample >> 1)]) * sample_scale * mute_scale * gain_rr; + + outputs[0].put(pos, lsamp_mixed); + outputs[1].put(pos, rsamp_mixed); samples_idx++; - decoded_samples++; pos++; if(samples_idx >= sample_count) { @@ -471,22 +475,14 @@ void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int void mas3507d_device::reset_playback() { - mp3dec_init(&mp3_dec); + std::fill(mp3data.begin(), mp3data.end(), 0); + std::fill(samples.begin(), samples.end(), 0); + + mp3dec->clear(); mp3data_count = 0; sample_count = 0; decoded_frame_count = 0; - decoded_samples = 0; - playback_status = PLAYBACK_STATE_IDLE; - is_started = false; samples_idx = 0; - std::fill(mp3data.begin(), mp3data.end(), 0); - std::fill(samples.begin(), samples.end(), 0); -} - -void mas3507d_device::start_playback() -{ - reset_playback(); - is_started = true; } void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) @@ -495,13 +491,10 @@ void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read int pos = 0; while(pos < csamples) { - if(is_started && sample_count == 0) + if(sample_count == 0) fill_buffer(); - if(!is_started || sample_count <= 0) { - playback_status = PLAYBACK_STATE_IDLE; - decoded_frame_count = 0; - decoded_samples = 0; + if(sample_count <= 0) { outputs[0].fill(0, pos); outputs[1].fill(0, pos); return; |