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 }