analyze.c 9.23 KB
Newer Older
Josh Coalson's avatar
Josh Coalson committed
1
/* flac - Command-line FLAC encoder/decoder
Josh Coalson's avatar
Josh Coalson committed
2
 * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009  Josh Coalson
Josh Coalson's avatar
Josh Coalson committed
3 4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
Miroslav Lichvar's avatar
Miroslav Lichvar committed
14 15 16
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Josh Coalson's avatar
Josh Coalson committed
17 18
 */

Josh Coalson's avatar
Josh Coalson committed
19 20 21 22 23
#if HAVE_CONFIG_H
#  include <config.h>
#endif

#include <errno.h>
24
#include <math.h>
Josh Coalson's avatar
Josh Coalson committed
25 26 27
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
28
#include <inttypes.h>
Josh Coalson's avatar
Josh Coalson committed
29 30 31 32
#include "FLAC/all.h"
#include "analyze.h"

typedef struct {
Josh Coalson's avatar
Josh Coalson committed
33
	FLAC__int32 residual;
Josh Coalson's avatar
Josh Coalson committed
34 35 36
	unsigned count;
} pair_t;

37 38 39 40 41 42 43 44 45 46 47 48 49 50
typedef struct {
	pair_t buckets[FLAC__MAX_BLOCK_SIZE];
	int peak_index;
	unsigned nbuckets;
	unsigned nsamples;
	double sum, sos;
	double variance;
	double mean;
	double stddev;
} subframe_stats_t;

static subframe_stats_t all_;

static void init_stats(subframe_stats_t *stats);
Josh Coalson's avatar
Josh Coalson committed
51
static void update_stats(subframe_stats_t *stats, FLAC__int32 residual, unsigned incr);
52
static void compute_stats(subframe_stats_t *stats);
Josh Coalson's avatar
Josh Coalson committed
53
static FLAC__bool dump_stats(const subframe_stats_t *stats, const char *filename);
Josh Coalson's avatar
Josh Coalson committed
54

Josh Coalson's avatar
Josh Coalson committed
55
void flac__analyze_init(analysis_options aopts)
Josh Coalson's avatar
Josh Coalson committed
56
{
57 58 59
	if(aopts.do_residual_gnuplot) {
		init_stats(&all_);
	}
Josh Coalson's avatar
Josh Coalson committed
60 61
}

62
void flac__analyze_frame(const FLAC__Frame *frame, unsigned frame_number, FLAC__uint64 frame_offset, unsigned frame_bytes, analysis_options aopts, FILE *fout)
Josh Coalson's avatar
Josh Coalson committed
63 64 65
{
	const unsigned channels = frame->header.channels;
	char outfilename[1024];
66
	subframe_stats_t stats;
67
	unsigned i, channel, partitions;
Josh Coalson's avatar
Josh Coalson committed
68 69

	/* do the human-readable part first */
70
	fprintf(fout, "frame=%u\toffset=%" PRIu64 "\tbits=%u\tblocksize=%u\tsample_rate=%u\tchannels=%u\tchannel_assignment=%s\n", frame_number, frame_offset, frame_bytes*8, frame->header.blocksize, frame->header.sample_rate, channels, FLAC__ChannelAssignmentString[frame->header.channel_assignment]);
Josh Coalson's avatar
Josh Coalson committed
71 72
	for(channel = 0; channel < channels; channel++) {
		const FLAC__Subframe *subframe = frame->subframes+channel;
73 74
		const FLAC__bool is_rice2 = subframe->data.fixed.entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2;
		const unsigned pesc = is_rice2? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER;
75
		fprintf(fout, "\tsubframe=%u\twasted_bits=%u\ttype=%s", channel, subframe->wasted_bits, FLAC__SubframeTypeString[subframe->type]);
Josh Coalson's avatar
Josh Coalson committed
76 77 78 79 80
		switch(subframe->type) {
			case FLAC__SUBFRAME_TYPE_CONSTANT:
				fprintf(fout, "\tvalue=%d\n", subframe->data.constant.value);
				break;
			case FLAC__SUBFRAME_TYPE_FIXED:
81 82
				FLAC__ASSERT(subframe->data.fixed.entropy_coding_method.type <= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2);
				fprintf(fout, "\torder=%u\tresidual_type=%s\tpartition_order=%u\n", subframe->data.fixed.order, is_rice2? "RICE2":"RICE", subframe->data.fixed.entropy_coding_method.data.partitioned_rice.order);
Josh Coalson's avatar
Josh Coalson committed
83 84
				for(i = 0; i < subframe->data.fixed.order; i++)
					fprintf(fout, "\t\twarmup[%u]=%d\n", i, subframe->data.fixed.warmup[i]);
85 86 87 88 89 90 91 92
				partitions = (1u << subframe->data.fixed.entropy_coding_method.data.partitioned_rice.order);
				for(i = 0; i < partitions; i++) {
					unsigned parameter = subframe->data.fixed.entropy_coding_method.data.partitioned_rice.contents->parameters[i];
					if(parameter == pesc)
						fprintf(fout, "\t\tparameter[%u]=ESCAPE, raw_bits=%u\n", i, subframe->data.fixed.entropy_coding_method.data.partitioned_rice.contents->raw_bits[i]);
					else
						fprintf(fout, "\t\tparameter[%u]=%u\n", i, parameter);
				}
Josh Coalson's avatar
Josh Coalson committed
93 94 95 96 97 98
				if(aopts.do_residual_text) {
					for(i = 0; i < frame->header.blocksize-subframe->data.fixed.order; i++)
						fprintf(fout, "\t\tresidual[%u]=%d\n", i, subframe->data.fixed.residual[i]);
				}
				break;
			case FLAC__SUBFRAME_TYPE_LPC:
99 100
				FLAC__ASSERT(subframe->data.lpc.entropy_coding_method.type <= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2);
				fprintf(fout, "\torder=%u\tqlp_coeff_precision=%u\tquantization_level=%d\tresidual_type=%s\tpartition_order=%u\n", subframe->data.lpc.order, subframe->data.lpc.qlp_coeff_precision, subframe->data.lpc.quantization_level, is_rice2? "RICE2":"RICE", subframe->data.lpc.entropy_coding_method.data.partitioned_rice.order);
101 102
				for(i = 0; i < subframe->data.lpc.order; i++)
					fprintf(fout, "\t\tqlp_coeff[%u]=%d\n", i, subframe->data.lpc.qlp_coeff[i]);
Josh Coalson's avatar
Josh Coalson committed
103 104
				for(i = 0; i < subframe->data.lpc.order; i++)
					fprintf(fout, "\t\twarmup[%u]=%d\n", i, subframe->data.lpc.warmup[i]);
105 106 107 108 109 110 111 112
				partitions = (1u << subframe->data.lpc.entropy_coding_method.data.partitioned_rice.order);
				for(i = 0; i < partitions; i++) {
					unsigned parameter = subframe->data.lpc.entropy_coding_method.data.partitioned_rice.contents->parameters[i];
					if(parameter == pesc)
						fprintf(fout, "\t\tparameter[%u]=ESCAPE, raw_bits=%u\n", i, subframe->data.lpc.entropy_coding_method.data.partitioned_rice.contents->raw_bits[i]);
					else
						fprintf(fout, "\t\tparameter[%u]=%u\n", i, parameter);
				}
Josh Coalson's avatar
Josh Coalson committed
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
				if(aopts.do_residual_text) {
					for(i = 0; i < frame->header.blocksize-subframe->data.lpc.order; i++)
						fprintf(fout, "\t\tresidual[%u]=%d\n", i, subframe->data.lpc.residual[i]);
				}
				break;
			case FLAC__SUBFRAME_TYPE_VERBATIM:
				fprintf(fout, "\n");
				break;
		}
	}

	/* now do the residual distributions if requested */
	if(aopts.do_residual_gnuplot) {
		for(channel = 0; channel < channels; channel++) {
			const FLAC__Subframe *subframe = frame->subframes+channel;
128
			unsigned residual_samples;
Josh Coalson's avatar
Josh Coalson committed
129

130
			init_stats(&stats);
Josh Coalson's avatar
Josh Coalson committed
131 132 133

			switch(subframe->type) {
				case FLAC__SUBFRAME_TYPE_FIXED:
134 135 136
					residual_samples = frame->header.blocksize - subframe->data.fixed.order;
					for(i = 0; i < residual_samples; i++)
						update_stats(&stats, subframe->data.fixed.residual[i], 1);
Josh Coalson's avatar
Josh Coalson committed
137 138
					break;
				case FLAC__SUBFRAME_TYPE_LPC:
139 140 141
					residual_samples = frame->header.blocksize - subframe->data.lpc.order;
					for(i = 0; i < residual_samples; i++)
						update_stats(&stats, subframe->data.lpc.residual[i], 1);
Josh Coalson's avatar
Josh Coalson committed
142 143 144 145 146
					break;
				default:
					break;
			}

147 148 149
			/* update all_ */
			for(i = 0; i < stats.nbuckets; i++) {
				update_stats(&all_, stats.buckets[i].residual, stats.buckets[i].count);
Josh Coalson's avatar
Josh Coalson committed
150 151 152 153
			}

			/* write the subframe */
			sprintf(outfilename, "f%06u.s%u.gp", frame_number, channel);
154
			compute_stats(&stats);
155

156
			(void)dump_stats(&stats, outfilename);
Josh Coalson's avatar
Josh Coalson committed
157 158 159 160
		}
	}
}

Josh Coalson's avatar
Josh Coalson committed
161
void flac__analyze_finish(analysis_options aopts)
Josh Coalson's avatar
Josh Coalson committed
162
{
163 164 165 166
	if(aopts.do_residual_gnuplot) {
		compute_stats(&all_);
		(void)dump_stats(&all_, "all");
	}
167 168 169 170 171 172 173 174 175
}

void init_stats(subframe_stats_t *stats)
{
	stats->peak_index = -1;
	stats->nbuckets = 0;
	stats->nsamples = 0;
	stats->sum = 0.0;
	stats->sos = 0.0;
Josh Coalson's avatar
Josh Coalson committed
176 177
}

Josh Coalson's avatar
Josh Coalson committed
178
void update_stats(subframe_stats_t *stats, FLAC__int32 residual, unsigned incr)
Josh Coalson's avatar
Josh Coalson committed
179 180
{
	unsigned i;
181 182 183 184 185
	const double r = (double)residual, a = r*incr;

	stats->nsamples += incr;
	stats->sum += a;
	stats->sos += (a*r);
Josh Coalson's avatar
Josh Coalson committed
186

187 188 189 190
	for(i = 0; i < stats->nbuckets; i++) {
		if(stats->buckets[i].residual == residual) {
			stats->buckets[i].count += incr;
			goto find_peak;
Josh Coalson's avatar
Josh Coalson committed
191 192
		}
	}
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
	/* not found, make a new bucket */
	i = stats->nbuckets;
	stats->buckets[i].residual = residual;
	stats->buckets[i].count = incr;
	stats->nbuckets++;
find_peak:
	if(stats->peak_index < 0 || stats->buckets[i].count > stats->buckets[stats->peak_index].count)
		stats->peak_index = i;
}

void compute_stats(subframe_stats_t *stats)
{
	stats->mean = stats->sum / (double)stats->nsamples;
	stats->variance = (stats->sos - (stats->sum * stats->sum / stats->nsamples)) / stats->nsamples;
	stats->stddev = sqrt(stats->variance);
Josh Coalson's avatar
Josh Coalson committed
208 209
}

Josh Coalson's avatar
Josh Coalson committed
210
FLAC__bool dump_stats(const subframe_stats_t *stats, const char *filename)
Josh Coalson's avatar
Josh Coalson committed
211 212 213
{
	FILE *outfile;
	unsigned i;
214 215 216
	const double m = stats->mean;
	const double s1 = stats->stddev, s2 = s1*2, s3 = s1*3, s4 = s1*4, s5 = s1*5, s6 = s1*6;
	const double p = stats->buckets[stats->peak_index].count;
Josh Coalson's avatar
Josh Coalson committed
217 218 219 220

	outfile = fopen(filename, "w");

	if(0 == outfile) {
Josh Coalson's avatar
Josh Coalson committed
221
		fprintf(stderr, "ERROR opening %s: %s\n", filename, strerror(errno));
Josh Coalson's avatar
Josh Coalson committed
222 223 224
		return false;
	}

225 226 227 228
	fprintf(outfile, "plot '-' title 'PDF', '-' title 'mean' with impulses, '-' title '1-stddev' with histeps, '-' title '2-stddev' with histeps, '-' title '3-stddev' with histeps, '-' title '4-stddev' with histeps, '-' title '5-stddev' with histeps, '-' title '6-stddev' with histeps\n");

	for(i = 0; i < stats->nbuckets; i++) {
		fprintf(outfile, "%d %u\n", stats->buckets[i].residual, stats->buckets[i].count);
Josh Coalson's avatar
Josh Coalson committed
229
	}
230 231 232 233 234 235 236 237 238 239 240
	fprintf(outfile, "e\n");

	fprintf(outfile, "%f %f\ne\n", stats->mean, p);
	fprintf(outfile, "%f %f\n%f %f\ne\n", m-s1, p*0.8, m+s1, p*0.8);
	fprintf(outfile, "%f %f\n%f %f\ne\n", m-s2, p*0.7, m+s2, p*0.7);
	fprintf(outfile, "%f %f\n%f %f\ne\n", m-s3, p*0.6, m+s3, p*0.6);
	fprintf(outfile, "%f %f\n%f %f\ne\n", m-s4, p*0.5, m+s4, p*0.5);
	fprintf(outfile, "%f %f\n%f %f\ne\n", m-s5, p*0.4, m+s5, p*0.4);
	fprintf(outfile, "%f %f\n%f %f\ne\n", m-s6, p*0.3, m+s6, p*0.3);

	fprintf(outfile, "pause -1 'waiting...'\n");
Josh Coalson's avatar
Josh Coalson committed
241 242 243 244

	fclose(outfile);
	return true;
}