Chris@0: /* Chris@0: * libmad - MPEG audio decoder library Chris@0: * Copyright (C) 2000-2004 Underbit Technologies, Inc. Chris@0: * Chris@0: * This program is free software; you can redistribute it and/or modify Chris@0: * it under the terms of the GNU General Public License as published by Chris@0: * the Free Software Foundation; either version 2 of the License, or Chris@0: * (at your option) any later version. Chris@0: * Chris@0: * This program is distributed in the hope that it will be useful, Chris@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of Chris@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Chris@0: * GNU General Public License for more details. Chris@0: * Chris@0: * You should have received a copy of the GNU General Public License Chris@0: * along with this program; if not, write to the Free Software Chris@0: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Chris@0: * Chris@0: * $Id: timer.c,v 1.18 2004/01/23 09:41:33 rob Exp $ Chris@0: */ Chris@0: Chris@0: # ifdef HAVE_CONFIG_H Chris@0: # include "config.h" Chris@0: # endif Chris@0: Chris@0: # include "global.h" Chris@0: Chris@0: # include Chris@0: Chris@0: # ifdef HAVE_ASSERT_H Chris@0: # include Chris@0: # endif Chris@0: Chris@0: # include "timer.h" Chris@0: Chris@0: mad_timer_t const mad_timer_zero = { 0, 0 }; Chris@0: Chris@0: /* Chris@0: * NAME: timer->compare() Chris@0: * DESCRIPTION: indicate relative order of two timers Chris@0: */ Chris@0: int mad_timer_compare(mad_timer_t timer1, mad_timer_t timer2) Chris@0: { Chris@0: signed long diff; Chris@0: Chris@0: diff = timer1.seconds - timer2.seconds; Chris@0: if (diff < 0) Chris@0: return -1; Chris@0: else if (diff > 0) Chris@0: return +1; Chris@0: Chris@0: diff = timer1.fraction - timer2.fraction; Chris@0: if (diff < 0) Chris@0: return -1; Chris@0: else if (diff > 0) Chris@0: return +1; Chris@0: Chris@0: return 0; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->negate() Chris@0: * DESCRIPTION: invert the sign of a timer Chris@0: */ Chris@0: void mad_timer_negate(mad_timer_t *timer) Chris@0: { Chris@0: timer->seconds = -timer->seconds; Chris@0: Chris@0: if (timer->fraction) { Chris@0: timer->seconds -= 1; Chris@0: timer->fraction = MAD_TIMER_RESOLUTION - timer->fraction; Chris@0: } Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->abs() Chris@0: * DESCRIPTION: return the absolute value of a timer Chris@0: */ Chris@0: mad_timer_t mad_timer_abs(mad_timer_t timer) Chris@0: { Chris@0: if (timer.seconds < 0) Chris@0: mad_timer_negate(&timer); Chris@0: Chris@0: return timer; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: reduce_timer() Chris@0: * DESCRIPTION: carry timer fraction into seconds Chris@0: */ Chris@0: static Chris@0: void reduce_timer(mad_timer_t *timer) Chris@0: { Chris@0: timer->seconds += timer->fraction / MAD_TIMER_RESOLUTION; Chris@0: timer->fraction %= MAD_TIMER_RESOLUTION; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: gcd() Chris@0: * DESCRIPTION: compute greatest common denominator Chris@0: */ Chris@0: static Chris@0: unsigned long gcd(unsigned long num1, unsigned long num2) Chris@0: { Chris@0: unsigned long tmp; Chris@0: Chris@0: while (num2) { Chris@0: tmp = num2; Chris@0: num2 = num1 % num2; Chris@0: num1 = tmp; Chris@0: } Chris@0: Chris@0: return num1; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: reduce_rational() Chris@0: * DESCRIPTION: convert rational expression to lowest terms Chris@0: */ Chris@0: static Chris@0: void reduce_rational(unsigned long *numer, unsigned long *denom) Chris@0: { Chris@0: unsigned long factor; Chris@0: Chris@0: factor = gcd(*numer, *denom); Chris@0: Chris@0: assert(factor != 0); Chris@0: Chris@0: *numer /= factor; Chris@0: *denom /= factor; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: scale_rational() Chris@0: * DESCRIPTION: solve numer/denom == ?/scale avoiding overflowing Chris@0: */ Chris@0: static Chris@0: unsigned long scale_rational(unsigned long numer, unsigned long denom, Chris@0: unsigned long scale) Chris@0: { Chris@0: reduce_rational(&numer, &denom); Chris@0: reduce_rational(&scale, &denom); Chris@0: Chris@0: assert(denom != 0); Chris@0: Chris@0: if (denom < scale) Chris@0: return numer * (scale / denom) + numer * (scale % denom) / denom; Chris@0: if (denom < numer) Chris@0: return scale * (numer / denom) + scale * (numer % denom) / denom; Chris@0: Chris@0: return numer * scale / denom; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->set() Chris@0: * DESCRIPTION: set timer to specific (positive) value Chris@0: */ Chris@0: void mad_timer_set(mad_timer_t *timer, unsigned long seconds, Chris@0: unsigned long numer, unsigned long denom) Chris@0: { Chris@0: timer->seconds = seconds; Chris@0: if (numer >= denom && denom > 0) { Chris@0: timer->seconds += numer / denom; Chris@0: numer %= denom; Chris@0: } Chris@0: Chris@0: switch (denom) { Chris@0: case 0: Chris@0: case 1: Chris@0: timer->fraction = 0; Chris@0: break; Chris@0: Chris@0: case MAD_TIMER_RESOLUTION: Chris@0: timer->fraction = numer; Chris@0: break; Chris@0: Chris@0: case 1000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 1000); Chris@0: break; Chris@0: Chris@0: case 8000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 8000); Chris@0: break; Chris@0: Chris@0: case 11025: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 11025); Chris@0: break; Chris@0: Chris@0: case 12000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 12000); Chris@0: break; Chris@0: Chris@0: case 16000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 16000); Chris@0: break; Chris@0: Chris@0: case 22050: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 22050); Chris@0: break; Chris@0: Chris@0: case 24000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 24000); Chris@0: break; Chris@0: Chris@0: case 32000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 32000); Chris@0: break; Chris@0: Chris@0: case 44100: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 44100); Chris@0: break; Chris@0: Chris@0: case 48000: Chris@0: timer->fraction = numer * (MAD_TIMER_RESOLUTION / 48000); Chris@0: break; Chris@0: Chris@0: default: Chris@0: timer->fraction = scale_rational(numer, denom, MAD_TIMER_RESOLUTION); Chris@0: break; Chris@0: } Chris@0: Chris@0: if (timer->fraction >= MAD_TIMER_RESOLUTION) Chris@0: reduce_timer(timer); Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->add() Chris@0: * DESCRIPTION: add one timer to another Chris@0: */ Chris@0: void mad_timer_add(mad_timer_t *timer, mad_timer_t incr) Chris@0: { Chris@0: timer->seconds += incr.seconds; Chris@0: timer->fraction += incr.fraction; Chris@0: Chris@0: if (timer->fraction >= MAD_TIMER_RESOLUTION) Chris@0: reduce_timer(timer); Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->multiply() Chris@0: * DESCRIPTION: multiply a timer by a scalar value Chris@0: */ Chris@0: void mad_timer_multiply(mad_timer_t *timer, signed long scalar) Chris@0: { Chris@0: mad_timer_t addend; Chris@0: unsigned long factor; Chris@0: Chris@0: factor = scalar; Chris@0: if (scalar < 0) { Chris@0: factor = -scalar; Chris@0: mad_timer_negate(timer); Chris@0: } Chris@0: Chris@0: addend = *timer; Chris@0: *timer = mad_timer_zero; Chris@0: Chris@0: while (factor) { Chris@0: if (factor & 1) Chris@0: mad_timer_add(timer, addend); Chris@0: Chris@0: mad_timer_add(&addend, addend); Chris@0: factor >>= 1; Chris@0: } Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->count() Chris@0: * DESCRIPTION: return timer value in selected units Chris@0: */ Chris@0: signed long mad_timer_count(mad_timer_t timer, enum mad_units units) Chris@0: { Chris@0: switch (units) { Chris@0: case MAD_UNITS_HOURS: Chris@0: return timer.seconds / 60 / 60; Chris@0: Chris@0: case MAD_UNITS_MINUTES: Chris@0: return timer.seconds / 60; Chris@0: Chris@0: case MAD_UNITS_SECONDS: Chris@0: return timer.seconds; Chris@0: Chris@0: case MAD_UNITS_DECISECONDS: Chris@0: case MAD_UNITS_CENTISECONDS: Chris@0: case MAD_UNITS_MILLISECONDS: Chris@0: Chris@0: case MAD_UNITS_8000_HZ: Chris@0: case MAD_UNITS_11025_HZ: Chris@0: case MAD_UNITS_12000_HZ: Chris@0: case MAD_UNITS_16000_HZ: Chris@0: case MAD_UNITS_22050_HZ: Chris@0: case MAD_UNITS_24000_HZ: Chris@0: case MAD_UNITS_32000_HZ: Chris@0: case MAD_UNITS_44100_HZ: Chris@0: case MAD_UNITS_48000_HZ: Chris@0: Chris@0: case MAD_UNITS_24_FPS: Chris@0: case MAD_UNITS_25_FPS: Chris@0: case MAD_UNITS_30_FPS: Chris@0: case MAD_UNITS_48_FPS: Chris@0: case MAD_UNITS_50_FPS: Chris@0: case MAD_UNITS_60_FPS: Chris@0: case MAD_UNITS_75_FPS: Chris@0: return timer.seconds * (signed long) units + Chris@0: (signed long) scale_rational(timer.fraction, MAD_TIMER_RESOLUTION, Chris@0: units); Chris@0: Chris@0: case MAD_UNITS_23_976_FPS: Chris@0: case MAD_UNITS_24_975_FPS: Chris@0: case MAD_UNITS_29_97_FPS: Chris@0: case MAD_UNITS_47_952_FPS: Chris@0: case MAD_UNITS_49_95_FPS: Chris@0: case MAD_UNITS_59_94_FPS: Chris@0: return (mad_timer_count(timer, -units) + 1) * 1000 / 1001; Chris@0: } Chris@0: Chris@0: /* unsupported units */ Chris@0: return 0; Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->fraction() Chris@0: * DESCRIPTION: return fractional part of timer in arbitrary terms Chris@0: */ Chris@0: unsigned long mad_timer_fraction(mad_timer_t timer, unsigned long denom) Chris@0: { Chris@0: timer = mad_timer_abs(timer); Chris@0: Chris@0: switch (denom) { Chris@0: case 0: Chris@0: return timer.fraction ? Chris@0: MAD_TIMER_RESOLUTION / timer.fraction : MAD_TIMER_RESOLUTION + 1; Chris@0: Chris@0: case MAD_TIMER_RESOLUTION: Chris@0: return timer.fraction; Chris@0: Chris@0: default: Chris@0: return scale_rational(timer.fraction, MAD_TIMER_RESOLUTION, denom); Chris@0: } Chris@0: } Chris@0: Chris@0: /* Chris@0: * NAME: timer->string() Chris@0: * DESCRIPTION: write a string representation of a timer using a template Chris@0: */ Chris@0: void mad_timer_string(mad_timer_t timer, Chris@0: char *dest, char const *format, enum mad_units units, Chris@0: enum mad_units fracunits, unsigned long subparts) Chris@0: { Chris@0: unsigned long hours, minutes, seconds, sub; Chris@0: unsigned int frac; Chris@0: Chris@0: timer = mad_timer_abs(timer); Chris@0: Chris@0: seconds = timer.seconds; Chris@0: frac = sub = 0; Chris@0: Chris@0: switch (fracunits) { Chris@0: case MAD_UNITS_HOURS: Chris@0: case MAD_UNITS_MINUTES: Chris@0: case MAD_UNITS_SECONDS: Chris@0: break; Chris@0: Chris@0: case MAD_UNITS_DECISECONDS: Chris@0: case MAD_UNITS_CENTISECONDS: Chris@0: case MAD_UNITS_MILLISECONDS: Chris@0: Chris@0: case MAD_UNITS_8000_HZ: Chris@0: case MAD_UNITS_11025_HZ: Chris@0: case MAD_UNITS_12000_HZ: Chris@0: case MAD_UNITS_16000_HZ: Chris@0: case MAD_UNITS_22050_HZ: Chris@0: case MAD_UNITS_24000_HZ: Chris@0: case MAD_UNITS_32000_HZ: Chris@0: case MAD_UNITS_44100_HZ: Chris@0: case MAD_UNITS_48000_HZ: Chris@0: Chris@0: case MAD_UNITS_24_FPS: Chris@0: case MAD_UNITS_25_FPS: Chris@0: case MAD_UNITS_30_FPS: Chris@0: case MAD_UNITS_48_FPS: Chris@0: case MAD_UNITS_50_FPS: Chris@0: case MAD_UNITS_60_FPS: Chris@0: case MAD_UNITS_75_FPS: Chris@0: { Chris@0: unsigned long denom; Chris@0: Chris@0: denom = MAD_TIMER_RESOLUTION / fracunits; Chris@0: Chris@0: frac = timer.fraction / denom; Chris@0: sub = scale_rational(timer.fraction % denom, denom, subparts); Chris@0: } Chris@0: break; Chris@0: Chris@0: case MAD_UNITS_23_976_FPS: Chris@0: case MAD_UNITS_24_975_FPS: Chris@0: case MAD_UNITS_29_97_FPS: Chris@0: case MAD_UNITS_47_952_FPS: Chris@0: case MAD_UNITS_49_95_FPS: Chris@0: case MAD_UNITS_59_94_FPS: Chris@0: /* drop-frame encoding */ Chris@0: /* N.B. this is only well-defined for MAD_UNITS_29_97_FPS */ Chris@0: { Chris@0: unsigned long frame, cycle, d, m; Chris@0: Chris@0: frame = mad_timer_count(timer, fracunits); Chris@0: Chris@0: cycle = -fracunits * 60 * 10 - (10 - 1) * 2; Chris@0: Chris@0: d = frame / cycle; Chris@0: m = frame % cycle; Chris@0: frame += (10 - 1) * 2 * d; Chris@0: if (m > 2) Chris@0: frame += 2 * ((m - 2) / (cycle / 10)); Chris@0: Chris@0: frac = frame % -fracunits; Chris@0: seconds = frame / -fracunits; Chris@0: } Chris@0: break; Chris@0: } Chris@0: Chris@0: switch (units) { Chris@0: case MAD_UNITS_HOURS: Chris@0: minutes = seconds / 60; Chris@0: hours = minutes / 60; Chris@0: Chris@0: sprintf(dest, format, Chris@0: hours, Chris@0: (unsigned int) (minutes % 60), Chris@0: (unsigned int) (seconds % 60), Chris@0: frac, sub); Chris@0: break; Chris@0: Chris@0: case MAD_UNITS_MINUTES: Chris@0: minutes = seconds / 60; Chris@0: Chris@0: sprintf(dest, format, Chris@0: minutes, Chris@0: (unsigned int) (seconds % 60), Chris@0: frac, sub); Chris@0: break; Chris@0: Chris@0: case MAD_UNITS_SECONDS: Chris@0: sprintf(dest, format, Chris@0: seconds, Chris@0: frac, sub); Chris@0: break; Chris@0: Chris@0: case MAD_UNITS_23_976_FPS: Chris@0: case MAD_UNITS_24_975_FPS: Chris@0: case MAD_UNITS_29_97_FPS: Chris@0: case MAD_UNITS_47_952_FPS: Chris@0: case MAD_UNITS_49_95_FPS: Chris@0: case MAD_UNITS_59_94_FPS: Chris@0: if (fracunits < 0) { Chris@0: /* not yet implemented */ Chris@0: sub = 0; Chris@0: } Chris@0: Chris@0: /* fall through */ Chris@0: Chris@0: case MAD_UNITS_DECISECONDS: Chris@0: case MAD_UNITS_CENTISECONDS: Chris@0: case MAD_UNITS_MILLISECONDS: Chris@0: Chris@0: case MAD_UNITS_8000_HZ: Chris@0: case MAD_UNITS_11025_HZ: Chris@0: case MAD_UNITS_12000_HZ: Chris@0: case MAD_UNITS_16000_HZ: Chris@0: case MAD_UNITS_22050_HZ: Chris@0: case MAD_UNITS_24000_HZ: Chris@0: case MAD_UNITS_32000_HZ: Chris@0: case MAD_UNITS_44100_HZ: Chris@0: case MAD_UNITS_48000_HZ: Chris@0: Chris@0: case MAD_UNITS_24_FPS: Chris@0: case MAD_UNITS_25_FPS: Chris@0: case MAD_UNITS_30_FPS: Chris@0: case MAD_UNITS_48_FPS: Chris@0: case MAD_UNITS_50_FPS: Chris@0: case MAD_UNITS_60_FPS: Chris@0: case MAD_UNITS_75_FPS: Chris@0: sprintf(dest, format, mad_timer_count(timer, units), sub); Chris@0: break; Chris@0: } Chris@0: }