Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
nicfit committed Mar 8, 2020
1 parent a698842 commit 821ed37
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 18 deletions.
56 changes: 50 additions & 6 deletions eyed3/id3/frames.py
Expand Up @@ -1324,14 +1324,14 @@ def peak(self):
def peak(self, v):
self._peak = v

def __init__(self, fid=b"RVA2"):
def __init__(self, fid=b"RVA2", identifier=None, channel_type=None, adjustment=None, peak=None):
assert fid == b"RVA2"
super().__init__(fid)

self._identifier = b""
self._channel_type = None
self._adjustment = None
self._peak = None
self.identifier = identifier or ""
self.channel_type = channel_type or self.CHANNEL_TYPE_OTHER
self.adjustment = adjustment or 0
self.peak = peak or 0

def parse(self, data, frame_header):
super().parse(data, frame_header)
Expand All @@ -1347,6 +1347,9 @@ def parse(self, data, frame_header):
if bits_per_peak:
self._peak = bytes2dec(data[4:4 + (bits_per_peak // 8)])

log.debug(f"Parsed RVA2: identifier={self.identifier} channel_type={self.channel_type} "
f"adjustment={self.adjustment} _adjustment={self._adjustment} peak={self.peak}")

def render(self):
assert self._channel_type is not None
if self.header is None:
Expand Down Expand Up @@ -1423,6 +1426,18 @@ class VolumeAdjustments:
other: int = 0
other_peak: int = 0

_channel_map = {
RelVolAdjFrameV24.CHANNEL_TYPE_MASTER: "master",
RelVolAdjFrameV24.CHANNEL_TYPE_OTHER: "other",
RelVolAdjFrameV24.CHANNEL_TYPE_FRONT_RIGHT: "front_right",
RelVolAdjFrameV24.CHANNEL_TYPE_FRONT_LEFT: "front_left",
RelVolAdjFrameV24.CHANNEL_TYPE_BACK_RIGHT: "back_right",
RelVolAdjFrameV24.CHANNEL_TYPE_BACK_LEFT: "back_left",
RelVolAdjFrameV24.CHANNEL_TYPE_FRONT_CENTER: "front_center",
RelVolAdjFrameV24.CHANNEL_TYPE_BACK_CENTER: "back_center",
RelVolAdjFrameV24.CHANNEL_TYPE_BASS: "bass",
}

@property
def has_master_channel(self) -> bool:
return bool(self.master or self.master_peak)
Expand Down Expand Up @@ -1464,11 +1479,38 @@ def boundsCheck(self):
if invalids:
raise ValueError(f"Invalid RVAD channel values: {','.join(invalids)}")

def setChannelAdj(self, chan_type, value):
setattr(self, self._channel_map[chan_type], value)

def setChannelPeak(self, chan_type, value):
setattr(self, f"{self._channel_map[chan_type]}_peak", value)

def __init__(self, fid=b"RVAD"):
assert fid == b"RVAD"
super().__init__(fid)
self.adjustments = None

def toV24(self) -> list:
"""Return a list of RVA2 frames"""
converted = []

def append(ch_type, ch_adj, ch_peak):
if not ch_adj and not ch_peak:
return
converted.append(
RelVolAdjFrameV24(channel_type=ch_type, adjustment=ch_adj / 512, peak=ch_peak)
)

for channel in ["front_right", "front_left", "back_right", "back_left",
"front_center", "bass"]:
chtype = getattr(RelVolAdjFrameV24, f"CHANNEL_TYPE_{channel.upper()}")
adj = getattr(self.adjustments, channel)
pk = getattr(self.adjustments, f"{channel}_peak")

append(chtype, adj, pk)

return converted

def parse(self, data, frame_header):
super().parse(data, frame_header)
if self.header.version not in (ID3_V2_3, ID3_V2_2):
Expand Down Expand Up @@ -1871,8 +1913,10 @@ def splitUnicode(data, encoding):
if (len(d) % 2) != 0:
(d, t) = data.split(b"\x00\x00\x00", 1)
d += b"\x00"
else:
raise NotImplementedError(f"Unknown ID3 encoding: {encoding}")
except ValueError as ex:
log.warning("Invalid 2-tuple ID3 frame data: %s", ex)
log.warning(f"Invalid 2-tuple ID3 frame data: {ex}")
d, t = data, b""

return d, t
Expand Down
28 changes: 23 additions & 5 deletions eyed3/id3/tag.py
Expand Up @@ -19,16 +19,15 @@
from ..utils.log import getLogger
log = getLogger(__name__)


class TagException(Error):
pass


ID3_V1_COMMENT_DESC = "ID3v1.x Comment"
ID3_V1_MAX_TEXTLEN = 30
DEFAULT_PADDING = 256


class TagException(Error):
pass


class Tag(core.Tag):
def __init__(self, **kwargs):
self.clear()
Expand Down Expand Up @@ -1183,6 +1182,25 @@ def fidHandled(fid):
description="Subtitle (converted)", text=tsst_frame.text)
converted_frames.append(tsst_frame)

# FIXME
# RVAD (v2.3) --> RVA2 (2.4)
if version == ID3_V2_4 and b"RVAD" in [f.id for f in flist]:
rvad = [f for f in flist if f.id == b"RVAD"][0]
for rva2 in rvad.toV24():
converted_frames.append(rva2)
flist.remove(rvad)
# RVA2 (v2.4) --> RVAD (2.3)
elif version == ID3_V2_3 and b"RVA2" in [f.id for f in flist]:
adj = frames.RelVolAdjFrameV23.VolumeAdjustments()
for rva2 in [f for f in flist if f.id == b"RVA2"]:
adj.setChannelAdj(rva2.channel_type, rva2.adjustment * 512)
adj.setChannelPeak(rva2.channel_type, rva2.peak)
flist.remove(rva2)

rvad = frames.RelVolAdjFrameV23()
rvad.adjustments = adj
converted_frames.append(rvad)

# Raise an error for frames that could not be converted.
if len(flist) != 0:
unconverted = ", ".join([f.id.decode("ascii") for f in flist])
Expand Down
11 changes: 8 additions & 3 deletions eyed3/utils/binfuncs.py
Expand Up @@ -18,13 +18,16 @@
################################################################################
import struct

MAX_INT16 = (2 ** 16) // 2
MIN_INT16 = -(MAX_INT16 - 1)


def bytes2bin(bites, sz=8):
"""Accepts a string of ``bytes`` (chars) and returns an array of bits
representing the bytes in big endian byte order. An optional max ``sz`` for
each byte (default 8 bits/byte) which can be used to mask out higher
bits."""
if 1 > sz > 8:
if sz < 1 or sz > 8:
raise ValueError(f"Invalid sz value: {sz}")

retval = []
Expand Down Expand Up @@ -97,7 +100,9 @@ def bytes2dec(bites, sz=8):
def dec2bin(n, p=1):
"""Convert a decimal value ``n`` to an array of bits (MSB first).
Optionally, pad the overall size to ``p`` bits."""
assert(n >= 0)
assert n >= 0
if type(n) is not int:
n = int(n)
retval = []

while n > 0:
Expand Down Expand Up @@ -143,7 +148,7 @@ def bytes2signedInt16(bites: bytes):

def signedInt162bytes(n: int):
n = int(n)
if -32767 <= n <= 32768:
if MIN_INT16 <= n <= MAX_INT16:
return struct.pack(">h", n)
raise ValueError(f"Signed int16 out of range: {n}")

6 changes: 2 additions & 4 deletions test/id3/test_frames.py
Expand Up @@ -233,10 +233,8 @@ def test_tag_compression(id3tag):


def test_encryption():
with pytest.raises(NotImplementedError):
Frame.encrypt("Iceburn")
with pytest.raises(NotImplementedError):
Frame.decrypt("Iceburn")
assert "Iceburn" == Frame.encrypt("Iceburn")
assert "Iceburn" == Frame.decrypt("Iceburn")


def test_LanguageCodeMixin():
Expand Down
2 changes: 2 additions & 0 deletions test/id3/test_rva.py
Expand Up @@ -151,3 +151,5 @@ def test_default_v24():
assert f2.peak == 666


#def test_RVAD_RVA2(audiofile):
# assert audiofile.tag

0 comments on commit 821ed37

Please sign in to comment.