Skip to content

Commit f8bcf75

Browse files
committed
Use interweaved
1 parent c443465 commit f8bcf75

File tree

1 file changed

+56
-77
lines changed

1 file changed

+56
-77
lines changed

src/render/audio.nim

Lines changed: 56 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,16 @@ proc close(getter: Getter) =
6262
avcodec_free_context(addr getter.decoderCtx)
6363
getter.container.close()
6464

65-
proc get(getter: Getter, start: int, endSample: int): seq[seq[int16]] =
65+
proc get(getter: Getter, start: int, endSample: int): seq[int16] =
6666
# start/end is in samples
6767
let container = getter.container
6868
let stream = getter.stream
6969
let decoderCtx = getter.decoderCtx
7070

7171
let targetSamples = endSample - start
7272

73-
# Initialize result with proper size (default zero-filled)
74-
result = @[newSeq[int16](targetSamples), newSeq[int16](targetSamples)]
73+
# Initialize result with proper size for interleaved stereo (default zero-filled)
74+
result = newSeq[int16](targetSamples * 2)
7575

7676
# Convert sample position to time and seek
7777
let sampleRate = stream.codecpar.sample_rate
@@ -131,60 +131,50 @@ proc get(getter: Getter, start: int, endSample: int): seq[seq[int16]] =
131131
let audioData = cast[ptr UncheckedArray[int16]](frame.data[0])
132132
for i in 0..<samplesToProcess:
133133
let frameIndex = samplesSkippedInFrame + i
134-
for ch in 0..<channels:
135-
result[ch][totalSamples + i] = audioData[frameIndex * channels + ch]
134+
let resultIndex = (totalSamples + i) * 2 # Interleaved index
135+
for ch in 0 ..< channels:
136+
result[resultIndex + ch] = audioData[frameIndex * channels + ch]
136137

137138
elif frame.format == AV_SAMPLE_FMT_S16P.cint:
138139
# Planar 16-bit
139140
for i in 0..<samplesToProcess:
140141
let frameIndex = samplesSkippedInFrame + i
141-
for ch in 0..<channels:
142+
let resultIndex = (totalSamples + i) * 2 # Interleaved index
143+
for ch in 0 ..< channels:
142144
if frame.data[ch] != nil:
143145
let channelData = cast[ptr UncheckedArray[int16]](frame.data[ch])
144-
result[ch][totalSamples + i] = channelData[frameIndex]
146+
result[resultIndex + ch] = channelData[frameIndex]
145147

146148
elif frame.format == AV_SAMPLE_FMT_FLT.cint:
147149
# Interleaved float
148150
let audioData = cast[ptr UncheckedArray[cfloat]](frame.data[0])
149151
for i in 0..<samplesToProcess:
150152
let frameIndex = samplesSkippedInFrame + i
151-
for ch in 0..<channels:
153+
let resultIndex = (totalSamples + i) * 2 # Interleaved index
154+
for ch in 0 ..< channels:
152155
# Convert float to 16-bit int with proper clamping
153156
let floatSample = audioData[frameIndex * channels + ch]
154157
let clampedSample = max(-1.0, min(1.0, floatSample))
155-
result[ch][totalSamples + i] = int16(clampedSample * 32767.0)
158+
result[resultIndex + ch] = int16(clampedSample * 32767.0)
156159

157160
elif frame.format == AV_SAMPLE_FMT_FLTP.cint:
158161
# Planar float
159162
for i in 0..<samplesToProcess:
160163
let frameIndex = samplesSkippedInFrame + i
161-
for ch in 0..<channels:
164+
let resultIndex = (totalSamples + i) * 2 # Interleaved index
165+
for ch in 0 ..< channels:
162166
if frame.data[ch] != nil:
163167
let channelData = cast[ptr UncheckedArray[cfloat]](frame.data[ch])
164168
# Convert float to 16-bit int with proper clamping
165169
let floatSample = channelData[frameIndex]
166170
let clampedSample = max(-1.0, min(1.0, floatSample))
167-
result[ch][totalSamples + i] = int16(clampedSample * 32767.0)
171+
result[resultIndex + ch] = int16(clampedSample * 32767.0)
168172
else:
169-
# Unsupported format - samples already initialized to silence
170-
discard
173+
error &"Unsupported audio format: {av_get_sample_fmt_name(frame.format)}"
171174

172175
totalSamples += samplesToProcess
173176
samplesProcessed += samples
174177

175-
# If we have mono input, duplicate to second channel
176-
if result.len >= 2 and result[0].len > 0 and result[1].len > 0:
177-
# Check if second channel is all zeros (mono source)
178-
var isSecondChannelEmpty = true
179-
for i in 0..<min(1000, result[1].len): # Check more samples for accuracy
180-
if result[1][i] != 0:
181-
isSecondChannelEmpty = false
182-
break
183-
184-
if isSecondChannelEmpty:
185-
# Copy first channel to second for stereo output using copyMem
186-
copyMem(addr result[1][0], addr result[0][0], result[0].len * sizeof(int16))
187-
188178
proc createAudioFilterGraph(clip: Clip, sr: int, layout: string): (ptr AVFilterGraph,
189179
ptr AVFilterContext, ptr AVFilterContext) =
190180
var filterGraph: ptr AVFilterGraph = avfilter_graph_alloc()
@@ -251,17 +241,17 @@ proc createAudioFilterGraph(clip: Clip, sr: int, layout: string): (ptr AVFilterG
251241
return (filterGraph, bufferSrc, bufferSink)
252242

253243
# Returns seq[int16] where channel data is interleaved: [L, R, L, R, L, R] etc.
254-
proc processAudioClip(clip: Clip, data: seq[seq[int16]], sourceSr: cint, targetSr: cint): seq[int16] =
255-
if data.len == 0 or data[0].len == 0:
244+
proc processAudioClip(clip: Clip, data: seq[int16], sourceSr: cint, targetSr: cint): seq[int16] =
245+
if data.len == 0:
256246
return @[]
257247

258248
# First apply speed/volume processing at source sample rate (if needed)
259249
var processedData = data
260250
if clip.speed != 1.0 or clip.volume != 1.0:
261-
let actualChannels = data.len
262-
let channels = if actualChannels == 1: 1 else: 2
263-
let samples = data[0].len
264-
let layout = (if channels == 1: "mono" else: "stereo")
251+
# Data is interleaved: [L, R, L, R, ...] so always stereo
252+
let channels = 2
253+
let samples = data.len div 2
254+
let layout = "stereo"
265255
let (filterGraph, bufferSrc, bufferSink) = createAudioFilterGraph(clip, sourceSr, layout)
266256
defer:
267257
if filterGraph != nil:
@@ -284,22 +274,13 @@ proc processAudioClip(clip: Clip, data: seq[seq[int16]], sourceSr: cint, targetS
284274
if av_frame_get_buffer(inputFrame, 0) < 0:
285275
error "Could not allocate input audio frame buffer"
286276

287-
# Copy input data to frame (planar format)
277+
# Copy input data to frame (convert from interleaved to planar format)
288278
for ch in 0..<channels:
289279
let channelData = cast[ptr UncheckedArray[int16]](inputFrame.data[ch])
290280
for i in 0..<samples:
291-
if ch == 0:
292-
if i < data[0].len:
293-
channelData[i] = data[0][i]
294-
else:
295-
channelData[i] = 0
296-
elif ch == 1:
297-
if actualChannels >= 2 and i < data[1].len:
298-
channelData[i] = data[1][i]
299-
elif i < data[0].len:
300-
channelData[i] = data[0][i]
301-
else:
302-
channelData[i] = 0
281+
let srcIndex = i * 2 + ch # Interleaved index
282+
if srcIndex < data.len:
283+
channelData[i] = data[srcIndex]
303284
else:
304285
channelData[i] = 0
305286

@@ -327,64 +308,61 @@ proc processAudioClip(clip: Clip, data: seq[seq[int16]], sourceSr: cint, targetS
327308

328309
outputFrames.add(outputFrame)
329310

330-
# Convert output frames back to seq[seq[int16]]
311+
# Convert output frames back to interleaved seq[int16]
331312
if outputFrames.len == 0:
332-
processedData = @[newSeq[int16](0), newSeq[int16](0)]
313+
processedData = @[]
333314
else:
334315
var totalSamples = 0
335316
for frame in outputFrames:
336317
totalSamples += frame.nb_samples.int
337318

338-
processedData = @[newSeq[int16](totalSamples), newSeq[int16](totalSamples)]
319+
processedData = newSeq[int16](totalSamples * 2) # Interleaved stereo
339320

340321
var sampleOffset = 0
341322
for frame in outputFrames:
342323
let frameSamples = frame.nb_samples.int
343324
let frameChannels = min(frame.ch_layout.nb_channels.int, 2)
344325

345326
if frame.format == AV_SAMPLE_FMT_S16P.cint:
346-
for ch in 0..<min(processedData.len, frameChannels):
347-
if frame.data[ch] != nil:
348-
let channelData = cast[ptr UncheckedArray[int16]](frame.data[ch])
349-
for i in 0..<frameSamples:
350-
if sampleOffset + i < processedData[ch].len:
351-
processedData[ch][sampleOffset + i] = channelData[i]
327+
# Convert from planar to interleaved
328+
for i in 0..<frameSamples:
329+
let interleavedIndex = (sampleOffset + i) * 2
330+
for ch in 0..<frameChannels:
331+
if frame.data[ch] != nil and interleavedIndex + ch < processedData.len:
332+
let channelData = cast[ptr UncheckedArray[int16]](frame.data[ch])
333+
processedData[interleavedIndex + ch] = channelData[i]
352334
elif frame.format == AV_SAMPLE_FMT_S16.cint:
335+
# Already interleaved, just copy
353336
let audioData = cast[ptr UncheckedArray[int16]](frame.data[0])
354337
for i in 0..<frameSamples:
355-
for ch in 0..<min(processedData.len, frameChannels):
356-
if sampleOffset + i < processedData[ch].len:
357-
processedData[ch][sampleOffset + i] = audioData[i * frameChannels + ch]
338+
let interleavedIndex = (sampleOffset + i) * 2
339+
for ch in 0..<frameChannels:
340+
if interleavedIndex + ch < processedData.len:
341+
processedData[interleavedIndex + ch] = audioData[i * frameChannels + ch]
358342

359343
sampleOffset += frameSamples
360344

361-
# Duplicate mono to stereo if needed
362-
if processedData.len >= 2 and processedData[0].len > 0 and processedData[1].len > 0:
345+
# Duplicate mono to stereo if needed (in interleaved format)
346+
if processedData.len >= 2:
363347
var isSecondChannelEmpty = true
364-
for i in 0..<min(1000, processedData[1].len):
365-
if processedData[1][i] != 0:
348+
for i in 0..<min(1000, processedData.len div 2):
349+
if processedData[i * 2 + 1] != 0:
366350
isSecondChannelEmpty = false
367351
break
368352
if isSecondChannelEmpty:
369-
copyMem(addr processedData[1][0], addr processedData[0][0], processedData[0].len * sizeof(int16))
353+
for i in 0..<(processedData.len div 2):
354+
processedData[i * 2 + 1] = processedData[i * 2]
370355

371356
# Now resample from source to target sample rate
372357
if sourceSr == targetSr:
373-
# Convert from channel-separated to interleaved format
374-
let channels = min(processedData.len, 2)
375-
let samples = if processedData.len > 0: processedData[0].len else: 0
376-
result = newSeq[int16](samples * 2) # Always use stereo output
377-
for i in 0..<samples:
378-
result[i * 2] = if channels > 0 and i < processedData[0].len: processedData[0][i] else: 0
379-
result[i * 2 + 1] = if channels > 1 and i < processedData[1].len: processedData[1][i] else:
380-
(if channels > 0 and i < processedData[0].len: processedData[0][i] else: 0)
381-
return result
358+
# Data is already in interleaved format
359+
return processedData
382360

383-
if processedData.len == 0 or processedData[0].len == 0:
361+
if processedData.len == 0:
384362
return @[]
385363

386-
let channels = processedData.len
387-
let samples = processedData[0].len
364+
let channels = 2 # Always stereo interleaved
365+
let samples = processedData.len div 2
388366

389367
# Create resampler for this conversion
390368
var resampler = newAudioResampler(AV_SAMPLE_FMT_S16P, if channels == 1: "mono" else: "stereo", targetSr.int)
@@ -406,12 +384,13 @@ proc processAudioClip(clip: Clip, data: seq[seq[int16]], sourceSr: cint, targetS
406384
if av_frame_get_buffer(inputFrame, 0) < 0:
407385
error "Could not allocate input frame buffer for resampling"
408386

409-
# Copy data to input frame
387+
# Copy data to input frame (convert from interleaved to planar)
410388
for ch in 0..<channels:
411389
let channelData = cast[ptr UncheckedArray[int16]](inputFrame.data[ch])
412390
for i in 0..<samples:
413-
if i < processedData[ch].len:
414-
channelData[i] = processedData[ch][i]
391+
let srcIndex = i * 2 + ch # Interleaved index
392+
if srcIndex < processedData.len:
393+
channelData[i] = processedData[srcIndex]
415394
else:
416395
channelData[i] = 0
417396

0 commit comments

Comments
 (0)