~q3cpma/mus

ref: cf29c2237d0c99e5240a55895c07564ef4635392 mus/mus_player/vorbis.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
132
133
134
135
#include <vorbis/vorbisfile.h>

#include "filter.h"
#include "log.h"
#include "misc.h"
#include "tag.h"
#include "vorbis.h"


static const char * errormsg(int code)
{
	switch (code)
	{
		case OV_HOLE:       return "missing or corrupt data encountered";
		case OV_EREAD:      return "read error";
		case OV_EFAULT:     return "internal decoder failure";
		case OV_EIMPL:      return "feature not implemented";
		case OV_EINVAL:     return "invalid or uninitialized argument";
		case OV_ENOTVORBIS: return "not a Vorbis file";
		case OV_EBADHEADER: return "corrupted or undecipherable Ogg header";
		case OV_EVERSION:   return "obsolete bitstream version";
		case OV_ENOTAUDIO:  return "packet not containing audio data found";
		case OV_EBADPACKET: return "invalid packet found";
		case OV_EBADLINK:   return "corrupt bitstream";
		case OV_ENOSEEK:    return "bitstream not seekable";
		case OV_FALSE:
		default:            return "unknown error";
	}
}

void * vorbis_init(int fd, Decoder *dec)
{
	const char *path = dec->tinfo->path;
	OggVorbis_File *vf = xmalloc(sizeof(OggVorbis_File));

	FILE *stream = fdopen(fd, "rb");
	if (!stream)
	{
		log_append(LOG_ERR, "%s: couldn't fdopen", path);
		return NULL;
	}
	const int ret = ov_open(stream, vf, NULL, 0);
	if (ret)
	{
		log_append(LOG_WARNING, "%s: %s", path, errormsg(ret));
		vorbis_free(vf);
		return NULL;
	}
	const vorbis_info *vi = ov_info(vf, -1);
	if (!vi)
	{
		log_append(LOG_WARNING, "%s: %s", path, errormsg(OV_EINVAL));
		vorbis_free(vf);
		return NULL;
	}
	if (vi->channels > 2)
	{
		log_append(LOG_ERR, "%s: only stereo and mono are supported",
			path);
		vorbis_free(vf);
		return NULL;
	}
	dec->tinfo->format.bits = 0;
	dec->tinfo->format.rate = vi->rate;
	dec->tinfo->format.channels = vi->channels;
	dec->tinfo->bitrate = vi->bitrate_nominal;

	dec->tinfo->duration = duration_init(ov_pcm_total(vf, -1), vi->rate);
	dec->tinfo->position = duration_init(0,					   vi->rate);

	const vorbis_comment *vc = ov_comment(vf, -1);
	for (int i = 0; i < vc->comments; ++i)
		tag_vorbis_comment_parse(vc->user_comments[i], dec->tinfo->tags);

	return vf;
}

void vorbis_free(void *vf)
{
	ov_clear((OggVorbis_File *)vf);
	free(vf);
}

static void vorbis_apply_gain(float **pcm, long channels, long samples,
	void *arg)
{
	const float gain = *(float *)arg;
	if (gain == 1.f)
		return;

	switch (channels)
	{
		case 1:
			for (long i = 0; i < samples; ++i)
				pcm[0][i] *= gain;
			break;

		case 2:
			for (long i = 0; i < samples; ++i)
			{
				pcm[0][i] *= gain;
				pcm[1][i] *= gain;
			}
			break;

		default: ; /* Only mono and stereo are accepted in vorbis_init() */
	}
}

void * vorbis_routine(void *arg)
{
	Decoder *dec = arg;
	OggVorbis_File *vf = dec->ctx;

	static char buf[BUFSIZ];
	const bool endianness = isbigendian();
	int bitstream;
	long ret;

	while ((ret = ov_read_filter(vf, buf, BUFSIZ, endianness, sizeof(int16_t),
				1, &bitstream, vorbis_apply_gain, &dec->tinfo->gain)) > 0)
	{
		if (dec->cancel_flag)
			break;
		dec->tinfo->position->nb_samples += ret / sizeof(int16_t) /
			dec->tinfo->format.channels;
		write_pcm((int16_t *)buf, dec->pipefd_wr, ret / sizeof(int16_t));
	}

	if (ret != 0)
		log_append(LOG_ERR, "%s: %s", dec->tinfo->path, errormsg(ret));

	decoder_end(dec);
	return NULL;
}