From fd0960a746f5a42760548d1467536940f9307040 Mon Sep 17 00:00:00 2001 From: "R. Belmont" Date: Mon, 31 Dec 2012 22:04:28 +0000 Subject: Split AMM decoder from YMZ770 and add MPEG layer 1/2 support [O. Galibert] De-skeletonized Sega Z80 type Digital Sound Board, hooked up to swa [R. Belmont, O. Galibert] --- src/emu/sound/mpeg_audio.c | 752 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 752 insertions(+) create mode 100644 src/emu/sound/mpeg_audio.c (limited to 'src/emu/sound/mpeg_audio.c') diff --git a/src/emu/sound/mpeg_audio.c b/src/emu/sound/mpeg_audio.c new file mode 100644 index 00000000000..94f11c72842 --- /dev/null +++ b/src/emu/sound/mpeg_audio.c @@ -0,0 +1,752 @@ +/*************************************************************************** + + MPEG audio support. Only layer2 and variants for now. + +***************************************************************************/ + +#include "emu.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; + + memset(audio_buffer, 0, sizeof(audio_buffer)); + audio_buffer_pos[0] = 16*32; + audio_buffer_pos[1] = 16*32; +} + + +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(); + 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> 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= 32767) + cval = 32767; + else + cval = int(val); + *output = cval; + output += step; + } +} -- cgit v1.2.3-70-g09d2