Skip to content

Commit

Permalink
midi: initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
twystd committed Aug 11, 2024
1 parent 6ace338 commit ad0a06d
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,8 @@ release:
@echo "# wader/fq":
@echo git push wader v${VERSION}:v${VERSION}
@echo "# edit draft release notes and publish"


midi:
go run . -d midi dv format/midi/testdata/test.mid

1 change: 1 addition & 0 deletions format/all/all.fqtest
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ $ fq -n _registry.groups.probe
"macho",
"macho_fat",
"matroska",
"midi",
"moc3",
"mp4",
"nes",
Expand Down
1 change: 1 addition & 0 deletions format/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
_ "github.com/wader/fq/format/markdown"
_ "github.com/wader/fq/format/math"
_ "github.com/wader/fq/format/matroska"
_ "github.com/wader/fq/format/midi"
_ "github.com/wader/fq/format/moc3"
_ "github.com/wader/fq/format/mp3"
_ "github.com/wader/fq/format/mp4"
Expand Down
1 change: 1 addition & 0 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ var (
MachO_Fat = &decode.Group{Name: "macho_fat"}
Markdown = &decode.Group{Name: "markdown"}
Matroska = &decode.Group{Name: "matroska"}
MIDI = &decode.Group{Name: "midi"}
MOC3 = &decode.Group{Name: "moc3"}
MP3 = &decode.Group{Name: "mp3"}
MP3_Frame = &decode.Group{Name: "mp3_frame"}
Expand Down
1 change: 1 addition & 0 deletions format/midi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
103 changes: 103 additions & 0 deletions format/midi/midi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package midi

// https://www.midi.org/specifications/item/the-midi-1-0-specification

import (
"bytes"
"embed"
"fmt"

"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
)

//go:embed midi.md
var midiFS embed.FS

func init() {
interp.RegisterFormat(
format.MIDI,
&decode.Format{
Description: "Standard MIDI file",
DecodeFn: decodeMIDI,
})

interp.RegisterFS(midiFS)
}

func decodeMIDIFile(d *decode.D) {
d.FieldStruct("header", decodeMThd)

d.FieldArray("tracks", func(d *decode.D) {
for i := 0; i < 2; i++ {
d.FieldStruct("track", decodeMTrk)
}
})
}

func decodeMThd(d *decode.D) {
d.AssertLeastBytesLeft(8)

if !bytes.Equal(d.PeekBytes(4), []byte("MThd")) {
d.Errorf("no MThd marker")
}

d.FieldArray("header", func(d *decode.D) {
tag := d.FieldUTF8NullFixedLen("tag", 4)
length := d.FieldU32("length")
format := d.FieldU16("format")
tracks := d.FieldU16("tracks")
division := d.FieldU16("division")
SMPTE := uint64(0x00)

if int(length) > 6 {
d.FieldRawLen("other", 8*int64(length-6))
}

if format != 0 && format != 1 && format != 2 {
d.Errorf("invalid MThd format %v (expected 0,1 or 2)", format)
}

if division&0x8000 == 0x8000 {
SMPTE = (division & 0xff00) >> 8
if SMPTE != 0xe8 && SMPTE != SMPTE && SMPTE != 0xe6 && SMPTE != 0xe5 {
d.Errorf("Invalid MThd division SMPTE timecode type %02X (expected E8, E7, E6 or E5)", SMPTE)
}
}

fmt.Printf(">> tag: %v\n", tag)
fmt.Printf(">> length: %v\n", length)
fmt.Printf(">> format: %v\n", format)
fmt.Printf(">> tracks: %v\n", tracks)
fmt.Printf(">> division: %v\n", division)
if division&0x8000 == 0x8000 {
fmt.Printf(">> SMPTE: %02x\n", SMPTE)
}
})

return
}

func decodeMTrk(d *decode.D) {
d.AssertLeastBytesLeft(8)

if !bytes.Equal(d.PeekBytes(4), []byte("MTrk")) {
d.Errorf("no MTrk marker")
}

tag := d.FieldUTF8NullFixedLen("tag", 4)
length := d.FieldU32("length")
d.FieldRawLen("data", 8*int64(length))

fmt.Printf(">> tag: %v\n", tag)
fmt.Printf(">> length: %v\n", length)
}

func decodeMIDI(d *decode.D) any {
d.Endian = decode.BigEndian

decodeMIDIFile(d)

return nil
}
7 changes: 7 additions & 0 deletions format/midi/midi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### Notes

### Authors

### References

1. [The Complete MIDI 1.0 Detailed Specification](https://www.midi.org/specifications/item/the-midi-1-0-specification)
21 changes: 21 additions & 0 deletions format/midi/testdata/help_midi.fqtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
$ fq -h midi
midi: Standard MIDI file decoder

Decode examples
===============

# Decode file as MIDI
$ fq -d midi . file


Limitations
===========


Authors
=======


References
==========

171 changes: 171 additions & 0 deletions format/midi/testdata/test.fqtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
$ fq -d midi dv test.mid
|00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f|0123456789abcdef|.{}: test.bson (bson) 0x0-0x141 (321)
0x000|41 01 00 00 |A... | size: 321 0x0-0x4 (4)
| | | elements[0:17]: 0x4-0x140 (316)
| | | [0]{}: element 0x4-0x11 (13)
0x000| 01 | . | type: "double" (1) (64-bit binary floating point) 0x4-0x5 (1)
0x000| 64 6f 75 00 | dou. | name: "dou" 0x5-0x9 (4)
0x000| 29 5c 8f c2 f5 b0 58| )\....X| value: 98.765 0x9-0x11 (8)
0x010|40 |@ |
| | | [1]{}: element 0x11-0x24 (19)
0x010| 02 | . | type: "string" (2) (UTF-8 string) 0x11-0x12 (1)
0x010| 73 74 72 00 | str. | name: "str" 0x12-0x16 (4)
0x010| 0a 00 00 00 | .... | length: 10 0x16-0x1a (4)
0x010| 6d 79 20 73 74 72| my str| value: "my string" 0x1a-0x24 (10)
0x020|69 6e 67 00 |ing. |
| | | [2]{}: element 0x24-0x66 (66)
0x020| 03 | . | type: "document" (3) (Embedded document) 0x24-0x25 (1)
0x020| 64 6f 63 00 | doc. | name: "doc" 0x25-0x29 (4)
| | | value{}: 0x29-0x66 (61)
0x020| 3d 00 00 00 | =... | size: 61 0x29-0x2d (4)
| | | elements[0:3]: 0x2d-0x65 (56)
| | | [0]{}: element 0x2d-0x45 (24)
0x020| 02 | . | type: "string" (2) (UTF-8 string) 0x2d-0x2e (1)
0x020| 6e 73| ns| name: "nstr" 0x2e-0x33 (5)
0x030|74 72 00 |tr. |
0x030| 0e 00 00 00 | .... | length: 14 0x33-0x37 (4)
0x030| 6e 65 73 74 65 64 20 73 74| nested st| value: "nested string" 0x37-0x45 (14)
0x040|72 69 6e 67 00 |ring. |
| | | [1]{}: element 0x45-0x57 (18)
0x040| 04 | . | type: "array" (4) (Array) 0x45-0x46 (1)
0x040| 6e 61 72 72 00 | narr. | name: "narr" 0x46-0x4b (5)
| | | value{}: 0x4b-0x57 (12)
0x040| 0c 00 00 00 | .... | size: 12 0x4b-0x4f (4)
| | | elements[0:1]: 0x4f-0x56 (7)
| | | [0]{}: element 0x4f-0x56 (7)
0x040| 10| .| type: "int32" (16) (32-bit integer) 0x4f-0x50 (1)
0x050|30 00 |0. | name: "0" 0x50-0x52 (2)
0x050| 85 ff ff ff | .... | value: -123 0x52-0x56 (4)
0x050| 00 | . | terminator: 0 (valid) 0x56-0x57 (1)
| | | [2]{}: element 0x57-0x65 (14)
0x050| 01 | . | type: "double" (1) (64-bit binary floating point) 0x57-0x58 (1)
0x050| 6e 64 6f 75 00 | ndou. | name: "ndou" 0x58-0x5d (5)
0x050| 29 5c 8f| )\.| value: 98.765 0x5d-0x65 (8)
0x060|c2 f5 b0 58 40 |...X@ |
0x060| 00 | . | terminator: 0 (valid) 0x65-0x66 (1)
| | | [3]{}: element 0x66-0x9f (57)
0x060| 04 | . | type: "array" (4) (Array) 0x66-0x67 (1)
0x060| 61 72 72 00 | arr. | name: "arr" 0x67-0x6b (4)
| | | value{}: 0x6b-0x9f (52)
0x060| 34 00 00 00 | 4... | size: 52 0x6b-0x6f (4)
| | | elements[0:3]: 0x6f-0x9e (47)
| | | [0]{}: element 0x6f-0x81 (18)
0x060| 02| .| type: "string" (2) (UTF-8 string) 0x6f-0x70 (1)
0x070|30 00 |0. | name: "0" 0x70-0x72 (2)
0x070| 0b 00 00 00 | .... | length: 11 0x72-0x76 (4)
0x070| 61 72 72 20 73 74 72 69 6e 67| arr string| value: "arr string" 0x76-0x81 (11)
0x080|00 |. |
| | | [1]{}: element 0x81-0x93 (18)
0x080| 03 | . | type: "document" (3) (Embedded document) 0x81-0x82 (1)
0x080| 31 00 | 1. | name: "1" 0x82-0x84 (2)
| | | value{}: 0x84-0x93 (15)
0x080| 0f 00 00 00 | .... | size: 15 0x84-0x88 (4)
| | | elements[0:1]: 0x88-0x92 (10)
| | | [0]{}: element 0x88-0x92 (10)
0x080| 10 | . | type: "int32" (16) (32-bit integer) 0x88-0x89 (1)
0x080| 6e 69 33 32 00 | ni32. | name: "ni32" 0x89-0x8e (5)
0x080| 85 ff| ..| value: -123 0x8e-0x92 (4)
0x090|ff ff |.. |
0x090| 00 | . | terminator: 0 (valid) 0x92-0x93 (1)
| | | [2]{}: element 0x93-0x9e (11)
0x090| 01 | . | type: "double" (1) (64-bit binary floating point) 0x93-0x94 (1)
0x090| 32 00 | 2. | name: "2" 0x94-0x96 (2)
0x090| 29 5c 8f c2 f5 b0 58 40 | )\....X@ | value: 98.765 0x96-0x9e (8)
0x090| 00 | . | terminator: 0 (valid) 0x9e-0x9f (1)
| | | [4]{}: element 0x9f-0xae (15)
0x090| 05| .| type: "binary" (5) (Binary data) 0x9f-0xa0 (1)
0x0a0|62 69 6e 00 |bin. | name: "bin" 0xa0-0xa4 (4)
0x0a0| 05 00 00 00 | .... | length: 5 0xa4-0xa8 (4)
0x0a0| 00 | . | subtype: 0 0xa8-0xa9 (1)
0x0a0| 00 01 02 03 04 | ..... | value: raw bits 0xa9-0xae (5)
| | | [5]{}: element 0xae-0xbf (17)
0x0a0| 07 | . | type: "object_id" (7) (ObjectId) 0xae-0xaf (1)
0x0a0| 5f| _| name: "_id" 0xaf-0xb3 (4)
0x0b0|69 64 00 |id. |
0x0b0| 64 4b 16 19 25 1b e7 40 e8 55 22 d6 | dK..%..@.U". | value: raw bits 0xb3-0xbf (12)
| | | [6]{}: element 0xbf-0xc5 (6)
0x0b0| 08| .| type: "boolean" (8) (Boolean) 0xbf-0xc0 (1)
0x0c0|62 6f 6f 00 |boo. | name: "boo" 0xc0-0xc4 (4)
0x0c0| 01 | . | value: 1 0xc4-0xc5 (1)
| | | [7]{}: element 0xc5-0xd2 (13)
0x0c0| 09 | . | type: "datetime" (9) (UTC datetime) 0xc5-0xc6 (1)
0x0c0| 64 61 74 00 | dat. | name: "dat" 0xc6-0xca (4)
0x0c0| da d8 50 c5 87 01| ..P...| value: 1682642622682 0xca-0xd2 (8)
0x0d0|00 00 |.. |
| | | [8]{}: element 0xd2-0xd7 (5)
0x0d0| 0a | . | type: "null" (10) (Null value) 0xd2-0xd3 (1)
0x0d0| 6e 75 6c 00 | nul. | name: "nul" 0xd3-0xd7 (4)
| | | value: null
| | | [9]{}: element 0xd7-0xea (19)
0x0d0| 0b | . | type: "regexp" (11) (Regular expression) 0xd7-0xd8 (1)
0x0d0| 72 65 67 00 | reg. | name: "reg" 0xd8-0xdc (4)
0x0d0| 6d 79 20 70| my p| value: "my pattern" 0xdc-0xe7 (11)
0x0e0|61 74 74 65 72 6e 00 |attern. |
0x0e0| 69 78 00 | ix. | options: "ix" 0xe7-0xea (3)
| | | [10]{}: element 0xea-0xfe (20)
0x0e0| 0d | . | type: "javascript" (13) (JavaScript code) 0xea-0xeb (1)
0x0e0| 6a 61 76 00 | jav. | name: "jav" 0xeb-0xef (4)
0x0e0| 0b| .| length: 11 0xef-0xf3 (4)
0x0f0|00 00 00 |... |
0x0f0| 76 61 72 20 78 20 3d 20 35 3b 00 | var x = 5;. | value: "var x = 5;" 0xf3-0xfe (11)
| | | [11]{}: element 0xfe-0x107 (9)
0x0f0| 10 | . | type: "int32" (16) (32-bit integer) 0xfe-0xff (1)
0x0f0| 69| i| name: "i32" 0xff-0x103 (4)
0x100|33 32 00 |32. |
0x100| 85 ff ff ff | .... | value: -123 0x103-0x107 (4)
| | | [12]{}: element 0x107-0x114 (13)
0x100| 11 | . | type: "timestamp" (17) (Timestamp) 0x107-0x108 (1)
0x100| 74 69 6d 00 | tim. | name: "tim" 0x108-0x10c (4)
0x100| 9e 17 4b 64| ..Kd| value: 529963620254 0x10c-0x114 (8)
0x110|7b 00 00 00 |{... |
| | | [13]{}: element 0x114-0x121 (13)
0x110| 12 | . | type: "int64" (18) (64-bit integer) 0x114-0x115 (1)
0x110| 69 36 34 00 | i64. | name: "i64" 0x115-0x119 (4)
0x110| 38 fe ff ff ff ff ff| 8......| value: -456 0x119-0x121 (8)
0x120|ff |. |
| | | [14]{}: element 0x121-0x136 (21)
0x120| 13 | . | type: "decimal128" (19) (128-bit decimal floating point) 0x121-0x122 (1)
0x120| 64 65 63 00 | dec. | name: "dec" 0x122-0x126 (4)
0x120| 40 e2 01 00 00 00 00 00 00 00| @.........| value: raw bits 0x126-0x136 (16)
0x130|00 00 00 00 3a 30 |....:0 |
| | | [15]{}: element 0x136-0x13b (5)
0x130| ff | . | type: "minkey" (255) (Min key) 0x136-0x137 (1)
0x130| 6d 69 6e 00 | min. | name: "min" 0x137-0x13b (4)
| | | value: null
| | | [16]{}: element 0x13b-0x140 (5)
0x130| 7f | . | type: "maxkey" (127) (Max key) 0x13b-0x13c (1)
0x130| 6d 61 78 00| max.| name: "max" 0x13c-0x140 (4)
| | | value: null
0x140|00| |.| | terminator: 0 (valid) 0x140-0x141 (1)
$ fq -d bson torepr test.bson
{
"_id": "dK\u0016\u0019%\u001b\ufffd@\ufffdU\"\ufffd",
"arr": [
"arr string",
{
"ni32": -123
},
98.765
],
"bin": "\u0000\u0001\u0002\u0003\u0004",
"boo": true,
"dat": 1682642622682,
"dec": "@\ufffd\u0001\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000:0",
"doc": {
"narr": [
-123
],
"ndou": 98.765,
"nstr": "nested string"
},
"dou": 98.765,
"i32": -123,
"i64": -456,
"jav": "var x = 5;",
"max": null,
"min": null,
"nul": null,
"reg": "my pattern",
"str": "my string",
"tim": 529963620254
}
Binary file added format/midi/testdata/test.mid
Binary file not shown.

0 comments on commit ad0a06d

Please sign in to comment.