github.com/boki/go-xmp@v1.0.1/models/ixml/model.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 // TODO: 16 // - io.Writer interface with maxLen and padding to combine with file writers 17 // - UID generator 18 // <Manufacturer><Device><DeviceSN><Date><Time><Random><FileSetIndex> 19 // - 3 chars for the Manufacturer ID: AAT, AVI, DIG, FOS, GAL, HHB, MTI, NAG, ZAX, etc... 20 // - 3 chars for the Device ID: CAN, PMX, POR, etc... 21 // - 5 chars for the Device Serial Number: 12345 22 // - 8 chars for the date: YYYYMMDD 23 // - 6 chars for the time: HHMMSS 24 // - 3 chars for pure random digits that can very well be the millisecond in the given time second, to ensure that two generations occurring in the same second have a different UID. 25 // - 4 chars for the file set index. These 4 digits are set to zero in the <FAMILY_UID> and to the <FILE_SET_INDEX> value in both the <FILE_UID> and BEXT Originator Reference of every files in the set. 26 27 // Package ixml implements the iXML audio chunk metadata standard for broadcast wave audio. 28 package ixml 29 30 import ( 31 "encoding/xml" 32 "fmt" 33 "strings" 34 35 "trimmer.io/go-xmp/models/xmp_dm" 36 "trimmer.io/go-xmp/xmp" 37 ) 38 39 const ixmlVersion = "2.0" 40 41 var ( 42 NsIXML = xmp.NewNamespace("iXML", "http://ns.adobe.com/ixml/1.0/", NewModel) 43 ) 44 45 func init() { 46 xmp.Register(NsIXML, xmp.SoundMetadata) 47 } 48 49 func NewModel(name string) xmp.Model { 50 return &IXML{ 51 Version: ixmlVersion, 52 } 53 } 54 55 func MakeModel(d *xmp.Document) (*IXML, error) { 56 m, err := d.MakeModel(NsIXML) 57 if err != nil { 58 return nil, err 59 } 60 x, _ := m.(*IXML) 61 return x, nil 62 } 63 64 func FindModel(d *xmp.Document) *IXML { 65 if m := d.FindModel(NsIXML); m != nil { 66 return m.(*IXML) 67 } 68 return nil 69 } 70 71 type IXML struct { 72 XMLName xml.Name `xml:"BWFXML" xmp:"-"` 73 Version string `xml:"IXML_VERSION" xmp:"iXML:version"` 74 Project string `xml:"PROJECT,omitempty" xmp:"iXML:project"` 75 SceneName string `xml:"SCENE,omitempty" xmp:"iXML:sceneName"` 76 SoundRoll string `xml:"TAPE,omitempty" xmp:"iXML:soundRoll"` 77 Take int `xml:"TAKE,omitempty" xmp:"iXML:take"` 78 TakeType TakeTypeList `xml:"TAKE_TYPE,omitempty" xmp:"iXML:takeType"` // v2.0+ 79 IsNotGood Bool `xml:"NO_GOOD,omitempty" xmp:"-"` // v1.9- 80 IsFalseStart Bool `xml:"FALSE_START,omitempty" xmp:"-"` // v1.9- 81 IsWildTrack Bool `xml:"WILD_TRACK,omitempty" xmp:"-"` // v1.9- 82 PreRecordSamplecount int `xml:"PRE_RECORD_SAMPLECOUNT,omitempty" xmp:"-"` // v1.3- 83 IsCircled Bool `xml:"CIRCLED,omitempty" xmp:"iXML:circled"` 84 FileUID string `xml:"FILE_UID,omitempty" xmp:"iXML:fileUid"` 85 UserBits string `xml:"UBITS,omitempty" xmp:"iXML:userBits"` 86 Note string `xml:"NOTE,omitempty" xmp:"iXML:note"` 87 SyncPoints SyncPointList `xml:"SYNC_POINT_LIST,omitempty" xmp:"iXML:syncPoints"` 88 Speed *Speed `xml:"SPEED,omitempty" xmp:"iXML:speed"` 89 History *History `xml:"HISTORY,omitempty" xmp:"iXML:history"` 90 FileSet *FileSet `xml:"FILE_SET,omitempty" xmp:"iXML:fileSet"` 91 TrackList TrackList `xml:"TRACK_LIST,omitempty" xmp:"iXML:trackList"` 92 UserData *UserData `xml:"USER,omitempty" xmp:"iXML:userData"` 93 Location *Location `xml:"LOCATION,omitempty" xmp:"iXML:location"` 94 Extension xmp.NamedExtensionArray `xml:",any" xmp:"iXML:extension,any"` 95 } 96 97 func (m *IXML) Namespaces() xmp.NamespaceList { 98 return xmp.NamespaceList{NsIXML} 99 } 100 101 func (m *IXML) Can(nsName string) bool { 102 return nsName == NsIXML.GetName() 103 } 104 105 func (x *IXML) SyncModel(d *xmp.Document) error { 106 return nil 107 } 108 109 func (x *IXML) SyncFromXMP(d *xmp.Document) error { 110 return nil 111 } 112 113 func (x *IXML) SyncToXMP(d *xmp.Document) error { 114 dm, err := xmpdm.MakeModel(d) 115 if err != nil { 116 return err 117 } 118 dm.TapeName = x.SoundRoll 119 dm.Scene = x.SceneName 120 dm.TrackNumber = x.Take 121 dm.LogComment = x.Note 122 dm.ProjectName = x.Project 123 dm.Good = xmp.Bool(x.IsCircled.Value()) 124 if x.Speed != nil { 125 dm.StartTimecode = x.Speed.XmpTimecode() 126 dm.AudioSampleRate = x.Speed.FileSampleRate.Value() 127 } 128 return nil 129 } 130 131 func (x *IXML) CanTag(tag string) bool { 132 _, err := xmp.GetNativeField(x, tag) 133 return err == nil 134 } 135 136 func (x *IXML) GetTag(tag string) (string, error) { 137 if v, err := xmp.GetNativeField(x, tag); err != nil { 138 return "", fmt.Errorf("%s: %v", NsIXML.GetName(), err) 139 } else { 140 return v, nil 141 } 142 } 143 144 func (x *IXML) SetTag(tag, value string) error { 145 if err := xmp.SetNativeField(x, tag, value); err != nil { 146 return fmt.Errorf("%s: %v", NsIXML.GetName(), err) 147 } 148 return nil 149 } 150 151 func (x *IXML) ParseXML(data []byte) error { 152 // must strip type funcs to avoid recursion 153 type _t IXML 154 m := &IXML{} 155 if err := xml.Unmarshal(data, (*_t)(m)); err != nil { 156 return fmt.Errorf("ixml: parse error: %v", err) 157 } 158 if err := m.upgrade(); err != nil { 159 return err 160 } 161 *x = *m 162 return nil 163 } 164 165 func (x *IXML) UnmarshalText(data []byte) error { 166 return x.ParseXML(data) 167 } 168 169 func (x *IXML) upgrade() error { 170 if err := x.v13_to_v14(); err != nil { 171 return err 172 } 173 if err := x.v1x_to_v20(); err != nil { 174 return err 175 } 176 return nil 177 } 178 179 func (x *IXML) v1x_to_v20() error { 180 if x.IsNotGood { 181 x.TakeType.Add(TakeTypeNoGood) 182 x.IsNotGood = false 183 } 184 if x.IsFalseStart { 185 x.TakeType.Add(TakeTypeFalseStart) 186 x.IsFalseStart = false 187 } 188 if x.IsWildTrack { 189 x.TakeType.Add(TakeTypeWildTrack) 190 x.IsWildTrack = false 191 } 192 if x.UserData != nil { 193 x.UserData.Comment = strings.TrimSpace(x.UserData.Comment) 194 } 195 return nil 196 } 197 198 func (x *IXML) v13_to_v14() error { 199 if x.PreRecordSamplecount > 0 { 200 if i, ok := x.SyncPoints.ContainsFunc(SyncPointPreRecordSamplecount); ok { 201 x.SyncPoints[i].Low = x.PreRecordSamplecount 202 } else { 203 x.SyncPoints = append(x.SyncPoints, SyncPoint{ 204 Type: SyncPointRelative, 205 Function: SyncPointPreRecordSamplecount, 206 Comment: "upgrade from deprecated property root.PRE_RECORD_SAMPLECOUNT", 207 Low: x.PreRecordSamplecount, 208 }) 209 } 210 x.PreRecordSamplecount = 0 211 } 212 return nil 213 }