Here’s two functions from my MusicMath class that do the conversions between bar/beat/subdivision and time. Note that tempo is in beats per second.
void timeToMetricPosition(double tempo, int subdivisionsPerBeat, int beatsPerMeasure, double time,
int& measure, int& beat, int& subdivision)
double dblSubdivisions = tempo * subdivisionsPerBeat * time;
int subdivisions = static_cast<int>(dblSubdivisions);
int beats = subdivisions / subdivisionsPerBeat;
subdivision = subdivisions % subdivisionsPerBeat;
measure = beats / beatsPerMeasure;
beat = beats % beatsPerMeasure;
double metricPositionToTime(double tempo, int subdivisionsPerBeat, int beatsPerMeasure,
int measure, int beat, int subdivision)
double beats = measure*beatsPerMeasure + beat + static_cast<double>(subdivision)/subdivisionsPerBeat;
The above can be generalized to handle meter changes by implementing a sequence of triples (time, meter, bar#) to encode at what time and bar a meter change occurs. Note the invariant for this sequence is that both time and bar# are monotonic increasing.
Given a time, t, you can compute the metric position (bar/beat/subdivision) by searching the sequence for the time, t0, of the nearest previous meter change (t0 <= t). You then compute the bars/beats/subdivisions in the span t - t0 using the meter at t0 and a function similiar to timeToMetricPosition, above. You get the final answer by adding the bar# at t0 to the previously computed bars to get bars+bar#/beats/subdivisions.
Given a metric position, bar/beat/subdivision, you can compute the time, t, by searching the sequence for the nearest previous meter change, (t0, meter, bar#) (t0 <= t). You then compute, dt, by using a function similiar to metricPositionToTime, above, passing it bar-bar#/beat/subdivision. You then get the final answer, t = t0 + dt.