| 1 | ;;; emms-info-native-opus.el --- EMMS Opus info support -*- lexical-binding: t; -*- |
| 2 | |
| 3 | ;; Copyright (C) 2020-2023 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: Petteri Hintsanen <petterih@iki.fi> |
| 6 | |
| 7 | ;; This file is part of EMMS. |
| 8 | |
| 9 | ;; EMMS is free software; you can redistribute it and/or modify it |
| 10 | ;; under the terms of the GNU General Public License as published by |
| 11 | ;; the Free Software Foundation; either version 3, or (at your option) |
| 12 | ;; any later version. |
| 13 | |
| 14 | ;; EMMS is distributed in the hope that it will be useful, but WITHOUT |
| 15 | ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| 16 | ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| 17 | ;; License for more details. |
| 18 | |
| 19 | ;; You should have received a copy of the GNU General Public License |
| 20 | ;; along with EMMS; see the file COPYING. If not, write to the Free |
| 21 | ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
| 22 | ;; MA 02110-1301, USA. |
| 23 | |
| 24 | ;;; Commentary: |
| 25 | |
| 26 | ;; This file contains Opus-specific code for `emms-info-native-ogg' |
| 27 | ;; feature. |
| 28 | |
| 29 | ;;; Code: |
| 30 | |
| 31 | (require 'emms-info-native-vorbis) |
| 32 | (require 'bindat) |
| 33 | |
| 34 | (defvar bindat-raw) |
| 35 | |
| 36 | (defvar emms-info-native-opus--channel-count 0 |
| 37 | "Last decoded Opus channel count.") |
| 38 | |
| 39 | (defconst emms-info-native-opus--id-magic-pattern "OpusHead" |
| 40 | "Opus identification header magic pattern.") |
| 41 | |
| 42 | (defconst emms-info-native-opus--channel-mapping-bindat-spec |
| 43 | (if (eval-when-compile (fboundp 'bindat-type)) |
| 44 | (bindat-type |
| 45 | (stream-count u8) |
| 46 | (coupled-count u8) |
| 47 | (channel-mapping vec emms-info-native-opus--channel-count)) |
| 48 | '((stream-count u8) |
| 49 | (coupled-count u8) |
| 50 | (channel-mapping vec (eval emms-info-native-opus--channel-count)))) |
| 51 | "Opus channel mapping table specification.") |
| 52 | |
| 53 | (defconst emms-info-native-opus--id-header-bindat-spec |
| 54 | (if (eval-when-compile (fboundp 'bindat-type)) |
| 55 | (bindat-type |
| 56 | (opus-head str 8) |
| 57 | (_ unit (unless (equal opus-head emms-info-native-opus--id-magic-pattern) |
| 58 | (error "Opus framing mismatch: expected `%s', got `%s'" |
| 59 | emms-info-native-opus--id-magic-pattern |
| 60 | opus-head))) |
| 61 | (opus-version u8) |
| 62 | (_ unit (unless (< opus-version 16) |
| 63 | (error "Opus version mismatch: expected < 16, got %s" |
| 64 | opus-version))) |
| 65 | (channel-count u8) |
| 66 | (_ unit (progn (setq emms-info-native-opus--channel-count channel-count) nil)) |
| 67 | (pre-skip uintr 16) |
| 68 | (sample-rate uintr 32) |
| 69 | (output-gain uintr 16) |
| 70 | (channel-mapping-family u8) |
| 71 | (_ . (if (> channel-mapping-family 0) |
| 72 | (type emms-info-native-opus--channel-mapping-bindat-spec) |
| 73 | (unit nil)))) |
| 74 | '((opus-head str 8) |
| 75 | (eval (unless (equal last emms-info-native-opus--id-magic-pattern) |
| 76 | (error "Opus framing mismatch: expected `%s', got `%s'" |
| 77 | emms-info-native-opus--id-magic-pattern |
| 78 | last))) |
| 79 | (opus-version u8) |
| 80 | (eval (unless (< last 16) |
| 81 | (error "Opus version mismatch: expected < 16, got %s" |
| 82 | last))) |
| 83 | (channel-count u8) |
| 84 | (eval (setq emms-info-native-opus--channel-count last)) |
| 85 | (pre-skip u16r) |
| 86 | (sample-rate u32r) |
| 87 | (output-gain u16r) |
| 88 | (channel-mapping-family u8) |
| 89 | (union (channel-mapping-family) |
| 90 | (0 nil) |
| 91 | (t (struct emms-info-native-opus--channel-mapping-bindat-spec))))) |
| 92 | "Opus identification header specification.") |
| 93 | |
| 94 | (defconst emms-info-native-opus--tags-magic-pattern "OpusTags" |
| 95 | "Opus comment header magic pattern.") |
| 96 | |
| 97 | (defconst emms-info-native-opus--comment-header-bindat-spec |
| 98 | (if (eval-when-compile (fboundp 'bindat-type)) |
| 99 | (bindat-type |
| 100 | (opus-tags str 8) |
| 101 | (_ unit (unless (equal opus-tags emms-info-native-opus--tags-magic-pattern) |
| 102 | (error "Opus framing mismatch: expected `%s', got `%s'" |
| 103 | emms-info-native-opus--tags-magic-pattern |
| 104 | opus-tags))) |
| 105 | (vendor-length uintr 32) |
| 106 | (_ unit (when (> vendor-length (length bindat-raw)) |
| 107 | (error "Opus vendor length %s is too long" |
| 108 | vendor-length))) |
| 109 | (vendor-string str vendor-length) |
| 110 | (user-comments-list-length uintr 32) |
| 111 | (_ unit (when (> user-comments-list-length (length bindat-raw)) |
| 112 | (error "Opus user comment list length %s is too long" |
| 113 | user-comments-list-length))) |
| 114 | (user-comments repeat user-comments-list-length |
| 115 | type emms-info-native-vorbis--comment-field-bindat-spec)) |
| 116 | '((opus-tags str 8) |
| 117 | (eval (unless (equal last emms-info-native-opus--tags-magic-pattern) |
| 118 | (error "Opus framing mismatch: expected `%s', got `%s'" |
| 119 | emms-info-native-opus--tags-magic-pattern |
| 120 | last))) |
| 121 | (vendor-length u32r) |
| 122 | (eval (when (> last (length bindat-raw)) |
| 123 | (error "Opus vendor length %s is too long" last))) |
| 124 | (vendor-string str (vendor-length)) |
| 125 | (user-comments-list-length u32r) |
| 126 | (eval (when (> last (length bindat-raw)) |
| 127 | (error "Opus user comment list length %s is too long" |
| 128 | last))) |
| 129 | (user-comments repeat |
| 130 | (user-comments-list-length) |
| 131 | (struct emms-info-native-vorbis--comment-field-bindat-spec)))) |
| 132 | "Opus comment header specification.") |
| 133 | |
| 134 | (defconst emms-info-native-opus--headers-bindat-spec |
| 135 | (if (eval-when-compile (fboundp 'bindat-type)) |
| 136 | (bindat-type |
| 137 | (identification-header type emms-info-native-opus--id-header-bindat-spec) |
| 138 | (comment-header type emms-info-native-opus--comment-header-bindat-spec)) |
| 139 | '((identification-header struct emms-info-native-opus--id-header-bindat-spec) |
| 140 | (comment-header struct emms-info-native-opus--comment-header-bindat-spec))) |
| 141 | "Specification for two first Opus header packets. |
| 142 | They are always an identification header followed by a comment |
| 143 | header.") |
| 144 | |
| 145 | (provide 'emms-info-native-opus) |
| 146 | |
| 147 | ;;; emms-info-native-opus.el ends here |