// license:BSD-3-Clause // copyright-holders:Olivier Galibert /*************************************************************************** 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_t *)_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; } 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> 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; } }