summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/mpeg_audio.h
blob: 53738671c16b458a686ae1ceb188c89ca22f21f6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/***************************************************************************

    MPEG audio support.  Only layer2 and variants for now.

***************************************************************************/

#ifndef MAME_SOUND_MPEG_AUDIO_H
#define MAME_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();

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_t *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 // MAME_SOUND_MPEG_AUDIO_H