Skip to content

Commit 4a063da

Browse files
committed
fix: arrangement parse logic (partly), closes #32
1 parent ceff918 commit 4a063da

3 files changed

Lines changed: 35 additions & 25 deletions

File tree

pyflp/arrangement.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ def _collect_events(self, enum_: type[EventEnum]):
377377
ins_events.append(events)
378378

379379
ins_dict: DefaultDict[int, list[AnyEvent]] = collections.defaultdict(list)
380-
for i in range(max(counter)):
380+
for i in range(max(counter, default=0)):
381381
for sublist in ins_events:
382382
try:
383383
event = sublist[i]
@@ -441,26 +441,25 @@ def __getitem__(self, index: SupportsIndex) -> Arrangement:
441441
# parse; it contains ArrangementID.New event followed by TimeMarker events
442442
# followed by 500 TrackID events. TimeMarkers occured before new arrangement
443443
# event in initial versions of FL20, making them harder to group.
444+
# TODO This logic might not work on older versions of FL.
444445
def __iter__(self) -> Iterator[Arrangement]:
445-
first = True
446-
events: list[AnyEvent] = []
447-
448-
def make_arr():
449-
return Arrangement(*events, version=self._kw["version"])
446+
"""Provides an iterator over :class:`Arrangement`s found in the project.
450447
448+
Raises:
449+
NoModelsFound: When no arrangements are found.
450+
"""
451+
arrs_evs: list[list[AnyEvent]] = [[] for _ in range(len(self))]
452+
idx = 0
451453
for event in self._events_tuple:
452454
if event.id == ArrangementID.New:
453-
if not first:
454-
yield make_arr()
455-
events = []
456-
first = not first
457-
elif event.id == ArrangementsID.Current:
458-
return make_arr() # last arrangement
455+
idx = event.value
459456

460457
for enum_ in (ArrangementID, TimeMarkerID, TrackID):
461458
if event.id in enum_:
462-
events.append(event)
463-
break
459+
arrs_evs[idx].append(event)
460+
461+
for arr_evs in arrs_evs:
462+
yield Arrangement(*arr_evs, version=self._kw["version"])
464463

465464
def __len__(self):
466465
"""The number of arrangements present in the project.
@@ -488,8 +487,8 @@ def current(self) -> Arrangement | None:
488487
index = event.value
489488
try:
490489
return list(self)[index]
491-
except IndexError:
492-
raise ModelNotFound(index)
490+
except IndexError as exc:
491+
raise ModelNotFound(index) from exc
493492

494493
height = EventProp[int](ArrangementsID.WindowHeight)
495494
"""Window height / track width used by the interface."""

pyflp/pattern.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ class PatternsID(EventEnum):
101101
# ChannelIID, _161, _162, Looped, Length occur when pattern is looped.
102102
# ChannelIID and _161 occur for every channel in order.
103103
# ! Looping a pattern puts timemarkers in it. The same TimeMarkerID events are
104-
# !used, which means I need to refactor it out from pyflp.arrangement.
104+
# ! used, which means I need to refactor it out from pyflp.arrangement.
105+
# TODO Patterns share TimeMarker events with Arrangements
105106
class PatternID(EventEnum):
106107
Looped = (26, BoolEvent)
107108
New = (WORD + 1, U16Event) # Marks the beginning of a new pattern, twice.

pyflp/project.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -164,22 +164,32 @@ def __init__(self, *events: AnyEvent, **kw: Unpack[_ProjectKW]):
164164
def __repr__(self) -> str:
165165
return f"FL Studio {str(self.version)} {self.format.name}"
166166

167-
def _collect_events(self, *enums: type[EventEnum]) -> list[AnyEvent]:
168-
events: list[AnyEvent] = []
167+
def _collect_events(self, *enums: type[EventEnum]):
169168
for event in self._events_tuple:
170169
for enum_ in enums:
171170
if event.id in enum_:
172-
events.append(event)
171+
yield event
173172
break
174-
return events
175173

176174
@property
177175
def arrangements(self) -> Arrangements:
178176
"""Provides an iterator over arrangements and other related properties."""
179-
return Arrangements(
180-
*self._collect_events(ArrangementID, ArrangementsID, TrackID, TimeMarkerID),
181-
version=self.version,
182-
)
177+
arrnew_occured = False
178+
filtered_events: list[AnyEvent] = []
179+
for event in self._collect_events(
180+
ArrangementID, ArrangementsID, TrackID, TimeMarkerID
181+
):
182+
if event.id == ArrangementID.New:
183+
arrnew_occured = True
184+
185+
# * Prevents accidentally passing on Pattern's timemarkers
186+
# TODO This logic will still be incorrect if arrangement's
187+
# timemarkers occur before ArrangementID.New event.
188+
elif event.id in TimeMarkerID and not arrnew_occured:
189+
continue
190+
filtered_events.append(event)
191+
192+
return Arrangements(*filtered_events, version=self.version)
183193

184194
artists = EventProp[str](ProjectID.Artists)
185195
"""Authors / artists info. to be embedded in exported WAV & MP3.

0 commit comments

Comments
 (0)