File size: 16,358 Bytes
7e6c03a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# Multihop Temporal Reasoning Tasks β€” Detailed Walkthrough

## Architecture Overview

All 8 new task generators follow a shared architecture built on `MultihopBaseGenerator`, which provides:
- **Scene building**: Constructing multi-event audio sequences with controlled order, durations, volumes, and silences
- **Dataset generation**: Sample-loop orchestration with balanced question type distribution
- **CSV output**: Standardized MCQ, open-text, and metadata CSV writing

```mermaid
graph TD
    A[MultihopBaseGenerator] --> B[ConditionalCountTaskGenerator]
    A --> C[ConditionalDurationTaskGenerator]
    A --> D[BetweenEventsTaskGenerator]
    A --> E[EventDensityTaskGenerator]
    A --> F[DurationGapTaskGenerator]
    A --> G[TemporalArithmeticTaskGenerator]
    A --> H[TemporalLoudnessTaskGenerator]
    A --> I[MultiHopTaskGenerator]
    
    J[ESC50Dataset] --> A
    K[PreprocessedESC50Dataset] --> C
    K --> F
    K --> G
    K --> I
    L[AudioProcessor] --> A
    M[QuestionGenerator] --> A
```

### Key Design Decision: Why a Shared Base?

The existing 7 tasks (count, duration, order, etc.) are standalone classes with no shared base. This worked because each task has fundamentally different audio construction logic (e.g., overlap uses `overlay()`, during_contains uses temporal containment, silence_gap uses controlled gaps).

The **multihop tasks are different** β€” they all build the same kind of audio scene (sequential events with gaps and volume variation) but ask different questions over that scene. The complexity is in the **question generation and answer computation**, not the audio construction. So a shared base class makes sense here.

---

## Base Class: `MultihopBaseGenerator`

**File:** [multihop_base.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/multihop_base.py)

### What it provides

| Method | Purpose |
|--------|---------|
| `__init__` | Loads config, initializes dataset/processor/question generator, creates output dirs |
| `build_sequential_scene()` | Builds an audio scene with N events, controlled gaps, optional per-event volume |
| `generate_dataset()` | Orchestrates sample generation loop with balanced question types |
| `_save_mcq_csv()` | Writes MCQ CSV in standard pipeline format |
| `_save_open_text_csv()` | Writes open-text CSV in standard pipeline format |
| `_save_metadata_csv()` | Writes metadata CSV |

### Scene Building Logic

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  gap_0  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  gap_1  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  gap_2  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Event 0 │◄──────►│ Event 1 │◄──────►│ Event 2 │◄──────►│ Event 3 β”‚
β”‚  cat_A   β”‚         β”‚  cat_B   β”‚         β”‚  cat_A   β”‚         β”‚  cat_C   β”‚
β”‚  vol: 0dBβ”‚         β”‚ vol:-6dB β”‚         β”‚ vol:+3dB β”‚         β”‚ vol:-2dB β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

1. Sample `n_events` unique categories from ESC-50
2. Load and extend each source clip to fill its time slot via `concatenate_to_target_duration()`
3. Apply per-event volume adjustments (if specified)
4. Generate controlled gap durations using `generate_controlled_gap_durations()` (ensures the longest gap is β‰₯ `gap_multiplier Γ— shortest gap`)
5. Concatenate: `event + silence + event + silence + ...`
6. Return per-event metadata (start_ms, end_ms, duration, volume, category)

---

## Task 1: `ConditionalCountTaskGenerator`

**File:** [task_conditional_count.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_conditional_count.py)
**Family:** Multihop Singular
**Reasoning chain:** Temporal condition β†’ Count

### What it does

Counts sound events **after applying a temporal filter**. Unlike the simple COUNT task which asks "how many unique sounds?", this task asks "how many X sounds occur **after** Y?" β€” requiring the model to first identify when Y occurs, then count X events only in the filtered region.

### Audio Construction

Builds a scene with **4–8 events**, intentionally allowing **repeated categories** (unlike between_events which uses all unique). This is critical because counting repeated occurrences is the core question.

```python
# Example scene: categories = [dog, cat, dog, bird, cat, dog, bird]
# count_after(target=dog, anchor=cat[0]) β†’ 3 (indices 2, 5 after first cat)
# count_before(target=bird, anchor=dog[2]) β†’ 1 (index 3 before third dog)
```

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `count_after` | Pick anchor index β†’ count `target_sound` in `categories[anchor+1:]` |
| `count_before` | Pick anchor index β†’ count `target_sound` in `categories[:anchor]` |
| `count_between` | Pick two anchor indices β†’ count events in `categories[idx1+1:idx2]` |
| `count_during` | Pick anchor β†’ count all other `target_sound` occurrences |
| `count_overlap` | Pick anchor β†’ count adjacent events (neighbors at Β±1 index) |

### MCQ Generation

Uses `QuestionGenerator.generate_count_mcq()` β€” generates 4 integer options including the correct count, with distractors sampled from nearby integers.

---

## Task 2: `ConditionalDurationTaskGenerator`

**File:** [task_conditional_duration.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_conditional_duration.py)
**Family:** Multihop Singular
**Reasoning chain:** Temporal condition β†’ Duration comparison

### What it does

Compares durations **only among events in a temporal subset**. "Which sound **after** Y lasts the longest?" requires: (1) identify Y's position, (2) consider only sounds after Y, (3) compare their durations.

### Audio Construction

Uses **`PreprocessedESC50Dataset`** (not plain ESC50Dataset) to get **effective durations** β€” the actual content duration after silence trimming. This ensures duration comparisons are meaningful (a 5s clip with 3s of trailing silence has effective duration 2s).

```python
# Each event loaded with known effective duration:
fname, fpath, eff_dur = self.dataset.sample_file_from_category_with_duration(cat)
```

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `longest_after` | Filter events after anchor β†’ `max(effective_durations)` |
| `shortest_after` | Filter events after anchor β†’ `min(effective_durations)` |
| `longest_before` | Filter events before anchor β†’ `max(effective_durations)` |
| `shortest_before` | Filter events before anchor β†’ `min(effective_durations)` |
| `repeated_compare` | Find category appearing both before AND after anchor β†’ compare their durations |

### MCQ Generation

Uses `QuestionGenerator.generate_category_mcq()` β€” the answer is a sound category name, with distractors drawn from other categories present in the audio.

---

## Task 3: `BetweenEventsTaskGenerator`

**File:** [task_between_events.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_between_events.py)
**Family:** Multihop Singular
**Reasoning chain:** Define temporal window β†’ Identify/Count events inside

### What it does

Identifies or counts sounds occurring **inside the temporal window between two anchor events**. "Which sound occurs between X and Y?" requires: (1) find X, (2) find Y, (3) identify events strictly between them.

### Audio Construction

Uses **all unique categories** (no repeats) so that "which sound between X and Y?" has an unambiguous answer. Scene has **5–8 events**.

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `identify_between` | Pick two anchors `idx1 < idx2` β†’ randomly select one event from `categories[idx1+1:idx2]` as answer |
| `count_between` | Pick two anchors β†’ `len(categories[idx1+1:idx2])` |
| `yes_no_between` | Pick target β†’ check if target is in the between-window (50/50 true/false balance) |

### Key Constraint

Anchors are chosen with **at least 2 positions apart** (`idx2 β‰₯ idx1 + 2`) to guarantee at least 1 event between them. Samples with fewer than 3 events are rejected.

---

## Task 4: `EventDensityTaskGenerator`

**File:** [task_event_density.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_event_density.py)
**Family:** Multihop Singular
**Reasoning chain:** Segment audio into regions β†’ Compare event counts

### What it does

Compares **event frequency across temporal regions**. "Which half of the audio contains more sound events?" requires counting events in each half and comparing.

### Audio Construction

Builds scenes with **6–10 events** (larger scenes to make density differences meaningful). **Allows repeated categories** so label-specific density questions are possible. Categories are shuffled randomly β€” the natural random distribution creates asymmetry.

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `half_density` | Split at midpoint β†’ compare `len(first_half)` vs `len(second_half)` β†’ answer is "first half" / "second half" / "equal" |
| `before_after_density` | Pick anchor β†’ compare count of events before vs after anchor |
| `label_density` | Pick anchor + target label β†’ compare count of `target_sound` before vs after anchor |

### MCQ Generation

Custom option construction β€” options are textual ("first half", "second half", "equal", "cannot determine") rather than category names.

---

## Task 5: `DurationGapTaskGenerator`

**File:** [task_duration_gap.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_duration_gap.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Duration reasoning ↔ Silence-gap reasoning

### What it does

**Crosses two task families** β€” compares sound event duration against silence gap duration. "Which is longer: the dog sound or the silence after the dog sound?" requires perceiving both the event's length and the gap's length, then comparing.

### Audio Construction

Uses `PreprocessedESC50Dataset` for effective durations. **Wider gap range** (500–4000ms) compared to other tasks, ensuring gaps are sometimes longer and sometimes shorter than events β€” making the comparison non-trivial.

Each event's metadata tracks both `effective_duration_ms` and `gap_before_ms` / `gap_after_ms`.

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `event_vs_after_gap` | Pick event (not last) β†’ compare `effective_duration_ms` vs `gap_after_ms` |
| `event_vs_before_gap` | Pick event (not first) β†’ compare `effective_duration_ms` vs `gap_before_ms` |
| `gap_vs_event` | Pick a gap between events A,B β†’ pick third event C β†’ compare `gap_duration` vs `C.effective_duration_ms` |

---

## Task 6: `TemporalArithmeticTaskGenerator`

**File:** [task_temporal_arithmetic.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_temporal_arithmetic.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Duration + Count/Repetition + Silence reasoning

### What it does

Compares **aggregated** durations β€” total active time of one label vs another, or total sound time vs total silence time. Requires mentally summing up all occurrences of a repeated sound.

### Audio Construction

Uses `PreprocessedESC50Dataset`. **Allows repeated categories** (critical for aggregation). Computes two aggregate quantities:
- **`label_totals`**: Total effective duration per category (summed across all occurrences)
- **`total_silence_ms`**: Sum of all gap durations

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `total_label_duration` | Pick two categories β†’ compare `label_totals[sound1]` vs `label_totals[sound2]` |
| `label_vs_silence` | Pick one category β†’ compare `label_totals[target]` vs `total_silence_ms` |
| `combined_duration` | Pick one category β†’ compare `label_totals[target]` vs `max(gap_durations)` (longest single silence) |

---

## Task 7: `TemporalLoudnessTaskGenerator`

**File:** [task_temporal_loudness.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_temporal_loudness.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Temporal filter β†’ Loudness comparison

### What it does

Combines **temporal filtering** with **loudness comparison**. "Which sound after Y is the loudest?" requires: (1) identify Y's position, (2) consider only sounds after Y, (3) compare their loudness levels.

### Audio Construction

Each event gets a **random volume adjustment** from `volume_range_db` (default: [-12, +6] dB). A minimum volume difference (`min_volume_diff_db`: 3dB) is enforced to ensure the loudest/softest distinction is perceivable.

Actual loudness is measured using **LUFS** (`get_lufs_loudness()`) for perceptually accurate loudness comparison.

### Question Types & Answer Computation

| Type | Logic |
|------|-------|
| `loudest_after` | Filter events after anchor β†’ `max(loudness_lufs)` |
| `softest_after` | Filter events after anchor β†’ `min(loudness_lufs)` |
| `loudest_before` | Filter events before anchor β†’ `max(loudness_lufs)` |
| `softest_before` | Filter events before anchor β†’ `min(loudness_lufs)` |
| `repeated_loudness` | Find category with multiple occurrences β†’ which occurrence is loudest? Also detects trend (louder/softer over time) |

---

## Task 8: `MultiHopTaskGenerator`

**File:** [task_multi_hop.py](file:///home/debarpanb1/TREA_2.0/pipeline/tasks/task_multi_hop.py)
**Family:** Multihop Inter-Task
**Reasoning chain:** Explicit 2–3 step chains across different properties

### What it does

The **most complex task** β€” requires explicit multi-step reasoning across different temporal/acoustic properties. "What sound occurs after the longest sound?" requires: (1) identify the longest sound (duration property), (2) find what comes after it (order property).

### Audio Construction

The **richest scene** β€” combines ALL controllable dimensions:
- **Duration variation** via PreprocessedESC50Dataset effective durations
- **Volume variation** via per-event dB adjustments ([-10, +6] dB range)
- **Silence variation** via controlled gaps (500–3000ms range, 2.5Γ— multiplier)
- **Category repetition** for count-based sub-questions

### Question Types & Reasoning Chains

| Type | Step 1 | Step 2 | Step 3 |
|------|--------|--------|--------|
| `after_longest` | Find longest duration event | Return next event in sequence | β€” |
| `before_longest` | Find longest duration event | Return previous event | β€” |
| `after_shortest` | Find shortest duration event | Return next event | β€” |
| `before_loudest` | Find loudest event (LUFS) | Return previous event | β€” |
| `after_longest_gap` | Find longest silence gap | Return event after that gap | (or count remaining events) |
| `loudest_after_anchor` | Find anchor position | Filter events after anchor | Find loudest among them |
| `longest_before_anchor` | Find anchor position | Filter events before anchor | Find longest among them |
| `count_before_loudest` | Find loudest event | Filter events before it | Count target_sound occurrences |
| `count_after_longest` | Find longest event | Filter events after it | Count target_sound occurrences |

### Answer Computation Example

```python
# after_longest: "What sound occurs after the longest sound?"
pivot_idx = durations_ms.index(max(durations_ms))  # Step 1: find longest
target_idx = pivot_idx + 1                          # Step 2: get next event
correct = categories[target_idx]                    # Answer: that category
```

---

## Summary: What Makes Each Task Unique

| Task | Audio Uniqueness | Question Uniqueness |
|------|-----------------|-------------------|
| **conditional_count** | Repeated categories | Temporal filter β†’ integer count |
| **conditional_duration** | PreprocessedESC50 effective durations | Temporal filter β†’ duration superlative |
| **between_events** | All unique categories, β‰₯3 events | Temporal window identification |
| **event_density** | 6–10 events, asymmetric distribution | Region-based count comparison |
| **duration_gap** | Wide gap range (500–4000ms) | Cross-property: event-dur vs silence-dur |
| **temporal_arithmetic** | Repeated categories + tracked totals | Aggregated duration arithmetic |
| **temporal_loudness** | Per-event volume variation + LUFS measurement | Temporal filter β†’ loudness superlative |
| **multi_hop** | All dimensions varied simultaneously | 2–3 step reasoning chains |