#include "mac.h" #include #include #include #include #include #include gchar *fmts[] = { "mac", "ape", NULL }; InputPlugin mac_plugin_info = { NULL, NULL, NULL, mac_init, mac_about, NULL, mac_is_our_file, NULL, mac_play_file, mac_stop, mac_pause, mac_seek, NULL, NULL, NULL, NULL, mac_cleanup, NULL, NULL, NULL, NULL, mac_get_song_info, mac_file_info_box, NULL, NULL, NULL, NULL, NULL, fmts, }; static PlayerInfo *mac_info; #ifdef __cplusplus extern "C"{ #endif static char *get_file_extname(const char *filename) { char *ext = strrchr(filename, '.'); if (ext != NULL) ++ext; return ext; } static char *get_tag_info(CAPETag *tag, wchar_t *fieldname) { if (!tag) { return NULL; } CAPETagField *pTagField = tag->GetTagField(fieldname); if (pTagField == NULL) { return ""; } const char *fieldValue; char *value; fieldValue = pTagField->GetFieldValue(); if (tag->GetAPETagVersion() == CURRENT_APE_TAG_VERSION) { value = GetANSIFromUTF8((unsigned char *)fieldValue); } else { value = (char *)g_malloc0(255); strcpy(value, fieldValue); } char *ret = g_strdup(value); free(value); return ret; } /* #undef XMMS_NEW_TITLEINPUT #define XMMS_NEW_TITLEINPUT(input) G_STMT_START { \ input = (TitleInput *)g_malloc0(sizeof(TitleInput)); \ input->__size = XMMS_TITLEINPUT_SIZE; \ input->__version = XMMS_TITLEINPUT_VERSION; \ } G_STMT_END */ static char *mac_format_title_string(char *name, CAPETag *tag) { gchar *filename = g_strdup(name); char *ret = NULL; TitleInput *input = NULL; bool hasTag = false; if (tag && (tag->GetHasID3Tag() || tag->GetHasAPETag())) { hasTag = true; input = bmp_title_input_new(); input->performer = get_tag_info(tag, APE_TAG_FIELD_ARTIST); input->album_name = get_tag_info(tag, APE_TAG_FIELD_ALBUM); input->track_name = get_tag_info(tag, APE_TAG_FIELD_TITLE); input->track_number = atoi(get_tag_info(tag, APE_TAG_FIELD_TRACK)); input->year = atoi(get_tag_info(tag, APE_TAG_FIELD_YEAR)); input->genre = get_tag_info(tag, APE_TAG_FIELD_GENRE); input->comment = get_tag_info(tag, APE_TAG_FIELD_COMMENT); input->file_name = g_strdup(g_basename(filename)); input->file_path = filename; input->file_ext = get_file_extname(filename); ret = xmms_get_titlestring(xmms_get_gentitle_format(), input); g_free(input); } if (!hasTag && !ret) { ret = (char *)g_strdup(g_basename(filename)); } return ret; } static int decompress_init(InputPlayback *playback) { char *filename = url2filename(playback->filename); int nRetVal; mac_info = (PlayerInfo *)g_malloc0(sizeof(PlayerInfo)); playback->playing = false; playback->eof = false; mac_info->seek_to = -1; IAPEDecompress *decompress = NULL; wchar_t *pUTF16 = GetUTF16FromANSI(filename); decompress = CreateIAPEDecompress(pUTF16, &nRetVal); free(pUTF16); if (!decompress || nRetVal != ERROR_SUCCESS) { return -1; } mac_info->pAPEDecompress = decompress; CAPETag *tag = (CAPETag *)decompress->GetInfo(APE_INFO_TAG); mac_info->title = mac_format_title_string(filename, tag); mac_info->sample_rate = decompress->GetInfo(APE_INFO_SAMPLE_RATE); mac_info->bits_per_sample = decompress->GetInfo(APE_INFO_BITS_PER_SAMPLE); mac_info->channels = decompress->GetInfo(APE_INFO_CHANNELS); mac_info->length_in_ms = decompress->GetInfo(APE_DECOMPRESS_LENGTH_MS); mac_info->block_align = decompress->GetInfo(APE_INFO_BLOCK_ALIGN); mac_info->sample_format = (mac_info->bits_per_sample == 16) ? FMT_S16_LE : FMT_S8; mac_info->seek_to = -1; playback->eof = false; /* printf("title = %s\n", mac_info->title); printf("sample = %d\n", mac_info->sample_rate); printf("bits per sampler = %d\n", mac_info->bits_per_sample); printf("channels = %d\n", mac_info->channels); printf("length = %d\n", mac_info->length_in_ms); printf("sample format = %s\n", (mac_info->sample_format == FMT_S16_LE) ? "FMT_S16_LE" : "FMT_S8"); */ return 0; } #define SAMPLES_PER_READ 512 static void *decode_loop(void *arg) { InputPlayback *playback = (InputPlayback *) arg; char data[9216]; int blocks_to_read, actrual_read; int nRetVal; int bytes, actrual_bytes; blocks_to_read = SAMPLES_PER_READ; bytes = blocks_to_read * mac_info->block_align; while (playback->playing) { if (!playback->eof) { actrual_read = blocks_to_read; actrual_bytes = bytes; nRetVal = mac_info->pAPEDecompress->GetData(data, blocks_to_read, &actrual_read); if (actrual_read) { if (actrual_read < blocks_to_read) { actrual_bytes = actrual_read * mac_info->block_align; } bytes = blocks_to_read * (mac_info->bits_per_sample / 8) * mac_info->channels; while (playback->output->buffer_free() < actrual_bytes && playback->playing && mac_info->seek_to == -1) { xmms_usleep(10000); } if (playback->playing && mac_info->seek_to == -1) { /* printf("actrual bytes = %d\n", actrual_bytes); playback->output->write_audio(data, actrual_bytes); */ produce_audio(playback->output->written_time(), (mac_info->sample_format == FMT_S16_LE) ? FMT_S16_LE : FMT_S8, mac_info->channels, bytes, data, &playback->playing); } } else { playback->eof = true; playback->output->buffer_free(); xmms_usleep(10000); } } else { xmms_usleep(10000); } if (mac_info->seek_to != -1) { int to_block = mac_info->seek_to * mac_info->sample_rate; mac_info->pAPEDecompress->Seek(to_block); playback->output->flush(mac_info->seek_to * 1000); mac_info->seek_to = -1; } } g_thread_exit(NULL); return NULL; } /*********************************************** * * * Interface Functions * * * ***********************************************/ InputPlugin *get_iplugin_info() { mac_plugin_info.description = g_strdup_printf("Monkey's Audio Codec Player %s", VERSION); return &mac_plugin_info; } void mac_init() { } void mac_about() { static GtkWidget *aboutbox = NULL; if (aboutbox != NULL) return; aboutbox = bmp_info_dialog( _("About Monkey's Audio Codec plugin"), _("MAC decoding engine by Matthew T. Ashland \nPlugin by SuperMMX "), _("OK"), FALSE, NULL, NULL); g_signal_connect(GTK_OBJECT(aboutbox), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &aboutbox); } int mac_is_our_file(char *filename) { char *ext; ext = strrchr(filename, '.'); if(ext) { if(!strcasecmp(ext, ".mac") || !strcasecmp(ext, ".ape") || !strcasecmp(ext, ".apl")) { return true; } } return false; } void mac_play_file(InputPlayback *playback) { guint32 bitrate = 0; if (decompress_init(playback) < 0) { return; } playback->playing = true; if(playback->output->open_audio(mac_info->sample_format, mac_info->sample_rate, mac_info->channels) == 0) { return; } bitrate = mac_info->pAPEDecompress->GetInfo(APE_DECOMPRESS_AVERAGE_BITRATE); mac_plugin_info.set_info(mac_info->title, mac_info->length_in_ms, bitrate * 1000, mac_info->sample_rate, mac_info->channels); mac_info->decoder_thread = g_thread_create(decode_loop, playback, TRUE, NULL); } void mac_stop(InputPlayback *playback) { if(playback->playing) { playback->playing = false; g_thread_join(mac_info->decoder_thread); playback->output->close_audio(); if (mac_info->pAPEDecompress) { delete mac_info->pAPEDecompress; mac_info->pAPEDecompress = NULL; } g_free(mac_info); mac_info = NULL; } } void mac_pause(InputPlayback *playback, short paused) { playback->output->pause(paused); } void mac_seek(InputPlayback *playback, int time) { mac_info->seek_to = time; playback->eof = false; while (mac_info->seek_to != -1) { xmms_usleep(10000); } } void mac_cleanup() { } void mac_get_song_info(char *filename, char **title, int *length) { url2filename(filename); int nRetVal = 0; if (filename == NULL) return; wchar_t *pUTF16Name = GetUTF16FromANSI(filename); IAPEDecompress *pDec = CreateIAPEDecompress(pUTF16Name, &nRetVal); if (nRetVal != ERROR_SUCCESS) { if (title) { static const char *errtitle = "Invalid MAC File: "; *title = (char *)g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); sprintf(*title, "%s\"%s\"", errtitle, filename); } if (length) { *length = -1; } return; } if (title) { CAPETag *apeTag = (CAPETag *)pDec->GetInfo(APE_INFO_TAG); *title = mac_format_title_string(filename, apeTag); } if (length) { *length = pDec->GetInfo(APE_DECOMPRESS_LENGTH_MS); } delete pDec; } #ifdef __cplusplus } #endif