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
|
// license:BSD-3-Clause
// copyright-holders:windyfairy
/***************************************************************************
MP3 audio decoder
***************************************************************************/
#include "emu.h"
#include "mp3_audio.h"
#define MINIMP3_IMPLEMENTATION
#define MAX_FRAME_SYNC_MATCHES 2
#include "minimp3/minimp3.h"
// To avoid modifying minimp3.h, forward declare mp3dec_local_t in mp3_audio.h and then make it an mp3dec_t using inheritance
struct mp3_audio::mp3dec_local_t : public mp3dec_t
{
};
mp3_audio::mp3_audio(const void *_base)
: base((const uint8_t *)_base)
{
dec = std::make_unique<mp3dec_local_t>();
clear();
}
mp3_audio::~mp3_audio()
{
}
void mp3_audio::register_save(device_t &host)
{
host.save_item(NAME(m_found_stream));
host.save_item(NAME(dec->header));
host.save_item(NAME(dec->reserv_buf));
host.save_item(NAME(dec->mdct_overlap));
host.save_item(NAME(dec->qmf_state));
host.save_item(NAME(dec->reserv));
host.save_item(NAME(dec->free_format_bytes));
}
void mp3_audio::clear()
{
mp3dec_init(dec.get());
m_found_stream = false;
}
bool mp3_audio::decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels)
{
mp3dec_frame_info_t info = {};
if (!m_found_stream)
{
// Guarantee a specified number of frames are buffered before starting decoding to ensure it's not full of garbage that looks like a valid frame
int free_format_bytes = 0;
int frame_bytes = 0;
int frame_offset = mp3d_find_frame(base, limit, &free_format_bytes, &frame_bytes);
if (frame_bytes && frame_offset + frame_bytes <= limit)
{
int i = 0, nmatch = 0;
for (i = frame_offset, nmatch = 0; nmatch < MAX_FRAME_SYNC_MATCHES; nmatch++)
{
i += hdr_frame_bytes(base + i, frame_bytes) + hdr_padding(base + i);
if (i + HDR_SIZE > limit || !hdr_compare(base + frame_offset, base + i))
break;
}
m_found_stream = nmatch >= MAX_FRAME_SYNC_MATCHES;
}
if (!m_found_stream)
{
output_samples = 0;
sample_rate = 0;
channels = 0;
pos = 0;
return false;
}
}
output_samples = mp3dec_decode_frame(dec.get(), base, limit, output, &info);
sample_rate = info.hz;
channels = info.channels;
pos = info.frame_bytes;
return pos > 0 && output_samples > 0;
}
|