Trying to figuring out how ma_encoder_init works #1046
-
|
Hi mate, thank you for developing miniaudio, and I really have quite some fun on messing around with the low level API, managed to played some audio files and generated some waveforms. Besides, since I am more familiar and feel more comfortable working on zig, I am currently contributing a zig binding for this library; however, I am currently puzzling to the function ma_encoder_init since I don't seem to find any documentation mentioning how to set up onWrite and onSeek function for a custom encoder and how it works, and seems the documentation and the example code only covers ma_encoder_init_file only. The reason I have to use this particular function instead ma_encoder_init_file is that I need to demonstrate the zig binding being functional and usable, but it is not a good idea to generate and to remove a file during the unit test, so I am thinking of building a custom encoder such that I can encode my raw sample into wav format stored in a buffer (instead of writing into an file), and verify the output. I have also looked into this discussion, but the initialization part of the encoder is missing from the example which is one of the key point to my question. I am currently having a look into the function ma_encoder_init__internal, but I am still figuring it out; thus, for the time being, I wish to know:
Please let me know if I have overlooked the documentation for this part or further details and clarification are required. Edit: I think I have figured out something for the onWrite function, but since I am working in zig, I need to find a way to explain that in C; nonetheless, I think I could answer some of the questions (I will keep on update this post when I have found a new finding): |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
|
Alright, this is the first time I have used the discussion board (where I normally use issues in other repo) and I didn't know that I need to set this board to Q&A; nonetheless, I have found my way out, and understood how things work. Before declaring the encoder, we need to have onWrite and onSeek presents so that the ma_encoder_init function can generate the file header using these two functions. To declare a onWrite function, the parameters are the following:
Thus, with knowing all these parameters, if we just want to write something into a buffer, all we need to do is to append the data into a buffer object of your choice stored in pEncoder, and update the number of pBytesWritten; however, since I am currently working in a zig binding, I don't have the c example for now, but the idea should be the same and I will convert that into c code if I have the time: fn testing_on_write(encoder: *Encoder, buffer_in: *anyopaque, bytes_to_write: usize, bytes_written: *usize) callconv(.c) Result {
const buffer_in_data: [*]u8 = @ptrCast(@alignCast(buffer_in));
const result_buffer: *TestingEncodedStorage = @ptrCast(@alignCast(encoder.getUserData()));
result_buffer.data_list.appendSlice(result_buffer.data_allocator, buffer_in_data[0..bytes_to_write]) catch return Result.out_of_memory;
bytes_written.* += bytes_to_write;
return Result.success;
}Since onSeek is not required for my case, I could just declare an empty function that fulfills the function requirement. fn testing_on_seek(encoder: *Encoder, offset: i64, origin: Vfs.SeekOrigin) callconv(.c) Result {
_ = encoder;
_ = offset;
_ = origin;
return Result.success;
}With the two functions, I can now creating the encoder with my own user data, which is a pointer to an array list: var encoder_result = TestingEncodedStorage{
.data_list = try std.ArrayList(u8).initCapacity(std.testing.allocator, 0),
.data_allocator = std.testing.allocator,
};
defer encoder_result.data_list.deinit(std.testing.allocator);
const encoder_cfg = Encoder.Config.init(.wav, .unsigned8, 1, 44100);
try expect(encoder_cfg.channels == 1);
try expect(encoder_cfg.encoding_format == .wav);
try expect(encoder_cfg.format == .unsigned8);
try expect(encoder_cfg.sample_rate == 44100);
var encoder = try Encoder.create(testing_on_write, testing_on_seek, @ptrCast(&encoder_result), encoder_cfg);
defer encoder.destroy();After all, the steps are identical to the documentation about encoder. But now I have a question: because I don't use onSeek, what is the purpose of it? |
Beta Was this translation helpful? Give feedback.
Alright, this is the first time I have used the discussion board (where I normally use issues in other repo) and I didn't know that I need to set this board to Q&A; nonetheless, I have found my way out, and understood how things work.
Before declaring the encoder, we need to have onWrite and onSeek presents so that the ma_encoder_init function can generate the file header using these two functions.
To declare a onWrite function, the parameters are the following:
ma_encoder* pEncoder: self explanatory, and if you need to return the encoded result into an arraylist or a buffer, you many put the buffer in pEncoder->userDataconst void* pBufferIn: this parameter stores the data to be writtenβ¦