~q3cpma/mus

ref: cf29c2237d0c99e5240a55895c07564ef4635392 mus/mus_player/opus.c -rw-r--r-- 3.2 KiB
cf29c223q3cpma Add a #pragma once to the new xoshiro128plus.h header 1 year, 7 months ago
                                                                                
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
#include <opus/opusfile.h>

#include "filter.h"
#include "log.h"
#include "math.h"
#include "misc.h"
#include "opus.h"
#include "tag.h"


static const char * errormsg(int code)
{
	switch (code)
	{
		case OP_HOLE:          return "missing or corrupt data encountered";
		case OP_EREAD:         return "read error";
		case OP_EFAULT:        return "internal decoder failure";
		case OP_EIMPL:         return "feature not implemented";
		case OP_EINVAL:        return "invalid or uninitialized argument";
		case OP_ENOTFORMAT:    return "not an Opus file";
		case OP_EBADHEADER:    return "corrupted or undecipherable Ogg header";
		case OP_EVERSION:      return "obsolete or unknown bitstream version";
		case OP_ENOTAUDIO:     return "packet not containing audio data found";
		case OP_EBADPACKET:    return "invalid packet found";
		case OP_EBADLINK:      return "corrupt bitstream";
		case OP_ENOSEEK:       return "bitstream not seekable";
		case OP_EBADTIMESTAMP: return "invalid packet timestamp";
		case OP_FALSE:
		default:               return "unknown error";
	}
}

void * opus_init(int fd, Decoder *dec)
{
	const char *path = dec->tinfo->path;

	OpusFileCallbacks opcb;
	FILE *stream = op_fdopen(&opcb, fd, "rb");
	if (!stream)
	{
		log_append(LOG_ERR, "%s: couldn't fdopen", path);
		return NULL;
	}

	int ret;
	OggOpusFile *of = op_open_callbacks(stream, &opcb, NULL, 0, &ret);
	if (ret)
	{
		log_append(LOG_WARNING, "%s: %s", path, errormsg(ret));
		op_free(of);
		return NULL;
	}

	int nbchan = op_channel_count(of, -1);
	if (nbchan > 2)
	{
		log_append(LOG_ERR, "%s: only stereo and mono are supported",
			path);
		op_free(of);
		return NULL;
	}
	dec->tinfo->format.bits = 0;
	dec->tinfo->format.rate = 48000;
	dec->tinfo->format.channels = nbchan;
	dec->tinfo->bitrate = op_bitrate(of, -1);

	dec->tinfo->duration = duration_init(op_pcm_total(of, -1), 48000);
	dec->tinfo->position = duration_init(0,                    48000);

	const OpusTags *ot = op_tags(of, -1);
	for (int i = 0; i < ot->comments; ++i)
		tag_vorbis_comment_parse(ot->user_comments[i], dec->tinfo->tags);

	if (!dec->tinfo->tags[RG_ALBUM_GAIN].empty ||
	   !dec->tinfo->tags[RG_TRACK_GAIN].empty)
	{
		log_append(LOG_WARNING, "%s: invalid ReplayGain tags, use R128_ "
			"tags", path);
		if (dec->tinfo->gain != 1.f)
		{
			/* Adapt to EBU R128 reference level: -5 dB*/
			const uint32_t gain = lrintf((dec->tinfo->gain - 5) * 256);
			op_set_gain_offset(of, OP_ABSOLUTE_GAIN, gain);
		}
	}
	else
	{
		switch (dec->rgtype)
		{
			case ALBUM_GAIN:
				op_set_gain_offset(of, OP_ALBUM_GAIN, 5 * 256);
				break;

			case TRACK_GAIN:
				op_set_gain_offset(of, OP_TRACK_GAIN, 5 * 256);
				break;

			case NO_GAIN:
				;
		}
	}

	return of;
}

void opus_free(void *opus_dec)
{
	op_free(opus_dec);
}

void * opus_routine(void *arg)
{
	Decoder *dec = arg;
	OggOpusFile *of = dec->ctx;

	static int16_t buf[BUFSIZ];
	int ret;

	while ((ret = op_read(of, buf, BUFSIZ, NULL)) > 0)
	{
		if (dec->cancel_flag)
			break;
		dec->tinfo->position->nb_samples += ret;
		write_pcm(buf, dec->pipefd_wr, ret * dec->tinfo->format.channels);
	}
	if (ret != 0)
		log_append(LOG_ERR, "%s: %s", dec->tinfo->path, errormsg(ret));

	decoder_end(dec);
	return NULL;
}