commit e5d1ba572689ab4505549e57777d01a4a448457a Author: jan Date: Mon Jul 11 23:18:16 2016 -0700 initial code commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13bbe5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/py_aax_decode/__init__.py b/py_aax_decode/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py_aax_decode/native.py b/py_aax_decode/native.py new file mode 100644 index 0000000..7af4bf5 --- /dev/null +++ b/py_aax_decode/native.py @@ -0,0 +1,233 @@ +from enum import Enum +import ctypes +import wave + + +class MetaData(Enum): + album_artist = 0x40617574 + publisher = 0x4070726F + artist = 0x406E6172 + title = 0x406C6465 # TIT3 + copyright = 0x40636F70 + date = 0x40706461 + JPEG = 0x40636172 + audible_id = 0x40706964 + album = 0x40746974 + # _unknown1 = 0x40707469 + # _unknown2 = 0x40736465 #SHORTDESC + # _unknown3 = 0x4075616 #STILLNOKLINGON? + + +def main(in_file, out_file): + handle = open_file(dll, in_file) + + a = (dll, handle) + channels = get_audio_channel_count(*a) + sample_rate = get_sample_rate(*a) + seek(*a, 0) + authenticate(*a) + + enc_buf = ctypes.create_string_buffer(0x400) + dec_buf = ctypes.create_string_buffer(0x400 * 200) + + length = 0 + with wave.open(out_file, 'wb') as wav: + wav.setnchannels(channels) + wav.setframerate(sample_rate) + + while True: + enc_data, enc_len = get_encoded_audio(*a, enc_buf) + + if enc_len == 0: + break + + dec_data, dec_len = decode_pcm_frame(*a, enc_buf, enc_len, dec_buf) + length += dec_len + + wav.writeframes(dec_buf) + + close_file(*a) + + +def open_file(dll, filename): + handle = ctypes.pointer(ctypes.c_byte) + res = dll.AAXOpenFileWinW(handle, filename) + + if res != 0: + raise Exception('AAXOpenFileWinW: {}'.format(res)) + + return handle + + +def close_file(dll, handle): + res = dll.AAXCloseFile(handle) + + if res != 0: + raise Exception('AAXCloseFile: {}'.format(res)) + + +def get_audio_channel_count(dll, handle): + n_channels = ctypes.c_uint() + res = dll.AAXGetAudioChannelCount(handle, n_channels) + + if res != 0: + raise Exception('AAXGetAudioChannelCount: {}'.format(res)) + + return n_channels + + +def get_sample_rate(dll, handle): + sample_rate = ctypes.c_uint() + res = dll.AAXGetSampleRate(handle, n_channels) + + if res != 0: + raise Exception('AAXGetSampleRate: {}'.format(res)) + + return sample_rate + + +def seek(dll, handle, position=0): + res = dll.AAXSeek(handle, position) + + if res != 0: + raise Exception('AAXSeek: {}'.format(res)) + + +def authenticate(dll, handle): + res = dll.AAXAuthenticateWin(handle) + + if res != 0: + raise Exception('AAXAuthenticateWin: {}'.format(res)) + + +def get_encoded_audio(dll, handle, buf=None): + if buf is None: + buf = ctypes.create_string_buffer(0x400) + + data_len = ctypes.c_uint() + res = dll.AAXGetEncodedAudio(handle, buf, len(buf), data_len) + + if res != 0: + raise Exception('AAXGetEncodedAudio: {}'.format(res)) + + return buf, data_len + + +def decode_pcm_frame(dll, handle, in_buf, in_len, out_buf=None): + if out_buf is None: + out_buf = ctypes.create_string_buffer(0x400 * 200) + + data_len = ctypes.c_uint() + res = dll.AAXDecodedPCMFrame(handle, in_buf, in_len, out_buf, len(out_buf), data_len) + + if res != 0: + raise Exception('AAXDecodedPCMFrame: {}'.format(res)) + + return out_buf, data_len + + +def get_chapter_text(dll, handle, chapter_num, buf=None): + if buf is None: + buf = ctypes.create_string_buffer(0x400) + + data_len = ctypes.c_uint() + res = dll.AAXGetChapterText(handle, chapter_num, buf, len(buf), data_len) + + if res != 0: + raise Exception('AAXGetChapterText: {}'.format(res)) + + return buf, data_len + + +def get_metadata(dll, handle, mdn): + data_len = ctypes.c_uint() + res = dll.AAXGetMetadataInfo(handle, mdn, None, data_len) + + if res != 0: + raise Exception('AAXGetMetadataInfo: {}'.format(res)) + + buf = ctypes.create_string_buffer(data_len) + res = dll.AAXGetMetadata(handle, mdn, buf, data_len) + + if res != 0: + raise Exception('AAXGetChapterText: {}'.format(res)) + + return buf, data_len + + +def seek_to_chapter(dll, handle, chapter): + res = dll.AAXSeekToChapter(handle, chapter) + + if res != 0: + raise Exception('AAXSeekToChapter: {}'.format(res)) + + +def get_chapter_count(dll, handle): + n_chapters = ctypes.c_uint() + res = dll.AAXGetChapterCount(handle, n_chapters) + + if res != 0: + raise Exception('AAXGetChapterCount: {}'.format(res)) + + return n_chapters + + +def get_current_chapter(dll, handle): + chapter = ctypes.c_uint() + res = dll.AAXGetCurrentChapter(handle, chapter) + + if res != 0: + raise Exception('AAXGetCurrentChapter: {}'.format(res)) + + return chapter + + +def get_duration(dll, handle): + duration = ctypes.c_uint() + res = dll.AAXGetDuration(handle, duration) + + if res != 0: + raise Exception('AAXGetDuration: {}'.format(res)) + + return duration + + +def get_playback_position(dll, handle): + position = ctypes.c_uint() + res = dll.AAXGetPlaybackPosition(handle, position) + + if res != 0: + raise Exception('AAXGetPlaybackPosition: {}'.format(res)) + + return position + + +def get_chapter_start_time(dll, handle, chapter): + position = ctypes.c_uint() + res = dll.AAXGetChapterStartTime(handle, position, chapter) + + if res != 0: + raise Exception('AAXGetChapterStartTime: {}'.format(res)) + + return position + + +def get_avg_bitrate(dll, handle): + bitrate = ctypes.c_uint() + res = dll.AAXGetAvgBitrate(handle, bitrate) + + if res != 0: + raise Exception('AAXGetAvgBitrate: {}'.format(res)) + + return bitrate + + +def get_max_bitrate(dll, handle): + bitrate = ctypes.c_uint() + res = dll.AAXGetMaxBitrate(handle, bitrate) + + if res != 0: + raise Exception('AAXGetAvgBitrate: {}'.format(res)) + + return bitrate +