from enum import Enum import ctypes from ctypes import byref 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, dll_path=None): if dll_path is None: dll_path = 'C:/Program Files (x86)/Audible/Bin/AAXSDKWin.dll' print(dll_path) dll = ctypes.CDLL(dll_path) 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, byref(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, byref(sample_rate)) if res != 0: raise Exception('AAXGetSampleRate: {}'.format(res)) return sample_rate def seek(dll, handle, position=0): res = dll.AAXSeek(handle, ctypes.c_int(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), byref(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), byref(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), byref(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, ctypes.c_int(mdn), None, byref(data_len)) if res != 0: raise Exception('AAXGetMetadataInfo: {}'.format(res)) buf = ctypes.create_string_buffer(data_len) res = dll.AAXGetMetadata(handle, ctypes.int(mdn), buf, byref(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, ctypes.c_uint(chapter)) if res != 0: raise Exception('AAXSeekToChapter: {}'.format(res)) def get_chapter_count(dll, handle): n_chapters = ctypes.c_uint() res = dll.AAXGetChapterCount(handle, byref(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, byref(chapter)) if res != 0: raise Exception('AAXGetCurrentChapter: {}'.format(res)) return chapter def get_duration(dll, handle): duration = ctypes.c_uint() res = dll.AAXGetDuration(handle, byref(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, byref(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, byref(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, byref(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, byref(bitrate)) if res != 0: raise Exception('AAXGetAvgBitrate: {}'.format(res)) return bitrate