Thanks for the updated code!
I think this should even be possible with a Conv1d using out_channels=out_channels*num_layers and a view on the result tensor.
I created a small example which demonstrates this approach.
For an easy comparison of both methods I set bias=False and initialized the weigths for each “layer” with numbers in range(num_layers). So the first layer uses all zeros, the second ones, the third twos, etc.
in_channels = 3
num_layers = 12
out_channels = 6
batch_size = 10
signal_length = 5
kernel_size = 1
features = torch.randn(batch_size, in_channels, signal_length)
features2 = features.clone()

convs = nn.ModuleList(
    [nn.Conv1d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, bias=False)
     for _ in range(num_layers)])
for i, conv in enumerate(convs):
    conv.weight.data.fill_(1 * i)

features = [conv(features) for conv in convs]
features = torch.stack(features, dim=1)
print('First layer: ', features[:, 0])
print('Second layer: ', features[:, 1])

# Second approach
conv1d = nn.Conv1d(
    in_channels=in_channels,
    out_channels=out_channels*num_layers,
    kernel_size=kernel_size,
    bias=False)

# Set manually weight to same as used in convs ModuleList
weight = [torch.ones(out_channels, in_channels, kernel_size) * i for i in range(num_layers)]
weight = torch.cat(weight)
conv1d.weight.data = weight

features2 = conv1d(features2)
features2 = features2.view(batch_size, num_layers, out_channels, -1)
print('First layer: ', features2[:, 0])
print('First layer: ', features2[:, 1])

# Print error for all layers
for idx in range(num_layers):
    print('Error in layer {}: {}'.format(
        idx, torch.abs(features[:, idx] - features2[:, idx]).sum()))