Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacing float2mozzy.py with new audio2mozzi.py #295

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

leobel96
Copy link

@leobel96 leobel96 commented Dec 5, 2024

No description provided.

@leobel96 leobel96 changed the title Fixes for flot2mozzi.py script Replacing float2mozzy.py with new audio2mozzi.py Dec 6, 2024
@leobel96
Copy link
Author

leobel96 commented Dec 6, 2024

As mentioned in the commit message:
The script allows to create mozzi tables from raw files and it's supposed to replace all the "*2mozzy.py" scripts thanks to its high configurability (it also supports int16/int32 tables in case they'll be supported in the future)

@tomcombriat
Copy link
Collaborator

Haven't try but looks good to me at first glance!

One thing that might be worth adding as an option would be to allow to make the table "symmetrical" (see #280 ), which, I think Audacity is not doing per default but which can be made easily with your script?

@leobel96
Copy link
Author

leobel96 commented Dec 6, 2024

One thing that might be worth adding as an option would be to allow to make the table "symmetrical"

Uhm, I guess that you are talking about Oscil wavetables here, not Sample ones, right?

@tomcombriat
Copy link
Collaborator

Principally yes, but can also be relevant for samples? (This allows to negate them).

@leobel96
Copy link
Author

leobel96 commented Dec 7, 2024

And with "symmetrical" you don't actually mean that the first half of the table has to be identical to the specular of the second part, right? You only mean that the last value has to be identical to the first one to not have a gap and avoid weird artifacts. So you just want me to append a tail of cells to the table that gradually connects the last cell to the first one. Did I understand everything correctly?

@tomcombriat
Copy link
Collaborator

Ah, sorry for the confusion, I think symmetrical I maybe not the right word. By that I mean more a symmetrical range, so that the samples go from -127 to 127 and not to 128. That does not the sound but that kind simplify things sometimes... That's maybe nitpicking...

For the start and end point, I think it makes sense that they start at 0 to avoid a click, but I guess that's more the job of the one cutting the samples than your script....

@leobel96
Copy link
Author

leobel96 commented Dec 7, 2024

so that the samples go from -127 to 127 and not to 128

Uhm, how can they go to 128 if they are int8_t 🤔? Did you mean -128? So you mean symmetrical respect to the X axis in a way that the minimum negative value is equal to the maximum positive one in absolute value?

@tomcombriat
Copy link
Collaborator

Yes, sorry for the typo and the confusion.
I guess Samples do not have to span the full range actually, but if it is easy to make the script so that -128 is not present that might be useful.

@leobel96
Copy link
Author

leobel96 commented Dec 7, 2024

So is the whole thing just a matter of replacing all the -128 with -127? That's it? 😂

@tomcombriat
Copy link
Collaborator

Well, that's one way to do it ;). But a cleaner way would be to renormalize the whole signal in that range (to avoid saturation/hard clip) around -127. But I actually think that it is probably nearly equivalent when starting with 8bits…

(For instance, you can check how I did it for some of the existing tables here (second code snip).

@leobel96
Copy link
Author

leobel96 commented Dec 7, 2024

Ah! This makes sense, your snippet makes everything more clear. I can add this bit in the next few days and amend the commit above

@tomcombriat
Copy link
Collaborator

Sometimes a bit of code is better than a full speech ;). Sorry for the confusion

@leobel96
Copy link
Author

Hey! I finally found some time to add that symmetrical part. Do you mind testing the script and, if it works as expected, merge it? @tomcombriat

@tomcombriat
Copy link
Collaborator

Nice! I'll have a look at it very soon!

Thanks for this

cnt_33 = 0
else:
cnt_33 = 0
if args.make_symmetrical:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this block should read:

        if args.make_symmetrical:
            value_to_remove = -max_output_value 
            if value_to_remove in out_values:
                in_min = value_to_remove
                in_max = max_output_value
                out_max = max_output_value
                out_min = -max_output_value+1
                out_values = (int((value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min) for value in out_values)

in the current version: value_to_remove = -max_output_value - 1 is -128-1=-129 for 8bits, hence the test is never passed. out_min should be set to -127 in that case to return the correct rounded value. Also note the conversion back to int :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything should be nicer now 🙂

out_values.append(
math.trunc((value * max_output_value) + 0.5)
if args.input_encoding == "float"
else value
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't some kind of normalization happen here in case of 16bits as input? From my tests, inputting a 16bits and expecting an 8bits output produces some way out of range values.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you are right, I didn't test it properly. Using some help from Numpy 😄

parser.add_argument(
"-e",
"--input-encoding",
choices=("float", "int"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In float mode, with default number of output bits, some values reach 128 (which do not fit). Maybe you should discard my comment on the make symmetric but subtract -1 to the max output value?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uhm, I didn't really understand this. Do you mean that the output of the script reaches 128? That's not expected

@@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""
For converting raw audio files to Mozzi table
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add some copyright info (you or Mozzi team and you depending on how much you drew from the original script) and a license (LGPL and GPL are already in Mozzi)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

for value in in_values:
cnt_33 = 0
out_values.append(
math.trunc((value * max_output_value) + 0.5)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In conjunction with my other comments on the output range, might be easier to use round instead of trunc?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note that I did not look at the original script, where some of the rough edges I found might already be ;).) The script is very easy to use (and made me discovered ArgumentParser… I was still doing that manually…)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this trunc thing was already present in the original script and confused me too. I wanted to keep this script as close as possible to the original. I think that round would work better too. Fixed in the new version.

fout.write(table)
fout.write("\n\n")
fout.write(f"#endif /* {tablename}_H_ */\n")
print(f"wrote {table} to {output_path}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally remove the print statements that, IMO, clutters the terminal.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or maybe as a --verbose argument?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, added a --verbose argument for this

The script allows to create mozzi tables from raw files and it's supposed to replace all the "*2mozzy.py" scripts thanks to its high configurability (it also supports int16/int32 tables in case they'll be supported in the future)
@tomcombriat
Copy link
Collaborator

Thanks! Give me a few days to test it :)

Copy link
Collaborator

@tomcombriat tomcombriat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay!

Works like a charm when converting to the same number of bits, but I think still fails a bit (pun unintended) when having different number of bits between input and output (that should work right?):

  • when scaling down (from 16 to 8 say) the output is very weird, looks like an overflow from the l.120.
  • when scaling up (from 8 to 16 say), the values are not changed (they stay in the 8 bits range)
  • when using --make-symmetrical, the values in the output files are floating points (even when saying 8 bits as an output)..

Here are the files I use for testing (simple sine wave, generated in Audacity, 1s at 8192sampling rate):
raws.zip

else:
raise Exception("Unrecognised --input-bits value")

output_array = input_array.astype(output_dtype)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this conversion should happen after. If you start with a 16 bits raw file, and want to convert it to 8bits, this line provokes an overflow, hence the values in output_array are not valid anymore…

I think, if I were to do it, I would probably make everything float, scale to the final output range, knowing the input range (or finding it actually, as long as you know how they are encoded) (your map_value seems to be made for that), and convert everything back in the output type at the very end (and think which "downscaler" to use… round? floor?). This might benefit from the higher resolution of the scaling done in float.

But maybe I understood you script wrong?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding on this, I see now that your mapping is not used when make_symmetrical is false. astype does not change the values, but just reinterpret them to the new type (100 as int8 will stay 100 as int16).

elif args.output_bits == 32:
output_dtype = np.int32
else:
raise Exception("Unrecognised --input-bits value")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I think this should read "--output-bits"

max_value_to_normalize = np.iinfo(output_dtype).max
min_final_value = np.iinfo(output_dtype).min + 1
max_final_value = max_value_to_normalize
if min_value_to_normalize in output_array:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand this line actually… When changing types (or working with non-normalized samples) this if is not necessary true, even though the values still need to be rescaled no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants