github.com/boki/go-xmp@v1.0.1/models/ixml/types.go (about)

     1  // Copyright (c) 2017-2018 Alexander Eichhorn
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  package ixml
    16  
    17  import (
    18  	"encoding/xml"
    19  	"fmt"
    20  	"strings"
    21  
    22  	"trimmer.io/go-xmp/models/xmp_dm"
    23  	"trimmer.io/go-xmp/xmp"
    24  )
    25  
    26  type Bool bool
    27  
    28  func (x Bool) Value() bool {
    29  	return bool(x)
    30  }
    31  
    32  func (x Bool) MarshalText() ([]byte, error) {
    33  	if x {
    34  		return []byte("TRUE"), nil
    35  	}
    36  	return []byte("FALSE"), nil
    37  }
    38  
    39  func (x *Bool) UnmarshalText(data []byte) error {
    40  	switch strings.ToLower(string(data)) {
    41  	case "true":
    42  		*x = true
    43  	case "false":
    44  		*x = false
    45  	default:
    46  		return fmt.Errorf("ixml: invalid bool value '%s'", string(data))
    47  	}
    48  	return nil
    49  }
    50  
    51  type SyncPoint struct {
    52  	XMLName       xml.Name              `xml:"SYNC_POINT"                            xmp:"-"`
    53  	Type          SyncPointType         `xml:"SYNC_POINT_TYPE"                       xmp:"iXML:syncPointType"`
    54  	Function      SyncPointFunctionType `xml:"SYNC_POINT_FUNCTION"                   xmp:"iXML:syncPointFunction"`
    55  	Comment       string                `xml:"SYNC_POINT_COMMENT,omitempty"          xmp:"iXML:syncPointComment"`
    56  	Low           int                   `xml:"SYNC_POINT_LOW,omitempty"              xmp:"iXML:syncPointLow"`
    57  	High          int                   `xml:"SYNC_POINT_HIGH,omitempty"             xmp:"iXML:syncPointHigh"`
    58  	EventDuration int64                 `xml:"SYNC_POINT_EVENT_DURATION,omitempty"   xmp:"iXML:syncPointEventDuration"`
    59  }
    60  
    61  type SyncPointList []SyncPoint
    62  
    63  func (x SyncPointList) ContainsFunc(f SyncPointFunctionType) (int, bool) {
    64  	for i, v := range x {
    65  		if v.Function == f {
    66  			return i, true
    67  		}
    68  	}
    69  	return -1, false
    70  }
    71  
    72  func (x *SyncPointList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    73  	depth := 1
    74  	for depth > 0 {
    75  		t, err := d.Token()
    76  		if err != nil {
    77  			return fmt.Errorf("%s: %v", NsIXML.GetName(), err)
    78  		}
    79  		switch t := t.(type) {
    80  		case xml.StartElement:
    81  			if t.Name.Local != "SYNC_POINT" {
    82  				d.Skip()
    83  				continue
    84  			}
    85  			sp := SyncPoint{}
    86  			if err := d.DecodeElement(&sp, &t); err != nil {
    87  				return fmt.Errorf("%s: %v", NsIXML.GetName(), err)
    88  			}
    89  			*x = append(*x, sp)
    90  
    91  		case xml.EndElement:
    92  			depth--
    93  		}
    94  	}
    95  	return nil
    96  }
    97  
    98  func (x SyncPointList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    99  	if len(x) == 0 {
   100  		return nil
   101  	}
   102  	if err := e.EncodeToken(start); err != nil {
   103  		return err
   104  	}
   105  	cToken := xml.StartElement{Name: xml.Name{Local: "SYNC_POINT_COUNT"}}
   106  	if err := e.EncodeElement(len(x), cToken); err != nil {
   107  		return err
   108  	}
   109  	for _, v := range x {
   110  		if err := e.EncodeElement(v, xml.StartElement{Name: xml.Name{Local: "SYNC_POINT"}}); err != nil {
   111  			return err
   112  		}
   113  	}
   114  	if err := e.EncodeToken(start.End()); err != nil {
   115  		return err
   116  	}
   117  	return e.Flush()
   118  }
   119  
   120  func (x SyncPointList) Typ() xmp.ArrayType {
   121  	return xmp.ArrayTypeOrdered
   122  }
   123  
   124  func (x SyncPointList) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   125  	return xmp.MarshalArray(e, node, x.Typ(), x)
   126  }
   127  
   128  func (x *SyncPointList) UnmarshalXMP(d *xmp.Decoder, node *xmp.Node, m xmp.Model) error {
   129  	return xmp.UnmarshalArray(d, node, x.Typ(), x)
   130  }
   131  
   132  type Speed struct {
   133  	XMLName                         xml.Name     `xml:"SPEED"                                          xmp:"-"`
   134  	Note                            string       `xml:"NOTE,omitempty"                                 xmp:"iXML:note"`
   135  	MasterSpeed                     xmp.Rational `xml:"MASTER_SPEED,omitempty"                         xmp:"iXML:masterSpeed"`
   136  	CurrentSpeed                    xmp.Rational `xml:"CURRENT_SPEED,omitempty"                        xmp:"iXML:currentSpeed"`
   137  	TimecodeRate                    xmp.Rational `xml:"TIMECODE_RATE,omitempty"                        xmp:"iXML:timecodeRate"`
   138  	TimecodeFlag                    TimecodeFlag `xml:"TIMECODE_FLAG,omitempty"                        xmp:"iXML:timecodeFlag"`
   139  	FileSampleRate                  xmp.Rational `xml:"FILE_SAMPLE_RATE,omitempty"                     xmp:"iXML:fileSampleRate"`
   140  	AudioBitDepth                   int          `xml:"AUDIO_BIT_DEPTH,omitempty"                      xmp:"iXML:audioBitDepth"`
   141  	DigitizerSampleRate             int          `xml:"DIGITIZER_SAMPLE_RATE,omitempty"                xmp:"iXML:digitizerSampleRate"`
   142  	TimestampSamplesSinceMidnightHI int          `xml:"TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI,omitempty"  xmp:"iXML:timestampSamplesSinceMidnightHI"`
   143  	TimestampSamplesSinceMidnightLO int          `xml:"TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO,omitempty"  xmp:"iXML:timestampSamplesSinceMidnightLO"`
   144  	TimestampSampleRate             int          `xml:"TIMESTAMP_SAMPLE_RATE,omitempty"                xmp:"iXML:timestampSampleRate"`
   145  }
   146  
   147  func (x Speed) XmpTimecode() xmpdm.Timecode {
   148  	samples := int64(x.TimestampSamplesSinceMidnightHI)<<32 | int64(x.TimestampSamplesSinceMidnightLO)
   149  	samples = samples * 1000
   150  	rate := int64(x.TimestampSampleRate) * 1000
   151  	if rate == 0 {
   152  		rate = int64(x.TimecodeRate.Value() * 1000)
   153  	}
   154  	sec := samples / rate
   155  	h := sec / 3600
   156  	m := (sec - h*3600) / 60
   157  	s := sec - h*3600 - m*60
   158  	f := int((samples % rate) * x.TimecodeRate.Num / rate / 1000)
   159  	isDrop := x.TimecodeFlag == TimecodeFlagDF
   160  	return xmpdm.Timecode{
   161  		Format:      xmpdm.ParseTimecodeFormat(x.TimecodeRate.Value(), isDrop),
   162  		H:           int(h),
   163  		M:           int(m),
   164  		S:           int(s),
   165  		F:           f,
   166  		IsDropFrame: isDrop,
   167  	}
   168  }
   169  
   170  type History struct {
   171  	XMLName          xml.Name `xml:"HISTORY"                      xmp:"-"`
   172  	OriginalFilename string   `xml:"ORIGINAL_FILENAME,omitempty"  xmp:"iXML:originalFilename"`
   173  	ParentFilename   string   `xml:"PARENT_FILENAME,omitempty"    xmp:"iXML:parentFilename"`
   174  	ParentUID        string   `xml:"PARENT_UID,omitempty"         xmp:"iXML:parentUID"`
   175  }
   176  
   177  type FileSet struct {
   178  	XMLName      xml.Name `xml:"FILE_SET"                  xmp:"-"`
   179  	TotalFiles   int      `xml:"TOTAL_FILES,omitempty"     xmp:"iXML:totalFiles"`
   180  	FamilyUID    string   `xml:"FAMILY_UID,omitempty"      xmp:"iXML:familyUID"`
   181  	FamilyName   string   `xml:"FAMILY_NAME,omitempty"     xmp:"iXML:familyName"`
   182  	FileSetIndex string   `xml:"FILE_SET_INDEX,omitempty"  xmp:"iXML:fileSetIndex"`
   183  }
   184  
   185  type Track struct {
   186  	XMLName         xml.Name     `xml:"TRACK"                       xmp:"-"`
   187  	ChannelIndex    int          `xml:"CHANNEL_INDEX,omitempty"     xmp:"iXML:channelIndex"`
   188  	InterleaveIndex int          `xml:"INTERLEAVE_INDEX,omitempty"  xmp:"iXML:interleaveIndex"`
   189  	Name            string       `xml:"NAME,omitempty"              xmp:"iXML:name"`
   190  	Function        FunctionType `xml:"FUNCTION,omitempty"          xmp:"iXML:function"`
   191  	DefaultMix      DefaultMix   `xml:"DEFAULT_MIX,omitempty"       xmp:"iXML:defaultMix"`
   192  }
   193  
   194  type TrackList []Track
   195  
   196  func (x *TrackList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
   197  	depth := 1
   198  	for depth > 0 {
   199  		t, err := d.Token()
   200  		if err != nil {
   201  			return fmt.Errorf("%s: %v", NsIXML.GetName(), err)
   202  		}
   203  		switch t := t.(type) {
   204  		case xml.StartElement:
   205  			if t.Name.Local != "TRACK" {
   206  				d.Skip()
   207  				continue
   208  			}
   209  			tt := Track{}
   210  			if err := d.DecodeElement(&tt, &t); err != nil {
   211  				return fmt.Errorf("%s: %v", NsIXML.GetName(), err)
   212  			}
   213  			*x = append(*x, tt)
   214  
   215  		case xml.EndElement:
   216  			depth--
   217  		}
   218  	}
   219  	return nil
   220  }
   221  
   222  func (x TrackList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
   223  	if len(x) == 0 {
   224  		return nil
   225  	}
   226  	if err := e.EncodeToken(start); err != nil {
   227  		return err
   228  	}
   229  	cToken := xml.StartElement{Name: xml.Name{Local: "TRACK_COUNT"}}
   230  	if err := e.EncodeElement(len(x), cToken); err != nil {
   231  		return err
   232  	}
   233  	for _, v := range x {
   234  		if err := e.EncodeElement(v, xml.StartElement{Name: xml.Name{Local: "TRACK"}}); err != nil {
   235  			return err
   236  		}
   237  	}
   238  	if err := e.EncodeToken(start.End()); err != nil {
   239  		return err
   240  	}
   241  	return e.Flush()
   242  }
   243  
   244  func (x TrackList) Typ() xmp.ArrayType {
   245  	return xmp.ArrayTypeOrdered
   246  }
   247  
   248  func (x TrackList) MarshalXMP(e *xmp.Encoder, node *xmp.Node, m xmp.Model) error {
   249  	return xmp.MarshalArray(e, node, x.Typ(), x)
   250  }
   251  
   252  func (x *TrackList) UnmarshalXMP(d *xmp.Decoder, node *xmp.Node, m xmp.Model) error {
   253  	return xmp.UnmarshalArray(d, node, x.Typ(), x)
   254  }
   255  
   256  type UserData struct {
   257  	XMLName                   xml.Name `xml:"USER"                                    xmp:"-"`
   258  	Comment                   string   `xml:",chardata"                               xmp:"iXML:comment"`
   259  	FullTitle                 string   `xml:"FULL_TITLE,omitempty"                    xmp:"iXML:fullTitle"`
   260  	DirectorName              string   `xml:"DIRECTOR_NAME,omitempty"                 xmp:"iXML:directorName"`
   261  	ProductionName            string   `xml:"PRODUCTION_NAME,omitempty"               xmp:"iXML:productionName"`
   262  	ProductionAddress         string   `xml:"PRODUCTION_ADDRESS,omitempty"            xmp:"iXML:productionAddress"`
   263  	ProductionEmail           string   `xml:"PRODUCTION_EMAIL,omitempty"              xmp:"iXML:productionEmail"`
   264  	ProductionPhone           string   `xml:"PRODUCTION_PHONE,omitempty"              xmp:"iXML:productionPhone"`
   265  	ProductionNote            string   `xml:"PRODUCTION_NOTE,omitempty"               xmp:"iXML:productionNote"`
   266  	SoundMixerName            string   `xml:"SOUND_MIXER_NAME,omitempty"              xmp:"iXML:soundMixerName"`
   267  	SoundMixerAddress         string   `xml:"SOUND_MIXER_ADDRESS,omitempty"           xmp:"iXML:soundMixerAddress"`
   268  	SoundMixerEmail           string   `xml:"SOUND_MIXER_EMAIL,omitempty"             xmp:"iXML:soundMixerEmail"`
   269  	SoundMixerPhone           string   `xml:"SOUND_MIXER_PHONE,omitempty"             xmp:"iXML:soundMixerPhone"`
   270  	SoundMixerNote            string   `xml:"SOUND_MIXER_NOTE,omitempty"              xmp:"iXML:soundMixerNote"`
   271  	AudioRecorderModel        string   `xml:"AUDIO_RECORDER_MODEL,omitempty"          xmp:"iXML:audioRecorderModel"`
   272  	AudioRecorderSerialNumber string   `xml:"AUDIO_RECORDER_SERIAL_NUMBER,omitempty"  xmp:"iXML:audioRecorderSerialNumber"`
   273  	AudioRecorderFirmware     string   `xml:"AUDIO_RECORDER_FIRMWARE,omitempty"       xmp:"iXML:audioRecorderFirmware"`
   274  }
   275  
   276  type Location struct {
   277  	XMLName          xml.Name         `xml:"LOCATION"                     xmp:"-"`
   278  	LocationName     string           `xml:"LOCATION_NAME,omitempty"      xmp:"iXML:locationName"`     //  Human readable description of location
   279  	LocationGps      string           `xml:"LOCATION_GPS,omitempty"       xmp:"iXML:locationGps"`      //  47.756787, -123.729977
   280  	LocationAltitude string           `xml:"LOCATION_ALTITUDE,omitempty"  xmp:"iXML:locationAltitude"` //
   281  	LocationType     LocationTypeList `xml:"LOCATION_TYPE,omitempty"      xmp:"iXML:locationType"`     //  [dictionary]
   282  	LocationTime     LocationTimeList `xml:"LOCATION_TIME,omitempty"      xmp:"iXML:locationTime"`     //  [dictionary]
   283  }
   284  
   285  // extended attribute for tracks: http://www.gallery.co.uk/ixml/defaultmix.html
   286  type DefaultMix struct {
   287  	XMLName xml.Name `xml:"DEFAULT_MIX"      xmp:"-"`
   288  	Level   string   `xml:"LEVEL,omitempty"  xmp:"iXML:level"` // Gain in dB as float, 'OFF' is an allowed value (e.g. -3.5)
   289  	Pan     string   `xml:"PAN,omitempty"    xmp:"iXML:pan"`   // Pan angle in degrees L or R of centre (e.g. 23.5R)
   290  }