Skip to content

Commit

Permalink
Updated audio outputs to conform to the AES57-2011 spec
Browse files Browse the repository at this point in the history
  • Loading branch information
pwinckles committed Aug 7, 2018
1 parent a79dc0b commit f6115da
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 305 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -666,16 +666,15 @@ private DefaultMutableTreeNode aesToNode (AESAudioMetadata aes)
region.add (timeline);
int nchan = aes.getNumChannels ();
if (nchan != AESAudioMetadata.NULL) {
String[] locs = aes.getMapLocations ();
region.add (new DefaultMutableTreeNode
("NumChannels: " + Integer.toString (nchan), false));
for (String loc : locs) {
for (int ch = 0; ch < nchan; ch++) {
// write a stream element for each channel
DefaultMutableTreeNode stream =
new DefaultMutableTreeNode ("Stream", true);
region.add (stream);
stream.add (new DefaultMutableTreeNode
("ChannelAssignment: " + loc, false));
("ChannelNum: " + Integer.toString (ch), false));
}
}
face.add (region);
Expand Down Expand Up @@ -745,95 +744,34 @@ private void addAESTimeRange (DefaultMutableTreeNode parent,
AESAudioMetadata.TimeDesc start,
AESAudioMetadata.TimeDesc duration)
{
// Put the start time in
DefaultMutableTreeNode node =
new DefaultMutableTreeNode ("Start", true);
// Put in boilerplate to match the AES schema
node.add (new DefaultMutableTreeNode
("FrameCount: 30", false));
node.add (new DefaultMutableTreeNode
("TimeBase: 1000"));
node.add (new DefaultMutableTreeNode
("VideoField: FIELD_1"));
node.add (new DefaultMutableTreeNode
("CountingMode: NTSC_NON_DROP_FRAME", false));
node.add (new DefaultMutableTreeNode
("Hours: " + start.getHours(), false));
node.add (new DefaultMutableTreeNode
("Minutes: " + start.getMinutes(), false));
node.add (new DefaultMutableTreeNode
("Seconds: " + start.getSeconds(), false));
node.add (new DefaultMutableTreeNode
("Frames: " + start.getFrames(), false));

// Do samples a bit more elaborately than is really necessary,
// to maintain parallelism with the xml schema.
DefaultMutableTreeNode snode = new DefaultMutableTreeNode ("Samples",
true);
double sr = start.getSampleRate ();
if (sr == 1.0) {
sr = _sampleRate;
}
snode.add (new DefaultMutableTreeNode ("SampleRate: S" +
Integer.toString ((int) sr),
false));
snode.add (new DefaultMutableTreeNode ("NumberOfSamples: " +
start.getSamples (), false ));
node.add (snode);

snode = new DefaultMutableTreeNode ("FilmFraming", true);
snode.add (new DefaultMutableTreeNode ("Framing: NOT_APPLICABLE",
false));
snode.add (new DefaultMutableTreeNode ("Type: ntscFilmFramingType",
false));
node.add (snode);
parent.add (node);
writeAESTimeRangePart(parent, "StartTime", start);

// Duration is optional.
if (duration != null) {
node = new DefaultMutableTreeNode ("Duration", true);
// Put in boilerplate to match the AES schema
node.add (new DefaultMutableTreeNode
("FrameCount: 30", false));
node.add (new DefaultMutableTreeNode
("TimeBase: 1000"));
node.add (new DefaultMutableTreeNode
("VideoField: FIELD_1"));
node.add (new DefaultMutableTreeNode
("CountingMode: NTSC_NON_DROP_FRAME", false));
node.add (new DefaultMutableTreeNode
("Hours: " + duration.getHours(), false));
node.add (new DefaultMutableTreeNode
("Minutes: " + duration.getMinutes(), false));
node.add (new DefaultMutableTreeNode
("Seconds: " + duration.getSeconds(), false));
node.add (new DefaultMutableTreeNode
("Frames: " + duration.getFrames(), false));

// Do samples a bit more elaborately than is really necessary,
// to maintain parallelism with the xml schema.
snode = new DefaultMutableTreeNode ("Samples", true);
sr = duration.getSampleRate ();
if (sr == 1.0) {
sr = _sampleRate;
}
snode.add (new DefaultMutableTreeNode ("SamplesRate S" +
Integer.toString ((int) sr),
false));
snode.add (new DefaultMutableTreeNode ("NumberOfSamples: " +
duration.getSamples (),
false ));
node.add (snode);

snode = new DefaultMutableTreeNode ("FilmFraming", true);
snode.add (new DefaultMutableTreeNode ("Framing: NOT_APPLICABLE",
false));
snode.add (new DefaultMutableTreeNode ("Type: ntscFilmFramingType",
false));
node.add (snode);
parent.add (node);
writeAESTimeRangePart(parent, "Duration", duration);
}
}

private void writeAESTimeRangePart(DefaultMutableTreeNode parent,
String name, AESAudioMetadata.TimeDesc timeDesc) {
double sampleRate = timeDesc.getSampleRate ();
if (sampleRate == 1.0) {
sampleRate = _sampleRate;
}

DefaultMutableTreeNode node =
new DefaultMutableTreeNode (name, true);
node.add(new DefaultMutableTreeNode(
"Value: " + String.valueOf(timeDesc.getSamples()), false));
node.add(new DefaultMutableTreeNode(
"EditRate: " + sampleRate, false));
node.add(new DefaultMutableTreeNode(
"FactorNumerator: 1", false));
node.add(new DefaultMutableTreeNode(
"FactorDenominator: 1", false));

parent.add (node);
}

/* Function for turning the textMD metadata into a subtree. */
private DefaultMutableTreeNode textMDToNode (TextMDMetadata textMD)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class AESAudioMetadata
******************************************************************/

/** Constant value for the SchemaVersion field */
public static final String SCHEMA_VERSION = "1.02b";
public static final String SCHEMA_VERSION = "1.0.0";

/** Constant value for the disposition field */
private static final String DEFAULT_DISPOSITION = "validation";
Expand Down Expand Up @@ -160,20 +160,9 @@ public void setBitrateReduction (String codecName,
* accessed through the public methods of this interface.
*/
public static interface TimeDesc {
/** Returns the hours component. */
public long getHours ();
/** Returns the minutes component. */
public long getMinutes ();
/** Returns the seconds component. */
public long getSeconds ();
/** Returns the frames component of the fraction of a second.
* We always consider frames to be thirtieths of a second. */
public long getFrames ();
/** Returns the samples remaining after the frames part of
* the fractional second. */
/** Returns the number of samples. */
public long getSamples ();
/** Returns the sample rate on which the samples remainder
* is based. */
/** Returns the sample rate. */
public double getSampleRate ();
}

Expand Down Expand Up @@ -371,26 +360,12 @@ public void setWordSize (int wordSize)
*/
class TimeDescImpl implements TimeDesc
{
private long _hours;
private long _minutes;
private long _seconds;
private long _frames;
private long _samples;
private double _sampleRate;
private long _frameCount;

/* Constructor rewritten to avoid rounding errors when converting to
* TCF. Now uses integer remainder math instead of floating point.
* Changed the base unit from a double representing seconds to a long
* representing samples. Changed all existing calls (that I could find)
* to this method to accomodate this change.
*
* @author David Ackerman
*/

public TimeDescImpl (long samples)
{
long _sample_count = samples;
_frameCount = 30;
_samples = samples;
_sampleRate = _curFormatRegion.getSampleRate ();

/* It seems that this method is initially called before a valid
Expand All @@ -400,73 +375,15 @@ public TimeDescImpl (long samples)
if (_sampleRate < 0) {
_sampleRate = 44100.0; //reasonable default value
}

long sample_in_1_frame = (long)(_sampleRate/_frameCount);
long sample_in_1_second = sample_in_1_frame * _frameCount;
long sample_in_1_minute = sample_in_1_frame * _frameCount * 60;
long sample_in_1_hour = sample_in_1_frame * _frameCount * 60 * 60;
long sample_in_1_day = sample_in_1_frame * _frameCount * 60 * 60 * 24;

// BWF allows for a negative timestamp but tcf does not, so adjust
// time accordingly
// this might be a good place to report a warning during validation
if (_sample_count < 0) {
_sample_count += sample_in_1_day;
_sample_count = (_sample_count % sample_in_1_day);
}

_hours = _sample_count / sample_in_1_hour;
_sample_count -= _hours * sample_in_1_hour;
_minutes = _sample_count / sample_in_1_minute;
_sample_count -= _minutes * sample_in_1_minute;
_seconds = _sample_count / sample_in_1_second;
_sample_count -= _seconds * sample_in_1_second;
_frames = _sample_count / sample_in_1_frame;
_sample_count -= _frames * sample_in_1_frame;
_samples = _sample_count;

/* At present TCF does not have the ability to handle time stamps
* > midnight. Industry practice is to roll the clock forward to
* zero or back to 23:59:59:29... when crossing this boundary
* condition.
*/
_hours = _hours % 24;
}

/** Returns the hours component. */
@Override
public long getHours () {
return _hours;
}

/** Returns the minutes component. */
@Override
public long getMinutes () {
return _minutes;
}

/** Returns the seconds component. */
@Override
public long getSeconds () {
return _seconds;
}

/** Returns the frames component of the fraction of a second.
* We always consider frames to be thirtieths of a second. */
@Override
public long getFrames () {
return _frames;
}

/** Returns the samples remaining after the frames part of
* the fractional second. */
/** Returns the number of samples. */
@Override
public long getSamples () {
return _samples;
}

/** Returns the sample rate on which the samples remainder
* is based. */
/** Returns the sample rate. */
@Override
public double getSampleRate () {
return _sampleRate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package edu.harvard.hul.ois.jhove.handler;

import edu.harvard.hul.ois.jhove.*;

import java.text.*;
import java.util.*;

Expand Down Expand Up @@ -994,17 +995,22 @@ private void showAESAudioMetadata (AESAudioMetadata aes, String margin,
if (startTime != null) {
writeAESTimeRange (margn3, startTime, f.getDuration());
}

// For the present, assume just one face region
AESAudioMetadata.FaceRegion facergn = f.getFaceRegion(0);
_writer.println(margn3 + "Region: ");
_writer.println (margn4 + "TimeRange: ");
writeAESTimeRange(margn4, facergn.getStartTime(), facergn.getDuration());

int nchan = aes.getNumChannels ();
if (nchan != AESAudioMetadata.NULL) {
_writer.println (margn4 + "NumChannels: " +
Integer.toString (nchan));
}
String[] locs = aes.getMapLocations ();
for (int ch = 0; ch < nchan; ch++) {
// write a stream description for each channel
_writer.println (margn4 + "Stream:");
_writer.println (margn5 + "ChannelNum: " + Integer.toString (ch));
_writer.println (margn5 + "ChannelAssignment: " + locs[ch]);
}
}

Expand Down Expand Up @@ -1062,62 +1068,29 @@ private void writeAESTimeRange (String baseIndent,
AESAudioMetadata.TimeDesc start,
AESAudioMetadata.TimeDesc duration)
{
final String margn1 = baseIndent + " ";
final String margn2 = margn1 + " ";
final String margn3 = margn2 + " ";
_writer.println (margn1 + "StartTime:");
_writer.println (margn2 + "FrameCount: 30");
_writer.println (margn2 + "TimeBase: 1000");
_writer.println (margn2 + "VideoField: FIELD_1");
_writer.println (margn2 + "CountingMode: NTSC_NON_DROP_FRAME");
_writer.println (margn2 + "Hours: " +
Long.toString (start.getHours ()));
_writer.println (margn2 + "Minutes: " +
Long.toString (start.getMinutes ()));
_writer.println (margn2 + "Seconds: " +
Long.toString (start.getSeconds ()));
_writer.println (margn2 + "Frames: " +
Long.toString (start.getFrames ()));
_writer.println (margn2 + "Samples: ");
double sr = start.getSampleRate ();
if (sr == 1.0) {
sr = _sampleRate;
}
_writer.println (margn3 + "SampleRate: S" +
Integer.toString ((int) sr));
_writer.println (margn3 + "NumberOfSamples: " +
Long.toString (start.getSamples ()));
_writer.println (margn2 + "FilmFraming: NOT_APPLICABLE");
_writer.println (margn3 + "Type: ntscFilmFramingType");

writeAESTimeRangePart(baseIndent, "StartTime", start);

if (duration != null) {
_writer.println (margn1 + "Duration:");
_writer.println (margn2 + "FrameCount: 30");
_writer.println (margn2 + "TimeBase: 1000");
_writer.println (margn2 + "VideoField: FIELD_1");
_writer.println (margn2 + "CountingMode: NTSC_NON_DROP_FRAME");
_writer.println (margn2 + "Hours: " +
Long.toString (duration.getHours ()));
_writer.println (margn2 + "Minutes: " +
Long.toString (duration.getMinutes ()));
_writer.println (margn2 + "Seconds: " +
Long.toString (duration.getSeconds ()));
_writer.println (margn2 + "Frames: " +
Long.toString (duration.getFrames ()));
_writer.println (margn2 + "Samples: ");
sr = duration.getSampleRate ();
if (sr == 1.0) {
sr = _sampleRate;
}
_writer.println (margn3 + "SampleRate: S" +
Integer.toString ((int) sr));
_writer.println (margn3 + "NumberOfSamples: " +
Long.toString (duration.getSamples ()));
_writer.println (margn2 + "FilmFraming: NOT_APPLICABLE");
_writer.println (margn3 + "Type: ntscFilmFramingType");
writeAESTimeRangePart(baseIndent, "Duration", duration);
}
}

private void writeAESTimeRangePart(String baseIndent, String name, AESAudioMetadata.TimeDesc timeDesc) {
String margn1 = baseIndent + " ";
String margn2 = margn1 + " ";

_writer.println (margn1 + name + ": ");

double sampleRate = timeDesc.getSampleRate();
if (sampleRate == 1.0) {
sampleRate = _sampleRate;
}

_writer.println (margn2 + "Value: " + String.valueOf(timeDesc.getSamples()));
_writer.println (margn2 + "EditRate: " + Integer.toString ((int) sampleRate));
_writer.println (margn2 + "FactorNumerator: 1");
_writer.println (margn2 + "FactorDenominator: 1");
}

/**
* Display the NISO image metadata formatted according to
Expand Down
Loading

0 comments on commit f6115da

Please sign in to comment.