diff options
Diffstat (limited to 'plugins')
312 files changed, 55684 insertions, 5948 deletions
diff --git a/plugins/aac/aac.c b/plugins/aac/aac.c index f7925b82..33418593 100644 --- a/plugins/aac/aac.c +++ b/plugins/aac/aac.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -19,11 +19,13 @@ #include <string.h> #include <stdio.h> +#include <unistd.h> #include <neaacdec.h> #ifdef HAVE_CONFIG_H #include "../../config.h" #endif #include <stdlib.h> +#include <math.h> #include "../../deadbeef.h" #include "aac_parser.h" @@ -54,12 +56,26 @@ static DB_functions_t *deadbeef; #define MP4FILE_CB MP4FileProvider #endif + +// aac channel mapping +// 0: Defined in AOT Specifc Config +// 1: 1 channel: front-center +// 2: 2 channels: front-left, front-right +// 3: 3 channels: front-center, front-left, front-right +// 4: 4 channels: front-center, front-left, front-right, back-center +// 5: 5 channels: front-center, front-left, front-right, back-left, back-right +// 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel +// 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel +// 8-15: Reserved + + typedef struct { DB_fileinfo_t info; NeAACDecHandle dec; DB_FILE *file; MP4FILE mp4file; MP4FILE_CB mp4reader; + NeAACDecFrameInfo frame_info; // last frame info int32_t timescale; uint32_t maxSampleSize; int mp4track; @@ -72,16 +88,17 @@ typedef struct { int currentsample; char buffer[AAC_BUFFER_SIZE]; int remaining; - int faad_channels; char out_buffer[OUT_BUFFER_SIZE]; int out_remaining; int num_errors; char *samplebuffer; + int remap[10]; + int noremap; } aac_info_t; // allocate codec control structure static DB_fileinfo_t * -aac_open (void) { +aac_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (aac_info_t)); aac_info_t *info = (aac_info_t *)_info; memset (info, 0, sizeof (aac_info_t)); @@ -101,6 +118,7 @@ aac_fs_seek (void *user_data, uint64_t position) { DB_FILE *fp = (DB_FILE *)user_data; return deadbeef->fseek (fp, position, SEEK_SET); } + #else static void * aac_fs_open (const char *fname, MP4FileMode mode) { @@ -134,7 +152,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration int firstframepos = -1; int fsize = -1; int offs = 0; - if (!fp->vfs->streaming) { + if (!fp->vfs->is_streaming ()) { int skip = deadbeef->junk_get_leading_size (fp); if (skip >= 0) { deadbeef->fseek (fp, skip, SEEK_SET); @@ -158,7 +176,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration int frame = 0; int scanframes = 1000; - if (fp->vfs->streaming) { + if (fp->vfs->is_streaming ()) { scanframes = 1; } @@ -183,7 +201,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration continue; } else { - trace ("aac: frame #%d sync: %d %d %d %d %d\n", frame, channels, samplerate, bitrate, samples, size); + trace ("aac: frame #%d sync: %dch %d %d %d %d\n", frame, channels, samplerate, bitrate, samples, size); frame++; nsamples += samples; if (!stream_sr) { @@ -200,7 +218,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration // *pchannels = stream_ch; // } framepos += size; - if (deadbeef->fseek (fp, size-sizeof(buf), SEEK_CUR) == -1) { + if (deadbeef->fseek (fp, size-(int)sizeof(buf), SEEK_CUR) == -1) { trace ("parse_aac_stream: invalid seek %d\n", size-sizeof(buf)); break; } @@ -213,6 +231,7 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration } *psamplerate = stream_sr; + *pchannels = stream_ch; if (ptotalsamples) { @@ -227,6 +246,12 @@ parse_aac_stream(DB_FILE *fp, int *psamplerate, int *pchannels, float *pduration trace ("aac: duration=%f (%d samples @ %d Hz), fsize=%d\n", *pduration, totalsamples, stream_sr, fsize); } + if (*psamplerate <= 24000) { + *psamplerate *= 2; + if (ptotalsamples) { + *ptotalsamples *= 2; + } + } return firstframepos; } @@ -287,7 +312,9 @@ aac_probe (DB_FILE *fp, const char *fname, MP4FILE_CB *cb, float *duration, int } *channels = mp4ff_get_channel_count (mp4, i); int samples = mp4ff_num_samples(mp4, i) * 1024; - trace ("mp4 nsamples=%d, samplerate=%d\n", samples, *samplerate); + samples = (int64_t)samples * (*samplerate) / mp4ff_time_scale (mp4, i); + + trace ("mp4 nsamples=%d, samplerate=%d, timescale=%d, duration=%lld\n", samples, *samplerate, mp4ff_time_scale(mp4, i), mp4ff_get_track_duration(mp4, i)); *duration = (float)samples / (*samplerate); if (totalsamples) { @@ -368,7 +395,7 @@ static int aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { aac_info_t *info = (aac_info_t *)_info; - info->file = deadbeef->fopen (it->fname); + info->file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->file) { return -1; } @@ -380,7 +407,7 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { int totalsamples = -1; int offs = -1; - if (!info->file->vfs->streaming) { + if (!info->file->vfs->is_streaming ()) { int skip = deadbeef->junk_get_leading_size (info->file); if (skip >= 0) { deadbeef->fseek (info->file, skip, SEEK_SET); @@ -406,9 +433,9 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->mp4reader.close = aac_fs_close; #endif - if (!info->file->vfs->streaming) { + if (!info->file->vfs->is_streaming ()) { #ifdef USE_MP4FF - trace ("aac_init: mp4ff_open_read %s\n", it->fname); + trace ("aac_init: mp4ff_open_read %s\n", deadbeef->pl_find_meta (it, ":URI")); info->mp4file = mp4ff_open_read (&info->mp4reader); if (info->mp4file) { int ntracks = mp4ff_total_tracks (info->mp4file); @@ -446,7 +473,6 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("NeAACDecInit2 returned error\n"); return -1; } - info->faad_channels = ch; samplerate = srate; channels = ch; NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration (info->dec); @@ -478,8 +504,8 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { } } #else - trace ("aac_init: MP4ReadProvider %s\n", it->fname); - info->mp4file = MP4ReadProvider (it->fname, 0, &info->mp4reader); + trace ("aac_init: MP4ReadProvider %s\n", deadbeef->pl_find_meta (it, ":URI")); + info->mp4file = MP4ReadProvider (deadbeef->pl_find_meta (it, ":URI"), 0, &info->mp4reader); info->mp4track = MP4FindTrackId(info->mp4file, 0, "audio", 0); trace ("aac_init: MP4FindTrackId returned %d\n", info->mp4track); if (info->mp4track >= 0) { @@ -494,7 +520,6 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { if (rc >= 0) { _info->samplerate = mp4ASC.samplingFrequency; _info->channels = MP4GetTrackAudioChannels (info->mp4file, info->mp4track); - info->faad_channels = _info->channels; totalsamples = MP4GetTrackNumberOfSamples (info->mp4file, info->mp4track) * 1024 * _info->channels; // init mp4 decoding @@ -505,7 +530,6 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("NeAACDecInit2 returned error\n"); return -1; } - info->faad_channels = ch; samplerate = srate; channels = ch; NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration (info->dec); @@ -551,8 +575,8 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("found aac stream\n"); } - _info->channels = channels; - _info->samplerate = samplerate; + _info->fmt.channels = channels; + _info->fmt.samplerate = samplerate; } else { // sync before attempting to init @@ -563,17 +587,16 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("aac: parse_aac_stream failed\n"); return -1; } - _info->channels = channels; - _info->samplerate = samplerate*2; trace("parse_aac_stream returned %x\n", offs); } + if (offs >= 0) { deadbeef->fseek (info->file, offs, SEEK_SET); } // duration = (float)totalsamples / samplerate; // deadbeef->pl_set_item_duration (it, duration); - _info->bps = 16; + _info->fmt.bps = 16; _info->plugin = &plugin; if (!info->mp4file) { @@ -586,7 +609,7 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->remaining = deadbeef->fread (info->buffer, 1, AAC_BUFFER_SIZE, info->file); NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration (info->dec); - conf->dontUpSampleImplicitSBR = 1; +// conf->dontUpSampleImplicitSBR = 1; NeAACDecSetConfiguration (info->dec, conf); unsigned long srate; unsigned char ch; @@ -608,11 +631,11 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { memmove (info->buffer, info->buffer + consumed, info->remaining - consumed); info->remaining -= consumed; } - info->faad_channels = ch; - _info->channels = ch; + _info->fmt.channels = ch; + _info->fmt.samplerate = srate; } - if (!info->file->vfs->streaming) { + if (!info->file->vfs->is_streaming ()) { if (it->endsample > 0) { info->startsample = it->startsample; info->endsample = it->endsample; @@ -624,6 +647,11 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { } } + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } + info->noremap = 0; + info->remap[0] = -1; trace ("init success\n"); return 0; @@ -651,13 +679,14 @@ aac_free (DB_fileinfo_t *_info) { } static int -aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +aac_read (DB_fileinfo_t *_info, char *bytes, int size) { aac_info_t *info = (aac_info_t *)_info; - int out_ch = min (_info->channels, 2); - if (!info->file->vfs->streaming) { - if (info->currentsample + size / (2 * out_ch) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * out_ch; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + if (!info->file->vfs->is_streaming ()) { + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { + trace ("aac_read: eof"); return 0; } } @@ -665,41 +694,105 @@ aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { int initsize = size; int eof = 0; - int sample_size = out_ch * (_info->bps >> 3); while (size > 0) { if (info->skipsamples > 0 && info->out_remaining > 0) { int skip = min (info->out_remaining, info->skipsamples); if (skip < info->out_remaining) { - memmove (info->out_buffer, info->out_buffer + skip * 2 * info->faad_channels, (info->out_remaining - skip) * 2 * info->faad_channels); + memmove (info->out_buffer, info->out_buffer + skip * samplesize, (info->out_remaining - skip) * samplesize); } info->out_remaining -= skip; info->skipsamples -= skip; } if (info->out_remaining > 0) { - int n = size / sample_size; + int n = size / samplesize; n = min (info->out_remaining, n); char *src = info->out_buffer; - for (int i = 0; i < n; i++) { - memcpy (bytes, src, sample_size); - bytes += sample_size; - src += info->faad_channels * 2; + if (info->noremap) { + memcpy (bytes, src, n * samplesize); + bytes += n * samplesize; + src += n * samplesize; } + else { + int i, j; + if (info->remap[0] == -1) { + // build remap mtx + + // FIXME: should build channelmask 1st; then remap based on channelmask + for (i = 0; i < _info->fmt.channels; i++) { + switch (info->frame_info.channel_position[i]) { + case FRONT_CHANNEL_CENTER: + trace ("FC->%d\n", i); + info->remap[2] = i; + break; + case FRONT_CHANNEL_LEFT: + trace ("FL->%d\n", i); + info->remap[0] = i; + break; + case FRONT_CHANNEL_RIGHT: + trace ("FR->%d\n", i); + info->remap[1] = i; + break; + case SIDE_CHANNEL_LEFT: + trace ("SL->%d\n", i); + info->remap[6] = i; + break; + case SIDE_CHANNEL_RIGHT: + trace ("SR->%d\n", i); + info->remap[7] = i; + break; + case BACK_CHANNEL_LEFT: + trace ("RL->%d\n", i); + info->remap[4] = i; + break; + case BACK_CHANNEL_RIGHT: + trace ("RR->%d\n", i); + info->remap[5] = i; + break; + case BACK_CHANNEL_CENTER: + trace ("BC->%d\n", i); + info->remap[8] = i; + break; + case LFE_CHANNEL: + trace ("LFE->%d\n", i); + info->remap[3] = i; + break; + default: + trace ("aac: unknown ch(%d)->%d\n", info->frame_info.channel_position[i], i); + break; + } + } + if (info->remap[0] == -1) { + info->remap[0] = 0; + } + if ((_info->fmt.channels == 1 && info->remap[0] == FRONT_CHANNEL_CENTER) + || (_info->fmt.channels == 2 && info->remap[0] == FRONT_CHANNEL_LEFT && info->remap[1] == FRONT_CHANNEL_RIGHT)) { + info->noremap = 1; + } + } + + for (i = 0; i < n; i++) { + for (j = 0; j < _info->fmt.channels; j++) { + ((int16_t *)bytes)[info->remap[j]] = ((int16_t *)src)[j]; + } + src += samplesize; + bytes += samplesize; + } + } + size -= n * samplesize; - size -= n * sample_size; if (n == info->out_remaining) { info->out_remaining = 0; } else { - memmove (info->out_buffer, src, (info->out_remaining - n) * info->faad_channels * 2); + memmove (info->out_buffer, src, (info->out_remaining - n) * samplesize); info->out_remaining -= n; } continue; } char *samples = NULL; - NeAACDecFrameInfo frame_info; if (info->mp4file) { unsigned char *buffer = NULL; @@ -732,7 +825,7 @@ aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { break; } info->mp4sample++; - samples = NeAACDecDecode(info->dec, &frame_info, buffer, buffer_size); + samples = NeAACDecDecode(info->dec, &info->frame_info, buffer, buffer_size); if (buffer) { free (buffer); @@ -750,11 +843,11 @@ aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { info->remaining += res; } - samples = NeAACDecDecode (info->dec, &frame_info, info->buffer, info->remaining); + samples = NeAACDecDecode (info->dec, &info->frame_info, info->buffer, info->remaining); if (!samples) { - trace ("NeAACDecDecode failed, consumed=%d\n", frame_info.bytesconsumed); + trace ("NeAACDecDecode failed, consumed=%d\n", info->frame_info.bytesconsumed); if (info->num_errors > 10) { - trace ("NeAACDecDecode failed 10 times, interrupting\n"); + trace ("NeAACDecDecode failed %d times, interrupting\n", info->num_errors); break; } info->num_errors++; @@ -762,7 +855,7 @@ aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { continue; } info->num_errors=0; - int consumed = frame_info.bytesconsumed; + int consumed = info->frame_info.bytesconsumed; if (consumed > info->remaining) { trace ("NeAACDecDecode consumed more than available! wtf?\n"); break; @@ -776,26 +869,19 @@ aac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } } - if (frame_info.samples > 0) { - memcpy (info->out_buffer, samples, frame_info.samples * 2); - info->out_remaining = frame_info.samples / frame_info.channels; + if (info->frame_info.samples > 0) { + memcpy (info->out_buffer, samples, info->frame_info.samples * 2); + info->out_remaining = info->frame_info.samples / info->frame_info.channels; } } - info->currentsample += (initsize-size) / sample_size; + info->currentsample += (initsize-size) / samplesize; return initsize-size; } // returns -1 on error, 0 on success int seek_raw_aac (aac_info_t *info, int sample) { - deadbeef->rewind (info->file); - int skip = deadbeef->junk_get_leading_size (info->file); - if (skip >= 0) { - deadbeef->fseek (info->file, skip, SEEK_SET); - } - - int offs = deadbeef->ftell (info->file); uint8_t buf[ADTS_HEADER_SIZE*8]; int nsamples = 0; @@ -828,14 +914,17 @@ seek_raw_aac (aac_info_t *info, int sample) { continue; } else { - //trace ("aac: frame #%d sync: %d %d %d %d %d\n", frame, channels, samplerate, bitrate, samples, size); + //trace ("aac: frame #%d(%d/%d) sync: %d %d %d %d %d\n", frame, curr_sample, sample, channels, samplerate, bitrate, frame_samples, size); frame++; - if (deadbeef->fseek (info->file, size-sizeof(buf), SEEK_CUR) == -1) { + if (deadbeef->fseek (info->file, size-(int)sizeof(buf), SEEK_CUR) == -1) { trace ("seek_raw_aac: invalid seek %d\n", size-sizeof(buf)); break; } bufsize = 0; } + if (samplerate <= 24000) { + frame_samples *= 2; + } } while (curr_sample + frame_samples < sample); if (curr_sample + frame_samples < sample) { @@ -855,6 +944,16 @@ aac_seek_sample (DB_fileinfo_t *_info, int sample) { info->skipsamples = sample - info->mp4sample * (info->mp4framesize-1); } else { + if (sample < info->currentsample, 1) { + int skip = deadbeef->junk_get_leading_size (info->file); + if (skip >= 0) { + deadbeef->fseek (info->file, skip, SEEK_SET); + } + else { + deadbeef->fseek (info->file, 0, SEEK_SET); + } + } + int res = seek_raw_aac (info, sample); if (res < 0) { return -1; @@ -864,13 +963,13 @@ aac_seek_sample (DB_fileinfo_t *_info, int sample) { info->remaining = 0; info->out_remaining = 0; info->currentsample = sample; - _info->readpos = (float)(info->currentsample - info->startsample) / _info->samplerate; + _info->readpos = (float)(info->currentsample - info->startsample) / _info->fmt.samplerate; return 0; } static int aac_seek (DB_fileinfo_t *_info, float t) { - return aac_seek_sample (_info, t * _info->samplerate); + return aac_seek_sample (_info, t * _info->fmt.samplerate); } #ifdef USE_MP4FF @@ -910,24 +1009,20 @@ aac_load_tags (DB_playItem_t *it, mp4ff_t *mp4) { free (s); } } - it->replaygain_track_gain = 0; - it->replaygain_track_peak = 1; - it->replaygain_album_gain = 0; - it->replaygain_album_peak = 1; if (mp4ff_meta_find_by_name(mp4, "replaygain_track_gain", &s)) { - it->replaygain_track_gain = atof (s); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (s)); free (s); } if (mp4ff_meta_find_by_name(mp4, "replaygain_track_peak", &s)) { - it->replaygain_track_peak = atof (s); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (s)); free (s); } if (mp4ff_meta_find_by_name(mp4, "replaygain_album_gain", &s)) { - it->replaygain_album_gain = atof (s); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (s)); free (s); } if (mp4ff_meta_find_by_name(mp4, "replaygain_album_peak", &s)) { - it->replaygain_album_peak = atof (s); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (s)); free (s); } deadbeef->pl_add_meta (it, "title", NULL); @@ -938,12 +1033,12 @@ aac_load_tags (DB_playItem_t *it, mp4ff_t *mp4) { int aac_read_metadata (DB_playItem_t *it) { #ifdef USE_MP4FF - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } - if (fp->vfs->streaming) { + if (fp->vfs->is_streaming ()) { deadbeef->fclose (fp); return -1; } @@ -971,8 +1066,91 @@ aac_read_metadata (DB_playItem_t *it) { } deadbeef->fclose (fp); #endif + return 0; } +#ifdef USE_MP4FF +#if 0 +static uint32_t +mp4ff_read_cb (void *user_data, void *buffer, uint32_t length) { +// trace ("aac_fs_read %d\n", length); + FILE *fp = (FILE *)user_data; + return fread (buffer, 1, length, fp); +} +static uint32_t +mp4ff_seek_cb (void *user_data, uint64_t position) { +// trace ("aac_fs_seek\n"); + FILE *fp = (FILE *)user_data; + return fseek (fp, position, SEEK_SET); +} +static uint32_t +mp4ff_write_cb(void *user_data, void *buffer, uint32_t length) { + FILE *fp = (FILE *)user_data; + return fwrite (buffer, 1, length, fp); +} + +static uint32_t +mp4ff_truncate_cb(void *user_data) +{ + FILE *fp = (FILE *)user_data; + ftruncate(fileno(fp), ftello(fp)); + return 0; +} +#endif +#endif + +#ifdef USE_MP4FF +#if 0 +static int +aac_write_metadata (DB_playItem_t *it) { + mp4ff_metadata_t md; + memset (&md, 0, sizeof (md)); + deadbeef->pl_lock (); + DB_metaInfo_t *meta = deadbeef->pl_get_metadata_head (it); + + // find numtags 1st + while (meta) { + if (meta->key[0] != ':') { + md.count++; + } + meta = meta->next; + } + + // fill tags + if (md.count) { + md.tags = malloc (sizeof (mp4ff_tag_t) * md.count); + int n = 0; + meta = deadbeef->pl_get_metadata_head (it); + while (meta) { + if (meta->key[0] != ':') { + md.tags[n].item = "";//(char *)meta->key; + md.tags[n].value = (char *)meta->value; + n++; + } + meta = meta->next; + } + } + + mp4ff_callback_t f = { + .read = mp4ff_read_cb, + .write = mp4ff_write_cb, + .seek = mp4ff_seek_cb, + .truncate = mp4ff_truncate_cb, + }; + + FILE *fp = fopen (deadbeef->pl_find_meta (it, ":URI"), "w"); + f.user_data = fp; + + mp4ff_meta_update (&f, &md); + if (md.tags) { + free (md.tags); + } + deadbeef->pl_unlock (); + return 0; +} +#endif +#endif + static DB_playItem_t * aac_insert (DB_playItem_t *after, const char *fname) { trace ("adding %s\n", fname); @@ -986,11 +1164,12 @@ aac_insert (DB_playItem_t *after, const char *fname) { float duration = -1; int totalsamples = 0; int samplerate = 0; + int channels = 0; int mp4track = -1; MP4FILE mp4 = NULL; - if (fp->vfs->streaming) { + if (fp->vfs->is_streaming ()) { trace ("streaming aac (%s)\n", fname); ftype = plugin.filetypes[0]; } @@ -1000,8 +1179,6 @@ aac_insert (DB_playItem_t *after, const char *fname) { deadbeef->fseek (fp, skip, SEEK_SET); } - int channels; - // slowwww! MP4FILE_CB cb = { #ifdef USE_MP4FF @@ -1032,12 +1209,10 @@ aac_insert (DB_playItem_t *after, const char *fname) { } } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = ftype; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", ftype); deadbeef->pl_set_item_duration (it, duration); -// trace ("duration: %f sec\n", duration); + trace ("duration: %f sec\n", duration); // read tags if (mp4) { @@ -1083,9 +1258,22 @@ aac_insert (DB_playItem_t *after, const char *fname) { deadbeef->pl_add_meta (it, "title", NULL); } + int64_t fsize = deadbeef->fgetlength (fp); + deadbeef->fclose (fp); if (duration > 0) { + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + deadbeef->pl_add_meta (it, ":BPS", "16"); + snprintf (s, sizeof (s), "%d", channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / duration * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); // embedded cue deadbeef->pl_lock (); const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); @@ -1123,23 +1311,45 @@ static const char *filetypes[] = { "RAW AAC", "MP4 AAC", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "aac", - .plugin.name = "AAC decoder based on FAAD2", - .plugin.descr = "aac (m4a, mp4, ...) player", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.name = "AAC player", + .plugin.descr = "plays aac files, supports raw aac files, as well as mp4 container", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified libmp4ff (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = aac_open, .init = aac_init, .free = aac_free, - .read_int16 = aac_read_int16, + .read = aac_read, .seek = aac_seek, .seek_sample = aac_seek_sample, .insert = aac_insert, .read_metadata = aac_read_metadata, +#ifdef USE_MP4FF + // mp4ff metadata writer doesn't work + // .write_metadata = aac_write_metadata, +#else +#endif .exts = exts, .filetypes = filetypes }; diff --git a/plugins/aac/aac_parser.c b/plugins/aac/aac_parser.c index 230dec9e..07c1ca6a 100644 --- a/plugins/aac/aac_parser.c +++ b/plugins/aac/aac_parser.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -77,7 +77,7 @@ aac_sync(const uint8_t *buf, int *channels, int *sample_rate, int *bit_rate, int //trace ("invalid channels\n"); return 0; } - trace ("channels %d\n", aac_channels[channel_conf]); + trace ("channels %d (#%d)\n", aac_channels[channel_conf], channel_conf); int orig_copy = (buf[3] & 0x20) >> 5; int home = (buf[3] & 0x10) >> 4; int copyright_ident_bit = (buf[3] & 0x08) >> 3; diff --git a/plugins/aac/aac_parser.h b/plugins/aac/aac_parser.h index 53e7916d..7d3b886e 100644 --- a/plugins/aac/aac_parser.h +++ b/plugins/aac/aac_parser.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/adplug/Makefile.am b/plugins/adplug/Makefile.am index a75f38b5..393f2d59 100644 --- a/plugins/adplug/Makefile.am +++ b/plugins/adplug/Makefile.am @@ -5,11 +5,12 @@ adlibdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = adplug.la AM_CFLAGS = $(CFLAGS) -std=c99 -I$(adplugpath)/adplug -I$(adplugpath)/libbinio +adplug_la_LDFLAGS = -module -nostdlib -lsupc++ -AM_CPPFLAGS = $(CXXFLAGS) -Dstricmp=strcasecmp -DVERSION=\"2.1\" -I$(adplugpath)/adplug -I$(adplugpath)/libbinio +AM_CPPFLAGS = $(CXXFLAGS) -Dstricmp=strcasecmp -DVERSION=\"2.1\" -I$(adplugpath)/adplug -I$(adplugpath)/libbinio -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -adplug_la_SOURCES = adplug-db.cpp\ - plugin.c\ +adplug_la_SOURCES = plugin.c\ + adplug-db.cpp\ libbinio/binfile.h\ libbinio/binio.h\ libbinio/binstr.h\ @@ -134,5 +135,4 @@ adplug_la_SOURCES = adplug-db.cpp\ # adplug/database.cpp # adplug/database.h -adplug_la_LDFLAGS = -module endif diff --git a/plugins/adplug/adplug-db.cpp b/plugins/adplug/adplug-db.cpp index 567d55ca..a25f3ffe 100644 --- a/plugins/adplug/adplug-db.cpp +++ b/plugins/adplug/adplug-db.cpp @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -31,11 +31,22 @@ //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) +int _Unwind_Resume_or_Rethrow; +int _Unwind_RaiseException; +int _Unwind_GetLanguageSpecificData; +int _Unwind_Resume; +int _Unwind_DeleteException; +int _Unwind_GetTextRelBase; +int _Unwind_SetIP; +int _Unwind_GetDataRelBase; +int _Unwind_GetRegionStart; +int _Unwind_SetGR; +int _Unwind_GetIPInfo; + extern "C" { extern DB_decoder_t adplug_plugin; -static DB_functions_t *deadbeef; - +DB_functions_t *deadbeef; const char *adplug_exts[] = { "A2M", "ADL", "AMD", "BAM", "CFF", "CMF", "D00", "DFM", "DMO", "DRO", "DTM", "HSC", "HSP", "IMF", "KSM", "LAA", "LDS", "M", "MAD", "MKJ", "MSC", "MTK", "RAD", "RAW", "RIX", "ROL", "S3M", "SA2", "SAT", "SCI", "SNG", "XAD", "XMS", "XSM", "JBM", NULL }; @@ -53,7 +64,7 @@ typedef struct { } adplug_info_t; DB_fileinfo_t * -adplug_open (void) { +adplug_open (uint32_t hints) { adplug_info_t *info = (adplug_info_t *)malloc (sizeof (adplug_info_t)); DB_fileinfo_t *_info = (DB_fileinfo_t *)info; memset (info, 0, sizeof (adplug_info_t)); @@ -67,17 +78,17 @@ adplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { adplug_info_t *info = (adplug_info_t *)_info; int samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); - int bps = deadbeef->get_output ()->bitspersample (); + int bps = 16; // NOTE: there's no need to support 8bit input, because adplug simply downgrades 16bit signal to 8bits int channels = 2; - info->opl = new CEmuopl (samplerate, true, channels == 2); + info->opl = new CEmuopl (samplerate, bps == 16 ? true : false, channels == 2); // opl->settype (Copl::TYPE_OPL2); - info->decoder = CAdPlug::factory (it->fname, info->opl, CAdPlug::players); + info->decoder = CAdPlug::factory (deadbeef->pl_find_meta (it, ":URI"), info->opl, CAdPlug::players); if (!info->decoder) { - trace ("adplug: failed to open %s\n", it->fname); + trace ("adplug: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } - info->subsong = it->tracknum; + info->subsong = deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0); info->decoder->rewind (info->subsong); float dur = deadbeef->pl_get_item_duration (it); info->totalsamples = dur * samplerate; @@ -86,9 +97,10 @@ adplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { // fill in mandatory plugin fields _info->plugin = &adplug_plugin; - _info->bps = bps; - _info->channels = channels; - _info->samplerate = samplerate; + _info->fmt.bps = bps; + _info->fmt.channels = channels; + _info->fmt.samplerate = samplerate; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; trace ("adplug_init ok (songlength=%d, duration=%f, totalsamples=%d)\n", info->decoder->songlength (info->subsong), deadbeef->pl_get_item_duration (it), info->totalsamples); @@ -112,16 +124,16 @@ adplug_free (DB_fileinfo_t *_info) { } int -adplug_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +adplug_read (DB_fileinfo_t *_info, char *bytes, int size) { // try decode `size' bytes // return number of decoded bytes // return 0 on EOF adplug_info_t *info = (adplug_info_t *)_info; bool playing = true; int i; - int sampsize = (_info->bps >> 3) * _info->channels; + int sampsize = (_info->fmt.bps / 8) * _info->fmt.channels; - if (info->currentsample + size/4 >= info->totalsamples) { + if (info->currentsample + size/sampsize >= info->totalsamples) { // clip size = (info->totalsamples - info->currentsample) * sampsize; trace ("adplug: clipped to %d\n", size); @@ -139,11 +151,11 @@ adplug_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { { while (info->toadd < 0) { - info->toadd += _info->samplerate; + info->toadd += _info->fmt.samplerate; playing = info->decoder->update (); // decoder->time_ms += 1000 / plr.p->getrefresh (); } - i = min (towrite, (long) (info->toadd / info->decoder->getrefresh () + 4) & ~3); + i = min (towrite, (long) (info->toadd / info->decoder->getrefresh () + sampsize) & ~(sampsize-1)); info->opl->update ((short *) sndbufpos, i); sndbufpos += i * sampsize; size -= i * sampsize; @@ -152,7 +164,7 @@ adplug_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { info->toadd -= (long) (info->decoder->getrefresh () * i); } info->currentsample += size/4; - _info->readpos = (float)info->currentsample / _info->samplerate; + _info->readpos = (float)info->currentsample / _info->fmt.samplerate; return initsize-size; } @@ -172,7 +184,7 @@ adplug_seek_sample (DB_fileinfo_t *_info, int sample) { while (info->currentsample < sample) { info->decoder->update (); - int framesize = _info->samplerate / info->decoder->getrefresh (); + int framesize = _info->fmt.samplerate / info->decoder->getrefresh (); info->currentsample += framesize; } @@ -183,7 +195,7 @@ adplug_seek_sample (DB_fileinfo_t *_info, int sample) { info->toadd = 0; trace ("adplug: new position after seek: %d of %d\n", info->currentsample, info->totalsamples); - _info->readpos = (float)info->currentsample / _info->samplerate; + _info->readpos = (float)info->currentsample / _info->fmt.samplerate; return 0; } @@ -193,7 +205,7 @@ adplug_seek (DB_fileinfo_t *_info, float time) { // seek to specified time in seconds // return 0 on success // return -1 on failure - return adplug_seek_sample (_info, time * _info->samplerate); + return adplug_seek_sample (_info, time * _info->fmt.samplerate); } static const char * @@ -252,11 +264,9 @@ adplug_insert (DB_playItem_t *after, const char *fname) { if (dur < 0.1) { continue; } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (adplug_plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = adplug_get_extension (fname); - it->tracknum = i; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, adplug_plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", adplug_get_extension (fname)); + deadbeef->pl_set_meta_int (it, ":TRACKNUM", i); deadbeef->pl_set_item_duration (it, dur); #if 0 // add metainfo diff --git a/plugins/adplug/libbinio/binfile.cpp b/plugins/adplug/libbinio/binfile.cpp index 336f1b3b..a20315ac 100644 --- a/plugins/adplug/libbinio/binfile.cpp +++ b/plugins/adplug/libbinio/binfile.cpp @@ -22,6 +22,8 @@ #include "binfile.h" +extern DB_functions_t *deadbeef; + /***** binfbase *****/ binfbase::binfbase() @@ -37,7 +39,8 @@ binfbase::~binfbase() void binfbase::close() { if(f != NULL) { - if(fclose(f) == EOF) err |= Fatal; else f = NULL; + deadbeef->fclose(f); + f = NULL; } else err |= NotOpen; } @@ -49,9 +52,9 @@ void binfbase::seek(long pos, Offset offs) if(f == NULL) { err |= NotOpen; return; } switch(offs) { - case Set: error = fseek(f, pos, SEEK_SET); break; - case Add: error = fseek(f, pos, SEEK_CUR); break; - case End: error = fseek(f, pos, SEEK_END); break; + case Set: error = deadbeef->fseek(f, pos, SEEK_SET); break; + case Add: error = deadbeef->fseek(f, pos, SEEK_CUR); break; + case End: error = deadbeef->fseek(f, pos, SEEK_END); break; } if(error == -1) err |= Fatal; @@ -63,7 +66,7 @@ long binfbase::pos() if(f == NULL) { err |= NotOpen; return 0; } - pos = ftell(f); + pos = deadbeef->ftell(f); if(pos == -1) { err |= Fatal; @@ -96,7 +99,7 @@ binifstream::~binifstream() void binifstream::open(const char *filename, const Mode mode) { - f = fopen(filename, "rb"); + f = deadbeef->fopen(filename); if(f == NULL) switch(errno) { @@ -118,8 +121,9 @@ binifstream::Byte binifstream::getByte() int read; if(f != NULL) { - read = fgetc(f); - if(read == EOF) err |= Eof; + if (1 != deadbeef->fread (&read, 1, 1, f)) { + err |= Eof; + } return (Byte)read; } else { err |= NotOpen; @@ -151,6 +155,7 @@ binofstream::~binofstream() void binofstream::open(const char *filename, const Mode mode) { +#if 0 const char *modestr = "wb"; // Check if append mode is desired @@ -168,6 +173,7 @@ void binofstream::open(const char *filename, const Mode mode) case ENOENT: err |= NotFound; break; default: err |= NotOpen; break; } +#endif } #if BINIO_ENABLE_STRING @@ -179,10 +185,12 @@ void binofstream::open(const std::string &filename, const Mode mode) void binofstream::putByte(Byte b) { +#if 0 if(f == NULL) { err |= NotOpen; return; } if(fputc(b, f) == EOF) err |= Fatal; +#endif } /***** binfstream *****/ @@ -220,11 +228,11 @@ void binfstream::open(const char *filename, const Mode mode) if(mode & Append) // Create & append modestr = "a+b"; - f = fopen(filename, modestr); + f = deadbeef->fopen(filename); // NoCreate & append (emulated -- not possible with standard C fopen()) if(f != NULL && (mode & Append) && (mode & NoCreate)) - ferror = fseek(f, 0, SEEK_END); + ferror = deadbeef->fseek(f, 0, SEEK_END); if(f == NULL || ferror == -1) { switch(errno) { diff --git a/plugins/adplug/libbinio/binfile.h b/plugins/adplug/libbinio/binfile.h index ad24baee..013e5777 100644 --- a/plugins/adplug/libbinio/binfile.h +++ b/plugins/adplug/libbinio/binfile.h @@ -24,6 +24,8 @@ #include "binio.h" +#include "../../../deadbeef.h" + class binfbase: virtual public binio { public: @@ -47,7 +49,7 @@ public: virtual long pos(); protected: - FILE *f; + DB_FILE *f; }; class binifstream: public binistream, virtual public binfbase diff --git a/plugins/adplug/plugin.c b/plugins/adplug/plugin.c index c4e8e04c..0b53da31 100644 --- a/plugins/adplug/plugin.c +++ b/plugins/adplug/plugin.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -26,13 +26,13 @@ extern const char *adplug_exts[]; extern const char *adplug_filetypes[]; DB_fileinfo_t * -adplug_open (void); +adplug_open (uint32_t hints); int adplug_init (DB_fileinfo_t *_info, DB_playItem_t *it); void adplug_free (DB_fileinfo_t *); int -adplug_read_int16 (DB_fileinfo_t *, char *bytes, int size); +adplug_read (DB_fileinfo_t *, char *bytes, int size); int adplug_seek_sample (DB_fileinfo_t *, int sample); int @@ -47,21 +47,40 @@ adplug_stop (void); // define plugin interface DB_decoder_t adplug_plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "adplug", .plugin.name = "Adplug player", .plugin.descr = "Adplug player (ADLIB OPL2/OPL3 emulator)", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified AdPlug library\n" + "Copyright (C) 1999 - 2010 Simon Peter, et al.\n" + "http://adplug.sourceforge.net/\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = adplug_start, .plugin.stop = adplug_stop, .open = adplug_open, .init = adplug_init, .free = adplug_free, - .read_int16 = adplug_read_int16, + .read = adplug_read, .seek = adplug_seek, .seek_sample = adplug_seek_sample, .insert = adplug_insert, diff --git a/plugins/alsa/alsa.c b/plugins/alsa/alsa.c index 96d7645b..809cb7ed 100644 --- a/plugins/alsa/alsa.c +++ b/plugins/alsa/alsa.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,8 +23,8 @@ #include "../../deadbeef.h" #include "../../config.h" -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) #define min(x,y) ((x)<(y)?(x):(y)) @@ -41,8 +41,7 @@ DB_functions_t *deadbeef; static snd_pcm_t *audio; static int alsa_terminate; -static int requested_rate = -1; -static int alsa_rate = 44100; +static ddb_waveformat_t requested_fmt; static int state; // one of output_state_t static uintptr_t mutex; static intptr_t alsa_tid; @@ -53,7 +52,6 @@ static snd_pcm_uframes_t period_size; static snd_pcm_uframes_t req_buffer_size; static snd_pcm_uframes_t req_period_size; -static int conf_alsa_resample = 0; static char conf_alsa_soundcard[100] = "default"; //static snd_async_handler_t *pcm_callback; @@ -91,7 +89,7 @@ static int palsa_free (void); static int -palsa_change_rate (int rate); +palsa_setformat (ddb_waveformat_t *fmt); static int palsa_play (void); @@ -106,12 +104,6 @@ static int palsa_unpause (void); static int -palsa_get_rate (void); - -static int -palsa_get_bps (void); - -static int palsa_get_channels (void); static int @@ -121,11 +113,21 @@ static void palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void*), void *userdata); static int -palsa_set_hw_params (int samplerate) { +palsa_set_hw_params (ddb_waveformat_t *fmt) { snd_pcm_hw_params_t *hw_params = NULL; -// int alsa_resample = conf_get_int ("alsa.resample", 0); int err = 0; + memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t)); + if (!plugin.fmt.channels) { + // generic format + plugin.fmt.bps = 16; + plugin.fmt.is_float = 0; + plugin.fmt.channels = 2; + plugin.fmt.samplerate = 44100; + plugin.fmt.channelmask = 3; + } +retry: + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); @@ -144,25 +146,87 @@ palsa_set_hw_params (int samplerate) { goto error; } - snd_pcm_format_t fmt; + snd_pcm_format_t sample_fmt; + + switch (plugin.fmt.bps) { + case 8: + sample_fmt = SND_PCM_FORMAT_S8; + break; + case 16: #if WORDS_BIGENDIAN - fmt = SND_PCM_FORMAT_S16_BE; + sample_fmt = SND_PCM_FORMAT_S16_BE; #else - fmt = SND_PCM_FORMAT_S16_LE; + sample_fmt = SND_PCM_FORMAT_S16_LE; #endif - if ((err = snd_pcm_hw_params_set_format (audio, hw_params, fmt)) < 0) { - fprintf (stderr, "cannot set sample format (%s)\n", - snd_strerror (err)); - goto error; + break; + case 24: +#if WORDS_BIGENDIAN + sample_fmt = SND_PCM_FORMAT_S24_3BE; +#else + sample_fmt = SND_PCM_FORMAT_S24_3LE; +#endif + break; + case 32: + if (plugin.fmt.is_float) { +#if WORDS_BIGENDIAN + sample_fmt = SND_PCM_FORMAT_FLOAT_BE; +#else + sample_fmt = SND_PCM_FORMAT_FLOAT_LE; +#endif + } + else { +#if WORDS_BIGENDIAN + sample_fmt = SND_PCM_FORMAT_S32_BE; +#else + sample_fmt = SND_PCM_FORMAT_S32_LE; +#endif + } + break; + }; + + if ((err = snd_pcm_hw_params_set_format (audio, hw_params, sample_fmt)) < 0) { + fprintf (stderr, "cannot set sample format (%s), trying all supported formats\n", snd_strerror (err)); + + int fmt_cnt[] = { 16, 24, 32, 32, 8 }; +#if WORDS_BIGENDIAN + int fmt[] = { SND_PCM_FORMAT_S16_BE, SND_PCM_FORMAT_S24_3BE, SND_PCM_FORMAT_S32_BE, SND_PCM_FORMAT_FLOAT_BE, SND_PCM_FORMAT_S8, -1 }; +#else + int fmt[] = { SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_FLOAT_LE, SND_PCM_FORMAT_S8, -1 }; +#endif + + // 1st try formats with higher bps + int i = 0; + for (i = 0; fmt[i] != -1; i++) { + if (fmt[i] != sample_fmt && fmt_cnt[i] > plugin.fmt.bps) { + if (snd_pcm_hw_params_set_format (audio, hw_params, fmt[i]) >= 0) { + break; + } + } + } + if (fmt[i] == -1) { + // next try formats with lower bps + i = 0; + for (i = 0; fmt[i] != -1; i++) { + if (fmt[i] != sample_fmt && fmt_cnt[i] < plugin.fmt.bps) { + if (snd_pcm_hw_params_set_format (audio, hw_params, fmt[i]) >= 0) { + break; + } + } + } + } + + if (fmt[i] == -1) { + goto error; + } } - snd_pcm_hw_params_get_format (hw_params, &fmt); - trace ("chosen sample format: %04Xh\n", (int)fmt); + snd_pcm_hw_params_get_format (hw_params, &sample_fmt); + trace ("chosen sample format: %04Xh\n", (int)sample_fmt); - int val = samplerate; + int val = plugin.fmt.samplerate; int ret = 0; - if ((err = snd_pcm_hw_params_set_rate_resample (audio, hw_params, conf_alsa_resample)) < 0) { + if ((err = snd_pcm_hw_params_set_rate_resample (audio, hw_params, 1)) < 0) { fprintf (stderr, "cannot setup resampling (%s)\n", snd_strerror (err)); goto error; @@ -173,10 +237,10 @@ palsa_set_hw_params (int samplerate) { snd_strerror (err)); goto error; } - alsa_rate = val; - trace ("chosen samplerate: %d Hz\n", alsa_rate); + plugin.fmt.samplerate = val; + trace ("chosen samplerate: %d Hz\n", val); - if ((err = snd_pcm_hw_params_set_channels (audio, hw_params, 2)) < 0) { + if ((err = snd_pcm_hw_params_set_channels (audio, hw_params, plugin.fmt.channels)) < 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)); goto error; @@ -201,8 +265,70 @@ palsa_set_hw_params (int samplerate) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)); goto error; + +// if (plugin.fmt.channels > 2 && plugin.fmt.samplerate >= 96000) { +// plugin.fmt.samplerate = 48000; +// fprintf (stderr, "falling back to 48000KHz\n"); +// goto retry; +// } + } + + plugin.fmt.is_float = 0; + switch (sample_fmt) { + case SND_PCM_FORMAT_S8: + plugin.fmt.bps = 8; + break; + case SND_PCM_FORMAT_S16_BE: + case SND_PCM_FORMAT_S16_LE: + plugin.fmt.bps = 16; + break; + case SND_PCM_FORMAT_S24_3BE: + case SND_PCM_FORMAT_S24_3LE: + plugin.fmt.bps = 24; + break; + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_S32_LE: + plugin.fmt.bps = 32; + break; + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_FLOAT_BE: + plugin.fmt.bps = 32; + plugin.fmt.is_float = 1; + break; + } + + trace ("chosen bps: %d (%s)\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int"); + + plugin.fmt.channels = nchan; + plugin.fmt.channelmask = 0; + if (nchan == 1) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT; + } + if (nchan == 2) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT; + } + if (nchan == 3) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_LOW_FREQUENCY; + } + if (nchan == 4) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT; + } + if (nchan == 5) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER; + } + if (nchan == 6) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER | DDB_SPEAKER_LOW_FREQUENCY; + } + if (nchan == 7) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER | DDB_SPEAKER_SIDE_LEFT | DDB_SPEAKER_SIDE_RIGHT; + } + if (nchan == 8) { + plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER | DDB_SPEAKER_SIDE_LEFT | DDB_SPEAKER_SIDE_RIGHT | DDB_SPEAKER_LOW_FREQUENCY; } error: + if (err < 0) { + memset (&plugin.fmt, 0, sizeof (ddb_waveformat_t)); + } if (hw_params) { snd_pcm_hw_params_free (hw_params); } @@ -216,10 +342,8 @@ palsa_init (void) { mutex = 0; // get and cache conf variables - strcpy (conf_alsa_soundcard, deadbeef->conf_get_str ("alsa_soundcard", "default")); - conf_alsa_resample = deadbeef->conf_get_int ("alsa.resample", 0); + deadbeef->conf_get_str ("alsa_soundcard", "default", conf_alsa_soundcard, sizeof (conf_alsa_soundcard)); trace ("alsa_soundcard: %s\n", conf_alsa_soundcard); - trace ("alsa.resample: %d\n", conf_alsa_resample); snd_pcm_sw_params_t *sw_params = NULL; state = OUTPUT_STATE_STOPPED; @@ -232,11 +356,11 @@ palsa_init (void) { mutex = deadbeef->mutex_create (); - if (requested_rate != -1) { - alsa_rate = requested_rate; + if (requested_fmt.samplerate != 0) { + memcpy (&plugin.fmt, &requested_fmt, sizeof (ddb_waveformat_t)); } - if (palsa_set_hw_params (alsa_rate) < 0) { + if (palsa_set_hw_params (&plugin.fmt) < 0) { goto open_error; } @@ -309,27 +433,61 @@ open_error: } int -palsa_change_rate (int rate) { - trace ("palsa_change_rate: %d\n", rate); - requested_rate = rate; +palsa_setformat (ddb_waveformat_t *fmt) { + memcpy (&requested_fmt, fmt, sizeof (ddb_waveformat_t)); + trace ("palsa_setformat %dbit %s %dch %dHz channelmask=%X\n", fmt->bps, fmt->is_float ? "float" : "int", fmt->channels, fmt->samplerate, fmt->channelmask); if (!audio) { - return alsa_rate; + return -1; } - if (rate == alsa_rate) { - trace ("palsa_change_rate %d: ignored\n", rate); - return rate; + if (!memcmp (fmt, &plugin.fmt, sizeof (ddb_waveformat_t))) { + trace ("palsa_setformat ignored\n"); + return 0; } - state = OUTPUT_STATE_STOPPED; +#if 0 + else { + trace ("switching format:\n" + "bps %d -> %d\n" + "is_float %d -> %d\n" + "is_multichannel %d -> %d\n" + "channels %d -> %d\n" + "samplerate %d -> %d\n" + "channelmask %d -> %d\n" + , fmt->bps, plugin.fmt.bps + , fmt->is_float, plugin.fmt.is_float + , fmt->is_multichannel, plugin.fmt.is_multichannel + , fmt->channels, plugin.fmt.channels + , fmt->samplerate, plugin.fmt.samplerate + , fmt->channelmask, plugin.fmt.channelmask + ); + } +#endif LOCK; + int s = state; + state = OUTPUT_STATE_STOPPED; snd_pcm_drop (audio); - int ret = palsa_set_hw_params (rate); + int ret = palsa_set_hw_params (fmt); UNLOCK; if (ret < 0) { - trace ("palsa_change_rate: impossible to set samplerate to %d\n", rate); - return alsa_rate; + trace ("palsa_change_rate: impossible to set requested format\n"); + return -1; } - trace ("chosen samplerate: %d\n", alsa_rate); - return alsa_rate; + trace ("new format %dbit %s %dch %dHz channelmask=%X\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int", plugin.fmt.channels, plugin.fmt.samplerate, plugin.fmt.channelmask); + + switch (s) { + case OUTPUT_STATE_STOPPED: + return palsa_stop (); + case OUTPUT_STATE_PLAYING: + return palsa_play (); + case OUTPUT_STATE_PAUSED: + if (0 != palsa_play ()) { + return -1; + } + if (0 != palsa_pause ()) { + return -1; + } + break; + } + return 0; } int @@ -453,33 +611,6 @@ palsa_unpause (void) { return 0; } -int -palsa_get_rate (void) { - if (!audio) { - palsa_init (); - } - return alsa_rate; -} - -int -palsa_get_bps (void) { - return 16; -} - -int -palsa_get_channels (void) { - return 2; -} - -static int -palsa_get_endianness (void) { -#if WORDS_BIGENDIAN - return 1; -#else - return 0; -#endif -} - static void palsa_thread (void *context) { prctl (PR_SET_NAME, "deadbeef-alsa", 0, 0, 0, 0); @@ -500,10 +631,10 @@ palsa_thread (void *context) { break; } err = 0; - char buf[period_size * 4]; - int bytes_to_write = palsa_callback (buf, period_size * 4); + char buf[period_size * (plugin.fmt.bps>>3) * plugin.fmt.channels]; + int bytes_to_write = palsa_callback (buf, period_size * (plugin.fmt.bps>>3) * plugin.fmt.channels); - if ( bytes_to_write >= 4 ) { + if (bytes_to_write >= (plugin.fmt.bps>>3) * plugin.fmt.channels) { err = snd_pcm_writei (audio, buf, snd_pcm_bytes_to_frames(audio, bytes_to_write)); } else { @@ -541,68 +672,29 @@ palsa_thread (void *context) { frames_to_deliver = snd_pcm_avail_update (audio); } UNLOCK; - usleep (period_size * 1000000 / alsa_rate / 2); + usleep (period_size * 1000000 / plugin.fmt.samplerate / 2); } } static int palsa_callback (char *stream, int len) { - int bytesread = deadbeef->streamer_read (stream, len); - -// FIXME: move volume control to streamer_read for copy optimization -#if 0 - int16_t vol[4]; - vol[0] = volume_get_amp () * 255; // that will be extra 8 bits - // pack 4 times - vol[1] = vol[2] = vol[3] = vol[0]; - - // apply volume with mmx - __asm__ volatile( - " mov %0, %%ecx\n\t" - " shr $4, %%ecx\n\t" - " mov %1, %%eax\n\t" - " movq %2, %mm1\n\t" - "1:\n\t" - " movq [%%eax], %mm0\n\t" - " movq %mm0, %mm2\n\t" - " movq %mm0, %mm3\n\t" - " pmullw %mm1, %mm2\n\t" - " pmulhw %mm1, %mm3\n\t" - " psrlw $8, %mm2\n\t" // discard lowest 8 bits - " psllw $8, %mm3\n\t" // shift left 8 lsbs of hiwords - " por %mm3, %mm2\n\t" // OR them together - " movq %mm3, [%%eax]\n\t" // load back to memory - " add $8, %%eax\n\t" - " dec %%ecx\n\t" - " jnz 1b\n\t" - : - : "r"(len), "r"(stream), "r"(vol) - : "%ecx", "%eax" - ); - -#else - int16_t ivolume = deadbeef->volume_get_amp () * 1000; - for (int i = 0; i < bytesread/2; i++) { - ((int16_t*)stream)[i] = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000); - } -#endif - return bytesread; + return deadbeef->streamer_read (stream, len); } static int palsa_configchanged (DB_event_t *ev, uintptr_t data) { - int alsa_resample = deadbeef->conf_get_int ("alsa.resample", 0); - const char *alsa_soundcard = deadbeef->conf_get_str ("alsa_soundcard", "default"); + deadbeef->conf_lock (); + const char *alsa_soundcard = deadbeef->conf_get_str_fast ("alsa_soundcard", "default"); int buffer = deadbeef->conf_get_int ("alsa.buffer", DEFAULT_BUFFER_SIZE); int period = deadbeef->conf_get_int ("alsa.period", DEFAULT_PERIOD_SIZE); if (audio && - (alsa_resample != conf_alsa_resample - || strcmp (alsa_soundcard, conf_alsa_soundcard) + (strcmp (alsa_soundcard, conf_alsa_soundcard) || buffer != req_buffer_size || period != req_period_size)) { trace ("alsa: config option changed, restarting\n"); deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0); } + deadbeef->conf_unlock (); return 0; } @@ -659,7 +751,6 @@ alsa_load (DB_functions_t *api) { } static const char settings_dlg[] = - "property \"Use ALSA resampling\" checkbox alsa.resample 0;\n" "property \"Release device while stopped\" checkbox alsa.freeonstop 0;\n" "property \"Preferred buffer size\" entry alsa.buffer " DEFAULT_BUFFER_SIZE_STR ";\n" "property \"Preferred period size\" entry alsa.period " DEFAULT_PERIOD_SIZE_STR ";\n" @@ -668,29 +759,40 @@ static const char settings_dlg[] = // define plugin interface static DB_output_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, - .plugin.nostop = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_OUTPUT, + .plugin.id = "alsa", .plugin.name = "ALSA output plugin", .plugin.descr = "plays sound through linux standard alsa library", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = alsa_start, .plugin.stop = alsa_stop, .plugin.configdialog = settings_dlg, .init = palsa_init, .free = palsa_free, - .change_rate = palsa_change_rate, + .setformat = palsa_setformat, .play = palsa_play, .stop = palsa_stop, .pause = palsa_pause, .unpause = palsa_unpause, .state = palsa_get_state, - .samplerate = palsa_get_rate, - .bitspersample = palsa_get_bps, - .channels = palsa_get_channels, - .endianness = palsa_get_endianness, .enum_soundcards = palsa_enum_soundcards, }; diff --git a/plugins/ao/Makefile b/plugins/ao/Makefile new file mode 100644 index 00000000..5e0b3f3f --- /dev/null +++ b/plugins/ao/Makefile @@ -0,0 +1,32 @@ +OUT=ao.so + +CC=gcc + +ZLIB_LIBS=-lz + +CFLAGS?=-O2 -fomit-frame-pointer +CFLAGS+=-Wall -fPIC -DPATH_MAX=1024 -DHAS_PSXCPU=1 -I../.. -I./ -Ieng_ssf -Ieng_qsf -Ieng_dsf + +LDFLAGS+=-module -shared $(ZLIB_LIBS) -lm + +SOURCES=plugin.c main.c corlett.c\ +eng_dsf/eng_dsf.c eng_dsf/dc_hw.c eng_dsf/aica.c eng_dsf/aicadsp.c eng_dsf/arm7.c eng_dsf/arm7i.c\ +eng_ssf/m68kcpu.c eng_ssf/m68kopac.c eng_ssf/m68kopdm.c eng_ssf/m68kopnz.c eng_ssf/m68kops.c \ +eng_ssf/scsp.c eng_ssf/scspdsp.c eng_ssf/sat_hw.c eng_ssf/eng_ssf.c\ +eng_qsf/eng_qsf.c eng_qsf/kabuki.c eng_qsf/qsound.c eng_qsf/z80.c eng_qsf/z80dasm.c\ +eng_psf/eng_psf.c eng_psf/psx.c eng_psf/psx_hw.c eng_psf/peops/spu.c \ +eng_psf/eng_psf2.c eng_psf/peops2/spu2.c eng_psf/peops2/dma2.c eng_psf/peops2/registers2.c\ +eng_psf/eng_spu.c + +OBJECTS=$(SOURCES:.c=.o) + +all: $(SOURCES) $(OUT) + +$(OUT): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -c -o $@ + +clean: + rm $(OBJECTS) $(OUT) diff --git a/plugins/ao/Makefile.am b/plugins/ao/Makefile.am deleted file mode 100644 index 39ccd749..00000000 --- a/plugins/ao/Makefile.am +++ /dev/null @@ -1,27 +0,0 @@ -if HAVE_AO -aodir = $(libdir)/$(PACKAGE) -pkglib_LTLIBRARIES = ao.la -ao_la_SOURCES = plugin.c main.c corlett.c\ -eng_dsf/eng_dsf.c eng_dsf/dc_hw.c eng_dsf/aica.c eng_dsf/aicadsp.c eng_dsf/arm7.c eng_dsf/arm7i.c\ -eng_ssf/m68kcpu.c eng_ssf/m68kopac.c eng_ssf/m68kopdm.c eng_ssf/m68kopnz.c eng_ssf/m68kops.c \ -eng_ssf/scsp.c eng_ssf/scspdsp.c eng_ssf/sat_hw.c eng_ssf/eng_ssf.c\ -eng_qsf/eng_qsf.c eng_qsf/kabuki.c eng_qsf/qsound.c eng_qsf/z80.c eng_qsf/z80dasm.c\ -eng_psf/eng_psf.c eng_psf/psx.c eng_psf/psx_hw.c eng_psf/peops/spu.c \ -eng_psf/eng_psf2.c eng_psf/peops2/spu2.c eng_psf/peops2/dma2.c eng_psf/peops2/registers2.c\ -eng_psf/eng_spu.c\ -ao.h corlett.h cpuintrf.h eng_protos.h mem.h osd_cpu.h\ -eng_dsf/aicadsp.h eng_dsf/aica.h eng_dsf/arm7.h eng_dsf/arm7i.h eng_dsf/arm7thumb.h eng_dsf/dc_hw.h\ -eng_ssf/m68kconf.h eng_ssf/m68kcpu.h eng_ssf/m68k.h eng_ssf/m68kmame.h eng_ssf/m68kops.h eng_ssf/sat_hw.h eng_ssf/scspdsp.h eng_ssf/scsp.h \ -eng_qsf/qsound.h eng_qsf/z80dasm.h eng_qsf/z80.h\ -eng_psf/cpuintrf.h eng_psf/mamemem.h eng_psf/psx.h\ -eng_psf/peops/adsr.h eng_psf/peops/dma.h eng_psf/peops/externals.h eng_psf/peops/gauss_i.h eng_psf/peops/registers.h eng_psf/peops/regs.h eng_psf/peops/spu.h eng_psf/peops/stdafx.h\ -eng_psf/peops2/adsr.h eng_psf/peops2/dma.h eng_psf/peops2/externals.h eng_psf/peops2/gauss_i.h eng_psf/peops2/psemuxa.h eng_psf/peops2/registers.h eng_psf/peops2/regs.h eng_psf/peops2/reverb.h eng_psf/peops2/spu.h eng_psf/peops2/stdafx.h - -ao_la_LDFLAGS = -module -fPIC - -EXTRA_DIST=eng_psf/peops/reverb.c eng_psf/peops/adsr.c eng_psf/peops/registers.c eng_psf/peops/dma.c eng_psf/peops2/spu2.c eng_psf/peops2/reverb2.c eng_psf/peops2/adsr2.c eng_dsf/arm7memil.c eng_dsf/aicalfo.c eng_ssf/scsplfo.c - -ao_la_LIBADD = $(LDADD) -AM_CFLAGS = $(CFLAGS) -Wall -DPATH_MAX=1024 -DHAS_PSXCPU=1 -I.. -Ieng_ssf -Ieng_qsf -Ieng_dsf -lm $(ZLIB_LIBS) -endif - diff --git a/plugins/ao/eng_ssf/m68kcpu.c b/plugins/ao/eng_ssf/m68kcpu.c index 9624ea5c..f9b442bc 100644 --- a/plugins/ao/eng_ssf/m68kcpu.c +++ b/plugins/ao/eng_ssf/m68kcpu.c @@ -37,8 +37,6 @@ static const char* copyright_notice = #include "m68kops.h" #include "m68kcpu.h" -#pragma GCC optimize("O0") - /* ======================================================================== */ /* ================================= DATA ================================= */ /* ======================================================================== */ diff --git a/plugins/ao/plugin.c b/plugins/ao/plugin.c index 2e29c4e0..02f70ef2 100644 --- a/plugins/ao/plugin.c +++ b/plugins/ao/plugin.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -48,7 +48,7 @@ typedef struct { } aoplug_info_t; static DB_fileinfo_t * -aoplug_open (void) { +aoplug_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (aoplug_info_t)); aoplug_info_t *info = (aoplug_info_t *)_info; memset (info, 0, sizeof (aoplug_info_t)); @@ -59,16 +59,17 @@ static int aoplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { aoplug_info_t *info = (aoplug_info_t *)_info; - _info->bps = 16; - _info->channels = 2; - _info->samplerate = 44100; + _info->fmt.bps = 16; + _info->fmt.channels = 2; + _info->fmt.samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; _info->plugin = &plugin; info->duration = deadbeef->pl_get_item_duration (it); - DB_FILE *file = deadbeef->fopen (it->fname); + DB_FILE *file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!file) { - trace ("psf: failed to fopen %s\n", it->fname); + trace ("psf: failed to fopen %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } @@ -81,7 +82,7 @@ aoplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { } if (deadbeef->fread(info->filebuffer, 1, info->filesize, file) != info->filesize) { - fprintf(stderr, "psf: file read error: %s\n", it->fname); + fprintf(stderr, "psf: file read error: %s\n", deadbeef->pl_find_meta (it, ":URI")); deadbeef->fclose (file); return -1; } @@ -93,7 +94,7 @@ aoplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { return -1; } - info->decoder = ao_start (info->type, it->fname, (uint8 *)info->filebuffer, info->filesize); + info->decoder = ao_start (info->type, deadbeef->pl_find_meta (it, ":URI"), (uint8 *)info->filebuffer, info->filesize); if (!info->decoder) { fprintf (stderr, "psf: ao_start failed\n"); return -1; @@ -116,11 +117,11 @@ aoplug_free (DB_fileinfo_t *_info) { } static int -aoplug_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +aoplug_read (DB_fileinfo_t *_info, char *bytes, int size) { aoplug_info_t *info = (aoplug_info_t *)_info; // printf ("aoplug_read_int16 %d samples, curr %d, end %d\n", size/4, info->currentsample, (int)(info->duration * _info->samplerate)); - if (info->currentsample >= info->duration * _info->samplerate) { + if (info->currentsample >= info->duration * _info->fmt.samplerate) { return 0; } @@ -152,7 +153,8 @@ aoplug_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { info->remaining = 735; } } - info->currentsample += (initsize-size) / (_info->channels * _info->bps/8); + info->currentsample += (initsize-size) / (_info->fmt.channels * _info->fmt.bps/8); + _info->readpos = (float)info->currentsample / _info->fmt.samplerate; return initsize-size; } @@ -168,13 +170,13 @@ aoplug_seek_sample (DB_fileinfo_t *_info, int sample) { info->skipsamples = sample; } info->currentsample = sample; - _info->readpos = (float)sample / _info->samplerate; + _info->readpos = (float)sample / _info->fmt.samplerate; return 0; } static int aoplug_seek (DB_fileinfo_t *_info, float time) { - return aoplug_seek_sample (_info, time * _info->samplerate); + return aoplug_seek_sample (_info, time * _info->fmt.samplerate); } static void @@ -250,9 +252,7 @@ aoplug_insert (DB_playItem_t *after, const char *fname) { free (buffer); - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); const char *ext = fname + strlen (fname); while (*ext != '.' && ext > fname) { ext--; @@ -260,38 +260,44 @@ aoplug_insert (DB_playItem_t *after, const char *fname) { if (*ext == '.') { ext++; if (!strcasecmp (ext, "psf") || !strcasecmp (ext, "minipsf")) { - it->filetype = filetypes[0]; + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[0]); } else if (!strcasecmp (ext, "psf2") || !strcasecmp (ext, "minipsf2")) { - it->filetype = filetypes[1]; + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[1]); } else if (!strcasecmp (ext, "spu")) { - it->filetype = filetypes[2]; + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[2]); } else if (!strcasecmp (ext, "ssf") || !strcasecmp (ext, "minissf")) { - it->filetype = filetypes[3]; - } - else if (!strcasecmp (ext, "dsf") || !strcasecmp (ext, "minidsf")) { - it->filetype = filetypes[5]; + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[3]); } else if (!strcasecmp (ext, "qsf") || !strcasecmp (ext, "miniqsf")) { - it->filetype = filetypes[4]; + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[4]); + } + else if (!strcasecmp (ext, "dsf") || !strcasecmp (ext, "minidsf")) { + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[5]); } } else { - it->filetype = filetypes[0]; + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[0]); } float duration = 120; + float fade = 0; if (have_info) { int i; for (i = 1; i < 9; i++) { if (!strncasecmp (info.title[i], "Length: ", 8)) { - int min, sec; - if (sscanf (info.info[i], "%d:%d", &min, &sec) == 2) { + printf ("len: %s\n", info.info[i]); + int min; + float sec; + if (sscanf (info.info[i], "%d:%f", &min, &sec) == 2) { duration = min * 60 + sec; } + else if (sscanf (info.info[i], "%f", &sec) == 1) { + duration = sec; + } aoplug_add_meta (it, NULL, info.info[i], info.title[i]); } else if (!strncasecmp (info.title[i], "Name: ", 6) || !strncasecmp (info.title[i], "Song: ", 6)) { @@ -312,12 +318,16 @@ aoplug_insert (DB_playItem_t *after, const char *fname) { else if (!strncasecmp (info.title[i], "Ripper: ", 8)) { aoplug_add_meta (it, "vendor", info.info[i], info.title[i]); } + else if (!strncasecmp (info.title[i], "Fade: ", 6)) { + fade = atof (info.info[i]); + aoplug_add_meta (it, NULL, info.info[i], info.title[i]); + } else { aoplug_add_meta (it, NULL, info.info[i], info.title[i]); } } } - deadbeef->pl_set_item_duration (it, duration); + deadbeef->pl_set_item_duration (it, duration+fade); deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); @@ -336,21 +346,40 @@ aoplug_stop (void) { static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "psf", .plugin.name = "Audio Overload plugin", .plugin.descr = "psf, psf2, spu, ssf, minidsf player based on Audio Overload library", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified aosdk-1.4.8 - library for playing .PSF (Sony PlayStation), .SPU (Sony PlayStation), .PSF2 (Sony PlayStation 2), .SSF (Sega Saturn), .DSF (Sega Dreamcast), and .QSF (Capcom QSound) audio file formats,\n" + "http://rbelmont.mameworld.info/?page_id=221\n" + "Copyright © 2007-2009 R. Belmont and Richard Bannister.\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = aoplug_start, .plugin.stop = aoplug_stop, .open = aoplug_open, .init = aoplug_init, .free = aoplug_free, - .read_int16 = aoplug_read_int16, + .read = aoplug_read, .seek = aoplug_seek, .seek_sample = aoplug_seek_sample, .insert = aoplug_insert, diff --git a/plugins/artwork/Makefile.am b/plugins/artwork/Makefile.am index b60c86cb..8f9ea166 100644 --- a/plugins/artwork/Makefile.am +++ b/plugins/artwork/Makefile.am @@ -5,6 +5,6 @@ artwork_la_SOURCES = artwork.c artwork.h albumartorg.c albumartorg.h lastfm.c la artwork_la_LDFLAGS = -module -artwork_la_LIBADD = $(LDADD) $(ARTWORK_DEPS_LIBS) -AM_CFLAGS = -std=c99 $(ARTWORK_DEPS_CFLAGS) +artwork_la_LIBADD = $(LDADD) $(ARTWORK_DEPS_LIBS) $(IMLIB2_DEPS_LIBS) +AM_CFLAGS = -std=c99 $(ARTWORK_DEPS_CFLAGS) $(IMLIB2_DEPS_CFLAGS) endif diff --git a/plugins/artwork/albumartorg.c b/plugins/artwork/albumartorg.c index 71b640de..ada7179f 100644 --- a/plugins/artwork/albumartorg.c +++ b/plugins/artwork/albumartorg.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/artwork/albumartorg.h b/plugins/artwork/albumartorg.h index 4d1d494a..49231b04 100644 --- a/plugins/artwork/albumartorg.h +++ b/plugins/artwork/albumartorg.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index fbbe71d9..b29db8f2 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -6,6 +6,8 @@ #include <dirent.h> #include <unistd.h> #include <fnmatch.h> +#include <inttypes.h> +#include <Imlib2.h> #include "../../deadbeef.h" #include "artwork.h" #include "lastfm.h" @@ -16,8 +18,8 @@ //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(...) -#define DEFAULT_COVER_PATH (PREFIX "/share/deadbeef/pixmaps/noartwork.jpg") -#define DEFAULT_FILEMASK "*cover*.jpg;*front*.jpg" +static char default_cover[PATH_MAX]; +#define DEFAULT_FILEMASK "*cover*.jpg;*front*.jpg;*folder*.jpg" static DB_artwork_plugin_t plugin; DB_functions_t *deadbeef; @@ -28,6 +30,7 @@ typedef struct cover_query_s { char *fname; char *artist; char *album; + int size; artwork_callback callback; void *user_data; struct cover_query_s *next; @@ -36,44 +39,50 @@ typedef struct cover_query_s { static cover_query_t *queue; static cover_query_t *queue_tail; static uintptr_t mutex; +static uintptr_t imlib_mutex; static uintptr_t cond; static volatile int terminate; static volatile int clear_queue; static intptr_t tid; -int artwork_enable_embedded; -int artwork_enable_local; -int artwork_enable_lfm; -int artwork_enable_aao; -int artwork_reset_time; -char artwork_filemask[200]; +static int artwork_enable_embedded; +static int artwork_enable_local; +static int artwork_enable_lfm; +static int artwork_enable_aao; +static time_t artwork_reset_time; +static char artwork_filemask[200]; -void -make_cache_dir_path (char *path, int size, const char *album, const char *artist) { - int sz = snprintf (path, size, "%s/artcache/", deadbeef->get_config_dir ()); - size -= sz; +static const char *get_default_cover (void) { + return default_cover; +} + +int +make_cache_dir_path (char *path, int size, const char *artist, int img_size) { + const char *cache = getenv ("XDG_CACHE_HOME"); + + int sz; + + if (img_size == -1) { + sz = snprintf (path, size, cache ? "%s/deadbeef/covers/" : "%s/.cache/deadbeef/covers/", cache ? cache : getenv ("HOME")); + } + else { + sz = snprintf (path, size, cache ? "%s/deadbeef/covers-%d/" : "%s/.cache/deadbeef/covers-%d/", cache ? cache : getenv ("HOME"), img_size); + } path += sz; - sz = snprintf (path, size, "%s", artist); + sz += snprintf (path, size-sz, "%s", artist); for (char *p = path; *p; p++) { if (*p == '/') { *p = '_'; } } + return sz; } void -make_cache_path (char *path, int size, const char *album, const char *artist) { - int sz = snprintf (path, size, "%s/artcache/", deadbeef->get_config_dir ()); - size -= sz; - path += sz; - - sz = snprintf (path, size, "%s", artist); - for (char *p = path; *p; p++) { - if (*p == '/') { - *p = '_'; - } - } +make_cache_path (char *path, int size, const char *album, const char *artist, int img_size) { + char *p = path; + int sz = make_cache_dir_path (path, size, artist, img_size); size -= sz; path += sz; sz = snprintf (path, size, "/%s.jpg", album); @@ -85,7 +94,7 @@ make_cache_path (char *path, int size, const char *album, const char *artist) { } void -queue_add (const char *fname, const char *artist, const char *album, artwork_callback callback, void *user_data) { +queue_add (const char *fname, const char *artist, const char *album, int img_size, artwork_callback callback, void *user_data) { if (!artist) { artist = ""; } @@ -106,6 +115,7 @@ queue_add (const char *fname, const char *artist, const char *album, artwork_cal q->fname = strdup (fname); q->artist = strdup (artist); q->album = strdup (album); + q->size = img_size; q->callback = callback; q->user_data = user_data; if (queue_tail) { @@ -166,8 +176,64 @@ check_dir (const char *dir, mode_t mode) #define BUFFER_SIZE 4096 static int -copy_file (const char *in, const char *out) { +copy_file (const char *in, const char *out, int img_size) { trace ("copying %s to %s\n", in, out); + + if (img_size != -1) { + deadbeef->mutex_lock (imlib_mutex); + // need to scale, use imlib2 + Imlib_Image img = imlib_load_image_immediately (in); + if (!img) { + trace ("file %s not found, or imlib2 can't load it\n", in); + deadbeef->mutex_unlock (imlib_mutex); + return -1; + } + imlib_context_set_image(img); + int w = imlib_image_get_width (); + int h = imlib_image_get_height (); + int sw, sh; + if (deadbeef->conf_get_int ("artwork.scale_towards_longer", 1)) { + if (w > h) { + sh = img_size; + sw = img_size * w / h; + } + else { + sw = img_size; + sh = img_size * h / w; + } + } + else { + if (w < h) { + sh = img_size; + sw = img_size * w / h; + } + else { + sw = img_size; + sh = img_size * h / w; + } + } + Imlib_Image scaled = imlib_create_image (sw, sh); + imlib_context_set_image (scaled); + imlib_blend_image_onto_image (img, 1, 0, 0, w, h, 0, 0, sw, sh); + Imlib_Load_Error err = 0; + imlib_image_set_format ("jpg"); + imlib_save_image_with_error_return (out, &err); + if (err != 0) { + trace ("imlib save %s returned %d\n", out, err); + imlib_free_image (); + imlib_context_set_image(img); + imlib_free_image (); + deadbeef->mutex_unlock (imlib_mutex); + return -1; + } + imlib_free_image (); + imlib_context_set_image(img); + imlib_free_image (); + deadbeef->mutex_unlock (imlib_mutex); + + return 0; + } + FILE *fin = fopen (in, "rb"); if (!fin) { trace ("artwork: failed to open file %s for reading\n", in); @@ -287,19 +353,31 @@ fetcher_thread (void *none) deadbeef->mutex_unlock (mutex); while (!terminate && queue && !clear_queue) { cover_query_t *param = queue; - char path [1024]; + char path [PATH_MAX]; struct dirent **files; int files_count; - make_cache_dir_path (path, sizeof (path), param->album, param->artist); + make_cache_dir_path (path, sizeof (path), param->artist, -1); trace ("cache folder: %s\n", path); if (!check_dir (path, 0755)) { queue_pop (); trace ("failed to create folder for %s %s\n", param->album, param->artist); continue; } + if (param->size != -1) { + make_cache_dir_path (path, sizeof (path), param->artist, param->size); + trace ("cache folder: %s\n", path); + if (!check_dir (path, 0755)) { + queue_pop (); + trace ("failed to create folder for %s %s\n", param->album, param->artist); + continue; + } + } trace ("fetching cover for %s %s\n", param->album, param->artist); + char cache_path[1024]; + make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); + int got_pic = 0; // try to load embedded from id3v2 if (deadbeef->is_local_file (param->fname)) { @@ -309,7 +387,6 @@ fetcher_thread (void *none) memset (&tag, 0, sizeof (tag)); DB_FILE *fp = deadbeef->fopen (param->fname); current_file = fp; - int got_id3v2_pic = 0; if (fp) { int res = deadbeef->junk_id3v2_read_full (NULL, &tag, fp); if (!res) { @@ -349,8 +426,6 @@ fetcher_thread (void *none) int sz = f->size - (data - f->data); char tmp_path[1024]; - char cache_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist); trace ("will write id3v2 APIC into %s\n", cache_path); snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); FILE *out = fopen (tmp_path, "w+b"); @@ -372,107 +447,90 @@ fetcher_thread (void *none) break; } unlink (tmp_path); - got_id3v2_pic = 1; + got_pic = 1; break; } } } - if (got_id3v2_pic) { - if (param->callback) { - param->callback (param->fname, param->artist, param->album, param->user_data); - } - queue_pop (); - continue; - } deadbeef->junk_id3v2_free (&tag); current_file = NULL; deadbeef->fclose (fp); } - } - } - // try to load embedded from apev2 - if (deadbeef->is_local_file (param->fname)) { - if (artwork_enable_embedded) { - trace ("trying to load artwork from apev2 tag for %s\n", param->fname); - DB_apev2_tag_t tag; - memset (&tag, 0, sizeof (tag)); - DB_FILE *fp = deadbeef->fopen (param->fname); - current_file = fp; - int got_apev2_pic = 0; - if (fp) { - int res = deadbeef->junk_apev2_read_full (NULL, &tag, fp); - if (!res) { - for (DB_apev2_frame_t *f = tag.frames; f; f = f->next) { - if (!strcasecmp (f->key, "cover art (front)")) { - uint8_t *name = f->data, *ext = f->data, *data = f->data; - uint8_t *end = f->data + f->size; - while (data < end && *data) - data++; - if (data == end) { - trace ("artwork: apev2 cover art frame has no name\n"); - continue; - } - int sz = end - ++data; - if (sz < 20) { - trace ("artwork: apev2 cover art frame is too small\n"); - continue; - } - ext = strrchr (name, '.'); - if (!ext || !*++ext) { - trace ("artwork: apev2 cover art name has no extension\n"); - continue; - } - if (strcasecmp (ext, "jpeg") && strcasecmp (ext, "jpg") && strcasecmp (ext, "png")) { - trace ("artwork: unsupported file type: %s\n", ext); - continue; - } - trace ("found apev2 cover art of %d bytes (%s)\n", sz, ext); - char tmp_path[1024]; - char cache_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist); - trace ("will write apev2 cover art into %s\n", cache_path); - snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); - FILE *out = fopen (tmp_path, "w+b"); - if (!out) { - trace ("artwork: failed to open %s for writing\n", tmp_path); - break; - } - if (fwrite (data, 1, sz, out) != sz) { - trace ("artwork: failed to write apev2 picture into %s\n", tmp_path); + // try to load embedded from apev2 + { + trace ("trying to load artwork from apev2 tag for %s\n", param->fname); + DB_apev2_tag_t tag; + memset (&tag, 0, sizeof (tag)); + DB_FILE *fp = deadbeef->fopen (param->fname); + current_file = fp; + if (fp) { + int res = deadbeef->junk_apev2_read_full (NULL, &tag, fp); + if (!res) { + for (DB_apev2_frame_t *f = tag.frames; f; f = f->next) { + if (!strcasecmp (f->key, "cover art (front)")) { + uint8_t *name = f->data, *ext = f->data, *data = f->data; + uint8_t *end = f->data + f->size; + while (data < end && *data) + data++; + if (data == end) { + trace ("artwork: apev2 cover art frame has no name\n"); + continue; + } + int sz = end - ++data; + if (sz < 20) { + trace ("artwork: apev2 cover art frame is too small\n"); + continue; + } + ext = strrchr (name, '.'); + if (!ext || !*++ext) { + trace ("artwork: apev2 cover art name has no extension\n"); + continue; + } + if (strcasecmp (ext, "jpeg") && strcasecmp (ext, "jpg") && strcasecmp (ext, "png")) { + trace ("artwork: unsupported file type: %s\n", ext); + continue; + } + trace ("found apev2 cover art of %d bytes (%s)\n", sz, ext); + char tmp_path[1024]; + char cache_path[1024]; + make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); + trace ("will write apev2 cover art into %s\n", cache_path); + snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); + FILE *out = fopen (tmp_path, "w+b"); + if (!out) { + trace ("artwork: failed to open %s for writing\n", tmp_path); + break; + } + if (fwrite (data, 1, sz, out) != sz) { + trace ("artwork: failed to write apev2 picture into %s\n", tmp_path); + fclose (out); + unlink (tmp_path); + break; + } fclose (out); + int err = rename (tmp_path, cache_path); + if (err != 0) { + trace ("Failed not move %s to %s: %s\n", tmp_path, cache_path, strerror (err)); + unlink (tmp_path); + break; + } unlink (tmp_path); + got_pic = 1; break; } - fclose (out); - int err = rename (tmp_path, cache_path); - if (err != 0) { - trace ("Failed not move %s to %s: %s\n", tmp_path, cache_path, strerror (err)); - unlink (tmp_path); - break; - } - unlink (tmp_path); - got_apev2_pic = 1; - break; } } - } - if (got_apev2_pic) { - if (param->callback) { - param->callback (param->fname, param->artist, param->album, param->user_data); - } - queue_pop (); - continue; + deadbeef->junk_apev2_free (&tag); + current_file = NULL; + deadbeef->fclose (fp); } - deadbeef->junk_apev2_free (&tag); - current_file = NULL; - deadbeef->fclose (fp); } } - if (artwork_enable_local) { + if (!got_pic && artwork_enable_local) { /* Searching in track directory */ strncpy (path, param->fname, sizeof (path)); char *slash = strrchr (path, '/'); @@ -492,48 +550,50 @@ fetcher_thread (void *none) strcat (path, files[0]->d_name); char cache_path[1024]; char tmp_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist); + make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); - copy_file (path, tmp_path); + copy_file (path, tmp_path, -1); int err = rename (tmp_path, cache_path); if (err != 0) { - trace ("Failed not move %s to %s: %s\n", tmp_path, cache_path, strerror (err)); + trace ("Failed to move %s to %s: %s\n", tmp_path, cache_path, strerror (err)); unlink (tmp_path); } int i; for (i = 0; i < files_count; i++) { free (files [i]); } - if (param->callback) { - param->callback (param->fname, param->artist, param->album, param->user_data); - } - queue_pop (); - continue; + got_pic = 1; } } } } - make_cache_path (path, sizeof (path), param->album, param->artist); - - if (artwork_enable_lfm && !fetch_from_lastfm (param->artist, param->album, path)) { - trace ("art found on last.fm for %s %s\n", param->album, param->artist); - } - else if (artwork_enable_aao && !fetch_from_albumart_org (param->artist, param->album, path)) { - trace ("art found on albumart.org for %s %s\n", param->album, param->artist); - } - else { - trace ("art not found for %s %s\n", param->album, param->artist); -// if (param->callback) { -// param->callback (DEFAULT_COVER_PATH, param->artist, param->album, param->user_data); -// } - queue_pop (); - continue; + if (!got_pic) { + if (artwork_enable_lfm && !fetch_from_lastfm (param->artist, param->album, cache_path)) { + got_pic = 1; + } + else if (artwork_enable_aao && !fetch_from_albumart_org (param->artist, param->album, cache_path)) { + got_pic = 1; + } } - trace ("downloaded art for %s %s\n", param->album, param->artist); - if (param->callback) { - param->callback (param->fname, param->artist, param->album, param->user_data); + if (got_pic) { + trace ("downloaded art for %s %s\n", param->album, param->artist); + if (param->size != -1) { + make_cache_dir_path (path, sizeof (path), param->artist, param->size); + trace ("cache folder: %s\n", path); + if (!check_dir (path, 0755)) { + trace ("failed to create folder %s\n", path); + queue_pop (); + continue; + } + char scaled_path[1024]; + make_cache_path (scaled_path, sizeof (scaled_path), param->album, param->artist, param->size); + copy_file (cache_path, scaled_path, param->size); + } + if (param->callback) { + param->callback (param->fname, param->artist, param->album, param->user_data); + } } queue_pop (); } @@ -552,8 +612,27 @@ fetcher_thread (void *none) } } +static char * +find_image (const char *path) { + struct stat stat_buf; + if (0 == stat (path, &stat_buf)) { + int cache_period = deadbeef->conf_get_int ("artwork.cache.period", 48); + time_t tm = time (NULL); + // invalidate cache every 2 days + if ((cache_period > 0 && (tm - stat_buf.st_mtime > cache_period * 60 * 60)) + || artwork_reset_time > stat_buf.st_mtime) { + trace ("reloading cached file %s\n", path); + unlink (path); + return NULL; + } + + return strdup (path); + } + return NULL; +} + char* -get_album_art (const char *fname, const char *artist, const char *album, artwork_callback callback, void *user_data) +get_album_art (const char *fname, const char *artist, const char *album, int size, artwork_callback callback, void *user_data) { // trace ("get_album_art: %s (%s - %s)\n", fname, artist, album); char path [1024]; @@ -568,33 +647,42 @@ get_album_art (const char *fname, const char *artist, const char *album, artwork if (!*artist || !*album) { //give up - return strdup (DEFAULT_COVER_PATH); + return size == -1 ? strdup (get_default_cover ()) : NULL; } if (!deadbeef->is_local_file (fname)) { - return strdup (DEFAULT_COVER_PATH); - } - - make_cache_path (path, sizeof (path), album, artist); - struct stat stat_buf; - if (0 == stat (path, &stat_buf)) { - int cache_period = deadbeef->conf_get_int ("artwork.cache.period", 48); - time_t tm = time (NULL); - // invalidate cache every 2 days - if ((cache_period > 0 && (tm - stat_buf.st_mtime > cache_period * 60 * 60)) - || artwork_reset_time > stat_buf.st_mtime) { - trace ("reloading cached file %s\n", path); - unlink (path); - queue_add (fname, artist, album, callback, user_data); - return strdup (DEFAULT_COVER_PATH); + return size == -1 ? strdup (get_default_cover ()) : NULL; + } + + make_cache_path (path, sizeof (path), album, artist, size); + char *p = find_image (path); + if (p) { + return p; + } + + if (size != -1) { + // check if we have unscaled image + char unscaled_path[1024]; + make_cache_path (unscaled_path, sizeof (unscaled_path), album, artist, -1); + p = find_image (unscaled_path); + if (p) { + free (p); + char dir[1024]; + make_cache_dir_path (dir, sizeof (dir), artist, size); + if (!check_dir (dir, 0755)) { + trace ("failed to create folder for %s\n", dir); + } + else { + int res = copy_file (unscaled_path, path, size); + if (!res) { + return strdup (path); + } + } } - - trace ("found %s in cache\n", path); - return strdup (path); } - queue_add (fname, artist, album, callback, user_data); - return strdup (DEFAULT_COVER_PATH); + queue_add (fname, artist, album, size, callback, user_data); + return size == -1 ? strdup (get_default_cover ()) : NULL; } DB_plugin_t * @@ -639,23 +727,25 @@ artwork_on_configchanged (DB_event_t *ev, uintptr_t data) { int new_artwork_enable_local = deadbeef->conf_get_int ("artwork.enable_localfolder", 1); int new_artwork_enable_lfm = deadbeef->conf_get_int ("artwork.enable_lastfm", 0); int new_artwork_enable_aao = deadbeef->conf_get_int ("artwork.enable_albumartorg", 0); + char new_artwork_filemask[200]; - strncpy (new_artwork_filemask, deadbeef->conf_get_str ("artwork.filemask", DEFAULT_FILEMASK), sizeof (new_artwork_filemask)); - new_artwork_filemask[sizeof(new_artwork_filemask)-1] = 0; + deadbeef->conf_get_str ("artwork.filemask", DEFAULT_FILEMASK, new_artwork_filemask, sizeof (new_artwork_filemask)); if (new_artwork_enable_embedded != artwork_enable_embedded || new_artwork_enable_local != artwork_enable_local || new_artwork_enable_lfm != artwork_enable_lfm || new_artwork_enable_aao != artwork_enable_aao || strcmp (new_artwork_filemask, artwork_filemask)) { + trace ("artwork config changed, invalidating cache...\n"); artwork_enable_embedded = new_artwork_enable_embedded; artwork_enable_local = new_artwork_enable_local; artwork_enable_lfm = new_artwork_enable_lfm; artwork_enable_aao = new_artwork_enable_aao; artwork_reset_time = time (NULL); strcpy (artwork_filemask, new_artwork_filemask); - deadbeef->conf_set_int ("artwork.cache_reset_time", artwork_reset_time); - deadbeef->sendmessage (M_PLAYLISTREFRESH, 0, 0, 0); + deadbeef->conf_set_int64 ("artwork.cache_reset_time", artwork_reset_time); + artwork_reset (0); + deadbeef->sendmessage (M_PLAYLIST_REFRESH, 0, 0, 0); } return 0; @@ -664,20 +754,33 @@ artwork_on_configchanged (DB_event_t *ev, uintptr_t data) { static int artwork_plugin_start (void) { + deadbeef->conf_lock (); + + const char *def_art = deadbeef->conf_get_str_fast ("gtkui.nocover_pixmap", NULL); + if (!def_art) { + snprintf (default_cover, sizeof (default_cover), "%s/noartwork.jpg", deadbeef->get_pixmap_dir ()); + } + else { + strcpy (default_cover, def_art); + } terminate = 0; artwork_enable_embedded = deadbeef->conf_get_int ("artwork.enable_embedded", 1); artwork_enable_local = deadbeef->conf_get_int ("artwork.enable_localfolder", 1); artwork_enable_lfm = deadbeef->conf_get_int ("artwork.enable_lastfm", 0); artwork_enable_aao = deadbeef->conf_get_int ("artwork.enable_albumartorg", 0); - artwork_reset_time = deadbeef->conf_get_int ("artwork.cache_reset_time", 0); + artwork_reset_time = deadbeef->conf_get_int64 ("artwork.cache_reset_time", 0); + + deadbeef->conf_get_str ("artwork.filemask", DEFAULT_FILEMASK, artwork_filemask, sizeof (artwork_filemask)); + + deadbeef->conf_unlock (); - strncpy (artwork_filemask, deadbeef->conf_get_str ("artwork.filemask", DEFAULT_FILEMASK), sizeof (artwork_filemask)); artwork_filemask[sizeof(artwork_filemask)-1] = 0; deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (artwork_on_configchanged), 0); mutex = deadbeef->mutex_create_nonrecursive (); + imlib_mutex = deadbeef->mutex_create_nonrecursive (); cond = deadbeef->cond_create (); tid = deadbeef->thread_start_low_priority (fetcher_thread, NULL); @@ -704,6 +807,10 @@ artwork_plugin_stop (void) deadbeef->mutex_free (mutex); mutex = 0; } + if (imlib_mutex) { + deadbeef->mutex_free (imlib_mutex); + imlib_mutex = 0; + } if (cond) { deadbeef->cond_free (cond); cond = 0; @@ -719,21 +826,42 @@ static const char settings_dlg[] = "property \"Local cover file mask\" entry artwork.filemask \"" DEFAULT_FILEMASK "\";\n" "property \"Fetch from last.fm\" checkbox artwork.enable_lastfm 0;\n" "property \"Fetch from albumart.org\" checkbox artwork.enable_albumartorg 0;\n" + "property \"Scale artwork towards longer side\" checkbox artwork.scale_towards_longer 1;\n" ; + // define plugin interface static DB_artwork_plugin_t plugin = { .plugin.plugin.api_vmajor = DB_API_VERSION_MAJOR, .plugin.plugin.api_vminor = DB_API_VERSION_MINOR, + .plugin.plugin.version_major = 1, + .plugin.plugin.version_minor = 0, .plugin.plugin.type = DB_PLUGIN_MISC, - .plugin.plugin.id = "cover_loader", + .plugin.plugin.id = "artwork", .plugin.plugin.name = "Album Artwork", .plugin.plugin.descr = "Loads album artwork either from local directories or from internet", - .plugin.plugin.author = "Viktor Semykin, Alexey Yakovenko", - .plugin.plugin.email = "thesame.ml@gmail.com, waker@users.sourceforge.net", + .plugin.plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.plugin.website = "http://deadbeef.sf.net", .plugin.plugin.start = artwork_plugin_start, .plugin.plugin.stop = artwork_plugin_stop, .plugin.plugin.configdialog = settings_dlg, .get_album_art = get_album_art, .reset = artwork_reset, + .get_default_cover = get_default_cover, }; diff --git a/plugins/artwork/artwork.h b/plugins/artwork/artwork.h index ab0fead6..130bf3fe 100644 --- a/plugins/artwork/artwork.h +++ b/plugins/artwork/artwork.h @@ -10,10 +10,11 @@ typedef void (*artwork_callback) (const char *fname, const char *artist, const c typedef struct { DB_misc_t plugin; // returns filename of cached image, or NULL - char* (*get_album_art) (const char *fname, const char *artist, const char *album, artwork_callback callback, void *user_data); + char* (*get_album_art) (const char *fname, const char *artist, const char *album, int size, artwork_callback callback, void *user_data); // this has to be called to clear queue on exit, before caller terminates // `fast=1' means "don't wait, just flush queue" void (*reset) (int fast); + const char *(*get_default_cover) (void); } DB_artwork_plugin_t; #endif /*__ARTWORK_H*/ diff --git a/plugins/artwork/escape.h b/plugins/artwork/escape.h index d87cf0a1..75d24091 100644 --- a/plugins/artwork/escape.h +++ b/plugins/artwork/escape.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/artwork/lastfm.c b/plugins/artwork/lastfm.c index ce95e9d4..2e78fd87 100644 --- a/plugins/artwork/lastfm.c +++ b/plugins/artwork/lastfm.c @@ -36,7 +36,7 @@ fetch_from_lastfm (const char *artist, const char *album, const char *dest) char buffer[1000]; memset (buffer, 0, sizeof (buffer)); char *img = NULL; - int size = deadbeef->fread (buffer, 1, sizeof (buffer), fp); + int size = deadbeef->fread (buffer, 1, sizeof (buffer)-1, fp); if (size > 0) { img = strstr (buffer, searchstr); } diff --git a/plugins/artwork/lastfm.h b/plugins/artwork/lastfm.h index 09ec817c..cef71919 100644 --- a/plugins/artwork/lastfm.h +++ b/plugins/artwork/lastfm.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/cdda/cdda.c b/plugins/cdda/cdda.c index 2ed54b51..b49c0cea 100644 --- a/plugins/cdda/cdda.c +++ b/plugins/cdda/cdda.c @@ -57,8 +57,6 @@ typedef struct { unsigned int current_sample; } cdda_info_t; -static uintptr_t mutex; -static intptr_t cddb_tid; struct cddb_thread_params { DB_playItem_t *items[100]; @@ -71,7 +69,7 @@ min (int a, int b) { } static DB_fileinfo_t * -cda_open (void) { +cda_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (cdda_info_t)); memset (_info, 0, sizeof (cdda_info_t)); return _info; @@ -81,18 +79,18 @@ static int cda_init (DB_fileinfo_t *_info, DB_playItem_t *it) { cdda_info_t *info = (cdda_info_t *)_info; - trace ("cdda: init %s\n", it->fname); + trace ("cdda: init %s\n", deadbeef->pl_find_meta (it, ":URI")); - size_t l = strlen (it->fname); + size_t l = strlen (deadbeef->pl_find_meta (it, ":URI")); char location[l+1]; - memcpy (location, it->fname, l+1); + memcpy (location, deadbeef->pl_find_meta (it, ":URI"), l+1); char *nr = strchr (location, '#'); if (nr) { *nr = 0; nr++; } else { - trace ("cdda: bad name: %s\n", it->fname); + trace ("cdda: bad name: %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } int track_nr = atoi (nr); @@ -115,9 +113,10 @@ cda_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("cdio nchannels: %d\n", channels); _info->plugin = &plugin; - _info->bps = 16, - _info->channels = 2, - _info->samplerate = 44100, + _info->fmt.bps = 16; + _info->fmt.channels = 2; + _info->fmt.samplerate = 44100; + _info->fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT; _info->readpos = 0; info->first_sector = cdio_get_track_lsn (info->cdio, track_nr); @@ -129,7 +128,7 @@ cda_init (DB_fileinfo_t *_info, DB_playItem_t *it) { } int -cda_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +cda_read (DB_fileinfo_t *_info, char *bytes, int size) { cdda_info_t *info = (cdda_info_t *)_info; int extrasize = 0; @@ -181,7 +180,7 @@ cda_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { retsize += extrasize; // trace ("requested: %d; tail_len: %d; size: %d; sectors_to_read: %d; return: %d\n", initsize, tail_len, size, sectors_to_read, retsize); info->current_sample += retsize / SAMPLESIZE; - _info->readpos = (float)info->current_sample / _info->samplerate; + _info->readpos = (float)info->current_sample / _info->fmt.samplerate; return retsize; } @@ -211,14 +210,14 @@ cda_seek_sample (DB_fileinfo_t *_info, int sample) memcpy (info->tail, buf + offset, SECTORSIZE - offset); info->current_sector = sector; info->current_sample = sample; - _info->readpos = (float)info->current_sample / _info->samplerate; + _info->readpos = (float)info->current_sample / _info->fmt.samplerate; return 0; } static int cda_seek (DB_fileinfo_t *_info, float sec) { - return cda_seek_sample (_info, sec * _info->samplerate); + return cda_seek_sample (_info, sec * _info->fmt.samplerate); } cddb_disc_t* @@ -244,7 +243,8 @@ resolve_disc (CdIo_t *cdio) conn = cddb_new(); - cddb_set_server_name (conn, deadbeef->conf_get_str ("cdda.freedb.host", DEFAULT_SERVER)); + deadbeef->conf_lock (); + cddb_set_server_name (conn, deadbeef->conf_get_str_fast ("cdda.freedb.host", DEFAULT_SERVER)); cddb_set_server_port (conn, deadbeef->conf_get_int ("cdda.freedb.port", DEFAULT_PORT)); if (!deadbeef->conf_get_int ("cdda.protocol", DEFAULT_PROTOCOL)) @@ -253,9 +253,10 @@ resolve_disc (CdIo_t *cdio) if (deadbeef->conf_get_int ("network.proxy", 0)) { cddb_set_server_port(conn, deadbeef->conf_get_int ("network.proxy.port", 8080)); - cddb_set_server_name(conn, deadbeef->conf_get_str ("network.proxy.address", "")); + cddb_set_server_name(conn, deadbeef->conf_get_str_fast ("network.proxy.address", "")); } } + deadbeef->conf_unlock (); int matches = cddb_query (conn, disc); if (matches == -1) @@ -286,10 +287,8 @@ insert_single_track (CdIo_t* cdio, DB_playItem_t *after, const char* file, int t int sector_count = cdio_get_track_sec_count (cdio, track_nr); - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (tmp); - it->filetype = "cdda"; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (tmp, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "cdda"); deadbeef->pl_set_item_duration (it, (float)sector_count / 75.0); snprintf (tmp, sizeof (tmp), "CD Track %02d", track_nr); @@ -320,9 +319,7 @@ cddb_thread (void *items_i) DB_playItem_t **items = params->items; trace ("calling resolve_disc\n"); - deadbeef->mutex_lock (mutex); cddb_disc_t* disc = resolve_disc (params->cdio); - deadbeef->mutex_unlock (mutex); if (!disc) { trace ("disc not resolved\n"); @@ -334,7 +331,6 @@ cddb_thread (void *items_i) } trace ("disc resolved\n"); - deadbeef->mutex_lock (mutex); const char *disc_title = cddb_disc_get_title (disc); const char *artist = cddb_disc_get_artist (disc); trace ("disc_title=%s, disk_artist=%s\n", disc_title, artist); @@ -359,9 +355,7 @@ cddb_thread (void *items_i) deadbeef->plug_trigger_event_trackinfochanged (items[i]); } cddb_disc_destroy (disc); - deadbeef->mutex_unlock (mutex); cleanup_thread_params (params); - cddb_tid = 0; deadbeef->plug_trigger_event_playlistchanged (); } @@ -508,10 +502,8 @@ cda_insert (DB_playItem_t *after, const char *fname) { if ((!got_cdtext || !prefer_cdtext) && enable_cddb) { trace ("cdda: querying freedb...\n"); - if (cddb_tid) { - deadbeef->thread_join (cddb_tid); - } - cddb_tid = deadbeef->thread_start (cddb_thread, p); //will destroy cdio + intptr_t tid = deadbeef->thread_start (cddb_thread, p); //will destroy cdio + deadbeef->thread_detach (tid); } else cleanup_thread_params (p); @@ -530,27 +522,12 @@ cda_insert (DB_playItem_t *after, const char *fname) { } static int -cda_start (void) { - mutex = deadbeef->mutex_create (); - return 0; -} - -static int -cda_stop (void) { - if (cddb_tid) { - trace ("cdda: waiting cddb query to end\n"); - deadbeef->thread_join (cddb_tid); - } - deadbeef->mutex_free (mutex); - return 0; -} - -static int cda_action_add_cd (DB_plugin_action_t *act, DB_playItem_t *it) { + deadbeef->pl_add_files_begin (deadbeef->plt_get_curr ()); deadbeef->pl_add_file ("all.cda", NULL, NULL); - //Wtf? - //playlist_refresh (); + deadbeef->pl_add_files_end (); + deadbeef->plug_trigger_event_playlistchanged (); } static DB_plugin_action_t add_cd_action = { @@ -581,23 +558,37 @@ static const char settings_dlg[] = // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "cda", .plugin.name = "Audio CD player", - .plugin.descr = "Audio CD plugins using libcdio and libcddb", - .plugin.author = "Viktor Semykin, Alexey Yakovenko", - .plugin.email = "thesame.ml@gmail.com, waker@users.sourceforge.net", + .plugin.descr = "Audio CD plugin using libcdio and libcddb", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", - .plugin.start = cda_start, - .plugin.stop = cda_stop, .plugin.configdialog = settings_dlg, .plugin.get_actions = cda_get_actions, .open = cda_open, .init = cda_init, .free = cda_free, - .read_int16 = cda_read_int16, + .read = cda_read, .seek = cda_seek, .seek_sample = cda_seek_sample, .insert = cda_insert, diff --git a/plugins/converter/Makefile b/plugins/converter/Makefile new file mode 100644 index 00000000..7cd742a0 --- /dev/null +++ b/plugins/converter/Makefile @@ -0,0 +1,32 @@ +CONVERTER_OUT=converter.so +GUI_OUT=converter_gtkui.so + +CC=gcc + +CFLAGS+=-Wall -D_GNU_SOURCE -std=c99 -fPIC -g -I../.. + +LDFLAGS+=-module -shared + +CONVERTER_SOURCES=converter.c +GUI_SOURCES=convgui.c interface.c support.c + +CONVERTER_OBJECTS=$(CONVERTER_SOURCES:.c=.o) +GUI_OBJECTS=$(GUI_SOURCES:.c=.o) + +all: $(CONVERTER_SOURCES) $(CONVERTER_OUT) $(GUI_SOURCES) $(GUI_OUT) + +$(CONVERTER_OUT): $(CONVERTER_OBJECTS) + $(CC) $(LDFLAGS) $(CONVERTER_OBJECTS) -o $@ + +GTK_CFLAGS=`pkg-config --cflags gtk+-2.0` +GTK_LIBS=`pkg-config --libs gtk+-2.0` + +$(GUI_OUT): $(GUI_OBJECTS) + $(CC) $(LDFLAGS) $(GUI_OBJECTS) $(GTK_LIBS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $(GTK_CFLAGS) $< -c -o $@ + +clean: + rm $(CONVERTER_OBJECTS) $(CONVERTER_OUT) $(GUI_OBJECTS) $(GUI_OUT) + diff --git a/plugins/converter/callbacks.c b/plugins/converter/callbacks.c new file mode 100644 index 00000000..b8fb56d3 --- /dev/null +++ b/plugins/converter/callbacks.c @@ -0,0 +1,11 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gtk/gtk.h> + +#include "callbacks.h" +#include "interface.h" +#include "support.h" + + diff --git a/plugins/converter/callbacks.h b/plugins/converter/callbacks.h new file mode 100644 index 00000000..7e323527 --- /dev/null +++ b/plugins/converter/callbacks.h @@ -0,0 +1,124 @@ +#include <gtk/gtk.h> + +void +on_converter_encoder_changed (GtkComboBox *combobox, + gpointer user_data); + +void +on_presets_cursor_changed (GtkTreeView *treeview, + gpointer user_data); + +void +on_dsp_preset_add_plugin_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_preset_remove_plugin_clicked (GtkButton *button, + gpointer user_data); + +void +on_converter_dsp_preset_changed (GtkComboBox *combobox, + gpointer user_data); + +void +on_dsp_preset_plugin_configure_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_preset_plugin_up_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_preset_plugin_down_clicked (GtkButton *button, + gpointer user_data); + +void +on_converter_output_format_changed (GtkComboBox *combobox, + gpointer user_data); + +GtkWidget* +encoder_cmdline_help_link_create (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2); + + +void +on_edit_encoder_presets_clicked (GtkButton *button, + gpointer user_data); + +void +on_edit_dsp_presets_clicked (GtkButton *button, + gpointer user_data); + +void +on_converter_output_browse_clicked (GtkButton *button, + gpointer user_data); + +void +on_converter_cancel_clicked (GtkButton *button, + gpointer user_data); + +void +on_converter_ok_clicked (GtkButton *button, + gpointer user_data); + +void +on_converterdlg_close (GtkDialog *dialog, + gpointer user_data); + +void +on_converterdlg_response (GtkDialog *dialog, + gint response_id, + gpointer user_data); + +gboolean +on_converterdlg_delete_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data); + +GtkWidget* +title_formatting_help_link_create (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2); + +void +on_output_folder_changed (GtkEditable *editable, + gpointer user_data); + +void +on_numthreads_changed (GtkEditable *editable, + gpointer user_data); + +void +on_overwrite_action_changed (GtkComboBox *combobox, + gpointer user_data); + +void +on_encoder_changed (GtkEditable *editable, + gpointer user_data); + +void +on_preserve_folder_browse_clicked (GtkButton *button, + gpointer user_data); + +void +on_fname_changed (GtkEditable *editable, + gpointer user_data); + +void +on_checkbutton1_toggled (GtkToggleButton *togglebutton, + gpointer user_data); + +void +on_preserve_folders_toggled (GtkToggleButton *togglebutton, + gpointer user_data); + +void +on_output_file_changed (GtkEditable *editable, + gpointer user_data); + +void +on_preserve_folder_root_changed (GtkEditable *editable, + gpointer user_data); + +void +on_preserve_root_folder_changed (GtkEditable *editable, + gpointer user_data); diff --git a/plugins/converter/converter.c b/plugins/converter/converter.c new file mode 100644 index 00000000..99d3b04b --- /dev/null +++ b/plugins/converter/converter.c @@ -0,0 +1,906 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include "converter.h" +#include <deadbeef.h> + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static ddb_converter_t plugin; +static DB_functions_t *deadbeef; + +static ddb_encoder_preset_t *encoder_presets; +static ddb_dsp_preset_t *dsp_presets; + +ddb_encoder_preset_t * +encoder_preset_alloc (void) { + ddb_encoder_preset_t *p = malloc (sizeof (ddb_encoder_preset_t)); + if (!p) { + fprintf (stderr, "failed to alloc ddb_encoder_preset_t\n"); + return NULL; + } + memset (p, 0, sizeof (ddb_encoder_preset_t)); + return p; +} + +void +encoder_preset_free (ddb_encoder_preset_t *p) { + if (p) { + if (p->title) { + free (p->title); + } + if (p->ext) { + free (p->ext); + } + if (p->encoder) { + free (p->encoder); + } + free (p); + } +} + +ddb_encoder_preset_t * +encoder_preset_load (const char *fname) { + int err = 1; + FILE *fp = fopen (fname, "rt"); + if (!fp) { + return NULL; + } + ddb_encoder_preset_t *p = encoder_preset_alloc (); + + char str[1024]; + while (fgets (str, sizeof (str), fp)) { + // chomp + char *cr = str + strlen (str) - 1; + while (*cr == '\n') { + cr--; + } + cr++; + *cr = 0; + + char *sp = strchr (str, ' '); + if (!sp) { + continue; + } + + *sp = 0; + char *item = sp + 1; + + if (!strcmp (str, "title")) { + p->title = strdup (item); + } + else if (!strcmp (str, "ext")) { + p->ext = strdup (item); + } + else if (!strcmp (str, "encoder")) { + p->encoder = strdup (item); + } + else if (!strcmp (str, "method")) { + p->method = atoi (item); + } + else if (!strcmp (str, "id3v2_version")) { + p->id3v2_version = atoi (item); + } + else if (!strcmp (str, "tag_id3v2")) { + p->tag_id3v2 = atoi (item); + } + else if (!strcmp (str, "tag_id3v1")) { + p->tag_id3v1 = atoi (item); + } + else if (!strcmp (str, "tag_apev2")) { + p->tag_apev2 = atoi (item); + } + else if (!strcmp (str, "tag_flac")) { + p->tag_flac = atoi (item); + } + else if (!strcmp (str, "tag_oggvorbis")) { + p->tag_oggvorbis = atoi (item); + } + } + + if (!p->title) { + p->title = strdup ("Untitled"); + } + if (!p->ext) { + p->ext = strdup (""); + } + if (!p->encoder) { + p->encoder = strdup (""); + } + + err = 0; + + if (err) { + encoder_preset_free (p); + p = NULL; + } + if (fp) { + fclose (fp); + } + return p; +} + +// @return -1 on path/write error, -2 if file already exists +int +encoder_preset_save (ddb_encoder_preset_t *p, int overwrite) { + const char *confdir = deadbeef->get_config_dir (); + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets", confdir) < 0) { + return -1; + } + mkdir (path, 0755); + if (snprintf (path, sizeof (path), "%s/presets/encoders", confdir) < 0) { + return -1; + } + mkdir (path, 0755); + if (snprintf (path, sizeof (path), "%s/presets/encoders/%s.txt", confdir, p->title) < 0) { + return -1; + } + + if (!overwrite) { + FILE *fp = fopen (path, "rb"); + if (fp) { + fclose (fp); + return -2; + } + } + + FILE *fp = fopen (path, "w+b"); + if (!fp) { + return -1; + } + + fprintf (fp, "title %s\n", p->title); + fprintf (fp, "ext %s\n", p->ext); + fprintf (fp, "encoder %s\n", p->encoder); + fprintf (fp, "method %d\n", p->method); + fprintf (fp, "id3v2_version %d\n", p->id3v2_version); + fprintf (fp, "tag_id3v2 %d\n", p->tag_id3v2); + fprintf (fp, "tag_id3v1 %d\n", p->tag_id3v1); + fprintf (fp, "tag_apev2 %d\n", p->tag_apev2); + fprintf (fp, "tag_flac %d\n", p->tag_flac); + fprintf (fp, "tag_oggvorbis %d\n", p->tag_oggvorbis); + + fclose (fp); + return 0; +} + +void +encoder_preset_copy (ddb_encoder_preset_t *to, ddb_encoder_preset_t *from) { + to->title = strdup (from->title); + to->ext = strdup (from->ext); + to->encoder = strdup (from->encoder); + to->method = from->method; + to->tag_id3v2 = from->tag_id3v2; + to->tag_id3v1 = from->tag_id3v1; + to->tag_apev2 = from->tag_apev2; + to->tag_flac = from->tag_flac; + to->tag_oggvorbis = from->tag_oggvorbis; + to->tag_mp3xing = from->tag_mp3xing; + to->id3v2_version = from->id3v2_version; +} + +ddb_encoder_preset_t * +encoder_preset_get_list (void) { + return encoder_presets; +} + +ddb_encoder_preset_t * +encoder_preset_get_for_idx (int idx) { + ddb_encoder_preset_t *p = encoder_presets; + while (p && idx--) { + p = p->next; + } + return p; +} + +void +encoder_preset_append (ddb_encoder_preset_t *p) { + // append + ddb_encoder_preset_t *tail = encoder_presets; + while (tail && tail->next) { + tail = tail->next; + } + if (tail) { + tail->next = p; + } + else { + encoder_presets = p; + } +} + +void +encoder_preset_remove (ddb_encoder_preset_t *p) { + ddb_encoder_preset_t *prev = encoder_presets; + while (prev && prev->next != p) { + prev = prev->next; + } + if (prev) { + prev->next = p->next; + } + else { + encoder_presets = p->next; + } +} + +void +encoder_preset_replace (ddb_encoder_preset_t *from, ddb_encoder_preset_t *to) { + ddb_encoder_preset_t *prev = encoder_presets; + while (prev && prev->next != from) { + prev = prev->next; + } + if (prev) { + prev->next = to; + } + else { + encoder_presets = to; + } + to->next = from->next; +} + +ddb_dsp_preset_t * +dsp_preset_alloc (void) { + ddb_dsp_preset_t *p = malloc (sizeof (ddb_dsp_preset_t)); + if (!p) { + fprintf (stderr, "failed to alloc ddb_dsp_preset_t\n"); + return NULL; + } + memset (p, 0, sizeof (ddb_dsp_preset_t)); + return p; +} + +void +dsp_preset_free (ddb_dsp_preset_t *p) { + if (p) { + if (p->title) { + free (p->title); + } + deadbeef->dsp_preset_free (p->chain); + free (p); + } +} + +void +dsp_preset_copy (ddb_dsp_preset_t *to, ddb_dsp_preset_t *from) { + to->title = strdup (from->title); + ddb_dsp_context_t *tail = NULL; + ddb_dsp_context_t *dsp = from->chain; + while (dsp) { + ddb_dsp_context_t *i = dsp->plugin->open (); + if (dsp->plugin->num_params) { + int n = dsp->plugin->num_params (); + for (int j = 0; j < n; j++) { + char s[1000] = ""; + dsp->plugin->get_param (dsp, j, s, sizeof (s)); + i->plugin->set_param (i, j, s); + } + } + if (tail) { + tail->next = i; + tail = i; + } + else { + to->chain = tail = i; + } + dsp = dsp->next; + } +} + +ddb_dsp_preset_t * +dsp_preset_get_list (void) { + return dsp_presets; +} + +ddb_dsp_preset_t * +dsp_preset_load (const char *fname) { + ddb_dsp_preset_t *p = dsp_preset_alloc (); + if (!p) { + return NULL; + } + memset (p, 0, sizeof (ddb_dsp_preset_t)); + const char *end = strrchr (fname, '.'); + if (!end) { + end = fname + strlen (fname); + } + const char *start = strrchr (fname, '/'); + if (!start) { + start = fname; + } + else { + start++; + } + + p->title = malloc (end-start+1); + memcpy (p->title, start, end-start); + p->title[end-start] = 0; + int err = deadbeef->dsp_preset_load (fname, &p->chain); + if (err != 0) { + dsp_preset_free (p); + return NULL; + } + return p; +} + +int +dsp_preset_save (ddb_dsp_preset_t *p, int overwrite) { + const char *confdir = deadbeef->get_config_dir (); + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets", confdir) < 0) { + return -1; + } + mkdir (path, 0755); + if (snprintf (path, sizeof (path), "%s/presets/dsp", confdir) < 0) { + return -1; + } + mkdir (path, 0755); + if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", confdir, p->title) < 0) { + return -1; + } + + if (!overwrite) { + FILE *fp = fopen (path, "rb"); + if (fp) { + fclose (fp); + return -2; + } + } + + return deadbeef->dsp_preset_save (path, p->chain); +} + +static int dirent_alphasort (const struct dirent **a, const struct dirent **b) { + return strcmp ((*a)->d_name, (*b)->d_name); +} + +int +scandir_preset_filter (const struct dirent *ent) { + char *ext = strrchr (ent->d_name, '.'); + if (ext && !strcasecmp (ext, ".txt")) { + return 1; + } + return 0; +} + +int +load_encoder_presets (void) { + ddb_encoder_preset_t *tail = NULL; + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets/encoders", deadbeef->get_config_dir ()) < 0) { + return -1; + } + struct dirent **namelist = NULL; + int n = scandir (path, &namelist, scandir_preset_filter, dirent_alphasort); + int i; + for (i = 0; i < n; i++) { + char s[1024]; + if (snprintf (s, sizeof (s), "%s/%s", path, namelist[i]->d_name) > 0){ + ddb_encoder_preset_t *p = encoder_preset_load (s); + if (p) { + if (tail) { + tail->next = p; + tail = p; + } + else { + encoder_presets = tail = p; + } + } + } + free (namelist[i]); + } + free (namelist); + return 0; +} + +int +load_dsp_presets (void) { + ddb_dsp_preset_t *tail = NULL; + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets/dsp", deadbeef->get_config_dir ()) < 0) { + return -1; + } + struct dirent **namelist = NULL; + int n = scandir (path, &namelist, scandir_preset_filter, dirent_alphasort); + int i; + for (i = 0; i < n; i++) { + char s[1024]; + if (snprintf (s, sizeof (s), "%s/%s", path, namelist[i]->d_name) > 0){ + ddb_dsp_preset_t *p = dsp_preset_load (s); + if (p) { + if (tail) { + tail->next = p; + tail = p; + } + else { + dsp_presets = tail = p; + } + } + } + free (namelist[i]); + } + free (namelist); + return 0; +} + +ddb_dsp_preset_t * +dsp_preset_get_for_idx (int idx) { + ddb_dsp_preset_t *p = dsp_presets; + while (p && idx--) { + p = p->next; + } + return p; +} + +void +dsp_preset_append (ddb_dsp_preset_t *p) { + // append + ddb_dsp_preset_t *tail = dsp_presets; + while (tail && tail->next) { + tail = tail->next; + } + if (tail) { + tail->next = p; + } + else { + dsp_presets = p; + } +} + +void +dsp_preset_remove (ddb_dsp_preset_t *p) { + ddb_dsp_preset_t *prev = dsp_presets; + while (prev && prev->next != p) { + prev = prev->next; + } + if (prev) { + prev->next = p->next; + } + else { + dsp_presets = p->next; + } +} + +void +dsp_preset_replace (ddb_dsp_preset_t *from, ddb_dsp_preset_t *to) { + ddb_dsp_preset_t *prev = dsp_presets; + while (prev && prev->next != from) { + prev = prev->next; + } + if (prev) { + prev->next = to; + } + else { + dsp_presets = to; + } + to->next = from->next; +} + +static void +get_output_path (DB_playItem_t *it, const char *outfolder, const char *outfile, ddb_encoder_preset_t *encoder_preset, char *out, int sz) { + char fname[PATH_MAX]; + int idx = deadbeef->pl_get_idx_of (it); + deadbeef->pl_format_title (it, idx, fname, sizeof (fname), -1, outfile); + // replace invalid chars + char *p = fname; + char invalid[] = "/\\?%*:|\"<>"; + while (*p) { + if (strchr (invalid, *p)) { + *p = '_'; + } + p++; + } + snprintf (out, sz, "%s/%s.%s", outfolder, fname, encoder_preset->ext); + +} + +int +convert (DB_playItem_t *it, const char *outfolder, const char *outfile, int output_bps, int output_is_float, int preserve_folder_structure, const char *root_folder, ddb_encoder_preset_t *encoder_preset, ddb_dsp_preset_t *dsp_preset, int *abort) { + int err = -1; + FILE *enc_pipe = NULL; + FILE *temp_file = NULL; + DB_decoder_t *dec = NULL; + DB_fileinfo_t *fileinfo = NULL; + char out[PATH_MAX] = ""; // full path to output file + char input_file_name[PATH_MAX] = ""; + dec = (DB_decoder_t *)deadbeef->plug_get_for_id (deadbeef->pl_find_meta (it, ":DECODER")); + + if (dec) { + fileinfo = dec->open (0); + if (fileinfo && dec->init (fileinfo, DB_PLAYITEM (it)) != 0) { + deadbeef->pl_lock (); + fprintf (stderr, "converter: failed to decode file %s\n", deadbeef->pl_find_meta (it, ":URI")); + deadbeef->pl_unlock (); + goto error; + } + if (fileinfo) { + if (output_bps == -1) { + output_bps = fileinfo->fmt.bps; + output_is_float = fileinfo->fmt.is_float; + } + + get_output_path (it, outfolder, outfile, encoder_preset, out, sizeof (out)); + if (encoder_preset->method == DDB_ENCODER_METHOD_FILE) { + const char *tmp = getenv ("TMPDIR"); + if (!tmp) { + tmp = "/tmp"; + } + snprintf (input_file_name, sizeof (input_file_name), "%s/ddbconvXXXXXX", tmp); + mktemp (input_file_name); + strcat (input_file_name, ".wav"); + } + else { + strcpy (input_file_name, "-"); + } + + char enc[2000]; + + // formatting: %o = outfile, %i = infile + char *e = encoder_preset->encoder; + char *o = enc; + *o = 0; + int len = sizeof (enc); + while (e && *e) { + if (len <= 0) { + fprintf (stderr, "converter: failed to assemble encoder command line - buffer is not big enough, try to shorten your parameters. max allowed length is %d characters\n", sizeof (enc)); + goto error; + } + if (e[0] == '%' && e[1]) { + if (e[1] == 'o') { + int l = snprintf (o, len, "\"%s\"", out); + o += l; + len -= l; + } + else if (e[1] == 'i') { + int l = snprintf (o, len, "\"%s\"", input_file_name); + o += l; + len -= l; + } + else { + strncpy (o, e, 2); + o += 2; + len -= 2; + } + e += 2; + } + else { + *o++ = *e++; + *o = 0; + len--; + } + } + + fprintf (stderr, "converter: will encode using: %s\n", enc); + + if (!encoder_preset->encoder[0]) { + // write to wave file + temp_file = fopen (out, "w+b"); + if (!temp_file) { + fprintf (stderr, "converter: failed to open output wave file %s\n", out); + goto error; + } + } + else if (encoder_preset->method == DDB_ENCODER_METHOD_FILE) { + temp_file = fopen (input_file_name, "w+b"); + if (!temp_file) { + fprintf (stderr, "converter: failed to open temp file %s\n", input_file_name); + goto error; + } + } + else { + enc_pipe = popen (enc, "w"); + if (!enc_pipe) { + fprintf (stderr, "converter: failed to open encoder\n"); + goto error; + } + } + + if (!temp_file) { + temp_file = enc_pipe; + } + + // write wave header + char wavehdr_int[] = { + 0x52, 0x49, 0x46, 0x46, 0x24, 0x70, 0x0d, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61 + }; + char wavehdr_float[] = { + 0x52, 0x49, 0x46, 0x46, 0x2a, 0xdf, 0x02, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x28, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x02, 0x00, 0x40, 0x1f, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x16, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0xc5, 0x5b, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61 + }; + char *wavehdr = output_is_float ? wavehdr_float : wavehdr_int; + int wavehdr_size = output_is_float ? sizeof (wavehdr_float) : sizeof (wavehdr_int); + int header_written = 0; + uint32_t outsize = 0; + uint32_t outsr = fileinfo->fmt.samplerate; + uint16_t outch = fileinfo->fmt.channels; + + int samplesize = fileinfo->fmt.channels * fileinfo->fmt.bps / 8; + int bs = 10250 * samplesize; + char buffer[bs * 4]; + int dspsize = bs / samplesize * sizeof (float) * fileinfo->fmt.channels; + char dspbuffer[dspsize * 4]; + int eof = 0; + for (;;) { + if (eof) { + break; + } + if (abort && *abort) { + break; + } + int sz = dec->read (fileinfo, buffer, bs); + + if (sz != bs) { + eof = 1; + } + if (dsp_preset) { + ddb_waveformat_t fmt; + ddb_waveformat_t outfmt; + memcpy (&fmt, &fileinfo->fmt, sizeof (fmt)); + memcpy (&outfmt, &fileinfo->fmt, sizeof (fmt)); + fmt.bps = 32; + fmt.is_float = 1; + deadbeef->pcm_convert (&fileinfo->fmt, buffer, &fmt, dspbuffer, sz); + + ddb_dsp_context_t *dsp = dsp_preset->chain; + int frames = sz / samplesize; + while (dsp) { + frames = dsp->plugin->process (dsp, (float *)dspbuffer, frames, sizeof (dspbuffer) / (fmt.channels * 4), &fmt, NULL); + dsp = dsp->next; + } + + outsr = fmt.samplerate; + outch = fmt.channels; + + outfmt.bps = output_bps; + outfmt.is_float = output_is_float; + outfmt.channels = outch; + outfmt.samplerate = outsr; + + int n = deadbeef->pcm_convert (&fmt, dspbuffer, &outfmt, buffer, frames * sizeof (float) * fmt.channels); + sz = n; + } + else if (fileinfo->fmt.bps != output_bps || fileinfo->fmt.is_float != output_is_float) { + ddb_waveformat_t outfmt; + memcpy (&outfmt, &fileinfo->fmt, sizeof (outfmt)); + outfmt.bps = output_bps; + outfmt.is_float = output_is_float; + outfmt.channels = outch; + outfmt.samplerate = outsr; + + int frames = sz / samplesize; + int n = deadbeef->pcm_convert (&fileinfo->fmt, buffer, &outfmt, dspbuffer, frames * samplesize); + memcpy (buffer, dspbuffer, n); + sz = n; + } + outsize += sz; + + if (!header_written) { + uint32_t size = (it->endsample-it->startsample) * outch * output_bps / 8; + if (!size) { + size = deadbeef->pl_get_item_duration (it) * fileinfo->fmt.samplerate * outch * output_bps / 8; + + } + + if (outsr != fileinfo->fmt.samplerate) { + uint64_t temp = size; + temp *= outsr; + temp /= fileinfo->fmt.samplerate; + size = temp; + } + + memcpy (&wavehdr[22], &outch, 2); + memcpy (&wavehdr[24], &outsr, 4); + uint16_t blockalign = outch * output_bps / 8; + memcpy (&wavehdr[32], &blockalign, 2); + memcpy (&wavehdr[34], &output_bps, 2); + + fwrite (wavehdr, 1, wavehdr_size, temp_file); + fwrite (&size, 1, sizeof (size), temp_file); + header_written = 1; + } + + if (sz != fwrite (buffer, 1, sz, temp_file)) { + fprintf (stderr, "converter: write error\n"); + goto error; + } + } + if (abort && *abort) { + goto error; + } + if (temp_file && temp_file != enc_pipe) { + fseek (temp_file, wavehdr_size, SEEK_SET); + fwrite (&outsize, 1, 4, temp_file); + + fclose (temp_file); + temp_file = NULL; + } + + if (encoder_preset->encoder[0] && encoder_preset->method == DDB_ENCODER_METHOD_FILE) { + enc_pipe = popen (enc, "w"); + } + } + } + err = 0; +error: + if (temp_file && temp_file != enc_pipe) { + fclose (temp_file); + temp_file = NULL; + } + if (enc_pipe) { + pclose (enc_pipe); + enc_pipe = NULL; + } + if (dec && fileinfo) { + dec->free (fileinfo); + fileinfo = NULL; + } + if (abort && *abort && out[0]) { + unlink (out); + } + if (input_file_name[0] && strcmp (input_file_name, "-")) { + unlink (input_file_name); + } + + // write junklib tags + uint32_t tagflags = JUNK_STRIP_ID3V2 | JUNK_STRIP_APEV2 | JUNK_STRIP_ID3V1; + if (encoder_preset->tag_id3v2) { + tagflags |= JUNK_WRITE_ID3V2; + } + if (encoder_preset->tag_id3v1) { + tagflags |= JUNK_WRITE_ID3V1; + } + if (encoder_preset->tag_apev2) { + tagflags |= JUNK_WRITE_APEV2; + } + DB_playItem_t *out_it = deadbeef->pl_item_alloc (); + deadbeef->pl_item_copy (out_it, it); + deadbeef->pl_replace_meta (out_it, ":URI", out); + deadbeef->pl_delete_meta (out_it, "cuesheet"); + + deadbeef->junk_rewrite_tags (out_it, tagflags, encoder_preset->id3v2_version + 3, "iso8859-1"); + + // write flac tags + if (encoder_preset->tag_flac) { + // find flac decoder plugin + DB_decoder_t **plugs = deadbeef->plug_get_decoder_list (); + DB_decoder_t *flac = NULL; + for (int i = 0; plugs[i]; i++) { + if (!strcmp (plugs[i]->plugin.id, "stdflac")) { + flac = plugs[i]; + break; + } + } + if (!flac) { + fprintf (stderr, "converter: flac plugin not found, cannot write flac metadata\n"); + } + else { + if (0 != flac->write_metadata (out_it)) { + fprintf (stderr, "converter: failed to write flac metadata, not a flac file?\n"); + } + } + } + + // write vorbis tags + if (encoder_preset->tag_oggvorbis) { + // find flac decoder plugin + DB_decoder_t **plugs = deadbeef->plug_get_decoder_list (); + DB_decoder_t *ogg = NULL; + for (int i = 0; plugs[i]; i++) { + if (!strcmp (plugs[i]->plugin.id, "stdogg")) { + ogg = plugs[i]; + break; + } + } + if (!ogg) { + fprintf (stderr, "converter: ogg plugin not found, cannot write ogg metadata\n"); + } + else { + if (0 != ogg->write_metadata (out_it)) { + fprintf (stderr, "converter: failed to write ogg metadata, not an ogg file?\n"); + } + } + } + + deadbeef->pl_item_unref (out_it); + + + return err; +} + +int +converter_cmd (int cmd, ...) { + return -1; +} + +int +converter_start (void) { + load_encoder_presets (); + load_dsp_presets (); + + return 0; +} + +int +converter_stop (void) { + return 0; +} + +// define plugin interface +static ddb_converter_t plugin = { + .misc.plugin.api_vmajor = DB_API_VERSION_MAJOR, + .misc.plugin.api_vminor = DB_API_VERSION_MINOR, + .misc.plugin.version_major = 1, + .misc.plugin.version_minor = 0, + .misc.plugin.type = DB_PLUGIN_MISC, + .misc.plugin.name = "Converter", + .misc.plugin.id = "converter", + .misc.plugin.descr = "Converts any supported formats to other formats.\n" + "Requires separate GUI plugin, e.g. Converter GTK UI\n", + .misc.plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .misc.plugin.website = "http://deadbeef.sf.net", + .misc.plugin.start = converter_start, + .misc.plugin.stop = converter_stop, + .misc.plugin.command = converter_cmd, + .encoder_preset_alloc = encoder_preset_alloc, + .encoder_preset_free = encoder_preset_free, + .encoder_preset_load = encoder_preset_load, + .encoder_preset_save = encoder_preset_save, + .encoder_preset_copy = encoder_preset_copy, + .encoder_preset_get_list = encoder_preset_get_list, + .encoder_preset_get_for_idx = encoder_preset_get_for_idx, + .encoder_preset_append = encoder_preset_append, + .encoder_preset_remove = encoder_preset_remove, + .encoder_preset_replace = encoder_preset_replace, + .dsp_preset_alloc = dsp_preset_alloc, + .dsp_preset_free = dsp_preset_free, + .dsp_preset_load = dsp_preset_load, + .dsp_preset_save = dsp_preset_save, + .dsp_preset_copy = dsp_preset_copy, + .dsp_preset_get_list = dsp_preset_get_list, + .dsp_preset_get_for_idx = dsp_preset_get_for_idx, + .dsp_preset_append = dsp_preset_append, + .dsp_preset_remove = dsp_preset_remove, + .dsp_preset_replace = dsp_preset_replace, + .get_output_path = get_output_path, + .convert = convert, +}; + +DB_plugin_t * +converter_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} diff --git a/plugins/converter/converter.glade b/plugins/converter/converter.glade new file mode 100644 index 00000000..fce698c5 --- /dev/null +++ b/plugins/converter/converter.glade @@ -0,0 +1,1839 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="converterdlg"> + <property name="visible">True</property> + <property name="title" translatable="yes">Converter</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">True</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area5"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="converter_cancel"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="converter_ok"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox26"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox67"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label103"> + <property name="visible">True</property> + <property name="label" translatable="yes">Output folder:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox68"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="output_folder"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">False</property> + <signal name="changed" handler="on_output_folder_changed" last_modification_time="Sun, 13 Mar 2011 11:25:59 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="converter_output_browse"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">...</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_converter_output_browse_clicked" last_modification_time="Thu, 02 Dec 2010 19:59:50 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox100"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label122"> + <property name="visible">True</property> + <property name="label" translatable="yes">Output file name:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox101"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="output_file"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Extension (e.g. .mp3) will be appended automatically. +Leave the field empty for default (%a - %t).</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">•</property> + <property name="activates_default">False</property> + <signal name="changed" handler="on_output_file_changed" last_modification_time="Sun, 13 Mar 2011 20:02:34 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="Custom" id="custom6"> + <property name="visible">True</property> + <property name="creation_function">title_formatting_help_link_create</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Fri, 03 Dec 2010 20:39:24 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox69"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label104"> + <property name="visible">True</property> + <property name="label" translatable="yes">Encoder:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox90"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkComboBox" id="encoder"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_converter_encoder_changed" last_modification_time="Mon, 06 Dec 2010 20:55:31 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="edit_encoder_presets"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_edit_encoder_presets_clicked" last_modification_time="Sat, 04 Dec 2010 15:20:49 GMT"/> + + <child> + <widget class="GtkImage" id="image469"> + <property name="visible">True</property> + <property name="stock">gtk-edit</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox86"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label114"> + <property name="visible">True</property> + <property name="label" translatable="yes">DSP preset:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox91"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkComboBox" id="dsp_preset"> + <property name="visible">True</property> + <property name="items" translatable="yes"></property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_converter_dsp_preset_changed" last_modification_time="Wed, 08 Dec 2010 21:22:19 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="edit_dsp_presets"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_edit_dsp_presets_clicked" last_modification_time="Sat, 04 Dec 2010 15:20:53 GMT"/> + + <child> + <widget class="GtkImage" id="image470"> + <property name="visible">True</property> + <property name="stock">gtk-edit</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox88"> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label116"> + <property name="visible">True</property> + <property name="label" translatable="yes">Number of threads:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="numthreads"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">False</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">1 0 100 1 10 0</property> + <signal name="changed" handler="on_numthreads_changed" last_modification_time="Sun, 13 Mar 2011 11:26:21 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox89"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label117"> + <property name="visible">True</property> + <property name="label" translatable="yes">Output sample format:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="output_format"> + <property name="visible">True</property> + <property name="items" translatable="yes">Keep source format +8 bit signed int +16 bit signed int +24 bit signed int +32 bit signed int +32 bit float</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_converter_output_format_changed" last_modification_time="Sun, 12 Dec 2010 16:55:42 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox99"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label121"> + <property name="visible">True</property> + <property name="label" translatable="yes">When file exists:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="overwrite_action"> + <property name="visible">True</property> + <property name="items" translatable="yes">Prompt +Overwrite</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_overwrite_action_changed" last_modification_time="Sun, 13 Mar 2011 11:26:30 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="preserve_folders"> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Preserve folder structure, starting from:</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_preserve_folders_toggled" last_modification_time="Sun, 13 Mar 2011 12:34:49 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox102"> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="preserve_root_folder"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">•</property> + <property name="activates_default">False</property> + <signal name="changed" handler="on_preserve_root_folder_changed" last_modification_time="Mon, 14 Mar 2011 21:00:54 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="preserve_folder_browse"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">...</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_preserve_folder_browse_clicked" last_modification_time="Sun, 13 Mar 2011 12:29:48 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="convpreset_editor"> + <property name="visible">True</property> + <property name="title" translatable="yes">Edit Encoder Preset</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox7"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area6"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="convpreset_cancel"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="convpreset_ok"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox27"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox70"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label105"> + <property name="visible">True</property> + <property name="label" translatable="yes">Title:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="title"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">Untitled Encoder</property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox96"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">9</property> + + <child> + <widget class="GtkLabel" id="label120"> + <property name="visible">True</property> + <property name="label" translatable="yes">Output file extension:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="ext"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">E.g. mp3</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox72"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label106"> + <property name="visible">True</property> + <property name="label" translatable="yes">Command line:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox93"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="encoder"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Example: lame - %o +%i for input file, %o for output file, - for stdin</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">True</property> + <signal name="changed" handler="on_encoder_changed" last_modification_time="Sun, 13 Mar 2011 11:48:10 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="Custom" id="custom4"> + <property name="visible">True</property> + <property name="creation_function">encoder_cmdline_help_link_create</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Sat, 04 Dec 2010 15:30:13 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label124"> + <property name="visible">True</property> + <property name="label" translatable="yes"><small>%o - output file name +%i - temporary input file name</small></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox73"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label107"> + <property name="visible">True</property> + <property name="label" translatable="yes">Method:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="method"> + <property name="visible">True</property> + <property name="items" translatable="yes">Pipe +Temporary file</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame9"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property> + + <child> + <widget class="GtkAlignment" id="alignment21"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkTable" id="table2"> + <property name="border_width">8</property> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">3</property> + <property name="homogeneous">False</property> + <property name="row_spacing">0</property> + <property name="column_spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="id3v1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">ID3v1</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="oggvorbis"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">OggVorbis</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="flac"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">FLAC</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox104"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkCheckButton" id="id3v2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">ID3v2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="id3v2_version"> + <property name="visible">True</property> + <property name="items" translatable="yes">2.3 +2.4</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label125"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Tag writer</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="dsppreset_editor"> + <property name="width_request">468</property> + <property name="height_request">254</property> + <property name="visible">True</property> + <property name="title" translatable="yes">DSP Preset Editor</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox9"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area8"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancelbutton6"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="okbutton6"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox30"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox81"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label111"> + <property name="visible">True</property> + <property name="label" translatable="yes">Title</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="title"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes">Untitled DSP Preset</property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox29"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox82"> + <property name="visible">True</property> + <property name="homogeneous">True</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="add"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Add</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_add_plugin_clicked" last_modification_time="Tue, 07 Dec 2010 20:11:31 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Remove</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_remove_plugin_clicked" last_modification_time="Tue, 07 Dec 2010 20:12:20 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="configure"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Configure</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_plugin_configure_clicked" last_modification_time="Thu, 09 Dec 2010 20:31:42 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox98"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow7"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="plugins"> + <property name="width_request">196</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox34"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="up"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-up</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_plugin_up_clicked" last_modification_time="Sun, 12 Dec 2010 13:42:49 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="down"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-down</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_plugin_down_clicked" last_modification_time="Sun, 12 Dec 2010 13:42:59 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="select_dsp_plugin"> + <property name="visible">True</property> + <property name="title" translatable="yes">Select DSP Plugin</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox10"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area9"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancelbutton7"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="okbutton7"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox31"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox85"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label113"> + <property name="visible">True</property> + <property name="label" translatable="yes">Plugin</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="plugin"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="preset_list"> + <property name="visible">True</property> + <property name="title" translatable="yes">Presets</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox11"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area10"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="okbutton8"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox33"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox94"> + <property name="visible">True</property> + <property name="homogeneous">True</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="add"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="edit"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-edit</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow8"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="presets"> + <property name="width_request">400</property> + <property name="height_request">176</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/plugins/converter/converter.gladep b/plugins/converter/converter.gladep new file mode 100644 index 00000000..fc0e5ab2 --- /dev/null +++ b/plugins/converter/converter.gladep @@ -0,0 +1,11 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd"> + +<glade-project> + <name>converter</name> + <program_name>converter</program_name> + <source_directory></source_directory> + <gnome_support>FALSE</gnome_support> + <output_main_file>FALSE</output_main_file> + <output_build_files>FALSE</output_build_files> +</glade-project> diff --git a/plugins/converter/converter.h b/plugins/converter/converter.h new file mode 100644 index 00000000..173d35b4 --- /dev/null +++ b/plugins/converter/converter.h @@ -0,0 +1,150 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __CONVERTER_H +#define __CONVERTER_H + +#include <stdint.h> +#include <deadbeef.h> + +enum { + DDB_ENCODER_METHOD_PIPE = 0, + DDB_ENCODER_METHOD_FILE = 1, +}; + +enum { + DDB_ENCODER_FMT_8BIT = 0x1, + DDB_ENCODER_FMT_16BIT = 0x2, + DDB_ENCODER_FMT_24BIT = 0x4, + DDB_ENCODER_FMT_32BIT = 0x8, + DDB_ENCODER_FMT_32BITFLOAT = 0x10, +}; + +typedef struct ddb_preset_s { + char *title; + struct ddb_preset_s *next; +} ddb_preset_t; + +typedef struct ddb_encoder_preset_s { + char *title; + struct ddb_encoder_preset_s *next; + char *ext; + char *encoder; + int method; // pipe or file + int tag_id3v2; + int tag_id3v1; + int tag_apev2; + int tag_flac; + int tag_oggvorbis; + int tag_mp3xing; + int id3v2_version; +} ddb_encoder_preset_t; + +typedef struct ddb_dsp_preset_s { + char *title; + struct ddb_dsp_preset_s *next; + ddb_dsp_context_t *chain; +} ddb_dsp_preset_t; + +typedef struct { + DB_misc_t misc; + + ///////////////////////////// + // encoder preset management + ///////////////////////////// + + ddb_encoder_preset_t * + (*encoder_preset_alloc) (void); + + void + (*encoder_preset_free) (ddb_encoder_preset_t *p); + + ddb_encoder_preset_t * + (*encoder_preset_load) (const char *fname); + + // @return -1 on path/write error, -2 if file already exists + int + (*encoder_preset_save) (ddb_encoder_preset_t *p, int overwrite); + + void + (*encoder_preset_copy) (ddb_encoder_preset_t *to, ddb_encoder_preset_t *from); + + ddb_encoder_preset_t * + (*encoder_preset_get_list) (void); + + ddb_encoder_preset_t * + (*encoder_preset_get_for_idx) (int idx); + + void + (*encoder_preset_append) (ddb_encoder_preset_t *p); + + void + (*encoder_preset_remove) (ddb_encoder_preset_t *p); + + void + (*encoder_preset_replace) (ddb_encoder_preset_t *from, ddb_encoder_preset_t *to); + + ///////////////////////////// + // dsp preset management + ///////////////////////////// + + ddb_dsp_preset_t * + (*dsp_preset_alloc) (void); + + void + (*dsp_preset_free) (ddb_dsp_preset_t *p); + + ddb_dsp_preset_t * + (*dsp_preset_load) (const char *fname); + + // @return -1 on path/write error, -2 if file already exists + int + (*dsp_preset_save) (ddb_dsp_preset_t *p, int overwrite); + + void + (*dsp_preset_copy) (ddb_dsp_preset_t *to, ddb_dsp_preset_t *from); + + ddb_dsp_preset_t * + (*dsp_preset_get_list) (void); + + ddb_dsp_preset_t * + (*dsp_preset_get_for_idx) (int idx); + + void + (*dsp_preset_append) (ddb_dsp_preset_t *p); + + void + (*dsp_preset_remove) (ddb_dsp_preset_t *p); + + void + (*dsp_preset_replace) (ddb_dsp_preset_t *from, ddb_dsp_preset_t *to); + + ///////////////////////////// + // converter + ///////////////////////////// + + + void + (*get_output_path) (DB_playItem_t *it, const char *outfolder, const char *outfile, ddb_encoder_preset_t *encoder_preset, char *out, int sz); + + int + (*convert) (DB_playItem_t *it, const char *outfolder, const char *outfile, int output_bps, int output_is_float, int preserve_folder_structure, const char *root_folder, ddb_encoder_preset_t *encoder_preset, ddb_dsp_preset_t *dsp_preset, int *abort); + +} ddb_converter_t; + +#endif diff --git a/plugins/converter/convgui.c b/plugins/converter/convgui.c new file mode 100644 index 00000000..286bfdd4 --- /dev/null +++ b/plugins/converter/convgui.c @@ -0,0 +1,1301 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <string.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <dirent.h> +#include "converter.h" +#include "support.h" +#include "interface.h" +#include "../gtkui/gtkui_api.h" + +DB_functions_t *deadbeef; + +ddb_converter_t *converter_plugin; +ddb_gtkui_t *gtkui_plugin; + +typedef struct { + GtkWidget *converter; + ddb_encoder_preset_t *current_encoder_preset; + ddb_dsp_preset_t *current_dsp_preset; + + DB_playItem_t **convert_items; + int convert_items_count; + char *outfolder; + char *outfile; + int preserve_folder_structure; + char *preserve_root_folder; + int output_bps; + int output_is_float; + int overwrite_action; + ddb_encoder_preset_t *encoder_preset; + ddb_dsp_preset_t *dsp_preset; + GtkWidget *progress; + GtkWidget *progress_entry; + int cancelled; + char *progress_text; +} converter_ctx_t; + +converter_ctx_t *current_ctx; + +void +fill_presets (GtkListStore *mdl, ddb_preset_t *head) { + ddb_preset_t *p = head; + while (p) { + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, p->title, -1); + p = p->next; + } +} + +void +on_converter_progress_cancel (GtkDialog *dialog, gint response_id, gpointer user_data) { + converter_ctx_t *ctx = user_data; + ctx->cancelled = 1; +} + +typedef struct { + GtkWidget *entry; + char *text; +} update_progress_info_t; + +static gboolean +update_progress_cb (gpointer ctx) { + update_progress_info_t *info = ctx; + gtk_entry_set_text (GTK_ENTRY (info->entry), info->text); + free (info->text); + g_object_unref (info->entry); + free (info); + return FALSE; +} + +static gboolean +destroy_progress_cb (gpointer ctx) { + gtk_widget_destroy (ctx); + return FALSE; +} + +struct overwrite_prompt_ctx { + char *fname; + uintptr_t mutex; + uintptr_t cond; + int result; +}; + +static gboolean +overwrite_prompt_cb (void *ctx) { + struct overwrite_prompt_ctx *ctl = ctx; + GtkWidget *mainwin = gtkui_plugin->get_mainwin (); + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (mainwin), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("The file already exists. Overwrite?")); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (mainwin)); + gtk_window_set_title (GTK_WINDOW (dlg), _("Converter warning")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), ctl->fname); + + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + ctl->result = response == GTK_RESPONSE_YES ? 1 : 0; + deadbeef->cond_signal (ctl->cond); + return FALSE; +} + +static void +converter_worker (void *ctx) { + converter_ctx_t *conv = ctx; + + for (int n = 0; n < conv->convert_items_count; n++) { + update_progress_info_t *info = malloc (sizeof (update_progress_info_t)); + info->entry = conv->progress_entry; + g_object_ref (info->entry); + info->text = strdup (deadbeef->pl_find_meta (conv->convert_items[n], ":URI")); + g_idle_add (update_progress_cb, info); + + char outpath[2000]; + converter_plugin->get_output_path (conv->convert_items[n], conv->outfolder, conv->outfile, conv->encoder_preset, outpath, sizeof (outpath)); + + int skip = 0; + struct stat st; + int res = stat(outpath, &st); + if (res == 0) { + if (conv->overwrite_action > 1 || conv->overwrite_action < 0) { + conv->overwrite_action = 0; + } + if (conv->overwrite_action == 0) { + // prompt if file exists + struct overwrite_prompt_ctx ctl; + ctl.mutex = deadbeef->mutex_create (); + ctl.cond = deadbeef->cond_create (); + ctl.fname = outpath; + ctl.result = 0; + gdk_threads_add_idle (overwrite_prompt_cb, &ctl); + deadbeef->cond_wait (ctl.cond, ctl.mutex); + deadbeef->cond_free (ctl.cond); + deadbeef->mutex_free (ctl.mutex); + if (ctl.result) { + unlink (outpath); + } + else { + skip = 1; + } + } + else if (conv->overwrite_action == 1) { + unlink (outpath); + } + } + + if (!skip) { + converter_plugin->convert (conv->convert_items[n], conv->outfolder, conv->outfile, conv->output_bps, conv->output_is_float, conv->preserve_folder_structure, conv->preserve_root_folder, conv->encoder_preset, conv->dsp_preset, &conv->cancelled); + } + if (conv->cancelled) { + for (; n < conv->convert_items_count; n++) { + deadbeef->pl_item_unref (conv->convert_items[n]); + } + break; + } + deadbeef->pl_item_unref (conv->convert_items[n]); + } + g_idle_add (destroy_progress_cb, conv->progress); + if (conv->convert_items) { + free (conv->convert_items); + } + free (conv->outfolder); + converter_plugin->encoder_preset_free (conv->encoder_preset); + converter_plugin->dsp_preset_free (conv->dsp_preset); + free (conv); +} + +int +converter_process (converter_ctx_t *conv) +{ + conv->outfolder = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (conv->converter, "output_folder")))); + const char *outfile = gtk_entry_get_text (GTK_ENTRY (lookup_widget (conv->converter, "output_file"))); + if (outfile[0] == 0) { + outfile = "%a - %t"; + } + conv->outfile = strdup (outfile); + conv->preserve_folder_structure = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (conv->converter, "preserve_folders"))); + conv->preserve_root_folder = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (conv->converter, "preserve_root_folder")))); + conv->overwrite_action = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (conv->converter, "overwrite_action"))); + + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "output_format")); + int selected_format = gtk_combo_box_get_active (combo); + switch (selected_format) { + case 1 ... 4: + conv->output_bps = selected_format * 8; + conv->output_is_float = 0; + break; + case 5: + conv->output_bps = 32; + conv->output_is_float = 1; + break; + default: + conv->output_bps = -1; // same as input, or encoder default + break; + } + + combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "encoder")); + int enc_preset = gtk_combo_box_get_active (combo); + ddb_encoder_preset_t *encoder_preset = NULL; + + if (enc_preset >= 0) { + encoder_preset = converter_plugin->encoder_preset_get_for_idx (enc_preset); + } + if (!encoder_preset) { + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (conv->converter), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Please select encoder")); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (conv->converter)); + gtk_window_set_title (GTK_WINDOW (dlg), _("Converter error")); + + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + return -1; + } + + combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "dsp_preset")); + int dsp_idx = gtk_combo_box_get_active (combo) - 1; + + ddb_dsp_preset_t *dsp_preset = NULL; + if (dsp_idx >= 0) { + dsp_preset = converter_plugin->dsp_preset_get_for_idx (dsp_idx); + } + + if (encoder_preset) { + conv->encoder_preset = converter_plugin->encoder_preset_alloc (); + converter_plugin->encoder_preset_copy (conv->encoder_preset, encoder_preset); + } + if (dsp_preset) { + conv->dsp_preset = converter_plugin->dsp_preset_alloc (); + converter_plugin->dsp_preset_copy (conv->dsp_preset, dsp_preset); + } + + GtkWidget *progress = gtk_dialog_new_with_buttons (_("Converting..."), GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); + GtkWidget *vbox = GTK_DIALOG (progress)->vbox; + GtkWidget *entry = gtk_entry_new (); + gtk_widget_set_size_request (entry, 400, -1); + gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE); + gtk_widget_show (entry); + gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 12); + + g_signal_connect ((gpointer)progress, "response", G_CALLBACK (on_converter_progress_cancel), conv); + + gtk_widget_show (progress); + + conv->progress = progress; + conv->progress_entry = entry; + intptr_t tid = deadbeef->thread_start (converter_worker, conv); + deadbeef->thread_detach (tid); + return 0; +} + +static int +converter_show (DB_plugin_action_t *act, DB_playItem_t *it) { + converter_ctx_t *conv = malloc (sizeof (converter_ctx_t)); + current_ctx = conv; + memset (conv, 0, sizeof (converter_ctx_t)); + + deadbeef->pl_lock (); + // copy list + int nsel = deadbeef->pl_getselcount (); + conv->convert_items_count = nsel; + if (0 < nsel) { + conv->convert_items = malloc (sizeof (DB_playItem_t *) * nsel); + if (conv->convert_items) { + int n = 0; + DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); + while (it) { + if (deadbeef->pl_is_selected (it)) { + assert (n < nsel); + deadbeef->pl_item_ref (it); + conv->convert_items[n++] = it; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + } + } + deadbeef->pl_unlock (); + + conv->converter = create_converterdlg (); + deadbeef->conf_lock (); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (conv->converter, "output_folder")), deadbeef->conf_get_str_fast ("converter.output_folder", "")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (conv->converter, "output_file")), deadbeef->conf_get_str_fast ("converter.output_file", "")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (conv->converter, "preserve_root_folder")), deadbeef->conf_get_str_fast ("converter.preserve_root_folder", "")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (conv->converter, "preserve_folders")), deadbeef->conf_get_int ("converter.preserve_folder_structure", 0)); + gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (conv->converter, "overwrite_action")), deadbeef->conf_get_int ("converter.overwrite_action", 0)); + deadbeef->conf_unlock (); + + GtkComboBox *combo; + // fill encoder presets + combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "encoder")); + GtkListStore *mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list ()); + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.encoder_preset", 0)); + + // fill dsp presets + combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "dsp_preset")); + mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, "Pass through", -1); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list ()); + + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.dsp_preset", -1) + 1); + + // select output format + combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "output_format")); + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.output_format", 0)); + + // overwrite action + combo = GTK_COMBO_BOX (lookup_widget (conv->converter, "overwrite_action")); + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.overwrite_action", 0)); + + for (;;) { + int response = gtk_dialog_run (GTK_DIALOG (conv->converter)); + if (response == GTK_RESPONSE_OK) { + int err = converter_process (conv); + if (err != 0) { + continue; + } + gtk_widget_destroy (conv->converter); + } + else { + // FIXME: clean up properly + gtk_widget_destroy (conv->converter); + if (conv->convert_items) { + for (int n = 0; n < conv->convert_items_count; n++) { + deadbeef->pl_item_unref (conv->convert_items[n]); + } + free (conv->convert_items); + } + free (conv); + } + current_ctx = NULL; + break; + } + return 0; +} + +void +on_converter_encoder_changed (GtkComboBox *combobox, + gpointer user_data) +{ + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder")); + int act = gtk_combo_box_get_active (combo); + deadbeef->conf_set_int ("converter.encoder_preset", act); +} + +void +on_converter_dsp_preset_changed (GtkComboBox *combobox, + gpointer user_data) +{ + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset")); + int act = gtk_combo_box_get_active (combo); + deadbeef->conf_set_int ("converter.dsp_preset", act-1); +} + +void +on_converter_output_browse_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Select folder..."), GTK_WINDOW (current_ctx->converter), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter)); + + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE); + // restore folder + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + // store folder + gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); + if (folder) { + deadbeef->conf_set_str ("filechooser.lastdir", folder); + g_free (folder); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); + } + if (response == GTK_RESPONSE_OK) { + folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg)); + gtk_widget_destroy (dlg); + if (folder) { + GtkWidget *entry = lookup_widget (current_ctx->converter, "output_folder"); + gtk_entry_set_text (GTK_ENTRY (entry), folder); + g_free (folder); + } + } + else { + gtk_widget_destroy (dlg); + } +} + + +void +on_output_folder_changed (GtkEntry *entry, + gpointer user_data) +{ + deadbeef->conf_set_str ("converter.output_folder", gtk_entry_get_text (entry)); + deadbeef->conf_save (); +} + + +void +on_numthreads_changed (GtkEditable *editable, + gpointer user_data) +{ + deadbeef->conf_set_int ("converter.threads", gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (editable))); + deadbeef->conf_save (); +} + +void +on_overwrite_action_changed (GtkComboBox *combobox, + gpointer user_data) +{ + deadbeef->conf_set_int ("converter.overwrite_action", gtk_combo_box_get_active (combobox)); + deadbeef->conf_save (); +} + +void +on_encoder_changed (GtkEditable *editable, + gpointer user_data) +{ + gtk_widget_set_has_tooltip (GTK_WIDGET (editable), TRUE); + + char enc[2000]; + const char *e = gtk_entry_get_text (GTK_ENTRY (editable)); + char *o = enc; + *o = 0; + int len = sizeof (enc); + while (e && *e) { + if (len <= 0) { + break; + } + if (e[0] == '%' && e[1]) { + if (e[1] == 'o') { + int l = snprintf (o, len, "\"OUTPUT_FILE_NAME\""); + o += l; + len -= l; + } + else if (e[1] == 'i') { + int l = snprintf (o, len, "\"TEMP_FILE_NAME\""); + o += l; + len -= l; + } + else { + strncpy (o, e, 2); + o += 2; + len -= 2; + } + e += 2; + } + else { + *o++ = *e++; + *o = 0; + len--; + } + } + + gtk_widget_set_tooltip_text (GTK_WIDGET (editable), enc); +} + +void +on_preserve_folder_browse_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Select folder..."), GTK_WINDOW (current_ctx->converter), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter)); + + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE); + // restore folder + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + // store folder + gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); + if (folder) { + deadbeef->conf_set_str ("filechooser.lastdir", folder); + g_free (folder); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); + } + if (response == GTK_RESPONSE_OK) { + folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg)); + gtk_widget_destroy (dlg); + if (folder) { + GtkWidget *entry = lookup_widget (current_ctx->converter, "preserve_root_folder"); + gtk_entry_set_text (GTK_ENTRY (entry), folder); + g_free (folder); + } + } + else { + gtk_widget_destroy (dlg); + } +} + +void +on_output_file_changed (GtkEntry *entry, + gpointer user_data) +{ + deadbeef->conf_set_str ("converter.output_file", gtk_entry_get_text (entry)); + deadbeef->conf_save (); +} + +void +on_preserve_folders_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("converter.preserve_folder_structure", gtk_toggle_button_get_active (togglebutton)); + deadbeef->conf_save (); +} + +void +on_preserve_root_folder_changed (GtkEntry *entry, + gpointer user_data) +{ + deadbeef->conf_set_str ("converter.preserve_root_folder", gtk_entry_get_text (entry)); + deadbeef->conf_save (); +} + +DB_decoder_t * +plug_get_decoder_for_id (const char *id) { + DB_decoder_t **plugins = deadbeef->plug_get_decoder_list (); + for (int c = 0; plugins[c]; c++) { + if (!strcmp (id, plugins[c]->plugin.id)) { + return plugins[c]; + } + } + return NULL; +} + +void +init_encoder_preset_from_dlg (GtkWidget *dlg, ddb_encoder_preset_t *p) { + p->title = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "title")))); + p->ext = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "ext")))); + p->encoder = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "encoder")))); + int method_idx = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (dlg, "method"))); + switch (method_idx) { + case 0: + p->method = DDB_ENCODER_METHOD_PIPE; + break; + case 1: + p->method = DDB_ENCODER_METHOD_FILE; + break; + } + + p->id3v2_version = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (dlg, "id3v2_version"))); + p->tag_id3v2 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v2"))); + p->tag_id3v1 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v1"))); + p->tag_apev2 = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "apev2"))); + p->tag_flac = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "flac"))); + p->tag_oggvorbis = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "oggvorbis"))); +} + +int +edit_encoder_preset (char *title, GtkWidget *toplevel, int overwrite) { + GtkWidget *dlg = create_convpreset_editor (); + gtk_window_set_title (GTK_WINDOW (dlg), title); + gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); + + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel)); + + ddb_encoder_preset_t *p = current_ctx->current_encoder_preset; + + if (p->title) { + gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "title")), p->title); + } + if (p->ext) { + gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "ext")), p->ext); + } + if (p->encoder) { + gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "encoder")), p->encoder); + } + gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (dlg, "method")), p->method); + + gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (dlg, "id3v2_version")), p->id3v2_version); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v2")), p->tag_id3v2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "id3v1")), p->tag_id3v1); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "apev2")), p->tag_apev2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "flac")), p->tag_flac); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "oggvorbis")), p->tag_oggvorbis); + + ddb_encoder_preset_t *old = p; + int r = GTK_RESPONSE_CANCEL; + for (;;) { + r = gtk_dialog_run (GTK_DIALOG (dlg)); + if (r == GTK_RESPONSE_OK) { + ddb_encoder_preset_t *p = converter_plugin->encoder_preset_alloc (); + if (p) { + init_encoder_preset_from_dlg (dlg, p); + int err = converter_plugin->encoder_preset_save (p, overwrite); + if (!err) { + if (old->title && strcmp (p->title, old->title)) { + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets/encoders/%s.txt", deadbeef->get_config_dir (), old->title) > 0) { + unlink (path); + } + } + free (old->title); + free (old->ext); + free (old->encoder); + + converter_plugin->encoder_preset_copy (old, p); + converter_plugin->encoder_preset_free (p); + } + else { + GtkWidget *warndlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed to save encoder preset")); + gtk_window_set_transient_for (GTK_WINDOW (warndlg), GTK_WINDOW (dlg)); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warndlg), err == -1 ? _("Check preset folder permissions, try to pick different title, or free up some disk space") : _("Preset with the same name already exists. Try to pick another title.")); + gtk_window_set_title (GTK_WINDOW (warndlg), _("Error")); + + /*int response = */gtk_dialog_run (GTK_DIALOG (warndlg)); + gtk_widget_destroy (warndlg); + continue; + } + } + } + break; + } + + gtk_widget_destroy (dlg); + return r; +} + +void +refresh_encoder_lists (GtkComboBox *combo, GtkTreeView *list) { + // presets list view + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list))); + + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + int idx = -1; + if (path && col) { + int *indices = gtk_tree_path_get_indices (path); + idx = *indices; + g_free (indices); + } + + gtk_list_store_clear (mdl); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list ()); + if (idx != -1) { + path = gtk_tree_path_new_from_indices (idx, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + } + + // presets combo box + int act = gtk_combo_box_get_active (combo); + mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + gtk_list_store_clear (mdl); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list ()); + gtk_combo_box_set_active (combo, act); +} + +void +on_encoder_preset_add (GtkButton *button, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + + current_ctx->current_encoder_preset = converter_plugin->encoder_preset_alloc (); + + if (GTK_RESPONSE_OK == edit_encoder_preset (_("Add new encoder"), toplevel, 0)) { + printf ("added new enc preset\n"); + converter_plugin->encoder_preset_append (current_ctx->current_encoder_preset); + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder")); + GtkWidget *list = lookup_widget (toplevel, "presets"); + printf ("refresh list\n"); + refresh_encoder_lists (combo, GTK_TREE_VIEW (list)); + } + + current_ctx->current_encoder_preset = NULL; +} + +void +on_encoder_preset_edit (GtkButton *button, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + GtkWidget *list = lookup_widget (toplevel, "presets"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + + ddb_encoder_preset_t *p = converter_plugin->encoder_preset_get_for_idx (idx); + current_ctx->current_encoder_preset = p; + + int r = edit_encoder_preset (_("Edit encoder"), toplevel, 1); + if (r == GTK_RESPONSE_OK) { + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder")); + refresh_encoder_lists (combo, GTK_TREE_VIEW (list)); + } + + current_ctx->current_encoder_preset = NULL; +} + +void +on_encoder_preset_remove (GtkButton *button, + gpointer user_data) +{ + + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + GtkWidget *list = lookup_widget (toplevel, "presets"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + + ddb_encoder_preset_t *p = converter_plugin->encoder_preset_get_for_idx (idx); + if (!p) { + return; + } + + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Remove preset")); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel)); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), _("This action will delete the selected preset. Are you sure?")); + gtk_window_set_title (GTK_WINDOW (dlg), _("Warning")); + + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + if (response == GTK_RESPONSE_YES) { + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets/encoders/%s.txt", deadbeef->get_config_dir (), p->title) > 0) { + unlink (path); + } + + converter_plugin->encoder_preset_remove (p); + converter_plugin->encoder_preset_free (p); + + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "encoder")); + refresh_encoder_lists (combo, GTK_TREE_VIEW (list)); + } +} + +void +on_edit_encoder_presets_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *dlg = create_preset_list (); + gtk_window_set_title (GTK_WINDOW (dlg), _("Encoders")); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter)); + g_signal_connect ((gpointer)lookup_widget (dlg, "add"), "clicked", G_CALLBACK (on_encoder_preset_add), NULL); + g_signal_connect ((gpointer)lookup_widget (dlg, "remove"), "clicked", G_CALLBACK (on_encoder_preset_remove), NULL); + g_signal_connect ((gpointer)lookup_widget (dlg, "edit"), "clicked", G_CALLBACK (on_encoder_preset_edit), NULL); + + GtkWidget *list = lookup_widget (dlg, "presets"); + GtkCellRenderer *title_cell = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Title"), title_cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), GTK_TREE_VIEW_COLUMN (col)); + GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (mdl)); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->encoder_preset_get_list ()); + int curr = deadbeef->conf_get_int ("converter.encoder_preset", -1); + if (curr != -1) { + GtkTreePath *path = gtk_tree_path_new_from_indices (curr, -1); + if (path && gtk_tree_path_get_depth (path) > 0) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + } + } + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); +} + +///// dsp preset gui + +void +fill_dsp_plugin_list (GtkListStore *mdl) { + struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list (); + int i; + for (i = 0; dsp[i]; i++) { + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, dsp[i]->plugin.name, -1); + } +} + +void +fill_dsp_preset_chain (GtkListStore *mdl) { + ddb_dsp_context_t *dsp = current_ctx->current_dsp_preset->chain; + while (dsp) { + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, dsp->plugin->plugin.name, -1); + dsp = dsp->next; + } +} + +void +on_dsp_preset_add_plugin_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *dlg = create_select_dsp_plugin (); + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel)); + gtk_window_set_title (GTK_WINDOW (dlg), _("Add plugin to DSP chain")); + + GtkComboBox *combo; + // fill encoder presets + combo = GTK_COMBO_BOX (lookup_widget (dlg, "plugin")); + GtkListStore *mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + fill_dsp_plugin_list (mdl); + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.last_selected_dsp", 0)); + + int r = gtk_dialog_run (GTK_DIALOG (dlg)); + if (r == GTK_RESPONSE_OK) { + // create new instance of the selected plugin + int idx = gtk_combo_box_get_active (combo); + struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list (); + int i; + ddb_dsp_context_t *inst = NULL; + for (i = 0; dsp[i]; i++) { + if (i == idx) { + inst = dsp[i]->open (); + break; + } + } + if (inst) { + // append to DSP chain + ddb_dsp_context_t *tail = current_ctx->current_dsp_preset->chain; + while (tail && tail->next) { + tail = tail->next; + } + if (tail) { + tail->next = inst; + } + else { + current_ctx->current_dsp_preset->chain = inst; + } + + // reinit list of instances + GtkWidget *list = lookup_widget (toplevel, "plugins"); + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list))); + gtk_list_store_clear (mdl); + fill_dsp_preset_chain (mdl); + } + else { + fprintf (stderr, "converter: failed to add DSP plugin to chain\n"); + } + } + gtk_widget_destroy (dlg); +} + + +void +on_dsp_preset_remove_plugin_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + GtkWidget *list = lookup_widget (toplevel, "plugins"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + if (idx == -1) { + return; + } + + ddb_dsp_context_t *p = current_ctx->current_dsp_preset->chain; + ddb_dsp_context_t *prev = NULL; + int i = idx; + while (p && i--) { + prev = p; + p = p->next; + } + if (p) { + if (prev) { + prev->next = p->next; + } + else { + current_ctx->current_dsp_preset->chain = p->next; + } + p->plugin->close (p); + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list))); + gtk_list_store_clear (mdl); + fill_dsp_preset_chain (mdl); + path = gtk_tree_path_new_from_indices (idx, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + } +} + +static ddb_dsp_context_t *current_dsp_context = NULL; + +void +dsp_ctx_set_param (const char *key, const char *value) { + current_dsp_context->plugin->set_param (current_dsp_context, atoi (key), value); +} + +void +dsp_ctx_get_param (const char *key, char *value, int len, const char *def) { + strncpy (value, def, len); + current_dsp_context->plugin->get_param (current_dsp_context, atoi (key), value, len); +} + +void +on_dsp_preset_plugin_configure_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + GtkWidget *list = lookup_widget (toplevel, "plugins"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + if (idx == -1) { + return; + } + ddb_dsp_context_t *p = current_ctx->current_dsp_preset->chain; + int i = idx; + while (p && i--) { + p = p->next; + } + if (!p || !p->plugin->configdialog) { + return; + } + current_dsp_context = p; + ddb_dialog_t conf = { + .title = p->plugin->plugin.name, + .layout = p->plugin->configdialog, + .set_param = dsp_ctx_set_param, + .get_param = dsp_ctx_get_param, + }; + gtkui_plugin->gui.run_dialog (&conf, 0, NULL, NULL); + current_dsp_context = NULL; +} + +void +on_dsp_preset_plugin_up_clicked (GtkButton *button, + gpointer user_data) +{ + +} + + +void +on_dsp_preset_plugin_down_clicked (GtkButton *button, + gpointer user_data) +{ + +} + + +int +edit_dsp_preset (const char *title, GtkWidget *toplevel, int overwrite) { + int r = GTK_RESPONSE_CANCEL; + + GtkWidget *dlg = create_dsppreset_editor (); + gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel)); + gtk_window_set_title (GTK_WINDOW (dlg), title); + + + // title + if (current_ctx->current_dsp_preset->title) { + gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "title")), current_ctx->current_dsp_preset->title); + } + + { + GtkWidget *list = lookup_widget (dlg, "plugins"); + GtkCellRenderer *title_cell = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Plugin"), title_cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), GTK_TREE_VIEW_COLUMN (col)); + GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (mdl)); + + fill_dsp_preset_chain (mdl); + } + + for (;;) { + r = gtk_dialog_run (GTK_DIALOG (dlg)); + + if (r == GTK_RESPONSE_OK) { + if (current_ctx->current_dsp_preset->title) { + free (current_ctx->current_dsp_preset->title); + } + current_ctx->current_dsp_preset->title = strdup (gtk_entry_get_text (GTK_ENTRY (lookup_widget (dlg, "title")))); + int err = converter_plugin->dsp_preset_save (current_ctx->current_dsp_preset, overwrite); + if (err < 0) { + GtkWidget *warndlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Failed to save DSP preset")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (warndlg), err == -1 ? _("Check preset folder permissions, try to pick different title, or free up some disk space") : _("Preset with the same name already exists. Try to pick another title.")); + gtk_window_set_title (GTK_WINDOW (warndlg), _("Error")); + + gtk_window_set_transient_for (GTK_WINDOW (warndlg), GTK_WINDOW (dlg)); + /*int response = */gtk_dialog_run (GTK_DIALOG (warndlg)); + gtk_widget_destroy (warndlg); + continue; + } + + } + + break; + } + + gtk_widget_destroy (dlg); + return r; +} + +void +refresh_dsp_lists (GtkComboBox *combo, GtkTreeView *list) { + // presets list view + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list))); + + GtkTreePath *path; + GtkTreeViewColumn *col; + int idx = -1; + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (path && col) { + int *indices = gtk_tree_path_get_indices (path); + idx = *indices; + g_free (indices); + } + + gtk_list_store_clear (mdl); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list ()); + if (idx != -1) { + path = gtk_tree_path_new_from_indices (idx, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + } + + // presets combo box + int act = gtk_combo_box_get_active (combo); + mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + gtk_list_store_clear (mdl); + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, "Pass through", -1); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list ()); + gtk_combo_box_set_active (combo, act); +} + + +void +on_dsp_preset_add (GtkButton *button, + gpointer user_data) +{ + + current_ctx->current_dsp_preset = converter_plugin->dsp_preset_alloc (); + + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + + if (GTK_RESPONSE_OK == edit_dsp_preset (_("New DSP Preset"), toplevel, 0)) { + converter_plugin->dsp_preset_append (current_ctx->current_dsp_preset); + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset")); + GtkWidget *list = lookup_widget (toplevel, "presets"); + refresh_dsp_lists (combo, GTK_TREE_VIEW (list)); + } + else { + converter_plugin->dsp_preset_free (current_ctx->current_dsp_preset); + } + + current_ctx->current_dsp_preset = NULL; +} + +void +on_dsp_preset_remove (GtkButton *button, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + GtkWidget *list = lookup_widget (toplevel, "presets"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + + ddb_dsp_preset_t *p = converter_plugin->dsp_preset_get_for_idx (idx); + if (!p) { + return; + } + + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (gtkui_plugin->get_mainwin ()), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Remove preset")); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (toplevel)); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), _("This action will delete the selected preset. Are you sure?")); + gtk_window_set_title (GTK_WINDOW (dlg), _("Warning")); + + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + if (response == GTK_RESPONSE_YES) { + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", deadbeef->get_config_dir (), p->title) > 0) { + unlink (path); + } + + converter_plugin->dsp_preset_remove (p); + converter_plugin->dsp_preset_free (p); + + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset")); + refresh_dsp_lists (combo, GTK_TREE_VIEW (list)); + } +} + +void +on_dsp_preset_edit (GtkButton *button, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button)); + + GtkWidget *list = lookup_widget (toplevel, "presets"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + if (idx == -1) { + return; + } + if (idx == 0) { + return; + } + + ddb_dsp_preset_t *p = converter_plugin->dsp_preset_get_for_idx (idx); + if (!p) { + return; + } + + current_ctx->current_dsp_preset = converter_plugin->dsp_preset_alloc (); + converter_plugin->dsp_preset_copy (current_ctx->current_dsp_preset, p); + + int r = edit_dsp_preset (_("Edit DSP Preset"), toplevel, 1); + if (r == GTK_RESPONSE_OK) { + // replace preset + converter_plugin->dsp_preset_replace (p, current_ctx->current_dsp_preset); + converter_plugin->dsp_preset_free (p); + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (current_ctx->converter, "dsp_preset")); + refresh_dsp_lists (combo, GTK_TREE_VIEW (list)); + } + else { + converter_plugin->dsp_preset_free (current_ctx->current_dsp_preset); + } + + current_ctx->current_dsp_preset = NULL; +} + +void +on_edit_dsp_presets_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *dlg = create_preset_list (); + gtk_window_set_title (GTK_WINDOW (dlg), _("DSP Presets")); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (current_ctx->converter)); + g_signal_connect ((gpointer)lookup_widget (dlg, "add"), "clicked", G_CALLBACK (on_dsp_preset_add), NULL); + g_signal_connect ((gpointer)lookup_widget (dlg, "remove"), "clicked", G_CALLBACK (on_dsp_preset_remove), NULL); + g_signal_connect ((gpointer)lookup_widget (dlg, "edit"), "clicked", G_CALLBACK (on_dsp_preset_edit), NULL); + + GtkWidget *list = lookup_widget (dlg, "presets"); + GtkCellRenderer *title_cell = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Title"), title_cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (list), GTK_TREE_VIEW_COLUMN (col)); + GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (list), GTK_TREE_MODEL (mdl)); + fill_presets (mdl, (ddb_preset_t *)converter_plugin->dsp_preset_get_list ()); + int curr = deadbeef->conf_get_int ("converter.dsp_preset", -1); + if (curr >= 0) { + GtkTreePath *path = gtk_tree_path_new_from_indices (curr, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + } + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); +} + + +void +on_converter_output_format_changed (GtkComboBox *combobox, + gpointer user_data) +{ + int idx = gtk_combo_box_get_active (combobox); + deadbeef->conf_set_int ("converter.output_format", idx); +} + +GtkWidget* +title_formatting_help_link_create (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2) +{ + GtkWidget *link = gtk_link_button_new_with_label ("http://sourceforge.net/apps/mediawiki/deadbeef/index.php?title=Title_Formatting", "Help"); + return link; +} + +GtkWidget* +encoder_cmdline_help_link_create (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2) +{ + GtkWidget *link = gtk_link_button_new_with_label ("http://sourceforge.net/apps/mediawiki/deadbeef/index.php?title=Encoder_Command_Line", "Help"); + return link; +} + +static DB_plugin_action_t convert_action = { + .title = "Convert", + .name = "convert", + .flags = DB_ACTION_CAN_MULTIPLE_TRACKS | DB_ACTION_ALLOW_MULTIPLE_TRACKS | DB_ACTION_SINGLE_TRACK, + .callback = converter_show, + .next = NULL +}; + +static DB_plugin_action_t * +convgui_get_actions (DB_playItem_t *it) +{ + return &convert_action; +} + +int +convgui_connect (void) { + gtkui_plugin = (ddb_gtkui_t *)deadbeef->plug_get_for_id ("gtkui"); + converter_plugin = (ddb_converter_t *)deadbeef->plug_get_for_id ("converter"); + if (!gtkui_plugin || !converter_plugin) { + return -1; + } + return 0; +} + +DB_misc_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 1, + .plugin.version_minor = 0, + .plugin.type = DB_PLUGIN_MISC, + .plugin.name = "Converter GTK UI", + .plugin.descr = "GTK2 User interface for the Converter plugin\n" + "Usage:\n" + "· select some tracks in playlist\n" + "· right click\n" + "· select «Convert»", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.website = "http://deadbeef.sf.net", + .plugin.get_actions = convgui_get_actions, + .plugin.connect = convgui_connect, +}; + +DB_plugin_t * +converter_gtkui_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} + diff --git a/plugins/converter/interface.c b/plugins/converter/interface.c new file mode 100644 index 00000000..5045a79d --- /dev/null +++ b/plugins/converter/interface.c @@ -0,0 +1,890 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "callbacks.h" +#include "interface.h" +#include "support.h" + +#define GLADE_HOOKUP_OBJECT(component,widget,name) \ + g_object_set_data_full (G_OBJECT (component), name, \ + gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) + +#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ + g_object_set_data (G_OBJECT (component), name, widget) + +GtkWidget* +create_converterdlg (void) +{ + GtkWidget *converterdlg; + GtkWidget *dialog_vbox6; + GtkWidget *vbox26; + GtkWidget *hbox67; + GtkWidget *label103; + GtkWidget *hbox68; + GtkWidget *output_folder; + GtkWidget *converter_output_browse; + GtkWidget *hbox100; + GtkWidget *label122; + GtkWidget *hbox101; + GtkWidget *output_file; + GtkWidget *custom6; + GtkWidget *hbox69; + GtkWidget *label104; + GtkWidget *hbox90; + GtkWidget *encoder; + GtkWidget *edit_encoder_presets; + GtkWidget *image469; + GtkWidget *hbox86; + GtkWidget *label114; + GtkWidget *hbox91; + GtkWidget *dsp_preset; + GtkWidget *edit_dsp_presets; + GtkWidget *image470; + GtkWidget *hbox88; + GtkWidget *label116; + GtkObject *numthreads_adj; + GtkWidget *numthreads; + GtkWidget *hbox89; + GtkWidget *label117; + GtkWidget *output_format; + GtkWidget *hbox99; + GtkWidget *label121; + GtkWidget *overwrite_action; + GtkWidget *preserve_folders; + GtkWidget *hbox102; + GtkWidget *preserve_root_folder; + GtkWidget *preserve_folder_browse; + GtkWidget *dialog_action_area5; + GtkWidget *converter_cancel; + GtkWidget *converter_ok; + GtkTooltips *tooltips; + + tooltips = gtk_tooltips_new (); + + converterdlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (converterdlg), _("Converter")); + gtk_window_set_modal (GTK_WINDOW (converterdlg), TRUE); + gtk_window_set_destroy_with_parent (GTK_WINDOW (converterdlg), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (converterdlg), GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_dialog_set_has_separator (GTK_DIALOG (converterdlg), FALSE); + + dialog_vbox6 = GTK_DIALOG (converterdlg)->vbox; + gtk_widget_show (dialog_vbox6); + + vbox26 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox26); + gtk_box_pack_start (GTK_BOX (dialog_vbox6), vbox26, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox26), 12); + + hbox67 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox67); + gtk_box_pack_start (GTK_BOX (vbox26), hbox67, FALSE, TRUE, 0); + + label103 = gtk_label_new (_("Output folder:")); + gtk_widget_show (label103); + gtk_box_pack_start (GTK_BOX (hbox67), label103, FALSE, FALSE, 0); + + hbox68 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox68); + gtk_box_pack_start (GTK_BOX (hbox67), hbox68, TRUE, TRUE, 0); + + output_folder = gtk_entry_new (); + gtk_widget_show (output_folder); + gtk_box_pack_start (GTK_BOX (hbox68), output_folder, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (output_folder), 9679); + + converter_output_browse = gtk_button_new_with_mnemonic (_("...")); + gtk_widget_show (converter_output_browse); + gtk_box_pack_start (GTK_BOX (hbox68), converter_output_browse, FALSE, FALSE, 0); + + hbox100 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox100); + gtk_box_pack_start (GTK_BOX (vbox26), hbox100, TRUE, TRUE, 0); + + label122 = gtk_label_new (_("Output file name:")); + gtk_widget_show (label122); + gtk_box_pack_start (GTK_BOX (hbox100), label122, FALSE, FALSE, 0); + + hbox101 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox101); + gtk_box_pack_start (GTK_BOX (hbox100), hbox101, TRUE, TRUE, 0); + + output_file = gtk_entry_new (); + gtk_widget_show (output_file); + gtk_box_pack_start (GTK_BOX (hbox101), output_file, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, output_file, _("Extension (e.g. .mp3) will be appended automatically.\nLeave the field empty for default (%a - %t)."), NULL); + gtk_entry_set_invisible_char (GTK_ENTRY (output_file), 8226); + + custom6 = title_formatting_help_link_create ("custom6", "", "", 0, 0); + gtk_widget_show (custom6); + gtk_box_pack_start (GTK_BOX (hbox101), custom6, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (custom6, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (custom6, GTK_CAN_DEFAULT); + + hbox69 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox69); + gtk_box_pack_start (GTK_BOX (vbox26), hbox69, FALSE, FALSE, 0); + + label104 = gtk_label_new (_("Encoder:")); + gtk_widget_show (label104); + gtk_box_pack_start (GTK_BOX (hbox69), label104, FALSE, FALSE, 0); + + hbox90 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox90); + gtk_box_pack_start (GTK_BOX (hbox69), hbox90, TRUE, TRUE, 0); + + encoder = gtk_combo_box_new_text (); + gtk_widget_show (encoder); + gtk_box_pack_start (GTK_BOX (hbox90), encoder, TRUE, TRUE, 0); + + edit_encoder_presets = gtk_button_new (); + gtk_widget_show (edit_encoder_presets); + gtk_box_pack_start (GTK_BOX (hbox90), edit_encoder_presets, FALSE, FALSE, 0); + + image469 = gtk_image_new_from_stock ("gtk-edit", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image469); + gtk_container_add (GTK_CONTAINER (edit_encoder_presets), image469); + + hbox86 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox86); + gtk_box_pack_start (GTK_BOX (vbox26), hbox86, FALSE, TRUE, 0); + + label114 = gtk_label_new (_("DSP preset:")); + gtk_widget_show (label114); + gtk_box_pack_start (GTK_BOX (hbox86), label114, FALSE, FALSE, 0); + + hbox91 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox91); + gtk_box_pack_start (GTK_BOX (hbox86), hbox91, TRUE, TRUE, 0); + + dsp_preset = gtk_combo_box_new_text (); + gtk_widget_show (dsp_preset); + gtk_box_pack_start (GTK_BOX (hbox91), dsp_preset, TRUE, TRUE, 0); + + edit_dsp_presets = gtk_button_new (); + gtk_widget_show (edit_dsp_presets); + gtk_box_pack_start (GTK_BOX (hbox91), edit_dsp_presets, FALSE, FALSE, 0); + + image470 = gtk_image_new_from_stock ("gtk-edit", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image470); + gtk_container_add (GTK_CONTAINER (edit_dsp_presets), image470); + + hbox88 = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start (GTK_BOX (vbox26), hbox88, FALSE, TRUE, 0); + + label116 = gtk_label_new (_("Number of threads:")); + gtk_widget_show (label116); + gtk_box_pack_start (GTK_BOX (hbox88), label116, FALSE, FALSE, 0); + + numthreads_adj = gtk_adjustment_new (1, 0, 100, 1, 10, 0); + numthreads = gtk_spin_button_new (GTK_ADJUSTMENT (numthreads_adj), 1, 0); + gtk_widget_show (numthreads); + gtk_box_pack_start (GTK_BOX (hbox88), numthreads, TRUE, TRUE, 0); + + hbox89 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox89); + gtk_box_pack_start (GTK_BOX (vbox26), hbox89, FALSE, TRUE, 0); + + label117 = gtk_label_new (_("Output sample format:")); + gtk_widget_show (label117); + gtk_box_pack_start (GTK_BOX (hbox89), label117, FALSE, FALSE, 0); + + output_format = gtk_combo_box_new_text (); + gtk_widget_show (output_format); + gtk_box_pack_start (GTK_BOX (hbox89), output_format, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (output_format), _("Keep source format")); + gtk_combo_box_append_text (GTK_COMBO_BOX (output_format), _("8 bit signed int")); + gtk_combo_box_append_text (GTK_COMBO_BOX (output_format), _("16 bit signed int")); + gtk_combo_box_append_text (GTK_COMBO_BOX (output_format), _("24 bit signed int")); + gtk_combo_box_append_text (GTK_COMBO_BOX (output_format), _("32 bit signed int")); + gtk_combo_box_append_text (GTK_COMBO_BOX (output_format), _("32 bit float")); + + hbox99 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox99); + gtk_box_pack_start (GTK_BOX (vbox26), hbox99, TRUE, TRUE, 0); + + label121 = gtk_label_new (_("When file exists:")); + gtk_widget_show (label121); + gtk_box_pack_start (GTK_BOX (hbox99), label121, FALSE, FALSE, 0); + + overwrite_action = gtk_combo_box_new_text (); + gtk_widget_show (overwrite_action); + gtk_box_pack_start (GTK_BOX (hbox99), overwrite_action, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (overwrite_action), _("Prompt")); + gtk_combo_box_append_text (GTK_COMBO_BOX (overwrite_action), _("Overwrite")); + + preserve_folders = gtk_check_button_new_with_mnemonic (_("Preserve folder structure, starting from:")); + gtk_box_pack_start (GTK_BOX (vbox26), preserve_folders, FALSE, FALSE, 0); + + hbox102 = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox26), hbox102, TRUE, TRUE, 0); + + preserve_root_folder = gtk_entry_new (); + gtk_widget_show (preserve_root_folder); + gtk_box_pack_start (GTK_BOX (hbox102), preserve_root_folder, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (preserve_root_folder), 8226); + + preserve_folder_browse = gtk_button_new_with_mnemonic (_("...")); + gtk_widget_show (preserve_folder_browse); + gtk_box_pack_start (GTK_BOX (hbox102), preserve_folder_browse, FALSE, FALSE, 0); + + dialog_action_area5 = GTK_DIALOG (converterdlg)->action_area; + gtk_widget_show (dialog_action_area5); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area5), GTK_BUTTONBOX_END); + + converter_cancel = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (converter_cancel); + gtk_dialog_add_action_widget (GTK_DIALOG (converterdlg), converter_cancel, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (converter_cancel, GTK_CAN_DEFAULT); + + converter_ok = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (converter_ok); + gtk_dialog_add_action_widget (GTK_DIALOG (converterdlg), converter_ok, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (converter_ok, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) output_folder, "changed", + G_CALLBACK (on_output_folder_changed), + NULL); + g_signal_connect ((gpointer) converter_output_browse, "clicked", + G_CALLBACK (on_converter_output_browse_clicked), + NULL); + g_signal_connect ((gpointer) output_file, "changed", + G_CALLBACK (on_output_file_changed), + NULL); + g_signal_connect ((gpointer) encoder, "changed", + G_CALLBACK (on_converter_encoder_changed), + NULL); + g_signal_connect ((gpointer) edit_encoder_presets, "clicked", + G_CALLBACK (on_edit_encoder_presets_clicked), + NULL); + g_signal_connect ((gpointer) dsp_preset, "changed", + G_CALLBACK (on_converter_dsp_preset_changed), + NULL); + g_signal_connect ((gpointer) edit_dsp_presets, "clicked", + G_CALLBACK (on_edit_dsp_presets_clicked), + NULL); + g_signal_connect ((gpointer) numthreads, "changed", + G_CALLBACK (on_numthreads_changed), + NULL); + g_signal_connect ((gpointer) output_format, "changed", + G_CALLBACK (on_converter_output_format_changed), + NULL); + g_signal_connect ((gpointer) overwrite_action, "changed", + G_CALLBACK (on_overwrite_action_changed), + NULL); + g_signal_connect ((gpointer) preserve_folders, "toggled", + G_CALLBACK (on_preserve_folders_toggled), + NULL); + g_signal_connect ((gpointer) preserve_root_folder, "changed", + G_CALLBACK (on_preserve_root_folder_changed), + NULL); + g_signal_connect ((gpointer) preserve_folder_browse, "clicked", + G_CALLBACK (on_preserve_folder_browse_clicked), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (converterdlg, converterdlg, "converterdlg"); + GLADE_HOOKUP_OBJECT_NO_REF (converterdlg, dialog_vbox6, "dialog_vbox6"); + GLADE_HOOKUP_OBJECT (converterdlg, vbox26, "vbox26"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox67, "hbox67"); + GLADE_HOOKUP_OBJECT (converterdlg, label103, "label103"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox68, "hbox68"); + GLADE_HOOKUP_OBJECT (converterdlg, output_folder, "output_folder"); + GLADE_HOOKUP_OBJECT (converterdlg, converter_output_browse, "converter_output_browse"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox100, "hbox100"); + GLADE_HOOKUP_OBJECT (converterdlg, label122, "label122"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox101, "hbox101"); + GLADE_HOOKUP_OBJECT (converterdlg, output_file, "output_file"); + GLADE_HOOKUP_OBJECT (converterdlg, custom6, "custom6"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox69, "hbox69"); + GLADE_HOOKUP_OBJECT (converterdlg, label104, "label104"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox90, "hbox90"); + GLADE_HOOKUP_OBJECT (converterdlg, encoder, "encoder"); + GLADE_HOOKUP_OBJECT (converterdlg, edit_encoder_presets, "edit_encoder_presets"); + GLADE_HOOKUP_OBJECT (converterdlg, image469, "image469"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox86, "hbox86"); + GLADE_HOOKUP_OBJECT (converterdlg, label114, "label114"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox91, "hbox91"); + GLADE_HOOKUP_OBJECT (converterdlg, dsp_preset, "dsp_preset"); + GLADE_HOOKUP_OBJECT (converterdlg, edit_dsp_presets, "edit_dsp_presets"); + GLADE_HOOKUP_OBJECT (converterdlg, image470, "image470"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox88, "hbox88"); + GLADE_HOOKUP_OBJECT (converterdlg, label116, "label116"); + GLADE_HOOKUP_OBJECT (converterdlg, numthreads, "numthreads"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox89, "hbox89"); + GLADE_HOOKUP_OBJECT (converterdlg, label117, "label117"); + GLADE_HOOKUP_OBJECT (converterdlg, output_format, "output_format"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox99, "hbox99"); + GLADE_HOOKUP_OBJECT (converterdlg, label121, "label121"); + GLADE_HOOKUP_OBJECT (converterdlg, overwrite_action, "overwrite_action"); + GLADE_HOOKUP_OBJECT (converterdlg, preserve_folders, "preserve_folders"); + GLADE_HOOKUP_OBJECT (converterdlg, hbox102, "hbox102"); + GLADE_HOOKUP_OBJECT (converterdlg, preserve_root_folder, "preserve_root_folder"); + GLADE_HOOKUP_OBJECT (converterdlg, preserve_folder_browse, "preserve_folder_browse"); + GLADE_HOOKUP_OBJECT_NO_REF (converterdlg, dialog_action_area5, "dialog_action_area5"); + GLADE_HOOKUP_OBJECT (converterdlg, converter_cancel, "converter_cancel"); + GLADE_HOOKUP_OBJECT (converterdlg, converter_ok, "converter_ok"); + GLADE_HOOKUP_OBJECT_NO_REF (converterdlg, tooltips, "tooltips"); + + return converterdlg; +} + +GtkWidget* +create_convpreset_editor (void) +{ + GtkWidget *convpreset_editor; + GtkWidget *dialog_vbox7; + GtkWidget *vbox27; + GtkWidget *hbox70; + GtkWidget *label105; + GtkWidget *title; + GtkWidget *hbox96; + GtkWidget *label120; + GtkWidget *ext; + GtkWidget *hbox72; + GtkWidget *label106; + GtkWidget *hbox93; + GtkWidget *encoder; + GtkWidget *custom4; + GtkWidget *label124; + GtkWidget *hbox73; + GtkWidget *label107; + GtkWidget *method; + GtkWidget *frame9; + GtkWidget *alignment21; + GtkWidget *table2; + GtkWidget *apev2; + GtkWidget *id3v1; + GtkWidget *oggvorbis; + GtkWidget *flac; + GtkWidget *hbox104; + GtkWidget *id3v2; + GtkWidget *id3v2_version; + GtkWidget *label125; + GtkWidget *dialog_action_area6; + GtkWidget *convpreset_cancel; + GtkWidget *convpreset_ok; + GtkTooltips *tooltips; + + tooltips = gtk_tooltips_new (); + + convpreset_editor = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (convpreset_editor), _("Edit Encoder Preset")); + gtk_window_set_modal (GTK_WINDOW (convpreset_editor), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (convpreset_editor), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox7 = GTK_DIALOG (convpreset_editor)->vbox; + gtk_widget_show (dialog_vbox7); + + vbox27 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox27); + gtk_box_pack_start (GTK_BOX (dialog_vbox7), vbox27, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox27), 12); + + hbox70 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox70); + gtk_box_pack_start (GTK_BOX (vbox27), hbox70, FALSE, TRUE, 0); + + label105 = gtk_label_new (_("Title:")); + gtk_widget_show (label105); + gtk_box_pack_start (GTK_BOX (hbox70), label105, FALSE, FALSE, 0); + + title = gtk_entry_new (); + gtk_widget_show (title); + gtk_box_pack_start (GTK_BOX (hbox70), title, TRUE, TRUE, 0); + gtk_entry_set_text (GTK_ENTRY (title), _("Untitled Encoder")); + gtk_entry_set_invisible_char (GTK_ENTRY (title), 9679); + gtk_entry_set_activates_default (GTK_ENTRY (title), TRUE); + + hbox96 = gtk_hbox_new (FALSE, 9); + gtk_widget_show (hbox96); + gtk_box_pack_start (GTK_BOX (vbox27), hbox96, FALSE, TRUE, 0); + + label120 = gtk_label_new (_("Output file extension:")); + gtk_widget_show (label120); + gtk_box_pack_start (GTK_BOX (hbox96), label120, FALSE, FALSE, 0); + + ext = gtk_entry_new (); + gtk_widget_show (ext); + gtk_box_pack_start (GTK_BOX (hbox96), ext, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, ext, _("E.g. mp3"), NULL); + gtk_entry_set_invisible_char (GTK_ENTRY (ext), 9679); + gtk_entry_set_activates_default (GTK_ENTRY (ext), TRUE); + + hbox72 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox72); + gtk_box_pack_start (GTK_BOX (vbox27), hbox72, FALSE, TRUE, 0); + + label106 = gtk_label_new (_("Command line:")); + gtk_widget_show (label106); + gtk_box_pack_start (GTK_BOX (hbox72), label106, FALSE, FALSE, 0); + + hbox93 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox93); + gtk_box_pack_start (GTK_BOX (hbox72), hbox93, TRUE, TRUE, 0); + + encoder = gtk_entry_new (); + gtk_widget_show (encoder); + gtk_box_pack_start (GTK_BOX (hbox93), encoder, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, encoder, _("Example: lame - %o\n%i for input file, %o for output file, - for stdin"), NULL); + gtk_entry_set_invisible_char (GTK_ENTRY (encoder), 9679); + gtk_entry_set_activates_default (GTK_ENTRY (encoder), TRUE); + + custom4 = encoder_cmdline_help_link_create ("custom4", "", "", 0, 0); + gtk_widget_show (custom4); + gtk_box_pack_start (GTK_BOX (hbox93), custom4, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (custom4, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (custom4, GTK_CAN_DEFAULT); + + label124 = gtk_label_new (_("<small>%o - output file name\n%i - temporary input file name</small>")); + gtk_widget_show (label124); + gtk_box_pack_start (GTK_BOX (vbox27), label124, FALSE, FALSE, 0); + gtk_label_set_use_markup (GTK_LABEL (label124), TRUE); + + hbox73 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox73); + gtk_box_pack_start (GTK_BOX (vbox27), hbox73, FALSE, TRUE, 0); + + label107 = gtk_label_new (_("Method:")); + gtk_widget_show (label107); + gtk_box_pack_start (GTK_BOX (hbox73), label107, FALSE, FALSE, 0); + + method = gtk_combo_box_new_text (); + gtk_widget_show (method); + gtk_box_pack_start (GTK_BOX (hbox73), method, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (method), _("Pipe")); + gtk_combo_box_append_text (GTK_COMBO_BOX (method), _("Temporary file")); + + frame9 = gtk_frame_new (NULL); + gtk_widget_show (frame9); + gtk_box_pack_start (GTK_BOX (vbox27), frame9, FALSE, FALSE, 0); + + alignment21 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment21); + gtk_container_add (GTK_CONTAINER (frame9), alignment21); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment21), 0, 0, 12, 0); + + table2 = gtk_table_new (2, 3, FALSE); + gtk_widget_show (table2); + gtk_container_add (GTK_CONTAINER (alignment21), table2); + gtk_container_set_border_width (GTK_CONTAINER (table2), 8); + gtk_table_set_col_spacings (GTK_TABLE (table2), 8); + + apev2 = gtk_check_button_new_with_mnemonic (_("APEv2")); + gtk_widget_show (apev2); + gtk_table_attach (GTK_TABLE (table2), apev2, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + id3v1 = gtk_check_button_new_with_mnemonic (_("ID3v1")); + gtk_widget_show (id3v1); + gtk_table_attach (GTK_TABLE (table2), id3v1, 2, 3, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + oggvorbis = gtk_check_button_new_with_mnemonic (_("OggVorbis")); + gtk_widget_show (oggvorbis); + gtk_table_attach (GTK_TABLE (table2), oggvorbis, 2, 3, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + flac = gtk_check_button_new_with_mnemonic (_("FLAC")); + gtk_widget_show (flac); + gtk_table_attach (GTK_TABLE (table2), flac, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + hbox104 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox104); + gtk_table_attach (GTK_TABLE (table2), hbox104, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + id3v2 = gtk_check_button_new_with_mnemonic (_("ID3v2")); + gtk_widget_show (id3v2); + gtk_box_pack_start (GTK_BOX (hbox104), id3v2, FALSE, FALSE, 0); + + id3v2_version = gtk_combo_box_new_text (); + gtk_widget_show (id3v2_version); + gtk_box_pack_start (GTK_BOX (hbox104), id3v2_version, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (id3v2_version), _("2.3")); + gtk_combo_box_append_text (GTK_COMBO_BOX (id3v2_version), _("2.4")); + + label125 = gtk_label_new (_("<b>Tag writer</b>")); + gtk_widget_show (label125); + gtk_frame_set_label_widget (GTK_FRAME (frame9), label125); + gtk_label_set_use_markup (GTK_LABEL (label125), TRUE); + + dialog_action_area6 = GTK_DIALOG (convpreset_editor)->action_area; + gtk_widget_show (dialog_action_area6); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area6), GTK_BUTTONBOX_END); + + convpreset_cancel = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (convpreset_cancel); + gtk_dialog_add_action_widget (GTK_DIALOG (convpreset_editor), convpreset_cancel, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (convpreset_cancel, GTK_CAN_DEFAULT); + + convpreset_ok = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (convpreset_ok); + gtk_dialog_add_action_widget (GTK_DIALOG (convpreset_editor), convpreset_ok, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (convpreset_ok, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) encoder, "changed", + G_CALLBACK (on_encoder_changed), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (convpreset_editor, convpreset_editor, "convpreset_editor"); + GLADE_HOOKUP_OBJECT_NO_REF (convpreset_editor, dialog_vbox7, "dialog_vbox7"); + GLADE_HOOKUP_OBJECT (convpreset_editor, vbox27, "vbox27"); + GLADE_HOOKUP_OBJECT (convpreset_editor, hbox70, "hbox70"); + GLADE_HOOKUP_OBJECT (convpreset_editor, label105, "label105"); + GLADE_HOOKUP_OBJECT (convpreset_editor, title, "title"); + GLADE_HOOKUP_OBJECT (convpreset_editor, hbox96, "hbox96"); + GLADE_HOOKUP_OBJECT (convpreset_editor, label120, "label120"); + GLADE_HOOKUP_OBJECT (convpreset_editor, ext, "ext"); + GLADE_HOOKUP_OBJECT (convpreset_editor, hbox72, "hbox72"); + GLADE_HOOKUP_OBJECT (convpreset_editor, label106, "label106"); + GLADE_HOOKUP_OBJECT (convpreset_editor, hbox93, "hbox93"); + GLADE_HOOKUP_OBJECT (convpreset_editor, encoder, "encoder"); + GLADE_HOOKUP_OBJECT (convpreset_editor, custom4, "custom4"); + GLADE_HOOKUP_OBJECT (convpreset_editor, label124, "label124"); + GLADE_HOOKUP_OBJECT (convpreset_editor, hbox73, "hbox73"); + GLADE_HOOKUP_OBJECT (convpreset_editor, label107, "label107"); + GLADE_HOOKUP_OBJECT (convpreset_editor, method, "method"); + GLADE_HOOKUP_OBJECT (convpreset_editor, frame9, "frame9"); + GLADE_HOOKUP_OBJECT (convpreset_editor, alignment21, "alignment21"); + GLADE_HOOKUP_OBJECT (convpreset_editor, table2, "table2"); + GLADE_HOOKUP_OBJECT (convpreset_editor, apev2, "apev2"); + GLADE_HOOKUP_OBJECT (convpreset_editor, id3v1, "id3v1"); + GLADE_HOOKUP_OBJECT (convpreset_editor, oggvorbis, "oggvorbis"); + GLADE_HOOKUP_OBJECT (convpreset_editor, flac, "flac"); + GLADE_HOOKUP_OBJECT (convpreset_editor, hbox104, "hbox104"); + GLADE_HOOKUP_OBJECT (convpreset_editor, id3v2, "id3v2"); + GLADE_HOOKUP_OBJECT (convpreset_editor, id3v2_version, "id3v2_version"); + GLADE_HOOKUP_OBJECT (convpreset_editor, label125, "label125"); + GLADE_HOOKUP_OBJECT_NO_REF (convpreset_editor, dialog_action_area6, "dialog_action_area6"); + GLADE_HOOKUP_OBJECT (convpreset_editor, convpreset_cancel, "convpreset_cancel"); + GLADE_HOOKUP_OBJECT (convpreset_editor, convpreset_ok, "convpreset_ok"); + GLADE_HOOKUP_OBJECT_NO_REF (convpreset_editor, tooltips, "tooltips"); + + return convpreset_editor; +} + +GtkWidget* +create_dsppreset_editor (void) +{ + GtkWidget *dsppreset_editor; + GtkWidget *dialog_vbox9; + GtkWidget *vbox30; + GtkWidget *hbox81; + GtkWidget *label111; + GtkWidget *title; + GtkWidget *vbox29; + GtkWidget *hbox82; + GtkWidget *add; + GtkWidget *remove; + GtkWidget *configure; + GtkWidget *hbox98; + GtkWidget *scrolledwindow7; + GtkWidget *plugins; + GtkWidget *vbox34; + GtkWidget *up; + GtkWidget *down; + GtkWidget *dialog_action_area8; + GtkWidget *cancelbutton6; + GtkWidget *okbutton6; + + dsppreset_editor = gtk_dialog_new (); + gtk_widget_set_size_request (dsppreset_editor, 468, 254); + gtk_window_set_title (GTK_WINDOW (dsppreset_editor), _("DSP Preset Editor")); + gtk_window_set_modal (GTK_WINDOW (dsppreset_editor), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (dsppreset_editor), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox9 = GTK_DIALOG (dsppreset_editor)->vbox; + gtk_widget_show (dialog_vbox9); + + vbox30 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox30); + gtk_box_pack_start (GTK_BOX (dialog_vbox9), vbox30, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox30), 12); + + hbox81 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox81); + gtk_box_pack_start (GTK_BOX (vbox30), hbox81, FALSE, TRUE, 0); + + label111 = gtk_label_new (_("Title")); + gtk_widget_show (label111); + gtk_box_pack_start (GTK_BOX (hbox81), label111, FALSE, FALSE, 0); + + title = gtk_entry_new (); + gtk_widget_show (title); + gtk_box_pack_start (GTK_BOX (hbox81), title, TRUE, TRUE, 0); + gtk_entry_set_text (GTK_ENTRY (title), _("Untitled DSP Preset")); + gtk_entry_set_invisible_char (GTK_ENTRY (title), 9679); + gtk_entry_set_activates_default (GTK_ENTRY (title), TRUE); + + vbox29 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox29); + gtk_box_pack_start (GTK_BOX (vbox30), vbox29, TRUE, TRUE, 0); + + hbox82 = gtk_hbox_new (TRUE, 8); + gtk_widget_show (hbox82); + gtk_box_pack_start (GTK_BOX (vbox29), hbox82, FALSE, TRUE, 0); + + add = gtk_button_new_with_mnemonic (_("Add")); + gtk_widget_show (add); + gtk_box_pack_start (GTK_BOX (hbox82), add, TRUE, TRUE, 0); + + remove = gtk_button_new_with_mnemonic (_("Remove")); + gtk_widget_show (remove); + gtk_box_pack_start (GTK_BOX (hbox82), remove, TRUE, TRUE, 0); + + configure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (configure); + gtk_box_pack_start (GTK_BOX (hbox82), configure, TRUE, TRUE, 0); + + hbox98 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox98); + gtk_box_pack_start (GTK_BOX (vbox29), hbox98, TRUE, TRUE, 0); + + scrolledwindow7 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow7); + gtk_box_pack_start (GTK_BOX (hbox98), scrolledwindow7, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow7), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow7), GTK_SHADOW_IN); + + plugins = gtk_tree_view_new (); + gtk_widget_show (plugins); + gtk_container_add (GTK_CONTAINER (scrolledwindow7), plugins); + gtk_widget_set_size_request (plugins, 196, -1); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (plugins), FALSE); + + vbox34 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox34); + gtk_box_pack_start (GTK_BOX (hbox98), vbox34, FALSE, FALSE, 0); + + up = gtk_button_new_from_stock ("gtk-go-up"); + gtk_widget_show (up); + gtk_box_pack_start (GTK_BOX (vbox34), up, FALSE, FALSE, 0); + + down = gtk_button_new_from_stock ("gtk-go-down"); + gtk_widget_show (down); + gtk_box_pack_start (GTK_BOX (vbox34), down, FALSE, FALSE, 0); + + dialog_action_area8 = GTK_DIALOG (dsppreset_editor)->action_area; + gtk_widget_show (dialog_action_area8); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area8), GTK_BUTTONBOX_END); + + cancelbutton6 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancelbutton6); + gtk_dialog_add_action_widget (GTK_DIALOG (dsppreset_editor), cancelbutton6, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (cancelbutton6, GTK_CAN_DEFAULT); + + okbutton6 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (okbutton6); + gtk_dialog_add_action_widget (GTK_DIALOG (dsppreset_editor), okbutton6, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (okbutton6, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) add, "clicked", + G_CALLBACK (on_dsp_preset_add_plugin_clicked), + NULL); + g_signal_connect ((gpointer) remove, "clicked", + G_CALLBACK (on_dsp_preset_remove_plugin_clicked), + NULL); + g_signal_connect ((gpointer) configure, "clicked", + G_CALLBACK (on_dsp_preset_plugin_configure_clicked), + NULL); + g_signal_connect ((gpointer) up, "clicked", + G_CALLBACK (on_dsp_preset_plugin_up_clicked), + NULL); + g_signal_connect ((gpointer) down, "clicked", + G_CALLBACK (on_dsp_preset_plugin_down_clicked), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (dsppreset_editor, dsppreset_editor, "dsppreset_editor"); + GLADE_HOOKUP_OBJECT_NO_REF (dsppreset_editor, dialog_vbox9, "dialog_vbox9"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, vbox30, "vbox30"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, hbox81, "hbox81"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, label111, "label111"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, title, "title"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, vbox29, "vbox29"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, hbox82, "hbox82"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, add, "add"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, remove, "remove"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, configure, "configure"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, hbox98, "hbox98"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, scrolledwindow7, "scrolledwindow7"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, plugins, "plugins"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, vbox34, "vbox34"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, up, "up"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, down, "down"); + GLADE_HOOKUP_OBJECT_NO_REF (dsppreset_editor, dialog_action_area8, "dialog_action_area8"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, cancelbutton6, "cancelbutton6"); + GLADE_HOOKUP_OBJECT (dsppreset_editor, okbutton6, "okbutton6"); + + return dsppreset_editor; +} + +GtkWidget* +create_select_dsp_plugin (void) +{ + GtkWidget *select_dsp_plugin; + GtkWidget *dialog_vbox10; + GtkWidget *vbox31; + GtkWidget *hbox85; + GtkWidget *label113; + GtkWidget *plugin; + GtkWidget *dialog_action_area9; + GtkWidget *cancelbutton7; + GtkWidget *okbutton7; + + select_dsp_plugin = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (select_dsp_plugin), _("Select DSP Plugin")); + gtk_window_set_modal (GTK_WINDOW (select_dsp_plugin), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (select_dsp_plugin), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox10 = GTK_DIALOG (select_dsp_plugin)->vbox; + gtk_widget_show (dialog_vbox10); + + vbox31 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox31); + gtk_box_pack_start (GTK_BOX (dialog_vbox10), vbox31, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox31), 12); + + hbox85 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox85); + gtk_box_pack_start (GTK_BOX (vbox31), hbox85, FALSE, FALSE, 0); + + label113 = gtk_label_new (_("Plugin")); + gtk_widget_show (label113); + gtk_box_pack_start (GTK_BOX (hbox85), label113, FALSE, FALSE, 0); + + plugin = gtk_combo_box_new_text (); + gtk_widget_show (plugin); + gtk_box_pack_start (GTK_BOX (hbox85), plugin, TRUE, TRUE, 0); + + dialog_action_area9 = GTK_DIALOG (select_dsp_plugin)->action_area; + gtk_widget_show (dialog_action_area9); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area9), GTK_BUTTONBOX_END); + + cancelbutton7 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancelbutton7); + gtk_dialog_add_action_widget (GTK_DIALOG (select_dsp_plugin), cancelbutton7, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (cancelbutton7, GTK_CAN_DEFAULT); + + okbutton7 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (okbutton7); + gtk_dialog_add_action_widget (GTK_DIALOG (select_dsp_plugin), okbutton7, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (okbutton7, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (select_dsp_plugin, select_dsp_plugin, "select_dsp_plugin"); + GLADE_HOOKUP_OBJECT_NO_REF (select_dsp_plugin, dialog_vbox10, "dialog_vbox10"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, vbox31, "vbox31"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, hbox85, "hbox85"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, label113, "label113"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, plugin, "plugin"); + GLADE_HOOKUP_OBJECT_NO_REF (select_dsp_plugin, dialog_action_area9, "dialog_action_area9"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, cancelbutton7, "cancelbutton7"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, okbutton7, "okbutton7"); + + return select_dsp_plugin; +} + +GtkWidget* +create_preset_list (void) +{ + GtkWidget *preset_list; + GtkWidget *dialog_vbox11; + GtkWidget *vbox33; + GtkWidget *hbox94; + GtkWidget *add; + GtkWidget *remove; + GtkWidget *edit; + GtkWidget *scrolledwindow8; + GtkWidget *presets; + GtkWidget *dialog_action_area10; + GtkWidget *okbutton8; + + preset_list = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (preset_list), _("Presets")); + gtk_window_set_modal (GTK_WINDOW (preset_list), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (preset_list), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox11 = GTK_DIALOG (preset_list)->vbox; + gtk_widget_show (dialog_vbox11); + + vbox33 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox33); + gtk_box_pack_start (GTK_BOX (dialog_vbox11), vbox33, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox33), 12); + + hbox94 = gtk_hbox_new (TRUE, 8); + gtk_widget_show (hbox94); + gtk_box_pack_start (GTK_BOX (vbox33), hbox94, FALSE, TRUE, 0); + + add = gtk_button_new_from_stock ("gtk-add"); + gtk_widget_show (add); + gtk_box_pack_start (GTK_BOX (hbox94), add, FALSE, TRUE, 0); + + remove = gtk_button_new_from_stock ("gtk-remove"); + gtk_widget_show (remove); + gtk_box_pack_start (GTK_BOX (hbox94), remove, FALSE, TRUE, 0); + + edit = gtk_button_new_from_stock ("gtk-edit"); + gtk_widget_show (edit); + gtk_box_pack_start (GTK_BOX (hbox94), edit, FALSE, TRUE, 0); + + scrolledwindow8 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow8); + gtk_box_pack_start (GTK_BOX (vbox33), scrolledwindow8, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow8), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow8), GTK_SHADOW_IN); + + presets = gtk_tree_view_new (); + gtk_widget_show (presets); + gtk_container_add (GTK_CONTAINER (scrolledwindow8), presets); + gtk_widget_set_size_request (presets, 400, 176); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (presets), FALSE); + + dialog_action_area10 = GTK_DIALOG (preset_list)->action_area; + gtk_widget_show (dialog_action_area10); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area10), GTK_BUTTONBOX_END); + + okbutton8 = gtk_button_new_from_stock ("gtk-close"); + gtk_widget_show (okbutton8); + gtk_dialog_add_action_widget (GTK_DIALOG (preset_list), okbutton8, GTK_RESPONSE_CLOSE); + GTK_WIDGET_SET_FLAGS (okbutton8, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (preset_list, preset_list, "preset_list"); + GLADE_HOOKUP_OBJECT_NO_REF (preset_list, dialog_vbox11, "dialog_vbox11"); + GLADE_HOOKUP_OBJECT (preset_list, vbox33, "vbox33"); + GLADE_HOOKUP_OBJECT (preset_list, hbox94, "hbox94"); + GLADE_HOOKUP_OBJECT (preset_list, add, "add"); + GLADE_HOOKUP_OBJECT (preset_list, remove, "remove"); + GLADE_HOOKUP_OBJECT (preset_list, edit, "edit"); + GLADE_HOOKUP_OBJECT (preset_list, scrolledwindow8, "scrolledwindow8"); + GLADE_HOOKUP_OBJECT (preset_list, presets, "presets"); + GLADE_HOOKUP_OBJECT_NO_REF (preset_list, dialog_action_area10, "dialog_action_area10"); + GLADE_HOOKUP_OBJECT (preset_list, okbutton8, "okbutton8"); + + return preset_list; +} + diff --git a/plugins/converter/interface.h b/plugins/converter/interface.h new file mode 100644 index 00000000..346f63af --- /dev/null +++ b/plugins/converter/interface.h @@ -0,0 +1,9 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_converterdlg (void); +GtkWidget* create_convpreset_editor (void); +GtkWidget* create_dsppreset_editor (void); +GtkWidget* create_select_dsp_plugin (void); +GtkWidget* create_preset_list (void); diff --git a/plugins/converter/support.c b/plugins/converter/support.c new file mode 100644 index 00000000..00aff298 --- /dev/null +++ b/plugins/converter/support.c @@ -0,0 +1,144 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#include <gtk/gtk.h> + +#include "support.h" + +GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (!parent) + parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey"); + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +static GList *pixmaps_directories = NULL; + +/* Use this function to set the directory containing installed pixmaps. */ +void +add_pixmap_directory (const gchar *directory) +{ + pixmaps_directories = g_list_prepend (pixmaps_directories, + g_strdup (directory)); +} + +/* This is an internally used function to find pixmap files. */ +static gchar* +find_pixmap_file (const gchar *filename) +{ + GList *elem; + + /* We step through each of the pixmaps directory to find it. */ + elem = pixmaps_directories; + while (elem) + { + gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, + G_DIR_SEPARATOR_S, filename); + if (g_file_test (pathname, G_FILE_TEST_EXISTS)) + return pathname; + g_free (pathname); + elem = elem->next; + } + return NULL; +} + +/* This is an internally used function to create pixmaps. */ +GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename) +{ + gchar *pathname = NULL; + GtkWidget *pixmap; + + if (!filename || !filename[0]) + return gtk_image_new (); + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return gtk_image_new (); + } + + pixmap = gtk_image_new_from_file (pathname); + g_free (pathname); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GdkPixbuf* +create_pixbuf (const gchar *filename) +{ + gchar *pathname = NULL; + GdkPixbuf *pixbuf; + GError *error = NULL; + + if (!filename || !filename[0]) + return NULL; + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning (_("Couldn't find pixmap file: %s"), filename); + return NULL; + } + + pixbuf = gdk_pixbuf_new_from_file (pathname, &error); + if (!pixbuf) + { + fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", + pathname, error->message); + g_error_free (error); + } + g_free (pathname); + return pixbuf; +} + +/* This is used to set ATK action descriptions. */ +void +glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description) +{ + gint n_actions, i; + + n_actions = atk_action_get_n_actions (action); + for (i = 0; i < n_actions; i++) + { + if (!strcmp (atk_action_get_name (action, i), action_name)) + atk_action_set_description (action, i, description); + } +} + diff --git a/plugins/converter/support.h b/plugins/converter/support.h new file mode 100644 index 00000000..a32649e5 --- /dev/null +++ b/plugins/converter/support.h @@ -0,0 +1,69 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gtk/gtk.h> + +/* + * Standard gettext macros. + */ +#ifdef ENABLE_NLS +# include <libintl.h> +# undef _ +# define _(String) dgettext (PACKAGE, String) +# define Q_(String) g_strip_context ((String), gettext (String)) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define textdomain(String) (String) +# define gettext(String) (String) +# define dgettext(Domain,Message) (Message) +# define dcgettext(Domain,Message,Type) (Message) +# define bindtextdomain(Domain,Directory) (Domain) +# define _(String) (String) +# define Q_(String) g_strip_context ((String), (String)) +# define N_(String) (String) +#endif + + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + + +/* Use this function to set the directory containing installed pixmaps. */ +void add_pixmap_directory (const gchar *directory); + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps used in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename); + +/* This is used to create the pixbufs used in the interface. */ +GdkPixbuf* create_pixbuf (const gchar *filename); + +/* This is used to set ATK action descriptions. */ +void glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description); + diff --git a/plugins/dca/Makefile.am b/plugins/dca/Makefile.am index 0ae4734c..9819c5aa 100644 --- a/plugins/dca/Makefile.am +++ b/plugins/dca/Makefile.am @@ -8,7 +8,6 @@ gettimeofday.c\ parse.c\ bitstream.c\ downmix.c\ -convert2s16.c\ audio_out.h\ dca.h\ dts.h\ @@ -26,6 +25,6 @@ bitstream.h dca_la_LDFLAGS = -module dca_la_LIBADD = $(LDADD) -lm -AM_CFLAGS = $(CFLAGS) -fPIC +AM_CFLAGS = $(CFLAGS) -fPIC -std=c99 endif diff --git a/plugins/dca/convert2s16.c b/plugins/dca/convert2s16.c deleted file mode 100644 index b0647eae..00000000 --- a/plugins/dca/convert2s16.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * convert2s16.c - * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> - * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> - * - * This file is part of a52dec, a free ATSC A-52 stream decoder. - * See http://liba52.sourceforge.net/ for updates. - * - * a52dec is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * a52dec is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -#include <inttypes.h> - -#include "dca.h" - -#include <stdio.h> - -#ifdef LIBDCA_DOUBLE -typedef float convert_t; -#else -typedef sample_t convert_t; -#endif - -static inline int16_t convert (int32_t i) -{ -#ifdef LIBDCA_FIXED - i >>= 15; -#else - i -= 0x43c00000; -#endif - return (i > 32767) ? 32767 : ((i < -32768) ? -32768 : i); -} - -void convert2s16_1 (convert_t * _f, int16_t * s16) -{ - int i; - int32_t * f = (int32_t *) _f; - - for (i = 0; i < 256; i++) { - s16[i] = convert (f[i]); - } -} - -void convert2s16_2 (convert_t * _f, int16_t * s16) -{ - int i; - int32_t * f = (int32_t *) _f; - - for (i = 0; i < 256; i++) { - s16[2*i] = convert (f[i]); - s16[2*i+1] = convert (f[i+256]); - } -} - -void convert2s16_3 (convert_t * _f, int16_t * s16) -{ - int i; - int32_t * f = (int32_t *) _f; - - for (i = 0; i < 256; i++) { - s16[3*i] = convert (f[i]); - s16[3*i+1] = convert (f[i+256]); - s16[3*i+2] = convert (f[i+512]); - } -} - -void convert2s16_4 (convert_t * _f, int16_t * s16) -{ - int i; - int32_t * f = (int32_t *) _f; - - for (i = 0; i < 256; i++) { - s16[4*i] = convert (f[i]); - s16[4*i+1] = convert (f[i+256]); - s16[4*i+2] = convert (f[i+512]); - s16[4*i+3] = convert (f[i+768]); - } -} - -void convert2s16_5 (convert_t * _f, int16_t * s16) -{ - int i; - int32_t * f = (int32_t *) _f; - - for (i = 0; i < 256; i++) { - s16[5*i] = convert (f[i]); - s16[5*i+1] = convert (f[i+256]); - s16[5*i+2] = convert (f[i+512]); - s16[5*i+3] = convert (f[i+768]); - s16[5*i+4] = convert (f[i+1024]); - } -} - -int channels_multi (int flags) -{ - if (flags & DCA_LFE) - return 6; - else if (flags & 1) /* center channel */ - return 5; - else if ((flags & DCA_CHANNEL_MASK) == DCA_2F2R) - return 4; - else - return 2; -} - -void convert2s16_multi (convert_t * _f, int16_t * s16, int flags) -{ - int i; - int32_t * f = (int32_t *) _f; - - switch (flags) { - case DCA_MONO: - for (i = 0; i < 256; i++) { - s16[5*i] = s16[5*i+1] = s16[5*i+2] = s16[5*i+3] = 0; - s16[5*i+4] = convert (f[i]); - } - break; - case DCA_CHANNEL: - case DCA_STEREO: - case DCA_DOLBY: - convert2s16_2 (_f, s16); - break; - case DCA_3F: - for (i = 0; i < 256; i++) { - s16[5*i] = convert (f[i]); - s16[5*i+1] = convert (f[i+512]); - s16[5*i+2] = s16[5*i+3] = 0; - s16[5*i+4] = convert (f[i+256]); - } - break; - case DCA_2F2R: - convert2s16_4 (_f, s16); - break; - case DCA_3F2R: - convert2s16_5 (_f, s16); - break; - case DCA_MONO | DCA_LFE: - for (i = 0; i < 256; i++) { - s16[6*i] = s16[6*i+1] = s16[6*i+2] = s16[6*i+3] = 0; - s16[6*i+4] = convert (f[i+256]); - s16[6*i+5] = convert (f[i]); - } - break; - case DCA_CHANNEL | DCA_LFE: - case DCA_STEREO | DCA_LFE: - case DCA_DOLBY | DCA_LFE: - for (i = 0; i < 256; i++) { - s16[6*i] = convert (f[i+256]); - s16[6*i+1] = convert (f[i+512]); - s16[6*i+2] = s16[6*i+3] = s16[6*i+4] = 0; - s16[6*i+5] = convert (f[i]); - } - break; - case DCA_3F | DCA_LFE: - for (i = 0; i < 256; i++) { - s16[6*i] = convert (f[i+256]); - s16[6*i+1] = convert (f[i+768]); - s16[6*i+2] = s16[6*i+3] = 0; - s16[6*i+4] = convert (f[i+512]); - s16[6*i+5] = convert (f[i]); - } - break; - case DCA_2F2R | DCA_LFE: - for (i = 0; i < 256; i++) { - s16[6*i] = convert (f[i+256]); - s16[6*i+1] = convert (f[i+512]); - s16[6*i+2] = convert (f[i+768]); - s16[6*i+3] = convert (f[i+1024]); - s16[6*i+4] = 0; - s16[6*i+5] = convert (f[i]); - } - break; - case DCA_3F2R | DCA_LFE: - for (i = 0; i < 256; i++) { - s16[6*i] = convert (f[i+256]); - s16[6*i+1] = convert (f[i+768]); - s16[6*i+2] = convert (f[i+1024]); - s16[6*i+3] = convert (f[i+1280]); - s16[6*i+4] = convert (f[i+512]); - s16[6*i+5] = convert (f[i]); - } - break; - } -} - -void s16_swap (int16_t * s16, int channels) -{ - int i; - uint16_t * u16 = (uint16_t *) s16; - - for (i = 0; i < 256 * channels; i++) - u16[i] = (u16[i] >> 8) | (u16[i] << 8); -} - -void s32_swap (int32_t * s32, int channels) -{ - int i; - uint32_t * u32 = (uint32_t *) s32; - - for (i = 0; i < 256 * channels; i++) - u32[i] = (u32[i] << 24) | ((u32[i] << 8)&0xFF0000) | - ((u32[i] >> 8)&0xFF00) | (u32[i] >> 24); -} diff --git a/plugins/dca/dcaplug.c b/plugins/dca/dcaplug.c index 9d7f251f..84a876ab 100644 --- a/plugins/dca/dcaplug.c +++ b/plugins/dca/dcaplug.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -73,7 +73,7 @@ static DB_decoder_t plugin; DB_functions_t *deadbeef; #define BUFFER_SIZE 24576 -#define OUT_BUFFER_SIZE 100000 // one block may be up to 22K samples, which is 88Kb for stereo +#define OUT_BUFFER_SIZE 25000 // one block may be up to 22K samples, which is 88Kb for stereo #define HEADER_SIZE 14 typedef struct { DB_fileinfo_t info; @@ -82,7 +82,6 @@ typedef struct { int startsample; int endsample; int currentsample; - int wavchannels; dca_state_t * state; int disable_adjust;// = 0; float gain;// = 1; @@ -95,7 +94,7 @@ typedef struct { int flags; int bit_rate; int frame_byte_size; - char output_buffer[OUT_BUFFER_SIZE]; + int16_t output_buffer[OUT_BUFFER_SIZE*6]; int remaining; int skipsamples; } ddb_dca_state_t; @@ -157,32 +156,35 @@ static int wav_channels (int flags, uint32_t * speaker_flags) return chans; } +static inline int16_t convert (int32_t i) +{ +#ifdef LIBDCA_FIXED + i >>= 15; +#else + i -= 0x43c00000; +#endif + return (i > 32767) ? 32767 : ((i < -32768) ? -32768 : i); +} + static int convert_samples (ddb_dca_state_t *state, int flags) { sample_t *_samples = dca_samples (state->state); - int chans, size; - uint32_t speaker_flags; - int16_t int16_samples[256*6]; - convert_t * samples = _samples; - chans = channels_multi (flags); - flags &= DCA_CHANNEL_MASK | DCA_LFE; + int samplesize = state->info.fmt.channels * state->info.fmt.bps / 8; - convert2s16_multi (samples, int16_samples, flags); + int n, i, c; + n = 256; + int16_t *dst = state->output_buffer + state->remaining * state->info.fmt.channels; - int16_t *dest = (int16_t*)(state->output_buffer + state->remaining * sizeof (int16_t) * 2); - int i; - for (i = 0; i < 256; i++) { - *dest = int16_samples[i * chans + 0]; - dest++; - *dest = int16_samples[i * chans + 1]; - dest++; + for (i = 0; i < n; i++) { + for (c = 0; c < state->info.fmt.channels; c++) { + *dst++ = convert (*((int32_t*)(_samples + 256 * c))); + } + _samples ++; } - state->remaining += 256; - //trace ("wrote %d bytes (chans=%d)\n", size, chans); - //fwrite (&ordered_samples, 1, size, out); + state->remaining += 256; return 0; } @@ -318,7 +320,7 @@ dts_open_wav (DB_FILE *fp, wavfmt_t *fmt, int64_t *totalsamples) { return -1; } - deadbeef->fseek (fp, fmtsize - sizeof (wavfmt_t), SEEK_CUR); + deadbeef->fseek (fp, (int)fmtsize - (int)sizeof (wavfmt_t), SEEK_CUR); // data subchunk @@ -343,7 +345,7 @@ dts_open_wav (DB_FILE *fp, wavfmt_t *fmt, int64_t *totalsamples) { } static DB_fileinfo_t * -dts_open (void) { +dts_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (ddb_dca_state_t)); ddb_dca_state_t *info = (ddb_dca_state_t *)_info; memset (info, 0, sizeof (ddb_dca_state_t)); @@ -354,9 +356,9 @@ static int dts_init (DB_fileinfo_t *_info, DB_playItem_t *it) { ddb_dca_state_t *info = (ddb_dca_state_t *)_info; - info->file = deadbeef->fopen (it->fname); + info->file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->file) { - trace ("dca: failed to open %s\n", it->fname); + trace ("dca: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } @@ -364,17 +366,15 @@ dts_init (DB_fileinfo_t *_info, DB_playItem_t *it) { int64_t totalsamples = -1; // WAV format if ((info->offset = dts_open_wav (info->file, &fmt, &totalsamples)) == -1) { - // try raw DTS @ 48KHz + // raw dts, leave detection to libdca info->offset = 0; totalsamples = -1; - info->wavchannels = 2; - _info->bps = 16; + _info->fmt.bps = 16; } else { - _info->bps = fmt.wBitsPerSample; - _info->channels = fmt.nChannels; - info->wavchannels = fmt.nChannels; - _info->samplerate = fmt.nSamplesPerSec; + _info->fmt.bps = fmt.wBitsPerSample; + _info->fmt.channels = fmt.nChannels; + _info->fmt.samplerate = fmt.nSamplesPerSec; } _info->plugin = &plugin; @@ -400,40 +400,48 @@ dts_init (DB_fileinfo_t *_info, DB_playItem_t *it) { int flags = info->flags &~ (DCA_LFE | DCA_ADJUST_LEVEL); switch (flags) { case DCA_MONO: - _info->channels = 1; + _info->fmt.channels = 1; + _info->fmt.channelmask = DDB_SPEAKER_FRONT_LEFT; break; case DCA_CHANNEL: case DCA_STEREO: case DCA_DOLBY: case DCA_STEREO_SUMDIFF: case DCA_STEREO_TOTAL: - _info->channels = 2; + _info->fmt.channels = 2; + _info->fmt.channelmask = (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); break; case DCA_3F: case DCA_2F1R: - _info->channels = 3; + _info->fmt.channels = 3; + _info->fmt.channelmask = (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_FRONT_CENTER); break; case DCA_2F2R: case DCA_3F1R: - _info->channels = 4; + _info->fmt.channels = 4; + _info->fmt.channelmask = (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT); break; case DCA_3F2R: - _info->channels = 5; + _info->fmt.channels = 5; + _info->fmt.channelmask = (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER); break; case DCA_4F2R: - _info->channels = 6; + _info->fmt.channels = 6; + _info->fmt.channelmask = (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_SIDE_LEFT | DDB_SPEAKER_SIDE_RIGHT); break; } if (info->flags & DCA_LFE) { - _info->channels++; + _info->fmt.channelmask |= DDB_SPEAKER_LOW_FREQUENCY; + _info->fmt.channels++; } - if (!_info->channels) { + + if (!_info->fmt.channels) { trace ("dts: invalid numchannels\n"); return -1; } - _info->samplerate = info->sample_rate; + _info->fmt.samplerate = info->sample_rate; if (it->endsample > 0) { info->startsample = it->startsample; @@ -445,7 +453,7 @@ dts_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->endsample = totalsamples-1; } - trace ("dca_init: nchannels: %d, samplerate: %d\n", _info->channels, _info->samplerate); + trace ("dca_init: nchannels: %d, samplerate: %d\n", _info->fmt.channels, _info->fmt.samplerate); return 0; } @@ -464,11 +472,12 @@ dts_free (DB_fileinfo_t *_info) { } static int -dts_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +dts_read (DB_fileinfo_t *_info, char *bytes, int size) { ddb_dca_state_t *info = (ddb_dca_state_t *)_info; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; if (info->endsample >= 0) { - if (info->currentsample + size / (2 * _info->channels) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * _info->channels; + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { return 0; } @@ -476,30 +485,25 @@ dts_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } int initsize = size; - int out_channels = _info->channels; - if (out_channels > 2) { - out_channels = 2; - } - int sample_size = ((_info->bps >> 3) * out_channels); while (size > 0) { if (info->skipsamples > 0 && info->remaining > 0) { int skip = min (info->remaining, info->skipsamples); - int sample_size = _info->bps/8 * info->wavchannels; if (skip < info->remaining) { - memmove (info->output_buffer, info->output_buffer + skip * sample_size, (info->remaining - skip) * sample_size); + memmove (info->output_buffer, info->output_buffer + skip * _info->fmt.channels, (info->remaining - skip) * samplesize); } info->remaining -= skip; info->skipsamples -= skip; } if (info->remaining > 0) { - int n = size / sample_size; + int n = size / samplesize; n = min (n, info->remaining); - memcpy (bytes, info->output_buffer, n * sample_size); + memcpy (bytes, info->output_buffer, n * samplesize); + if (info->remaining > n) { - memmove (info->output_buffer, info->output_buffer + n * sample_size, (info->remaining - n) * sample_size); + memmove (info->output_buffer, info->output_buffer + n * _info->fmt.channels, (info->remaining - n) * samplesize); } - bytes += n * sample_size; - size -= n * sample_size; + bytes += n * samplesize; + size -= n * samplesize; info->remaining -= n; // trace ("dca: write %d samples\n", n); } @@ -514,7 +518,7 @@ dts_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } } - info->currentsample += (initsize-size) / sample_size; + info->currentsample += (initsize-size) / samplesize; deadbeef->streamer_set_bitrate (info->bit_rate/1000); return initsize-size; } @@ -532,14 +536,14 @@ dts_seek_sample (DB_fileinfo_t *_info, int sample) { info->skipsamples = sample - nframe * info->frame_length; info->currentsample = sample; - _info->readpos = (float)(sample - info->startsample) / _info->samplerate; + _info->readpos = (float)(sample - info->startsample) / _info->fmt.samplerate; return 0; } static int dts_seek (DB_fileinfo_t *_info, float time) { ddb_dca_state_t *info = (ddb_dca_state_t *)_info; - return dts_seek_sample (_info, time * _info->samplerate); + return dts_seek_sample (_info, time * _info->fmt.samplerate); } static DB_playItem_t * @@ -576,6 +580,7 @@ dts_insert (DB_playItem_t *after, const char *fname) { // it's dts uint8_t buffer[BUFFER_SIZE]; size_t size = deadbeef->fread (buffer, 1, sizeof (buffer), fp); + trace ("got size: %d (requested %d)\n", size, sizeof (buffer)); ddb_dca_state_t state; memset (&state, 0, sizeof (state)); state.state = dca_init (0); @@ -602,10 +607,8 @@ dts_insert (DB_playItem_t *after, const char *fname) { dur = (float)totalsamples / state.sample_rate; } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = filetype; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", filetype); deadbeef->pl_set_item_duration (it, dur); deadbeef->fclose (fp); @@ -644,22 +647,39 @@ dts_stop (void) { // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "dts", .plugin.name = "dts decoder", - .plugin.descr = "dts decoder using libdca from VLC project", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.descr = "plays dts-encoded files using libdca from VLC project", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified libdca from VLC Player project,\n" + "developed by Gildas Bazin <gbazin@videolan.org>" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = dts_start, .plugin.stop = dts_stop, .open = dts_open, .init = dts_init, .free = dts_free, - .read_int16 = dts_read_int16, -// .read_float32 = dts_read_float32, + .read = dts_read, .seek = dts_seek, .seek_sample = dts_seek_sample, .insert = dts_insert, diff --git a/plugins/dsp_libsrc/Makefile.am b/plugins/dsp_libsrc/Makefile.am new file mode 100644 index 00000000..42c6a347 --- /dev/null +++ b/plugins/dsp_libsrc/Makefile.am @@ -0,0 +1,12 @@ +if HAVE_DSP_SRC +pkglib_LTLIBRARIES = dsp_libsrc.la + +dsp_libsrc_la_SOURCES = src.c src.h + +dsp_libsrc_la_LDFLAGS = -module + +dsp_libsrc_la_LIBADD = $(LIBADD) $(LIBSAMPLERATE_DEPS_LIBS) + +dsp_libsrc_la_CFLAGS = $(CFLAGS) $(LIBSAMPLERATE_DEPS_CFLAGS) -std=c99 + +endif diff --git a/plugins/dsp_libsrc/src.c b/plugins/dsp_libsrc/src.c new file mode 100644 index 00000000..be9c350e --- /dev/null +++ b/plugins/dsp_libsrc/src.c @@ -0,0 +1,286 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <samplerate.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "conf.h" +#include "threading.h" +#include "deadbeef.h" +#include "src.h" + +//#define trace(...) { fprintf(stderr, __VA_ARGS__); } +#define trace(fmt,...) + +static DB_functions_t *deadbeef; + +#define SRC_BUFFER 16000 +#define SRC_MAX_CHANNELS 8 + +static DB_dsp_t plugin; + +typedef struct { + ddb_dsp_context_t ctx; + + int channels; + int quality; + float samplerate; + SRC_STATE *src; + SRC_DATA srcdata; + int remaining; // number of input samples in SRC buffer + __attribute__((__aligned__(16))) char in_fbuffer[sizeof(float)*SRC_BUFFER*SRC_MAX_CHANNELS]; + unsigned quality_changed : 1; + unsigned need_reset : 1; +} ddb_src_libsamplerate_t; + +ddb_dsp_context_t* +ddb_src_open (void) { + ddb_src_libsamplerate_t *src = malloc (sizeof (ddb_src_libsamplerate_t)); + DDB_INIT_DSP_CONTEXT (src,ddb_src_libsamplerate_t,&plugin); + + src->samplerate = 44100; + src->quality = 2; + src->channels = -1; + return (ddb_dsp_context_t *)src; +} + +void +ddb_src_close (ddb_dsp_context_t *_src) { + ddb_src_libsamplerate_t *src = (ddb_src_libsamplerate_t*)_src; + if (src->src) { + src_delete (src->src); + src->src = NULL; + } + free (src); +} + +void +ddb_src_reset (ddb_dsp_context_t *_src) { + ddb_src_libsamplerate_t *src = (ddb_src_libsamplerate_t*)_src; + src->need_reset = 1; +} + + +void +ddb_src_set_ratio (ddb_dsp_context_t *_src, float ratio) { + ddb_src_libsamplerate_t *src = (ddb_src_libsamplerate_t*)_src; + if (src->srcdata.src_ratio != ratio) { + src->srcdata.src_ratio = ratio; + src_set_ratio (src->src, ratio); + } +} + +int +ddb_src_process (ddb_dsp_context_t *_src, float *samples, int nframes, int maxframes, ddb_waveformat_t *fmt, float *r) { + ddb_src_libsamplerate_t *src = (ddb_src_libsamplerate_t*)_src; + + if (fmt->samplerate == src->samplerate) { + return nframes; + } + + if (src->need_reset || src->channels != fmt->channels || src->quality_changed || !src->src) { + src->quality_changed = 0; + src->remaining = 0; + if (src->src) { + src_delete (src->src); + src->src = NULL; + } + src->channels = fmt->channels; + src->src = src_new (src->quality, src->channels, NULL); + src->need_reset = 0; + } + + float ratio = src->samplerate / fmt->samplerate; + ddb_src_set_ratio (_src, ratio); + fmt->samplerate = src->samplerate; + + int numoutframes = 0; + int outsize = nframes*24; + float outbuf[outsize*fmt->channels]; + memset (outbuf, 0, sizeof (outbuf)); + char *output = (char *)outbuf; + float *input = samples; + int inputsize = nframes; + + int samplesize = fmt->channels * sizeof (float); + + do { + // add more frames to input SRC buffer + int n = inputsize; + if (n >= SRC_BUFFER - src->remaining) { + n = SRC_BUFFER - src->remaining; + } + + if (n > 0) { + memcpy (&src->in_fbuffer[src->remaining*samplesize], samples, n * samplesize); + + src->remaining += n; + samples += n * fmt->channels; + } + if (!src->remaining) { + trace ("WARNING: SRC input buffer starved\n"); + break; + } + + // call libsamplerate + src->srcdata.data_in = (float *)src->in_fbuffer; + src->srcdata.data_out = (float *)output; + src->srcdata.input_frames = src->remaining; + src->srcdata.output_frames = outsize; + src->srcdata.end_of_input = 0; + trace ("src input: %d, ratio %f, buffersize: %d\n", src->srcdata.input_frames, src->srcdata.src_ratio, sizeof (outbuf)); + int src_err = src_process (src->src, &src->srcdata); + trace ("src output: %d, used: %d\n", src->srcdata.output_frames_gen, src->srcdata.input_frames_used); + + if (src_err) { + const char *err = src_strerror (src_err) ; + fprintf (stderr, "src_process error %s\n" + "srcdata.data_in=%p, srcdata.data_out=%p, srcdata.input_frames=%d, srcdata.output_frames=%d, srcdata.src_ratio=%f", err, src->srcdata.data_in, src->srcdata.data_out, (int)src->srcdata.input_frames, (int)src->srcdata.output_frames, src->srcdata.src_ratio); + return nframes; + } + + inputsize -= n; + output += src->srcdata.output_frames_gen * samplesize; + numoutframes += src->srcdata.output_frames_gen; + outsize -= src->srcdata.output_frames_gen; + + // calculate how many unused input samples left + src->remaining -= src->srcdata.input_frames_used; + // copy spare samples for next update + if (src->remaining > 0 && src->srcdata.input_frames_used > 0) { + memmove (src->in_fbuffer, &src->in_fbuffer[src->srcdata.input_frames_used*samplesize], src->remaining * samplesize); + } + if (src->srcdata.output_frames_gen == 0) { + trace ("src: output_frames_gen=0, interrupt\n"); + break; + } + } while (inputsize > 0 && outsize > 0); + + memcpy (input, outbuf, numoutframes * fmt->channels * sizeof (float)); + //static FILE *out = NULL; + //if (!out) { + // out = fopen ("out.raw", "w+b"); + //} + //fwrite (input, 1, numoutframes*sizeof(float)*(*nchannels), out); + + fmt->samplerate = src->samplerate; + trace ("src: ratio=%f, in=%d, out=%d\n", ratio, nframes, numoutframes); + return numoutframes; +} + +int +ddb_src_num_params (void) { + return SRC_PARAM_COUNT; +} + +const char * +ddb_src_get_param_name (int p) { + switch (p) { + case SRC_PARAM_QUALITY: + return "Quality"; + case SRC_PARAM_SAMPLERATE: + return "Samplerate"; + default: + fprintf (stderr, "ddb_src_get_param_name: invalid param index (%d)\n", p); + } +} + +void +ddb_src_set_param (ddb_dsp_context_t *ctx, int p, const char *val) { + switch (p) { + case SRC_PARAM_SAMPLERATE: + ((ddb_src_libsamplerate_t*)ctx)->samplerate = atof (val); + if (((ddb_src_libsamplerate_t*)ctx)->samplerate < 8000) { + ((ddb_src_libsamplerate_t*)ctx)->samplerate = 8000; + } + if (((ddb_src_libsamplerate_t*)ctx)->samplerate > 192000) { + ((ddb_src_libsamplerate_t*)ctx)->samplerate = 192000; + } + break; + case SRC_PARAM_QUALITY: + ((ddb_src_libsamplerate_t*)ctx)->quality = atoi (val); + ((ddb_src_libsamplerate_t*)ctx)->quality_changed = 1; + break; + default: + fprintf (stderr, "ddb_src_set_param: invalid param index (%d)\n", p); + } +} + +void +ddb_src_get_param (ddb_dsp_context_t *ctx, int p, char *val, int sz) { + switch (p) { + case SRC_PARAM_SAMPLERATE: + snprintf (val, sz, "%f", ((ddb_src_libsamplerate_t*)ctx)->samplerate); + break; + case SRC_PARAM_QUALITY: + snprintf (val, sz, "%d", ((ddb_src_libsamplerate_t*)ctx)->quality); + break; + default: + fprintf (stderr, "ddb_src_get_param: invalid param index (%d)\n", p); + } +} + +static const char settings_dlg[] = + "property \"Target Samplerate\" spinbtn[8192,192000,1] 0 48000;\n" + "property \"Quality / Algorythm\" select[5] 1 2 SINC_BEST_QUALITY SINC_MEDIUM_QUALITY SINC_FASTEST ZERO_ORDER_HOLD LINEAR;\n" +; + +static DB_dsp_t plugin = { + .plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.api_vminor = DB_API_VERSION_MINOR, + .open = ddb_src_open, + .close = ddb_src_close, + .process = ddb_src_process, + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DSP, + .plugin.id = "SRC", + .plugin.name = "Resampler (Secret Rabbit Code)", + .plugin.descr = "High quality samplerate converter using libsamplerate, http://www.mega-nerd.com/SRC/", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.website = "http://deadbeef.sf.net", + .num_params = ddb_src_num_params, + .get_param_name = ddb_src_get_param_name, + .set_param = ddb_src_set_param, + .get_param = ddb_src_get_param, + .reset = ddb_src_reset, + .configdialog = settings_dlg, +}; + +DB_plugin_t * +dsp_libsrc_load (DB_functions_t *f) { + deadbeef = f; + return &plugin.plugin; +} diff --git a/plugins/dsp_libsrc/src.h b/plugins/dsp_libsrc/src.h new file mode 100644 index 00000000..bb9a15e2 --- /dev/null +++ b/plugins/dsp_libsrc/src.h @@ -0,0 +1,28 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __SRC_H +#define __SRC_H + +enum { + SRC_PARAM_SAMPLERATE = 0, + SRC_PARAM_QUALITY = 1, + SRC_PARAM_COUNT +}; + +#endif diff --git a/plugins/dumb/Makefile.am b/plugins/dumb/Makefile index de165bdd..e9b05233 100644 --- a/plugins/dumb/Makefile.am +++ b/plugins/dumb/Makefile @@ -1,14 +1,12 @@ -if HAVE_DUMB -dumbpath=@top_srcdir@/plugins/dumb/dumb-kode54 +CC=gcc -EXTRA_DIST = $(dumbpath)/readme.txt $(dumbpath)/ChangeLog $(dumbpath)/licence.txt $(dumbpath)/release.txt $(dumbpath)/todo.txt +dumbpath=dumb-kode54 -pkglib_LTLIBRARIES = dumb.la +CFLAGS+=-Wall -fPIC -D_GNU_SOURCE -I$(dumbpath)/include -std=c99 -AM_CFLAGS = $(CFLAGS) -I$(dumbpath)/include -dumb_la_LDFLAGS = -module -lm +LDFLAGS+=-module -shared -lm -dumb_la_SOURCES =\ +SOURCES=\ dumb-kode54/src/it/readam.c\ dumb-kode54/src/it/readstm.c\ dumb-kode54/src/it/loads3m.c\ @@ -79,29 +77,20 @@ dumb-kode54/src/helpers/riff.c\ dumb-kode54/src/helpers/memfile.c\ dumb-kode54/src/helpers/sampbuf.c\ dumb-kode54/src/helpers/barray.c\ -dumb-kode54/studio/include/guitop.h\ -dumb-kode54/studio/include/dumbgui.h\ -dumb-kode54/studio/include/options.h\ -dumb-kode54/studio/include/subclip.h\ -dumb-kode54/studio/include/main.h\ -dumb-kode54/studio/include/guiproc.h\ -dumb-kode54/studio/include/dumbmenu.h\ -dumb-kode54/studio/include/dumbdesk.h\ -dumb-kode54/src/tools/it/modulus.h\ -dumb-kode54/include/internal/it.h\ -dumb-kode54/include/internal/dumb.h\ -dumb-kode54/include/internal/barray.h\ -dumb-kode54/include/internal/riff.h\ -dumb-kode54/include/internal/aldumb.h\ -dumb-kode54/include/dumb.h\ -dumb-kode54/include/aldumb.h\ -dumb-kode54/winamp/in_duh.h\ -dumb-kode54/winamp/in2.h\ -dumb-kode54/winamp/resource.h\ -dumb-kode54/winamp/out.h\ -dumb-kode54/winamp/gui.h\ -dumb-kode54/src/helpers/resample.inc\ -dumb-kode54/src/helpers/resamp2.inc\ -dumb-kode54/src/helpers/resamp3.inc\ cdumb.c -endif + +OBJECTS=$(SOURCES:.c=.o) + +OUT=dumb.so + +all: $(SOURCES) $(OUT) + +$(OUT): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -c -o $@ + +clean: + rm $(OBJECTS) $(OUT) + diff --git a/plugins/dumb/cdumb.c b/plugins/dumb/cdumb.c index b8189eeb..b5367640 100644 --- a/plugins/dumb/cdumb.c +++ b/plugins/dumb/cdumb.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -51,7 +51,7 @@ static DUH* open_module(const char *fname, const char *ext, int *start_order, int *is_it, int *is_dos, const char **filetype); static DB_fileinfo_t * -cdumb_open (void) { +cdumb_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (dumb_info_t)); memset (_info, 0, sizeof (dumb_info_t)); return _info; @@ -59,26 +59,27 @@ cdumb_open (void) { static int cdumb_init (DB_fileinfo_t *_info, DB_playItem_t *it) { - trace ("cdumb_init %s\n", it->fname); + trace ("cdumb_init %s\n", deadbeef->pl_find_meta (it, ":URI")); dumb_info_t *info = (dumb_info_t *)_info; int start_order = 0; int is_dos, is_it; - const char *ext = it->fname + strlen (it->fname) - 1; - while (*ext != '.' && ext > it->fname) { + const char *ext = deadbeef->pl_find_meta (it, ":URI") + strlen (deadbeef->pl_find_meta (it, ":URI")) - 1; + while (*ext != '.' && ext > deadbeef->pl_find_meta (it, ":URI")) { ext--; } ext++; const char *ftype; - info->duh = open_module(it->fname, ext, &start_order, &is_it, &is_dos, &ftype); + info->duh = open_module(deadbeef->pl_find_meta (it, ":URI"), ext, &start_order, &is_it, &is_dos, &ftype); dumb_it_do_initial_runthrough (info->duh); _info->plugin = &plugin; - _info->bps = 16; - _info->channels = 2; - _info->samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); + _info->fmt.bps = deadbeef->conf_get_int ("dumb.8bitoutput", 0) ? 8 : 16; + _info->fmt.channels = 2; + _info->fmt.samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); _info->readpos = 0; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); if (cdumb_startrenderer (_info) < 0) { return -1; @@ -139,12 +140,13 @@ static int cdumb_read (DB_fileinfo_t *_info, char *bytes, int size) { trace ("cdumb_read req %d\n", size); dumb_info_t *info = (dumb_info_t *)_info; - int length = size / 4; + int samplesize = (_info->fmt.bps >> 3) * _info->fmt.channels; + int length = size / samplesize; long ret; - ret = duh_render (info->renderer, 16, 0, 1, 65536.f / _info->samplerate, length, bytes); - _info->readpos += ret / (float)_info->samplerate; - trace ("cdumb_read %d\n", ret*4); - return ret*4; + ret = duh_render (info->renderer, _info->fmt.bps, 0, 1, 65536.f / _info->fmt.samplerate, length, bytes); + _info->readpos += ret / (float)_info->fmt.samplerate; + trace ("cdumb_read %d\n", ret*samplesize); + return ret*samplesize; } static int @@ -159,8 +161,8 @@ cdumb_seek (DB_fileinfo_t *_info, float time) { else { time -= _info->readpos; } - int pos = time * _info->samplerate; - duh_sigrenderer_generate_samples (info->renderer, 0, 65536.0f / _info->samplerate, pos, NULL); + int pos = time * _info->fmt.samplerate; + duh_sigrenderer_generate_samples (info->renderer, 0, 65536.0f / _info->fmt.samplerate, pos, NULL); _info->readpos = duh_sigrenderer_get_position (info->renderer) / 65536.f; return 0; } @@ -691,8 +693,8 @@ static DUH * open_module(const char *fname, const char *ext, int *start_order, i return duh; } -static const char *convstr (const char* str, int sz) { - static char out[2048]; +static const char * +convstr (const char* str, int sz, char *out, int out_sz) { int i; for (i = 0; i < sz; i++) { if (str[i] != ' ') { @@ -705,11 +707,11 @@ static const char *convstr (const char* str, int sz) { } // check for utf8 (hack) - if (deadbeef->junk_iconv (str, sz, out, sizeof (out), "utf-8", "utf-8") >= 0) { + if (deadbeef->junk_iconv (str, sz, out, out_sz, "utf-8", "utf-8") >= 0) { return out; } - if (deadbeef->junk_iconv (str, sz, out, sizeof (out), "utf-8", "iso8859-1") >= 0) { + if (deadbeef->junk_iconv (str, sz, out, out_sz, "cp1252", "utf-8") >= 0) { return out; } @@ -717,25 +719,10 @@ static const char *convstr (const char* str, int sz) { return NULL; } -static DB_playItem_t * -cdumb_insert (DB_playItem_t *after, const char *fname) { - const char *ext = fname + strlen (fname) - 1; - while (*ext != '.' && ext > fname) { - ext--; - } - ext++; - int start_order = 0; - int is_it; - int is_dos; - const char *ftype; - DUH* duh = open_module(fname, ext, &start_order, &is_it, &is_dos, &ftype); - if (!duh) { - return NULL; - } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); +static void +read_metadata_internal (DB_playItem_t *it, DUMB_IT_SIGDATA *itsd) { + char temp[2048]; + if (itsd->name[0]) { int tl = sizeof(itsd->name); int i; @@ -744,15 +731,90 @@ cdumb_insert (DB_playItem_t *after, const char *fname) { deadbeef->pl_add_meta (it, "title", NULL); } else { - deadbeef->pl_add_meta (it, "title", convstr ((char*)&itsd->name, sizeof(itsd->name))); + deadbeef->pl_add_meta (it, "title", convstr ((char*)&itsd->name, sizeof(itsd->name), temp, sizeof (temp))); } } else { deadbeef->pl_add_meta (it, "title", NULL); } + int i; + for (i = 0; i < itsd->n_instruments; i++) { + char key[100]; + snprintf (key, sizeof (key), "INST%03d", i); + deadbeef->pl_add_meta (it, key, convstr ((char *)&itsd->instrument[i].name, sizeof (itsd->instrument[i].name), temp, sizeof (temp))); + } + for (i = 0; i < itsd->n_samples; i++) { + char key[100]; + snprintf (key, sizeof (key), "SAMP%03d", i); + deadbeef->pl_add_meta (it, key, convstr ((char *)&itsd->sample[i].name, sizeof (itsd->sample[i].name), temp, sizeof (temp))); + } + + char s[100]; + + snprintf (s, sizeof (s), "%d", itsd->n_orders); + deadbeef->pl_add_meta (it, ":MOD_ORDERS", s); + snprintf (s, sizeof (s), "%d", itsd->n_instruments); + deadbeef->pl_add_meta (it, ":MOD_INSTRUMENTS", s); + snprintf (s, sizeof (s), "%d", itsd->n_samples); + deadbeef->pl_add_meta (it, ":MOD_SAMPLES", s); + snprintf (s, sizeof (s), "%d", itsd->n_patterns); + deadbeef->pl_add_meta (it, ":MOD_PATTERNS", s); + snprintf (s, sizeof (s), "%d", itsd->n_pchannels); + deadbeef->pl_add_meta (it, ":MOD_CHANNELS", s); +} + +static int +cdumb_read_metadata (DB_playItem_t *it) { + const char *fname = deadbeef->pl_find_meta (it, ":URI"); + const char *ext = strrchr (fname, '.'); + if (ext) { + ext++; + } + else { + ext = ""; + } + int start_order = 0; + int is_it; + int is_dos; + const char *ftype; + DUH* duh = open_module(fname, ext, &start_order, &is_it, &is_dos, &ftype); + if (!duh) { + unload_duh (duh); + return -1; + } + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + + deadbeef->pl_delete_all_meta (it); + read_metadata_internal (it, itsd); + unload_duh (duh); + return 0; +} + +static DB_playItem_t * +cdumb_insert (DB_playItem_t *after, const char *fname) { + const char *ext = strrchr (fname, '.'); + if (ext) { + ext++; + } + else { + ext = ""; + } + int start_order = 0; + int is_it; + int is_dos; + const char *ftype; + DUH* duh = open_module(fname, ext, &start_order, &is_it, &is_dos, &ftype); + if (!duh) { + return NULL; + } + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + + read_metadata_internal (it, itsd); + dumb_it_do_initial_runthrough (duh); deadbeef->pl_set_item_duration (it, duh_get_length (duh)/65536.0f); - it->filetype = ftype; + deadbeef->pl_add_meta (it, ":FILETYPE", ftype); // printf ("duration: %f\n", _info->duration); after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); @@ -811,18 +873,39 @@ static const char *filetypes[] = { "IT", "XM", "S3M", "STM", "669", "PTM", "PSM" static const char settings_dlg[] = "property \"Resampling quality (0..2, higher is better)\" entry dumb.resampling_quality 2;\n" + "property \"8-bit output (default is 16)\" checkbox dumb.8bitoutput 0;\n" ; + // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "stddumb", .plugin.name = "DUMB module player", .plugin.descr = "module player based on DUMB library", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses a fork of DUMB (Dynamic Universal Music Bibliotheque), Version 0.9.3\n" + "Copyright (C) 2001-2005 Ben Davis, Robert J Ohannessian and Julien Cugniere\n" + "Uses code from kode54's foobar2000 plugin, http://kode54.foobar2000.org/\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = cgme_start, .plugin.stop = cgme_stop, @@ -830,9 +913,10 @@ static DB_decoder_t plugin = { .open = cdumb_open, .init = cdumb_init, .free = cdumb_free, - .read_int16 = cdumb_read, + .read = cdumb_read, .seek = cdumb_seek, .insert = cdumb_insert, + .read_metadata = cdumb_read_metadata, .exts = exts, .filetypes = filetypes }; diff --git a/plugins/dumb/dumb-kode54/src/it/loadmod.c b/plugins/dumb/dumb-kode54/src/it/loadmod.c index 0b7dc618..dd43b611 100644 --- a/plugins/dumb/dumb-kode54/src/it/loadmod.c +++ b/plugins/dumb/dumb-kode54/src/it/loadmod.c @@ -26,7 +26,7 @@ * pointer to the DUH struct. When you have finished with it, you must * pass the pointer to unload_duh() so that the memory can be freed. */ -DUH *dumb_load_mod_quick(const char *filename, int restrict) +DUH *dumb_load_mod_quick(const char *filename, int restr) { DUH *duh; DUMBFILE *f = dumbfile_open(filename); @@ -34,7 +34,7 @@ DUH *dumb_load_mod_quick(const char *filename, int restrict) if (!f) return NULL; - duh = dumb_read_mod_quick(f, restrict); + duh = dumb_read_mod_quick(f, restr); dumbfile_close(f); diff --git a/plugins/dumb/dumb-kode54/src/it/loadmod2.c b/plugins/dumb/dumb-kode54/src/it/loadmod2.c index 7847d19f..c1f46e4c 100644 --- a/plugins/dumb/dumb-kode54/src/it/loadmod2.c +++ b/plugins/dumb/dumb-kode54/src/it/loadmod2.c @@ -21,9 +21,9 @@ -DUH *dumb_load_mod(const char *filename, int restrict) +DUH *dumb_load_mod(const char *filename, int restr) { - DUH *duh = dumb_load_mod_quick(filename, restrict); + DUH *duh = dumb_load_mod_quick(filename, restr); dumb_it_do_initial_runthrough(duh); return duh; } diff --git a/plugins/dumb/dumb-kode54/src/it/readmod.c b/plugins/dumb/dumb-kode54/src/it/readmod.c index a934af40..538d86ba 100644 --- a/plugins/dumb/dumb-kode54/src/it/readmod.c +++ b/plugins/dumb/dumb-kode54/src/it/readmod.c @@ -439,7 +439,7 @@ static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, long *remain) } -static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict) +static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restr) { DUMB_IT_SIGDATA *sigdata; int n_channels; @@ -554,7 +554,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict) } // moo - if ( restrict && sigdata->n_samples == 15 ) + if ( restr && sigdata->n_samples == 15 ) { free(sigdata); dumbfile_close(f); @@ -760,13 +760,13 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict) return sigdata; } -DUH *dumb_read_mod_quick(DUMBFILE *f, int restrict) +DUH *dumb_read_mod_quick(DUMBFILE *f, int restr) { sigdata_t *sigdata; DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - sigdata = it_mod_load_sigdata(f, restrict); + sigdata = it_mod_load_sigdata(f, restr); if (!sigdata) return NULL; diff --git a/plugins/dumb/dumb-kode54/src/it/readmod2.c b/plugins/dumb/dumb-kode54/src/it/readmod2.c index 102ead12..299c584e 100644 --- a/plugins/dumb/dumb-kode54/src/it/readmod2.c +++ b/plugins/dumb/dumb-kode54/src/it/readmod2.c @@ -21,9 +21,9 @@ -DUH *dumb_read_mod(DUMBFILE *f, int restrict) +DUH *dumb_read_mod(DUMBFILE *f, int restr) { - DUH *duh = dumb_read_mod_quick(f, restrict); + DUH *duh = dumb_read_mod_quick(f, restr); dumb_it_do_initial_runthrough(duh); return duh; } diff --git a/plugins/ffap/ffap.c b/plugins/ffap/ffap.c index deaa06dc..7abf0103 100644 --- a/plugins/ffap/ffap.c +++ b/plugins/ffap/ffap.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> based on apedec from FFMpeg Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> based upon libdemac from Dave Chapman. @@ -36,6 +36,7 @@ #include <stdlib.h> //#include <alloca.h> #include <assert.h> +#include <math.h> #include "../../deadbeef.h" #ifdef TARGET_ANDROID @@ -678,7 +679,7 @@ ffap_free (DB_fileinfo_t *_info) } static DB_fileinfo_t * -ffap_open (void) { +ffap_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (ape_info_t)); memset (_info, 0, sizeof (ape_info_t)); return _info; @@ -689,7 +690,7 @@ ffap_init (DB_fileinfo_t *_info, DB_playItem_t *it) { ape_info_t *info = (ape_info_t*)_info; - info->fp = deadbeef->fopen (it->fname); + info->fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->fp) { return -1; } @@ -726,9 +727,10 @@ ffap_init (DB_fileinfo_t *_info, DB_playItem_t *it) } _info->plugin = &plugin; - _info->bps = info->ape_ctx.bps; - _info->samplerate = info->ape_ctx.samplerate; - _info->channels = info->ape_ctx.channels; + _info->fmt.bps = info->ape_ctx.bps; + _info->fmt.samplerate = info->ape_ctx.samplerate; + _info->fmt.channels = info->ape_ctx.channels; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; if (it->endsample > 0) { info->startsample = it->startsample; @@ -1560,16 +1562,16 @@ ape_decode_frame(DB_fileinfo_t *_info, void *data, int *data_size) { ape_info_t *info = (ape_info_t*)_info; APEContext *s = &info->ape_ctx; - int16_t *samples = data; + char *samples = data; int nblocks; int i, n; int blockstodecode; int bytes_used; - int samplesize = _info->bps>>3; + int samplesize = _info->fmt.bps/8 * s->channels;; /* should not happen but who knows */ - if (BLOCKS_PER_LOOP * samplesize * s->channels > *data_size) { - fprintf (stderr, "ape: Packet size is too big! (max is %d where you have %d)\n", *data_size, BLOCKS_PER_LOOP * samplesize * s->channels); + if (BLOCKS_PER_LOOP * samplesize > *data_size) { + fprintf (stderr, "ape: Packet size is too big! (max is %d where you have %d)\n", *data_size, BLOCKS_PER_LOOP * samplesize); return -1; } @@ -1665,17 +1667,58 @@ ape_decode_frame(DB_fileinfo_t *_info, void *data, int *data_size) int skip = min (s->samplestoskip, blockstodecode); i = skip; - for (; i < blockstodecode; i++) { - *samples++ = (int16_t)(s->decoded0[i]>>(_info->bps-16)); - if(s->channels == 2) { - *samples++ = (int16_t)(s->decoded1[i]>>(_info->bps-16)); + if (_info->fmt.bps == 32) { + for (; i < blockstodecode; i++) { + *((int32_t*)samples) = s->decoded0[i]; + samples += 4; + if(s->channels > 1) { + *((int32_t*)samples) = s->decoded1[i]; + samples += 4; + } + } + } + else if (_info->fmt.bps == 24) { + for (; i < blockstodecode; i++) { + int32_t sample = s->decoded0[i]; + + samples[0] = sample&0xff; + samples[1] = (sample&0xff00)>>8; + samples[2] = (sample&0xff0000)>>16; + samples += 3; + if(s->channels > 1) { + sample = s->decoded1[i]; + samples[0] = sample&0xff; + samples[1] = (sample&0xff00)>>8; + samples[2] = (sample&0xff0000)>>16; + samples += 3; + } + } + } + else if (_info->fmt.bps == 16) { + for (; i < blockstodecode; i++) { + *((int16_t*)samples) = (int16_t)s->decoded0[i]; + samples += 2; + if(s->channels > 1) { + *((int16_t*)samples) = (int16_t)s->decoded1[i]; + samples += 2; + } + } + } + else if (_info->fmt.bps == 8) { + for (; i < blockstodecode; i++) { + *samples = (int16_t)s->decoded0[i]; + samples++; + if(s->channels > 1) { + *samples = (int16_t)s->decoded1[i]; + samples++; + } } } s->samplestoskip -= skip; s->samples -= blockstodecode; - *data_size = (blockstodecode - skip) * 2 * s->channels; + *data_size = (blockstodecode - skip) * samplesize; // ape_ctx.currentsample += blockstodecode - skip; bytes_used = s->samples ? s->ptr - s->last_ptr : s->packet_remaining; @@ -1699,6 +1742,9 @@ ffap_insert (DB_playItem_t *after, const char *fname) { if (!fp) { return NULL; } + + int64_t fsize = deadbeef->fgetlength (fp); + int skip = deadbeef->junk_get_leading_size (fp); if (skip > 0) { deadbeef->fseek (fp, skip, SEEK_SET); @@ -1718,10 +1764,8 @@ ffap_insert (DB_playItem_t *after, const char *fname) { float duration = ape_ctx.totalsamples / (float)ape_ctx.samplerate; DB_playItem_t *it = NULL; - it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "APE"; + it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "APE"); deadbeef->pl_set_item_duration (it, duration); /*int v2err = */deadbeef->junk_id3v2_read (it, fp); @@ -1752,6 +1796,19 @@ ffap_insert (DB_playItem_t *after, const char *fname) { } deadbeef->pl_unlock (); + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + snprintf (s, sizeof (s), "%d", ape_ctx.bps); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", ape_ctx.channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", ape_ctx.samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / duration * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); + cue = deadbeef->pl_insert_cue (after, it, ape_ctx.totalsamples, ape_ctx.samplerate); if (cue) { deadbeef->pl_item_unref (it); @@ -1760,6 +1817,7 @@ ffap_insert (DB_playItem_t *after, const char *fname) { } deadbeef->pl_add_meta (it, "title", NULL); + after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); @@ -1767,11 +1825,14 @@ ffap_insert (DB_playItem_t *after, const char *fname) { } static int -ffap_read_int16 (DB_fileinfo_t *_info, char *buffer, int size) { +ffap_read (DB_fileinfo_t *_info, char *buffer, int size) { ape_info_t *info = (ape_info_t*)_info; - if (info->ape_ctx.currentsample + size / ((info->info.bps / 8) * info->ape_ctx.channels) > info->endsample) { - size = (info->endsample - info->ape_ctx.currentsample + 1) * (info->info.bps / 8) * info->ape_ctx.channels; - trace ("size truncated to %d bytes (%d samples), cursample=%d, info->endsample=%d, totalsamples=%d\n", size, size / (info->info.bps / 8) / info->ape_ctx.channels, info->ape_ctx.currentsample, info->endsample, info->ape_ctx.totalsamples); + + int samplesize = _info->fmt.bps / 8 * info->ape_ctx.channels; + + if (info->ape_ctx.currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->ape_ctx.currentsample + 1) * samplesize; + trace ("size truncated to %d bytes (%d samples), cursample=%d, info->endsample=%d, totalsamples=%d\n", size, size / samplesize, info->ape_ctx.currentsample, info->endsample, info->ape_ctx.totalsamples); if (size <= 0) { return 0; } @@ -1808,8 +1869,8 @@ ffap_read_int16 (DB_fileinfo_t *_info, char *buffer, int size) { } info->ape_ctx.remaining -= sz; } - info->ape_ctx.currentsample += (inits - size) / (2 * info->ape_ctx.channels); - _info->readpos = (info->ape_ctx.currentsample-info->startsample) / (float)_info->samplerate; + info->ape_ctx.currentsample += (inits - size) / samplesize; + _info->readpos = (info->ape_ctx.currentsample-info->startsample) / (float)_info->fmt.samplerate; return inits - size; } @@ -1844,12 +1905,12 @@ ffap_seek_sample (DB_fileinfo_t *_info, int sample) { static int ffap_seek (DB_fileinfo_t *_info, float seconds) { - return ffap_seek_sample (_info, seconds * _info->samplerate); + return ffap_seek_sample (_info, seconds * _info->fmt.samplerate); } static int ffap_read_metadata (DB_playItem_t *it) { - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -1899,19 +1960,37 @@ static const char *filetypes[] = { "APE", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "ffap", .plugin.name = "Monkey's Audio (APE) decoder", .plugin.descr = "APE player based on code from libavc and rockbox", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "based on apedec from FFMpeg Copyright (c) 2007 Benjamin Zores <ben@geexbox.org>\n" + "based upon libdemac from Dave Chapman.\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = ffap_open, .init = ffap_init, .free = ffap_free, - .read_int16 = ffap_read_int16, + .read = ffap_read, .seek = ffap_seek, .seek_sample = ffap_seek_sample, .insert = ffap_insert, diff --git a/plugins/ffmpeg/ChangeLog b/plugins/ffmpeg/ChangeLog new file mode 100644 index 00000000..f3179866 --- /dev/null +++ b/plugins/ffmpeg/ChangeLog @@ -0,0 +1,5 @@ +version 1.2 + Added AMR support via libopencore + +version 1.1 + Switched to ffmpeg commit 25472 diff --git a/plugins/ffmpeg/ffmpeg.c b/plugins/ffmpeg/ffmpeg.c index f27e403b..020f2a1c 100644 --- a/plugins/ffmpeg/ffmpeg.c +++ b/plugins/ffmpeg/ffmpeg.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -55,7 +55,11 @@ static DB_decoder_t plugin; static DB_functions_t *deadbeef; -static const char * exts[] = { "m4a", "wma", "aa3", "oma", "ac3", "vqf", NULL }; +#define DEFAULT_EXTS "m4a;wma;aa3;oma;ac3;vqf;amr" + +#define EXT_MAX 100 + +static char * exts[EXT_MAX] = {NULL}; enum { FT_ALAC = 0, @@ -63,10 +67,11 @@ enum { FT_ATRAC3 = 2, FT_VQF = 3, FT_AC3 = 4, + FT_AMR = 5, FT_UNKNOWN = 5 }; -static const char *filetypes[] = { "ALAC", "WMA", "ATRAC3", "VQF", "AC3", "FFMPEG (unknown)", NULL }; +static const char *filetypes[] = { "FFMPEG", NULL }; #define FF_PROTOCOL_NAME "deadbeef" @@ -93,7 +98,7 @@ static DB_playItem_t *current_track; static DB_fileinfo_t *current_info; static DB_fileinfo_t * -ffmpeg_open (void) { +ffmpeg_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (ffmpeg_info_t)); memset (_info, 0, sizeof (ffmpeg_info_t)); return _info; @@ -102,19 +107,19 @@ ffmpeg_open (void) { static int ffmpeg_init (DB_fileinfo_t *_info, DB_playItem_t *it) { ffmpeg_info_t *info = (ffmpeg_info_t *)_info; - trace ("ffmpeg init %s\n", it->fname); + trace ("ffmpeg init %s\n", deadbeef->pl_find_meta (it, ":URI")); // prepare to decode the track // return -1 on failure int ret; - int l = strlen (it->fname); + int l = strlen (deadbeef->pl_find_meta (it, ":URI")); char *uri = alloca (l + sizeof (FF_PROTOCOL_NAME) + 1); int i; // construct uri memcpy (uri, FF_PROTOCOL_NAME, sizeof (FF_PROTOCOL_NAME)-1); memcpy (uri + sizeof (FF_PROTOCOL_NAME)-1, ":", 1); - memcpy (uri + sizeof (FF_PROTOCOL_NAME), it->fname, l); + memcpy (uri + sizeof (FF_PROTOCOL_NAME), deadbeef->pl_find_meta (it, ":URI"), l); uri[sizeof (FF_PROTOCOL_NAME) + l] = 0; trace ("ffmpeg: uri: %s\n", uri); @@ -149,10 +154,10 @@ ffmpeg_init (DB_fileinfo_t *_info, DB_playItem_t *it) { if (info->codec == NULL) { - trace ("ffmpeg can't decode %s\n", it->fname); + trace ("ffmpeg can't decode %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } - trace ("ffmpeg can decode %s\n", it->fname); + trace ("ffmpeg can decode %s\n", deadbeef->pl_find_meta (it, ":URI")); trace ("ffmpeg: codec=%s, stream=%d\n", info->codec->name, i); if (avcodec_open (info->ctx, info->codec) < 0) { @@ -160,18 +165,7 @@ ffmpeg_init (DB_fileinfo_t *_info, DB_playItem_t *it) { return -1; } - if (!strcasecmp (info->codec->name, "alac")) { - it->filetype = filetypes[FT_ALAC]; - } - else if (strcasestr (info->codec->name, "wma")) { - it->filetype = filetypes[FT_WMA]; - } - else if (strcasestr (info->codec->name, "ac3")) { - it->filetype = filetypes[FT_AC3]; - } - else { - it->filetype = filetypes[FT_UNKNOWN]; - } + deadbeef->pl_replace_meta (it, ":FILETYPE", info->codec->name); int bps = av_get_bits_per_sample_format (info->ctx->sample_fmt); int samplerate = info->ctx->sample_rate; @@ -196,9 +190,20 @@ ffmpeg_init (DB_fileinfo_t *_info, DB_playItem_t *it) { // fill in mandatory plugin fields _info->plugin = &plugin; _info->readpos = 0; - _info->bps = bps; - _info->channels = info->ctx->channels; - _info->samplerate = samplerate; + _info->fmt.bps = bps; + _info->fmt.channels = info->ctx->channels; + _info->fmt.samplerate = samplerate; + + + int64_t layout = info->ctx->channel_layout; + if (layout != 0, 0) { + _info->fmt.channelmask = layout; + } + else { + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } + } // subtrack info info->currentsample = 0; @@ -222,7 +227,7 @@ ffmpeg_free (DB_fileinfo_t *_info) { if (info->buffer) { free (info->buffer); } - // free everything allocated in _init and _read_int16 + // free everything allocated in _init and _read if (info->have_packet) { av_free_packet (&info->pkt); } @@ -237,14 +242,14 @@ ffmpeg_free (DB_fileinfo_t *_info) { } static int -ffmpeg_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +ffmpeg_read (DB_fileinfo_t *_info, char *bytes, int size) { trace ("ffmpeg_read_int16 %d\n", size); ffmpeg_info_t *info = (ffmpeg_info_t*)_info; - int out_ch = min (2, _info->channels); + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; - if (info->endsample >= 0 && info->currentsample + size / (2 * out_ch) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * out_ch; + if (info->endsample >= 0 && info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { return 0; } @@ -259,20 +264,13 @@ ffmpeg_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { if (info->left_in_buffer > 0) { // int sz = min (size, info->left_in_buffer); - int nsamples = size / out_ch / 2; - int nsamples_buf = info->left_in_buffer / 2 / _info->channels; + int nsamples = size / samplesize; + int nsamples_buf = info->left_in_buffer / samplesize; nsamples = min (nsamples, nsamples_buf); - int sz = nsamples * 2 * _info->channels; - for (int i = 0; i < nsamples; i++) { - *((int16_t*)bytes) = ((int16_t*)info->buffer)[i * _info->channels + 0]; - bytes += 2; - size -= 2; - if (out_ch > 1) { - *((int16_t*)bytes) = ((int16_t*)info->buffer)[i * _info->channels + 1]; - bytes += 2; - size -= 2; - } - } + int sz = nsamples * samplesize; + memcpy (bytes, info->buffer, nsamples*samplesize); + bytes += nsamples * samplesize; + size -= nsamples * samplesize; if (sz != info->left_in_buffer) { memmove (info->buffer, info->buffer+sz, info->left_in_buffer-sz); } @@ -359,8 +357,8 @@ ffmpeg_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } } - info->currentsample += (initsize-size) / (2 * out_ch); - _info->readpos = (float)info->currentsample / _info->samplerate; + info->currentsample += (initsize-size) / samplesize; + _info->readpos = (float)info->currentsample / _info->fmt.samplerate; return initsize-size; } @@ -376,7 +374,7 @@ ffmpeg_seek_sample (DB_fileinfo_t *_info, int sample) { info->have_packet = 0; } sample += info->startsample; - int64_t tm = (int64_t)sample/ _info->samplerate * AV_TIME_BASE; + int64_t tm = (int64_t)sample/ _info->fmt.samplerate * AV_TIME_BASE; trace ("ffmpeg: seek to sample: %d, t: %d\n", sample, (int)tm); info->left_in_packet = 0; info->left_in_buffer = 0; @@ -387,7 +385,7 @@ ffmpeg_seek_sample (DB_fileinfo_t *_info, int sample) { // update readpos info->currentsample = sample; - _info->readpos = (float)(sample - info->startsample) / _info->samplerate; + _info->readpos = (float)(sample - info->startsample) / _info->fmt.samplerate; return 0; } @@ -397,7 +395,7 @@ ffmpeg_seek (DB_fileinfo_t *_info, float time) { // seek to specified time in seconds // return 0 on success // return -1 on failure - return ffmpeg_seek_sample (_info, time * _info->samplerate); + return ffmpeg_seek_sample (_info, time * _info->fmt.samplerate); } static const char *map[] = { @@ -529,13 +527,11 @@ ffmpeg_insert (DB_playItem_t *after, const char *fname) { int totalsamples = fctx->duration * samplerate / AV_TIME_BASE; - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); // FIXME: get proper codec - it->filetype = filetypes[FT_UNKNOWN]; + deadbeef->pl_replace_meta (it, ":FILETYPE", codec->name); - if (!deadbeef->is_local_file (it->fname)) { + if (!deadbeef->is_local_file (deadbeef->pl_find_meta (it, ":URI"))) { deadbeef->pl_set_item_duration (it, -1); } else { @@ -544,6 +540,32 @@ ffmpeg_insert (DB_playItem_t *after, const char *fname) { // add metainfo ffmpeg_read_metadata_internal (it, fctx); + + int64_t fsize = -1; + + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); + if (fp) { + if (!fp->vfs->is_streaming ()) { + fsize = deadbeef->fgetlength (fp); + } + deadbeef->fclose (fp); + } + + if (fsize >= 0 && duration > 0) { + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + snprintf (s, sizeof (s), "%d", av_get_bits_per_sample_format (ctx->sample_fmt)); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", ctx->channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / duration * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); + } + // free decoder avcodec_close (ctx); av_close_input_file(fctx); @@ -576,7 +598,7 @@ ffmpeg_vfs_open(URLContext *h, const char *filename, int flags) if (f == NULL) return -ENOENT; - if (f->vfs->streaming) { + if (f->vfs->is_streaming ()) { deadbeef->fset_track (f, current_track); if (current_info) { current_info->file = f; @@ -608,9 +630,9 @@ ffmpeg_vfs_seek(URLContext *h, int64_t pos, int whence) DB_FILE *f = h->priv_data; if (whence == AVSEEK_SIZE) { - return f->vfs->streaming ? -1 : deadbeef->fgetlength (h->priv_data); + return f->vfs->is_streaming () ? -1 : deadbeef->fgetlength (h->priv_data); } - else if (f->vfs->streaming) { + else if (f->vfs->is_streaming ()) { return -1; } else { @@ -636,12 +658,50 @@ static URLProtocol vfswrapper = { .url_close = ffmpeg_vfs_close, }; +static void +ffmpeg_init_exts (void) { + deadbeef->conf_lock (); + const char *new_exts = deadbeef->conf_get_str_fast ("ffmpeg.extensions", DEFAULT_EXTS); + for (int i = 0; exts[i]; i++) { + free (exts[i]); + } + exts[0] = NULL; + + int n = 0; + while (*new_exts) { + if (n >= EXT_MAX) { + fprintf (stderr, "ffmpeg: too many extensions, max is %d\n", EXT_MAX); + break; + } + const char *e = new_exts; + while (*e && *e != ';') { + e++; + } + if (e != new_exts) { + char *ext = malloc (e-new_exts+1); + memcpy (ext, new_exts, e-new_exts); + ext[e-new_exts] = 0; + exts[n++] = ext; + } + if (*e == 0) { + break; + } + new_exts = e+1; + } + exts[n] = NULL; + deadbeef->conf_unlock (); +} + +static int +ffmpeg_on_configchanged (DB_event_t *ev, uintptr_t data) { + ffmpeg_init_exts (); + return 0; +} + static int ffmpeg_start (void) { - // do one-time plugin initialization here - // e.g. starting threads for background processing, subscribing to events, etc - // return 0 on success - // return -1 on failure + ffmpeg_init_exts (); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (ffmpeg_on_configchanged), 0); avcodec_init (); av_register_all (); av_register_protocol (&vfswrapper); @@ -650,28 +710,29 @@ ffmpeg_start (void) { static int ffmpeg_stop (void) { - // undo everything done in _start here - // return 0 on success - // return -1 on failure - trace ("ffmpeg stop\n"); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (ffmpeg_on_configchanged), 0); + for (int i = 0; exts[i]; i++) { + free (exts[i]); + } + exts[0] = NULL; return 0; } int ffmpeg_read_metadata (DB_playItem_t *it) { - trace ("ffmpeg_read_metadata: fname %s\n", it->fname); + trace ("ffmpeg_read_metadata: fname %s\n", deadbeef->pl_find_meta (it, ":URI")); AVCodec *codec = NULL; AVCodecContext *ctx = NULL; AVFormatContext *fctx = NULL; int ret; - int l = strlen (it->fname); + int l = strlen (deadbeef->pl_find_meta (it, ":URI")); char *uri = alloca (l + sizeof (FF_PROTOCOL_NAME) + 1); int i; // construct uri memcpy (uri, FF_PROTOCOL_NAME, sizeof (FF_PROTOCOL_NAME)-1); memcpy (uri + sizeof (FF_PROTOCOL_NAME)-1, ":", 1); - memcpy (uri + sizeof (FF_PROTOCOL_NAME), it->fname, l); + memcpy (uri + sizeof (FF_PROTOCOL_NAME), deadbeef->pl_find_meta (it, ":URI"), l); uri[sizeof (FF_PROTOCOL_NAME) + l] = 0; trace ("ffmpeg: uri: %s\n", uri); @@ -694,7 +755,7 @@ ffmpeg_read_metadata (DB_playItem_t *it) { } if (codec == NULL) { - trace ("ffmpeg can't decode %s\n", it->fname); + trace ("ffmpeg can't decode %s\n", deadbeef->pl_find_meta (it, ":URI")); av_close_input_file(fctx); return -1; } @@ -711,30 +772,49 @@ ffmpeg_read_metadata (DB_playItem_t *it) { return 0; } +static const char settings_dlg[] = + "property \"File Extensions (separate with ';')\" entry ffmpeg.extensions \"" DEFAULT_EXTS "\";\n" +; + // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 2, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "ffmpeg", .plugin.name = "FFMPEG audio player", .plugin.descr = "decodes audio formats using FFMPEG libavcodec", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = ffmpeg_start, .plugin.stop = ffmpeg_stop, + .plugin.configdialog = settings_dlg, .open = ffmpeg_open, .init = ffmpeg_init, .free = ffmpeg_free, - .read_int16 = ffmpeg_read_int16, -// .read_float32 = ffmpeg_read_float32, + .read = ffmpeg_read, .seek = ffmpeg_seek, .seek_sample = ffmpeg_seek_sample, .insert = ffmpeg_insert, .read_metadata = ffmpeg_read_metadata, - .exts = exts, + .exts = (const char **)exts, .filetypes = filetypes }; diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c index 00cdf598..6c136e9d 100644 --- a/plugins/flac/flac.c +++ b/plugins/flac/flac.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #include <stdlib.h> #include <FLAC/stream_decoder.h> #include <FLAC/metadata.h> +#include <math.h> #include "../../deadbeef.h" static DB_decoder_t plugin; @@ -38,12 +39,13 @@ typedef struct { FLAC__StreamDecoder *decoder; char *buffer; // this buffer always has float samples int remaining; // bytes remaining in buffer from last read - int startsample; - int endsample; - int currentsample; - int totalsamples; + int64_t startsample; + int64_t endsample; + int64_t currentsample; + int64_t totalsamples; int flac_critical_error; int init_stop_decoding; + int tagsize; DB_FILE *file; // used only on insert @@ -104,24 +106,52 @@ cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *fra if (info->bitrate > 0) { deadbeef->streamer_set_bitrate (info->bitrate); } + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; int bufsize = BUFFERSIZE - info->remaining; - int bufsamples = bufsize / (_info->channels * _info->bps / 8); + int bufsamples = bufsize / samplesize; int nsamples = min (bufsamples, frame->header.blocksize); char *bufptr = &info->buffer[info->remaining]; - float mul = 1.f/ ((1 << (_info->bps-1))-1); - int channels = _info->channels; - if (channels > 2) { - channels = 2; - } - int readbytes = frame->header.blocksize * channels * _info->bps / 8; + int readbytes = frame->header.blocksize * samplesize; - for (int i = 0; i < nsamples; i++) { - for (int c = 0; c < channels; c++) { - int32_t sample = inputbuffer[c][i]; - *((float*)bufptr) = sample * mul; - bufptr += sizeof (float); - info->remaining += sizeof (float); + if (_info->fmt.bps == 32) { + for (int i = 0; i < nsamples; i++) { + for (int c = 0; c < _info->fmt.channels; c++) { + int32_t sample = inputbuffer[c][i]; + *((int32_t*)bufptr) = sample; + bufptr += 4; + info->remaining += 4; + } + } + } + else if (_info->fmt.bps == 24) { + for (int i = 0; i < nsamples; i++) { + for (int c = 0; c < _info->fmt.channels; c++) { + int32_t sample = inputbuffer[c][i]; + *bufptr++ = sample&0xff; + *bufptr++ = (sample&0xff00)>>8; + *bufptr++ = (sample&0xff0000)>>16; + info->remaining += 3; + } + } + } + else if (_info->fmt.bps == 16) { + for (int i = 0; i < nsamples; i++) { + for (int c = 0; c < _info->fmt.channels; c++) { + int32_t sample = inputbuffer[c][i]; + *bufptr++ = sample&0xff; + *bufptr++ = (sample&0xff00)>>8; + info->remaining += 2; + } + } + } + else if (_info->fmt.bps == 8) { + for (int i = 0; i < nsamples; i++) { + for (int c = 0; c < _info->fmt.channels; c++) { + int32_t sample = inputbuffer[c][i]; + *bufptr++ = sample&0xff; + info->remaining += 1; + } } } if (readbytes > bufsize) { @@ -136,10 +166,12 @@ cflac_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMe DB_fileinfo_t *_info = (DB_fileinfo_t *)client_data; flac_info_t *info = (flac_info_t *)_info; info->totalsamples = metadata->data.stream_info.total_samples; - _info->samplerate = metadata->data.stream_info.sample_rate; - _info->channels = metadata->data.stream_info.channels; - _info->bps = metadata->data.stream_info.bits_per_sample; - + _info->fmt.samplerate = metadata->data.stream_info.sample_rate; + _info->fmt.channels = metadata->data.stream_info.channels; + _info->fmt.bps = metadata->data.stream_info.bits_per_sample; + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } } static void @@ -164,7 +196,7 @@ cflac_init_error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecode } static DB_fileinfo_t * -cflac_open (void) { +cflac_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (flac_info_t)); memset (_info, 0, sizeof (flac_info_t)); return _info; @@ -172,10 +204,10 @@ cflac_open (void) { static int cflac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { - trace ("cflac_init %s\n", it->fname); + trace ("cflac_init %s\n", deadbeef->pl_find_meta (it, ":URI")); flac_info_t *info = (flac_info_t *)_info; - info->file = deadbeef->fopen (it->fname); + info->file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->file) { trace ("cflac_init failed to open file\n"); return -1; @@ -183,8 +215,8 @@ cflac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->flac_critical_error = 0; - const char *ext = it->fname + strlen (it->fname); - while (ext > it->fname && *ext != '/' && *ext != '.') { + const char *ext = deadbeef->pl_find_meta (it, ":URI") + strlen (deadbeef->pl_find_meta (it, ":URI")); + while (ext > deadbeef->pl_find_meta (it, ":URI") && *ext != '/' && *ext != '.') { ext--; } if (*ext == '.') { @@ -237,7 +269,7 @@ cflac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("cflac_init bad decoder status\n"); return -1; } - //_info->samplerate = -1; + //_info->fmt.samplerate = -1; if (!FLAC__stream_decoder_process_until_end_of_metadata (info->decoder)) { trace ("cflac_init metadata failed\n"); return -1; @@ -247,7 +279,7 @@ cflac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { _info->plugin = &plugin; _info->readpos = 0; - if (_info->samplerate <= 0) { // not a FLAC stream + if (_info->fmt.samplerate <= 0) { // not a FLAC stream fprintf (stderr, "corrupted/invalid flac stream\n"); return -1; } @@ -259,14 +291,24 @@ cflac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { if (res) { fsize -= position; } - FLAC__uint64 flac_totalsamples = FLAC__stream_decoder_get_total_samples (info->decoder); - float sec = flac_totalsamples / _info->samplerate; + int64_t totalsamples = FLAC__stream_decoder_get_total_samples (info->decoder); + if (totalsamples <= 0) { + return -1; + } + float sec = totalsamples / (float)_info->fmt.samplerate; if (sec > 0) { info->bitrate = fsize / sec * 8 / 1000; } else { info->bitrate = -1; } + const char *channelmask = deadbeef->pl_find_meta (it, "WAVEFORMAT_EXTENSIBLE_CHANNELMASK"); + if (channelmask) { + uint32_t cm = 0; + if (1 == sscanf (channelmask, "0x%X", &cm)) { + _info->fmt.channelmask = cm; + } + } info->buffer = malloc (BUFFERSIZE); info->remaining = 0; @@ -304,53 +346,39 @@ cflac_free (DB_fileinfo_t *_info) { if (info->buffer) { free (info->buffer); } + if (info->file) { + deadbeef->fclose (info->file); + } free (_info); } } static int -cflac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +cflac_read (DB_fileinfo_t *_info, char *bytes, int size) { flac_info_t *info = (flac_info_t *)_info; - if (size / (2 * _info->channels) + info->currentsample > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * _info->channels; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + if (size / samplesize + info->currentsample > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; trace ("size truncated to %d bytes, cursample=%d, endsample=%d\n", size, info->currentsample, info->endsample); if (size <= 0) { return 0; } } - int n_output_channels = _info->channels; - if (n_output_channels > 2) { - n_output_channels = 2; - } int initsize = size; do { if (info->remaining) { - int n_input_frames = info->remaining / sizeof (float) / n_output_channels; - int n_output_frames = size / n_output_channels / sizeof (int16_t); - int n = min (n_input_frames, n_output_frames); + int sz = min(size, info->remaining); + memcpy (bytes, info->buffer, sz); -// trace ("flac: [1] if=%d, of=%d, n=%d, rem=%d, size=%d\n", n_input_frames, n_output_frames, n, info->remaining, size); - // convert from float to int16 - float *in = (float *)info->buffer; - for (int i = 0; i < n; i++) { - *((int16_t *)bytes) = (int16_t)((*in) * 0x7fff); - size -= sizeof (int16_t); - bytes += sizeof (int16_t); - if (n_output_channels == 2) { - *((int16_t *)bytes) = (int16_t)((*(in+1)) * 0x7fff); - size -= sizeof (int16_t); - bytes += sizeof (int16_t); - } - in += n_output_channels; - } - int sz = n * sizeof (float) * n_output_channels; + size -= sz; + bytes += sz; if (sz < info->remaining) { memmove (info->buffer, &info->buffer[sz], info->remaining - sz); } info->remaining -= sz; - info->currentsample += n; - _info->readpos += (float)n / _info->samplerate; -// trace ("flac: [2] if=%d, of=%d, n=%d, rem=%d, size=%d\n", n_input_frames, n_output_frames, n, info->remaining, size); + int n = sz / samplesize; + info->currentsample += sz / samplesize; + _info->readpos += (float)n / _info->fmt.samplerate; } if (!size) { break; @@ -372,17 +400,18 @@ cflac_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { return initsize - size; } +#if 0 static int cflac_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { flac_info_t *info = (flac_info_t *)_info; - if (size / (4 * _info->channels) + info->currentsample > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 4 * _info->channels; + if (size / (4 * _info->fmt.channels) + info->currentsample > info->endsample) { + size = (info->endsample - info->currentsample + 1) * 4 * _info->fmt.channels; trace ("size truncated to %d bytes, cursample=%d, endsample=%d\n", size, info->currentsample, info->endsample); if (size <= 0) { return 0; } } - int n_output_channels = _info->channels; + int n_output_channels = _info->fmt.channels; if (n_output_channels > 2) { n_output_channels = 2; } @@ -411,7 +440,7 @@ cflac_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { } info->remaining -= sz; info->currentsample += n; - _info->readpos += (float)n / _info->samplerate; + _info->readpos += (float)n / _info->fmt.samplerate; } if (!size) { break; @@ -432,6 +461,7 @@ cflac_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { return initsize - size; } +#endif static int cflac_seek_sample (DB_fileinfo_t *_info, int sample) { @@ -442,13 +472,13 @@ cflac_seek_sample (DB_fileinfo_t *_info, int sample) { if (!FLAC__stream_decoder_seek_absolute (info->decoder, (FLAC__uint64)(sample))) { return -1; } - _info->readpos = (float)(sample - info->startsample)/ _info->samplerate; + _info->readpos = (float)(sample - info->startsample)/ _info->fmt.samplerate; return 0; } static int cflac_seek (DB_fileinfo_t *_info, float time) { - return cflac_seek_sample (_info, time * _info->samplerate); + return cflac_seek_sample (_info, time * _info->fmt.samplerate); } static FLAC__StreamDecoderWriteStatus @@ -493,13 +523,53 @@ static const char *metainfo[] = { "DISCNUMBER", "disc", "COPYRIGHT", "copyright", "TOTALTRACKS", "numtracks", + "TRACKTOTAL", "numtracks", "ALBUM ARTIST", "band", NULL }; static void +cflac_add_metadata (DB_playItem_t *it, char *s, int length) { + int m; + for (m = 0; metainfo[m]; m += 2) { + int l = strlen (metainfo[m]); + if (length > l && !strncasecmp (metainfo[m], s, l) && s[l] == '=') { + deadbeef->pl_append_meta (it, metainfo[m+1], s + l + 1); + break; + } + } + if (!metainfo[m]) { + if (!strncasecmp (s, "CUESHEET=", 9)) { + deadbeef->pl_add_meta (it, "cuesheet", s + 9); + } + else if (!strncasecmp (s, "replaygain_album_gain=", 22)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (s+22)); + } + else if (!strncasecmp (s, "replaygain_album_peak=", 22)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (s+22)); + } + else if (!strncasecmp (s, "replaygain_track_gain=", 22)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (s+22)); + } + else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (s+22)); + } + else { + const char *eq = strchr (s, '='); + if (eq) { + char key[eq - s+1]; + strncpy (key, s, eq-s); + key[eq-s] = 0; + deadbeef->pl_append_meta (it, key, eq+1); + } + } + } +} + +static void cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { flac_info_t *info = (flac_info_t *)client_data; + info->tagsize += metadata->length; DB_fileinfo_t *_info = &info->info; if (info->init_stop_decoding) { trace ("error flag is set, ignoring init_metadata callback..\n"); @@ -508,9 +578,10 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str DB_playItem_t *it = info->it; //it->tracknum = 0; if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { - trace ("flac: samplerate=%d, channels=%d\n", metadata->data.stream_info.sample_rate, metadata->data.stream_info.channels); - _info->samplerate = metadata->data.stream_info.sample_rate; - _info->channels = metadata->data.stream_info.channels; + trace ("flac: samplerate=%d, channels=%d, totalsamples=%d\n", metadata->data.stream_info.sample_rate, metadata->data.stream_info.channels, metadata->data.stream_info.total_samples); + _info->fmt.samplerate = metadata->data.stream_info.sample_rate; + _info->fmt.channels = metadata->data.stream_info.channels; + _info->fmt.bps = metadata->data.stream_info.bits_per_sample; info->totalsamples = metadata->data.stream_info.total_samples; deadbeef->pl_set_item_duration (it, metadata->data.stream_info.total_samples / (float)metadata->data.stream_info.sample_rate); } @@ -520,30 +591,7 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str const FLAC__StreamMetadata_VorbisComment_Entry *c = &vc->comments[i]; if (c->length > 0) { char *s = c->entry; - int m; - for (m = 0; metainfo[m]; m += 2) { - int l = strlen (metainfo[m]); - if (c->length > l && !strncasecmp (metainfo[m], s, l) && s[l] == '=') { - deadbeef->pl_append_meta (it, metainfo[m+1], s + l + 1); - } - } - if (!metainfo[m]) { - if (!strncasecmp (s, "CUESHEET=", 9)) { - deadbeef->pl_add_meta (it, "cuesheet", s + 9); - } - else if (!strncasecmp (s, "replaygain_album_gain=", 22)) { - it->replaygain_album_gain = atof (s + 22); - } - else if (!strncasecmp (s, "replaygain_album_peak=", 22)) { - it->replaygain_album_peak = atof (s + 22); - } - else if (!strncasecmp (s, "replaygain_track_gain=", 22)) { - it->replaygain_track_gain = atof (s + 22); - } - else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { - it->replaygain_track_peak = atof (s + 22); - } - } + cflac_add_metadata (it, s, c->length); } } deadbeef->pl_add_meta (it, "title", NULL); @@ -621,9 +669,7 @@ cflac_insert (DB_playItem_t *after, const char *fname) { // read all metadata FLAC__stream_decoder_set_md5_checking(decoder, 0); FLAC__stream_decoder_set_metadata_respond_all (decoder); - it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); + it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); info.it = it; if (skip > 0) { deadbeef->fseek (info.file, skip, SEEK_SET); @@ -648,12 +694,25 @@ cflac_insert (DB_playItem_t *after, const char *fname) { } FLAC__stream_decoder_delete(decoder); decoder = NULL; - it->filetype = isogg ? "OggFLAC" : "FLAC"; + deadbeef->pl_add_meta (it, ":FILETYPE", isogg ? "OggFLAC" : "FLAC"); + + char s[100]; + int64_t fsize = deadbeef->fgetlength (info.file); + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + snprintf (s, sizeof (s), "%d", info.info.fmt.channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", info.info.fmt.bps); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", info.info.fmt.samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + snprintf (s, sizeof (s), "%d", (int)roundf((fsize-info.tagsize) / deadbeef->pl_get_item_duration (it) * 8 / 1000)); + deadbeef->pl_add_meta (it, ":BITRATE", s); // try embedded cue const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); if (cuesheet) { - DB_playItem_t *last = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), info.totalsamples, info.info.samplerate); + DB_playItem_t *last = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), info.totalsamples, info.info.fmt.samplerate); if (last) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (last); @@ -662,7 +721,7 @@ cflac_insert (DB_playItem_t *after, const char *fname) { } // try external cue - DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, info.totalsamples, info.info.samplerate); + DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, info.totalsamples, info.info.fmt.samplerate); if (cue_after) { if (info.file) { deadbeef->fclose (info.file); @@ -702,7 +761,7 @@ cflac_read_metadata (DB_playItem_t *it) { trace ("cflac_read_metadata: FLAC__metadata_chain_new failed\n"); return -1; } - FLAC__bool res = FLAC__metadata_chain_read (chain, it->fname); + FLAC__bool res = FLAC__metadata_chain_read (chain, deadbeef->pl_find_meta (it, ":URI")); if (!res) { trace ("cflac_read_metadata: FLAC__metadata_chain_read failed\n"); goto error; @@ -719,17 +778,20 @@ cflac_read_metadata (DB_playItem_t *it) { do { FLAC__StreamMetadata *data = FLAC__metadata_iterator_get_block (iter); if (data && data->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - // delete all crap - for (int m = 0; metainfo[m]; m += 2) { - int offs = 0; - do { - offs = FLAC__metadata_object_vorbiscomment_find_entry_from (data, offs, metainfo[m]); - if (offs != -1) { - FLAC__StreamMetadata_VorbisComment_Entry *comm = &data->data.vorbis_comment.comments[offs]; - deadbeef->pl_append_meta (it, metainfo[m+1], comm->entry + strlen (metainfo[m])+1); - offs++; - } - } while (offs != -1); + const FLAC__StreamMetadata_VorbisComment *vc = &data->data.vorbis_comment; + for (int i = 0; i < vc->num_comments; i++) { + const FLAC__StreamMetadata_VorbisComment_Entry *c = &vc->comments[i]; + if (c->length > 0) { + char *s = c->entry; + cflac_add_metadata (it, s, c->length); + } + } + deadbeef->pl_add_meta (it, "title", NULL); + if (vc->num_comments > 0) { + uint32_t f = deadbeef->pl_get_item_flags (it); + f &= ~DDB_TAG_MASK; + f |= DDB_TAG_VORBISCOMMENTS; + deadbeef->pl_set_item_flags (it, f); } } } while (FLAC__metadata_iterator_next (iter)); @@ -764,7 +826,7 @@ cflac_write_metadata (DB_playItem_t *it) { trace ("cflac_write_metadata: FLAC__metadata_chain_new failed\n"); return -1; } - FLAC__bool res = FLAC__metadata_chain_read (chain, it->fname); + FLAC__bool res = FLAC__metadata_chain_read (chain, deadbeef->pl_find_meta (it, ":URI")); if (!res) { trace ("cflac_write_metadata: FLAC__metadata_chain_read failed\n"); goto error; @@ -776,53 +838,80 @@ cflac_write_metadata (DB_playItem_t *it) { trace ("cflac_write_metadata: FLAC__metadata_iterator_new failed\n"); goto error; } + FLAC__StreamMetadata *data = NULL; + // find existing vorbiscomment block FLAC__metadata_iterator_init (iter, chain); do { - FLAC__StreamMetadata *data = FLAC__metadata_iterator_get_block (iter); + data = FLAC__metadata_iterator_get_block (iter); if (data && data->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - for (int m = 0; metainfo[m]; m += 2) { - const char *val = deadbeef->pl_find_meta (it, metainfo[m+1]); - if (val) { - do {} while (1 == FLAC__metadata_object_vorbiscomment_remove_entry_matching (data, metainfo[m])); + break; + } + } while (FLAC__metadata_iterator_next (iter)); + + if (data) { + // delete all comments + FLAC__metadata_object_vorbiscomment_resize_comments (data, 0); + } + else { + // create new and add to chain + data = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); + if (!data) { + fprintf (stderr, "flac: failed to allocate new vorbis comment block\n"); + goto error; + } + if(!FLAC__metadata_iterator_insert_block_after(iter, data)) { + fprintf (stderr, "flac: failed to append vorbis comment block to chain\n"); + goto error; + } + } + + DB_metaInfo_t *m = deadbeef->pl_get_metadata_head (it); + while (m) { + if (m->key[0] != ':') { + int i; + for (i = 0; metainfo[i]; i += 2) { + if (!strcasecmp (metainfo[i+1], m->key)) { + break; } - if (val && *val) { - while (val) { - const char *next = strchr (val, '\n'); - int l; - if (next) { - l = next - val; - next++; - } - else { - l = strlen (val); - } - if (l > 0) { - char s[100+l+1]; - int n = snprintf (s, sizeof (s), "%s=", metainfo[m]); - strncpy (s+n, val, l); - *(s+n+l) = 0; - FLAC__StreamMetadata_VorbisComment_Entry ent = { - .length = strlen (s), - .entry = (FLAC__byte*)s - }; - //FLAC__metadata_object_vorbiscomment_replace_comment (data, ent, 1, 1); - FLAC__metadata_object_vorbiscomment_append_comment (data, ent, 1); - } - val = next; + } + const char *val = m->value; + if (val && *val) { + while (val) { + const char *next = strchr (val, '\n'); + int l; + if (next) { + l = next - val; + next++; + } + else { + l = strlen (val); + } + if (l > 0) { + char s[100+l+1]; + int n = snprintf (s, sizeof (s), "%s=", metainfo[i] ? metainfo[i] : m->key); + strncpy (s+n, val, l); + *(s+n+l) = 0; + FLAC__StreamMetadata_VorbisComment_Entry ent = { + .length = strlen (s), + .entry = (FLAC__byte*)s + }; + FLAC__metadata_object_vorbiscomment_append_comment (data, ent, 1); } + val = next; } } } - } while (FLAC__metadata_iterator_next (iter)); + m = m->next; + } - FLAC__metadata_iterator_delete (iter); if (!FLAC__metadata_chain_write (chain, 1, 0)) { trace ("cflac_write_metadata: FLAC__metadata_chain_write failed\n"); goto error; } err = 0; error: + FLAC__metadata_iterator_delete (iter); if (chain) { FLAC__metadata_chain_delete (chain); } @@ -837,20 +926,35 @@ static const char *filetypes[] = { "FLAC", "OggFLAC", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "stdflac", .plugin.name = "FLAC decoder", .plugin.descr = "FLAC decoder using libFLAC", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = cflac_open, .init = cflac_init, .free = cflac_free, - .read_int16 = cflac_read_int16, - .read_float32 = cflac_read_float32, + .read = cflac_read, +// .read_float32 = cflac_read_float32, .seek = cflac_seek, .seek_sample = cflac_seek_sample, .insert = cflac_insert, diff --git a/plugins/gme/Makefile.am b/plugins/gme/Makefile.am index 61351f9c..b325cbdf 100644 --- a/plugins/gme/Makefile.am +++ b/plugins/gme/Makefile.am @@ -112,8 +112,9 @@ game-music-emu-0.5.5/gme/Ym2413_Emu.h\ game-music-emu-0.5.5/gme/Ym2612_Emu.h\ game-music-emu-0.5.5/gme/gme_types.h -gme_la_LDFLAGS = -module +gme_la_LDFLAGS = -module -nostdlib -lsupc++ +gme_la_LIBADD = $(ZLIB_LIBS) -gme_la_LIBADD = $(LDADD) -lstdc++ -AM_CFLAGS = $(CFLAGS) -I$(gmepath) -std=c99 -DGME_VERSION_055 +AM_CFLAGS = $(CFLAGS) $(ZLIB_CFLAGS) -I$(gmepath) -std=c99 -DGME_VERSION_055 +AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables endif diff --git a/plugins/gme/cgme.c b/plugins/gme/cgme.c index 5d2920f0..8eb6d8d3 100644 --- a/plugins/gme/cgme.c +++ b/plugins/gme/cgme.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,8 +19,21 @@ #include <stdlib.h> #include <string.h> #include "gme/gme.h" +#include <zlib.h> #include "../../deadbeef.h" +int _Unwind_Resume_or_Rethrow; +int _Unwind_RaiseException; +int _Unwind_GetLanguageSpecificData; +int _Unwind_Resume; +int _Unwind_DeleteException; +int _Unwind_GetTextRelBase; +int _Unwind_SetIP; +int _Unwind_GetDataRelBase; +int _Unwind_GetRegionStart; +int _Unwind_SetGR; +int _Unwind_GetIPInfo; + //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -36,35 +49,124 @@ typedef struct { } gme_fileinfo_t; static DB_fileinfo_t * -cgme_open (void) { +cgme_open (uint32_t hint) { DB_fileinfo_t *_info = malloc (sizeof (gme_fileinfo_t)); memset (_info, 0, sizeof (gme_fileinfo_t)); return _info; } static int +read_gzfile (const char *fname, char **buffer, int *size) { + FILE *fp = fopen (fname, "rb"); + if (!fp) { + trace ("failed to fopen %s\n", fname); + return -1; + } + fseek (fp, 0, SEEK_END); + size_t sz = ftell (fp); + fclose (fp); + + sz *= 2; + int readsize = sz; + *buffer = malloc (sz); + if (!(*buffer)) { + return -1; + } + + gzFile gz = gzopen (fname, "rb"); + if (!gz) { + trace ("failed to gzopen %s\n", fname); + return -1; + } + *size = 0; + int nb; + int pos = 0; + do { + nb = gzread (gz, *buffer + pos, readsize); + if (nb < 0) { + free (*buffer); + trace ("failed to gzread from %s\n", fname); + return -1; + } + if (nb > 0) { + pos += nb; + *size += nb; + } + if (nb != readsize) { + break; + } + else { + readsize = sz; + sz *= 2; + *buffer = realloc (*buffer, sz); + } + } while (nb > 0); + gzclose (gz); + trace ("got %d bytes from %s\n", *size, fname); + + return 0; +} + +static int cgme_init (DB_fileinfo_t *_info, DB_playItem_t *it) { gme_fileinfo_t *info = (gme_fileinfo_t*)_info; int samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); - if (gme_open_file (it->fname, &info->emu, samplerate)) { + + gme_err_t res; + const char *ext = strrchr (deadbeef->pl_find_meta (it, ":URI"), '.'); + if (ext && !strcasecmp (ext, ".vgz")) { + trace ("opening gzipped vgm...\n"); + char *buffer; + int sz; + if (!read_gzfile (deadbeef->pl_find_meta (it, ":URI"), &buffer, &sz)) { + res = gme_open_data (buffer, sz, &info->emu, samplerate); + free (buffer); + } + } + else { + DB_FILE *f = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); + int64_t sz = deadbeef->fgetlength (f); + if (sz <= 0) { + deadbeef->fclose (f); + return -1; + } + char *buf = malloc (sz); + if (!buf) { + deadbeef->fclose (f); + return -1; + } + int64_t rb = deadbeef->fread (buf, 1, sz, f); + deadbeef->fclose(f); + if (rb != sz) { + free (buf); + return -1; + } + + res = gme_open_data (buf, sz, &info->emu, samplerate); + free (buf); + } + + if (res) { + trace ("failed with error %d\n", res); return -1; } gme_mute_voices (info->emu, info->cgme_voicemask); - gme_start_track (info->emu, it->tracknum); + gme_start_track (info->emu, deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)); #ifdef GME_VERSION_055 gme_info_t *inf; - gme_track_info (info->emu, &inf, it->tracknum); + gme_track_info (info->emu, &inf, deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)); #else track_info_t _inf; - gme_track_info (info->emu, &inf, it->tracknum); + gme_track_info (info->emu, &inf, deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)); track_info_t *inf = &_inf; #endif _info->plugin = &plugin; - _info->bps = 16; - _info->channels = 2; - _info->samplerate = samplerate; + _info->fmt.bps = 16; + _info->fmt.channels = 2; + _info->fmt.samplerate = samplerate; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); info->duration = deadbeef->pl_get_item_duration (it); info->reallength = inf->length; _info->readpos = 0; @@ -83,7 +185,7 @@ cgme_free (DB_fileinfo_t *_info) { static int cgme_read (DB_fileinfo_t *_info, char *bytes, int size) { gme_fileinfo_t *info = (gme_fileinfo_t*)_info; - float t = (size/4) / (float)_info->samplerate; + float t = (size/4) / (float)_info->fmt.samplerate; if (_info->readpos + t >= info->duration) { t = info->duration - _info->readpos; if (t <= 0) { @@ -114,11 +216,76 @@ cgme_seek (DB_fileinfo_t *_info, float time) { return 0; } +static void +cgme_add_meta (DB_playItem_t *it, const char *key, const char *value) { + if (!value) { + return; + } + char len = strlen (value); + char out[1024]; + // check for utf8 (hack) + if (deadbeef->junk_iconv (value, len, out, sizeof (out), "utf-8", "utf-8") >= 0) { + deadbeef->pl_add_meta (it, key, out); + return; + } + + if (deadbeef->junk_iconv (value, len, out, sizeof (out), "iso8859-1", "utf-8") >= 0) { + deadbeef->pl_add_meta (it, key, out); + return; + } + + if (deadbeef->junk_iconv (value, len, out, sizeof (out), "SHIFT-JIS", "utf-8") >= 0) { + deadbeef->pl_add_meta (it, key, out); + return; + } + + // FIXME: try other encodings? + + return; +} + static DB_playItem_t * cgme_insert (DB_playItem_t *after, const char *fname) { Music_Emu *emu; trace ("gme_open_file %s\n", fname); - if (!gme_open_file (fname, &emu, gme_info_only)) { + + gme_err_t res; + + const char *ext = strrchr (fname, '.'); + if (ext && !strcasecmp (ext, ".vgz")) { + trace ("opening gzipped vgm...\n"); + char *buffer; + int sz; + if (!read_gzfile (fname, &buffer, &sz)) { + res = gme_open_data (buffer, sz, &emu, gme_info_only); + free (buffer); + } + } + else { + DB_FILE *f = deadbeef->fopen (fname); + int64_t sz = deadbeef->fgetlength (f); + if (sz <= 0) { + deadbeef->fclose (f); + return NULL; + } + char *buf = malloc (sz); + if (!buf) { + deadbeef->fclose (f); + return NULL; + } + int64_t rb = deadbeef->fread (buf, 1, sz, f); + deadbeef->fclose(f); + if (rb != sz) { + free (buf); + return NULL; + } + + res = gme_open_data (buf, sz, &emu, gme_info_only); + free (buf); + } + + + if (!res) { int cnt = gme_track_count (emu); trace ("track cnt %d\n", cnt); for (int i = 0; i < cnt; i++) { @@ -131,9 +298,7 @@ cgme_insert (DB_playItem_t *after, const char *fname) { track_info_t *inf = &_inf; #endif if (!ret) { - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); char str[1024]; if (inf->song[0]) { snprintf (str, 1024, "%d %s - %s", i, inf->game, inf->song); @@ -142,11 +307,11 @@ cgme_insert (DB_playItem_t *after, const char *fname) { snprintf (str, 1024, "%d %s - ?", i, inf->game); } trace ("track subtune %d %s, length=%d\n", i, str, inf->length); - it->tracknum = i; + deadbeef->pl_set_meta_int (it, ":TRACKNUM", i); // add metadata - deadbeef->pl_add_meta (it, "system", inf->system); - deadbeef->pl_add_meta (it, "album", inf->game); + cgme_add_meta (it, "system", inf->system); + cgme_add_meta (it, "album", inf->game); int tl = sizeof (inf->song); int n; for (n = 0; i < tl && inf->song[n] && inf->song[n] == ' '; n++); @@ -154,16 +319,16 @@ cgme_insert (DB_playItem_t *after, const char *fname) { deadbeef->pl_add_meta (it, "title", NULL); } else { - deadbeef->pl_add_meta (it, "title", inf->song); + cgme_add_meta (it, "title", inf->song); } - deadbeef->pl_add_meta (it, "artist", inf->author); - deadbeef->pl_add_meta (it, "copyright", inf->copyright); - deadbeef->pl_add_meta (it, "comment", inf->comment); - deadbeef->pl_add_meta (it, "dumper", inf->dumper); + cgme_add_meta (it, "artist", inf->author); + cgme_add_meta (it, "copyright", inf->copyright); + cgme_add_meta (it, "comment", inf->comment); + cgme_add_meta (it, "dumper", inf->dumper); char trk[10]; snprintf (trk, 10, "%d", i+1); - deadbeef->pl_add_meta (it, "track", trk); - if (inf->length == -1) { + cgme_add_meta (it, "track", trk); + if (inf->length == -1 || inf->length == 0) { float songlength = deadbeef->conf_get_float ("gme.songlength", 3); deadbeef->pl_set_item_duration (it, songlength * 60.f); } @@ -174,12 +339,12 @@ cgme_insert (DB_playItem_t *after, const char *fname) { while (ext >= fname && *ext != '.') { ext--; } - it->filetype = NULL; if (*ext == '.') { ext++; for (int i = 0; plugin.exts[i]; i++) { if (!strcasecmp (ext, plugin.exts[i])) { - it->filetype = plugin.exts[i]; + deadbeef->pl_add_meta (it, ":FILETYPE", plugin.exts[i]); + break; } } } @@ -187,7 +352,7 @@ cgme_insert (DB_playItem_t *after, const char *fname) { deadbeef->pl_item_unref (it); } else { - printf ("gme error: %s\n", ret); + trace ("gme error: %s\n", ret); } } if (emu) { @@ -195,7 +360,7 @@ cgme_insert (DB_playItem_t *after, const char *fname) { } } else { - printf ("error adding %s\n", fname); + trace ("gme_open_file/data failed\n"); } return after; } @@ -241,14 +406,31 @@ static const char settings_dlg[] = // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "stdgme", .plugin.name = "Game_Music_Emu decoder", .plugin.descr = "chiptune music player based on GME", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses Game-Music-Emu v0.5.5 by Shay Green <gblargg@gmail.com>, http://www.slack.net/~ant/libs\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = cgme_start, .plugin.stop = cgme_stop, @@ -256,7 +438,7 @@ static DB_decoder_t plugin = { .open = cgme_open, .init = cgme_init, .free = cgme_free, - .read_int16 = cgme_read, + .read = cgme_read, .seek = cgme_seek, .insert = cgme_insert, .exts = exts, diff --git a/plugins/gme/game-music-emu-0.5.5/gme/Sap_Apu.cpp b/plugins/gme/game-music-emu-0.5.5/gme/Sap_Apu.cpp index fa9bc4b1..fb56f5dc 100644 --- a/plugins/gme/game-music-emu-0.5.5/gme/Sap_Apu.cpp +++ b/plugins/gme/game-music-emu-0.5.5/gme/Sap_Apu.cpp @@ -90,7 +90,7 @@ void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) memset( &oscs [i], 0, offsetof (osc_t,output) ); } -inline void Sap_Apu::calc_periods() +void Sap_Apu::calc_periods() { // 15/64 kHz clock int divider = 28; diff --git a/plugins/gtkui/Makefile.am b/plugins/gtkui/Makefile.am index 609917ed..3cfc1cce 100644 --- a/plugins/gtkui/Makefile.am +++ b/plugins/gtkui/Makefile.am @@ -1,10 +1,7 @@ if HAVE_GTKUI gtkuidir = $(libdir)/$(PACKAGE) -pkglib_LTLIBRARIES = gtkui.la - gtkui_VALASOURCES = ddbequalizer.vala ddbseekbar.vala ddbcellrenderertextmultiline.vala gtkui_VALABUILTSOURCES = $(gtkui_VALASOURCES:.vala=.c) ddbequalizer.h ddbseekbar.h ddbcellrenderertextmultiline.h - if MAINTAINER_MODE BUILT_SOURCES = vala.stamp vala.stamp: $(gtkui_VALASOURCES) @@ -12,10 +9,13 @@ vala.stamp: $(gtkui_VALASOURCES) $(VALAC) -C -H ddbequalizer.h --library ddbequalizer gtkui.vapi --pkg=gtk+-2.0 ddbequalizer.vala $(VALAC) -C -H ddbseekbar.h --library ddbseekbar gtkui.vapi --pkg=gtk+-2.0 ddbseekbar.vala touch $@ + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(gtkui_VALABUILTSOURCES) endif -gtkui_la_SOURCES = $(gtkui_VALABUILTSOURCES)\ - gtkui.c gtkui.h\ +GTKUI_SOURCES = gtkui.c gtkui.h\ callbacks.c interface.c support.c callbacks.h interface.h support.h\ ddblistview.c ddblistview.h\ mainplaylist.c mainplaylist.h\ @@ -32,18 +32,39 @@ gtkui_la_SOURCES = $(gtkui_VALABUILTSOURCES)\ plcommon.c plcommon.h\ prefwin.c\ eq.c eq.h\ - actions.c actions.h - -gtkui_la_LDFLAGS = -module + actions.c actions.h\ + dspconfig.c dspconfig.h\ + tagwritersettings.c tagwritersettings.h\ + wingeom.c wingeom.h -gtkui_la_LIBADD = $(LDADD) $(GTKUI_DEPS_LIBS) $(NOTIFY_DEPS_LIBS) -AM_CFLAGS = -std=c99 $(GTKUI_DEPS_CFLAGS) $(NOTIFY_DEPS_CFLAGS) +sdkdir = $(pkgincludedir) +sdk_HEADERS = gtkui_api.h EXTRA_DIST = $(gtkui_VALASOURCES) deadbeef.glade -if MAINTAINER_MODE -CLEANFILES = \ - $(BUILT_SOURCES) \ - $(gtkui_VALABUILTSOURCES) +if STATICLINK +pkglib_LTLIBRARIES = ddb_gui_GTK2.la ddb_gui_GTK2.fallback.la +else +pkglib_LTLIBRARIES = ddb_gui_GTK2.la endif + +# normal lib +ddb_gui_GTK2_la_SOURCES = $(gtkui_VALABUILTSOURCES) $(GTKUI_SOURCES) +ddb_gui_GTK2_la_LDFLAGS = -module +ddb_gui_GTK2_la_LIBADD = $(LDADD) $(GTKUI_DEPS_LIBS) +ddb_gui_GTK2_la_CFLAGS = -std=c99 $(GTKUI_DEPS_CFLAGS) + +# fallback lib +if STATICLINK +GTK_ROOT=../../../deadbeef-deps/gtk-debian/usr + +ddb_gui_GTK2_fallback_la_SOURCES = $(gtkui_VALABUILTSOURCES) $(GTKUI_SOURCES) +ddb_gui_GTK2_fallback_la_LDFLAGS = -module + +ddb_gui_GTK2_fallback_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib $(GTK_ROOT)/lib/libgtk-x11-2.0.la $(GTK_ROOT)/lib/libgdk-x11-2.0.la $(GTK_ROOT)/lib/libpangoft2-1.0.la $(GTK_ROOT)/lib/libpangocairo-1.0.la $(GTK_ROOT)/lib/libgdk_pixbuf-2.0.la -lm $(GTK_ROOT)/lib/libcairo.la $(GTK_ROOT)/lib/libpango-1.0.la $(GTK_ROOT)/lib/libgobject-2.0.la $(GTK_ROOT)/lib/libgmodule-2.0.la $(GTK_ROOT)/lib/libgthread-2.0.la -lrt $(GTK_ROOT)/lib/libglib-2.0.la + +ddb_gui_GTK2_fallback_la_CFLAGS = -std=c99 -I $(GTK_ROOT)/include -I $(GTK_ROOT)/lib/gtk-2.0/include -I $(GTK_ROOT)/include/glib-2.0 -I $(GTK_ROOT)/include/gtk-2.0 -I $(GTK_ROOT)/include/cairo -I $(GTK_ROOT)/lib/glib-2.0/include/ -I $(GTK_ROOT)/include/pango-1.0 -I $(GTK_ROOT)/include/atk-1.0 -DULTRA_COMPATIBLE=1 + +endif + endif diff --git a/plugins/gtkui/actions.c b/plugins/gtkui/actions.c index ac2904d2..0ddbdceb 100644 --- a/plugins/gtkui/actions.c +++ b/plugins/gtkui/actions.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net>, + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>, Viktor Semykin <thesame.ml@gmail.com> This program is free software; you can redistribute it and/or diff --git a/plugins/gtkui/actions.h b/plugins/gtkui/actions.h index b064647f..aacff34b 100644 --- a/plugins/gtkui/actions.h +++ b/plugins/gtkui/actions.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net>, + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>, Viktor Semykin <thesame.ml@gmail.com> This program is free software; you can redistribute it and/or diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index dd26a422..3c775f16 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,6 +43,7 @@ #include "parser.h" #include "drawing.h" #include "eq.h" +#include "wingeom.h" //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -58,26 +59,37 @@ extern DB_functions_t *deadbeef; // defined in gtkui.c static gboolean file_filter_func (const GtkFileFilterInfo *filter_info, gpointer data) { // get ext - const char *p = filter_info->filename + strlen (filter_info->filename)-1; - while (p >= filter_info->filename) { - if (*p == '.') { - break; - } - p--; - } - if (*p != '.') { + const char *p = strrchr (filter_info->filename, '.'); + if (!p) { return FALSE; } p++; + + // get beginning of fname + const char *fn = strrchr (filter_info->filename, '/'); + if (!fn) { + fn = filter_info->filename; + } + else { + fn++; + } + + DB_decoder_t **codecs = deadbeef->plug_get_decoder_list (); for (int i = 0; codecs[i]; i++) { if (codecs[i]->exts && codecs[i]->insert) { const char **exts = codecs[i]->exts; - if (exts) { - for (int e = 0; exts[e]; e++) { - if (!strcasecmp (exts[e], p)) { - return TRUE; - } + for (int e = 0; exts[e]; e++) { + if (!strcasecmp (exts[e], p)) { + return TRUE; + } + } + } + if (codecs[i]->prefixes && codecs[i]->insert) { + const char **prefixes = codecs[i]->prefixes; + for (int e = 0; prefixes[e]; e++) { + if (!strncasecmp (prefixes[e], fn, strlen(prefixes[e])) && *(fn + strlen (prefixes[e])) == '.') { + return TRUE; } } } @@ -88,6 +100,18 @@ file_filter_func (const GtkFileFilterInfo *filter_info, gpointer data) { if (!strcasecmp (p, "m3u")) { return TRUE; } + + // test container (vfs) formats + DB_vfs_t **vfsplugs = deadbeef->plug_get_vfs_list (); + for (int i = 0; vfsplugs[i]; i++) { + if (vfsplugs[i]->is_container) { + if (vfsplugs[i]->is_container (filter_info->filename)) { + return TRUE; + } + } + } + + return FALSE; } @@ -121,7 +145,9 @@ on_open_activate (GtkMenuItem *menuitem, gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), TRUE); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); int response = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); @@ -155,7 +181,9 @@ on_add_files_activate (GtkMenuItem *menuitem, gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), TRUE); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); int response = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); @@ -189,19 +217,26 @@ on_add_folders_activate (GtkMenuItem *menuitem, { GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Add folder(s) to playlist..."), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + GtkWidget *box = gtk_hbox_new (FALSE, 8); + gtk_widget_show (box); + GtkWidget *check = gtk_check_button_new_with_mnemonic (_("Follow symlinks")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), deadbeef->conf_get_int ("add_folders_follow_symlinks", 0)); g_signal_connect ((gpointer) check, "toggled", G_CALLBACK (on_follow_symlinks_toggled), NULL); gtk_widget_show (check); - gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dlg), check); + gtk_box_pack_start (GTK_BOX (box), check, FALSE, FALSE, 0); + + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dlg), box); set_file_filter (dlg, NULL); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), TRUE); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); int response = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); @@ -248,10 +283,10 @@ on_select_all1_activate (GtkMenuItem *menuitem, { deadbeef->pl_select_all (); DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (pl, DDB_REFRESH_LIST); pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (pl) { - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (pl, DDB_REFRESH_LIST); } } @@ -263,7 +298,7 @@ void on_stopbtn_clicked (GtkButton *button, gpointer user_data) { - deadbeef->sendmessage (M_STOPSONG, 0, 0, 0); + deadbeef->sendmessage (M_STOP, 0, 0, 0); } @@ -271,7 +306,7 @@ void on_playbtn_clicked (GtkButton *button, gpointer user_data) { - deadbeef->sendmessage (M_PLAYSONG, 0, 0, 0); + deadbeef->sendmessage (M_PLAY_CURRENT, 0, 0, 0); } @@ -279,7 +314,7 @@ void on_pausebtn_clicked (GtkButton *button, gpointer user_data) { - deadbeef->sendmessage (M_PAUSESONG, 0, 0, 0); + deadbeef->sendmessage (M_TOGGLE_PAUSE, 0, 0, 0); } @@ -287,7 +322,7 @@ void on_prevbtn_clicked (GtkButton *button, gpointer user_data) { - deadbeef->sendmessage (M_PREVSONG, 0, 0, 0); + deadbeef->sendmessage (M_PREV, 0, 0, 0); } @@ -295,7 +330,7 @@ void on_nextbtn_clicked (GtkButton *button, gpointer user_data) { - deadbeef->sendmessage (M_NEXTSONG, 0, 0, 0); + deadbeef->sendmessage (M_NEXT, 0, 0, 0); } @@ -303,7 +338,7 @@ void on_playrand_clicked (GtkButton *button, gpointer user_data) { - deadbeef->sendmessage (M_PLAYRANDOM, 0, 0, 0); + deadbeef->sendmessage (M_PLAY_RANDOM, 0, 0, 0); } gboolean @@ -314,7 +349,7 @@ on_mainwin_key_press_event (GtkWidget *widget, uint32_t maskedstate = (event->state &~ (GDK_LOCK_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD5_MASK)) & 0xfff; if ((maskedstate == GDK_MOD1_MASK || maskedstate == 0) && event->keyval == GDK_n) { // button for that one is not in toolbar anymore, so handle it manually - deadbeef->sendmessage (M_PLAYRANDOM, 0, 0, 0); + deadbeef->sendmessage (M_PLAY_RANDOM, 0, 0, 0); } else if ((maskedstate == GDK_MOD1_MASK || maskedstate == 0) && event->keyval >= GDK_1 && event->keyval <= GDK_9) { int pl = event->keyval - GDK_1; @@ -334,7 +369,8 @@ void on_order_linear_activate (GtkMenuItem *menuitem, gpointer user_data) { - deadbeef->conf_set_int ("playback.order", 0); + deadbeef->conf_set_int ("playback.order", PLAYBACK_ORDER_LINEAR); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } @@ -342,15 +378,24 @@ void on_order_shuffle_activate (GtkMenuItem *menuitem, gpointer user_data) { - deadbeef->conf_set_int ("playback.order", 1); + deadbeef->conf_set_int ("playback.order", PLAYBACK_ORDER_SHUFFLE_TRACKS); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } +void +on_order_shuffle_albums_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + deadbeef->conf_set_int ("playback.order", PLAYBACK_ORDER_SHUFFLE_ALBUMS); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); +} void on_order_random_activate (GtkMenuItem *menuitem, gpointer user_data) { - deadbeef->conf_set_int ("playback.order", 2); + deadbeef->conf_set_int ("playback.order", PLAYBACK_ORDER_RANDOM); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } @@ -601,21 +646,7 @@ on_mainwin_configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data) { -#if GTK_CHECK_VERSION(2,2,0) - GdkWindowState window_state = gdk_window_get_state (GDK_WINDOW (widget->window)); -#else - GdkWindowState window_state = gdk_window_get_state (G_OBJECT (widget)); -#endif - if (!(window_state & GDK_WINDOW_STATE_MAXIMIZED) && gtk_widget_get_visible (widget)) { - int x, y; - int w, h; - gtk_window_get_position (GTK_WINDOW (widget), &x, &y); - gtk_window_get_size (GTK_WINDOW (widget), &w, &h); - deadbeef->conf_set_int ("mainwin.geometry.x", x); - deadbeef->conf_set_int ("mainwin.geometry.y", y); - deadbeef->conf_set_int ("mainwin.geometry.w", w); - deadbeef->conf_set_int ("mainwin.geometry.h", h); - } + wingeom_save (widget, "mainwin"); return FALSE; } @@ -633,7 +664,6 @@ on_find_activate (GtkMenuItem *menuitem, gpointer user_data) { search_start (); - search_restore_attrs (); } void @@ -689,7 +719,7 @@ on_help1_activate (GtkMenuItem *menuitem, gpointer user_data) { char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", _("help.txt")); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), _("help.txt")); show_info_window (fname, _("Help"), &helpwindow); } @@ -702,7 +732,7 @@ on_about1_activate (GtkMenuItem *menuitem, char s[200]; snprintf (s, sizeof (s), _("About DeaDBeeF %s"), VERSION); char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "about.txt"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "about.txt"); show_info_window (fname, s, &aboutwindow); } @@ -715,7 +745,7 @@ on_changelog1_activate (GtkMenuItem *menuitem, char s[200]; snprintf (s, sizeof (s), _("DeaDBeeF %s ChangeLog"), VERSION); char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "ChangeLog"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "ChangeLog"); show_info_window (fname, s, &changelogwindow); } @@ -726,7 +756,7 @@ on_gpl1_activate (GtkMenuItem *menuitem, gpointer user_data) { char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "COPYING.GPLv2"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "COPYING.GPLv2"); show_info_window (fname, "GNU GENERAL PUBLIC LICENSE Version 2", &gplwindow); } @@ -737,7 +767,7 @@ on_lgpl1_activate (GtkMenuItem *menuitem, gpointer user_data) { char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "COPYING.LGPLv2.1"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "COPYING.LGPLv2.1"); show_info_window (fname, "GNU LESSER GENERAL PUBLIC LICENSE Version 2.1", &lgplwindow); } @@ -781,26 +811,7 @@ on_mainwin_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer user_data) { - // based on pidgin maximization handler -#if GTK_CHECK_VERSION(2,2,0) - if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { - if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { - deadbeef->conf_set_int ("mainwin.geometry.maximized", 1); - } - else { - deadbeef->conf_set_int ("mainwin.geometry.maximized", 0); - } - } -#else - GdkWindowState new_window_state = gdk_window_get_state(G_OBJECT(widget)); - - if (new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { - deadbeef->conf_set_int ("mainwin.geometry.maximized", 1); - } - else { - deadbeef->conf_set_int ("mainwin.geometry.maximized", 0); - } -#endif + wingeom_save_max (event, widget, "mainwin"); return FALSE; } @@ -935,10 +946,10 @@ on_deselect_all1_activate (GtkMenuItem *menuitem, } deadbeef->pl_unlock (); DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (pl, DDB_REFRESH_LIST); pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (pl) { - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (pl, DDB_REFRESH_LIST); } } @@ -962,7 +973,7 @@ on_invert_selection1_activate (GtkMenuItem *menuitem, } deadbeef->pl_unlock (); DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (pl, DDB_REFRESH_LIST); } @@ -1079,7 +1090,96 @@ on_translators1_activate (GtkMenuItem *menuitem, char s[200]; snprintf (s, sizeof (s), _("DeaDBeeF Translators")); char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "translators.txt"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "translators.txt"); show_info_window (fname, s, &translatorswindow); } + +GtkWidget* +title_formatting_help_link_create (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2) +{ + GtkWidget *link = gtk_link_button_new_with_label ("http://sourceforge.net/apps/mediawiki/deadbeef/index.php?title=Title_Formatting", "Help"); + return link; +} + + +void +on_album1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_artist1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_date1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + +void +on_custom2_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GtkWidget *dlg = create_sortbydlg (); + gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); + + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (dlg, "sortorder")); + GtkEntry *entry = GTK_ENTRY (lookup_widget (dlg, "sortfmt")); + + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("gtkui.sortby_order", 0)); + deadbeef->conf_lock (); + gtk_entry_set_text (entry, deadbeef->conf_get_str_fast ("gtkui.sortby_fmt", "")); + deadbeef->conf_unlock (); + + int r = gtk_dialog_run (GTK_DIALOG (dlg)); + + if (r == GTK_RESPONSE_OK) { + GtkComboBox *combo = GTK_COMBO_BOX (lookup_widget (dlg, "sortorder")); + GtkEntry *entry = GTK_ENTRY (lookup_widget (dlg, "sortfmt")); + int order = gtk_combo_box_get_active (combo); + const char *fmt = gtk_entry_get_text (entry); + + deadbeef->conf_set_int ("gtkui.sortby_order", order); + deadbeef->conf_set_str ("gtkui.sortby_fmt", fmt); + + deadbeef->pl_sort (PL_MAIN, -1, fmt, order == 0 ? 1 : 0); + + DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); + ddb_listview_clear_sort (pl); + ddb_listview_refresh (pl, DDB_REFRESH_LIST); + } + + gtk_widget_destroy (dlg); + dlg = NULL; +} + + +void +on_sortfmt_activate (GtkEntry *entry, + gpointer user_data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry)); + gtk_dialog_response (GTK_DIALOG (toplevel), GTK_RESPONSE_OK); +} + + + +GtkWidget* +create_plugin_weblink (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2) +{ + GtkWidget *link = gtk_link_button_new_with_label ("", "WWW"); + gtk_widget_set_sensitive (link, FALSE); + return link; +} diff --git a/plugins/gtkui/callbacks.h b/plugins/gtkui/callbacks.h index b88df53c..4f15d342 100644 --- a/plugins/gtkui/callbacks.h +++ b/plugins/gtkui/callbacks.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -291,16 +291,6 @@ void volumebar_expose (GtkWidget *widget, int x, int y, int w, int h); - -void -on_progress_abort (GtkButton *button, - gpointer user_data); - -gboolean -on_addprogress_delete_event (GtkWidget *widget, - GdkEvent *event, - gpointer user_data); - gboolean on_volumebar_scroll_event (GtkWidget *widget, GdkEventScroll *event, @@ -983,3 +973,157 @@ on_jump_to_current_track1_activate (GtkMenuItem *menuitem, void on_translators1_activate (GtkMenuItem *menuitem, gpointer user_data); + + +GtkWidget* +title_formatting_help_link_create (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2); + +void +on_album1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_artist1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_date1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_custom2_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_sortfmt_activate (GtkEntry *entry, + gpointer user_data); + +void +gtkui_dialog_response_ok (GtkEntry *entry, + gpointer user_data); + + +void +on_shuffle_albums1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_order_shuffle_albums_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_dsp_add_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_remove_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_configure_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_up_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_down_clicked (GtkButton *button, + gpointer user_data); + +void +on_auto_name_playlist_from_folder_toggled + (GtkToggleButton *togglebutton, + gpointer user_data); + +void +on_dsp_preset_changed (GtkComboBox *combobox, + gpointer user_data); + +void +on_dsp_preset_save_clicked (GtkButton *button, + gpointer user_data); + +void +on_dsp_preset_load_clicked (GtkButton *button, + gpointer user_data); + +void +on_plug_copyright_clicked (GtkButton *button, + gpointer user_data); + +GtkWidget* +create_plugin_weblink (gchar *widget_name, gchar *string1, gchar *string2, + gint int1, gint int2); + +gboolean +on_metalist_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data); + +void +on_tagwriter_settings_clicked (GtkButton *button, + gpointer user_data); + +gboolean +on_trackproperties_configure_event (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data); + +void +on_trackproperties_state_changed (GtkWidget *widget, + GtkStateType state, + gpointer user_data); + +gboolean +on_trackproperties_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer user_data); + +gboolean +on_prefwin_configure_event (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data); + +gboolean +on_prefwin_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer user_data); + +void +on_prefwin_realize (GtkWidget *widget, + gpointer user_data); + +gboolean +on_prefwin_map_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data); + +void +on_replaygain_preamp_value_changed (GtkRange *range, + gpointer user_data); + +void +on_tabstrip_text_color_set (GtkColorButton *colorbutton, + gpointer user_data); + +void +on_gui_plugin_changed (GtkComboBox *combobox, + gpointer user_data); + +void +on_seekbar_fps_value_changed (GtkRange *range, + gpointer user_data); + +void +on_gui_fps_value_changed (GtkRange *range, + gpointer user_data); + +void +on_add_from_archives_toggled (GtkToggleButton *togglebutton, + gpointer user_data); + +void +on_ignore_archives_toggled (GtkToggleButton *togglebutton, + gpointer user_data); diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index 7fb50554..4118aa66 100644 --- a/plugins/gtkui/coverart.c +++ b/plugins/gtkui/coverart.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -21,12 +21,11 @@ #include <string.h> #include <stdlib.h> #include <unistd.h> +#include <sys/stat.h> #include "coverart.h" #include "../artwork/artwork.h" #include "gtkui.h" -#define DEFAULT_COVER_PATH (PREFIX "/share/deadbeef/pixmaps/noartwork.jpg") - //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(...) @@ -38,6 +37,7 @@ extern DB_artwork_plugin_t *coverart_plugin; typedef struct { struct timeval tm; char *fname; + time_t filetime; int width; GdkPixbuf *pixbuf; } cached_pixbuf_t; @@ -140,10 +140,13 @@ loading_thread (void *none) { usleep (500000); continue; } - -// GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (queue->fname, NULL); + struct stat stat_buf; + if (stat (queue->fname, &stat_buf) < 0) { + trace ("failed to stat file %s\n", queue->fname); + } + GdkPixbuf *pixbuf = NULL; GError *error = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale (queue->fname, queue->width, queue->width, TRUE, &error); + pixbuf = gdk_pixbuf_new_from_file_at_scale (queue->fname, queue->width, queue->width, TRUE, &error); if (!pixbuf) { unlink (queue->fname); fprintf (stderr, "gdk_pixbuf_new_from_file_at_scale %s %d failed, error: %s\n", queue->fname, queue->width, error->message); @@ -151,9 +154,13 @@ loading_thread (void *none) { g_error_free (error); error = NULL; } - pixbuf = gdk_pixbuf_new_from_file_at_scale (DEFAULT_COVER_PATH, queue->width, queue->width, TRUE, &error); + const char *defpath = coverart_plugin->get_default_cover (); + if (stat (defpath, &stat_buf) < 0) { + trace ("failed to stat file %s\n", queue->fname); + } + pixbuf = gdk_pixbuf_new_from_file_at_scale (defpath, queue->width, queue->width, TRUE, &error); if (!pixbuf) { - fprintf (stderr, "gdk_pixbuf_new_from_file_at_scale %s %d failed, error: %s\n", DEFAULT_COVER_PATH, queue->width, error->message); + fprintf (stderr, "gdk_pixbuf_new_from_file_at_scale %s %d failed, error: %s\n", defpath, queue->width, error->message); } } if (error) { @@ -163,43 +170,16 @@ loading_thread (void *none) { if (!pixbuf) { // make default empty image pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 2, 2); + stat_buf.st_mtime = 0; } -#if 0 - else { - int w, h; - w = gdk_pixbuf_get_width (pixbuf); - h = gdk_pixbuf_get_height (pixbuf); - int width = queue->width; - if (w != width) { - int height; - if (w > h) { - height = width * h / w; - } - else if (h > w) { - height = width; - width = height * w / h; - } - else { - height = width; - } - if (width < 5 || height < 5) { - trace ("will not scale to %dx%d\n", width, height); - queue_pop (); - continue; - } - trace ("scaling %dx%d -> %dx%d\n", w, h, width, height); - GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR); - g_object_unref (pixbuf); - pixbuf = scaled; - } - } -#endif if (cache_min != -1) { deadbeef->mutex_lock (mutex); + cache[cache_min].filetime = stat_buf.st_mtime; cache[cache_min].pixbuf = pixbuf; cache[cache_min].fname = strdup (queue->fname); gettimeofday (&cache[cache_min].tm, NULL); cache[cache_min].width = queue->width; + struct stat stat_buf; deadbeef->mutex_unlock (mutex); } queue_pop (); @@ -231,11 +211,15 @@ get_pixbuf (const char *fname, int width) { for (int i = 0; i < CACHE_SIZE; i++) { if (cache[i].pixbuf) { if (!strcmp (fname, cache[i].fname) && cache[i].width == width) { - gettimeofday (&cache[i].tm, NULL); - GdkPixbuf *pb = cache[i].pixbuf; - g_object_ref (pb); - deadbeef->mutex_unlock (mutex); - return pb; + // check if cached filetime hasn't changed + struct stat stat_buf; + if (!stat (fname, &stat_buf) && stat_buf.st_mtime == cache[i].filetime) { + gettimeofday (&cache[i].tm, NULL); + GdkPixbuf *pb = cache[i].pixbuf; + g_object_ref (pb); + deadbeef->mutex_unlock (mutex); + return pb; + } } } } @@ -257,7 +241,7 @@ get_cover_art (const char *fname, const char *artist, const char *album, int wid if (!coverart_plugin) { return NULL; } - char *image_fname = coverart_plugin->get_album_art (fname, artist, album, cover_avail_callback, (void *)(intptr_t)width); + char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, (void *)(intptr_t)width); if (image_fname) { GdkPixbuf *pb = get_pixbuf (image_fname, width); free (image_fname); diff --git a/plugins/gtkui/coverart.h b/plugins/gtkui/coverart.h index 75a96429..689e0f80 100644 --- a/plugins/gtkui/coverart.h +++ b/plugins/gtkui/coverart.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.c b/plugins/gtkui/ddbcellrenderertextmultiline.c index 867c70e4..a6e95853 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.c +++ b/plugins/gtkui/ddbcellrenderertextmultiline.c @@ -1,4 +1,4 @@ -/* ddbcellrenderertextmultiline.c generated by valac 0.10.0, the Vala compiler +/* ddbcellrenderertextmultiline.c generated by valac 0.10.2, the Vala compiler * generated from ddbcellrenderertextmultiline.vala, do not modify */ /* @@ -52,6 +52,7 @@ typedef struct _DdbCellRendererTextMultiline DdbCellRendererTextMultiline; typedef struct _DdbCellRendererTextMultilineClass DdbCellRendererTextMultilineClass; typedef struct _DdbCellRendererTextMultilinePrivate DdbCellRendererTextMultilinePrivate; #define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) +#define _gtk_tree_path_free0(var) ((var == NULL) ? NULL : (var = (gtk_tree_path_free (var), NULL))) struct _DdbCellEditableTextView { GtkTextView parent_instance; @@ -213,7 +214,7 @@ static void ddb_cell_renderer_text_multiline_gtk_cell_renderer_text_editing_done buf = _g_object_ref0 (gtk_text_view_get_buffer ((GtkTextView*) entry)); gtk_text_buffer_get_iter_at_offset (buf, &begin, 0); gtk_text_buffer_get_iter_at_offset (buf, &end, -1); - new_text = g_strdup (gtk_text_buffer_get_text (buf, &begin, &end, TRUE)); + new_text = gtk_text_buffer_get_text (buf, &begin, &end, TRUE); g_signal_emit_by_name ((GtkCellRendererText*) _self_, "edited", entry->tree_path, new_text); _g_free0 (new_text); _g_object_unref0 (buf); @@ -236,12 +237,20 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk DdbCellRendererTextMultiline * self; GtkCellEditable* result = NULL; gboolean _tmp0_; - DdbCellEditableTextView* _tmp1_; - char* _tmp2_; - GtkTextBuffer* buf; - char* _tmp3_ = NULL; + GtkTreePath* p; + GtkTreeView* tv; + GtkListStore* store; + GtkTreeIter iter = {0}; + GValue v = {0}; + GValue _tmp1_ = {0}; + GValue _tmp2_; + gint mult; + DdbCellEditableTextView* _tmp3_; char* _tmp4_; - gboolean _tmp5_; + GtkTextBuffer* buf; + char* _tmp5_ = NULL; + char* _tmp6_; + gboolean _tmp7_; self = (DdbCellRendererTextMultiline*) base; g_return_val_if_fail (event != NULL, NULL); g_return_val_if_fail (widget != NULL, NULL); @@ -250,14 +259,24 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk result = GTK_CELL_EDITABLE (NULL); return result; } - self->priv->entry = (_tmp1_ = g_object_ref_sink (ddb_cell_editable_text_view_new ()), _g_object_unref0 (self->priv->entry), _tmp1_); - self->priv->entry->tree_path = (_tmp2_ = g_strdup (path), _g_free0 (self->priv->entry->tree_path), _tmp2_); + p = gtk_tree_path_new_from_string (path); + tv = _g_object_ref0 (GTK_TREE_VIEW (widget)); + store = _g_object_ref0 (GTK_LIST_STORE (gtk_tree_view_get_model (tv))); + gtk_tree_model_get_iter ((GtkTreeModel*) store, &iter, p); + gtk_tree_model_get_value ((GtkTreeModel*) store, &iter, 3, &_tmp1_); + v = (_tmp2_ = _tmp1_, G_IS_VALUE (&v) ? (g_value_unset (&v), NULL) : NULL, _tmp2_); + mult = g_value_get_int (&v); + self->priv->entry = (_tmp3_ = g_object_ref_sink (ddb_cell_editable_text_view_new ()), _g_object_unref0 (self->priv->entry), _tmp3_); + if (mult != 0) { + g_object_set ((GtkCellRendererText*) self, "text", "", NULL); + } + self->priv->entry->tree_path = (_tmp4_ = g_strdup (path), _g_free0 (self->priv->entry->tree_path), _tmp4_); buf = gtk_text_buffer_new (NULL); - if ((_tmp5_ = (_tmp4_ = (g_object_get ((GtkCellRendererText*) self, "text", &_tmp3_, NULL), _tmp3_)) != NULL, _g_free0 (_tmp4_), _tmp5_)) { - char* _tmp6_ = NULL; - char* _tmp7_; - gtk_text_buffer_set_text (buf, _tmp7_ = (g_object_get ((GtkCellRendererText*) self, "text", &_tmp6_, NULL), _tmp6_), -1); - _g_free0 (_tmp7_); + if ((_tmp7_ = (_tmp6_ = (g_object_get ((GtkCellRendererText*) self, "text", &_tmp5_, NULL), _tmp5_)) != NULL, _g_free0 (_tmp6_), _tmp7_)) { + char* _tmp8_ = NULL; + char* _tmp9_; + gtk_text_buffer_set_text (buf, _tmp9_ = (g_object_get ((GtkCellRendererText*) self, "text", &_tmp8_, NULL), _tmp8_), -1); + _g_free0 (_tmp9_); } gtk_text_view_set_buffer ((GtkTextView*) self->priv->entry, buf); g_signal_connect (self->priv->entry, "editing-done", (GCallback) ddb_cell_renderer_text_multiline_gtk_cell_renderer_text_editing_done, self); @@ -266,6 +285,10 @@ static GtkCellEditable* ddb_cell_renderer_text_multiline_real_start_editing (Gtk gtk_widget_show ((GtkWidget*) self->priv->entry); result = GTK_CELL_EDITABLE (self->priv->entry); _g_object_unref0 (buf); + G_IS_VALUE (&v) ? (g_value_unset (&v), NULL) : NULL; + _g_object_unref0 (store); + _g_object_unref0 (tv); + _gtk_tree_path_free0 (p); return result; } diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.h b/plugins/gtkui/ddbcellrenderertextmultiline.h index 787beb50..2fec6b26 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.h +++ b/plugins/gtkui/ddbcellrenderertextmultiline.h @@ -1,4 +1,4 @@ -/* ddbcellrenderertextmultiline.h generated by valac 0.10.0, the Vala compiler, do not modify */ +/* ddbcellrenderertextmultiline.h generated by valac 0.10.2, the Vala compiler, do not modify */ #ifndef __DDBCELLRENDERERTEXTMULTILINE_H__ diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.vala b/plugins/gtkui/ddbcellrenderertextmultiline.vala index 75e7bdc9..4587cff9 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.vala +++ b/plugins/gtkui/ddbcellrenderertextmultiline.vala @@ -79,7 +79,21 @@ namespace Ddb { if (!editable) { return (Gtk.CellEditable)null; } + + Gtk.TreePath p = new Gtk.TreePath.from_string (path); + Gtk.TreeView tv = (Gtk.TreeView)widget; + Gtk.ListStore store = (Gtk.ListStore)tv.get_model(); + Gtk.TreeIter iter; + store.get_iter (out iter, p); + GLib.Value v; + store.get_value (iter, 3, out v); + int mult = v.get_int (); + entry = new CellEditableTextView (); + if (mult != 0) { + text = ""; + } + entry.tree_path = path; Gtk.TextBuffer buf = new Gtk.TextBuffer (null); if (text != null) { diff --git a/plugins/gtkui/ddbequalizer.c b/plugins/gtkui/ddbequalizer.c index 201277ad..7cd9ee4f 100644 --- a/plugins/gtkui/ddbequalizer.c +++ b/plugins/gtkui/ddbequalizer.c @@ -1,4 +1,4 @@ -/* ddbequalizer.c generated by valac 0.10.0, the Vala compiler +/* ddbequalizer.c generated by valac 0.10.2, the Vala compiler * generated from ddbequalizer.vala, do not modify */ /* diff --git a/plugins/gtkui/ddbequalizer.h b/plugins/gtkui/ddbequalizer.h index 19f98c23..2ce2ba5c 100644 --- a/plugins/gtkui/ddbequalizer.h +++ b/plugins/gtkui/ddbequalizer.h @@ -1,4 +1,4 @@ -/* ddbequalizer.h generated by valac 0.10.0, the Vala compiler, do not modify */ +/* ddbequalizer.h generated by valac 0.10.2, the Vala compiler, do not modify */ #ifndef __DDBEQUALIZER_H__ diff --git a/plugins/gtkui/ddblistview.c b/plugins/gtkui/ddblistview.c index 89a27cff..479c03c8 100644 --- a/plugins/gtkui/ddblistview.c +++ b/plugins/gtkui/ddblistview.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -88,20 +88,19 @@ static void ddb_listview_destroy(GtkObject *object); // fwd decls void ddb_listview_free_groups (DdbListview *listview); -static inline void -draw_drawable (GdkDrawable *window, GdkGC *gc, GdkDrawable *drawable, int x1, int y1, int x2, int y2, int w, int h); + +//static inline void +//draw_drawable (GdkDrawable *window, GdkGC *gc, GdkDrawable *drawable, int x1, int y1, int x2, int y2, int w, int h); ////// list functions //// void ddb_listview_list_render (DdbListview *ps, int x, int y, int w, int h); void -ddb_listview_list_expose (DdbListview *ps, int x, int y, int w, int h); -void ddb_listview_list_render_row_background (DdbListview *ps, DdbListviewIter it, int even, int cursor, int x, int y, int w, int h); void ddb_listview_list_render_row_foreground (DdbListview *ps, DdbListviewIter it, DdbListviewIter group_it, int even, int cursor, int group_y, int x, int y, int w, int h); -void -ddb_listview_list_render_row (DdbListview *ps, int row, DdbListviewIter it, int expose); +//void +//ddb_listview_list_render_row (DdbListview *ps, int row, DdbListviewIter it); void ddb_listview_list_track_dragdrop (DdbListview *ps, int y); int @@ -122,8 +121,6 @@ ddb_listview_get_row_pos (DdbListview *listview, int pos); ////// header functions //// void ddb_listview_header_render (DdbListview *ps); -void -ddb_listview_header_expose (DdbListview *ps, int x, int y, int w, int h); ////// column management functions //// void @@ -282,8 +279,9 @@ static void ddb_listview_init(DdbListview *listview) { // init instance - create all subwidgets, and insert into table + draw_init_font (GTK_WIDGET(listview)->style); - listview->rowheight = draw_get_font_size () + 12; + listview->rowheight = draw_get_listview_rowheight (); listview->col_movepos = -1; listview->drag_motion_y = -1; @@ -470,18 +468,6 @@ ddb_listview_destroy(GtkObject *object) gdk_cursor_unref (listview->cursor_drag); listview->cursor_drag = NULL; } - if (listview->backbuf) { - g_object_unref (listview->backbuf); - listview->backbuf = NULL; - } - if (listview->backbuf_header) { - g_object_unref (listview->backbuf_header); - listview->backbuf_header = NULL; - } - -// if (G_OBJECT_CLASS (ddb_listview_parent_class)) { -// G_OBJECT_CLASS (ddb_listview_parent_class)->destroy (object); -// } } void @@ -492,7 +478,7 @@ ddb_listview_refresh (DdbListview *listview, uint32_t flags) { if (height != listview->fullheight) { flags |= DDB_REFRESH_VSCROLL; } - ddb_listview_list_render (listview, 0, 0, listview->list->allocation.width, listview->list->allocation.height); + gtk_widget_queue_draw (listview->list); } if (flags & DDB_REFRESH_VSCROLL) { ddb_listview_list_setup_vscroll (listview); @@ -501,13 +487,7 @@ ddb_listview_refresh (DdbListview *listview, uint32_t flags) { ddb_listview_list_setup_hscroll (listview); } if (flags & DDB_REFRESH_COLUMNS) { - ddb_listview_header_render (listview); - } - if (flags & DDB_EXPOSE_COLUMNS) { - ddb_listview_header_expose (listview, 0, 0, listview->header->allocation.width, listview->header->allocation.height); - } - if (flags & DDB_EXPOSE_LIST) { - ddb_listview_list_expose (listview, 0, 0, listview->list->allocation.width, listview->list->allocation.height); + gtk_widget_queue_draw (listview->header); } } @@ -516,9 +496,9 @@ ddb_listview_list_realize (GtkWidget *widget, gpointer user_data) { GtkTargetEntry entry = { - .target = "STRING", + .target = "DDB_URI_LIST", .flags = GTK_TARGET_SAME_APP, - TARGET_SAMEWIDGET + .info = TARGET_SAMEWIDGET }; // setup drag-drop target gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, &entry, 1, GDK_ACTION_COPY | GDK_ACTION_MOVE); @@ -534,7 +514,7 @@ ddb_listview_list_configure_event (GtkWidget *widget, DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); draw_init_font (widget->style); - int height = draw_get_font_size () + 12; + int height = draw_get_listview_rowheight (); if (height != ps->rowheight) { ps->rowheight = height; ddb_listview_build_groups (ps); @@ -543,13 +523,7 @@ ddb_listview_list_configure_event (GtkWidget *widget, ddb_listview_list_setup_vscroll (ps); ddb_listview_list_setup_hscroll (ps); widget = ps->list; - if (ps->backbuf) { - g_object_unref (ps->backbuf); - ps->backbuf = NULL; - } - ps->backbuf = gdk_pixmap_new (widget->window, widget->allocation.width, widget->allocation.height, -1); - ddb_listview_list_render (ps, 0, 0, widget->allocation.width, widget->allocation.height); return FALSE; } @@ -608,9 +582,6 @@ ddb_listview_list_pickpoint_y (DdbListview *listview, int y, DdbListviewGroup ** void ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { - if (!listview->backbuf) { - return; - } GtkWidget *treeview = theme_treeview; if (treeview->style->depth == -1) { return; // drawing was called too early @@ -627,7 +598,7 @@ ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { abs_idx += grp->num_items; grp = grp->next; } - draw_begin ((uintptr_t)listview->backbuf); + draw_begin ((uintptr_t)listview->list->window); int ii = 0; while (grp && grp_y < y + h + listview->scrollpos) { @@ -641,7 +612,7 @@ ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { if (grp_y + listview->grouptitle_height >= y + listview->scrollpos && grp_y < y + h + listview->scrollpos) { ddb_listview_list_render_row_background (listview, NULL, idx & 1, 0, -listview->hscrollpos, grp_y - listview->scrollpos, listview->totalwidth, listview->grouptitle_height); if (listview->binding->draw_group_title && listview->grouptitle_height > 0) { - listview->binding->draw_group_title (listview, listview->backbuf, it, -listview->hscrollpos, grp_y - listview->scrollpos, listview->totalwidth, listview->grouptitle_height); + listview->binding->draw_group_title (listview, listview->list->window, it, -listview->hscrollpos, grp_y - listview->scrollpos, listview->totalwidth, listview->grouptitle_height); } } for (int i = 0; i < grp->num_items; i++) { @@ -654,7 +625,7 @@ ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { } if (grp_y + listview->grouptitle_height + (i+1) * listview->rowheight >= y + listview->scrollpos && grp_y + listview->grouptitle_height + i * listview->rowheight < y + h + listview->scrollpos) { - gdk_draw_rectangle (listview->backbuf, listview->list->style->bg_gc[GTK_STATE_NORMAL], TRUE, -listview->hscrollpos, grp_y + listview->grouptitle_height + i * listview->rowheight - listview->scrollpos, listview->totalwidth, listview->rowheight); + gdk_draw_rectangle (listview->list->window, listview->list->style->bg_gc[GTK_STATE_NORMAL], TRUE, -listview->hscrollpos, grp_y + listview->grouptitle_height + i * listview->rowheight - listview->scrollpos, listview->totalwidth, listview->rowheight); ddb_listview_list_render_row_background (listview, it, (idx + 1 + i) & 1, (abs_idx+i) == listview->binding->cursor () ? 1 : 0, -listview->hscrollpos, grp_y + listview->grouptitle_height + i * listview->rowheight - listview->scrollpos, listview->totalwidth, listview->rowheight); ddb_listview_list_render_row_foreground (listview, it, grp->head, (idx + 1 + i) & 1, (idx+i) == listview->binding->cursor () ? 1 : 0, i * listview->rowheight, -listview->hscrollpos, grp_y + listview->grouptitle_height + i * listview->rowheight - listview->scrollpos, listview->totalwidth, listview->rowheight); } @@ -675,13 +646,13 @@ ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { if (filler > 0) { int theming = !gtkui_override_listview_colors (); if (theming) { - gtk_paint_flat_box (treeview->style, listview->backbuf, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, "cell_even_ruled", x, grp_y - listview->scrollpos + listview->grouptitle_height + listview->rowheight * grp->num_items, w, filler); + gtk_paint_flat_box (treeview->style, listview->list->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, "cell_even_ruled", x, grp_y - listview->scrollpos + listview->grouptitle_height + listview->rowheight * grp->num_items, w, filler); } else { GdkColor clr; - GdkGC *gc = gdk_gc_new (listview->backbuf); + GdkGC *gc = gdk_gc_new (listview->list->window); gdk_gc_set_rgb_fg_color (gc, (gtkui_get_listview_even_row_color (&clr), &clr)); - gdk_draw_rectangle (listview->backbuf, gc, TRUE, x, grp_y - listview->scrollpos + listview->grouptitle_height + listview->rowheight * grp->num_items, w, filler); + gdk_draw_rectangle (listview->list->window, gc, TRUE, x, grp_y - listview->scrollpos + listview->grouptitle_height + listview->rowheight * grp->num_items, w, filler); g_object_unref (gc); } @@ -694,16 +665,16 @@ ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { } if (grp_y < y + h + listview->scrollpos) { int hh = y + h - (grp_y - listview->scrollpos); -// gdk_draw_rectangle (listview->backbuf, listview->list->style->bg_gc[GTK_STATE_NORMAL], TRUE, x, grp_y - listview->scrollpos, w, hh); +// gdk_draw_rectangle (listview->list->window, listview->list->style->bg_gc[GTK_STATE_NORMAL], TRUE, x, grp_y - listview->scrollpos, w, hh); int theming = !gtkui_override_listview_colors (); if (theming) { - gtk_paint_flat_box (treeview->style, listview->backbuf, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, "cell_even_ruled", x, grp_y - listview->scrollpos, w, hh); + gtk_paint_flat_box (treeview->style, listview->list->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, "cell_even_ruled", x, grp_y - listview->scrollpos, w, hh); } else { GdkColor clr; - GdkGC *gc = gdk_gc_new (listview->backbuf); + GdkGC *gc = gdk_gc_new (listview->list->window); gdk_gc_set_rgb_fg_color (gc, (gtkui_get_listview_even_row_color (&clr), &clr)); - gdk_draw_rectangle (listview->backbuf, gc, TRUE, x, grp_y - listview->scrollpos, w, hh); + gdk_draw_rectangle (listview->list->window, gc, TRUE, x, grp_y - listview->scrollpos, w, hh); g_object_unref (gc); } } @@ -711,16 +682,6 @@ ddb_listview_list_render (DdbListview *listview, int x, int y, int w, int h) { draw_end (); } -gboolean -ddb_listview_list_expose_event (GtkWidget *widget, - GdkEventExpose *event, - gpointer user_data) -{ - DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); - ddb_listview_list_expose (ps, event->area.x, event->area.y, event->area.width, event->area.height); - return FALSE; -} - static void ddb_listview_draw_dnd_marker (DdbListview *ps) { if (ps->drag_motion_y < 0) { @@ -740,15 +701,20 @@ ddb_listview_draw_dnd_marker (DdbListview *ps) { } -void -ddb_listview_list_expose (DdbListview *listview, int x, int y, int w, int h) { - GtkWidget *widget = listview->list; - if (widget->window && listview->backbuf) { - draw_drawable (widget->window, widget->style->black_gc, listview->backbuf, x, y, x, y, w, h); +gboolean +ddb_listview_list_expose_event (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data) +{ + DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); + widget = ps->list; + if (widget->window) { + ddb_listview_list_render (ps, event->area.x, event->area.y, event->area.width, event->area.height); } - if (listview->drag_motion_y >= 0 && listview->drag_motion_y-listview->scrollpos-3 < y+h && listview->drag_motion_y-listview->scrollpos+3 >= y) { - ddb_listview_draw_dnd_marker (listview); + if (ps->drag_motion_y >= 0 && ps->drag_motion_y-ps->scrollpos-3 < event->area.y+event->area.height && ps->drag_motion_y-ps->scrollpos+3 >= event->area.y) { + ddb_listview_draw_dnd_marker (ps); } + return FALSE; } gboolean @@ -795,19 +761,23 @@ ddb_listview_vscroll_value_changed (GtkRange *widget, if (di > 0) { // scroll down // copy scrolled part of buffer - draw_drawable (ps->backbuf, widget->style->black_gc, ps->backbuf, 0, d, 0, 0, widget->allocation.width, widget->allocation.height-d); + gdk_draw_drawable (ps->list->window, widget->style->black_gc, ps->list->window, 0, d, 0, 0, widget->allocation.width, widget->allocation.height-d); +// draw_drawable (ps->list->window, widget->style->black_gc, ps->list->window, 0, d, 0, 0, widget->allocation.width, widget->allocation.height-d); // redraw other part int start = height-d-1; ps->scrollpos = newscroll; - ddb_listview_list_render (ps, 0, start, ps->list->allocation.width, widget->allocation.height-start); + gtk_widget_queue_draw_area (ps->list, 0, start, ps->list->allocation.width, widget->allocation.height-start); +// ddb_listview_list_render (ps, 0, start, ps->list->allocation.width, widget->allocation.height-start); } else { // scroll up // copy scrolled part of buffer - draw_drawable (ps->backbuf, widget->style->black_gc, ps->backbuf, 0, 0, 0, d, widget->allocation.width, widget->allocation.height-d); + gdk_draw_drawable (ps->list->window, widget->style->black_gc, ps->list->window, 0, 0, 0, d, widget->allocation.width, widget->allocation.height-d); + //draw_drawable (ps->list->window, widget->style->black_gc, ps->list->window, 0, 0, 0, d, widget->allocation.width, widget->allocation.height-d); // redraw other part ps->scrollpos = newscroll; - ddb_listview_list_render (ps, 0, 0, ps->list->allocation.width, d+1); + gtk_widget_queue_draw_area (ps->list, 0, 0, ps->list->allocation.width, d+1); + //ddb_listview_list_render (ps, 0, 0, ps->list->allocation.width, d+1); } } else { @@ -815,7 +785,8 @@ ddb_listview_vscroll_value_changed (GtkRange *widget, ps->scrollpos = newscroll; ddb_listview_list_render (ps, 0, 0, widget->allocation.width, widget->allocation.height); } - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); + gtk_widget_queue_draw (ps->list); +// draw_drawable (widget->window, widget->style->black_gc, ps->list->window, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); } } @@ -877,17 +848,6 @@ ddb_listview_list_drag_drop (GtkWidget *widget, gpointer user_data) { return TRUE; -#if 0 - if (drag_context->targets) { - GdkAtom target_type = GDK_POINTER_TO_ATOM (g_list_nth_data (drag_context->targets, TARGET_SAMEWIDGET)); - if (!target_type) { - return FALSE; - } -// gtk_drag_get_data (widget, drag_context, target_type, time); - return TRUE; - } - return FALSE; -#endif } @@ -942,6 +902,7 @@ ddb_listview_list_drag_data_received (GtkWidget *widget, guint time, gpointer user_data) { + printf ("target_type: %d, format: %d\n", target_type, data->format); DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); ps->scroll_direction = 0; // interrupt autoscrolling, if on ps->scroll_active = 0; @@ -972,7 +933,7 @@ ddb_listview_list_drag_data_received (GtkWidget *widget, UNREF (it); } } - else if (target_type == 1) { + else if (target_type == 1 && data->format == 32) { // list of 32bit ints, DDB_URI_LIST target uint32_t *d= (uint32_t *)ptr; int plt = *d; d++; @@ -1013,17 +974,17 @@ ddb_listview_list_drag_leave (GtkWidget *widget, } // debug function for gdk_draw_drawable -static inline void -draw_drawable (GdkDrawable *window, GdkGC *gc, GdkDrawable *drawable, int x1, int y1, int x2, int y2, int w, int h) { - gint width1, height1; - gint width2, height2; - gdk_drawable_get_size (window, &width1, &height1); - gdk_drawable_get_size (drawable, &width2, &height2); -// assert (y1 >= 0 && y1 + h < height2); -// assert (y2 >= 0 && y2 + h < height1); -// printf ("dd: %p %p %p %d %d %d %d %d %d\n", window, gc, drawable, x1, y1, x2, y2, w, h); - gdk_draw_drawable (window, gc, drawable, x1, y1, x2, y2, w, h); -} +//static inline void +//draw_drawable (GdkDrawable *window, GdkGC *gc, GdkDrawable *drawable, int x1, int y1, int x2, int y2, int w, int h) { +// gint width1, height1; +// gint width2, height2; +// gdk_drawable_get_size (window, &width1, &height1); +// gdk_drawable_get_size (drawable, &width2, &height2); +//// assert (y1 >= 0 && y1 + h < height2); +//// assert (y2 >= 0 && y2 + h < height1); +//// printf ("dd: %p %p %p %d %d %d %d %d %d\n", window, gc, drawable, x1, y1, x2, y2, w, h); +// gdk_draw_drawable (window, gc, drawable, x1, y1, x2, y2, w, h); +//} int ddb_listview_get_vscroll_pos (DdbListview *listview) { @@ -1117,8 +1078,7 @@ ddb_listview_list_setup_vscroll (DdbListview *ps) { if (ps->fullheight <= ps->list->allocation.height) { gtk_widget_hide (scroll); ps->scrollpos = 0; - ddb_listview_list_render (ps, 0, 0, list->allocation.width, list->allocation.height); - ddb_listview_list_expose (ps, 0, 0, list->allocation.width, list->allocation.height); + gtk_widget_queue_draw (ps->list); } else { gtk_widget_show (scroll); @@ -1194,8 +1154,9 @@ ddb_listview_list_get_drawinfo (DdbListview *listview, int row, DdbListviewGroup return -1; } +#if 0 void -ddb_listview_list_render_row (DdbListview *listview, int row, DdbListviewIter it, int expose) { +ddb_listview_list_render_row (DdbListview *listview, int row, DdbListviewIter it) { DdbListviewGroup *grp; int even; int cursor; @@ -1213,20 +1174,34 @@ ddb_listview_list_render_row (DdbListview *listview, int row, DdbListviewIter it return; } - draw_begin ((uintptr_t)listview->backbuf); + draw_begin ((uintptr_t)listview->list->window); ddb_listview_list_render_row_background (listview, it, even, cursor, x, y, w, h); if (it) { ddb_listview_list_render_row_foreground (listview, it, grp->head, even, cursor, group_y, x, y, w, h); } draw_end (); - if (expose) { - draw_drawable (listview->list->window, listview->list->style->black_gc, listview->backbuf, 0, y, 0, y, listview->list->allocation.width, h); - } } +#endif void ddb_listview_draw_row (DdbListview *listview, int row, DdbListviewIter it) { - ddb_listview_list_render_row (listview, row, it, 1); + DdbListviewGroup *grp; + int even; + int cursor; + int x, y, w, h; + int group_y; + if (ddb_listview_list_get_drawinfo (listview, row, &grp, &even, &cursor, &group_y, &x, &y, &w, &h) == -1) { + return; + } + + if (y + h <= 0) { + return; + } + + if (y > GTK_WIDGET (listview)->allocation.height) { + return; + } + gtk_widget_queue_draw_area (listview->list, 0, y, listview->list->allocation.width, h); } // coords passed are window-relative @@ -1247,26 +1222,26 @@ ddb_listview_list_render_row_background (DdbListview *ps, DdbListviewIter it, in if (theming || !sel) { if (theming) { // draw background for selection -- workaround for New Wave theme (translucency) - gtk_paint_flat_box (treeview->style, ps->backbuf, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, even ? "cell_even_ruled" : "cell_odd_ruled", x, y, w, h); + gtk_paint_flat_box (treeview->style, ps->list->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, treeview, even ? "cell_even_ruled" : "cell_odd_ruled", x, y, w, h); } else { GdkColor clr; - GdkGC *gc = gdk_gc_new (ps->backbuf); + GdkGC *gc = gdk_gc_new (ps->list->window); gdk_gc_set_rgb_fg_color (gc, even ? (gtkui_get_listview_even_row_color (&clr), &clr) : (gtkui_get_listview_odd_row_color (&clr), &clr)); - gdk_draw_rectangle (ps->backbuf, gc, TRUE, x, y, w, h); + gdk_draw_rectangle (ps->list->window, gc, TRUE, x, y, w, h); g_object_unref (gc); } } if (sel) { if (theming) { - gtk_paint_flat_box (treeview->style, ps->backbuf, GTK_STATE_SELECTED, GTK_SHADOW_NONE, NULL, treeview, even ? "cell_even_ruled" : "cell_odd_ruled", x, y, w, h); + gtk_paint_flat_box (treeview->style, ps->list->window, GTK_STATE_SELECTED, GTK_SHADOW_NONE, NULL, treeview, even ? "cell_even_ruled" : "cell_odd_ruled", x, y, w, h); } else { GdkColor clr; - GdkGC *gc = gdk_gc_new (ps->backbuf); + GdkGC *gc = gdk_gc_new (ps->list->window); gdk_gc_set_rgb_fg_color (gc, (gtkui_get_listview_selection_color (&clr), &clr)); - gdk_draw_rectangle (ps->backbuf, gc, TRUE, x, y, w, h); + gdk_draw_rectangle (ps->list->window, gc, TRUE, x, y, w, h); g_object_unref (gc); } } @@ -1275,9 +1250,9 @@ ddb_listview_list_render_row_background (DdbListview *ps, DdbListviewIter it, in // but we want it anyway //treeview->style->fg_gc[GTK_STATE_NORMAL] GdkColor clr; - GdkGC *gc = gdk_gc_new (ps->backbuf); + GdkGC *gc = gdk_gc_new (ps->list->window); gdk_gc_set_rgb_fg_color (gc, (gtkui_get_listview_cursor_color (&clr), &clr)); - gdk_draw_rectangle (ps->backbuf, gc, FALSE, x, y, w-1, h-1); + gdk_draw_rectangle (ps->list->window, gc, FALSE, x, y, w-1, h-1); g_object_unref (gc); } } @@ -1285,7 +1260,7 @@ ddb_listview_list_render_row_background (DdbListview *ps, DdbListviewIter it, in void ddb_listview_list_render_row_foreground (DdbListview *ps, DdbListviewIter it, DdbListviewIter group_it, int even, int cursor, int group_y, int x, int y, int w, int h) { int width, height; - draw_get_canvas_size ((uintptr_t)ps->backbuf, &width, &height); + draw_get_canvas_size ((uintptr_t)ps->list->window, &width, &height); if (it && ps->binding->is_selected (it)) { GdkColor *clr = &theme_treeview->style->fg[GTK_STATE_SELECTED]; float rgb[3] = { clr->red/65535.f, clr->green/65535.f, clr->blue/65535.f }; @@ -1300,7 +1275,7 @@ ddb_listview_list_render_row_foreground (DdbListview *ps, DdbListviewIter it, Dd int cidx = 0; for (c = ps->columns; c; c = c->next, cidx++) { int cw = c->width; - ps->binding->draw_column_data (ps, ps->backbuf, it, ps->grouptitle_height > 0 ? group_it : NULL, cidx, group_y, x, y, cw, h); + ps->binding->draw_column_data (ps, ps->list->window, it, ps->grouptitle_height > 0 ? group_it : NULL, cidx, group_y, x, y, cw, h); x += cw; } } @@ -1308,8 +1283,7 @@ ddb_listview_list_render_row_foreground (DdbListview *ps, DdbListviewIter it, Dd void ddb_listview_header_expose (DdbListview *ps, int x, int y, int w, int h) { - GtkWidget *widget = ps->header; - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf_header, x, y, x, y, w, h); + ddb_listview_header_render (ps); } void @@ -1347,7 +1321,7 @@ ddb_listview_select_single (DdbListview *ps, int sel) { } UNREF (it); if (nchanged >= NUM_CHANGED_ROWS_BEFORE_FULL_REDRAW) { - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (ps, DDB_REFRESH_LIST); ps->binding->selection_changed (it, -1); // that means "selection changed a lot, redraw everything" } ps->area_selection_start = sel; @@ -1587,7 +1561,7 @@ ddb_listview_list_mouse1_released (DdbListview *ps, int state, int ex, int ey, d #if 0 void ddb_listview_list_dbg_draw_areasel (GtkWidget *widget, int x, int y) { - // erase previous rect using 4 blits from ps->backbuffer + // erase previous rect using 4 blits from ps->list->windowfer if (areaselect_dx != -1) { int sx = min (areaselect_x, areaselect_dx); int sy = min (areaselect_y, areaselect_dy); @@ -1595,11 +1569,11 @@ ddb_listview_list_dbg_draw_areasel (GtkWidget *widget, int x, int y) { int dy = max (areaselect_y, areaselect_dy); int w = dx - sx + 1; int h = dy - sy + 1; - //draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, sx, sy, sx, sy, dx - sx + 1, dy - sy + 1); - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, sx, sy, sx, sy, w, 1); - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, sx, sy, sx, sy, 1, h); - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, sx, sy + h - 1, sx, sy + h - 1, w, 1); - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, sx + w - 1, sy, sx + w - 1, sy, 1, h); + //draw_drawable (widget->window, widget->style->black_gc, ps->list->window, sx, sy, sx, sy, dx - sx + 1, dy - sy + 1); + draw_drawable (widget->window, widget->style->black_gc, ps->list->window, sx, sy, sx, sy, w, 1); + draw_drawable (widget->window, widget->style->black_gc, ps->list->window, sx, sy, sx, sy, 1, h); + draw_drawable (widget->window, widget->style->black_gc, ps->list->window, sx, sy + h - 1, sx, sy + h - 1, w, 1); + draw_drawable (widget->window, widget->style->black_gc, ps->list->window, sx + w - 1, sy, sx + w - 1, sy, 1, h); } areaselect_dx = x; areaselect_dy = y; @@ -1675,7 +1649,7 @@ ddb_listview_list_mousemove (DdbListview *ps, GdkEventMotion *ev, int ex, int ey ps->dragwait = 0; ps->drag_source_playlist = deadbeef->plt_get_curr (); GtkTargetEntry entry = { - .target = "STRING", + .target = "DDB_URI_LIST", .flags = GTK_TARGET_SAME_WIDGET, .info = TARGET_SAMEWIDGET }; @@ -1778,7 +1752,7 @@ ddb_listview_list_mousemove (DdbListview *ps, GdkEventMotion *ev, int ex, int ey } UNREF (it); if (nchanged >= NUM_CHANGED_ROWS_BEFORE_FULL_REDRAW) { - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (ps, DDB_REFRESH_LIST); ps->binding->selection_changed (it, -1); // that means "selection changed a lot, redraw everything" } ps->area_selection_start = start; @@ -1841,10 +1815,8 @@ ddb_listview_list_set_hscroll (DdbListview *ps, int newscroll) { { ps->hscrollpos = newscroll; GtkWidget *widget = ps->list; - ddb_listview_header_render (ps); - ddb_listview_header_expose (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height); - ddb_listview_list_render (ps, 0, 0, widget->allocation.width, widget->allocation.height); - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, 0, 0, 0, 0, widget->allocation.width, widget->allocation.height); + gtk_widget_queue_draw (ps->header); + gtk_widget_queue_draw (ps->list); } } @@ -1971,7 +1943,7 @@ ddb_listview_handle_keypress (DdbListview *ps, int keyval, int state) { } UNREF (it); if (nchanged >= NUM_CHANGED_ROWS_BEFORE_FULL_REDRAW) { - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (ps, DDB_REFRESH_LIST); ps->binding->selection_changed (it, -1); // that means "selection changed a lot, redraw everything" } } @@ -2018,7 +1990,7 @@ ddb_listview_list_track_dragdrop (DdbListview *ps, int y) { GtkWidget *widget = ps->list; if (ps->drag_motion_y != -1) { // erase previous track - draw_drawable (widget->window, widget->style->black_gc, ps->backbuf, 0, ps->drag_motion_y-3-ps->scrollpos, 0, ps->drag_motion_y-ps->scrollpos-3, widget->allocation.width, 7); + gtk_widget_queue_draw_area (ps->list, 0, ps->drag_motion_y-ps->scrollpos-3, widget->allocation.width, 7); } if (y == -1) { @@ -2077,7 +2049,7 @@ ddb_listview_list_drag_end (GtkWidget *widget, gpointer user_data) { DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); - ddb_listview_refresh (ps, DDB_REFRESH_LIST|DDB_EXPOSE_LIST); + ddb_listview_refresh (ps, DDB_REFRESH_LIST); ps->scroll_direction = 0; ps->scroll_pointer_y = -1; } @@ -2094,17 +2066,17 @@ ddb_listview_header_render (DdbListview *ps) { // fill background and draw bottom line #if !HEADERS_GTKTHEME - GdkGC *gc = gdk_gc_new (ps->backbuf_header); + GdkGC *gc = gdk_gc_new (ps->header->window); GdkColor clr; gdk_gc_set_rgb_fg_color (gc, (gtkui_get_tabstrip_base_color (&clr), &clr)); - gdk_draw_rectangle (ps->backbuf_header, gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); + gdk_draw_rectangle (ps->header->window, gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); gdk_gc_set_rgb_fg_color (gc, (gtkui_get_tabstrip_dark_color (&clr), &clr)); - gdk_draw_line (ps->backbuf_header, gc, 0, widget->allocation.height-1, widget->allocation.width, widget->allocation.height-1); + gdk_draw_line (ps->header->window, gc, 0, widget->allocation.height-1, widget->allocation.width, widget->allocation.height-1); #else - gtk_paint_box (theme_button->style, ps->backbuf_header, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, widget, detail, -10, -10, widget->allocation.width+20, widget->allocation.height+20); - gdk_draw_line (ps->backbuf_header, widget->style->mid_gc[GTK_STATE_NORMAL], 0, widget->allocation.height-1, widget->allocation.width, widget->allocation.height-1); + gtk_paint_box (theme_button->style, ps->header->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, widget, detail, -10, -10, widget->allocation.width+20, widget->allocation.height+20); + gdk_draw_line (ps->header->window, widget->style->mid_gc[GTK_STATE_NORMAL], 0, widget->allocation.height-1, widget->allocation.width, widget->allocation.height-1); #endif - draw_begin ((uintptr_t)ps->backbuf_header); + draw_begin ((uintptr_t)ps->header->window); x = -ps->hscrollpos; DdbListviewColumn *c; int need_draw_moving = 0; @@ -2131,11 +2103,11 @@ ddb_listview_header_render (DdbListview *ps) { if (w > 0) { #if !HEADERS_GTKTHEME gdk_gc_set_rgb_fg_color (gc, (gtkui_get_tabstrip_dark_color (&clr), &clr)); - gdk_draw_line (ps->backbuf_header, gc, xx+w - 2, 2, xx+w - 2, h-4); + gdk_draw_line (ps->header->window, gc, xx+w - 2, 2, xx+w - 2, h-4); gdk_gc_set_rgb_fg_color (gc, (gtkui_get_tabstrip_light_color (&clr), &clr)); - gdk_draw_line (ps->backbuf_header, gc, xx+w - 1, 2, xx+w - 1, h-4); + gdk_draw_line (ps->header->window, gc, xx+w - 1, 2, xx+w - 1, h-4); #else - gtk_paint_vline (widget->style, ps->backbuf_header, GTK_STATE_NORMAL, NULL, widget, NULL, 2, h-4, xx+w - 2); + gtk_paint_vline (widget->style, ps->header->window, GTK_STATE_NORMAL, NULL, widget, NULL, 2, h-4, xx+w - 2); #endif GdkColor *gdkfg = &theme_button->style->fg[0]; float fg[3] = {(float)gdkfg->red/0xffff, (float)gdkfg->green/0xffff, (float)gdkfg->blue/0xffff}; @@ -2151,7 +2123,7 @@ ddb_listview_header_render (DdbListview *ps) { } if (sort) { int dir = sort == 1 ? GTK_ARROW_DOWN : GTK_ARROW_UP; - gtk_paint_arrow (widget->style, ps->backbuf_header, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, widget, NULL, dir, TRUE, xx + w-arrow_sz-5, widget->allocation.height/2-arrow_sz/2, arrow_sz, arrow_sz); + gtk_paint_arrow (widget->style, ps->header->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, widget, NULL, dir, TRUE, xx + w-arrow_sz-5, widget->allocation.height/2-arrow_sz/2, arrow_sz, arrow_sz); } } else { @@ -2177,14 +2149,14 @@ ddb_listview_header_render (DdbListview *ps) { #endif // draw empty slot if (x < widget->allocation.width) { - gtk_paint_box (theme_button->style, ps->backbuf_header, GTK_STATE_ACTIVE, GTK_SHADOW_ETCHED_IN, NULL, widget, "button", x, 0, w, h); + gtk_paint_box (theme_button->style, ps->header->window, GTK_STATE_ACTIVE, GTK_SHADOW_ETCHED_IN, NULL, widget, "button", x, 0, w, h); } x = ps->col_movepos - ps->hscrollpos; if (x >= widget->allocation.width) { break; } if (w > 0) { - gtk_paint_box (theme_button->style, ps->backbuf_header, GTK_STATE_SELECTED, GTK_SHADOW_OUT, NULL, widget, "button", x, 0, w, h); + gtk_paint_box (theme_button->style, ps->header->window, GTK_STATE_SELECTED, GTK_SHADOW_OUT, NULL, widget, "button", x, 0, w, h); GdkColor *gdkfg = &theme_button->style->fg[GTK_STATE_SELECTED]; float fg[3] = {(float)gdkfg->red/0xffff, (float)gdkfg->green/0xffff, (float)gdkfg->blue/0xffff}; draw_set_fg_color (fg); @@ -2224,12 +2196,6 @@ ddb_listview_header_configure_event (GtkWidget *widget, if (height != widget->allocation.height) { gtk_widget_set_size_request (widget, -1, height); } - if (ps->backbuf_header) { - g_object_unref (ps->backbuf_header); - ps->backbuf_header = NULL; - } - ps->backbuf_header = gdk_pixmap_new (widget->window, widget->allocation.width, widget->allocation.height, -1); - ddb_listview_header_render (ps); return FALSE; } @@ -2268,7 +2234,9 @@ ddb_listview_header_motion_notify_event (GtkWidget *widget, ev_x = event->x; ev_y = event->y; ev_state = event->state; +#if GTK_CHECK_VERSION(2,12,0) && !defined(ULTRA_COMPATIBLE) gdk_event_request_motions (event); +#endif if ((ev_state & GDK_BUTTON1_MASK) && ps->header_prepare) { if (gtk_drag_check_threshold (widget, ev_x, ps->prev_header_x, 0, 0)) { @@ -2304,13 +2272,11 @@ ddb_listview_header_motion_notify_event (GtkWidget *widget, // colhdr_anim_swap (ps, c1, c2, x1, x2); // force redraw of everything // ddb_listview_list_setup_hscroll (ps); - ddb_listview_list_render (ps, 0, 0, ps->list->allocation.width, ps->list->allocation.height); - ddb_listview_list_expose (ps, 0, 0, ps->list->allocation.width, ps->list->allocation.height); + gtk_widget_queue_draw (ps->list); } else { // only redraw that if not animating - ddb_listview_header_render (ps); - ddb_listview_header_expose (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height); + gtk_widget_queue_draw (ps->header); } } else if (ps->header_sizing >= 0) { @@ -2334,10 +2300,8 @@ ddb_listview_header_motion_notify_event (GtkWidget *widget, ddb_listview_list_setup_vscroll (ps); ddb_listview_list_setup_hscroll (ps); ps->block_redraw_on_scroll = 0; - ddb_listview_header_render (ps); - ddb_listview_header_expose (ps, 0, 0, ps->header->allocation.width, ps->header->allocation.height); - ddb_listview_list_render (ps, 0, 0, ps->list->allocation.width, ps->list->allocation.height); - ddb_listview_list_expose (ps, 0, 0, ps->list->allocation.width, ps->list->allocation.height); + gtk_widget_queue_draw (ps->header); + gtk_widget_queue_draw (ps->list); ps->binding->column_size_changed (ps, ps->header_sizing); } else { @@ -2451,7 +2415,7 @@ ddb_listview_header_button_release_event (GtkWidget *widget, else if (sort_order == 2) { c->sort_order = 1; } - ps->binding->col_sort (i, c->sort_order, c->user_data); + ps->binding->col_sort (i, c->sort_order-1, c->user_data); sorted = 1; } else { @@ -2459,7 +2423,7 @@ ddb_listview_header_button_release_event (GtkWidget *widget, } x += w; } - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_COLUMNS | DDB_EXPOSE_LIST | DDB_EXPOSE_COLUMNS); + ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_COLUMNS); } else { ps->header_sizing = -1; @@ -2478,7 +2442,7 @@ ddb_listview_header_button_release_event (GtkWidget *widget, } if (ps->header_dragging >= 0) { ps->header_dragging = -1; - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_COLUMNS | DDB_EXPOSE_LIST | DDB_EXPOSE_COLUMNS | DDB_REFRESH_HSCROLL); + ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_COLUMNS | DDB_REFRESH_HSCROLL); } } ps->binding->columns_changed (ps); @@ -2630,7 +2594,9 @@ ddb_listview_motion_notify_event (GtkWidget *widget, { int x = event->x; int y = event->y; +#if GTK_CHECK_VERSION(2,12,0) && !defined(ULTRA_COMPATIBLE) gdk_event_request_motions (event); +#endif DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); ddb_listview_list_mousemove (ps, event, x, y); return FALSE; @@ -2959,6 +2925,5 @@ ddb_listview_clear_sort (DdbListview *listview) { for (c = listview->columns; c; c = c->next) { c->sort_order = 0; } - ddb_listview_header_render (listview); - ddb_listview_header_expose (listview, 0, 0, listview->header->allocation.width, listview->header->allocation.height); + gtk_widget_queue_draw (listview->header); } diff --git a/plugins/gtkui/ddblistview.h b/plugins/gtkui/ddblistview.h index 55333384..d0a7ceb9 100644 --- a/plugins/gtkui/ddblistview.h +++ b/plugins/gtkui/ddblistview.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -103,8 +103,6 @@ struct _DdbListview { GtkWidget *hscrollbar; int totalwidth; // width of listview, including invisible (scrollable) part - GdkPixmap *backbuf; - GdkPixmap *backbuf_header; const char *title; // unique id, used for config writing, etc int lastpos[2]; // last mouse position (for list widget) // current state @@ -205,8 +203,6 @@ enum { DDB_REFRESH_HSCROLL = 2, DDB_REFRESH_VSCROLL = 4, DDB_REFRESH_LIST = 8, - DDB_EXPOSE_COLUMNS = 16, - DDB_EXPOSE_LIST = 32, }; void ddb_listview_refresh (DdbListview *listview, uint32_t flags); diff --git a/plugins/gtkui/ddbseekbar.c b/plugins/gtkui/ddbseekbar.c index 407b347c..1ad285a7 100644 --- a/plugins/gtkui/ddbseekbar.c +++ b/plugins/gtkui/ddbseekbar.c @@ -1,4 +1,4 @@ -/* ddbseekbar.c generated by valac 0.10.0, the Vala compiler +/* ddbseekbar.c generated by valac 0.10.2, the Vala compiler * generated from ddbseekbar.vala, do not modify */ /* @@ -138,7 +138,7 @@ static gboolean ddb_seekbar_real_configure_event (GtkWidget* base, GdkEventConfi DdbSeekbar* ddb_seekbar_construct (GType object_type) { - DdbSeekbar * self; + DdbSeekbar * self = NULL; self = (DdbSeekbar*) gtk_widget_new (object_type, NULL); return self; } diff --git a/plugins/gtkui/ddbseekbar.h b/plugins/gtkui/ddbseekbar.h index f501a00c..c975654e 100644 --- a/plugins/gtkui/ddbseekbar.h +++ b/plugins/gtkui/ddbseekbar.h @@ -1,4 +1,4 @@ -/* ddbseekbar.h generated by valac 0.10.0, the Vala compiler, do not modify */ +/* ddbseekbar.h generated by valac 0.10.2, the Vala compiler, do not modify */ #ifndef __DDBSEEKBAR_H__ diff --git a/plugins/gtkui/ddbtabstrip.c b/plugins/gtkui/ddbtabstrip.c index ea7905c7..d6920a9f 100644 --- a/plugins/gtkui/ddbtabstrip.c +++ b/plugins/gtkui/ddbtabstrip.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -43,7 +43,14 @@ extern GtkWidget *theme_button; void plt_get_title_wrapper (int plt, char *buffer, int len) { - deadbeef->plt_get_title (plt, buffer, len); + if (plt == -1) { + strcpy (buffer, ""); + return; + } + deadbeef->plt_lock (); + void *p = deadbeef->plt_get_handle (plt); + deadbeef->plt_get_title (p, buffer, len); + deadbeef->plt_unlock (); char *end; if (!g_utf8_validate (buffer, -1, (const gchar **)&end)) { *end = 0; @@ -135,6 +142,11 @@ ddb_tabstrip_size_allocate (GtkWidget *widget, } } + +gboolean +on_tabstrip_scroll_event (GtkWidget *widget, + GdkEventScroll *event); + gboolean on_tabstrip_button_press_event (GtkWidget *widget, GdkEventButton *event); @@ -213,6 +225,7 @@ ddb_tabstrip_class_init(DdbTabStripClass *class) widget_class->button_release_event = on_tabstrip_button_release_event; widget_class->configure_event = on_tabstrip_configure_event; widget_class->motion_notify_event = on_tabstrip_motion_notify_event; + widget_class->scroll_event= on_tabstrip_scroll_event; widget_class->drag_motion = on_tabstrip_drag_motion_event; widget_class->drag_drop = on_tabstrip_drag_drop; widget_class->drag_end = on_tabstrip_drag_end; @@ -452,6 +465,32 @@ tabstrip_adjust_hscroll (DdbTabStrip *ts) { } void +set_tab_text_color (int idx) { + if (idx == -1) { + return; + } + deadbeef->plt_lock (); + void *plt = deadbeef->plt_get_handle (idx); + const char *clr = deadbeef->plt_find_meta (plt, "gui.color"); + int fallback = 1; + if (clr) { + int r, g, b; + if (3 == sscanf (clr, "%02x%02x%02x", &r, &g, &b)) { + fallback = 0; + float fg[3] = {(float)r/0xff, (float)g/0xff, (float)b/0xff}; + draw_set_fg_color (fg); + } + } + if (fallback) { + GdkColor color; + gtkui_get_tabstrip_text_color (&color); + float fg[3] = {(float)color.red/0xffff, (float)color.green/0xffff, (float)color.blue/0xffff}; + draw_set_fg_color (fg); + } + deadbeef->plt_unlock (); +} + +void tabstrip_render (DdbTabStrip *ts) { GtkWidget *widget = GTK_WIDGET (ts); GdkDrawable *backbuf = gtk_widget_get_window (widget); @@ -514,9 +553,8 @@ tabstrip_render (DdbTabStrip *ts) { ddb_tabstrip_draw_tab (widget, backbuf, idx == tab_selected, x, y, w, h); char tab_title[100]; plt_get_title_wrapper (idx, tab_title, sizeof (tab_title)); - GdkColor *color = &widget->style->text[GTK_STATE_NORMAL]; - float fg[3] = {(float)color->red/0xffff, (float)color->green/0xffff, (float)color->blue/0xffff}; - draw_set_fg_color (fg); + + set_tab_text_color (idx); draw_text (x + text_left_padding, y + h/2 - draw_get_font_size()/2 + text_vert_offset, w, 0, tab_title); } x += w - tab_overlap_size; @@ -541,9 +579,7 @@ tabstrip_render (DdbTabStrip *ts) { ddb_tabstrip_draw_tab (widget, backbuf, 1, x, y, w, h); char tab_title[100]; plt_get_title_wrapper (idx, tab_title, sizeof (tab_title)); - GdkColor *color = &widget->style->text[GTK_STATE_NORMAL]; - float fg[3] = {(float)color->red/0xffff, (float)color->green/0xffff, (float)color->blue/0xffff}; - draw_set_fg_color (fg); + set_tab_text_color (idx); draw_text (x + text_left_padding, y + h/2 - draw_get_font_size()/2 + text_vert_offset, w, 0, tab_title); } else { @@ -562,9 +598,7 @@ tabstrip_render (DdbTabStrip *ts) { ddb_tabstrip_draw_tab (widget, backbuf, 1, x, y, w, h); char tab_title[100]; plt_get_title_wrapper (idx, tab_title, sizeof (tab_title)); - GdkColor *color = &widget->style->text[GTK_STATE_NORMAL]; - float fg[3] = {(float)color->red/0xffff, (float)color->green/0xffff, (float)color->blue/0xffff}; - draw_set_fg_color (fg); + set_tab_text_color (idx); draw_text (x + text_left_padding, y + h/2 - draw_get_font_size()/2 + text_vert_offset, w, 0, tab_title); } break; @@ -621,17 +655,23 @@ void on_rename_playlist1_activate (GtkMenuItem *menuitem, gpointer user_data) { - GtkWidget *dlg = create_editplaylistdlg (); + GtkWidget *dlg = create_entrydialog (); gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); gtk_window_set_title (GTK_WINDOW (dlg), _("Edit playlist")); - GtkWidget *e = lookup_widget (dlg, "title"); + GtkWidget *e; + e = lookup_widget (dlg, "title_label"); + gtk_label_set_text (GTK_LABEL(e), _("Title:")); + e = lookup_widget (dlg, "title"); char t[100]; plt_get_title_wrapper (tab_clicked, t, sizeof (t)); gtk_entry_set_text (GTK_ENTRY (e), t); int res = gtk_dialog_run (GTK_DIALOG (dlg)); if (res == GTK_RESPONSE_OK) { const char *text = gtk_entry_get_text (GTK_ENTRY (e)); - deadbeef->plt_set_title (tab_clicked, text); + deadbeef->plt_lock (); + void *p = deadbeef->plt_get_handle (tab_clicked); + deadbeef->plt_set_title (p, text); + deadbeef->plt_unlock (); } gtk_widget_destroy (dlg); } @@ -643,6 +683,7 @@ on_remove_playlist1_activate (GtkMenuItem *menuitem, { if (tab_clicked != -1) { deadbeef->plt_remove (tab_clicked); + playlist_refresh (); int playlist = deadbeef->plt_get_curr (); deadbeef->conf_set_int ("playlist.current", playlist); } @@ -739,8 +780,26 @@ tabstrip_scroll_cb (gpointer data) { } gboolean -on_tabstrip_button_press_event (GtkWidget *widget, - GdkEventButton *event) +on_tabstrip_scroll_event(GtkWidget *widget, + GdkEventScroll *event) +{ + DdbTabStrip *ts = DDB_TABSTRIP (widget); + + if(event->direction == GDK_SCROLL_UP) + { + tabstrip_scroll_left(ts); + } + else if (event->direction == GDK_SCROLL_DOWN) + { + tabstrip_scroll_right(ts); + } + + return TRUE; +} + +gboolean +on_tabstrip_button_press_event(GtkWidget *widget, + GdkEventButton *event) { DdbTabStrip *ts = DDB_TABSTRIP (widget); tab_clicked = get_tab_under_cursor (ts, event->x); @@ -818,6 +877,7 @@ on_tabstrip_button_press_event (GtkWidget *widget, else if (deadbeef->conf_get_int ("gtkui.mmb_delete_playlist", 1)) { if (tab_clicked != -1) { deadbeef->plt_remove (tab_clicked); + playlist_refresh (); int playlist = deadbeef->plt_get_curr (); deadbeef->conf_set_int ("playlist.current", playlist); } @@ -879,7 +939,9 @@ on_tabstrip_motion_notify_event (GtkWidget *widget, ev_x = event->x; ev_y = event->y; ev_state = event->state; +#if GTK_CHECK_VERSION(2,12,0) && !defined(ULTRA_COMPATIBLE) gdk_event_request_motions (event); +#endif if ((ev_state & GDK_BUTTON1_MASK) && ts->prepare) { if (gtk_drag_check_threshold (widget, ev_x, ts->prev_x, 0, 0)) { ts->prepare = 0; diff --git a/plugins/gtkui/ddbtabstrip.h b/plugins/gtkui/ddbtabstrip.h index 7f530e2f..c68b95f1 100644 --- a/plugins/gtkui/ddbtabstrip.h +++ b/plugins/gtkui/ddbtabstrip.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/ddbvolumebar.c b/plugins/gtkui/ddbvolumebar.c index 83389f7f..b0df2c9a 100644 --- a/plugins/gtkui/ddbvolumebar.c +++ b/plugins/gtkui/ddbvolumebar.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/ddbvolumebar.h b/plugins/gtkui/ddbvolumebar.h index 530e556e..d2cfbe61 100644 --- a/plugins/gtkui/ddbvolumebar.h +++ b/plugins/gtkui/ddbvolumebar.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/deadbeef.glade b/plugins/gtkui/deadbeef.glade index 4f38af30..0351668d 100644 --- a/plugins/gtkui/deadbeef.glade +++ b/plugins/gtkui/deadbeef.glade @@ -62,7 +62,7 @@ <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/> <child internal-child="image"> - <widget class="GtkImage" id="image452"> + <widget class="GtkImage" id="image512"> <property name="visible">True</property> <property name="stock">gtk-open</property> <property name="icon_size">1</property> @@ -89,7 +89,7 @@ <signal name="activate" handler="on_add_files_activate" last_modification_time="Sat, 04 Jul 2009 13:04:01 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image453"> + <widget class="GtkImage" id="image513"> <property name="visible">True</property> <property name="stock">gtk-add</property> <property name="icon_size">1</property> @@ -110,7 +110,7 @@ <signal name="activate" handler="on_add_folders_activate" last_modification_time="Sun, 06 Sep 2009 17:51:40 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image454"> + <widget class="GtkImage" id="image514"> <property name="visible">True</property> <property name="stock">gtk-add</property> <property name="icon_size">1</property> @@ -190,7 +190,7 @@ <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/> <child internal-child="image"> - <widget class="GtkImage" id="image455"> + <widget class="GtkImage" id="image515"> <property name="visible">True</property> <property name="stock">gtk-quit</property> <property name="icon_size">1</property> @@ -224,7 +224,7 @@ <signal name="activate" handler="on_clear1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image456"> + <widget class="GtkImage" id="image516"> <property name="visible">True</property> <property name="stock">gtk-clear</property> <property name="icon_size">1</property> @@ -283,7 +283,7 @@ <signal name="activate" handler="on_remove1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image457"> + <widget class="GtkImage" id="image517"> <property name="visible">True</property> <property name="stock">gtk-remove</property> <property name="icon_size">1</property> @@ -320,6 +320,55 @@ </child> <child> + <widget class="GtkMenuItem" id="sort_by1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Sort By</property> + <property name="use_underline">True</property> + + <child> + <widget class="GtkMenu" id="sort_by1_menu"> + + <child> + <widget class="GtkMenuItem" id="album1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Album</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_album1_activate" last_modification_time="Fri, 03 Dec 2010 21:03:21 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="artist1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Artist</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_artist1_activate" last_modification_time="Fri, 03 Dec 2010 21:03:21 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="date1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Date</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_date1_activate" last_modification_time="Fri, 03 Dec 2010 21:03:21 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="custom2"> + <property name="visible">True</property> + <property name="label" translatable="yes">Custom</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_custom2_activate" last_modification_time="Fri, 03 Dec 2010 21:03:21 GMT"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + + <child> <widget class="GtkSeparatorMenuItem" id="separator5"> <property name="visible">True</property> </widget> @@ -422,7 +471,7 @@ <child> <widget class="GtkRadioMenuItem" id="order_shuffle"> <property name="visible">True</property> - <property name="label" translatable="yes">Shuffle</property> + <property name="label" translatable="yes">Shuffle tracks</property> <property name="use_underline">True</property> <property name="active">True</property> <property name="group">order_linear</property> @@ -431,6 +480,17 @@ </child> <child> + <widget class="GtkRadioMenuItem" id="order_shuffle_albums"> + <property name="visible">True</property> + <property name="label" translatable="yes">Shuffle albums</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="group">order_linear</property> + <signal name="activate" handler="on_order_shuffle_albums_activate" last_modification_time="Sun, 12 Dec 2010 18:14:47 GMT"/> + </widget> + </child> + + <child> <widget class="GtkRadioMenuItem" id="order_random"> <property name="visible">True</property> <property name="label" translatable="yes">Random</property> @@ -558,7 +618,7 @@ <signal name="activate" handler="on_help1_activate" last_modification_time="Tue, 08 Sep 2009 17:32:06 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image458"> + <widget class="GtkImage" id="image518"> <property name="visible">True</property> <property name="stock">gtk-help</property> <property name="icon_size">1</property> @@ -618,7 +678,7 @@ <signal name="activate" handler="on_about1_activate" last_modification_time="Sat, 04 Jul 2009 12:57:58 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image459"> + <widget class="GtkImage" id="image519"> <property name="visible">True</property> <property name="stock">gtk-about</property> <property name="icon_size">1</property> @@ -639,7 +699,7 @@ <signal name="activate" handler="on_translators1_activate" last_modification_time="Sun, 19 Sep 2010 13:38:07 GMT"/> <child internal-child="image"> - <widget class="GtkImage" id="image460"> + <widget class="GtkImage" id="image520"> <property name="visible">True</property> <property name="stock">gtk-about</property> <property name="icon_size">1</property> @@ -1177,10 +1237,10 @@ </child> </widget> -<widget class="GtkWindow" id="addprogress"> +<widget class="GtkWindow" id="progressdlg"> <property name="border_width">12</property> <property name="visible">True</property> - <property name="title" translatable="yes">Adding files...</property> + <property name="title">progressdlg</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="modal">True</property> @@ -1193,7 +1253,6 @@ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> <property name="focus_on_map">True</property> <property name="urgency_hint">False</property> - <signal name="delete_event" handler="on_addprogress_delete_event" last_modification_time="Sun, 16 Aug 2009 17:20:03 GMT"/> <child> <widget class="GtkVBox" id="vbox6"> @@ -1252,14 +1311,13 @@ </child> <child> - <widget class="GtkButton" id="button3"> + <widget class="GtkButton" id="cancelbtn"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="label">gtk-cancel</property> <property name="use_stock">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_progress_abort" last_modification_time="Mon, 25 Oct 2010 20:04:28 GMT"/> </widget> <packing> <property name="padding">0</property> @@ -1336,7 +1394,7 @@ <property name="visible">True</property> <property name="title" translatable="yes">Track Properties</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> - <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="window_position">GTK_WIN_POS_MOUSE</property> <property name="modal">False</property> <property name="resizable">True</property> <property name="destroy_with_parent">False</property> @@ -1349,6 +1407,8 @@ <property name="urgency_hint">False</property> <signal name="key_press_event" handler="on_trackproperties_key_press_event" last_modification_time="Thu, 31 Dec 2009 13:46:40 GMT"/> <signal name="delete_event" handler="on_trackproperties_delete_event" last_modification_time="Sat, 02 Jan 2010 21:38:32 GMT"/> + <signal name="configure_event" handler="on_trackproperties_configure_event" last_modification_time="Thu, 10 Mar 2011 11:06:48 GMT"/> + <signal name="window_state_event" handler="on_trackproperties_window_state_event" last_modification_time="Thu, 10 Mar 2011 11:08:02 GMT"/> <child> <widget class="GtkNotebook" id="notebook3"> @@ -1387,6 +1447,7 @@ <property name="fixed_height_mode">False</property> <property name="hover_selection">False</property> <property name="hover_expand">False</property> + <signal name="button_press_event" handler="on_metalist_button_press_event" last_modification_time="Sun, 27 Feb 2011 18:14:41 GMT"/> </widget> </child> </widget> @@ -1398,22 +1459,21 @@ </child> <child> - <widget class="GtkHButtonBox" id="hbuttonbox1"> + <widget class="GtkHBox" id="hbox98"> <property name="visible">True</property> - <property name="layout_style">GTK_BUTTONBOX_END</property> + <property name="homogeneous">False</property> <property name="spacing">0</property> <child> - <widget class="GtkButton" id="write_tags"> + <widget class="GtkButton" id="settings"> <property name="visible">True</property> - <property name="can_default">True</property> <property name="can_focus">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_write_tags_clicked" last_modification_time="Sat, 27 Mar 2010 20:48:33 GMT"/> + <signal name="clicked" handler="on_tagwriter_settings_clicked" last_modification_time="Wed, 09 Mar 2011 20:01:45 GMT"/> <child> - <widget class="GtkAlignment" id="alignment11"> + <widget class="GtkAlignment" id="alignment24"> <property name="visible">True</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> @@ -1425,15 +1485,15 @@ <property name="right_padding">0</property> <child> - <widget class="GtkHBox" id="hbox52"> + <widget class="GtkHBox" id="hbox99"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">2</property> <child> - <widget class="GtkImage" id="image390"> + <widget class="GtkImage" id="image522"> <property name="visible">True</property> - <property name="stock">gtk-apply</property> + <property name="stock">gtk-preferences</property> <property name="icon_size">4</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> @@ -1448,9 +1508,9 @@ </child> <child> - <widget class="GtkLabel" id="label88"> + <widget class="GtkLabel" id="label123"> <property name="visible">True</property> - <property name="label" translatable="yes">_Apply</property> + <property name="label" translatable="yes">Settings</property> <property name="use_underline">True</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> @@ -1476,81 +1536,174 @@ </widget> </child> </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> </child> <child> - <widget class="GtkButton" id="closebtn"> + <widget class="GtkHButtonBox" id="hbuttonbox1"> <property name="visible">True</property> - <property name="can_default">True</property> - <property name="can_focus">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_closebtn_clicked" last_modification_time="Thu, 01 Apr 2010 12:45:33 GMT"/> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <property name="spacing">0</property> <child> - <widget class="GtkAlignment" id="alignment12"> + <widget class="GtkButton" id="write_tags"> <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">0</property> - <property name="yscale">0</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">0</property> - <property name="right_padding">0</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_write_tags_clicked" last_modification_time="Sat, 27 Mar 2010 20:48:33 GMT"/> <child> - <widget class="GtkHBox" id="hbox53"> + <widget class="GtkAlignment" id="alignment11"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">2</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> <child> - <widget class="GtkImage" id="image391"> + <widget class="GtkHBox" id="hbox52"> <property name="visible">True</property> - <property name="stock">gtk-close</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image390"> + <property name="visible">True</property> + <property name="stock">gtk-apply</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label88"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Apply</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkButton" id="closebtn"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_closebtn_clicked" last_modification_time="Thu, 01 Apr 2010 12:45:33 GMT"/> + + <child> + <widget class="GtkAlignment" id="alignment12"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> <child> - <widget class="GtkLabel" id="label89"> + <widget class="GtkHBox" id="hbox53"> <property name="visible">True</property> - <property name="label" translatable="yes">_Close</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image391"> + <property name="visible">True</property> + <property name="stock">gtk-close</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label89"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Close</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> </child> </widget> </child> </widget> </child> </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> </child> </widget> <packing> @@ -1749,7 +1902,7 @@ <widget class="GtkDialog" id="editcolumndlg"> <property name="border_width">12</property> <property name="visible">True</property> - <property name="title" translatable="yes">editcolumndlg</property> + <property name="title">editcolumndlg</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">True</property> @@ -2037,8 +2190,8 @@ Artist - Album Artist Album Title -Length -Track +Duration +Track No Band / Album Artist Custom</property> <property name="add_tearoffs">False</property> @@ -2091,16 +2244,44 @@ Custom</property> </child> <child> - <widget class="GtkEntry" id="format"> + <widget class="GtkHBox" id="hbox74"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">True</property> - <property name="visibility">True</property> - <property name="max_length">0</property> - <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="format"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="Custom" id="title_formatting_help_link"> + <property name="visible">True</property> + <property name="creation_function">title_formatting_help_link_create</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Fri, 03 Dec 2010 20:39:24 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -2168,38 +2349,6 @@ Right</property> <property name="fill">False</property> </packing> </child> - - <child> - <widget class="GtkLabel" id="label25"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Format conversions (start with %): - [a]rtist, [t]itle, al[b]um, [B]and, [C]omposer - track[n]umber, [N]totaltracks, - [l]ength, [y]ear, [g]enre, [c]omment, - copy[r]ight, [f]ilename, [F]ullPathname, [T]ags, - [d]irectory, [D]irectoryWithPath -Example: %a - %t [%l]</property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">True</property> - <property name="xalign">0.10000000149</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> </widget> <packing> <property name="padding">0</property> @@ -2213,15 +2362,11 @@ Example: %a - %t [%l]</property> <widget class="GtkDialog" id="prefwin"> <property name="border_width">12</property> - <property name="width_request">630</property> - <property name="height_request">400</property> <property name="visible">True</property> <property name="title" translatable="yes">Preferences</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> - <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="window_position">GTK_WIN_POS_CENTER</property> <property name="modal">False</property> - <property name="default_width">630</property> - <property name="default_height">400</property> <property name="resizable">True</property> <property name="destroy_with_parent">False</property> <property name="decorated">True</property> @@ -2232,6 +2377,9 @@ Example: %a - %t [%l]</property> <property name="focus_on_map">True</property> <property name="urgency_hint">False</property> <property name="has_separator">True</property> + <signal name="configure_event" handler="on_prefwin_configure_event" last_modification_time="Thu, 10 Mar 2011 11:14:37 GMT"/> + <signal name="window_state_event" handler="on_prefwin_window_state_event" last_modification_time="Thu, 10 Mar 2011 11:14:42 GMT"/> + <signal name="realize" handler="on_prefwin_realize" last_modification_time="Thu, 10 Mar 2011 11:20:24 GMT"/> <child internal-child="vbox"> <widget class="GtkVBox" id="dialog-vbox2"> @@ -2249,73 +2397,11 @@ Example: %a - %t [%l]</property> <property name="visible">True</property> <property name="can_default">True</property> <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> <property name="focus_on_click">True</property> <property name="response_id">-7</property> - - <child> - <widget class="GtkAlignment" id="alignment14"> - <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">0</property> - <property name="yscale">0</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">0</property> - <property name="right_padding">0</property> - - <child> - <widget class="GtkHBox" id="hbox55"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">2</property> - - <child> - <widget class="GtkImage" id="image393"> - <property name="visible">True</property> - <property name="stock">gtk-close</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkLabel" id="label91"> - <property name="visible">True</property> - <property name="label" translatable="yes">_Close</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - </child> - </widget> - </child> </widget> </child> </widget> @@ -2483,38 +2569,18 @@ Example: %a - %t [%l]</property> <property name="spacing">8</property> <child> - <widget class="GtkCheckButton" id="pref_dynsamplerate"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Allow dynamic samplerate switching</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="clicked" handler="on_pref_dynsamplerate_clicked" last_modification_time="Tue, 26 Jan 2010 19:46:12 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox9"> + <widget class="GtkHBox" id="hbox10"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">8</property> <child> - <widget class="GtkLabel" id="label6"> + <widget class="GtkLabel" id="label8"> <property name="visible">True</property> - <property name="label" translatable="yes">Samplerate conversion quality:</property> + <property name="label" translatable="yes">Replaygain mode:</property> <property name="use_underline">False</property> <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_RIGHT</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> <property name="wrap">False</property> <property name="selectable">False</property> <property name="xalign">0</property> @@ -2534,16 +2600,15 @@ Example: %a - %t [%l]</property> </child> <child> - <widget class="GtkComboBox" id="pref_src_quality"> + <widget class="GtkComboBox" id="pref_replaygain_mode"> + <property name="width_request">337</property> <property name="visible">True</property> - <property name="items">sinc_best_quality -sinc_medium_quality -sinc_fastest -zero_order_hold -linear</property> + <property name="items" translatable="yes">Disable +Track +Album</property> <property name="add_tearoffs">False</property> <property name="focus_on_click">True</property> - <signal name="changed" handler="on_pref_src_quality_changed" last_modification_time="Sat, 10 Oct 2009 19:02:36 GMT"/> + <signal name="changed" handler="on_pref_replaygain_mode_changed" last_modification_time="Sat, 10 Oct 2009 19:22:23 GMT"/> </widget> <packing> <property name="padding">0</property> @@ -2560,21 +2625,41 @@ linear</property> </child> <child> - <widget class="GtkHBox" id="hbox10"> + <widget class="GtkCheckButton" id="pref_replaygain_scale"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Replaygain peak scale</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="clicked" handler="on_pref_replaygain_scale_clicked" last_modification_time="Sat, 10 Oct 2009 18:52:10 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox100"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">8</property> <child> - <widget class="GtkLabel" id="label8"> + <widget class="GtkLabel" id="label124"> <property name="visible">True</property> - <property name="label" translatable="yes">Replaygain mode:</property> + <property name="label" translatable="yes">Replaygain preamp:</property> <property name="use_underline">False</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> <property name="wrap">False</property> <property name="selectable">False</property> - <property name="xalign">0</property> + <property name="xalign">0.5</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> @@ -2591,15 +2676,41 @@ linear</property> </child> <child> - <widget class="GtkComboBox" id="pref_replaygain_mode"> - <property name="width_request">337</property> + <widget class="GtkLabel" id="label125"> <property name="visible">True</property> - <property name="items" translatable="yes">Disable -Track -Album</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - <signal name="changed" handler="on_pref_replaygain_mode_changed" last_modification_time="Sat, 10 Oct 2009 19:22:23 GMT"/> + <property name="label" translatable="yes">-12 dB</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHScale" id="replaygain_preamp"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="draw_value">True</property> + <property name="value_pos">GTK_POS_BOTTOM</property> + <property name="digits">0</property> + <property name="update_policy">GTK_UPDATE_CONTINUOUS</property> + <property name="inverted">False</property> + <property name="adjustment">0 -12 12 0 0 0</property> + <signal name="value_changed" handler="on_replaygain_preamp_value_changed" last_modification_time="Sat, 12 Mar 2011 14:20:29 GMT"/> </widget> <packing> <property name="padding">0</property> @@ -2607,26 +2718,31 @@ Album</property> <property name="fill">True</property> </packing> </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkCheckButton" id="pref_replaygain_scale"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Replaygain peak scale</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="clicked" handler="on_pref_replaygain_scale_clicked" last_modification_time="Sat, 10 Oct 2009 18:52:10 GMT"/> + <child> + <widget class="GtkLabel" id="label126"> + <property name="visible">True</property> + <property name="label" translatable="yes">+12 dB</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -2707,6 +2823,26 @@ Album</property> <property name="fill">False</property> </packing> </child> + + <child> + <widget class="GtkCheckButton" id="ignore_archives"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Don't add from archives when adding folders</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_ignore_archives_toggled" last_modification_time="Tue, 05 Apr 2011 20:41:18 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> <packing> <property name="tab_expand">False</property> @@ -2738,6 +2874,283 @@ Album</property> </child> <child> + <widget class="GtkVBox" id="vbox29"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox80"> + <property name="visible">True</property> + <property name="homogeneous">True</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="dsp_add"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_add_clicked" last_modification_time="Wed, 29 Dec 2010 11:54:39 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="dsp_remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_remove_clicked" last_modification_time="Wed, 29 Dec 2010 11:54:43 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="dsp_configure"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Configure</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_configure_clicked" last_modification_time="Wed, 29 Dec 2010 11:54:47 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox81"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow7"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="dsp_listview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">False</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox30"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkButton" id="dsp_up"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-up</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_up_clicked" last_modification_time="Wed, 29 Dec 2010 11:54:52 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="dsp_down"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-go-down</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_down_clicked" last_modification_time="Wed, 29 Dec 2010 11:54:55 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox86"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label114"> + <property name="visible">True</property> + <property name="label" translatable="yes">DSP Chain Preset</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBoxEntry" id="dsp_preset"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="has_frame">True</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_dsp_preset_changed" last_modification_time="Wed, 05 Jan 2011 19:38:28 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="dsp_preset_save"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-save</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_save_clicked" last_modification_time="Wed, 05 Jan 2011 19:38:10 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="dsp_preset_load"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Load</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_dsp_preset_load_clicked" last_modification_time="Wed, 05 Jan 2011 19:38:13 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label110"> + <property name="visible">True</property> + <property name="label" translatable="yes">DSP</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">tab</property> + </packing> + </child> + + <child> <widget class="GtkVBox" id="vbox9"> <property name="border_width">12</property> <property name="visible">True</property> @@ -2845,6 +3258,83 @@ Album</property> </child> <child> + <widget class="GtkCheckButton" id="auto_name_playlist_from_folder"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Auto-name playlists when adding a single folder</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_auto_name_playlist_from_folder_toggled" last_modification_time="Tue, 04 Jan 2011 20:49:15 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox102"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label129"> + <property name="visible">True</property> + <property name="label" translatable="yes">Interface refresh rate (times per second):</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHScale" id="gui_fps"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="draw_value">True</property> + <property name="value_pos">GTK_POS_RIGHT</property> + <property name="digits">0</property> + <property name="update_policy">GTK_UPDATE_CONTINUOUS</property> + <property name="inverted">False</property> + <property name="adjustment">10 1 30 0 0 0</property> + <signal name="value_changed" handler="on_gui_fps_value_changed" last_modification_time="Mon, 04 Apr 2011 20:11:49 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> <widget class="GtkHBox" id="hbox64"> <property name="visible">True</property> <property name="homogeneous">False</property> @@ -2961,7 +3451,55 @@ Album</property> </child> <child> - <placeholder/> + <widget class="GtkHBox" id="hbox101"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label128"> + <property name="visible">True</property> + <property name="label" translatable="yes">GUI Plugin (changing requires restart):</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="gui_plugin"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_gui_plugin_changed" last_modification_time="Wed, 23 Mar 2011 20:30:20 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> </child> </widget> <packing> @@ -3199,7 +3737,7 @@ Album</property> <widget class="GtkTable" id="tabstrip_colors_group"> <property name="visible">True</property> <property name="n_rows">2</property> - <property name="n_columns">4</property> + <property name="n_columns">5</property> <property name="homogeneous">True</property> <property name="row_spacing">0</property> <property name="column_spacing">8</property> @@ -3387,6 +3925,52 @@ Album</property> <property name="y_options"></property> </packing> </child> + + <child> + <widget class="GtkLabel" id="label127"> + <property name="visible">True</property> + <property name="label" translatable="yes">Text</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">expand</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkColorButton" id="tabstrip_text"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="use_alpha">False</property> + <property name="focus_on_click">True</property> + <signal name="color_set" handler="on_tabstrip_text_color_set" last_modification_time="Wed, 23 Mar 2011 20:09:34 GMT"/> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">expand</property> + <property name="y_options"></property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -4148,705 +4732,6 @@ SOCKS5_HOSTNAME</property> </child> <child> - <widget class="GtkVBox" id="vbox18"> - <property name="border_width">12</property> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> - - <child> - <widget class="GtkFrame" id="frame5"> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <property name="label_yalign">0.5</property> - <property name="shadow_type">GTK_SHADOW_NONE</property> - - <child> - <widget class="GtkAlignment" id="alignment3"> - <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">12</property> - <property name="right_padding">0</property> - - <child> - <widget class="GtkVBox" id="vbox19"> - <property name="border_width">12</property> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkHBox" id="hbox38"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkCheckButton" id="write_id3v2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write ID3v2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_write_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:43:51 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="write_id3v1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write ID3v1</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_write_id3v1_toggled" last_modification_time="Tue, 30 Mar 2010 20:43:55 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="write_apev2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write APEv2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_write_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:43:59 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox40"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkCheckButton" id="strip_id3v2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip ID3v2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_strip_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:03 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="strip_id3v1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip ID3v1</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_strip_id3v1_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:07 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="strip_apev2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip APEv2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_strip_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:12 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox36"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkLabel" id="label69"> - <property name="visible">True</property> - <property name="label" translatable="yes">ID3v2 version</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="id3v2_version"> - <property name="visible">True</property> - <property name="items" translatable="yes">2.3 (Recommended) -2.4</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - <signal name="changed" handler="on_id3v2_version_changed" last_modification_time="Tue, 30 Mar 2010 20:44:27 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox39"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkLabel" id="label71"> - <property name="visible">True</property> - <property name="label" translatable="yes">ID3v1 character encoding (default is iso8859-1)</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkEntry" id="id3v1_encoding"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">True</property> - <property name="visibility">True</property> - <property name="max_length">0</property> - <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">False</property> - <signal name="changed" handler="on_id3v1_encoding_changed" last_modification_time="Tue, 30 Mar 2010 20:44:34 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - - <child> - <widget class="GtkLabel" id="label68"> - <property name="visible">True</property> - <property name="label"><b>MP3</b></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox41"> - <property name="visible">True</property> - <property name="homogeneous">True</property> - <property name="spacing">0</property> - - <child> - <widget class="GtkFrame" id="frame6"> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <property name="label_yalign">0.5</property> - <property name="shadow_type">GTK_SHADOW_NONE</property> - - <child> - <widget class="GtkAlignment" id="alignment4"> - <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">12</property> - <property name="right_padding">0</property> - - <child> - <widget class="GtkVBox" id="vbox20"> - <property name="border_width">12</property> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkHBox" id="hbox37"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkCheckButton" id="ape_write_id3v2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write ID3v2.4</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_ape_write_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:42 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="ape_write_apev2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write APEv2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_ape_write_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:46 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox45"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkCheckButton" id="ape_strip_id3v2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip ID3v2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_ape_strip_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:50 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="ape_strip_apev2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip APEv2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_ape_strip_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:54 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - - <child> - <widget class="GtkLabel" id="label70"> - <property name="visible">True</property> - <property name="label"><b>APE</b></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkFrame" id="frame7"> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <property name="label_yalign">0.5</property> - <property name="shadow_type">GTK_SHADOW_NONE</property> - - <child> - <widget class="GtkAlignment" id="alignment5"> - <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">12</property> - <property name="right_padding">0</property> - - <child> - <widget class="GtkVBox" id="vbox_wv"> - <property name="border_width">12</property> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkHBox" id="hbox44"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkCheckButton" id="wv_write_apev2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write APEv2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_wv_write_apev2_toggled" last_modification_time="Tue, 06 Apr 2010 19:36:17 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="wv_write_id3v1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Write ID3v1</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_wv_write_id3v1_toggled" last_modification_time="Tue, 06 Apr 2010 19:36:13 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox43"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkCheckButton" id="wv_strip_apev2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip APEv2</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_wv_strip_apev2_toggled" last_modification_time="Tue, 06 Apr 2010 19:40:53 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="wv_strip_id3v1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Strip ID3v1</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_wv_strip_id3v1_toggled" last_modification_time="Tue, 06 Apr 2010 19:40:57 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - </child> - </widget> - </child> - - <child> - <widget class="GtkLabel" id="label79"> - <property name="visible">True</property> - <property name="label"><b>WavPack</b></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="tab_expand">False</property> - <property name="tab_fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkLabel" id="label67"> - <property name="visible">True</property> - <property name="label" translatable="yes">Tag writer</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="type">tab</property> - </packing> - </child> - - <child> <widget class="GtkHPaned" id="hpaned1"> <property name="border_width">12</property> <property name="visible">True</property> @@ -4892,249 +4777,138 @@ SOCKS5_HOSTNAME</property> <property name="spacing">8</property> <child> - <widget class="GtkHBox" id="hbox16"> + <widget class="GtkScrolledWindow" id="scrolledwindow8"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkLabel" id="label11"> - <property name="visible">True</property> - <property name="label" translatable="yes">Description:</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> <child> - <widget class="GtkEntry" id="pref_plugin_descr"> + <widget class="GtkTextView" id="plug_description"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="editable">False</property> - <property name="visibility">True</property> - <property name="max_length">0</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">False</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">False</property> </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> </child> </widget> <packing> <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> + <property name="expand">True</property> + <property name="fill">True</property> </packing> </child> <child> - <widget class="GtkHBox" id="hbox17"> + <widget class="GtkHBox" id="hbox20"> <property name="visible">True</property> <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkLabel" id="label12"> - <property name="visible">True</property> - <property name="label" translatable="yes">Author(s):</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <property name="spacing">0</property> <child> - <widget class="GtkEntry" id="pref_plugin_author"> + <widget class="GtkButton" id="configure_plugin"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_focus">True</property> - <property name="editable">False</property> - <property name="visibility">True</property> - <property name="max_length">0</property> - <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">False</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox18"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> - - <child> - <widget class="GtkLabel" id="label13"> - <property name="visible">True</property> - <property name="label" translatable="yes">Email:</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <signal name="clicked" handler="on_configure_plugin_clicked" last_modification_time="Tue, 01 Dec 2009 16:54:36 GMT"/> - <child> - <widget class="GtkEntry" id="pref_plugin_email"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">False</property> - <property name="visibility">True</property> - <property name="max_length">0</property> - <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">False</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <child> + <widget class="GtkAlignment" id="alignment15"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> - <child> - <widget class="GtkHBox" id="hbox19"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">8</property> + <child> + <widget class="GtkHBox" id="hbox56"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> - <child> - <widget class="GtkLabel" id="label14"> - <property name="visible">True</property> - <property name="label" translatable="yes">Website:</property> - <property name="use_underline">False</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <child> + <widget class="GtkImage" id="image394"> + <property name="visible">True</property> + <property name="stock">gtk-preferences</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> - <child> - <widget class="GtkEntry" id="pref_plugin_website"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">False</property> - <property name="visibility">True</property> - <property name="max_length">0</property> - <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">False</property> + <child> + <widget class="GtkLabel" id="label92"> + <property name="visible">True</property> + <property name="label" translatable="yes">Configure</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> </widget> <packing> <property name="padding">0</property> <property name="expand">True</property> - <property name="fill">True</property> + <property name="fill">False</property> </packing> </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox20"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> <child> - <widget class="GtkButton" id="configure_plugin"> + <widget class="GtkButton" id="plug_copyright"> <property name="visible">True</property> + <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_configure_plugin_clicked" last_modification_time="Tue, 01 Dec 2009 16:54:36 GMT"/> + <signal name="clicked" handler="on_plug_copyright_clicked" last_modification_time="Sun, 27 Feb 2011 10:24:22 GMT"/> <child> - <widget class="GtkAlignment" id="alignment15"> + <widget class="GtkAlignment" id="alignment20"> <property name="visible">True</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> @@ -5146,15 +4920,15 @@ SOCKS5_HOSTNAME</property> <property name="right_padding">0</property> <child> - <widget class="GtkHBox" id="hbox56"> + <widget class="GtkHBox" id="hbox88"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">2</property> <child> - <widget class="GtkImage" id="image394"> + <widget class="GtkImage" id="image521"> <property name="visible">True</property> - <property name="stock">gtk-preferences</property> + <property name="stock">gtk-about</property> <property name="icon_size">4</property> <property name="xalign">0.5</property> <property name="yalign">0.5</property> @@ -5169,9 +4943,9 @@ SOCKS5_HOSTNAME</property> </child> <child> - <widget class="GtkLabel" id="label92"> + <widget class="GtkLabel" id="label117"> <property name="visible">True</property> - <property name="label" translatable="yes">Configure</property> + <property name="label" translatable="yes">Copyright</property> <property name="use_underline">True</property> <property name="use_markup">False</property> <property name="justify">GTK_JUSTIFY_LEFT</property> @@ -5203,6 +4977,21 @@ SOCKS5_HOSTNAME</property> <property name="fill">False</property> </packing> </child> + + <child> + <widget class="Custom" id="weblink"> + <property name="visible">True</property> + <property name="creation_function">create_plugin_weblink</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Sun, 27 Feb 2011 11:39:00 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">False</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -5256,10 +5045,10 @@ SOCKS5_HOSTNAME</property> </child> </widget> -<widget class="GtkDialog" id="editplaylistdlg"> +<widget class="GtkDialog" id="entrydialog"> <property name="border_width">8</property> <property name="visible">True</property> - <property name="title" translatable="yes">editplaylistdlg</property> + <property name="title">EntryDialog</property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">False</property> @@ -5457,7 +5246,7 @@ SOCKS5_HOSTNAME</property> <property name="spacing">8</property> <child> - <widget class="GtkLabel" id="label40"> + <widget class="GtkLabel" id="title_label"> <property name="visible">True</property> <property name="label" translatable="yes">Title:</property> <property name="use_underline">False</property> @@ -5991,16 +5780,44 @@ SOCKS5_HOSTNAME</property> </child> <child> - <widget class="GtkEntry" id="format"> + <widget class="GtkHBox" id="hbox75"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">True</property> - <property name="visibility">True</property> - <property name="max_length">0</property> - <property name="text" translatable="yes"></property> - <property name="has_frame">True</property> - <property name="invisible_char">â—</property> - <property name="activates_default">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="format"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="Custom" id="custom1"> + <property name="visible">True</property> + <property name="creation_function">title_formatting_help_link_create</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Fri, 03 Dec 2010 20:39:24 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -6015,30 +5832,347 @@ SOCKS5_HOSTNAME</property> <property name="fill">False</property> </packing> </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="sortbydlg"> + <property name="visible">True</property> + <property name="title" translatable="yes">Sort by...</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox8"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area7"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> <child> - <widget class="GtkLabel" id="label82"> + <widget class="GtkButton" id="cancelbutton5"> <property name="visible">True</property> + <property name="can_default">True</property> <property name="can_focus">True</property> - <property name="label" translatable="yes">Format conversions (start with %): - [a]rtist, [t]itle, al[b]um, [B]and, [C]omposer - track[n]umber, [N]totaltracks, - [l]ength, [y]ear, [g]enre, [c]omment, - copy[r]ight, [f]ilename, [T]ags -Example: %a - %t [%l]</property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">True</property> - <property name="xalign">0.10000000149</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="okbutton5"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox28"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox76"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label108"> + <property name="visible">True</property> + <property name="label" translatable="yes">Format</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox77"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkEntry" id="sortfmt"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">False</property> + <signal name="activate" handler="on_sortfmt_activate" last_modification_time="Fri, 03 Dec 2010 22:08:09 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="Custom" id="custom3"> + <property name="visible">True</property> + <property name="creation_function">title_formatting_help_link_create</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Fri, 03 Dec 2010 20:39:24 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox78"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label109"> + <property name="visible">True</property> + <property name="label" translatable="yes">Order</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="sortorder"> + <property name="visible">True</property> + <property name="items" translatable="yes">Ascending +Descending</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> +</widget> + +<widget class="GtkDialog" id="select_dsp_plugin"> + <property name="visible">True</property> + <property name="title" translatable="yes">Select DSP Plugin</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox10"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area9"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="cancelbutton7"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="okbutton7"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox31"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox85"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label113"> + <property name="visible">True</property> + <property name="label" translatable="yes">Plugin</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="plugin"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> @@ -6057,4 +6191,733 @@ Example: %a - %t [%l]</property> </child> </widget> +<widget class="GtkDialog" id="tagwritersettings"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Tag Writer Settings</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">True</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">False</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox11"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area10"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="closebutton2"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-7</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox32"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkFrame" id="frame8"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment21"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox33"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox89"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="write_id3v2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write ID3v2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_write_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:43:51 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="write_id3v1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write ID3v1</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_write_id3v1_toggled" last_modification_time="Tue, 30 Mar 2010 20:43:55 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="write_apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_write_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:43:59 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox90"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="strip_id3v2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip ID3v2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_strip_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:03 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="strip_id3v1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip ID3v1</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_strip_id3v1_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:07 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="strip_apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_strip_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:12 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox91"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label118"> + <property name="visible">True</property> + <property name="label" translatable="yes">ID3v2 version</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="id3v2_version"> + <property name="visible">True</property> + <property name="items" translatable="yes">2.3 (Recommended) +2.4</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_id3v2_version_changed" last_modification_time="Tue, 30 Mar 2010 20:44:27 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox92"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkLabel" id="label119"> + <property name="visible">True</property> + <property name="label" translatable="yes">ID3v1 character encoding (default is iso8859-1)</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="id3v1_encoding"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â—</property> + <property name="activates_default">False</property> + <signal name="changed" handler="on_id3v1_encoding_changed" last_modification_time="Tue, 30 Mar 2010 20:44:34 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label120"> + <property name="visible">True</property> + <property name="label"><b>MP3</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox93"> + <property name="visible">True</property> + <property name="homogeneous">True</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkFrame" id="frame9"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment22"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox34"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox94"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="ape_write_id3v2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write ID3v2.4</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_ape_write_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:42 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="ape_write_apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_ape_write_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:46 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox95"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="ape_strip_id3v2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip ID3v2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_ape_strip_id3v2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:50 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="ape_strip_apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_ape_strip_apev2_toggled" last_modification_time="Tue, 30 Mar 2010 20:44:54 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label121"> + <property name="visible">True</property> + <property name="label"><b>APE</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkFrame" id="frame10"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="label_yalign">0.5</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + + <child> + <widget class="GtkAlignment" id="alignment23"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="vbox35"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkHBox" id="hbox96"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="wv_write_apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_wv_write_apev2_toggled" last_modification_time="Tue, 06 Apr 2010 19:36:17 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="wv_write_id3v1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Write ID3v1</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_wv_write_id3v1_toggled" last_modification_time="Tue, 06 Apr 2010 19:36:13 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox97"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child> + <widget class="GtkCheckButton" id="wv_strip_apev2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip APEv2</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_wv_strip_apev2_toggled" last_modification_time="Tue, 06 Apr 2010 19:40:53 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="wv_strip_id3v1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Strip ID3v1</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_wv_strip_id3v1_toggled" last_modification_time="Tue, 06 Apr 2010 19:40:57 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + + <child> + <widget class="GtkLabel" id="label122"> + <property name="visible">True</property> + <property name="label"><b>WavPack</b></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + </glade-interface> diff --git a/plugins/gtkui/deadbeef.gladep b/plugins/gtkui/deadbeef.gladep index 88874320..d0428a88 100644 --- a/plugins/gtkui/deadbeef.gladep +++ b/plugins/gtkui/deadbeef.gladep @@ -7,5 +7,6 @@ <source_directory></source_directory> <gnome_support>FALSE</gnome_support> <output_main_file>FALSE</output_main_file> + <output_support_files>FALSE</output_support_files> <output_build_files>FALSE</output_build_files> </glade-project> diff --git a/plugins/gtkui/drawing.h b/plugins/gtkui/drawing.h index e8c7e99d..b73c9f97 100644 --- a/plugins/gtkui/drawing.h +++ b/plugins/gtkui/drawing.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -97,6 +97,9 @@ void gtkui_get_tabstrip_base_color (GdkColor *clr); void +gtkui_get_tabstrip_text_color (GdkColor *clr); + +void gtkui_get_listview_even_row_color (GdkColor *clr); void @@ -126,4 +129,7 @@ gtkui_override_bar_colors (void); int gtkui_override_tabstrip_colors (void); +int +draw_get_listview_rowheight (void); + #endif // __DRAWING_H diff --git a/plugins/gtkui/dspconfig.c b/plugins/gtkui/dspconfig.c new file mode 100644 index 00000000..268f1b16 --- /dev/null +++ b/plugins/gtkui/dspconfig.c @@ -0,0 +1,482 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gtk/gtk.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <dirent.h> +#include <sys/stat.h> +#include "callbacks.h" +#include "interface.h" +#include "support.h" +#include "deadbeef.h" +#include "gtkui.h" +#include "pluginconf.h" + +static ddb_dsp_context_t *chain; +static GtkWidget *prefwin; + +static ddb_dsp_context_t * +dsp_clone (ddb_dsp_context_t *from) { + ddb_dsp_context_t *dsp = from->plugin->open (); + char param[2000]; + if (from->plugin->num_params) { + int n = from->plugin->num_params (); + for (int i = 0; i < n; i++) { + from->plugin->get_param (from, i, param, sizeof (param)); + dsp->plugin->set_param (dsp, i, param); + } + } + dsp->enabled = from->enabled; + return dsp; +} + +static void +fill_dsp_chain (GtkListStore *mdl) { + ddb_dsp_context_t *dsp = chain; + while (dsp) { + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, dsp->plugin->plugin.name, -1); + dsp = dsp->next; + } +} + +static int dirent_alphasort (const struct dirent **a, const struct dirent **b) { + return strcmp ((*a)->d_name, (*b)->d_name); +} + +static int +scandir_preset_filter (const struct dirent *ent) { + char *ext = strrchr (ent->d_name, '.'); + if (ext && !strcasecmp (ext, ".txt")) { + return 1; + } + return 0; +} + +void +dsp_setup_init (GtkWidget *_prefwin) { + prefwin = _prefwin; + // copy current dsp chain + ddb_dsp_context_t *streamer_chain = deadbeef->streamer_get_dsp_chain (); + + ddb_dsp_context_t *tail = NULL; + while (streamer_chain) { + ddb_dsp_context_t *new = dsp_clone (streamer_chain); + if (tail) { + tail->next = new; + tail = new; + } + else { + chain = tail = new; + } + streamer_chain = streamer_chain->next; + } + + // fill dsp_listview + GtkWidget *listview = lookup_widget (prefwin, "dsp_listview"); + + + GtkCellRenderer *title_cell = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Plugin"), title_cell, "text", 0, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (listview), GTK_TREE_VIEW_COLUMN (col)); + GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (listview), GTK_TREE_MODEL (mdl)); + + fill_dsp_chain (mdl); + + // set last preset name + GtkWidget *combobox = lookup_widget (prefwin, "dsp_preset"); + GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox)); + if (entry) { + deadbeef->conf_lock (); + gtk_entry_set_text (GTK_ENTRY (entry), deadbeef->conf_get_str_fast ("gtkui.conf_dsp_preset", "")); + deadbeef->conf_unlock (); + } + + // fill list of presets + mdl = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combobox))); + struct dirent **namelist = NULL; + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets/dsp", deadbeef->get_config_dir ()) > 0) { + int n = scandir (path, &namelist, scandir_preset_filter, dirent_alphasort); + int i; + for (i = 0; i < n; i++) { + char title[100]; + strcpy (title, namelist[i]->d_name); + char *e = strrchr (title, '.'); + if (e) { + *e = 0; + } + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, title, -1); + free (namelist[i]); + } + free (namelist); + } +} + +void +dsp_setup_free (void) { + while (chain) { + ddb_dsp_context_t *next = chain->next; + chain->plugin->close (chain); + chain = next; + } + prefwin = NULL; +} + +static void +fill_dsp_plugin_list (GtkListStore *mdl) { + struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list (); + int i; + for (i = 0; dsp[i]; i++) { + GtkTreeIter iter; + gtk_list_store_append (mdl, &iter); + gtk_list_store_set (mdl, &iter, 0, dsp[i]->plugin.name, -1); + } +} + +static void +update_streamer (void) { + deadbeef->streamer_set_dsp_chain (chain); +} + +void +on_dsp_add_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *dlg = create_select_dsp_plugin (); + gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (prefwin)); + gtk_window_set_title (GTK_WINDOW (dlg), _("Add plugin to DSP chain")); + + GtkComboBox *combo; + // fill encoder presets + combo = GTK_COMBO_BOX (lookup_widget (dlg, "plugin")); + GtkListStore *mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + fill_dsp_plugin_list (mdl); + gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.last_selected_dsp", 0)); + + int r = gtk_dialog_run (GTK_DIALOG (dlg)); + if (r == GTK_RESPONSE_OK) { + // create new instance of the selected plugin + int idx = gtk_combo_box_get_active (combo); + struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list (); + int i; + ddb_dsp_context_t *inst = NULL; + for (i = 0; dsp[i]; i++) { + if (i == idx) { + inst = dsp[i]->open (); + break; + } + } + if (inst) { + // append to DSP chain + ddb_dsp_context_t *tail = chain; + while (tail && tail->next) { + tail = tail->next; + } + if (tail) { + tail->next = inst; + } + else { + chain = inst; + } + + // reinit list of instances + GtkWidget *list = lookup_widget (prefwin, "dsp_listview"); + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list))); + gtk_list_store_clear (mdl); + fill_dsp_chain (mdl); + update_streamer (); + } + else { + fprintf (stderr, "prefwin: failed to add DSP plugin to chain\n"); + } + } + gtk_widget_destroy (dlg); +} + +static int +listview_get_index (GtkWidget *list) { + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return - 1; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + return idx; +} + +void +on_dsp_remove_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *list = lookup_widget (prefwin, "dsp_listview"); + int idx = listview_get_index (list); + if (idx == -1) { + return; + } + + ddb_dsp_context_t *p = chain; + ddb_dsp_context_t *prev = NULL; + int i = idx; + while (p && i--) { + prev = p; + p = p->next; + } + if (p) { + if (prev) { + prev->next = p->next; + } + else { + chain = p->next; + } + p->plugin->close (p); + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list))); + gtk_list_store_clear (mdl); + fill_dsp_chain (mdl); + GtkTreePath *path = gtk_tree_path_new_from_indices (idx, -1); + GtkTreeViewColumn *col; + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + update_streamer (); + } +} + +static ddb_dsp_context_t *current_dsp_context = NULL; + +void +dsp_ctx_set_param (const char *key, const char *value) { + current_dsp_context->plugin->set_param (current_dsp_context, atoi (key), value); +} + +void +dsp_ctx_get_param (const char *key, char *value, int len, const char *def) { + strncpy (value, def, len); + current_dsp_context->plugin->get_param (current_dsp_context, atoi (key), value, len); +} + +int +button_cb (int btn, void *ctx) { + if (btn == ddb_button_apply) { + update_streamer (); + } + return 1; +} + +void +on_dsp_configure_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *list = lookup_widget (prefwin, "dsp_listview"); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col); + if (!path || !col) { + // nothing selected + return; + } + int *indices = gtk_tree_path_get_indices (path); + int idx = *indices; + g_free (indices); + if (idx == -1) { + return; + } + ddb_dsp_context_t *p = chain; + int i = idx; + while (p && i--) { + p = p->next; + } + if (!p || !p->plugin->configdialog) { + return; + } + current_dsp_context = p; + ddb_dialog_t conf = { + .title = p->plugin->plugin.name, + .layout = p->plugin->configdialog, + .set_param = dsp_ctx_set_param, + .get_param = dsp_ctx_get_param, + }; + int response = gtkui_run_dialog (prefwin, &conf, 0, button_cb, NULL); + if (response == ddb_button_ok) { + update_streamer (); + } + current_dsp_context = NULL; +} + +static int +swap_items (GtkWidget *list, int idx) { + ddb_dsp_context_t *prev = NULL; + ddb_dsp_context_t *p = chain; + + int n = idx; + while (n > 0 && p) { + prev = p; + p = p->next; + n--; + } + + if (!p || !p->next) { + return -1; + } + + ddb_dsp_context_t *moved = p->next; + + if (!moved) { + return -1; + } + + ddb_dsp_context_t *last = moved ? moved->next : NULL; + + if (prev) { + p->next = last; + prev->next = moved; + moved->next = p; + } + else { + p->next = last; + chain = moved; + moved->next = p; + } + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list))); + gtk_list_store_clear (mdl); + fill_dsp_chain (mdl); + return 0; +} + + +void +on_dsp_up_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *list = lookup_widget (prefwin, "dsp_listview"); + int idx = listview_get_index (list); + if (idx <= 0) { + return; + } + + if (-1 == swap_items (list, idx-1)) { + return; + } + GtkTreePath *path = gtk_tree_path_new_from_indices (idx-1, -1); + GtkTreeViewColumn *col; + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + update_streamer (); +} + + +void +on_dsp_down_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *list = lookup_widget (prefwin, "dsp_listview"); + int idx = listview_get_index (list); + if (idx == -1) { + return; + } + + if (-1 == swap_items (list, idx)) { + return; + } + GtkTreePath *path = gtk_tree_path_new_from_indices (idx+1, -1); + GtkTreeViewColumn *col; + gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, col, FALSE); + gtk_tree_path_free (path); + update_streamer (); +} + +void +on_dsp_preset_changed (GtkComboBox *combobox, + gpointer user_data) +{ + GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox)); + if (entry) { + deadbeef->conf_set_str ("gtkui.conf_dsp_preset", gtk_entry_get_text (GTK_ENTRY (entry))); + } +} + + +void +on_dsp_preset_save_clicked (GtkButton *button, + gpointer user_data) +{ + const char *confdir = deadbeef->get_config_dir (); + char path[1024]; + if (snprintf (path, sizeof (path), "%s/presets", confdir) < 0) { + return; + } + mkdir (path, 0755); + if (snprintf (path, sizeof (path), "%s/presets/dsp", confdir) < 0) { + return; + } + GtkWidget *combobox = lookup_widget (prefwin, "dsp_preset"); + GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox)); + if (!entry) { + return; + } + + const char *text = gtk_entry_get_text (GTK_ENTRY (entry)); + mkdir (path, 0755); + if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", confdir, text) < 0) { + return; + } + deadbeef->dsp_preset_save (path, chain); +} + + +void +on_dsp_preset_load_clicked (GtkButton *button, + gpointer user_data) +{ + GtkWidget *combobox = lookup_widget (prefwin, "dsp_preset"); + GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox)); + if (entry) { + const char *text = gtk_entry_get_text (GTK_ENTRY (entry)); + char path[PATH_MAX]; + if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", deadbeef->get_config_dir (), text) > 0) { + ddb_dsp_context_t *new_chain = NULL; + int res = deadbeef->dsp_preset_load (path, &new_chain); + if (!res) { + deadbeef->dsp_preset_free (chain); + chain = new_chain; + GtkWidget *list = lookup_widget (prefwin, "dsp_listview"); + GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list))); + gtk_list_store_clear (mdl); + fill_dsp_chain (mdl); + update_streamer (); + } + } + } +} + diff --git a/plugins/supereq/supereq.h b/plugins/gtkui/dspconfig.h index 32298ef1..6b7f5310 100644 --- a/plugins/supereq/supereq.h +++ b/plugins/gtkui/dspconfig.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,16 +16,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef __DSPCONFIG_H +#define __DSPCONFIG_H -#ifndef __SUPEREQ_H -#define __SUPEREQ_H +void +dsp_setup_init (GtkWidget *prefwin); -typedef struct DB_supereq_dsp_s { - DB_dsp_t dsp; - float (*get_band) (int band); - void (*set_band) (int band, float value); - float (*get_preamp) (void); - void (*set_preamp) (float value); -} DB_supereq_dsp_t; +void +dsp_setup_free (void); #endif + diff --git a/plugins/gtkui/eq.c b/plugins/gtkui/eq.c index 93f92af5..bfb77703 100644 --- a/plugins/gtkui/eq.c +++ b/plugins/gtkui/eq.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -22,7 +22,6 @@ #include <stdlib.h> #include "gtkui.h" #include "support.h" -#include "../supereq/supereq.h" #include "ddbequalizer.h" static GtkWidget *eqcont; @@ -40,46 +39,66 @@ amp_to_db (float amp) { return 20*log10 (amp); } -DB_supereq_dsp_t * -get_supereq_plugin (void) { - DB_dsp_t **plugs = deadbeef->plug_get_dsp_list (); - for (int i = 0; plugs[i]; i++) { - if (plugs[i]->plugin.id && !strcmp (plugs[i]->plugin.id, "supereq")) { - return (DB_supereq_dsp_t *)plugs[i]; +ddb_dsp_context_t * +get_supereq (void) { + ddb_dsp_context_t *dsp = deadbeef->streamer_get_dsp_chain (); + while (dsp) { + if (!strcmp (dsp->plugin->plugin.id, "supereq")) { + return dsp; } + dsp = dsp->next; } + return NULL; } +static void +set_param (ddb_dsp_context_t *eq, int i, float v) { + char fv[100]; + snprintf (fv, sizeof (fv), "%f", v); + eq->plugin->set_param (eq, i, fv); +} + void eq_value_changed (DdbEqualizer *widget) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - for (int i = 0; i < 18; i++) { - eq->set_band (i, db_to_amp (ddb_equalizer_get_band (widget, i))); + ddb_dsp_context_t *eq = get_supereq (); + if (eq) { + for (int i = 0; i < 18; i++) { + set_param (eq, i+1, ddb_equalizer_get_band (widget, i)); + } + set_param (eq, 0, ddb_equalizer_get_preamp (widget)); } - eq->set_preamp (db_to_amp (ddb_equalizer_get_preamp (widget))); } void on_enable_toggled (GtkToggleButton *togglebutton, gpointer user_data) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - eq->dsp.enable (gtk_toggle_button_get_active (togglebutton)); + ddb_dsp_context_t *eq = get_supereq (); + if (eq) { + int enabled = gtk_toggle_button_get_active (togglebutton) ? 1 : 0; + eq->enabled = enabled; + deadbeef->streamer_dsp_refresh (); + } } void on_zero_all_clicked (GtkButton *button, gpointer user_data) { if (eqwin) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - eq->set_preamp (1); - ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0); - for (int i = 0; i < 18; i++) { - ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0); - eq->set_band (i, 1); + ddb_dsp_context_t *eq = get_supereq (); + if (eq) { + ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0); + set_param (eq, 0, 0); + for (int i = 0; i < 18; i++) { + // set gui + ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0); + + // set dsp + set_param (eq, i+1, 0); + } + gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); } - gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); } } @@ -87,10 +106,12 @@ void on_zero_preamp_clicked (GtkButton *button, gpointer user_data) { if (eqwin) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - eq->set_preamp (1); - ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0); - gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); + ddb_dsp_context_t *eq = get_supereq (); + if (eq) { + set_param (eq, 0, 0); + ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0); + gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); + } } } @@ -98,17 +119,19 @@ void on_zero_bands_clicked (GtkButton *button, gpointer user_data) { if (eqwin) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - for (int i = 0; i < 18; i++) { - ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0); - eq->set_band (i, 1); + ddb_dsp_context_t *eq = get_supereq (); + if (eq) { + for (int i = 0; i < 18; i++) { + ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0); + set_param (eq, i+1, 0); + } + gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); } - gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); } } void -on_save_preset_clicked (GtkButton *button, +on_save_preset_clicked (GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Save DeaDBeeF EQ Preset"), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL); @@ -130,11 +153,19 @@ on_save_preset_clicked (GtkButton *button, if (fname) { FILE *fp = fopen (fname, "w+b"); if (fp) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - for (int i = 0; i < 18; i++) { - fprintf (fp, "%f\n", amp_to_db (eq->get_band (i))); + ddb_dsp_context_t *eq = get_supereq (); + if (eq) { + char fv[100]; + float v; + for (int i = 0; i < 18; i++) { + eq->plugin->get_param (eq, i+1, fv, sizeof (fv)); + v = atof (fv); + fprintf (fp, "%f\n", v); + } + eq->plugin->get_param (eq, 0, fv, sizeof (fv)); + v = atof (fv); + fprintf (fp, "%f\n", v); } - fprintf (fp, "%f\n", amp_to_db (eq->get_preamp ())); fclose (fp); } g_free (fname); @@ -146,7 +177,7 @@ on_save_preset_clicked (GtkButton *button, } void -on_load_preset_clicked (GtkButton *button, +on_load_preset_clicked (GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Load DeaDBeeF EQ Preset..."), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); @@ -158,7 +189,9 @@ on_load_preset_clicked (GtkButton *button, gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); int response = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); @@ -186,13 +219,13 @@ on_load_preset_clicked (GtkButton *button, fclose (fp); if (i == 19) { // apply and save config - DB_supereq_dsp_t *eq = get_supereq_plugin (); + ddb_dsp_context_t *eq = get_supereq (); if (eq) { - eq->set_preamp (db_to_amp (vals[18])); + set_param (eq, 0, vals[18]); ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), vals[18]); for (int i = 0; i < 18; i++) { ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, vals[i]); - eq->set_band (i, db_to_amp (vals[i])); + set_param (eq, i+1, vals[i]); } gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); deadbeef->conf_save (); @@ -221,7 +254,10 @@ on_import_fb2k_preset_clicked (GtkButton *button, gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); + int response = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); @@ -249,13 +285,13 @@ on_import_fb2k_preset_clicked (GtkButton *button, fclose (fp); if (i == 18) { // apply and save config - DB_supereq_dsp_t *eq = get_supereq_plugin (); + ddb_dsp_context_t *eq = get_supereq (); if (eq) { - eq->set_preamp (1); + set_param (eq, 0, 0); ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0); for (int i = 0; i < 18; i++) { - ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, vals[i]); - eq->set_band (i, db_to_amp (vals[i])); + ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, amp_to_db (vals[i])); + set_param (eq, i+1, amp_to_db (vals[i])); } gdk_window_invalidate_rect (eqwin->window, NULL, FALSE); deadbeef->conf_save (); @@ -272,11 +308,40 @@ on_import_fb2k_preset_clicked (GtkButton *button, } void +on_presets_clicked (GtkButton *button, + gpointer user_data) { + GtkWidget *menu = gtk_menu_new (); + GtkWidget *menuitem; + + menuitem = gtk_menu_item_new_with_mnemonic (_("Save Preset")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + + g_signal_connect ((gpointer) menuitem, "activate", + G_CALLBACK (on_save_preset_clicked), + NULL); + + menuitem = gtk_menu_item_new_with_mnemonic (_("Load Preset")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + + g_signal_connect ((gpointer) menuitem, "activate", + G_CALLBACK (on_load_preset_clicked), + NULL); + + menuitem = gtk_menu_item_new_with_mnemonic (_("Import Foobar2000 Preset")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + + g_signal_connect ((gpointer) menuitem, "activate", + G_CALLBACK (on_import_fb2k_preset_clicked), + NULL); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); +} + +void eq_window_show (void) { - DB_supereq_dsp_t *eq = get_supereq_plugin (); - if (!eq) { - return; - } if (!eqcont) { eqcont = gtk_vbox_new (FALSE, 8); GtkWidget *parent= lookup_widget (mainwin, "plugins_bottom_vbox"); @@ -292,7 +357,8 @@ eq_window_show (void) { eqenablebtn = button = gtk_check_button_new_with_label (_("Enable")); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eqenablebtn), deadbeef->conf_get_int ("supereq.enable", 0)); + ddb_dsp_context_t *eq = get_supereq (); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eqenablebtn), eq ? eq->enabled : 0); g_signal_connect ((gpointer) button, "toggled", G_CALLBACK (on_enable_toggled), NULL); @@ -318,37 +384,29 @@ eq_window_show (void) { G_CALLBACK (on_zero_bands_clicked), NULL); - button = gtk_button_new_with_label (_("Save Preset")); + button = gtk_button_new_with_label (_("Presets")); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0); g_signal_connect ((gpointer) button, "clicked", - G_CALLBACK (on_save_preset_clicked), - NULL); - - button = gtk_button_new_with_label (_("Load Preset")); - gtk_widget_show (button); - gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0); - g_signal_connect ((gpointer) button, "clicked", - G_CALLBACK (on_load_preset_clicked), - NULL); - - button = gtk_button_new_with_label (_("Import Foobar2000 Preset")); - gtk_widget_show (button); - gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0); - g_signal_connect ((gpointer) button, "clicked", - G_CALLBACK (on_import_fb2k_preset_clicked), + G_CALLBACK (on_presets_clicked), NULL); eqwin = GTK_WIDGET (ddb_equalizer_new()); g_signal_connect (eqwin, "on_changed", G_CALLBACK (eq_value_changed), 0); gtk_widget_set_size_request (eqwin, -1, 200); - - ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), amp_to_db (eq->get_preamp ())); - for (int i = 0; i < 18; i++) { - if (eq) { - float val = eq->get_band (i); - ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, amp_to_db (val)); + if (eq) { + char fv[100]; + float v; + eq->plugin->get_param (eq, 0, fv, sizeof (fv)); + v = atof (fv); + ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), v); + for (int i = 0; i < 18; i++) { + if (eq) { + eq->plugin->get_param (eq, i+1, fv, sizeof (fv)); + v = atof (fv); + ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, v); + } } } diff --git a/plugins/gtkui/eq.h b/plugins/gtkui/eq.h index 9e54916e..570cd935 100644 --- a/plugins/gtkui/eq.h +++ b/plugins/gtkui/eq.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -29,8 +29,8 @@ eq_window_hide (void); void eq_window_destroy (void); -struct DB_supereq_dsp_s * -get_supereq_plugin (void); +ddb_dsp_context_t * +get_supereq (void); void eq_redraw (void); diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c index 3f58bb09..d33764c0 100644 --- a/plugins/gtkui/fileman.c +++ b/plugins/gtkui/fileman.c @@ -2,6 +2,7 @@ #include <gtk/gtk.h> #include <stdlib.h> #include <ctype.h> +#include <string.h> #include "gtkui.h" #include "ddblistview.h" #include "progress.h" @@ -9,10 +10,8 @@ void gtkpl_add_dir (DdbListview *ps, char *folder) { - g_idle_add (gtkui_progress_show_idle, NULL); gtkui_original_pl_add_dir (folder, gtkui_add_file_info_cb, NULL); g_free (folder); - g_idle_add (gtkui_progress_hide_idle, NULL); } static void @@ -23,10 +22,30 @@ gtkpl_adddir_cb (gpointer data, gpointer userdata) { void gtkpl_add_dirs (GSList *lst) { - g_idle_add (gtkui_progress_show_idle, NULL); + deadbeef->plt_lock (); + deadbeef->pl_add_files_begin (deadbeef->plt_get_curr ()); + if (g_slist_length (lst) == 1 + && deadbeef->conf_get_int ("gtkui.name_playlist_from_folder", 0)) { + int plt = deadbeef->plt_get_curr (); + if (plt != -1) { + void *p = deadbeef->plt_get_handle (plt); + char t[1000]; + if (!deadbeef->plt_get_title (p, t, sizeof (t))) { + char *def = _("New Playlist"); + if (!strncmp (t, def, strlen (def))) { + const char *folder = strrchr ((char*)lst->data, '/'); + if (!folder) { + folder = lst->data; + } + deadbeef->plt_set_title (p, folder+1); + } + } + } + } + deadbeef->plt_unlock (); g_slist_foreach(lst, gtkpl_adddir_cb, NULL); g_slist_free (lst); - g_idle_add (gtkui_progress_hide_idle, NULL); + deadbeef->pl_add_files_end (); } static void @@ -37,10 +56,10 @@ gtkpl_addfile_cb (gpointer data, gpointer userdata) { void gtkpl_add_files (GSList *lst) { - g_idle_add (gtkui_progress_show_idle, NULL); + deadbeef->pl_add_files_begin (deadbeef->plt_get_curr ()); g_slist_foreach(lst, gtkpl_addfile_cb, NULL); g_slist_free (lst); - g_idle_add (gtkui_progress_hide_idle, NULL); + deadbeef->pl_add_files_end (); } static void @@ -51,7 +70,8 @@ add_dirs_worker (void *data) { void gtkui_add_dirs (GSList *lst) { - deadbeef->thread_start (add_dirs_worker, lst); + intptr_t tid = deadbeef->thread_start (add_dirs_worker, lst); + deadbeef->thread_detach (tid); } static void @@ -62,7 +82,9 @@ add_files_worker (void *data) { void gtkui_add_files (struct _GSList *lst) { - deadbeef->thread_start (add_files_worker, lst); + deadbeef->pl_add_files_begin (deadbeef->plt_get_curr ()); + intptr_t tid = deadbeef->thread_start (add_files_worker, lst); + deadbeef->thread_detach (tid); } static void @@ -73,14 +95,16 @@ open_files_worker (void *data) { extern GtkWidget *mainwin; DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); ddb_listview_set_cursor (pl, 0); - deadbeef->sendmessage (M_PLAYSONG, 0, 1, 0); + deadbeef->sendmessage (M_PLAY_CURRENT, 0, 1, 0); } void gtkui_open_files (struct _GSList *lst) { deadbeef->pl_clear (); playlist_refresh (); - deadbeef->thread_start (open_files_worker, lst); + + intptr_t tid = deadbeef->thread_start (open_files_worker, lst); + deadbeef->thread_detach (tid); } void @@ -140,7 +164,7 @@ set_dnd_cursor_idle (gpointer data) { void gtkpl_add_fm_dropped_files (DB_playItem_t *drop_before, char *ptr, int length) { DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - g_idle_add (gtkui_progress_show_idle, NULL); + deadbeef->pl_add_files_begin (deadbeef->plt_get_curr ()); DdbListviewIter first = NULL; DdbListviewIter after = NULL; @@ -188,7 +212,7 @@ gtkpl_add_fm_dropped_files (DB_playItem_t *drop_before, char *ptr, int length) { } free (ptr); - g_idle_add (gtkui_progress_hide_idle, NULL); + deadbeef->pl_add_files_end (); g_idle_add (set_dnd_cursor_idle, first); } @@ -222,5 +246,6 @@ gtkui_receive_fm_drop (DB_playItem_t *before, char *mem, int length) { } data->drop_before = before; // since it happens in separate thread, we need to addref - deadbeef->thread_start (fmdrop_worker, data); + intptr_t tid = deadbeef->thread_start (fmdrop_worker, data); + deadbeef->thread_detach (tid); } diff --git a/plugins/gtkui/gdkdrawing.c b/plugins/gtkui/gdkdrawing.c index c7c2339b..cb795ccb 100644 --- a/plugins/gtkui/gdkdrawing.c +++ b/plugins/gtkui/gdkdrawing.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -197,6 +197,7 @@ static GdkColor gtkui_tabstrip_dark_color; static GdkColor gtkui_tabstrip_mid_color; static GdkColor gtkui_tabstrip_light_color; static GdkColor gtkui_tabstrip_base_color; +static GdkColor gtkui_tabstrip_text_color; static GdkColor gtkui_listview_even_row_color; static GdkColor gtkui_listview_odd_row_color; @@ -226,6 +227,7 @@ gtkui_override_tabstrip_colors (void) { void gtkui_init_theme_colors (void) { + deadbeef->conf_lock (); override_listview_colors= deadbeef->conf_get_int ("gtkui.override_listview_colors", 0); override_bar_colors = deadbeef->conf_get_int ("gtkui.override_bar_colors", 0); override_tabstrip_colors = deadbeef->conf_get_int ("gtkui.override_tabstrip_colors", 0); @@ -241,11 +243,11 @@ gtkui_init_theme_colors (void) { } else { snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->base[GTK_STATE_SELECTED].red, style->base[GTK_STATE_SELECTED].green, style->base[GTK_STATE_SELECTED].blue); - clr = deadbeef->conf_get_str ("gtkui.color.bar_foreground", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.bar_foreground", color_text); sscanf (clr, "%hd %hd %hd", >kui_bar_foreground_color.red, >kui_bar_foreground_color.green, >kui_bar_foreground_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->fg[GTK_STATE_NORMAL].red, style->fg[GTK_STATE_NORMAL].green, style->fg[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.bar_background", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.bar_background", color_text); sscanf (clr, "%hd %hd %hd", >kui_bar_background_color.red, >kui_bar_background_color.green, >kui_bar_background_color.blue); } @@ -255,23 +257,28 @@ gtkui_init_theme_colors (void) { memcpy (>kui_tabstrip_mid_color, &style->mid[GTK_STATE_NORMAL], sizeof (GdkColor)); memcpy (>kui_tabstrip_light_color, &style->light[GTK_STATE_NORMAL], sizeof (GdkColor)); memcpy (>kui_tabstrip_base_color, &style->bg[GTK_STATE_NORMAL], sizeof (GdkColor)); + memcpy (>kui_tabstrip_text_color, &style->text[GTK_STATE_NORMAL], sizeof (GdkColor)); } else { snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->dark[GTK_STATE_NORMAL].red, style->dark[GTK_STATE_NORMAL].green, style->dark[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.tabstrip_dark", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.tabstrip_dark", color_text); sscanf (clr, "%hd %hd %hd", >kui_tabstrip_dark_color.red, >kui_tabstrip_dark_color.green, >kui_tabstrip_dark_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->mid[GTK_STATE_NORMAL].red, style->mid[GTK_STATE_NORMAL].green, style->mid[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.tabstrip_mid", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.tabstrip_mid", color_text); sscanf (clr, "%hd %hd %hd", >kui_tabstrip_mid_color.red, >kui_tabstrip_mid_color.green, >kui_tabstrip_mid_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->light[GTK_STATE_NORMAL].red, style->light[GTK_STATE_NORMAL].green, style->light[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.tabstrip_light", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.tabstrip_light", color_text); sscanf (clr, "%hd %hd %hd", >kui_tabstrip_light_color.red, >kui_tabstrip_light_color.green, >kui_tabstrip_light_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->bg[GTK_STATE_NORMAL].red, style->bg[GTK_STATE_NORMAL].green, style->bg[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.tabstrip_base", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.tabstrip_base", color_text); sscanf (clr, "%hd %hd %hd", >kui_tabstrip_base_color.red, >kui_tabstrip_base_color.green, >kui_tabstrip_base_color.blue); + + snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->text[GTK_STATE_NORMAL].red, style->text[GTK_STATE_NORMAL].green, style->text[GTK_STATE_NORMAL].blue); + clr = deadbeef->conf_get_str_fast ("gtkui.color.tabstrip_text", color_text); + sscanf (clr, "%hd %hd %hd", >kui_tabstrip_text_color.red, >kui_tabstrip_text_color.green, >kui_tabstrip_text_color.blue); } if (!override_listview_colors) { @@ -284,29 +291,30 @@ gtkui_init_theme_colors (void) { } else { snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->light[GTK_STATE_NORMAL].red, style->light[GTK_STATE_NORMAL].green, style->light[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.listview_even_row", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.listview_even_row", color_text); sscanf (clr, "%hd %hd %hd", >kui_listview_even_row_color.red, >kui_listview_even_row_color.green, >kui_listview_even_row_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->mid[GTK_STATE_NORMAL].red, style->mid[GTK_STATE_NORMAL].green, style->mid[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.listview_odd_row", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.listview_odd_row", color_text); sscanf (clr, "%hd %hd %hd", >kui_listview_odd_row_color.red, >kui_listview_odd_row_color.green, >kui_listview_odd_row_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->mid[GTK_STATE_NORMAL].red, style->mid[GTK_STATE_NORMAL].green, style->mid[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.listview_selection", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.listview_selection", color_text); sscanf (clr, "%hd %hd %hd", >kui_listview_selection_color.red, >kui_listview_selection_color.green, >kui_listview_selection_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->fg[GTK_STATE_NORMAL].red, style->fg[GTK_STATE_NORMAL].green, style->fg[GTK_STATE_NORMAL].blue); - clr = deadbeef->conf_get_str ("gtkui.color.listview_text", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.listview_text", color_text); sscanf (clr, "%hd %hd %hd", >kui_listview_text_color.red, >kui_listview_text_color.green, >kui_listview_text_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->fg[GTK_STATE_SELECTED].red, style->fg[GTK_STATE_SELECTED].green, style->fg[GTK_STATE_SELECTED].blue); - clr = deadbeef->conf_get_str ("gtkui.color.listview_selected_text", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.listview_selected_text", color_text); sscanf (clr, "%hd %hd %hd", >kui_listview_selected_text_color.red, >kui_listview_selected_text_color.green, >kui_listview_selected_text_color.blue); snprintf (color_text, sizeof (color_text), "%hd %hd %hd", style->fg[GTK_STATE_SELECTED].red, style->fg[GTK_STATE_SELECTED].green, style->fg[GTK_STATE_SELECTED].blue); - clr = deadbeef->conf_get_str ("gtkui.color.listview_cursor", color_text); + clr = deadbeef->conf_get_str_fast ("gtkui.color.listview_cursor", color_text); sscanf (clr, "%hd %hd %hd", >kui_listview_cursor_color.red, >kui_listview_cursor_color.green, >kui_listview_cursor_color.blue); } + deadbeef->conf_unlock (); } void @@ -340,6 +348,11 @@ gtkui_get_tabstrip_base_color (GdkColor *clr) { } void +gtkui_get_tabstrip_text_color (GdkColor *clr) { + memcpy (clr, >kui_tabstrip_text_color, sizeof (GdkColor)); +} + +void gtkui_get_listview_even_row_color (GdkColor *clr) { memcpy (clr, >kui_listview_even_row_color, sizeof (GdkColor)); } @@ -368,3 +381,16 @@ void gtkui_get_listview_cursor_color (GdkColor *clr) { memcpy (clr, >kui_listview_cursor_color, sizeof (GdkColor)); } + +int +draw_get_listview_rowheight (void) { + PangoFontDescription *font_desc = font_style->font_desc; + PangoFontMetrics *metrics = pango_context_get_metrics (pangoctx, + font_desc, + pango_context_get_language (pangoctx)); + int row_height = (pango_font_metrics_get_ascent (metrics) + + pango_font_metrics_get_descent (metrics)); + pango_font_metrics_unref (metrics); + return PANGO_PIXELS(row_height)+6; +} + diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index 35da1527..6b34e1e9 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -44,11 +44,14 @@ #include "ddbtabstrip.h" #include "eq.h" #include "actions.h" +#include "pluginconf.h" +#include "gtkui_api.h" +#include "wingeom.h" -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) -static DB_gui_t plugin; +static ddb_gtkui_t plugin; DB_functions_t *deadbeef; static intptr_t gtk_tid; @@ -116,6 +119,7 @@ update_songinfo (gpointer ctx) { if (!gtk_widget_get_visible (mainwin) || iconified) { return FALSE; } + DB_output_t *output = deadbeef->get_output (); char sbtext_new[512] = "-"; float songpos = last_songpos; @@ -141,7 +145,7 @@ update_songinfo (gpointer ctx) { float duration = track ? deadbeef->pl_get_item_duration (track) : -1; - if (deadbeef->get_output ()->state () == OUTPUT_STATE_STOPPED || !track || !c) { + if (!output || (output->state () == OUTPUT_STATE_STOPPED || !track || !c)) { snprintf (sbtext_new, sizeof (sbtext_new), _("Stopped | %d tracks | %s total playtime"), deadbeef->pl_getcount (PL_MAIN), totaltime_str); songpos = 0; } @@ -154,15 +158,15 @@ update_songinfo (gpointer ctx) { const char *mode; char temp[20]; - if (c->channels <= 2) { - mode = c->channels == 1 ? _("Mono") : _("Stereo"); + if (c->fmt.channels <= 2) { + mode = c->fmt.channels == 1 ? _("Mono") : _("Stereo"); } else { - snprintf (temp, sizeof (temp), "%dch Multichannel", c->channels); + snprintf (temp, sizeof (temp), "%dch Multichannel", c->fmt.channels); mode = temp; } - int samplerate = c->samplerate; - int bitspersample = c->bps; + int samplerate = c->fmt.samplerate; + int bitspersample = c->fmt.bps; songpos = playpos; // codec_unlock (); @@ -187,7 +191,11 @@ update_songinfo (gpointer ctx) { } } const char *spaused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED ? _("Paused | ") : ""; - snprintf (sbtext_new, sizeof (sbtext_new), _("%s%s %s| %dHz | %d bit | %s | %d:%02d / %s | %d tracks | %s total playtime"), spaused, track->filetype ? track->filetype:"-", sbitrate, samplerate, bitspersample, mode, minpos, secpos, t, deadbeef->pl_getcount (PL_MAIN), totaltime_str); + const char *filetype = deadbeef->pl_find_meta (track, ":FILETYPE"); + if (!filetype) { + filetype = "-"; + } + snprintf (sbtext_new, sizeof (sbtext_new), _("%s%s %s| %dHz | %d bit | %s | %d:%02d / %s | %d tracks | %s total playtime"), spaused, filetype, sbitrate, samplerate, bitspersample, mode, minpos, secpos, t, deadbeef->pl_getcount (PL_MAIN), totaltime_str); } if (strcmp (sbtext_new, sb_text)) { @@ -223,7 +231,7 @@ update_songinfo (gpointer ctx) { void set_tray_tooltip (const char *text) { if (trayicon) { -#if (GTK_MINOR_VERSION < 16) +#if !GTK_CHECK_VERSION(2,16,0) || defined(ULTRA_COMPATIBLE) gtk_status_icon_set_tooltip (trayicon, text); #else gtk_status_icon_set_tooltip_text (trayicon, text); @@ -274,15 +282,7 @@ mainwin_toggle_visible (void) { gtk_widget_hide (mainwin); } else { - int x = deadbeef->conf_get_int ("mainwin.geometry.x", 40); - int y = deadbeef->conf_get_int ("mainwin.geometry.y", 40); - int w = deadbeef->conf_get_int ("mainwin.geometry.w", 500); - int h = deadbeef->conf_get_int ("mainwin.geometry.h", 300); - gtk_window_move (GTK_WINDOW (mainwin), x, y); - gtk_window_resize (GTK_WINDOW (mainwin), w, h); - if (deadbeef->conf_get_int ("mainwin.geometry.maximized", 0)) { - gtk_window_maximize (GTK_WINDOW (mainwin)); - } + wingeom_restore (mainwin, "mainwin", 40, 40, 500, 300, 0); if (iconified) { gtk_window_deiconify (GTK_WINDOW(mainwin)); } @@ -292,7 +292,7 @@ mainwin_toggle_visible (void) { } } -#if GTK_MINOR_VERSION<=14 +#if !GTK_CHECK_VERSION(2,14,0) || defined(ULTRA_COMPATIBLE) gboolean on_trayicon_activate (GtkWidget *widget, @@ -314,7 +314,7 @@ on_trayicon_button_press_event (GtkWidget *widget, mainwin_toggle_visible (); } else if (event->button == 2) { - deadbeef->sendmessage (M_PAUSESONG, 0, 0, 0); + deadbeef->sendmessage (M_TOGGLE_PAUSE, 0, 0, 0); } return FALSE; } @@ -398,13 +398,13 @@ gtkui_set_titlebar (DB_playItem_t *it) { else { deadbeef->pl_item_ref (it); } + char fmt[500]; char str[600]; - const char *fmt; if (it) { - fmt = deadbeef->conf_get_str ("gtkui.titlebar_playing", "%a - %t - DeaDBeeF-%V"); + deadbeef->conf_get_str ("gtkui.titlebar_playing", "%a - %t - DeaDBeeF-%V", fmt, sizeof (fmt)); } else { - fmt = deadbeef->conf_get_str ("gtkui.titlebar_stopped", "DeaDBeeF-%V"); + deadbeef->conf_get_str ("gtkui.titlebar_stopped", "DeaDBeeF-%V", fmt, sizeof (fmt)); } deadbeef->pl_format_title (it, -1, str, sizeof (str), -1, fmt); gtk_window_set_title (GTK_WINDOW (mainwin), str); @@ -478,7 +478,7 @@ gtkui_on_paused (DB_event_state_t *ev, uintptr_t data) { void playlist_refresh (void) { DdbListview *ps = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL | DDB_EXPOSE_LIST); + ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); } @@ -532,11 +532,11 @@ gtkui_on_playlistswitch (DB_event_t *ev, uintptr_t data) { return 0; } -static int -gtkui_on_frameupdate (DB_event_t *ev, uintptr_t data) { - g_idle_add (update_songinfo, NULL); +static gboolean +gtkui_on_frameupdate (gpointer data) { + update_songinfo (NULL); - return 0; + return TRUE; } static gboolean @@ -571,7 +571,9 @@ gtkui_update_status_icon (gpointer unused) { // system tray icon traymenu = create_traymenu (); - const char *icon_name = deadbeef->conf_get_str ("gtkui.custom_tray_icon", TRAY_ICON); + char tmp[1000]; + const char *icon_name = tmp; + deadbeef->conf_get_str ("gtkui.custom_tray_icon", TRAY_ICON, tmp, sizeof (tmp)); GtkIconTheme *theme = gtk_icon_theme_get_default(); if (!gtk_icon_theme_has_icon(theme, icon_name)) @@ -583,12 +585,19 @@ gtkui_update_status_icon (gpointer unused) { icon_name = icon_is_builtin ? "deadbeef" : icon_name; } - trayicon = gtk_status_icon_new_from_icon_name(icon_name); + if (!gtk_icon_theme_has_icon(theme, icon_name)) { + char iconpath[1024]; + snprintf (iconpath, sizeof (iconpath), "%s/deadbeef.png", deadbeef->get_prefix ()); + trayicon = gtk_status_icon_new_from_file(iconpath); + } + else { + trayicon = gtk_status_icon_new_from_icon_name(icon_name); + } if (hide_tray_icon) { g_object_set (trayicon, "visible", FALSE, NULL); } -#if GTK_MINOR_VERSION <= 14 +#if !GTK_CHECK_VERSION(2,14,0) || defined(ULTRA_COMPATIBLE) g_signal_connect ((gpointer)trayicon, "activate", G_CALLBACK (on_trayicon_activate), NULL); #else g_signal_connect ((gpointer)trayicon, "scroll_event", G_CALLBACK (on_trayicon_scroll_event), NULL); @@ -614,7 +623,7 @@ gtkui_on_configchanged (DB_event_t *ev, uintptr_t data) { const char *w; // order - const char *orderwidgets[3] = { "order_linear", "order_shuffle", "order_random" }; + const char *orderwidgets[4] = { "order_linear", "order_shuffle", "order_random", "order_shuffle_albums" }; w = orderwidgets[deadbeef->conf_get_int ("playback.order", PLAYBACK_ORDER_LINEAR)]; gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, w)), TRUE); @@ -663,13 +672,31 @@ save_playlist_as (void) { gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dlg), TRUE); gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dlg), "untitled.dbpl"); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.playlist.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.playlist.lastdir", "")); + deadbeef->conf_unlock (); GtkFileFilter* flt; flt = gtk_file_filter_new (); gtk_file_filter_set_name (flt, _("DeaDBeeF playlist files (*.dbpl)")); gtk_file_filter_add_pattern (flt, "*.dbpl"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt); + DB_playlist_t **plug = deadbeef->plug_get_playlist_list (); + for (int i = 0; plug[i]; i++) { + if (plug[i]->extensions && plug[i]->load) { + const char **exts = plug[i]->extensions; + if (exts && plug[i]->save) { + for (int e = 0; exts[e]; e++) { + char s[100]; + flt = gtk_file_filter_new (); + gtk_file_filter_set_name (flt, exts[e]); + snprintf (s, sizeof (s), "*.%s", exts[e]); + gtk_file_filter_add_pattern (flt, s); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt); + } + } + } + } int res = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder @@ -716,6 +743,29 @@ on_playlist_save_as_activate (GtkMenuItem *menuitem, save_playlist_as (); } +static gboolean +playlist_filter_func (const GtkFileFilterInfo *filter_info, gpointer data) { + // get ext + const char *p = strrchr (filter_info->filename, '.'); + if (!p) { + return FALSE; + } + p++; + DB_playlist_t **plug = deadbeef->plug_get_playlist_list (); + for (int i = 0; plug[i]; i++) { + if (plug[i]->extensions && plug[i]->load) { + const char **exts = plug[i]->extensions; + if (exts) { + for (int e = 0; exts[e]; e++) { + if (!strcasecmp (exts[e], p)) { + return TRUE; + } + } + } + } + } + return FALSE; +} void on_playlist_load_activate (GtkMenuItem *menuitem, @@ -724,13 +774,21 @@ on_playlist_load_activate (GtkMenuItem *menuitem, GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Load Playlist"), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.playlist.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.playlist.lastdir", "")); + deadbeef->conf_unlock (); GtkFileFilter* flt; flt = gtk_file_filter_new (); - gtk_file_filter_set_name (flt, _("DeaDBeeF playlist files (*.dbpl)")); + gtk_file_filter_set_name (flt, "Supported playlist formats"); + gtk_file_filter_add_custom (flt, GTK_FILE_FILTER_FILENAME, playlist_filter_func, NULL, NULL); gtk_file_filter_add_pattern (flt, "*.dbpl"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt); + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dlg), flt); + flt = gtk_file_filter_new (); + gtk_file_filter_set_name (flt, _("Other files (*)")); + gtk_file_filter_add_pattern (flt, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt); int res = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder @@ -767,7 +825,9 @@ on_add_location_activate (GtkMenuItem *menuitem, if (entry) { const char *text = gtk_entry_get_text (entry); if (text) { + deadbeef->pl_add_files_begin (deadbeef->plt_get_curr ()); deadbeef->pl_add_file (text, NULL, NULL); + deadbeef->pl_add_files_end (); playlist_refresh (); } } @@ -867,13 +927,16 @@ gtkui_add_new_playlist (void) { else { snprintf (name, sizeof (name), _("New Playlist (%d)"), idx); } + deadbeef->plt_lock (); for (i = 0; i < cnt; i++) { char t[100]; - deadbeef->plt_get_title (i, t, sizeof (t)); + void *plt = deadbeef->plt_get_handle (i); + deadbeef->plt_get_title (plt, t, sizeof (t)); if (!strcasecmp (t, name)) { break; } } + deadbeef->plt_unlock (); if (i == cnt) { return deadbeef->plt_add (cnt, name); } @@ -895,12 +958,34 @@ tabstrip_redraw (void) { } static int gtk_initialized = 0; +static gint refresh_timeout = 0; + +void +gtkui_setup_gui_refresh (void) { + int fps = deadbeef->conf_get_int ("gtkui.refresh_rate", 10); + if (fps < 1) { + fps = 1; + } + else if (fps > 30) { + fps = 30; + } + + int tm = 1000/fps; + + if (refresh_timeout) { + g_source_remove (refresh_timeout); + refresh_timeout = 0; + } + + refresh_timeout = g_timeout_add (tm, gtkui_on_frameupdate, NULL); +} void gtkui_thread (void *ctx) { // let's start some gtk g_thread_init (NULL); - add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); +// add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); + add_pixmap_directory (deadbeef->get_pixmap_dir ()); gdk_threads_init (); gdk_threads_enter (); @@ -918,19 +1003,15 @@ gtkui_thread (void *ctx) { mainwin = create_mainwin (); gtkpl_init (); +#if PORTABLE + char iconpath[1024]; + snprintf (iconpath, sizeof (iconpath), "%s/deadbeef.png", deadbeef->get_prefix ()); + gtk_window_set_icon_from_file (GTK_WINDOW (mainwin), iconpath, NULL); +#else gtk_window_set_icon_name (GTK_WINDOW (mainwin), "deadbeef"); +#endif - { - int x = deadbeef->conf_get_int ("mainwin.geometry.x", 40); - int y = deadbeef->conf_get_int ("mainwin.geometry.y", 40); - int w = deadbeef->conf_get_int ("mainwin.geometry.w", 500); - int h = deadbeef->conf_get_int ("mainwin.geometry.h", 300); - gtk_window_move (GTK_WINDOW (mainwin), x, y); - gtk_window_resize (GTK_WINDOW (mainwin), w, h); - if (deadbeef->conf_get_int ("mainwin.geometry.maximized", 0)) { - gtk_window_maximize (GTK_WINDOW (mainwin)); - } - } + wingeom_restore (mainwin, "mainwin", 40, 40, 500, 300, 0); gtkui_on_configchanged (NULL, 0); gtkui_init_theme_colors (); @@ -984,14 +1065,17 @@ gtkui_thread (void *ctx) { deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (gtkui_on_paused), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTCHANGED, DB_CALLBACK (gtkui_on_playlistchanged), 0); - deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (gtkui_on_frameupdate), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (gtkui_on_volumechanged), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (gtkui_on_configchanged), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_OUTPUTCHANGED, DB_CALLBACK (gtkui_on_outputchanged), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTSWITCH, DB_CALLBACK (gtkui_on_playlistswitch), 0); + gtkui_setup_gui_refresh (); + + char fmt[500]; char str[600]; - deadbeef->pl_format_title (NULL, -1, str, sizeof (str), -1, deadbeef->conf_get_str ("gtkui.titlebar_stopped", "DeaDBeeF-%V")); + deadbeef->conf_get_str ("gtkui.titlebar_stopped", "DeaDBeeF-%V", fmt, sizeof (fmt)); + deadbeef->pl_format_title (NULL, -1, str, sizeof (str), -1, fmt); gtk_window_set_title (GTK_WINDOW (mainwin), str); gtk_initialized = 1; @@ -1024,7 +1108,7 @@ gtkui_set_progress_text_idle (gpointer data) { gboolean gtkui_progress_hide_idle (gpointer data) { progress_hide (); - deadbeef->sendmessage (M_PLAYLISTREFRESH, 0, 0, 0); + deadbeef->sendmessage (M_PLAYLIST_REFRESH, 0, 0, 0); //playlist_refresh (); return FALSE; } @@ -1034,13 +1118,13 @@ gtkui_add_file_info_cb (DB_playItem_t *it, void *data) { if (progress_is_aborted ()) { return -1; } - g_idle_add (gtkui_set_progress_text_idle, it->fname); + g_idle_add (gtkui_set_progress_text_idle, (gpointer)deadbeef->pl_find_meta (it, ":URI")); return 0; } int (*gtkui_original_pl_add_dir) (const char *dirname, int (*cb)(DB_playItem_t *it, void *data), void *user_data); int (*gtkui_original_pl_add_file) (const char *fname, int (*cb)(DB_playItem_t *it, void *data), void *user_data); -void (*gtkui_original_pl_add_files_begin) (void); +void (*gtkui_original_pl_add_files_begin) (int plt); void (*gtkui_original_pl_add_files_end) (void); int @@ -1056,9 +1140,9 @@ gtkui_pl_add_file (const char *filename, int (*cb)(DB_playItem_t *it, void *data } void -gtkui_pl_add_files_begin (void) { +gtkui_pl_add_files_begin (int plt) { g_idle_add (gtkui_progress_show_idle, NULL); - gtkui_original_pl_add_files_begin (); + gtkui_original_pl_add_files_begin (plt); } void @@ -1093,6 +1177,8 @@ gtkui_playlist_set_curr (int playlist) { static int gtkui_start (void) { + fprintf (stderr, "gtkui plugin compiled for gtk version: %d.%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); + // gtk must be running in separate thread gtk_initialized = 0; gtk_tid = deadbeef->thread_start (gtkui_thread, NULL); @@ -1117,11 +1203,13 @@ gtkui_start (void) { return 0; } +static DB_plugin_t *supereq_plugin; + gboolean gtkui_connect_cb (void *none) { // equalizer GtkWidget *eq_mi = lookup_widget (mainwin, "view_eq"); - if (!get_supereq_plugin ()) { + if (!supereq_plugin) { gtk_widget_hide (GTK_WIDGET (eq_mi)); } else { @@ -1138,17 +1226,19 @@ gtkui_connect_cb (void *none) { DB_plugin_t **plugins = deadbeef->plug_get_list (); for (int i = 0; plugins[i]; i++) { DB_plugin_t *p = plugins[i]; - if (p->id && !strcmp (p->id, "cover_loader")) { + if (p->id && !strcmp (p->id, "artwork")) { trace ("gtkui: found cover-art loader plugin\n"); coverart_plugin = (DB_artwork_plugin_t *)p; break; } } + gtkui_playlist_changed (); return FALSE; } static int gtkui_connect (void) { + supereq_plugin = deadbeef->plug_get_for_id ("supereq"); // need to do it in gtk thread g_idle_add (gtkui_connect_cb, NULL); @@ -1174,7 +1264,6 @@ gtkui_stop (void) { deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0); deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (gtkui_on_paused), 0); deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTCHANGED, DB_CALLBACK (gtkui_on_playlistchanged), 0); - deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (gtkui_on_frameupdate), 0); deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (gtkui_on_volumechanged), 0); deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (gtkui_on_configchanged), 0); deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_OUTPUTCHANGED, DB_CALLBACK (gtkui_on_outputchanged), 0); @@ -1190,8 +1279,13 @@ gtkui_stop (void) { return 0; } +GtkWidget * +gtkui_get_mainwin (void) { + return mainwin; +} + DB_plugin_t * -gtkui_load (DB_functions_t *api) { +ddb_gui_GTK2_load (DB_functions_t *api) { deadbeef = api; return DB_PLUGIN (&plugin); } @@ -1205,19 +1299,37 @@ static const char settings_dlg[] = ; // define plugin interface -static DB_gui_t plugin = { - DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, - .plugin.nostop = 1, - .plugin.type = DB_PLUGIN_MISC, - .plugin.name = "Standard GTK2 user interface", - .plugin.descr = "Default DeaDBeeF GUI", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", - .plugin.website = "http://deadbeef.sf.net", - .plugin.start = gtkui_start, - .plugin.stop = gtkui_stop, - .plugin.connect = gtkui_connect, - .plugin.configdialog = settings_dlg, +static ddb_gtkui_t plugin = { + .gui.plugin.api_vmajor = DB_API_VERSION_MAJOR, + .gui.plugin.api_vminor = DB_API_VERSION_MINOR, + .gui.plugin.version_major = 1, + .gui.plugin.version_minor = 0, + .gui.plugin.type = DB_PLUGIN_MISC, + .gui.plugin.id = "gtkui", + .gui.plugin.name = "Standard GTK2 user interface", + .gui.plugin.descr = "Default DeaDBeeF GUI", + .gui.plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .gui.plugin.website = "http://deadbeef.sf.net", + .gui.plugin.start = gtkui_start, + .gui.plugin.stop = gtkui_stop, + .gui.plugin.connect = gtkui_connect, + .gui.plugin.configdialog = settings_dlg, + .gui.run_dialog = gtkui_run_dialog_root, + .get_mainwin = gtkui_get_mainwin, }; diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h index 53db09af..4074ddb2 100644 --- a/plugins/gtkui/gtkui.h +++ b/plugins/gtkui/gtkui.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -25,12 +25,20 @@ #include <gtk/gtk.h> +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#if defined(ULTRA_COMPATIBLE) +#warning compiling for compatibility with gtk <2.14 +#endif + // workaround to make older gtk compatible with vala codegen -#if !GTK_CHECK_VERSION(2,14,0) +#if !GTK_CHECK_VERSION(2,14,0) || defined(ULTRA_COMPATIBLE) #define gtk_widget_get_window(widget) ((widget)->window) #endif -#if !GTK_CHECK_VERSION(2,18,0) +#if !GTK_CHECK_VERSION(2,18,0) || defined(ULTRA_COMPATIBLE) #define gtk_widget_set_has_window(widget, has_window) \ if (has_window) GTK_WIDGET_UNSET_FLAGS (widget, GTK_NO_WINDOW); \ else GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW); @@ -39,7 +47,7 @@ #define gtk_widget_get_has_window(widget) (!GTK_WIDGET_NO_WINDOW(widget)) #endif -#if !GTK_CHECK_VERSION(2,20,0) +#if !GTK_CHECK_VERSION(2,20,0) || defined(ULTRA_COMPATIBLE) #define gtk_widget_get_realized(widget) (GTK_WIDGET_REALIZED(widget)) #endif @@ -66,11 +74,6 @@ gtkui_open_files (struct _GSList *lst); void gtkui_receive_fm_drop (DB_playItem_t *before, char *mem, int length); -// plugin configuration dialogs - -void -plugin_configure (GtkWidget *parentwin, DB_plugin_t *p); - void preferences_fill_soundcards (void); @@ -160,4 +163,7 @@ gtkui_focus_on_playing_track (void); void gtkui_playlist_set_curr (int playlist); +void +gtkui_setup_gui_refresh (); + #endif diff --git a/plugins/gtkui/gtkui_api.h b/plugins/gtkui/gtkui_api.h new file mode 100644 index 00000000..596aeb17 --- /dev/null +++ b/plugins/gtkui/gtkui_api.h @@ -0,0 +1,28 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __GTKUI_API_H +#define __GTKUI_API_H + +typedef struct { + DB_gui_t gui; + GtkWidget * (*get_mainwin) (void); +} ddb_gtkui_t; + +#endif diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index 1b255c52..5d3dde1a 100644 --- a/plugins/gtkui/interface.c +++ b/plugins/gtkui/interface.c @@ -35,12 +35,12 @@ create_mainwin (void) GtkWidget *File; GtkWidget *File_menu; GtkWidget *open; - GtkWidget *image452; + GtkWidget *image512; GtkWidget *separator2; GtkWidget *add_files; - GtkWidget *image453; + GtkWidget *image513; GtkWidget *add_folders; - GtkWidget *image454; + GtkWidget *image514; GtkWidget *add_location1; GtkWidget *separatormenuitem1; GtkWidget *new_playlist1; @@ -49,20 +49,26 @@ create_mainwin (void) GtkWidget *playlist_save_as; GtkWidget *separator8; GtkWidget *quit; - GtkWidget *image455; + GtkWidget *image515; GtkWidget *Edit; GtkWidget *Edit_menu; GtkWidget *clear1; - GtkWidget *image456; + GtkWidget *image516; GtkWidget *select_all1; GtkWidget *deselect_all1; GtkWidget *invert_selection1; GtkWidget *Selection; GtkWidget *Selection_menu; GtkWidget *remove1; - GtkWidget *image457; + GtkWidget *image517; GtkWidget *crop1; GtkWidget *find1; + GtkWidget *sort_by1; + GtkWidget *sort_by1_menu; + GtkWidget *album1; + GtkWidget *artist1; + GtkWidget *date1; + GtkWidget *custom2; GtkWidget *separator5; GtkWidget *preferences; GtkWidget *View; @@ -78,6 +84,7 @@ create_mainwin (void) GSList *order_linear_group = NULL; GtkWidget *order_linear; GtkWidget *order_shuffle; + GtkWidget *order_shuffle_albums; GtkWidget *order_random; GtkWidget *Looping; GtkWidget *Looping_menu; @@ -93,16 +100,16 @@ create_mainwin (void) GtkWidget *Help; GtkWidget *Help_menu; GtkWidget *help1; - GtkWidget *image458; + GtkWidget *image518; GtkWidget *changelog1; GtkWidget *separator10; GtkWidget *gpl1; GtkWidget *lgpl1; GtkWidget *separator9; GtkWidget *about1; - GtkWidget *image459; + GtkWidget *image519; GtkWidget *translators1; - GtkWidget *image460; + GtkWidget *image520; GtkWidget *hbox2; GtkWidget *hbox3; GtkWidget *stopbtn; @@ -153,9 +160,9 @@ create_mainwin (void) GDK_O, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); - image452 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); - gtk_widget_show (image452); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image452); + image512 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); + gtk_widget_show (image512); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image512); separator2 = gtk_separator_menu_item_new (); gtk_widget_show (separator2); @@ -166,17 +173,17 @@ create_mainwin (void) gtk_widget_show (add_files); gtk_container_add (GTK_CONTAINER (File_menu), add_files); - image453 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image453); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image453); + image513 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image513); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image513); add_folders = gtk_image_menu_item_new_with_mnemonic (_("Add folder(s)")); gtk_widget_show (add_folders); gtk_container_add (GTK_CONTAINER (File_menu), add_folders); - image454 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image454); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image454); + image514 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image514); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image514); add_location1 = gtk_menu_item_new_with_mnemonic (_("Add location")); gtk_widget_show (add_location1); @@ -218,9 +225,9 @@ create_mainwin (void) GDK_Q, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); - image455 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU); - gtk_widget_show (image455); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image455); + image515 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU); + gtk_widget_show (image515); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image515); Edit = gtk_menu_item_new_with_mnemonic (_("_Edit")); gtk_widget_show (Edit); @@ -233,9 +240,9 @@ create_mainwin (void) gtk_widget_show (clear1); gtk_container_add (GTK_CONTAINER (Edit_menu), clear1); - image456 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU); - gtk_widget_show (image456); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image456); + image516 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU); + gtk_widget_show (image516); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image516); select_all1 = gtk_menu_item_new_with_mnemonic (_("Select all")); gtk_widget_show (select_all1); @@ -266,9 +273,9 @@ create_mainwin (void) gtk_widget_show (remove1); gtk_container_add (GTK_CONTAINER (Selection_menu), remove1); - image457 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU); - gtk_widget_show (image457); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image457); + image517 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU); + gtk_widget_show (image517); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image517); crop1 = gtk_menu_item_new_with_mnemonic (_("Crop")); gtk_widget_show (crop1); @@ -281,6 +288,29 @@ create_mainwin (void) GDK_F, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); + sort_by1 = gtk_menu_item_new_with_mnemonic (_("Sort By")); + gtk_widget_show (sort_by1); + gtk_container_add (GTK_CONTAINER (Edit_menu), sort_by1); + + sort_by1_menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (sort_by1), sort_by1_menu); + + album1 = gtk_menu_item_new_with_mnemonic (_("Album")); + gtk_widget_show (album1); + gtk_container_add (GTK_CONTAINER (sort_by1_menu), album1); + + artist1 = gtk_menu_item_new_with_mnemonic (_("Artist")); + gtk_widget_show (artist1); + gtk_container_add (GTK_CONTAINER (sort_by1_menu), artist1); + + date1 = gtk_menu_item_new_with_mnemonic (_("Date")); + gtk_widget_show (date1); + gtk_container_add (GTK_CONTAINER (sort_by1_menu), date1); + + custom2 = gtk_menu_item_new_with_mnemonic (_("Custom")); + gtk_widget_show (custom2); + gtk_container_add (GTK_CONTAINER (sort_by1_menu), custom2); + separator5 = gtk_separator_menu_item_new (); gtk_widget_show (separator5); gtk_container_add (GTK_CONTAINER (Edit_menu), separator5); @@ -333,12 +363,18 @@ create_mainwin (void) gtk_container_add (GTK_CONTAINER (Order_menu), order_linear); gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_linear), TRUE); - order_shuffle = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Shuffle")); + order_shuffle = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Shuffle tracks")); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_shuffle)); gtk_widget_show (order_shuffle); gtk_container_add (GTK_CONTAINER (Order_menu), order_shuffle); gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_shuffle), TRUE); + order_shuffle_albums = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Shuffle albums")); + order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_shuffle_albums)); + gtk_widget_show (order_shuffle_albums); + gtk_container_add (GTK_CONTAINER (Order_menu), order_shuffle_albums); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_shuffle_albums), TRUE); + order_random = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, _("Random")); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_random)); gtk_widget_show (order_random); @@ -409,9 +445,9 @@ create_mainwin (void) gtk_widget_show (help1); gtk_container_add (GTK_CONTAINER (Help_menu), help1); - image458 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU); - gtk_widget_show (image458); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image458); + image518 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU); + gtk_widget_show (image518); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image518); changelog1 = gtk_menu_item_new_with_mnemonic (_("_ChangeLog")); gtk_widget_show (changelog1); @@ -439,17 +475,17 @@ create_mainwin (void) gtk_widget_show (about1); gtk_container_add (GTK_CONTAINER (Help_menu), about1); - image459 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); - gtk_widget_show (image459); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (about1), image459); + image519 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); + gtk_widget_show (image519); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (about1), image519); translators1 = gtk_image_menu_item_new_with_mnemonic (_("_Translators")); gtk_widget_show (translators1); gtk_container_add (GTK_CONTAINER (Help_menu), translators1); - image460 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); - gtk_widget_show (image460); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (translators1), image460); + image520 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); + gtk_widget_show (image520); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (translators1), image520); hbox2 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox2); @@ -645,6 +681,18 @@ create_mainwin (void) g_signal_connect ((gpointer) find1, "activate", G_CALLBACK (on_find_activate), NULL); + g_signal_connect ((gpointer) album1, "activate", + G_CALLBACK (on_album1_activate), + NULL); + g_signal_connect ((gpointer) artist1, "activate", + G_CALLBACK (on_artist1_activate), + NULL); + g_signal_connect ((gpointer) date1, "activate", + G_CALLBACK (on_date1_activate), + NULL); + g_signal_connect ((gpointer) custom2, "activate", + G_CALLBACK (on_custom2_activate), + NULL); g_signal_connect ((gpointer) preferences, "activate", G_CALLBACK (on_preferences_activate), NULL); @@ -666,6 +714,9 @@ create_mainwin (void) g_signal_connect ((gpointer) order_shuffle, "activate", G_CALLBACK (on_order_shuffle_activate), NULL); + g_signal_connect ((gpointer) order_shuffle_albums, "activate", + G_CALLBACK (on_order_shuffle_albums_activate), + NULL); g_signal_connect ((gpointer) order_random, "activate", G_CALLBACK (on_order_random_activate), NULL); @@ -731,12 +782,12 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, File, "File"); GLADE_HOOKUP_OBJECT (mainwin, File_menu, "File_menu"); GLADE_HOOKUP_OBJECT (mainwin, open, "open"); - GLADE_HOOKUP_OBJECT (mainwin, image452, "image452"); + GLADE_HOOKUP_OBJECT (mainwin, image512, "image512"); GLADE_HOOKUP_OBJECT (mainwin, separator2, "separator2"); GLADE_HOOKUP_OBJECT (mainwin, add_files, "add_files"); - GLADE_HOOKUP_OBJECT (mainwin, image453, "image453"); + GLADE_HOOKUP_OBJECT (mainwin, image513, "image513"); GLADE_HOOKUP_OBJECT (mainwin, add_folders, "add_folders"); - GLADE_HOOKUP_OBJECT (mainwin, image454, "image454"); + GLADE_HOOKUP_OBJECT (mainwin, image514, "image514"); GLADE_HOOKUP_OBJECT (mainwin, add_location1, "add_location1"); GLADE_HOOKUP_OBJECT (mainwin, separatormenuitem1, "separatormenuitem1"); GLADE_HOOKUP_OBJECT (mainwin, new_playlist1, "new_playlist1"); @@ -745,20 +796,26 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, playlist_save_as, "playlist_save_as"); GLADE_HOOKUP_OBJECT (mainwin, separator8, "separator8"); GLADE_HOOKUP_OBJECT (mainwin, quit, "quit"); - GLADE_HOOKUP_OBJECT (mainwin, image455, "image455"); + GLADE_HOOKUP_OBJECT (mainwin, image515, "image515"); GLADE_HOOKUP_OBJECT (mainwin, Edit, "Edit"); GLADE_HOOKUP_OBJECT (mainwin, Edit_menu, "Edit_menu"); GLADE_HOOKUP_OBJECT (mainwin, clear1, "clear1"); - GLADE_HOOKUP_OBJECT (mainwin, image456, "image456"); + GLADE_HOOKUP_OBJECT (mainwin, image516, "image516"); GLADE_HOOKUP_OBJECT (mainwin, select_all1, "select_all1"); GLADE_HOOKUP_OBJECT (mainwin, deselect_all1, "deselect_all1"); GLADE_HOOKUP_OBJECT (mainwin, invert_selection1, "invert_selection1"); GLADE_HOOKUP_OBJECT (mainwin, Selection, "Selection"); GLADE_HOOKUP_OBJECT (mainwin, Selection_menu, "Selection_menu"); GLADE_HOOKUP_OBJECT (mainwin, remove1, "remove1"); - GLADE_HOOKUP_OBJECT (mainwin, image457, "image457"); + GLADE_HOOKUP_OBJECT (mainwin, image517, "image517"); GLADE_HOOKUP_OBJECT (mainwin, crop1, "crop1"); GLADE_HOOKUP_OBJECT (mainwin, find1, "find1"); + GLADE_HOOKUP_OBJECT (mainwin, sort_by1, "sort_by1"); + GLADE_HOOKUP_OBJECT (mainwin, sort_by1_menu, "sort_by1_menu"); + GLADE_HOOKUP_OBJECT (mainwin, album1, "album1"); + GLADE_HOOKUP_OBJECT (mainwin, artist1, "artist1"); + GLADE_HOOKUP_OBJECT (mainwin, date1, "date1"); + GLADE_HOOKUP_OBJECT (mainwin, custom2, "custom2"); GLADE_HOOKUP_OBJECT (mainwin, separator5, "separator5"); GLADE_HOOKUP_OBJECT (mainwin, preferences, "preferences"); GLADE_HOOKUP_OBJECT (mainwin, View, "View"); @@ -773,6 +830,7 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, Order_menu, "Order_menu"); GLADE_HOOKUP_OBJECT (mainwin, order_linear, "order_linear"); GLADE_HOOKUP_OBJECT (mainwin, order_shuffle, "order_shuffle"); + GLADE_HOOKUP_OBJECT (mainwin, order_shuffle_albums, "order_shuffle_albums"); GLADE_HOOKUP_OBJECT (mainwin, order_random, "order_random"); GLADE_HOOKUP_OBJECT (mainwin, Looping, "Looping"); GLADE_HOOKUP_OBJECT (mainwin, Looping_menu, "Looping_menu"); @@ -787,16 +845,16 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, Help, "Help"); GLADE_HOOKUP_OBJECT (mainwin, Help_menu, "Help_menu"); GLADE_HOOKUP_OBJECT (mainwin, help1, "help1"); - GLADE_HOOKUP_OBJECT (mainwin, image458, "image458"); + GLADE_HOOKUP_OBJECT (mainwin, image518, "image518"); GLADE_HOOKUP_OBJECT (mainwin, changelog1, "changelog1"); GLADE_HOOKUP_OBJECT (mainwin, separator10, "separator10"); GLADE_HOOKUP_OBJECT (mainwin, gpl1, "gpl1"); GLADE_HOOKUP_OBJECT (mainwin, lgpl1, "lgpl1"); GLADE_HOOKUP_OBJECT (mainwin, separator9, "separator9"); GLADE_HOOKUP_OBJECT (mainwin, about1, "about1"); - GLADE_HOOKUP_OBJECT (mainwin, image459, "image459"); + GLADE_HOOKUP_OBJECT (mainwin, image519, "image519"); GLADE_HOOKUP_OBJECT (mainwin, translators1, "translators1"); - GLADE_HOOKUP_OBJECT (mainwin, image460, "image460"); + GLADE_HOOKUP_OBJECT (mainwin, image520, "image520"); GLADE_HOOKUP_OBJECT (mainwin, hbox2, "hbox2"); GLADE_HOOKUP_OBJECT (mainwin, hbox3, "hbox3"); GLADE_HOOKUP_OBJECT (mainwin, stopbtn, "stopbtn"); @@ -1027,26 +1085,26 @@ create_traymenu (void) } GtkWidget* -create_addprogress (void) +create_progressdlg (void) { - GtkWidget *addprogress; + GtkWidget *progressdlg; GtkWidget *vbox6; GtkWidget *progresstitle; GtkWidget *hbox7; GtkWidget *label22; - GtkWidget *button3; + GtkWidget *cancelbtn; - addprogress = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_container_set_border_width (GTK_CONTAINER (addprogress), 12); - gtk_window_set_title (GTK_WINDOW (addprogress), _("Adding files...")); - gtk_window_set_position (GTK_WINDOW (addprogress), GTK_WIN_POS_CENTER_ON_PARENT); - gtk_window_set_modal (GTK_WINDOW (addprogress), TRUE); - gtk_window_set_skip_taskbar_hint (GTK_WINDOW (addprogress), TRUE); - gtk_window_set_skip_pager_hint (GTK_WINDOW (addprogress), TRUE); + progressdlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (progressdlg), 12); + gtk_window_set_title (GTK_WINDOW (progressdlg), "progressdlg"); + gtk_window_set_position (GTK_WINDOW (progressdlg), GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_modal (GTK_WINDOW (progressdlg), TRUE); + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (progressdlg), TRUE); + gtk_window_set_skip_pager_hint (GTK_WINDOW (progressdlg), TRUE); vbox6 = gtk_vbox_new (FALSE, 8); gtk_widget_show (vbox6); - gtk_container_add (GTK_CONTAINER (addprogress), vbox6); + gtk_container_add (GTK_CONTAINER (progressdlg), vbox6); progresstitle = gtk_entry_new (); gtk_widget_show (progresstitle); @@ -1064,26 +1122,19 @@ create_addprogress (void) gtk_widget_show (label22); gtk_box_pack_start (GTK_BOX (hbox7), label22, TRUE, FALSE, 0); - button3 = gtk_button_new_from_stock ("gtk-cancel"); - gtk_widget_show (button3); - gtk_box_pack_start (GTK_BOX (hbox7), button3, FALSE, FALSE, 0); - - g_signal_connect ((gpointer) addprogress, "delete_event", - G_CALLBACK (on_addprogress_delete_event), - NULL); - g_signal_connect ((gpointer) button3, "clicked", - G_CALLBACK (on_progress_abort), - NULL); + cancelbtn = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancelbtn); + gtk_box_pack_start (GTK_BOX (hbox7), cancelbtn, FALSE, FALSE, 0); /* Store pointers to all widgets, for use by lookup_widget(). */ - GLADE_HOOKUP_OBJECT_NO_REF (addprogress, addprogress, "addprogress"); - GLADE_HOOKUP_OBJECT (addprogress, vbox6, "vbox6"); - GLADE_HOOKUP_OBJECT (addprogress, progresstitle, "progresstitle"); - GLADE_HOOKUP_OBJECT (addprogress, hbox7, "hbox7"); - GLADE_HOOKUP_OBJECT (addprogress, label22, "label22"); - GLADE_HOOKUP_OBJECT (addprogress, button3, "button3"); - - return addprogress; + GLADE_HOOKUP_OBJECT_NO_REF (progressdlg, progressdlg, "progressdlg"); + GLADE_HOOKUP_OBJECT (progressdlg, vbox6, "vbox6"); + GLADE_HOOKUP_OBJECT (progressdlg, progresstitle, "progresstitle"); + GLADE_HOOKUP_OBJECT (progressdlg, hbox7, "hbox7"); + GLADE_HOOKUP_OBJECT (progressdlg, label22, "label22"); + GLADE_HOOKUP_OBJECT (progressdlg, cancelbtn, "cancelbtn"); + + return progressdlg; } GtkWidget* @@ -1135,6 +1186,12 @@ create_trackproperties (void) GtkWidget *vbox16; GtkWidget *scrolledwindow5; GtkWidget *metalist; + GtkWidget *hbox98; + GtkWidget *settings; + GtkWidget *alignment24; + GtkWidget *hbox99; + GtkWidget *image522; + GtkWidget *label123; GtkWidget *hbuttonbox1; GtkWidget *write_tags; GtkWidget *alignment11; @@ -1161,6 +1218,7 @@ create_trackproperties (void) trackproperties = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_set_size_request (trackproperties, 400, 400); gtk_window_set_title (GTK_WINDOW (trackproperties), _("Track Properties")); + gtk_window_set_position (GTK_WINDOW (trackproperties), GTK_WIN_POS_MOUSE); gtk_window_set_skip_taskbar_hint (GTK_WINDOW (trackproperties), TRUE); gtk_window_set_skip_pager_hint (GTK_WINDOW (trackproperties), TRUE); @@ -1184,9 +1242,33 @@ create_trackproperties (void) gtk_container_add (GTK_CONTAINER (scrolledwindow5), metalist); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (metalist), TRUE); + hbox98 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox98); + gtk_box_pack_start (GTK_BOX (vbox16), hbox98, FALSE, FALSE, 0); + + settings = gtk_button_new (); + gtk_widget_show (settings); + gtk_box_pack_start (GTK_BOX (hbox98), settings, FALSE, FALSE, 0); + + alignment24 = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_widget_show (alignment24); + gtk_container_add (GTK_CONTAINER (settings), alignment24); + + hbox99 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox99); + gtk_container_add (GTK_CONTAINER (alignment24), hbox99); + + image522 = gtk_image_new_from_stock ("gtk-preferences", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image522); + gtk_box_pack_start (GTK_BOX (hbox99), image522, FALSE, FALSE, 0); + + label123 = gtk_label_new_with_mnemonic (_("Settings")); + gtk_widget_show (label123); + gtk_box_pack_start (GTK_BOX (hbox99), label123, FALSE, FALSE, 0); + hbuttonbox1 = gtk_hbutton_box_new (); gtk_widget_show (hbuttonbox1); - gtk_box_pack_start (GTK_BOX (vbox16), hbuttonbox1, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox98), hbuttonbox1, TRUE, TRUE, 0); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox1), GTK_BUTTONBOX_END); write_tags = gtk_button_new (); @@ -1287,6 +1369,18 @@ create_trackproperties (void) g_signal_connect ((gpointer) trackproperties, "delete_event", G_CALLBACK (on_trackproperties_delete_event), NULL); + g_signal_connect ((gpointer) trackproperties, "configure_event", + G_CALLBACK (on_trackproperties_configure_event), + NULL); + g_signal_connect ((gpointer) trackproperties, "window_state_event", + G_CALLBACK (on_trackproperties_window_state_event), + NULL); + g_signal_connect ((gpointer) metalist, "button_press_event", + G_CALLBACK (on_metalist_button_press_event), + NULL); + g_signal_connect ((gpointer) settings, "clicked", + G_CALLBACK (on_tagwriter_settings_clicked), + NULL); g_signal_connect ((gpointer) write_tags, "clicked", G_CALLBACK (on_write_tags_clicked), NULL); @@ -1303,6 +1397,12 @@ create_trackproperties (void) GLADE_HOOKUP_OBJECT (trackproperties, vbox16, "vbox16"); GLADE_HOOKUP_OBJECT (trackproperties, scrolledwindow5, "scrolledwindow5"); GLADE_HOOKUP_OBJECT (trackproperties, metalist, "metalist"); + GLADE_HOOKUP_OBJECT (trackproperties, hbox98, "hbox98"); + GLADE_HOOKUP_OBJECT (trackproperties, settings, "settings"); + GLADE_HOOKUP_OBJECT (trackproperties, alignment24, "alignment24"); + GLADE_HOOKUP_OBJECT (trackproperties, hbox99, "hbox99"); + GLADE_HOOKUP_OBJECT (trackproperties, image522, "image522"); + GLADE_HOOKUP_OBJECT (trackproperties, label123, "label123"); GLADE_HOOKUP_OBJECT (trackproperties, hbuttonbox1, "hbuttonbox1"); GLADE_HOOKUP_OBJECT (trackproperties, write_tags, "write_tags"); GLADE_HOOKUP_OBJECT (trackproperties, alignment11, "alignment11"); @@ -1343,11 +1443,12 @@ create_editcolumndlg (void) GtkWidget *id; GtkWidget *hbox31; GtkWidget *fmtlabel; + GtkWidget *hbox74; GtkWidget *format; + GtkWidget *title_formatting_help_link; GtkWidget *hbox32; GtkWidget *label38; GtkWidget *align; - GtkWidget *label25; GtkWidget *dialog_action_area1; GtkWidget *cancelbutton1; GtkWidget *alignment9; @@ -1362,7 +1463,7 @@ create_editcolumndlg (void) editcolumndlg = gtk_dialog_new (); gtk_container_set_border_width (GTK_CONTAINER (editcolumndlg), 12); - gtk_window_set_title (GTK_WINDOW (editcolumndlg), _("editcolumndlg")); + gtk_window_set_title (GTK_WINDOW (editcolumndlg), "editcolumndlg"); gtk_window_set_modal (GTK_WINDOW (editcolumndlg), TRUE); gtk_window_set_type_hint (GTK_WINDOW (editcolumndlg), GDK_WINDOW_TYPE_HINT_DIALOG); @@ -1408,8 +1509,8 @@ create_editcolumndlg (void) gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Artist")); gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Album")); gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Title")); - gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Length")); - gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Track")); + gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Duration")); + gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Track No")); gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Band / Album Artist")); gtk_combo_box_append_text (GTK_COMBO_BOX (id), _("Custom")); @@ -1422,12 +1523,22 @@ create_editcolumndlg (void) gtk_box_pack_start (GTK_BOX (hbox31), fmtlabel, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (fmtlabel), 0, 0.5); + hbox74 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox74); + gtk_box_pack_start (GTK_BOX (hbox31), hbox74, TRUE, TRUE, 0); + format = gtk_entry_new (); gtk_widget_show (format); - gtk_box_pack_start (GTK_BOX (hbox31), format, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox74), format, TRUE, TRUE, 0); gtk_entry_set_invisible_char (GTK_ENTRY (format), 9679); gtk_entry_set_activates_default (GTK_ENTRY (format), TRUE); + title_formatting_help_link = title_formatting_help_link_create ("title_formatting_help_link", "", "", 0, 0); + gtk_widget_show (title_formatting_help_link); + gtk_box_pack_start (GTK_BOX (hbox74), title_formatting_help_link, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (title_formatting_help_link, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (title_formatting_help_link, GTK_CAN_DEFAULT); + hbox32 = gtk_hbox_new (FALSE, 8); gtk_widget_show (hbox32); gtk_box_pack_start (GTK_BOX (vbox14), hbox32, FALSE, FALSE, 0); @@ -1443,14 +1554,6 @@ create_editcolumndlg (void) gtk_combo_box_append_text (GTK_COMBO_BOX (align), _("Left")); gtk_combo_box_append_text (GTK_COMBO_BOX (align), _("Right")); - label25 = gtk_label_new (_("Format conversions (start with %):\n [a]rtist, [t]itle, al[b]um, [B]and, [C]omposer\n track[n]umber, [N]totaltracks,\n [l]ength, [y]ear, [g]enre, [c]omment,\n copy[r]ight, [f]ilename, [F]ullPathname, [T]ags,\n [d]irectory, [D]irectoryWithPath\nExample: %a - %t [%l]")); - gtk_widget_show (label25); - gtk_box_pack_start (GTK_BOX (vbox14), label25, TRUE, TRUE, 0); - GTK_WIDGET_SET_FLAGS (label25, GTK_CAN_FOCUS); - gtk_label_set_use_markup (GTK_LABEL (label25), TRUE); - gtk_label_set_selectable (GTK_LABEL (label25), TRUE); - gtk_misc_set_alignment (GTK_MISC (label25), 0.1, 0.5); - dialog_action_area1 = GTK_DIALOG (editcolumndlg)->action_area; gtk_widget_show (dialog_action_area1); gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1), GTK_BUTTONBOX_END); @@ -1513,11 +1616,12 @@ create_editcolumndlg (void) GLADE_HOOKUP_OBJECT (editcolumndlg, id, "id"); GLADE_HOOKUP_OBJECT (editcolumndlg, hbox31, "hbox31"); GLADE_HOOKUP_OBJECT (editcolumndlg, fmtlabel, "fmtlabel"); + GLADE_HOOKUP_OBJECT (editcolumndlg, hbox74, "hbox74"); GLADE_HOOKUP_OBJECT (editcolumndlg, format, "format"); + GLADE_HOOKUP_OBJECT (editcolumndlg, title_formatting_help_link, "title_formatting_help_link"); GLADE_HOOKUP_OBJECT (editcolumndlg, hbox32, "hbox32"); GLADE_HOOKUP_OBJECT (editcolumndlg, label38, "label38"); GLADE_HOOKUP_OBJECT (editcolumndlg, align, "align"); - GLADE_HOOKUP_OBJECT (editcolumndlg, label25, "label25"); GLADE_HOOKUP_OBJECT_NO_REF (editcolumndlg, dialog_action_area1, "dialog_action_area1"); GLADE_HOOKUP_OBJECT (editcolumndlg, cancelbutton1, "cancelbutton1"); GLADE_HOOKUP_OBJECT (editcolumndlg, alignment9, "alignment9"); @@ -1548,31 +1652,57 @@ create_prefwin (void) GtkWidget *pref_soundcard; GtkWidget *Sound; GtkWidget *vbox8; - GtkWidget *pref_dynsamplerate; - GtkWidget *hbox9; - GtkWidget *label6; - GtkWidget *pref_src_quality; GtkWidget *hbox10; GtkWidget *label8; GtkWidget *pref_replaygain_mode; GtkWidget *pref_replaygain_scale; + GtkWidget *hbox100; + GtkWidget *label124; + GtkWidget *label125; + GtkWidget *replaygain_preamp; + GtkWidget *label126; GtkWidget *hbox66; GtkWidget *cli_add_to_playlist; GtkWidget *cli_playlist_name; GtkWidget *resume_last_session; + GtkWidget *ignore_archives; GtkWidget *label39; + GtkWidget *vbox29; + GtkWidget *hbox80; + GtkWidget *dsp_add; + GtkWidget *dsp_remove; + GtkWidget *dsp_configure; + GtkWidget *hbox81; + GtkWidget *scrolledwindow7; + GtkWidget *dsp_listview; + GtkWidget *vbox30; + GtkWidget *dsp_up; + GtkWidget *dsp_down; + GtkWidget *hbox86; + GtkWidget *label114; + GtkWidget *dsp_preset; + GtkWidget *dsp_preset_save; + GtkWidget *dsp_preset_load; + GtkWidget *label110; GtkWidget *vbox9; GtkWidget *pref_close_send_to_tray; GtkWidget *mmb_delete_playlist; GtkWidget *hide_tray_icon; GtkWidget *embolden_current; GtkWidget *hide_delete_from_disk; + GtkWidget *auto_name_playlist_from_folder; + GtkWidget *hbox102; + GtkWidget *label129; + GtkWidget *gui_fps; GtkWidget *hbox64; GtkWidget *label101; GtkWidget *titlebar_format_playing; GtkWidget *hbox65; GtkWidget *label102; GtkWidget *titlebar_format_stopped; + GtkWidget *hbox101; + GtkWidget *label128; + GtkWidget *gui_plugin; GtkWidget *label2; GtkWidget *notebook4; GtkWidget *vbox21; @@ -1594,6 +1724,8 @@ create_prefwin (void) GtkWidget *tabstrip_dark; GtkWidget *tabstrip_base; GtkWidget *label76; + GtkWidget *label127; + GtkWidget *tabstrip_text; GtkWidget *label74; GtkWidget *vbox23; GtkWidget *override_listview_colors; @@ -1630,82 +1762,32 @@ create_prefwin (void) GtkWidget *label98; GtkWidget *proxypassword; GtkWidget *label16; - GtkWidget *vbox18; - GtkWidget *frame5; - GtkWidget *alignment3; - GtkWidget *vbox19; - GtkWidget *hbox38; - GtkWidget *write_id3v2; - GtkWidget *write_id3v1; - GtkWidget *write_apev2; - GtkWidget *hbox40; - GtkWidget *strip_id3v2; - GtkWidget *strip_id3v1; - GtkWidget *strip_apev2; - GtkWidget *hbox36; - GtkWidget *label69; - GtkWidget *id3v2_version; - GtkWidget *hbox39; - GtkWidget *label71; - GtkWidget *id3v1_encoding; - GtkWidget *label68; - GtkWidget *hbox41; - GtkWidget *frame6; - GtkWidget *alignment4; - GtkWidget *vbox20; - GtkWidget *hbox37; - GtkWidget *ape_write_id3v2; - GtkWidget *ape_write_apev2; - GtkWidget *hbox45; - GtkWidget *ape_strip_id3v2; - GtkWidget *ape_strip_apev2; - GtkWidget *label70; - GtkWidget *frame7; - GtkWidget *alignment5; - GtkWidget *vbox_wv; - GtkWidget *hbox44; - GtkWidget *wv_write_apev2; - GtkWidget *wv_write_id3v1; - GtkWidget *hbox43; - GtkWidget *wv_strip_apev2; - GtkWidget *wv_strip_id3v1; - GtkWidget *label79; - GtkWidget *label67; GtkWidget *hpaned1; GtkWidget *scrolledwindow2; GtkWidget *pref_pluginlist; GtkWidget *vbox12; - GtkWidget *hbox16; - GtkWidget *label11; - GtkWidget *pref_plugin_descr; - GtkWidget *hbox17; - GtkWidget *label12; - GtkWidget *pref_plugin_author; - GtkWidget *hbox18; - GtkWidget *label13; - GtkWidget *pref_plugin_email; - GtkWidget *hbox19; - GtkWidget *label14; - GtkWidget *pref_plugin_website; + GtkWidget *scrolledwindow8; + GtkWidget *plug_description; GtkWidget *hbox20; GtkWidget *configure_plugin; GtkWidget *alignment15; GtkWidget *hbox56; GtkWidget *image394; GtkWidget *label92; + GtkWidget *plug_copyright; + GtkWidget *alignment20; + GtkWidget *hbox88; + GtkWidget *image521; + GtkWidget *label117; + GtkWidget *weblink; GtkWidget *label3; GtkWidget *dialog_action_area2; GtkWidget *closebutton1; - GtkWidget *alignment14; - GtkWidget *hbox55; - GtkWidget *image393; - GtkWidget *label91; prefwin = gtk_dialog_new (); - gtk_widget_set_size_request (prefwin, 630, 400); gtk_container_set_border_width (GTK_CONTAINER (prefwin), 12); gtk_window_set_title (GTK_WINDOW (prefwin), _("Preferences")); - gtk_window_set_default_size (GTK_WINDOW (prefwin), 630, 400); + gtk_window_set_position (GTK_WINDOW (prefwin), GTK_WIN_POS_CENTER); gtk_window_set_type_hint (GTK_WINDOW (prefwin), GDK_WINDOW_TYPE_HINT_DIALOG); dialog_vbox2 = GTK_DIALOG (prefwin)->vbox; @@ -1756,29 +1838,6 @@ create_prefwin (void) gtk_container_add (GTK_CONTAINER (notebook), vbox8); gtk_container_set_border_width (GTK_CONTAINER (vbox8), 12); - pref_dynsamplerate = gtk_check_button_new_with_mnemonic (_("Allow dynamic samplerate switching")); - gtk_widget_show (pref_dynsamplerate); - gtk_box_pack_start (GTK_BOX (vbox8), pref_dynsamplerate, FALSE, FALSE, 0); - - hbox9 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox9); - gtk_box_pack_start (GTK_BOX (vbox8), hbox9, FALSE, FALSE, 0); - - label6 = gtk_label_new (_("Samplerate conversion quality:")); - gtk_widget_show (label6); - gtk_box_pack_start (GTK_BOX (hbox9), label6, FALSE, FALSE, 0); - gtk_label_set_justify (GTK_LABEL (label6), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5); - - pref_src_quality = gtk_combo_box_new_text (); - gtk_widget_show (pref_src_quality); - gtk_box_pack_start (GTK_BOX (hbox9), pref_src_quality, TRUE, TRUE, 0); - gtk_combo_box_append_text (GTK_COMBO_BOX (pref_src_quality), "sinc_best_quality"); - gtk_combo_box_append_text (GTK_COMBO_BOX (pref_src_quality), "sinc_medium_quality"); - gtk_combo_box_append_text (GTK_COMBO_BOX (pref_src_quality), "sinc_fastest"); - gtk_combo_box_append_text (GTK_COMBO_BOX (pref_src_quality), "zero_order_hold"); - gtk_combo_box_append_text (GTK_COMBO_BOX (pref_src_quality), "linear"); - hbox10 = gtk_hbox_new (FALSE, 8); gtk_widget_show (hbox10); gtk_box_pack_start (GTK_BOX (vbox8), hbox10, FALSE, FALSE, 0); @@ -1800,6 +1859,28 @@ create_prefwin (void) gtk_widget_show (pref_replaygain_scale); gtk_box_pack_start (GTK_BOX (vbox8), pref_replaygain_scale, FALSE, FALSE, 0); + hbox100 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox100); + gtk_box_pack_start (GTK_BOX (vbox8), hbox100, FALSE, FALSE, 0); + + label124 = gtk_label_new (_("Replaygain preamp:")); + gtk_widget_show (label124); + gtk_box_pack_start (GTK_BOX (hbox100), label124, FALSE, FALSE, 0); + + label125 = gtk_label_new (_("-12 dB")); + gtk_widget_show (label125); + gtk_box_pack_start (GTK_BOX (hbox100), label125, FALSE, FALSE, 0); + + replaygain_preamp = gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (0, -12, 12, 0, 0, 0))); + gtk_widget_show (replaygain_preamp); + gtk_box_pack_start (GTK_BOX (hbox100), replaygain_preamp, TRUE, TRUE, 0); + gtk_scale_set_value_pos (GTK_SCALE (replaygain_preamp), GTK_POS_BOTTOM); + gtk_scale_set_digits (GTK_SCALE (replaygain_preamp), 0); + + label126 = gtk_label_new (_("+12 dB")); + gtk_widget_show (label126); + gtk_box_pack_start (GTK_BOX (hbox100), label126, FALSE, FALSE, 0); + hbox66 = gtk_hbox_new (FALSE, 8); gtk_widget_show (hbox66); gtk_box_pack_start (GTK_BOX (vbox8), hbox66, FALSE, FALSE, 0); @@ -1817,10 +1898,86 @@ create_prefwin (void) gtk_widget_show (resume_last_session); gtk_box_pack_start (GTK_BOX (vbox8), resume_last_session, FALSE, FALSE, 0); + ignore_archives = gtk_check_button_new_with_mnemonic (_("Don't add from archives when adding folders")); + gtk_widget_show (ignore_archives); + gtk_box_pack_start (GTK_BOX (vbox8), ignore_archives, FALSE, FALSE, 0); + label39 = gtk_label_new (_("Playback")); gtk_widget_show (label39); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1), label39); + vbox29 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox29); + gtk_container_add (GTK_CONTAINER (notebook), vbox29); + gtk_container_set_border_width (GTK_CONTAINER (vbox29), 12); + + hbox80 = gtk_hbox_new (TRUE, 8); + gtk_widget_show (hbox80); + gtk_box_pack_start (GTK_BOX (vbox29), hbox80, FALSE, TRUE, 0); + + dsp_add = gtk_button_new_from_stock ("gtk-add"); + gtk_widget_show (dsp_add); + gtk_box_pack_start (GTK_BOX (hbox80), dsp_add, FALSE, TRUE, 0); + + dsp_remove = gtk_button_new_from_stock ("gtk-remove"); + gtk_widget_show (dsp_remove); + gtk_box_pack_start (GTK_BOX (hbox80), dsp_remove, FALSE, TRUE, 0); + + dsp_configure = gtk_button_new_with_mnemonic (_("Configure")); + gtk_widget_show (dsp_configure); + gtk_box_pack_start (GTK_BOX (hbox80), dsp_configure, FALSE, TRUE, 0); + + hbox81 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox81); + gtk_box_pack_start (GTK_BOX (vbox29), hbox81, TRUE, TRUE, 0); + + scrolledwindow7 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow7); + gtk_box_pack_start (GTK_BOX (hbox81), scrolledwindow7, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow7), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow7), GTK_SHADOW_IN); + + dsp_listview = gtk_tree_view_new (); + gtk_widget_show (dsp_listview); + gtk_container_add (GTK_CONTAINER (scrolledwindow7), dsp_listview); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dsp_listview), FALSE); + + vbox30 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox30); + gtk_box_pack_start (GTK_BOX (hbox81), vbox30, FALSE, TRUE, 0); + + dsp_up = gtk_button_new_from_stock ("gtk-go-up"); + gtk_widget_show (dsp_up); + gtk_box_pack_start (GTK_BOX (vbox30), dsp_up, FALSE, FALSE, 0); + + dsp_down = gtk_button_new_from_stock ("gtk-go-down"); + gtk_widget_show (dsp_down); + gtk_box_pack_start (GTK_BOX (vbox30), dsp_down, FALSE, FALSE, 0); + + hbox86 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox86); + gtk_box_pack_start (GTK_BOX (vbox29), hbox86, FALSE, TRUE, 0); + + label114 = gtk_label_new (_("DSP Chain Preset")); + gtk_widget_show (label114); + gtk_box_pack_start (GTK_BOX (hbox86), label114, FALSE, FALSE, 0); + + dsp_preset = gtk_combo_box_entry_new_text (); + gtk_widget_show (dsp_preset); + gtk_box_pack_start (GTK_BOX (hbox86), dsp_preset, FALSE, TRUE, 0); + + dsp_preset_save = gtk_button_new_from_stock ("gtk-save"); + gtk_widget_show (dsp_preset_save); + gtk_box_pack_start (GTK_BOX (hbox86), dsp_preset_save, FALSE, FALSE, 0); + + dsp_preset_load = gtk_button_new_with_mnemonic (_("_Load")); + gtk_widget_show (dsp_preset_load); + gtk_box_pack_start (GTK_BOX (hbox86), dsp_preset_load, FALSE, FALSE, 0); + + label110 = gtk_label_new (_("DSP")); + gtk_widget_show (label110); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 2), label110); + vbox9 = gtk_vbox_new (FALSE, 8); gtk_widget_show (vbox9); gtk_container_add (GTK_CONTAINER (notebook), vbox9); @@ -1846,6 +2003,24 @@ create_prefwin (void) gtk_widget_show (hide_delete_from_disk); gtk_box_pack_start (GTK_BOX (vbox9), hide_delete_from_disk, FALSE, FALSE, 0); + auto_name_playlist_from_folder = gtk_check_button_new_with_mnemonic (_("Auto-name playlists when adding a single folder")); + gtk_widget_show (auto_name_playlist_from_folder); + gtk_box_pack_start (GTK_BOX (vbox9), auto_name_playlist_from_folder, FALSE, FALSE, 0); + + hbox102 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox102); + gtk_box_pack_start (GTK_BOX (vbox9), hbox102, FALSE, FALSE, 0); + + label129 = gtk_label_new (_("Interface refresh rate (times per second):")); + gtk_widget_show (label129); + gtk_box_pack_start (GTK_BOX (hbox102), label129, FALSE, FALSE, 0); + + gui_fps = gtk_hscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (10, 1, 30, 0, 0, 0))); + gtk_widget_show (gui_fps); + gtk_box_pack_start (GTK_BOX (hbox102), gui_fps, TRUE, TRUE, 0); + gtk_scale_set_value_pos (GTK_SCALE (gui_fps), GTK_POS_RIGHT); + gtk_scale_set_digits (GTK_SCALE (gui_fps), 0); + hbox64 = gtk_hbox_new (FALSE, 8); gtk_widget_show (hbox64); gtk_box_pack_start (GTK_BOX (vbox9), hbox64, FALSE, FALSE, 0); @@ -1874,9 +2049,21 @@ create_prefwin (void) gtk_box_pack_start (GTK_BOX (hbox65), titlebar_format_stopped, TRUE, TRUE, 0); gtk_entry_set_invisible_char (GTK_ENTRY (titlebar_format_stopped), 8226); + hbox101 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox101); + gtk_box_pack_start (GTK_BOX (vbox9), hbox101, FALSE, FALSE, 0); + + label128 = gtk_label_new (_("GUI Plugin (changing requires restart):")); + gtk_widget_show (label128); + gtk_box_pack_start (GTK_BOX (hbox101), label128, FALSE, FALSE, 0); + + gui_plugin = gtk_combo_box_new_text (); + gtk_widget_show (gui_plugin); + gtk_box_pack_start (GTK_BOX (hbox101), gui_plugin, TRUE, TRUE, 0); + label2 = gtk_label_new (_("GUI")); gtk_widget_show (label2); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 2), label2); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 3), label2); notebook4 = gtk_notebook_new (); gtk_widget_show (notebook4); @@ -1937,7 +2124,7 @@ create_prefwin (void) gtk_widget_show (override_tabstrip_colors); gtk_box_pack_start (GTK_BOX (vbox22), override_tabstrip_colors, FALSE, FALSE, 0); - tabstrip_colors_group = gtk_table_new (2, 4, TRUE); + tabstrip_colors_group = gtk_table_new (2, 5, TRUE); gtk_widget_show (tabstrip_colors_group); gtk_box_pack_start (GTK_BOX (vbox22), tabstrip_colors_group, TRUE, TRUE, 0); gtk_table_set_col_spacings (GTK_TABLE (tabstrip_colors_group), 8); @@ -1994,6 +2181,19 @@ create_prefwin (void) (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label76), 0, 0.5); + label127 = gtk_label_new (_("Text")); + gtk_widget_show (label127); + gtk_table_attach (GTK_TABLE (tabstrip_colors_group), label127, 4, 5, 0, 1, + (GtkAttachOptions) (GTK_EXPAND), + (GtkAttachOptions) (0), 0, 0); + gtk_misc_set_alignment (GTK_MISC (label127), 0, 0.5); + + tabstrip_text = gtk_color_button_new (); + gtk_widget_show (tabstrip_text); + gtk_table_attach (GTK_TABLE (tabstrip_colors_group), tabstrip_text, 4, 5, 1, 2, + (GtkAttachOptions) (GTK_EXPAND), + (GtkAttachOptions) (0), 0, 0); + label74 = gtk_label_new (_("Tab strip colors")); gtk_widget_show (label74); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook4), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook4), 1), label74); @@ -2096,7 +2296,7 @@ create_prefwin (void) label100 = gtk_label_new (_("Colors")); gtk_widget_show (label100); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 3), label100); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 4), label100); vbox11 = gtk_vbox_new (FALSE, 8); gtk_widget_show (vbox11); @@ -2183,187 +2383,7 @@ create_prefwin (void) label16 = gtk_label_new (_("Network")); gtk_widget_show (label16); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 4), label16); - - vbox18 = gtk_vbox_new (FALSE, 0); - gtk_widget_show (vbox18); - gtk_container_add (GTK_CONTAINER (notebook), vbox18); - gtk_container_set_border_width (GTK_CONTAINER (vbox18), 12); - - frame5 = gtk_frame_new (NULL); - gtk_widget_show (frame5); - gtk_box_pack_start (GTK_BOX (vbox18), frame5, FALSE, TRUE, 0); - gtk_frame_set_shadow_type (GTK_FRAME (frame5), GTK_SHADOW_NONE); - - alignment3 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_widget_show (alignment3); - gtk_container_add (GTK_CONTAINER (frame5), alignment3); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3), 0, 0, 12, 0); - - vbox19 = gtk_vbox_new (FALSE, 8); - gtk_widget_show (vbox19); - gtk_container_add (GTK_CONTAINER (alignment3), vbox19); - gtk_container_set_border_width (GTK_CONTAINER (vbox19), 12); - - hbox38 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox38); - gtk_box_pack_start (GTK_BOX (vbox19), hbox38, FALSE, FALSE, 0); - - write_id3v2 = gtk_check_button_new_with_mnemonic (_("Write ID3v2")); - gtk_widget_show (write_id3v2); - gtk_box_pack_start (GTK_BOX (hbox38), write_id3v2, FALSE, FALSE, 0); - - write_id3v1 = gtk_check_button_new_with_mnemonic (_("Write ID3v1")); - gtk_widget_show (write_id3v1); - gtk_box_pack_start (GTK_BOX (hbox38), write_id3v1, FALSE, FALSE, 0); - - write_apev2 = gtk_check_button_new_with_mnemonic (_("Write APEv2")); - gtk_widget_show (write_apev2); - gtk_box_pack_start (GTK_BOX (hbox38), write_apev2, FALSE, FALSE, 0); - - hbox40 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox40); - gtk_box_pack_start (GTK_BOX (vbox19), hbox40, FALSE, FALSE, 0); - - strip_id3v2 = gtk_check_button_new_with_mnemonic (_("Strip ID3v2")); - gtk_widget_show (strip_id3v2); - gtk_box_pack_start (GTK_BOX (hbox40), strip_id3v2, FALSE, FALSE, 0); - - strip_id3v1 = gtk_check_button_new_with_mnemonic (_("Strip ID3v1")); - gtk_widget_show (strip_id3v1); - gtk_box_pack_start (GTK_BOX (hbox40), strip_id3v1, FALSE, FALSE, 0); - - strip_apev2 = gtk_check_button_new_with_mnemonic (_("Strip APEv2")); - gtk_widget_show (strip_apev2); - gtk_box_pack_start (GTK_BOX (hbox40), strip_apev2, FALSE, FALSE, 0); - - hbox36 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox36); - gtk_box_pack_start (GTK_BOX (vbox19), hbox36, TRUE, TRUE, 0); - - label69 = gtk_label_new (_("ID3v2 version")); - gtk_widget_show (label69); - gtk_box_pack_start (GTK_BOX (hbox36), label69, FALSE, FALSE, 0); - - id3v2_version = gtk_combo_box_new_text (); - gtk_widget_show (id3v2_version); - gtk_box_pack_start (GTK_BOX (hbox36), id3v2_version, TRUE, TRUE, 0); - gtk_combo_box_append_text (GTK_COMBO_BOX (id3v2_version), _("2.3 (Recommended)")); - gtk_combo_box_append_text (GTK_COMBO_BOX (id3v2_version), _("2.4")); - - hbox39 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox39); - gtk_box_pack_start (GTK_BOX (vbox19), hbox39, TRUE, TRUE, 0); - - label71 = gtk_label_new (_("ID3v1 character encoding (default is iso8859-1)")); - gtk_widget_show (label71); - gtk_box_pack_start (GTK_BOX (hbox39), label71, FALSE, FALSE, 0); - - id3v1_encoding = gtk_entry_new (); - gtk_widget_show (id3v1_encoding); - gtk_box_pack_start (GTK_BOX (hbox39), id3v1_encoding, TRUE, TRUE, 0); - gtk_entry_set_invisible_char (GTK_ENTRY (id3v1_encoding), 9679); - - label68 = gtk_label_new ("<b>MP3</b>"); - gtk_widget_show (label68); - gtk_frame_set_label_widget (GTK_FRAME (frame5), label68); - gtk_label_set_use_markup (GTK_LABEL (label68), TRUE); - - hbox41 = gtk_hbox_new (TRUE, 0); - gtk_widget_show (hbox41); - gtk_box_pack_start (GTK_BOX (vbox18), hbox41, FALSE, TRUE, 0); - - frame6 = gtk_frame_new (NULL); - gtk_widget_show (frame6); - gtk_box_pack_start (GTK_BOX (hbox41), frame6, TRUE, TRUE, 0); - gtk_frame_set_shadow_type (GTK_FRAME (frame6), GTK_SHADOW_NONE); - - alignment4 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_widget_show (alignment4); - gtk_container_add (GTK_CONTAINER (frame6), alignment4); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment4), 0, 0, 12, 0); - - vbox20 = gtk_vbox_new (FALSE, 8); - gtk_widget_show (vbox20); - gtk_container_add (GTK_CONTAINER (alignment4), vbox20); - gtk_container_set_border_width (GTK_CONTAINER (vbox20), 12); - - hbox37 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox37); - gtk_box_pack_start (GTK_BOX (vbox20), hbox37, TRUE, TRUE, 0); - - ape_write_id3v2 = gtk_check_button_new_with_mnemonic (_("Write ID3v2.4")); - gtk_widget_show (ape_write_id3v2); - gtk_box_pack_start (GTK_BOX (hbox37), ape_write_id3v2, FALSE, FALSE, 0); - - ape_write_apev2 = gtk_check_button_new_with_mnemonic (_("Write APEv2")); - gtk_widget_show (ape_write_apev2); - gtk_box_pack_start (GTK_BOX (hbox37), ape_write_apev2, FALSE, FALSE, 0); - - hbox45 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox45); - gtk_box_pack_start (GTK_BOX (vbox20), hbox45, TRUE, TRUE, 0); - - ape_strip_id3v2 = gtk_check_button_new_with_mnemonic (_("Strip ID3v2")); - gtk_widget_show (ape_strip_id3v2); - gtk_box_pack_start (GTK_BOX (hbox45), ape_strip_id3v2, FALSE, FALSE, 0); - - ape_strip_apev2 = gtk_check_button_new_with_mnemonic (_("Strip APEv2")); - gtk_widget_show (ape_strip_apev2); - gtk_box_pack_start (GTK_BOX (hbox45), ape_strip_apev2, FALSE, FALSE, 0); - - label70 = gtk_label_new ("<b>APE</b>"); - gtk_widget_show (label70); - gtk_frame_set_label_widget (GTK_FRAME (frame6), label70); - gtk_label_set_use_markup (GTK_LABEL (label70), TRUE); - - frame7 = gtk_frame_new (NULL); - gtk_widget_show (frame7); - gtk_box_pack_start (GTK_BOX (hbox41), frame7, TRUE, TRUE, 0); - gtk_frame_set_shadow_type (GTK_FRAME (frame7), GTK_SHADOW_NONE); - - alignment5 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_widget_show (alignment5); - gtk_container_add (GTK_CONTAINER (frame7), alignment5); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment5), 0, 0, 12, 0); - - vbox_wv = gtk_vbox_new (FALSE, 8); - gtk_widget_show (vbox_wv); - gtk_container_add (GTK_CONTAINER (alignment5), vbox_wv); - gtk_container_set_border_width (GTK_CONTAINER (vbox_wv), 12); - - hbox44 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox44); - gtk_box_pack_start (GTK_BOX (vbox_wv), hbox44, FALSE, FALSE, 0); - - wv_write_apev2 = gtk_check_button_new_with_mnemonic (_("Write APEv2")); - gtk_widget_show (wv_write_apev2); - gtk_box_pack_start (GTK_BOX (hbox44), wv_write_apev2, FALSE, FALSE, 0); - - wv_write_id3v1 = gtk_check_button_new_with_mnemonic (_("Write ID3v1")); - gtk_widget_show (wv_write_id3v1); - gtk_box_pack_start (GTK_BOX (hbox44), wv_write_id3v1, FALSE, FALSE, 0); - - hbox43 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox43); - gtk_box_pack_start (GTK_BOX (vbox_wv), hbox43, FALSE, FALSE, 0); - - wv_strip_apev2 = gtk_check_button_new_with_mnemonic (_("Strip APEv2")); - gtk_widget_show (wv_strip_apev2); - gtk_box_pack_start (GTK_BOX (hbox43), wv_strip_apev2, FALSE, FALSE, 0); - - wv_strip_id3v1 = gtk_check_button_new_with_mnemonic (_("Strip ID3v1")); - gtk_widget_show (wv_strip_id3v1); - gtk_box_pack_start (GTK_BOX (hbox43), wv_strip_id3v1, FALSE, FALSE, 0); - - label79 = gtk_label_new ("<b>WavPack</b>"); - gtk_widget_show (label79); - gtk_frame_set_label_widget (GTK_FRAME (frame7), label79); - gtk_label_set_use_markup (GTK_LABEL (label79), TRUE); - - label67 = gtk_label_new (_("Tag writer")); - gtk_widget_show (label67); - gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 5), label67); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 5), label16); hpaned1 = gtk_hpaned_new (); gtk_widget_show (hpaned1); @@ -2388,65 +2408,17 @@ create_prefwin (void) gtk_paned_pack2 (GTK_PANED (hpaned1), vbox12, TRUE, TRUE); gtk_container_set_border_width (GTK_CONTAINER (vbox12), 12); - hbox16 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox16); - gtk_box_pack_start (GTK_BOX (vbox12), hbox16, FALSE, FALSE, 0); - - label11 = gtk_label_new (_("Description:")); - gtk_widget_show (label11); - gtk_box_pack_start (GTK_BOX (hbox16), label11, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (label11), 0, 0.5); - - pref_plugin_descr = gtk_entry_new (); - gtk_widget_show (pref_plugin_descr); - gtk_box_pack_start (GTK_BOX (hbox16), pref_plugin_descr, TRUE, TRUE, 0); - gtk_editable_set_editable (GTK_EDITABLE (pref_plugin_descr), FALSE); - gtk_entry_set_invisible_char (GTK_ENTRY (pref_plugin_descr), 9679); - - hbox17 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox17); - gtk_box_pack_start (GTK_BOX (vbox12), hbox17, FALSE, FALSE, 0); - - label12 = gtk_label_new (_("Author(s):")); - gtk_widget_show (label12); - gtk_box_pack_start (GTK_BOX (hbox17), label12, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (label12), 0, 0.5); - - pref_plugin_author = gtk_entry_new (); - gtk_widget_show (pref_plugin_author); - gtk_box_pack_start (GTK_BOX (hbox17), pref_plugin_author, TRUE, TRUE, 0); - gtk_editable_set_editable (GTK_EDITABLE (pref_plugin_author), FALSE); - gtk_entry_set_invisible_char (GTK_ENTRY (pref_plugin_author), 9679); - - hbox18 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox18); - gtk_box_pack_start (GTK_BOX (vbox12), hbox18, FALSE, FALSE, 0); - - label13 = gtk_label_new (_("Email:")); - gtk_widget_show (label13); - gtk_box_pack_start (GTK_BOX (hbox18), label13, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (label13), 0, 0.5); - - pref_plugin_email = gtk_entry_new (); - gtk_widget_show (pref_plugin_email); - gtk_box_pack_start (GTK_BOX (hbox18), pref_plugin_email, TRUE, TRUE, 0); - gtk_editable_set_editable (GTK_EDITABLE (pref_plugin_email), FALSE); - gtk_entry_set_invisible_char (GTK_ENTRY (pref_plugin_email), 9679); - - hbox19 = gtk_hbox_new (FALSE, 8); - gtk_widget_show (hbox19); - gtk_box_pack_start (GTK_BOX (vbox12), hbox19, FALSE, FALSE, 0); - - label14 = gtk_label_new (_("Website:")); - gtk_widget_show (label14); - gtk_box_pack_start (GTK_BOX (hbox19), label14, FALSE, FALSE, 0); - gtk_misc_set_alignment (GTK_MISC (label14), 0, 0.5); - - pref_plugin_website = gtk_entry_new (); - gtk_widget_show (pref_plugin_website); - gtk_box_pack_start (GTK_BOX (hbox19), pref_plugin_website, TRUE, TRUE, 0); - gtk_editable_set_editable (GTK_EDITABLE (pref_plugin_website), FALSE); - gtk_entry_set_invisible_char (GTK_ENTRY (pref_plugin_website), 9679); + scrolledwindow8 = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrolledwindow8); + gtk_box_pack_start (GTK_BOX (vbox12), scrolledwindow8, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow8), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow8), GTK_SHADOW_IN); + + plug_description = gtk_text_view_new (); + gtk_widget_show (plug_description); + gtk_container_add (GTK_CONTAINER (scrolledwindow8), plug_description); + gtk_text_view_set_editable (GTK_TEXT_VIEW (plug_description), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (plug_description), FALSE); hbox20 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox20); @@ -2455,6 +2427,7 @@ create_prefwin (void) configure_plugin = gtk_button_new (); gtk_widget_show (configure_plugin); gtk_box_pack_start (GTK_BOX (hbox20), configure_plugin, TRUE, FALSE, 0); + gtk_widget_set_sensitive (configure_plugin, FALSE); alignment15 = gtk_alignment_new (0.5, 0.5, 0, 0); gtk_widget_show (alignment15); @@ -2472,6 +2445,33 @@ create_prefwin (void) gtk_widget_show (label92); gtk_box_pack_start (GTK_BOX (hbox56), label92, FALSE, FALSE, 0); + plug_copyright = gtk_button_new (); + gtk_widget_show (plug_copyright); + gtk_box_pack_start (GTK_BOX (hbox20), plug_copyright, TRUE, FALSE, 0); + gtk_widget_set_sensitive (plug_copyright, FALSE); + + alignment20 = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_widget_show (alignment20); + gtk_container_add (GTK_CONTAINER (plug_copyright), alignment20); + + hbox88 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox88); + gtk_container_add (GTK_CONTAINER (alignment20), hbox88); + + image521 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image521); + gtk_box_pack_start (GTK_BOX (hbox88), image521, FALSE, FALSE, 0); + + label117 = gtk_label_new_with_mnemonic (_("Copyright")); + gtk_widget_show (label117); + gtk_box_pack_start (GTK_BOX (hbox88), label117, FALSE, FALSE, 0); + + weblink = create_plugin_weblink ("weblink", "", "", 0, 0); + gtk_widget_show (weblink); + gtk_box_pack_start (GTK_BOX (hbox20), weblink, TRUE, FALSE, 0); + GTK_WIDGET_UNSET_FLAGS (weblink, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (weblink, GTK_CAN_DEFAULT); + label3 = gtk_label_new (_("Plugins")); gtk_widget_show (label3); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 6), label3); @@ -2481,32 +2481,19 @@ create_prefwin (void) gtk_widget_show (dialog_action_area2); gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area2), GTK_BUTTONBOX_END); - closebutton1 = gtk_button_new (); + closebutton1 = gtk_button_new_from_stock ("gtk-close"); gtk_widget_show (closebutton1); gtk_dialog_add_action_widget (GTK_DIALOG (prefwin), closebutton1, GTK_RESPONSE_CLOSE); GTK_WIDGET_SET_FLAGS (closebutton1, GTK_CAN_DEFAULT); - alignment14 = gtk_alignment_new (0.5, 0.5, 0, 0); - gtk_widget_show (alignment14); - gtk_container_add (GTK_CONTAINER (closebutton1), alignment14); - - hbox55 = gtk_hbox_new (FALSE, 2); - gtk_widget_show (hbox55); - gtk_container_add (GTK_CONTAINER (alignment14), hbox55); - - image393 = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_BUTTON); - gtk_widget_show (image393); - gtk_box_pack_start (GTK_BOX (hbox55), image393, FALSE, FALSE, 0); - - label91 = gtk_label_new_with_mnemonic (_("_Close")); - gtk_widget_show (label91); - gtk_box_pack_start (GTK_BOX (hbox55), label91, FALSE, FALSE, 0); - - g_signal_connect ((gpointer) pref_dynsamplerate, "clicked", - G_CALLBACK (on_pref_dynsamplerate_clicked), + g_signal_connect ((gpointer) prefwin, "configure_event", + G_CALLBACK (on_prefwin_configure_event), NULL); - g_signal_connect ((gpointer) pref_src_quality, "changed", - G_CALLBACK (on_pref_src_quality_changed), + g_signal_connect ((gpointer) prefwin, "window_state_event", + G_CALLBACK (on_prefwin_window_state_event), + NULL); + g_signal_connect ((gpointer) prefwin, "realize", + G_CALLBACK (on_prefwin_realize), NULL); g_signal_connect ((gpointer) pref_replaygain_mode, "changed", G_CALLBACK (on_pref_replaygain_mode_changed), @@ -2514,6 +2501,9 @@ create_prefwin (void) g_signal_connect ((gpointer) pref_replaygain_scale, "clicked", G_CALLBACK (on_pref_replaygain_scale_clicked), NULL); + g_signal_connect ((gpointer) replaygain_preamp, "value_changed", + G_CALLBACK (on_replaygain_preamp_value_changed), + NULL); g_signal_connect ((gpointer) cli_add_to_playlist, "toggled", G_CALLBACK (on_cli_add_to_playlist_toggled), NULL); @@ -2523,6 +2513,33 @@ create_prefwin (void) g_signal_connect ((gpointer) resume_last_session, "toggled", G_CALLBACK (on_resume_last_session_toggled), NULL); + g_signal_connect ((gpointer) ignore_archives, "toggled", + G_CALLBACK (on_ignore_archives_toggled), + NULL); + g_signal_connect ((gpointer) dsp_add, "clicked", + G_CALLBACK (on_dsp_add_clicked), + NULL); + g_signal_connect ((gpointer) dsp_remove, "clicked", + G_CALLBACK (on_dsp_remove_clicked), + NULL); + g_signal_connect ((gpointer) dsp_configure, "clicked", + G_CALLBACK (on_dsp_configure_clicked), + NULL); + g_signal_connect ((gpointer) dsp_up, "clicked", + G_CALLBACK (on_dsp_up_clicked), + NULL); + g_signal_connect ((gpointer) dsp_down, "clicked", + G_CALLBACK (on_dsp_down_clicked), + NULL); + g_signal_connect ((gpointer) dsp_preset, "changed", + G_CALLBACK (on_dsp_preset_changed), + NULL); + g_signal_connect ((gpointer) dsp_preset_save, "clicked", + G_CALLBACK (on_dsp_preset_save_clicked), + NULL); + g_signal_connect ((gpointer) dsp_preset_load, "clicked", + G_CALLBACK (on_dsp_preset_load_clicked), + NULL); g_signal_connect ((gpointer) pref_close_send_to_tray, "clicked", G_CALLBACK (on_pref_close_send_to_tray_clicked), NULL); @@ -2538,12 +2555,21 @@ create_prefwin (void) g_signal_connect ((gpointer) hide_delete_from_disk, "toggled", G_CALLBACK (on_hide_delete_from_disk_toggled), NULL); + g_signal_connect ((gpointer) auto_name_playlist_from_folder, "toggled", + G_CALLBACK (on_auto_name_playlist_from_folder_toggled), + NULL); + g_signal_connect ((gpointer) gui_fps, "value_changed", + G_CALLBACK (on_gui_fps_value_changed), + NULL); g_signal_connect ((gpointer) titlebar_format_playing, "changed", G_CALLBACK (on_titlebar_format_playing_changed), NULL); g_signal_connect ((gpointer) titlebar_format_stopped, "changed", G_CALLBACK (on_titlebar_format_stopped_changed), NULL); + g_signal_connect ((gpointer) gui_plugin, "changed", + G_CALLBACK (on_gui_plugin_changed), + NULL); g_signal_connect ((gpointer) override_bar_colors, "toggled", G_CALLBACK (on_override_bar_colors_toggled), NULL); @@ -2568,6 +2594,9 @@ create_prefwin (void) g_signal_connect ((gpointer) tabstrip_base, "color_set", G_CALLBACK (on_tabstrip_base_color_set), NULL); + g_signal_connect ((gpointer) tabstrip_text, "color_set", + G_CALLBACK (on_tabstrip_text_color_set), + NULL); g_signal_connect ((gpointer) override_listview_colors, "toggled", G_CALLBACK (on_override_listview_colors_toggled), NULL); @@ -2607,60 +2636,15 @@ create_prefwin (void) g_signal_connect ((gpointer) proxypassword, "changed", G_CALLBACK (on_proxypassword_changed), NULL); - g_signal_connect ((gpointer) write_id3v2, "toggled", - G_CALLBACK (on_write_id3v2_toggled), - NULL); - g_signal_connect ((gpointer) write_id3v1, "toggled", - G_CALLBACK (on_write_id3v1_toggled), - NULL); - g_signal_connect ((gpointer) write_apev2, "toggled", - G_CALLBACK (on_write_apev2_toggled), - NULL); - g_signal_connect ((gpointer) strip_id3v2, "toggled", - G_CALLBACK (on_strip_id3v2_toggled), - NULL); - g_signal_connect ((gpointer) strip_id3v1, "toggled", - G_CALLBACK (on_strip_id3v1_toggled), - NULL); - g_signal_connect ((gpointer) strip_apev2, "toggled", - G_CALLBACK (on_strip_apev2_toggled), - NULL); - g_signal_connect ((gpointer) id3v2_version, "changed", - G_CALLBACK (on_id3v2_version_changed), - NULL); - g_signal_connect ((gpointer) id3v1_encoding, "changed", - G_CALLBACK (on_id3v1_encoding_changed), - NULL); - g_signal_connect ((gpointer) ape_write_id3v2, "toggled", - G_CALLBACK (on_ape_write_id3v2_toggled), - NULL); - g_signal_connect ((gpointer) ape_write_apev2, "toggled", - G_CALLBACK (on_ape_write_apev2_toggled), - NULL); - g_signal_connect ((gpointer) ape_strip_id3v2, "toggled", - G_CALLBACK (on_ape_strip_id3v2_toggled), - NULL); - g_signal_connect ((gpointer) ape_strip_apev2, "toggled", - G_CALLBACK (on_ape_strip_apev2_toggled), - NULL); - g_signal_connect ((gpointer) wv_write_apev2, "toggled", - G_CALLBACK (on_wv_write_apev2_toggled), - NULL); - g_signal_connect ((gpointer) wv_write_id3v1, "toggled", - G_CALLBACK (on_wv_write_id3v1_toggled), - NULL); - g_signal_connect ((gpointer) wv_strip_apev2, "toggled", - G_CALLBACK (on_wv_strip_apev2_toggled), - NULL); - g_signal_connect ((gpointer) wv_strip_id3v1, "toggled", - G_CALLBACK (on_wv_strip_id3v1_toggled), - NULL); g_signal_connect ((gpointer) pref_pluginlist, "cursor_changed", G_CALLBACK (on_pref_pluginlist_cursor_changed), NULL); g_signal_connect ((gpointer) configure_plugin, "clicked", G_CALLBACK (on_configure_plugin_clicked), NULL); + g_signal_connect ((gpointer) plug_copyright, "clicked", + G_CALLBACK (on_plug_copyright_clicked), + NULL); /* Store pointers to all widgets, for use by lookup_widget(). */ GLADE_HOOKUP_OBJECT_NO_REF (prefwin, prefwin, "prefwin"); @@ -2675,31 +2659,57 @@ create_prefwin (void) GLADE_HOOKUP_OBJECT (prefwin, pref_soundcard, "pref_soundcard"); GLADE_HOOKUP_OBJECT (prefwin, Sound, "Sound"); GLADE_HOOKUP_OBJECT (prefwin, vbox8, "vbox8"); - GLADE_HOOKUP_OBJECT (prefwin, pref_dynsamplerate, "pref_dynsamplerate"); - GLADE_HOOKUP_OBJECT (prefwin, hbox9, "hbox9"); - GLADE_HOOKUP_OBJECT (prefwin, label6, "label6"); - GLADE_HOOKUP_OBJECT (prefwin, pref_src_quality, "pref_src_quality"); GLADE_HOOKUP_OBJECT (prefwin, hbox10, "hbox10"); GLADE_HOOKUP_OBJECT (prefwin, label8, "label8"); GLADE_HOOKUP_OBJECT (prefwin, pref_replaygain_mode, "pref_replaygain_mode"); GLADE_HOOKUP_OBJECT (prefwin, pref_replaygain_scale, "pref_replaygain_scale"); + GLADE_HOOKUP_OBJECT (prefwin, hbox100, "hbox100"); + GLADE_HOOKUP_OBJECT (prefwin, label124, "label124"); + GLADE_HOOKUP_OBJECT (prefwin, label125, "label125"); + GLADE_HOOKUP_OBJECT (prefwin, replaygain_preamp, "replaygain_preamp"); + GLADE_HOOKUP_OBJECT (prefwin, label126, "label126"); GLADE_HOOKUP_OBJECT (prefwin, hbox66, "hbox66"); GLADE_HOOKUP_OBJECT (prefwin, cli_add_to_playlist, "cli_add_to_playlist"); GLADE_HOOKUP_OBJECT (prefwin, cli_playlist_name, "cli_playlist_name"); GLADE_HOOKUP_OBJECT (prefwin, resume_last_session, "resume_last_session"); + GLADE_HOOKUP_OBJECT (prefwin, ignore_archives, "ignore_archives"); GLADE_HOOKUP_OBJECT (prefwin, label39, "label39"); + GLADE_HOOKUP_OBJECT (prefwin, vbox29, "vbox29"); + GLADE_HOOKUP_OBJECT (prefwin, hbox80, "hbox80"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_add, "dsp_add"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_remove, "dsp_remove"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_configure, "dsp_configure"); + GLADE_HOOKUP_OBJECT (prefwin, hbox81, "hbox81"); + GLADE_HOOKUP_OBJECT (prefwin, scrolledwindow7, "scrolledwindow7"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_listview, "dsp_listview"); + GLADE_HOOKUP_OBJECT (prefwin, vbox30, "vbox30"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_up, "dsp_up"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_down, "dsp_down"); + GLADE_HOOKUP_OBJECT (prefwin, hbox86, "hbox86"); + GLADE_HOOKUP_OBJECT (prefwin, label114, "label114"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_preset, "dsp_preset"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_preset_save, "dsp_preset_save"); + GLADE_HOOKUP_OBJECT (prefwin, dsp_preset_load, "dsp_preset_load"); + GLADE_HOOKUP_OBJECT (prefwin, label110, "label110"); GLADE_HOOKUP_OBJECT (prefwin, vbox9, "vbox9"); GLADE_HOOKUP_OBJECT (prefwin, pref_close_send_to_tray, "pref_close_send_to_tray"); GLADE_HOOKUP_OBJECT (prefwin, mmb_delete_playlist, "mmb_delete_playlist"); GLADE_HOOKUP_OBJECT (prefwin, hide_tray_icon, "hide_tray_icon"); GLADE_HOOKUP_OBJECT (prefwin, embolden_current, "embolden_current"); GLADE_HOOKUP_OBJECT (prefwin, hide_delete_from_disk, "hide_delete_from_disk"); + GLADE_HOOKUP_OBJECT (prefwin, auto_name_playlist_from_folder, "auto_name_playlist_from_folder"); + GLADE_HOOKUP_OBJECT (prefwin, hbox102, "hbox102"); + GLADE_HOOKUP_OBJECT (prefwin, label129, "label129"); + GLADE_HOOKUP_OBJECT (prefwin, gui_fps, "gui_fps"); GLADE_HOOKUP_OBJECT (prefwin, hbox64, "hbox64"); GLADE_HOOKUP_OBJECT (prefwin, label101, "label101"); GLADE_HOOKUP_OBJECT (prefwin, titlebar_format_playing, "titlebar_format_playing"); GLADE_HOOKUP_OBJECT (prefwin, hbox65, "hbox65"); GLADE_HOOKUP_OBJECT (prefwin, label102, "label102"); GLADE_HOOKUP_OBJECT (prefwin, titlebar_format_stopped, "titlebar_format_stopped"); + GLADE_HOOKUP_OBJECT (prefwin, hbox101, "hbox101"); + GLADE_HOOKUP_OBJECT (prefwin, label128, "label128"); + GLADE_HOOKUP_OBJECT (prefwin, gui_plugin, "gui_plugin"); GLADE_HOOKUP_OBJECT (prefwin, label2, "label2"); GLADE_HOOKUP_OBJECT (prefwin, notebook4, "notebook4"); GLADE_HOOKUP_OBJECT (prefwin, vbox21, "vbox21"); @@ -2721,6 +2731,8 @@ create_prefwin (void) GLADE_HOOKUP_OBJECT (prefwin, tabstrip_dark, "tabstrip_dark"); GLADE_HOOKUP_OBJECT (prefwin, tabstrip_base, "tabstrip_base"); GLADE_HOOKUP_OBJECT (prefwin, label76, "label76"); + GLADE_HOOKUP_OBJECT (prefwin, label127, "label127"); + GLADE_HOOKUP_OBJECT (prefwin, tabstrip_text, "tabstrip_text"); GLADE_HOOKUP_OBJECT (prefwin, label74, "label74"); GLADE_HOOKUP_OBJECT (prefwin, vbox23, "vbox23"); GLADE_HOOKUP_OBJECT (prefwin, override_listview_colors, "override_listview_colors"); @@ -2757,88 +2769,39 @@ create_prefwin (void) GLADE_HOOKUP_OBJECT (prefwin, label98, "label98"); GLADE_HOOKUP_OBJECT (prefwin, proxypassword, "proxypassword"); GLADE_HOOKUP_OBJECT (prefwin, label16, "label16"); - GLADE_HOOKUP_OBJECT (prefwin, vbox18, "vbox18"); - GLADE_HOOKUP_OBJECT (prefwin, frame5, "frame5"); - GLADE_HOOKUP_OBJECT (prefwin, alignment3, "alignment3"); - GLADE_HOOKUP_OBJECT (prefwin, vbox19, "vbox19"); - GLADE_HOOKUP_OBJECT (prefwin, hbox38, "hbox38"); - GLADE_HOOKUP_OBJECT (prefwin, write_id3v2, "write_id3v2"); - GLADE_HOOKUP_OBJECT (prefwin, write_id3v1, "write_id3v1"); - GLADE_HOOKUP_OBJECT (prefwin, write_apev2, "write_apev2"); - GLADE_HOOKUP_OBJECT (prefwin, hbox40, "hbox40"); - GLADE_HOOKUP_OBJECT (prefwin, strip_id3v2, "strip_id3v2"); - GLADE_HOOKUP_OBJECT (prefwin, strip_id3v1, "strip_id3v1"); - GLADE_HOOKUP_OBJECT (prefwin, strip_apev2, "strip_apev2"); - GLADE_HOOKUP_OBJECT (prefwin, hbox36, "hbox36"); - GLADE_HOOKUP_OBJECT (prefwin, label69, "label69"); - GLADE_HOOKUP_OBJECT (prefwin, id3v2_version, "id3v2_version"); - GLADE_HOOKUP_OBJECT (prefwin, hbox39, "hbox39"); - GLADE_HOOKUP_OBJECT (prefwin, label71, "label71"); - GLADE_HOOKUP_OBJECT (prefwin, id3v1_encoding, "id3v1_encoding"); - GLADE_HOOKUP_OBJECT (prefwin, label68, "label68"); - GLADE_HOOKUP_OBJECT (prefwin, hbox41, "hbox41"); - GLADE_HOOKUP_OBJECT (prefwin, frame6, "frame6"); - GLADE_HOOKUP_OBJECT (prefwin, alignment4, "alignment4"); - GLADE_HOOKUP_OBJECT (prefwin, vbox20, "vbox20"); - GLADE_HOOKUP_OBJECT (prefwin, hbox37, "hbox37"); - GLADE_HOOKUP_OBJECT (prefwin, ape_write_id3v2, "ape_write_id3v2"); - GLADE_HOOKUP_OBJECT (prefwin, ape_write_apev2, "ape_write_apev2"); - GLADE_HOOKUP_OBJECT (prefwin, hbox45, "hbox45"); - GLADE_HOOKUP_OBJECT (prefwin, ape_strip_id3v2, "ape_strip_id3v2"); - GLADE_HOOKUP_OBJECT (prefwin, ape_strip_apev2, "ape_strip_apev2"); - GLADE_HOOKUP_OBJECT (prefwin, label70, "label70"); - GLADE_HOOKUP_OBJECT (prefwin, frame7, "frame7"); - GLADE_HOOKUP_OBJECT (prefwin, alignment5, "alignment5"); - GLADE_HOOKUP_OBJECT (prefwin, vbox_wv, "vbox_wv"); - GLADE_HOOKUP_OBJECT (prefwin, hbox44, "hbox44"); - GLADE_HOOKUP_OBJECT (prefwin, wv_write_apev2, "wv_write_apev2"); - GLADE_HOOKUP_OBJECT (prefwin, wv_write_id3v1, "wv_write_id3v1"); - GLADE_HOOKUP_OBJECT (prefwin, hbox43, "hbox43"); - GLADE_HOOKUP_OBJECT (prefwin, wv_strip_apev2, "wv_strip_apev2"); - GLADE_HOOKUP_OBJECT (prefwin, wv_strip_id3v1, "wv_strip_id3v1"); - GLADE_HOOKUP_OBJECT (prefwin, label79, "label79"); - GLADE_HOOKUP_OBJECT (prefwin, label67, "label67"); GLADE_HOOKUP_OBJECT (prefwin, hpaned1, "hpaned1"); GLADE_HOOKUP_OBJECT (prefwin, scrolledwindow2, "scrolledwindow2"); GLADE_HOOKUP_OBJECT (prefwin, pref_pluginlist, "pref_pluginlist"); GLADE_HOOKUP_OBJECT (prefwin, vbox12, "vbox12"); - GLADE_HOOKUP_OBJECT (prefwin, hbox16, "hbox16"); - GLADE_HOOKUP_OBJECT (prefwin, label11, "label11"); - GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_descr, "pref_plugin_descr"); - GLADE_HOOKUP_OBJECT (prefwin, hbox17, "hbox17"); - GLADE_HOOKUP_OBJECT (prefwin, label12, "label12"); - GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_author, "pref_plugin_author"); - GLADE_HOOKUP_OBJECT (prefwin, hbox18, "hbox18"); - GLADE_HOOKUP_OBJECT (prefwin, label13, "label13"); - GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_email, "pref_plugin_email"); - GLADE_HOOKUP_OBJECT (prefwin, hbox19, "hbox19"); - GLADE_HOOKUP_OBJECT (prefwin, label14, "label14"); - GLADE_HOOKUP_OBJECT (prefwin, pref_plugin_website, "pref_plugin_website"); + GLADE_HOOKUP_OBJECT (prefwin, scrolledwindow8, "scrolledwindow8"); + GLADE_HOOKUP_OBJECT (prefwin, plug_description, "plug_description"); GLADE_HOOKUP_OBJECT (prefwin, hbox20, "hbox20"); GLADE_HOOKUP_OBJECT (prefwin, configure_plugin, "configure_plugin"); GLADE_HOOKUP_OBJECT (prefwin, alignment15, "alignment15"); GLADE_HOOKUP_OBJECT (prefwin, hbox56, "hbox56"); GLADE_HOOKUP_OBJECT (prefwin, image394, "image394"); GLADE_HOOKUP_OBJECT (prefwin, label92, "label92"); + GLADE_HOOKUP_OBJECT (prefwin, plug_copyright, "plug_copyright"); + GLADE_HOOKUP_OBJECT (prefwin, alignment20, "alignment20"); + GLADE_HOOKUP_OBJECT (prefwin, hbox88, "hbox88"); + GLADE_HOOKUP_OBJECT (prefwin, image521, "image521"); + GLADE_HOOKUP_OBJECT (prefwin, label117, "label117"); + GLADE_HOOKUP_OBJECT (prefwin, weblink, "weblink"); GLADE_HOOKUP_OBJECT (prefwin, label3, "label3"); GLADE_HOOKUP_OBJECT_NO_REF (prefwin, dialog_action_area2, "dialog_action_area2"); GLADE_HOOKUP_OBJECT (prefwin, closebutton1, "closebutton1"); - GLADE_HOOKUP_OBJECT (prefwin, alignment14, "alignment14"); - GLADE_HOOKUP_OBJECT (prefwin, hbox55, "hbox55"); - GLADE_HOOKUP_OBJECT (prefwin, image393, "image393"); - GLADE_HOOKUP_OBJECT (prefwin, label91, "label91"); return prefwin; } GtkWidget* -create_editplaylistdlg (void) +create_entrydialog (void) { - GtkWidget *editplaylistdlg; + GtkWidget *entrydialog; GtkWidget *dialog_vbox3; GtkWidget *vbox15; GtkWidget *hbox33; - GtkWidget *label40; + GtkWidget *title_label; GtkWidget *title; GtkWidget *dialog_action_area3; GtkWidget *cancelbutton2; @@ -2852,13 +2815,13 @@ create_editplaylistdlg (void) GtkWidget *image395; GtkWidget *label93; - editplaylistdlg = gtk_dialog_new (); - gtk_container_set_border_width (GTK_CONTAINER (editplaylistdlg), 8); - gtk_window_set_title (GTK_WINDOW (editplaylistdlg), _("editplaylistdlg")); - gtk_window_set_destroy_with_parent (GTK_WINDOW (editplaylistdlg), TRUE); - gtk_window_set_type_hint (GTK_WINDOW (editplaylistdlg), GDK_WINDOW_TYPE_HINT_DIALOG); + entrydialog = gtk_dialog_new (); + gtk_container_set_border_width (GTK_CONTAINER (entrydialog), 8); + gtk_window_set_title (GTK_WINDOW (entrydialog), "EntryDialog"); + gtk_window_set_destroy_with_parent (GTK_WINDOW (entrydialog), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (entrydialog), GDK_WINDOW_TYPE_HINT_DIALOG); - dialog_vbox3 = GTK_DIALOG (editplaylistdlg)->vbox; + dialog_vbox3 = GTK_DIALOG (entrydialog)->vbox; gtk_widget_show (dialog_vbox3); vbox15 = gtk_vbox_new (FALSE, 0); @@ -2870,9 +2833,9 @@ create_editplaylistdlg (void) gtk_widget_show (hbox33); gtk_box_pack_start (GTK_BOX (vbox15), hbox33, TRUE, TRUE, 0); - label40 = gtk_label_new (_("Title:")); - gtk_widget_show (label40); - gtk_box_pack_start (GTK_BOX (hbox33), label40, FALSE, FALSE, 0); + title_label = gtk_label_new (_("Title:")); + gtk_widget_show (title_label); + gtk_box_pack_start (GTK_BOX (hbox33), title_label, FALSE, FALSE, 0); title = gtk_entry_new (); gtk_widget_show (title); @@ -2880,13 +2843,13 @@ create_editplaylistdlg (void) gtk_entry_set_invisible_char (GTK_ENTRY (title), 8226); gtk_entry_set_activates_default (GTK_ENTRY (title), TRUE); - dialog_action_area3 = GTK_DIALOG (editplaylistdlg)->action_area; + dialog_action_area3 = GTK_DIALOG (entrydialog)->action_area; gtk_widget_show (dialog_action_area3); gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area3), GTK_BUTTONBOX_END); cancelbutton2 = gtk_button_new (); gtk_widget_show (cancelbutton2); - gtk_dialog_add_action_widget (GTK_DIALOG (editplaylistdlg), cancelbutton2, GTK_RESPONSE_CANCEL); + gtk_dialog_add_action_widget (GTK_DIALOG (entrydialog), cancelbutton2, GTK_RESPONSE_CANCEL); GTK_WIDGET_SET_FLAGS (cancelbutton2, GTK_CAN_DEFAULT); alignment17 = gtk_alignment_new (0.5, 0.5, 0, 0); @@ -2907,7 +2870,7 @@ create_editplaylistdlg (void) okbutton2 = gtk_button_new (); gtk_widget_show (okbutton2); - gtk_dialog_add_action_widget (GTK_DIALOG (editplaylistdlg), okbutton2, GTK_RESPONSE_OK); + gtk_dialog_add_action_widget (GTK_DIALOG (entrydialog), okbutton2, GTK_RESPONSE_OK); GTK_WIDGET_SET_FLAGS (okbutton2, GTK_CAN_DEFAULT); alignment16 = gtk_alignment_new (0.5, 0.5, 0, 0); @@ -2927,25 +2890,25 @@ create_editplaylistdlg (void) gtk_box_pack_start (GTK_BOX (hbox57), label93, FALSE, FALSE, 0); /* Store pointers to all widgets, for use by lookup_widget(). */ - GLADE_HOOKUP_OBJECT_NO_REF (editplaylistdlg, editplaylistdlg, "editplaylistdlg"); - GLADE_HOOKUP_OBJECT_NO_REF (editplaylistdlg, dialog_vbox3, "dialog_vbox3"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, vbox15, "vbox15"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, hbox33, "hbox33"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, label40, "label40"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, title, "title"); - GLADE_HOOKUP_OBJECT_NO_REF (editplaylistdlg, dialog_action_area3, "dialog_action_area3"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, cancelbutton2, "cancelbutton2"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, alignment17, "alignment17"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, hbox58, "hbox58"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, image396, "image396"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, label94, "label94"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, okbutton2, "okbutton2"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, alignment16, "alignment16"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, hbox57, "hbox57"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, image395, "image395"); - GLADE_HOOKUP_OBJECT (editplaylistdlg, label93, "label93"); - - return editplaylistdlg; + GLADE_HOOKUP_OBJECT_NO_REF (entrydialog, entrydialog, "entrydialog"); + GLADE_HOOKUP_OBJECT_NO_REF (entrydialog, dialog_vbox3, "dialog_vbox3"); + GLADE_HOOKUP_OBJECT (entrydialog, vbox15, "vbox15"); + GLADE_HOOKUP_OBJECT (entrydialog, hbox33, "hbox33"); + GLADE_HOOKUP_OBJECT (entrydialog, title_label, "title_label"); + GLADE_HOOKUP_OBJECT (entrydialog, title, "title"); + GLADE_HOOKUP_OBJECT_NO_REF (entrydialog, dialog_action_area3, "dialog_action_area3"); + GLADE_HOOKUP_OBJECT (entrydialog, cancelbutton2, "cancelbutton2"); + GLADE_HOOKUP_OBJECT (entrydialog, alignment17, "alignment17"); + GLADE_HOOKUP_OBJECT (entrydialog, hbox58, "hbox58"); + GLADE_HOOKUP_OBJECT (entrydialog, image396, "image396"); + GLADE_HOOKUP_OBJECT (entrydialog, label94, "label94"); + GLADE_HOOKUP_OBJECT (entrydialog, okbutton2, "okbutton2"); + GLADE_HOOKUP_OBJECT (entrydialog, alignment16, "alignment16"); + GLADE_HOOKUP_OBJECT (entrydialog, hbox57, "hbox57"); + GLADE_HOOKUP_OBJECT (entrydialog, image395, "image395"); + GLADE_HOOKUP_OBJECT (entrydialog, label93, "label93"); + + return entrydialog; } GtkWidget* @@ -3067,8 +3030,9 @@ create_groupbydlg (void) GtkWidget *vbox25; GtkWidget *hbox46; GtkWidget *label81; + GtkWidget *hbox75; GtkWidget *format; - GtkWidget *label82; + GtkWidget *custom1; GtkWidget *dialog_action_area4; GtkWidget *cancelbutton4; GtkWidget *alignment7; @@ -3101,19 +3065,21 @@ create_groupbydlg (void) gtk_widget_show (label81); gtk_box_pack_start (GTK_BOX (hbox46), label81, FALSE, FALSE, 0); + hbox75 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox75); + gtk_box_pack_start (GTK_BOX (hbox46), hbox75, TRUE, TRUE, 0); + format = gtk_entry_new (); gtk_widget_show (format); - gtk_box_pack_start (GTK_BOX (hbox46), format, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (hbox75), format, TRUE, TRUE, 0); gtk_entry_set_invisible_char (GTK_ENTRY (format), 9679); gtk_entry_set_activates_default (GTK_ENTRY (format), TRUE); - label82 = gtk_label_new (_("Format conversions (start with %):\n [a]rtist, [t]itle, al[b]um, [B]and, [C]omposer\n track[n]umber, [N]totaltracks,\n [l]ength, [y]ear, [g]enre, [c]omment,\n copy[r]ight, [f]ilename, [T]ags\nExample: %a - %t [%l]")); - gtk_widget_show (label82); - gtk_box_pack_start (GTK_BOX (vbox25), label82, FALSE, FALSE, 0); - GTK_WIDGET_SET_FLAGS (label82, GTK_CAN_FOCUS); - gtk_label_set_use_markup (GTK_LABEL (label82), TRUE); - gtk_label_set_selectable (GTK_LABEL (label82), TRUE); - gtk_misc_set_alignment (GTK_MISC (label82), 0.1, 0.5); + custom1 = title_formatting_help_link_create ("custom1", "", "", 0, 0); + gtk_widget_show (custom1); + gtk_box_pack_start (GTK_BOX (hbox75), custom1, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (custom1, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (custom1, GTK_CAN_DEFAULT); dialog_action_area4 = GTK_DIALOG (groupbydlg)->action_area; gtk_widget_show (dialog_action_area4); @@ -3167,8 +3133,9 @@ create_groupbydlg (void) GLADE_HOOKUP_OBJECT (groupbydlg, vbox25, "vbox25"); GLADE_HOOKUP_OBJECT (groupbydlg, hbox46, "hbox46"); GLADE_HOOKUP_OBJECT (groupbydlg, label81, "label81"); + GLADE_HOOKUP_OBJECT (groupbydlg, hbox75, "hbox75"); GLADE_HOOKUP_OBJECT (groupbydlg, format, "format"); - GLADE_HOOKUP_OBJECT (groupbydlg, label82, "label82"); + GLADE_HOOKUP_OBJECT (groupbydlg, custom1, "custom1"); GLADE_HOOKUP_OBJECT_NO_REF (groupbydlg, dialog_action_area4, "dialog_action_area4"); GLADE_HOOKUP_OBJECT (groupbydlg, cancelbutton4, "cancelbutton4"); GLADE_HOOKUP_OBJECT (groupbydlg, alignment7, "alignment7"); @@ -3184,3 +3151,514 @@ create_groupbydlg (void) return groupbydlg; } +GtkWidget* +create_sortbydlg (void) +{ + GtkWidget *sortbydlg; + GtkWidget *dialog_vbox8; + GtkWidget *vbox28; + GtkWidget *hbox76; + GtkWidget *label108; + GtkWidget *hbox77; + GtkWidget *sortfmt; + GtkWidget *custom3; + GtkWidget *hbox78; + GtkWidget *label109; + GtkWidget *sortorder; + GtkWidget *dialog_action_area7; + GtkWidget *cancelbutton5; + GtkWidget *okbutton5; + + sortbydlg = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (sortbydlg), _("Sort by...")); + gtk_window_set_modal (GTK_WINDOW (sortbydlg), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (sortbydlg), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox8 = GTK_DIALOG (sortbydlg)->vbox; + gtk_widget_show (dialog_vbox8); + + vbox28 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox28); + gtk_box_pack_start (GTK_BOX (dialog_vbox8), vbox28, FALSE, FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox28), 12); + + hbox76 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox76); + gtk_box_pack_start (GTK_BOX (vbox28), hbox76, FALSE, TRUE, 0); + + label108 = gtk_label_new (_("Format")); + gtk_widget_show (label108); + gtk_box_pack_start (GTK_BOX (hbox76), label108, FALSE, FALSE, 0); + + hbox77 = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox77); + gtk_box_pack_start (GTK_BOX (hbox76), hbox77, TRUE, TRUE, 0); + + sortfmt = gtk_entry_new (); + gtk_widget_show (sortfmt); + gtk_box_pack_start (GTK_BOX (hbox77), sortfmt, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (sortfmt), 9679); + + custom3 = title_formatting_help_link_create ("custom3", "", "", 0, 0); + gtk_widget_show (custom3); + gtk_box_pack_start (GTK_BOX (hbox77), custom3, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (custom3, GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS (custom3, GTK_CAN_DEFAULT); + + hbox78 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox78); + gtk_box_pack_start (GTK_BOX (vbox28), hbox78, TRUE, TRUE, 0); + + label109 = gtk_label_new (_("Order")); + gtk_widget_show (label109); + gtk_box_pack_start (GTK_BOX (hbox78), label109, FALSE, FALSE, 0); + + sortorder = gtk_combo_box_new_text (); + gtk_widget_show (sortorder); + gtk_box_pack_start (GTK_BOX (hbox78), sortorder, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (sortorder), _("Ascending")); + gtk_combo_box_append_text (GTK_COMBO_BOX (sortorder), _("Descending")); + + dialog_action_area7 = GTK_DIALOG (sortbydlg)->action_area; + gtk_widget_show (dialog_action_area7); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area7), GTK_BUTTONBOX_END); + + cancelbutton5 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancelbutton5); + gtk_dialog_add_action_widget (GTK_DIALOG (sortbydlg), cancelbutton5, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (cancelbutton5, GTK_CAN_DEFAULT); + + okbutton5 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (okbutton5); + gtk_dialog_add_action_widget (GTK_DIALOG (sortbydlg), okbutton5, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (okbutton5, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) sortfmt, "activate", + G_CALLBACK (on_sortfmt_activate), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (sortbydlg, sortbydlg, "sortbydlg"); + GLADE_HOOKUP_OBJECT_NO_REF (sortbydlg, dialog_vbox8, "dialog_vbox8"); + GLADE_HOOKUP_OBJECT (sortbydlg, vbox28, "vbox28"); + GLADE_HOOKUP_OBJECT (sortbydlg, hbox76, "hbox76"); + GLADE_HOOKUP_OBJECT (sortbydlg, label108, "label108"); + GLADE_HOOKUP_OBJECT (sortbydlg, hbox77, "hbox77"); + GLADE_HOOKUP_OBJECT (sortbydlg, sortfmt, "sortfmt"); + GLADE_HOOKUP_OBJECT (sortbydlg, custom3, "custom3"); + GLADE_HOOKUP_OBJECT (sortbydlg, hbox78, "hbox78"); + GLADE_HOOKUP_OBJECT (sortbydlg, label109, "label109"); + GLADE_HOOKUP_OBJECT (sortbydlg, sortorder, "sortorder"); + GLADE_HOOKUP_OBJECT_NO_REF (sortbydlg, dialog_action_area7, "dialog_action_area7"); + GLADE_HOOKUP_OBJECT (sortbydlg, cancelbutton5, "cancelbutton5"); + GLADE_HOOKUP_OBJECT (sortbydlg, okbutton5, "okbutton5"); + + return sortbydlg; +} + +GtkWidget* +create_select_dsp_plugin (void) +{ + GtkWidget *select_dsp_plugin; + GtkWidget *dialog_vbox10; + GtkWidget *vbox31; + GtkWidget *hbox85; + GtkWidget *label113; + GtkWidget *plugin; + GtkWidget *dialog_action_area9; + GtkWidget *cancelbutton7; + GtkWidget *okbutton7; + + select_dsp_plugin = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (select_dsp_plugin), _("Select DSP Plugin")); + gtk_window_set_modal (GTK_WINDOW (select_dsp_plugin), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (select_dsp_plugin), GDK_WINDOW_TYPE_HINT_DIALOG); + + dialog_vbox10 = GTK_DIALOG (select_dsp_plugin)->vbox; + gtk_widget_show (dialog_vbox10); + + vbox31 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox31); + gtk_box_pack_start (GTK_BOX (dialog_vbox10), vbox31, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox31), 12); + + hbox85 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox85); + gtk_box_pack_start (GTK_BOX (vbox31), hbox85, FALSE, FALSE, 0); + + label113 = gtk_label_new (_("Plugin")); + gtk_widget_show (label113); + gtk_box_pack_start (GTK_BOX (hbox85), label113, FALSE, FALSE, 0); + + plugin = gtk_combo_box_new_text (); + gtk_widget_show (plugin); + gtk_box_pack_start (GTK_BOX (hbox85), plugin, TRUE, TRUE, 0); + + dialog_action_area9 = GTK_DIALOG (select_dsp_plugin)->action_area; + gtk_widget_show (dialog_action_area9); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area9), GTK_BUTTONBOX_END); + + cancelbutton7 = gtk_button_new_from_stock ("gtk-cancel"); + gtk_widget_show (cancelbutton7); + gtk_dialog_add_action_widget (GTK_DIALOG (select_dsp_plugin), cancelbutton7, GTK_RESPONSE_CANCEL); + GTK_WIDGET_SET_FLAGS (cancelbutton7, GTK_CAN_DEFAULT); + + okbutton7 = gtk_button_new_from_stock ("gtk-ok"); + gtk_widget_show (okbutton7); + gtk_dialog_add_action_widget (GTK_DIALOG (select_dsp_plugin), okbutton7, GTK_RESPONSE_OK); + GTK_WIDGET_SET_FLAGS (okbutton7, GTK_CAN_DEFAULT); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (select_dsp_plugin, select_dsp_plugin, "select_dsp_plugin"); + GLADE_HOOKUP_OBJECT_NO_REF (select_dsp_plugin, dialog_vbox10, "dialog_vbox10"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, vbox31, "vbox31"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, hbox85, "hbox85"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, label113, "label113"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, plugin, "plugin"); + GLADE_HOOKUP_OBJECT_NO_REF (select_dsp_plugin, dialog_action_area9, "dialog_action_area9"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, cancelbutton7, "cancelbutton7"); + GLADE_HOOKUP_OBJECT (select_dsp_plugin, okbutton7, "okbutton7"); + + return select_dsp_plugin; +} + +GtkWidget* +create_tagwritersettings (void) +{ + GtkWidget *tagwritersettings; + GtkWidget *dialog_vbox11; + GtkWidget *vbox32; + GtkWidget *frame8; + GtkWidget *alignment21; + GtkWidget *vbox33; + GtkWidget *hbox89; + GtkWidget *write_id3v2; + GtkWidget *write_id3v1; + GtkWidget *write_apev2; + GtkWidget *hbox90; + GtkWidget *strip_id3v2; + GtkWidget *strip_id3v1; + GtkWidget *strip_apev2; + GtkWidget *hbox91; + GtkWidget *label118; + GtkWidget *id3v2_version; + GtkWidget *hbox92; + GtkWidget *label119; + GtkWidget *id3v1_encoding; + GtkWidget *label120; + GtkWidget *hbox93; + GtkWidget *frame9; + GtkWidget *alignment22; + GtkWidget *vbox34; + GtkWidget *hbox94; + GtkWidget *ape_write_id3v2; + GtkWidget *ape_write_apev2; + GtkWidget *hbox95; + GtkWidget *ape_strip_id3v2; + GtkWidget *ape_strip_apev2; + GtkWidget *label121; + GtkWidget *frame10; + GtkWidget *alignment23; + GtkWidget *vbox35; + GtkWidget *hbox96; + GtkWidget *wv_write_apev2; + GtkWidget *wv_write_id3v1; + GtkWidget *hbox97; + GtkWidget *wv_strip_apev2; + GtkWidget *wv_strip_id3v1; + GtkWidget *label122; + GtkWidget *dialog_action_area10; + GtkWidget *closebutton2; + + tagwritersettings = gtk_dialog_new (); + gtk_container_set_border_width (GTK_CONTAINER (tagwritersettings), 12); + gtk_window_set_title (GTK_WINDOW (tagwritersettings), _("Tag Writer Settings")); + gtk_window_set_modal (GTK_WINDOW (tagwritersettings), TRUE); + gtk_window_set_type_hint (GTK_WINDOW (tagwritersettings), GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_dialog_set_has_separator (GTK_DIALOG (tagwritersettings), FALSE); + + dialog_vbox11 = GTK_DIALOG (tagwritersettings)->vbox; + gtk_widget_show (dialog_vbox11); + + vbox32 = gtk_vbox_new (FALSE, 0); + gtk_widget_show (vbox32); + gtk_box_pack_start (GTK_BOX (dialog_vbox11), vbox32, TRUE, TRUE, 0); + + frame8 = gtk_frame_new (NULL); + gtk_widget_show (frame8); + gtk_box_pack_start (GTK_BOX (vbox32), frame8, FALSE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame8), GTK_SHADOW_NONE); + + alignment21 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment21); + gtk_container_add (GTK_CONTAINER (frame8), alignment21); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment21), 0, 0, 12, 0); + + vbox33 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox33); + gtk_container_add (GTK_CONTAINER (alignment21), vbox33); + gtk_container_set_border_width (GTK_CONTAINER (vbox33), 12); + + hbox89 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox89); + gtk_box_pack_start (GTK_BOX (vbox33), hbox89, FALSE, FALSE, 0); + + write_id3v2 = gtk_check_button_new_with_mnemonic (_("Write ID3v2")); + gtk_widget_show (write_id3v2); + gtk_box_pack_start (GTK_BOX (hbox89), write_id3v2, FALSE, FALSE, 0); + + write_id3v1 = gtk_check_button_new_with_mnemonic (_("Write ID3v1")); + gtk_widget_show (write_id3v1); + gtk_box_pack_start (GTK_BOX (hbox89), write_id3v1, FALSE, FALSE, 0); + + write_apev2 = gtk_check_button_new_with_mnemonic (_("Write APEv2")); + gtk_widget_show (write_apev2); + gtk_box_pack_start (GTK_BOX (hbox89), write_apev2, FALSE, FALSE, 0); + + hbox90 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox90); + gtk_box_pack_start (GTK_BOX (vbox33), hbox90, FALSE, FALSE, 0); + + strip_id3v2 = gtk_check_button_new_with_mnemonic (_("Strip ID3v2")); + gtk_widget_show (strip_id3v2); + gtk_box_pack_start (GTK_BOX (hbox90), strip_id3v2, FALSE, FALSE, 0); + + strip_id3v1 = gtk_check_button_new_with_mnemonic (_("Strip ID3v1")); + gtk_widget_show (strip_id3v1); + gtk_box_pack_start (GTK_BOX (hbox90), strip_id3v1, FALSE, FALSE, 0); + + strip_apev2 = gtk_check_button_new_with_mnemonic (_("Strip APEv2")); + gtk_widget_show (strip_apev2); + gtk_box_pack_start (GTK_BOX (hbox90), strip_apev2, FALSE, FALSE, 0); + + hbox91 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox91); + gtk_box_pack_start (GTK_BOX (vbox33), hbox91, TRUE, TRUE, 0); + + label118 = gtk_label_new (_("ID3v2 version")); + gtk_widget_show (label118); + gtk_box_pack_start (GTK_BOX (hbox91), label118, FALSE, FALSE, 0); + + id3v2_version = gtk_combo_box_new_text (); + gtk_widget_show (id3v2_version); + gtk_box_pack_start (GTK_BOX (hbox91), id3v2_version, TRUE, TRUE, 0); + gtk_combo_box_append_text (GTK_COMBO_BOX (id3v2_version), _("2.3 (Recommended)")); + gtk_combo_box_append_text (GTK_COMBO_BOX (id3v2_version), _("2.4")); + + hbox92 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox92); + gtk_box_pack_start (GTK_BOX (vbox33), hbox92, TRUE, TRUE, 0); + + label119 = gtk_label_new (_("ID3v1 character encoding (default is iso8859-1)")); + gtk_widget_show (label119); + gtk_box_pack_start (GTK_BOX (hbox92), label119, FALSE, FALSE, 0); + + id3v1_encoding = gtk_entry_new (); + gtk_widget_show (id3v1_encoding); + gtk_box_pack_start (GTK_BOX (hbox92), id3v1_encoding, TRUE, TRUE, 0); + gtk_entry_set_invisible_char (GTK_ENTRY (id3v1_encoding), 9679); + + label120 = gtk_label_new ("<b>MP3</b>"); + gtk_widget_show (label120); + gtk_frame_set_label_widget (GTK_FRAME (frame8), label120); + gtk_label_set_use_markup (GTK_LABEL (label120), TRUE); + + hbox93 = gtk_hbox_new (TRUE, 0); + gtk_widget_show (hbox93); + gtk_box_pack_start (GTK_BOX (vbox32), hbox93, FALSE, TRUE, 0); + + frame9 = gtk_frame_new (NULL); + gtk_widget_show (frame9); + gtk_box_pack_start (GTK_BOX (hbox93), frame9, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame9), GTK_SHADOW_NONE); + + alignment22 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment22); + gtk_container_add (GTK_CONTAINER (frame9), alignment22); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment22), 0, 0, 12, 0); + + vbox34 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox34); + gtk_container_add (GTK_CONTAINER (alignment22), vbox34); + gtk_container_set_border_width (GTK_CONTAINER (vbox34), 12); + + hbox94 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox94); + gtk_box_pack_start (GTK_BOX (vbox34), hbox94, TRUE, TRUE, 0); + + ape_write_id3v2 = gtk_check_button_new_with_mnemonic (_("Write ID3v2.4")); + gtk_widget_show (ape_write_id3v2); + gtk_box_pack_start (GTK_BOX (hbox94), ape_write_id3v2, FALSE, FALSE, 0); + + ape_write_apev2 = gtk_check_button_new_with_mnemonic (_("Write APEv2")); + gtk_widget_show (ape_write_apev2); + gtk_box_pack_start (GTK_BOX (hbox94), ape_write_apev2, FALSE, FALSE, 0); + + hbox95 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox95); + gtk_box_pack_start (GTK_BOX (vbox34), hbox95, TRUE, TRUE, 0); + + ape_strip_id3v2 = gtk_check_button_new_with_mnemonic (_("Strip ID3v2")); + gtk_widget_show (ape_strip_id3v2); + gtk_box_pack_start (GTK_BOX (hbox95), ape_strip_id3v2, FALSE, FALSE, 0); + + ape_strip_apev2 = gtk_check_button_new_with_mnemonic (_("Strip APEv2")); + gtk_widget_show (ape_strip_apev2); + gtk_box_pack_start (GTK_BOX (hbox95), ape_strip_apev2, FALSE, FALSE, 0); + + label121 = gtk_label_new ("<b>APE</b>"); + gtk_widget_show (label121); + gtk_frame_set_label_widget (GTK_FRAME (frame9), label121); + gtk_label_set_use_markup (GTK_LABEL (label121), TRUE); + + frame10 = gtk_frame_new (NULL); + gtk_widget_show (frame10); + gtk_box_pack_start (GTK_BOX (hbox93), frame10, TRUE, TRUE, 0); + gtk_frame_set_shadow_type (GTK_FRAME (frame10), GTK_SHADOW_NONE); + + alignment23 = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_widget_show (alignment23); + gtk_container_add (GTK_CONTAINER (frame10), alignment23); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment23), 0, 0, 12, 0); + + vbox35 = gtk_vbox_new (FALSE, 8); + gtk_widget_show (vbox35); + gtk_container_add (GTK_CONTAINER (alignment23), vbox35); + gtk_container_set_border_width (GTK_CONTAINER (vbox35), 12); + + hbox96 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox96); + gtk_box_pack_start (GTK_BOX (vbox35), hbox96, FALSE, FALSE, 0); + + wv_write_apev2 = gtk_check_button_new_with_mnemonic (_("Write APEv2")); + gtk_widget_show (wv_write_apev2); + gtk_box_pack_start (GTK_BOX (hbox96), wv_write_apev2, FALSE, FALSE, 0); + + wv_write_id3v1 = gtk_check_button_new_with_mnemonic (_("Write ID3v1")); + gtk_widget_show (wv_write_id3v1); + gtk_box_pack_start (GTK_BOX (hbox96), wv_write_id3v1, FALSE, FALSE, 0); + + hbox97 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox97); + gtk_box_pack_start (GTK_BOX (vbox35), hbox97, FALSE, FALSE, 0); + + wv_strip_apev2 = gtk_check_button_new_with_mnemonic (_("Strip APEv2")); + gtk_widget_show (wv_strip_apev2); + gtk_box_pack_start (GTK_BOX (hbox97), wv_strip_apev2, FALSE, FALSE, 0); + + wv_strip_id3v1 = gtk_check_button_new_with_mnemonic (_("Strip ID3v1")); + gtk_widget_show (wv_strip_id3v1); + gtk_box_pack_start (GTK_BOX (hbox97), wv_strip_id3v1, FALSE, FALSE, 0); + + label122 = gtk_label_new ("<b>WavPack</b>"); + gtk_widget_show (label122); + gtk_frame_set_label_widget (GTK_FRAME (frame10), label122); + gtk_label_set_use_markup (GTK_LABEL (label122), TRUE); + + dialog_action_area10 = GTK_DIALOG (tagwritersettings)->action_area; + gtk_widget_show (dialog_action_area10); + gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area10), GTK_BUTTONBOX_END); + + closebutton2 = gtk_button_new_from_stock ("gtk-close"); + gtk_widget_show (closebutton2); + gtk_dialog_add_action_widget (GTK_DIALOG (tagwritersettings), closebutton2, GTK_RESPONSE_CLOSE); + GTK_WIDGET_SET_FLAGS (closebutton2, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) write_id3v2, "toggled", + G_CALLBACK (on_write_id3v2_toggled), + NULL); + g_signal_connect ((gpointer) write_id3v1, "toggled", + G_CALLBACK (on_write_id3v1_toggled), + NULL); + g_signal_connect ((gpointer) write_apev2, "toggled", + G_CALLBACK (on_write_apev2_toggled), + NULL); + g_signal_connect ((gpointer) strip_id3v2, "toggled", + G_CALLBACK (on_strip_id3v2_toggled), + NULL); + g_signal_connect ((gpointer) strip_id3v1, "toggled", + G_CALLBACK (on_strip_id3v1_toggled), + NULL); + g_signal_connect ((gpointer) strip_apev2, "toggled", + G_CALLBACK (on_strip_apev2_toggled), + NULL); + g_signal_connect ((gpointer) id3v2_version, "changed", + G_CALLBACK (on_id3v2_version_changed), + NULL); + g_signal_connect ((gpointer) id3v1_encoding, "changed", + G_CALLBACK (on_id3v1_encoding_changed), + NULL); + g_signal_connect ((gpointer) ape_write_id3v2, "toggled", + G_CALLBACK (on_ape_write_id3v2_toggled), + NULL); + g_signal_connect ((gpointer) ape_write_apev2, "toggled", + G_CALLBACK (on_ape_write_apev2_toggled), + NULL); + g_signal_connect ((gpointer) ape_strip_id3v2, "toggled", + G_CALLBACK (on_ape_strip_id3v2_toggled), + NULL); + g_signal_connect ((gpointer) ape_strip_apev2, "toggled", + G_CALLBACK (on_ape_strip_apev2_toggled), + NULL); + g_signal_connect ((gpointer) wv_write_apev2, "toggled", + G_CALLBACK (on_wv_write_apev2_toggled), + NULL); + g_signal_connect ((gpointer) wv_write_id3v1, "toggled", + G_CALLBACK (on_wv_write_id3v1_toggled), + NULL); + g_signal_connect ((gpointer) wv_strip_apev2, "toggled", + G_CALLBACK (on_wv_strip_apev2_toggled), + NULL); + g_signal_connect ((gpointer) wv_strip_id3v1, "toggled", + G_CALLBACK (on_wv_strip_id3v1_toggled), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (tagwritersettings, tagwritersettings, "tagwritersettings"); + GLADE_HOOKUP_OBJECT_NO_REF (tagwritersettings, dialog_vbox11, "dialog_vbox11"); + GLADE_HOOKUP_OBJECT (tagwritersettings, vbox32, "vbox32"); + GLADE_HOOKUP_OBJECT (tagwritersettings, frame8, "frame8"); + GLADE_HOOKUP_OBJECT (tagwritersettings, alignment21, "alignment21"); + GLADE_HOOKUP_OBJECT (tagwritersettings, vbox33, "vbox33"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox89, "hbox89"); + GLADE_HOOKUP_OBJECT (tagwritersettings, write_id3v2, "write_id3v2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, write_id3v1, "write_id3v1"); + GLADE_HOOKUP_OBJECT (tagwritersettings, write_apev2, "write_apev2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox90, "hbox90"); + GLADE_HOOKUP_OBJECT (tagwritersettings, strip_id3v2, "strip_id3v2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, strip_id3v1, "strip_id3v1"); + GLADE_HOOKUP_OBJECT (tagwritersettings, strip_apev2, "strip_apev2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox91, "hbox91"); + GLADE_HOOKUP_OBJECT (tagwritersettings, label118, "label118"); + GLADE_HOOKUP_OBJECT (tagwritersettings, id3v2_version, "id3v2_version"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox92, "hbox92"); + GLADE_HOOKUP_OBJECT (tagwritersettings, label119, "label119"); + GLADE_HOOKUP_OBJECT (tagwritersettings, id3v1_encoding, "id3v1_encoding"); + GLADE_HOOKUP_OBJECT (tagwritersettings, label120, "label120"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox93, "hbox93"); + GLADE_HOOKUP_OBJECT (tagwritersettings, frame9, "frame9"); + GLADE_HOOKUP_OBJECT (tagwritersettings, alignment22, "alignment22"); + GLADE_HOOKUP_OBJECT (tagwritersettings, vbox34, "vbox34"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox94, "hbox94"); + GLADE_HOOKUP_OBJECT (tagwritersettings, ape_write_id3v2, "ape_write_id3v2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, ape_write_apev2, "ape_write_apev2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox95, "hbox95"); + GLADE_HOOKUP_OBJECT (tagwritersettings, ape_strip_id3v2, "ape_strip_id3v2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, ape_strip_apev2, "ape_strip_apev2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, label121, "label121"); + GLADE_HOOKUP_OBJECT (tagwritersettings, frame10, "frame10"); + GLADE_HOOKUP_OBJECT (tagwritersettings, alignment23, "alignment23"); + GLADE_HOOKUP_OBJECT (tagwritersettings, vbox35, "vbox35"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox96, "hbox96"); + GLADE_HOOKUP_OBJECT (tagwritersettings, wv_write_apev2, "wv_write_apev2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, wv_write_id3v1, "wv_write_id3v1"); + GLADE_HOOKUP_OBJECT (tagwritersettings, hbox97, "hbox97"); + GLADE_HOOKUP_OBJECT (tagwritersettings, wv_strip_apev2, "wv_strip_apev2"); + GLADE_HOOKUP_OBJECT (tagwritersettings, wv_strip_id3v1, "wv_strip_id3v1"); + GLADE_HOOKUP_OBJECT (tagwritersettings, label122, "label122"); + GLADE_HOOKUP_OBJECT_NO_REF (tagwritersettings, dialog_action_area10, "dialog_action_area10"); + GLADE_HOOKUP_OBJECT (tagwritersettings, closebutton2, "closebutton2"); + + return tagwritersettings; +} + diff --git a/plugins/gtkui/interface.h b/plugins/gtkui/interface.h index 5c4b5923..d27c75e6 100644 --- a/plugins/gtkui/interface.h +++ b/plugins/gtkui/interface.h @@ -5,11 +5,14 @@ GtkWidget* create_mainwin (void); GtkWidget* create_searchwin (void); GtkWidget* create_traymenu (void); -GtkWidget* create_addprogress (void); +GtkWidget* create_progressdlg (void); GtkWidget* create_helpwindow (void); GtkWidget* create_trackproperties (void); GtkWidget* create_editcolumndlg (void); GtkWidget* create_prefwin (void); -GtkWidget* create_editplaylistdlg (void); +GtkWidget* create_entrydialog (void); GtkWidget* create_addlocationdlg (void); GtkWidget* create_groupbydlg (void); +GtkWidget* create_sortbydlg (void); +GtkWidget* create_select_dsp_plugin (void); +GtkWidget* create_tagwritersettings (void); diff --git a/plugins/gtkui/mainplaylist.c b/plugins/gtkui/mainplaylist.c index 2df3bd0e..340cbfb3 100644 --- a/plugins/gtkui/mainplaylist.c +++ b/plugins/gtkui/mainplaylist.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -127,9 +127,9 @@ gboolean playlist_tooltip_handler (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer unused) { GtkWidget *pl = lookup_widget (mainwin, "playlist"); - DB_playItem_t *item = (DB_playItem_t *)ddb_listview_get_iter_from_coord (DDB_LISTVIEW (pl), 0, y); - if (item && item->fname) { - gtk_tooltip_set_text (tooltip, item->fname); + DB_playItem_t *it = (DB_playItem_t *)ddb_listview_get_iter_from_coord (DDB_LISTVIEW (pl), 0, y); + if (it) { + gtk_tooltip_set_text (tooltip, deadbeef->pl_find_meta (it, ":URI")); return TRUE; } return FALSE; @@ -143,13 +143,13 @@ main_col_sort (int col, int sort_order, void *user_data) { deadbeef->pl_sort (PL_MAIN, c->id, c->format, sort_order-1); } void main_handle_doubleclick (DdbListview *listview, DdbListviewIter iter, int idx) { - deadbeef->sendmessage (M_PLAYSONGNUM, 0, idx, 0); + deadbeef->sendmessage (M_PLAY_NUM, 0, idx, 0); } void main_selection_changed (DdbListviewIter it, int idx) { DdbListview *search = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (idx == -1) { - ddb_listview_refresh (search, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (search, DDB_REFRESH_LIST); } else { ddb_listview_draw_row (search, search_get_idx ((DB_playItem_t *)it), it); @@ -173,8 +173,8 @@ void main_draw_group_title (DdbListview *listview, GdkDrawable *drawable, DdbLis draw_set_fg_color (rgb); } int ew, eh; - draw_text (x + 5, y + height/2 - draw_get_font_size ()/2 - 2, width-10, 0, str); draw_get_text_extents (str, -1, &ew, &eh); + draw_text (x + 5, y + height/2 - draw_get_font_size ()/2 - 2, ew+5, 0, str); draw_line (x + 5 + ew + 3, y+height/2, x + width, y+height/2); } } @@ -305,7 +305,7 @@ main_playlist_init (GtkWidget *widget) { add_column_helper (listview, _("Playing"), 50, DB_COLUMN_PLAYING, NULL, 0); add_column_helper (listview, _("Artist / Album"), 150, -1, "%a - %b", 0); add_column_helper (listview, _("Track No"), 50, -1, "%n", 1); - add_column_helper (listview, _("Title / Track Artist"), 150, -1, "%t", 0); + add_column_helper (listview, _("Title"), 150, -1, "%t", 0); add_column_helper (listview, _("Duration"), 50, -1, "%l", 0); } else { @@ -325,7 +325,9 @@ main_playlist_init (GtkWidget *widget) { g_object_set_property (G_OBJECT (widget), "has-tooltip", &value); g_signal_connect (G_OBJECT (widget), "query-tooltip", G_CALLBACK (playlist_tooltip_handler), NULL); } - strncpy (group_by_str, deadbeef->conf_get_str ("playlist.group_by", ""), sizeof (group_by_str)); + deadbeef->conf_lock (); + strncpy (group_by_str, deadbeef->conf_get_str_fast ("playlist.group_by", ""), sizeof (group_by_str)); + deadbeef->conf_unlock (); group_by_str[sizeof (group_by_str)-1] = 0; } @@ -337,7 +339,7 @@ void main_refresh (void) { if (mainwin && gtk_widget_get_visible (mainwin)) { DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_VSCROLL | DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (pl, DDB_REFRESH_VSCROLL | DDB_REFRESH_LIST); } } diff --git a/plugins/gtkui/mainplaylist.h b/plugins/gtkui/mainplaylist.h index af266c4a..b17ef63d 100644 --- a/plugins/gtkui/mainplaylist.h +++ b/plugins/gtkui/mainplaylist.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/parser.c b/plugins/gtkui/parser.c index 96558c9e..6ee81f9a 100644 --- a/plugins/gtkui/parser.c +++ b/plugins/gtkui/parser.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/plugins/gtkui/parser.h b/plugins/gtkui/parser.h index b5d48a04..ac411577 100644 --- a/plugins/gtkui/parser.h +++ b/plugins/gtkui/parser.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c index beff39bb..216b7122 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -120,7 +120,7 @@ void draw_column_data (DdbListview *listview, GdkDrawable *drawable, DdbListview if (!album || !*album) { album = deadbeef->pl_find_meta (group_it, "title"); } - GdkPixbuf *pixbuf = get_cover_art (((DB_playItem_t *)group_it)->fname, artist, album, art_width); + GdkPixbuf *pixbuf = get_cover_art (deadbeef->pl_find_meta (((DB_playItem_t *)group_it), ":URI"), artist, album, art_width); if (pixbuf) { int pw = gdk_pixbuf_get_width (pixbuf); int ph = gdk_pixbuf_get_height (pixbuf); @@ -181,10 +181,10 @@ void draw_column_data (DdbListview *listview, GdkDrawable *drawable, DdbListview draw_init_font_bold (); } if (calign_right) { - draw_text (x+5, y + height/2 - draw_get_font_size ()/2 - 2, cwidth-10, 1, text); + draw_text (x+5, y + (height-1)/2 - draw_get_font_size ()/2, cwidth-10, 1, text); } else { - draw_text (x + 5, y + height/2 - draw_get_font_size ()/2 - 2, cwidth-10, 0, text); + draw_text (x + 5, y + (height-1)/2 - draw_get_font_size ()/2, cwidth-10, 0, text); } if (gtkui_embolden_current_track && it && it == playing_track) { draw_init_font_normal (); @@ -238,14 +238,15 @@ main_reload_metadata_activate DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (menuitem), "ps")); DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); while (it) { - if (deadbeef->pl_is_selected (it) && deadbeef->is_local_file (it->fname) && it->decoder_id) { + const char *decoder_id = deadbeef->pl_find_meta (it, ":DECODER"); + if (deadbeef->pl_is_selected (it) && deadbeef->is_local_file (deadbeef->pl_find_meta (it, ":URI")) && decoder_id) { uint32_t f = deadbeef->pl_get_item_flags (it); if (!(f & DDB_IS_SUBTRACK)) { f &= ~DDB_TAG_MASK; deadbeef->pl_set_item_flags (it, f); DB_decoder_t **decoders = deadbeef->plug_get_decoder_list (); for (int i = 0; decoders[i]; i++) { - if (!strcmp (decoders[i]->plugin.id, it->decoder_id)) { + if (!strcmp (decoders[i]->plugin.id, decoder_id)) { if (decoders[i]->read_metadata) { decoders[i]->read_metadata (it); } @@ -266,13 +267,7 @@ void main_properties_activate (GtkMenuItem *menuitem, gpointer user_data) { - DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (clicked_idx, PL_MAIN); - if (!it) { - fprintf (stderr, "attempt to view properties of non-existing item\n"); - return; - } - show_track_properties_dlg (it); - deadbeef->pl_item_unref (it); + show_track_properties_dlg (); } void @@ -337,8 +332,8 @@ on_remove_from_disk_activate (GtkMenuItem *menuitem, DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); while (it) { - if (deadbeef->pl_is_selected (it) && deadbeef->is_local_file (it->fname)) { - unlink (it->fname); + if (deadbeef->pl_is_selected (it) && deadbeef->is_local_file (deadbeef->pl_find_meta (it, ":URI"))) { + unlink (deadbeef->pl_find_meta (it, ":URI")); } DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); deadbeef->pl_item_unref (it); @@ -460,6 +455,7 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { DB_plugin_t **plugins = deadbeef->plug_get_list(); int i; + int added_entries = 0; for (i = 0; plugins[i]; i++) { if (!plugins[i]->get_actions) @@ -474,6 +470,7 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { if (action->flags & DB_ACTION_COMMON) continue; count++; + added_entries++; GtkWidget *actionitem; actionitem = gtk_menu_item_new_with_mnemonic (_(action->title)); gtk_widget_show (actionitem); @@ -492,13 +489,14 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { gtk_widget_set_sensitive (GTK_WIDGET (actionitem), FALSE); } } - if (count > 0) - { - separator8 = gtk_separator_menu_item_new (); - gtk_widget_show (separator8); - gtk_container_add (GTK_CONTAINER (playlist_menu), separator8); - gtk_widget_set_sensitive (separator8, FALSE); - } + } + + if (added_entries > 0) + { + separator8 = gtk_separator_menu_item_new (); + gtk_widget_show (separator8); + gtk_container_add (GTK_CONTAINER (playlist_menu), separator8); + gtk_widget_set_sensitive (separator8, FALSE); } properties1 = gtk_menu_item_new_with_mnemonic (_("Properties")); @@ -733,7 +731,7 @@ on_add_column_activate (GtkMenuItem *menuitem, int align = gtk_combo_box_get_active (GTK_COMBO_BOX (lookup_widget (dlg, "align"))); ddb_listview_column_insert (last_playlist, active_column, title, 100, align, inf->id == DB_COLUMN_ALBUM_ART ? 100 : 0, inf); - ddb_listview_refresh (last_playlist, DDB_REFRESH_COLUMNS | DDB_REFRESH_LIST | DDB_REFRESH_HSCROLL | DDB_EXPOSE_LIST | DDB_EXPOSE_COLUMNS); + ddb_listview_refresh (last_playlist, DDB_REFRESH_COLUMNS | DDB_REFRESH_LIST | DDB_REFRESH_HSCROLL); } gtk_widget_destroy (dlg); } @@ -808,7 +806,7 @@ on_edit_column_activate (GtkMenuItem *menuitem, init_column (inf, id, format); ddb_listview_column_set_info (last_playlist, active_column, title, width, align, inf->id == DB_COLUMN_ALBUM_ART ? width : 0, inf); - ddb_listview_refresh (last_playlist, DDB_REFRESH_COLUMNS | DDB_REFRESH_LIST | DDB_EXPOSE_LIST | DDB_EXPOSE_COLUMNS); + ddb_listview_refresh (last_playlist, DDB_REFRESH_COLUMNS | DDB_REFRESH_LIST); } gtk_widget_destroy (dlg); } @@ -822,7 +820,7 @@ on_remove_column_activate (GtkMenuItem *menuitem, return; ddb_listview_column_remove (last_playlist, active_column); - ddb_listview_refresh (last_playlist, DDB_REFRESH_COLUMNS | DDB_REFRESH_LIST | DDB_REFRESH_HSCROLL | DDB_EXPOSE_LIST | DDB_EXPOSE_COLUMNS); + ddb_listview_refresh (last_playlist, DDB_REFRESH_COLUMNS | DDB_REFRESH_LIST | DDB_REFRESH_HSCROLL); } GtkWidget* diff --git a/plugins/gtkui/plcommon.h b/plugins/gtkui/plcommon.h index 8332cfcb..6a3114c1 100644 --- a/plugins/gtkui/plcommon.h +++ b/plugins/gtkui/plcommon.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/pluginconf.c b/plugins/gtkui/pluginconf.c index 601f72e8..c7c73ae8 100644 --- a/plugins/gtkui/pluginconf.c +++ b/plugins/gtkui/pluginconf.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -30,6 +30,7 @@ #include "gtkui.h" #include "parser.h" #include "support.h" +#include "pluginconf.h" //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -42,14 +43,16 @@ on_prop_browse_file (GtkButton *button, gpointer user_data) { gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE); // restore folder - gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str ("filechooser.lastdir", "")); + deadbeef->conf_lock (); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", "")); + deadbeef->conf_unlock (); int response = gtk_dialog_run (GTK_DIALOG (dlg)); // store folder gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg)); if (folder) { deadbeef->conf_set_str ("filechooser.lastdir", folder); g_free (folder); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } if (response == GTK_RESPONSE_OK) { gchar *file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg)); @@ -65,14 +68,14 @@ on_prop_browse_file (GtkButton *button, gpointer user_data) { } } -static void apply_conf (GtkWidget *w, DB_plugin_t *p) { +static void apply_conf (GtkWidget *w, ddb_dialog_t *conf) { // parse script char token[MAX_TOKEN]; - const char *script = p->configdialog; + const char *script = conf->layout; parser_line = 1; while (script = gettoken (script, token)) { if (strcmp (token, "property")) { - fprintf (stderr, "invalid token while loading plugin %s config dialog: %s at line %d\n", p->name, token, parser_line); + fprintf (stderr, "invalid token while loading plugin %s config dialog: %s at line %d\n", conf->title, token, parser_line); break; } char labeltext[MAX_TOKEN]; @@ -85,8 +88,34 @@ static void apply_conf (GtkWidget *w, DB_plugin_t *p) { if (!script) { break; } + + // skip containers + if (!strncmp (type, "hbox[", 5) || !strncmp (type, "vbox[", 5)) { + // skip to ; + char semicolon[MAX_TOKEN]; + while (script = gettoken_warn_eof (script, semicolon)) { + if (!strcmp (semicolon, ";")) { + break; + } + } + continue; + } + + // ignore layout options char key[MAX_TOKEN]; - script = gettoken_warn_eof (script, key); + const char *skiptokens[] = { "vert", NULL }; + for (;;) { + script = gettoken_warn_eof (script, key); + int i = 0; + for (i = 0; skiptokens[i]; i++) { + if (!strcmp (key, skiptokens[i])) { + break; + } + } + if (!skiptokens[i]) { + break; + } + } if (!script) { break; } @@ -95,39 +124,66 @@ static void apply_conf (GtkWidget *w, DB_plugin_t *p) { if (!script) { break; } - script = gettoken_warn_eof (script, token); - if (!script) { - break; - } - if (strcmp (token, ";")) { - fprintf (stderr, "expected `;' while loading plugin %s config dialog: %s at line %d\n", p->name, token, parser_line); - break; - } // fetch data GtkWidget *widget = lookup_widget (w, key); if (widget) { if (!strcmp (type, "entry") || !strcmp (type, "password")) { - deadbeef->conf_set_str (key, gtk_entry_get_text (GTK_ENTRY (widget))); + conf->set_param (key, gtk_entry_get_text (GTK_ENTRY (widget))); } else if (!strcmp (type, "file")) { if (deadbeef->conf_get_int ("gtkui.pluginconf.use_filechooser_button", 0)) { // filechooser - deadbeef->conf_set_str (key, gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget))); + conf->set_param (key, gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget))); } else { - deadbeef->conf_set_str (key, gtk_entry_get_text (GTK_ENTRY (widget))); + conf->set_param (key, gtk_entry_get_text (GTK_ENTRY (widget))); } } else if (!strcmp (type, "checkbox")) { - deadbeef->conf_set_int (key, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))); + conf->set_param (key, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) ? "1" : "0"); + } + else if (!strncmp (type, "hscale[", 7) || !strncmp (type, "vscale[", 7)) { + char s[20]; + snprintf (s, sizeof (s), "%f", gtk_range_get_value (GTK_RANGE (widget))); + conf->set_param (key, s); } - else if (!strncmp (type, "hscale[", 7)) { - deadbeef->conf_set_float (key, gtk_range_get_value (GTK_RANGE (widget))); + else if (!strncmp (type, "spinbtn[", 8)) { + char s[20]; + snprintf (s, sizeof (s), "%f", (float)gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget))); + conf->set_param (key, s); + } + else if (!strncmp (type, "select[", 7)) { + int n; + if (1 != sscanf (type+6, "[%d]", &n)) { + break; + } + for (int i = 0; i < n; i++) { + char value[MAX_TOKEN]; + script = gettoken_warn_eof (script, value); + if (!script) { + break; + } + } + if (!script) { + break; + } + char s[20]; + snprintf (s, sizeof (s), "%d", gtk_combo_box_get_active (GTK_COMBO_BOX (widget))); + conf->set_param (key, s); } } + + script = gettoken_warn_eof (script, token); + if (!script) { + break; + } + if (strcmp (token, ";")) { + fprintf (stderr, "expected `;' while loading plugin %s config dialog: %s at line %d\n", conf->title, token, parser_line); + break; + } } - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } static void @@ -135,22 +191,69 @@ prop_changed (GtkWidget *editable, gpointer user_data) { gtk_dialog_set_response_sensitive (GTK_DIALOG (user_data), GTK_RESPONSE_APPLY, TRUE); } -void -plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { +int +ddb_button_from_gtk_response (int response) { + switch (response) { + case GTK_RESPONSE_OK: + return ddb_button_ok; + case GTK_RESPONSE_CANCEL: + return ddb_button_cancel; + case GTK_RESPONSE_CLOSE: + return ddb_button_close; + case GTK_RESPONSE_APPLY: + return ddb_button_apply; + case GTK_RESPONSE_YES: + return ddb_button_yes; + case GTK_RESPONSE_NO: + return ddb_button_no; + } + return -1; +} + +int +gtkui_run_dialog (GtkWidget *parentwin, ddb_dialog_t *conf, uint32_t buttons, int (*callback)(int button, void *ctx), void *ctx) { // create window char title[200]; - snprintf (title, sizeof (title), _("Setup %s"), p->name); - GtkWidget *win = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parentwin), GTK_DIALOG_MODAL, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); - gtk_dialog_set_default_response (GTK_DIALOG (win), GTK_RESPONSE_OK); + snprintf (title, sizeof (title), _("Configure %s"), conf->title); + GtkWidget *win; + if (!buttons) { + win = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parentwin), GTK_DIALOG_MODAL, GTK_STOCK_APPLY, GTK_RESPONSE_APPLY, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (win), GTK_RESPONSE_OK); + } + else { + win = gtk_dialog_new_with_buttons (title, GTK_WINDOW (parentwin), GTK_DIALOG_MODAL, NULL); + if (buttons & (1<<ddb_button_ok)) { + gtk_dialog_add_button (GTK_DIALOG (win), GTK_STOCK_OK, GTK_RESPONSE_OK); + } + if (buttons & (1<<ddb_button_cancel)) { + gtk_dialog_add_button (GTK_DIALOG (win), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + } + if (buttons & (1<<ddb_button_close)) { + gtk_dialog_add_button (GTK_DIALOG (win), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); + } + if (buttons & (1<<ddb_button_apply)) { + gtk_dialog_add_button (GTK_DIALOG (win), GTK_STOCK_APPLY, GTK_RESPONSE_APPLY); + } + if (buttons & (1<<ddb_button_yes)) { + gtk_dialog_add_button (GTK_DIALOG (win), GTK_STOCK_YES, GTK_RESPONSE_YES); + } + if (buttons & (1<<ddb_button_no)) { + gtk_dialog_add_button (GTK_DIALOG (win), GTK_STOCK_NO, GTK_RESPONSE_NO); + } + } gtk_window_set_type_hint (GTK_WINDOW (win), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_container_set_border_width (GTK_CONTAINER(win), 12); gtk_window_set_title (GTK_WINDOW (win), title); gtk_window_set_modal (GTK_WINDOW (win), TRUE); gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parentwin)); - GtkWidget *vbox; - vbox = GTK_DIALOG (win)->vbox; - gtk_box_set_spacing (GTK_BOX (vbox), 8); + + GtkWidget *widgets[100] = {NULL}; + int pack[100] = {0}; + int ncurr = 0; + + widgets[ncurr] = GTK_DIALOG (win)->vbox; + gtk_box_set_spacing (GTK_BOX (widgets[ncurr]), 8); GtkWidget *action_area = GTK_DIALOG (win)->action_area; gtk_widget_show (action_area); gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END); @@ -158,11 +261,11 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { // parse script char token[MAX_TOKEN]; - const char *script = p->configdialog; + const char *script = conf->layout; parser_line = 1; while (script = gettoken (script, token)) { if (strcmp (token, "property")) { - fprintf (stderr, "invalid token while loading plugin %s config dialog: %s at line %d\n", p->name, token, parser_line); + fprintf (stderr, "invalid token while loading plugin %s config dialog: %s at line %d\n", conf->title, token, parser_line); break; } char labeltext[MAX_TOKEN]; @@ -170,34 +273,100 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { if (!script) { break; } + + if (ncurr > 0) { + pack[ncurr]--; + if (pack[ncurr] < 0) { + ncurr--; + } + } + char type[MAX_TOKEN]; script = gettoken_warn_eof (script, type); if (!script) { break; } + + if (!strncmp (type, "hbox[", 5) || !strncmp (type, "vbox[", 5)) { + ncurr++; + int n = 0; + if (1 != sscanf (type+4, "[%d]", &n)) { + break; + } + pack[ncurr] = n; + + int vert = 0; + int hmg = FALSE; + int fill = FALSE; + int expand = FALSE; + int border = 0; + int spacing = 8; + int height = 100; + + char param[MAX_TOKEN]; + for (;;) { + script = gettoken_warn_eof (script, param); + if (!script) { + break; + } + if (!strcmp (param, ";")) { + break; + } + else if (!strcmp (param, "hmg")) { + hmg = TRUE; + } + else if (!strcmp (param, "fill")) { + fill = TRUE; + } + else if (!strcmp (param, "expand")) { + expand = TRUE; + } + else if (!strncmp (param, "border=", 7)) { + border = atoi (param+7); + } + else if (!strncmp (param, "spacing=", 8)) { + spacing = atoi (param+8); + } + else if (!strncmp (param, "height=", 7)) { + height = atoi (param+7); + } + } + + widgets[ncurr] = vert ? gtk_vbox_new (TRUE, spacing) : gtk_hbox_new (TRUE, spacing); + gtk_widget_set_size_request (widgets[ncurr], -1, height); + gtk_widget_show (widgets[ncurr]); + gtk_box_pack_start (GTK_BOX(widgets[ncurr-1]), widgets[ncurr], fill, expand, border); + continue; + } + + int vertical = 0; + char key[MAX_TOKEN]; - script = gettoken_warn_eof (script, key); - if (!script) { - break; + for (;;) { + script = gettoken_warn_eof (script, key); + if (!script) { + break; + } + if (!strcmp (key, "vert")) { + vertical = 1; + } + else { + break; + } } + char def[MAX_TOKEN]; script = gettoken_warn_eof (script, def); if (!script) { break; } - script = gettoken_warn_eof (script, token); - if (!script) { - break; - } - if (strcmp (token, ";")) { - fprintf (stderr, "expected `;' while loading plugin %s config dialog: %s at line %d\n", p->name, token, parser_line); - break; - } // add to dialog GtkWidget *label = NULL; GtkWidget *prop = NULL; GtkWidget *cont = NULL; + char value[1000]; + conf->get_param (key, value, sizeof (value), def); if (!strcmp (type, "entry") || !strcmp (type, "password")) { label = gtk_label_new (_(labeltext)); gtk_widget_show (label); @@ -205,13 +374,17 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { gtk_entry_set_activates_default (GTK_ENTRY (prop), TRUE); g_signal_connect (G_OBJECT (prop), "changed", G_CALLBACK (prop_changed), win); gtk_widget_show (prop); - gtk_entry_set_text (GTK_ENTRY (prop), deadbeef->conf_get_str (key, def)); + gtk_entry_set_text (GTK_ENTRY (prop), value); + + if (!strcmp (type, "password")) { + gtk_entry_set_visibility (GTK_ENTRY (prop), FALSE); + } } else if (!strcmp (type, "checkbox")) { prop = gtk_check_button_new_with_label (_(labeltext)); g_signal_connect (G_OBJECT (prop), "toggled", G_CALLBACK (prop_changed), win); gtk_widget_show (prop); - int val = deadbeef->conf_get_int (key, atoi (def)); + int val = atoi (value); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (prop), val); } else if (!strcmp (type, "file")) { @@ -220,7 +393,7 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { if (deadbeef->conf_get_int ("gtkui.pluginconf.use_filechooser_button", 0)) { prop = gtk_file_chooser_button_new (_(labeltext), GTK_FILE_CHOOSER_ACTION_OPEN); gtk_widget_show (prop); - gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (prop), deadbeef->conf_get_str (key, def)); + gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (prop), value); g_signal_connect (G_OBJECT (prop), "file-set", G_CALLBACK (prop_changed), win); } else { @@ -231,7 +404,7 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { g_signal_connect (G_OBJECT (prop), "changed", G_CALLBACK (prop_changed), win); gtk_widget_show (prop); gtk_editable_set_editable (GTK_EDITABLE (prop), FALSE); - gtk_entry_set_text (GTK_ENTRY (prop), deadbeef->conf_get_str (key, def)); + gtk_entry_set_text (GTK_ENTRY (prop), value); gtk_box_pack_start (GTK_BOX (cont), prop, TRUE, TRUE, 0); GtkWidget *btn = gtk_button_new_with_label ("…"); gtk_widget_show (btn); @@ -239,36 +412,88 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { g_signal_connect (G_OBJECT (btn), "clicked", G_CALLBACK (on_prop_browse_file), prop); } } - else if (!strncmp (type, "hscale[", 7)) { + else if (!strncmp (type, "select[", 7)) { + int n; + if (1 != sscanf (type+6, "[%d]", &n)) { + break; + } + + label = gtk_label_new (_(labeltext)); + gtk_widget_show (label); + + prop = gtk_combo_box_new_text (); + gtk_widget_show (prop); + + for (int i = 0; i < n; i++) { + char entry[MAX_TOKEN]; + script = gettoken_warn_eof (script, entry); + if (!script) { + break; + } + + gtk_combo_box_append_text (GTK_COMBO_BOX (prop), entry); + } + if (!script) { + break; + } + gtk_combo_box_set_active (GTK_COMBO_BOX (prop), atoi (value)); + g_signal_connect ((gpointer) prop, "changed", + G_CALLBACK (prop_changed), + win); + } + else if (!strncmp (type, "hscale[", 7) || !strncmp (type, "vscale[", 7) || !strncmp (type, "spinbtn[", 8)) { float min, max, step; - if (3 != sscanf (type+6, "[%f,%f,%f]", &min, &max, &step)) { - min = 0; - max = 100; - step = 1; + const char *args; + if (type[0] == 's') { + args = type + 7; } + else { + args = type + 6; + } + if (3 != sscanf (args, "[%f,%f,%f]", &min, &max, &step)) { + break; + } + int invert = 0; if (min >= max) { float tmp = min; min = max; max = tmp; - break; + invert = 1; } if (step <= 0) { step = 1; } - prop = gtk_hscale_new_with_range (min, max, step); + if (type[0] == 's') { + prop = gtk_spin_button_new_with_range (min, max, step); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (prop), atof (value)); + } + else { + prop = type[0] == 'h' ? gtk_hscale_new_with_range (min, max, step) : gtk_vscale_new_with_range (min, max, step); + if (invert) { + gtk_range_set_inverted (GTK_RANGE (prop), TRUE); + } + gtk_range_set_value (GTK_RANGE (prop), (gdouble)atof (value)); + gtk_scale_set_value_pos (GTK_SCALE (prop), GTK_POS_RIGHT); + } label = gtk_label_new (_(labeltext)); gtk_widget_show (label); g_signal_connect (G_OBJECT (prop), "value-changed", G_CALLBACK (prop_changed), win); gtk_widget_show (prop); - gtk_range_set_value (GTK_RANGE (prop), (gdouble)deadbeef->conf_get_float (key, (float)*def)); - gtk_scale_set_value_pos (GTK_SCALE (prop), GTK_POS_RIGHT); } - if (!strcmp (type, "password")) { - gtk_entry_set_visibility (GTK_ENTRY (prop), FALSE); + + script = gettoken_warn_eof (script, token); + if (!script) { + break; + } + if (strcmp (token, ";")) { + fprintf (stderr, "expected `;' while loading plugin %s config dialog: %s at line %d\n", conf->title, token, parser_line); + break; } + + if (label && prop) { GtkWidget *hbox = NULL; - hbox = gtk_hbox_new (FALSE, 8); + hbox = vertical ? gtk_vbox_new (FALSE, 8) : gtk_hbox_new (FALSE, 8); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), cont ? cont : prop, TRUE, TRUE, 0); @@ -281,7 +506,7 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { g_object_set_data (G_OBJECT (win), key, prop); } if (cont) { - gtk_box_pack_start (GTK_BOX (vbox), cont, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (widgets[ncurr]), cont, FALSE, FALSE, 0); } } @@ -290,10 +515,21 @@ plugin_configure (GtkWidget *parentwin, DB_plugin_t *p) { gtk_dialog_set_response_sensitive (GTK_DIALOG (win), GTK_RESPONSE_APPLY, FALSE); response = gtk_dialog_run (GTK_DIALOG (win)); if (response == GTK_RESPONSE_APPLY || response == GTK_RESPONSE_OK) { - apply_conf (win, p); + apply_conf (win, conf); + } + if (callback) { + int btn = ddb_button_from_gtk_response (response); + if (!callback (btn, ctx)) { + break; + } } } while (response == GTK_RESPONSE_APPLY); gtk_widget_destroy (win); + int btn = ddb_button_from_gtk_response (response); + return btn; } - +int +gtkui_run_dialog_root (ddb_dialog_t *conf, uint32_t buttons, int (*callback)(int button, void *ctx), void *ctx) { + return gtkui_run_dialog (mainwin, conf, buttons, callback, ctx); +} diff --git a/plugins/gtkui/pluginconf.h b/plugins/gtkui/pluginconf.h new file mode 100644 index 00000000..102691aa --- /dev/null +++ b/plugins/gtkui/pluginconf.h @@ -0,0 +1,28 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __PLUGINCONF_H +#define __PLUGINCONF_H + +int +gtkui_run_dialog (GtkWidget *parentwin, ddb_dialog_t *conf, uint32_t buttons, int (*callback)(int button, void *ctx), void *ctx); + +int +gtkui_run_dialog_root (ddb_dialog_t *conf, uint32_t buttons, int (*callback)(int button, void *ctx), void *ctx); + +#endif diff --git a/plugins/gtkui/prefwin.c b/plugins/gtkui/prefwin.c index 4e1c42b0..9883fc96 100644 --- a/plugins/gtkui/prefwin.c +++ b/plugins/gtkui/prefwin.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -34,6 +34,9 @@ #include "support.h" #include "eq.h" #include "ddblistview.h" +#include "pluginconf.h" +#include "dspconfig.h" +#include "wingeom.h" #define GLADE_HOOKUP_OBJECT(component,widget,name) \ g_object_set_data_full (G_OBJECT (component), name, \ @@ -56,9 +59,11 @@ gtk_enum_sound_callback (const char *name, const char *desc, void *userdata) { GtkComboBox *combobox = GTK_COMBO_BOX (userdata); gtk_combo_box_append_text (combobox, desc); - if (!strcmp (deadbeef->conf_get_str ("alsa_soundcard", "default"), name)) { + deadbeef->conf_lock (); + if (!strcmp (deadbeef->conf_get_str_fast ("alsa_soundcard", "default"), name)) { gtk_combo_box_set_active (combobox, num_alsa_devices); } + deadbeef->conf_unlock (); strncpy (alsa_device_names[num_alsa_devices], name, 63); alsa_device_names[num_alsa_devices][63] = 0; @@ -66,40 +71,23 @@ gtk_enum_sound_callback (const char *name, const char *desc, void *userdata) { } void -on_plugin_active_toggled (GtkCellRendererToggle *cell_renderer, gchar *path, GtkTreeModel *model) { - GtkTreePath *p = gtk_tree_path_new_from_string (path); - if (p) { - int *indices = gtk_tree_path_get_indices (p); - //gtk_tree_path_free (p); // wtf?? gtk crashes on this - if (indices) { - DB_plugin_t **plugins = deadbeef->plug_get_list (); - DB_plugin_t *plug = plugins[*indices]; - gboolean state; - GtkTreeIter iter; - gtk_tree_model_get_iter (model, &iter, p); - gtk_tree_model_get (model, &iter, 0, &state, -1); - if (!deadbeef->plug_activate (plug, !state)) { - gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, !state, -1); - } - } - g_free (indices); - } -} - -void preferences_fill_soundcards (void) { if (!prefwin) { return; } - const char *s = deadbeef->conf_get_str ("alsa_soundcard", "default"); GtkComboBox *combobox = GTK_COMBO_BOX (lookup_widget (prefwin, "pref_soundcard")); GtkTreeModel *mdl = gtk_combo_box_get_model (combobox); gtk_list_store_clear (GTK_LIST_STORE (mdl)); gtk_combo_box_append_text (combobox, _("Default Audio Device")); + + deadbeef->conf_lock (); + const char *s = deadbeef->conf_get_str_fast ("alsa_soundcard", "default"); if (!strcmp (s, "default")) { gtk_combo_box_set_active (combobox, 0); } + deadbeef->conf_unlock (); + num_alsa_devices = 1; strcpy (alsa_device_names[0], "default"); if (deadbeef->get_output ()->enum_soundcards) { @@ -269,6 +257,7 @@ prefwin_init_theme_colors (void) { gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "tabstrip_mid")), (gtkui_get_tabstrip_mid_color (&clr), &clr)); gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "tabstrip_light")), (gtkui_get_tabstrip_light_color (&clr), &clr)); gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "tabstrip_base")), (gtkui_get_tabstrip_base_color (&clr), &clr)); + gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "tabstrip_text")), (gtkui_get_tabstrip_text_color (&clr), &clr)); gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "listview_even_row")), (gtkui_get_listview_even_row_color (&clr), &clr)); gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "listview_odd_row")), (gtkui_get_listview_odd_row_color (&clr), &clr)); gtk_color_button_set_color (GTK_COLOR_BUTTON (lookup_widget (prefwin, "listview_selected_row")), (gtkui_get_listview_selection_color (&clr), &clr)); @@ -472,15 +461,16 @@ on_preferences_activate (GtkMenuItem *menuitem, if (prefwin) { return; } + deadbeef->conf_lock (); GtkWidget *w = prefwin = create_prefwin (); gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (mainwin)); GtkComboBox *combobox = NULL; // output plugin selection - const char *outplugname = deadbeef->conf_get_str ("output_plugin", _("ALSA output plugin")); combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_output_plugin")); + const char *outplugname = deadbeef->conf_get_str_fast ("output_plugin", "ALSA output plugin"); DB_output_t **out_plugs = deadbeef->plug_get_output_list (); for (int i = 0; out_plugs[i]; i++) { gtk_combo_box_append_text (combobox, out_plugs[i]->plugin.name); @@ -500,13 +490,6 @@ on_preferences_activate (GtkMenuItem *menuitem, G_CALLBACK (on_pref_soundcard_changed), NULL); - // alsa resampling - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_dynsamplerate")), deadbeef->conf_get_int ("playback.dynsamplerate", 0)); - - // src_quality - combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_src_quality")); - gtk_combo_box_set_active (combobox, deadbeef->conf_get_int ("src_quality", 2)); - // replaygain_mode combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_replaygain_mode")); gtk_combo_box_set_active (combobox, deadbeef->conf_get_int ("replaygain_mode", 0)); @@ -514,6 +497,12 @@ on_preferences_activate (GtkMenuItem *menuitem, // replaygain_scale gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_replaygain_scale")), deadbeef->conf_get_int ("replaygain_scale", 1)); + // replaygain_preamp + gtk_range_set_value (GTK_RANGE (lookup_widget (w, "replaygain_preamp")), deadbeef->conf_get_int ("replaygain_preamp", 0)); + + // dsp + dsp_setup_init (prefwin); + // close_send_to_tray gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_close_send_to_tray")), deadbeef->conf_get_int ("close_send_to_tray", 0)); @@ -529,19 +518,38 @@ on_preferences_activate (GtkMenuItem *menuitem, // hide_delete_from_disk gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "hide_delete_from_disk")), deadbeef->conf_get_int ("gtkui.hide_remove_from_disk", 0)); + // auto-rename playlist from folder name + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "auto_name_playlist_from_folder")), deadbeef->conf_get_int ("gtkui.name_playlist_from_folder", 0)); + + // refresh rate + int val = deadbeef->conf_get_int ("gtkui.refresh_rate", 10); + gtk_range_set_value (GTK_RANGE (lookup_widget (w, "gui_fps")), val); + + // add from archives + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "ignore_archives")), deadbeef->conf_get_int ("ignore_archives", 1)); // titlebar text - gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "titlebar_format_playing")), deadbeef->conf_get_str ("gtkui.titlebar_playing", "%a - %t - DeaDBeeF-%V")); - gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "titlebar_format_stopped")), deadbeef->conf_get_str ("gtkui.titlebar_stopped", "DeaDBeeF-%V")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "titlebar_format_playing")), deadbeef->conf_get_str_fast ("gtkui.titlebar_playing", "%a - %t - DeaDBeeF-%V")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "titlebar_format_stopped")), deadbeef->conf_get_str_fast ("gtkui.titlebar_stopped", "DeaDBeeF-%V")); // cli playlist int active = deadbeef->conf_get_int ("cli_add_to_specific_playlist", 1); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "cli_add_to_playlist")), active); gtk_widget_set_sensitive (lookup_widget (prefwin, "cli_playlist_name"), active); - gtk_entry_set_text (GTK_ENTRY (lookup_widget (prefwin, "cli_playlist_name")), deadbeef->conf_get_str ("cli_add_playlist_name", "Default")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (prefwin, "cli_playlist_name")), deadbeef->conf_get_str_fast ("cli_add_playlist_name", "Default")); // resume last session gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "resume_last_session")), deadbeef->conf_get_int ("resume_last_session", 0)); + // fill gui plugin list + combobox = GTK_COMBO_BOX (lookup_widget (w, "gui_plugin")); + const char **names = deadbeef->plug_get_gui_names (); + for (int i = 0; names[i]; i++) { + gtk_combo_box_append_text (combobox, names[i]); + if (!strcmp (names[i], deadbeef->conf_get_str_fast ("gui_plugin", "GTK2"))) { + gtk_combo_box_set_active (combobox, i); + } + } + // override bar colors int override = deadbeef->conf_get_int ("gtkui.override_bar_colors", 0); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "override_bar_colors")), override); @@ -562,10 +570,10 @@ on_preferences_activate (GtkMenuItem *menuitem, // network gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_network_enableproxy")), deadbeef->conf_get_int ("network.proxy", 0)); - gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyaddress")), deadbeef->conf_get_str ("network.proxy.address", "")); - gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyport")), deadbeef->conf_get_str ("network.proxy.port", "8080")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyaddress")), deadbeef->conf_get_str_fast ("network.proxy.address", "")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "pref_network_proxyport")), deadbeef->conf_get_str_fast ("network.proxy.port", "8080")); combobox = GTK_COMBO_BOX (lookup_widget (w, "pref_network_proxytype")); - const char *type = deadbeef->conf_get_str ("network.proxy.type", "HTTP"); + const char *type = deadbeef->conf_get_str_fast ("network.proxy.type", "HTTP"); if (!strcasecmp (type, "HTTP")) { gtk_combo_box_set_active (combobox, 0); } @@ -584,8 +592,8 @@ on_preferences_activate (GtkMenuItem *menuitem, else if (!strcasecmp (type, "SOCKS5_HOSTNAME")) { gtk_combo_box_set_active (combobox, 5); } - gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "proxyuser")), deadbeef->conf_get_str ("network.proxy.username", "")); - gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "proxypassword")), deadbeef->conf_get_str ("network.proxy.password", "")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "proxyuser")), deadbeef->conf_get_str_fast ("network.proxy.username", "")); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "proxypassword")), deadbeef->conf_get_str_fast ("network.proxy.password", "")); // list of plugins GtkTreeView *tree = GTK_TREE_VIEW (lookup_widget (w, "pref_pluginlist")); @@ -622,7 +630,6 @@ on_preferences_activate (GtkMenuItem *menuitem, gtk_tree_view_set_model (tree, GTK_TREE_MODEL (store)); gtk_widget_set_sensitive (lookup_widget (prefwin, "configure_plugin"), FALSE); -// gtk_widget_show (w); // hotkeys DB_plugin_t *hotkeys = deadbeef->plug_get_for_id ("hotkeys"); @@ -630,42 +637,9 @@ on_preferences_activate (GtkMenuItem *menuitem, prefwin_add_hotkeys_tab (prefwin); } - // tag writer - int strip_id3v2 = deadbeef->conf_get_int ("mp3.strip_id3v2", 0); - int strip_id3v1 = deadbeef->conf_get_int ("mp3.strip_id3v1", 0); - int strip_apev2 = deadbeef->conf_get_int ("mp3.strip_apev2", 0); - int write_id3v2 = deadbeef->conf_get_int ("mp3.write_id3v2", 1); - int write_id3v1 = deadbeef->conf_get_int ("mp3.write_id3v1", 1); - int write_apev2 = deadbeef->conf_get_int ("mp3.write_apev2", 0); - int id3v2_version = deadbeef->conf_get_int ("mp3.id3v2_version", 3); - const char *id3v1_encoding = deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1"); - int ape_strip_id3v2 = deadbeef->conf_get_int ("ape.strip_id3v2", 0); - int ape_strip_apev2 = deadbeef->conf_get_int ("ape.strip_apev2", 0); - int ape_write_id3v2 = deadbeef->conf_get_int ("ape.write_id3v2", 0); - int ape_write_apev2 = deadbeef->conf_get_int ("ape.write_apev2", 1); - int wv_strip_apev2 = deadbeef->conf_get_int ("wv.strip_apev2", 0); - int wv_strip_id3v1 = deadbeef->conf_get_int ("wv.strip_id3v1", 0); - int wv_write_apev2 = deadbeef->conf_get_int ("wv.write_apev2", 1); - int wv_write_id3v1 = deadbeef->conf_get_int ("wv.write_id3v1", 0); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "strip_id3v2")), strip_id3v2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "strip_id3v1")), strip_id3v1); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "strip_apev2")), strip_apev2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "write_id3v2")), write_id3v2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "write_id3v1")), write_id3v1); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "write_apev2")), write_apev2); - gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (prefwin, "id3v2_version")), id3v2_version != 4 ? 0 : 1); - gtk_entry_set_text (GTK_ENTRY (lookup_widget (prefwin, "id3v1_encoding")), id3v1_encoding); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "ape_strip_id3v2")), ape_strip_id3v2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "ape_strip_apev2")), ape_strip_apev2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "ape_write_apev2")), ape_write_apev2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "ape_write_id3v2")), ape_write_id3v2); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "wv_strip_id3v1")), wv_strip_id3v1); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "wv_strip_apev2")), wv_strip_apev2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "wv_write_apev2")), wv_write_apev2); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (prefwin, "wv_write_id3v1")), wv_write_id3v1); - + deadbeef->conf_unlock (); gtk_dialog_run (GTK_DIALOG (prefwin)); + dsp_setup_free (); gtk_widget_destroy (prefwin); deadbeef->conf_save (); prefwin = NULL; @@ -678,11 +652,13 @@ on_pref_soundcard_changed (GtkComboBox *combobox, { int active = gtk_combo_box_get_active (combobox); if (active >= 0 && active < num_alsa_devices) { - const char *soundcard = deadbeef->conf_get_str ("alsa_soundcard", "default"); + deadbeef->conf_lock (); + const char *soundcard = deadbeef->conf_get_str_fast ("alsa_soundcard", "default"); if (strcmp (soundcard, alsa_device_names[active])) { deadbeef->conf_set_str ("alsa_soundcard", alsa_device_names[active]); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } + deadbeef->conf_unlock (); } } @@ -690,13 +666,14 @@ void on_pref_output_plugin_changed (GtkComboBox *combobox, gpointer user_data) { - const char *outplugname = deadbeef->conf_get_str ("output_plugin", _("ALSA output plugin")); int active = gtk_combo_box_get_active (combobox); DB_output_t **out_plugs = deadbeef->plug_get_output_list (); DB_output_t *prev = NULL; DB_output_t *new = NULL; + deadbeef->conf_lock (); + const char *outplugname = deadbeef->conf_get_str_fast ("output_plugin", "ALSA output plugin"); for (int i = 0; out_plugs[i]; i++) { if (!strcmp (out_plugs[i]->plugin.name, outplugname)) { prev = out_plugs[i]; @@ -705,6 +682,7 @@ on_pref_output_plugin_changed (GtkComboBox *combobox, new = out_plugs[i]; } } + deadbeef->conf_unlock (); if (!new) { fprintf (stderr, "failed to find output plugin selected in preferences window\n"); @@ -718,32 +696,12 @@ on_pref_output_plugin_changed (GtkComboBox *combobox, } void -on_pref_dynsamplerate_clicked (GtkButton *button, - gpointer user_data) -{ - int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); - deadbeef->conf_set_int ("playback.dynsamplerate", active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); -} - - -void -on_pref_src_quality_changed (GtkComboBox *combobox, - gpointer user_data) -{ - int active = gtk_combo_box_get_active (combobox); - deadbeef->conf_set_int ("src_quality", active == -1 ? 2 : active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); -} - - -void on_pref_replaygain_mode_changed (GtkComboBox *combobox, gpointer user_data) { int active = gtk_combo_box_get_active (combobox); deadbeef->conf_set_int ("replaygain_mode", active == -1 ? 0 : active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } void @@ -752,7 +710,16 @@ on_pref_replaygain_scale_clicked (GtkButton *button, { int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); deadbeef->conf_set_int ("replaygain_scale", active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); +} + +void +on_replaygain_preamp_value_changed (GtkRange *range, + gpointer user_data) +{ + float val = gtk_range_get_value (range); + deadbeef->conf_set_float ("replaygain_preamp", val); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } @@ -762,7 +729,7 @@ on_pref_close_send_to_tray_clicked (GtkButton *button, { int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); deadbeef->conf_set_int ("close_send_to_tray", active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } void @@ -771,7 +738,7 @@ on_hide_tray_icon_toggled (GtkToggleButton *togglebutton, { int active = gtk_toggle_button_get_active (togglebutton); deadbeef->conf_set_int ("gtkui.hide_tray_icon", active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } void @@ -797,21 +764,45 @@ on_pref_pluginlist_cursor_changed (GtkTreeView *treeview, DB_plugin_t *p = plugins[*indices]; g_free (indices); assert (p); - GtkWidget *w = prefwin;//GTK_WIDGET (gtk_widget_get_parent_window (GTK_WIDGET (treeview))); + GtkWidget *w = prefwin; assert (w); - GtkEntry *e = GTK_ENTRY (lookup_widget (w, "pref_plugin_descr")); - gtk_entry_set_text (e, p->descr ? p->descr : ""); - e = GTK_ENTRY (lookup_widget (w, "pref_plugin_author")); - gtk_entry_set_text (e, p->author ? p->author : ""); - e = GTK_ENTRY (lookup_widget (w, "pref_plugin_email")); - gtk_entry_set_text (e, p->email ? p->email : ""); - e = GTK_ENTRY (lookup_widget (w, "pref_plugin_website")); - gtk_entry_set_text (e, p->website ? p->website : ""); + if (p->descr) { + GtkTextView *tv = GTK_TEXT_VIEW (lookup_widget (w, "plug_description")); + + GtkTextBuffer *buffer = gtk_text_buffer_new (NULL); + + gtk_text_buffer_set_text (buffer, p->descr, strlen(p->descr)); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (tv), buffer); + g_object_unref (buffer); + } + + GtkWidget *link = lookup_widget (w, "weblink"); + if (p->website) { + gtk_link_button_set_uri (GTK_LINK_BUTTON(link), p->website); + gtk_widget_set_sensitive (link, TRUE); + } + else { + gtk_link_button_set_uri (GTK_LINK_BUTTON(link), ""); + gtk_widget_set_sensitive (link, FALSE); + } + + GtkWidget *cpr = lookup_widget (w, "plug_copyright"); + if (p->copyright) { + gtk_widget_set_sensitive (cpr, TRUE); + } + else { + gtk_widget_set_sensitive (cpr, FALSE); + } gtk_widget_set_sensitive (lookup_widget (prefwin, "configure_plugin"), p->configdialog ? TRUE : FALSE); } void +gtkui_conf_get_str (const char *key, char *value, int len, const char *def) { + deadbeef->conf_get_str (key, def, value, len); +} + +void on_configure_plugin_clicked (GtkButton *button, gpointer user_data) { @@ -828,7 +819,13 @@ on_configure_plugin_clicked (GtkButton *button, DB_plugin_t **plugins = deadbeef->plug_get_list (); DB_plugin_t *p = plugins[*indices]; if (p->configdialog) { - plugin_configure (prefwin, p); + ddb_dialog_t conf = { + .title = p->name, + .layout = p->configdialog, + .set_param = deadbeef->conf_set_str, + .get_param = gtkui_conf_get_str, + }; + gtkui_run_dialog (prefwin, &conf, 0, NULL, NULL); } } @@ -837,10 +834,10 @@ redraw_headers (void) { DdbListview *playlist = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); DdbListview *search = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (playlist) { - ddb_listview_refresh (playlist, DDB_REFRESH_COLUMNS | DDB_EXPOSE_COLUMNS); + ddb_listview_refresh (playlist, DDB_REFRESH_COLUMNS); } if (search) { - ddb_listview_refresh (search, DDB_REFRESH_COLUMNS | DDB_EXPOSE_COLUMNS); + ddb_listview_refresh (search, DDB_REFRESH_COLUMNS); } } @@ -853,7 +850,7 @@ on_tabstrip_light_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.tabstrip_light", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); redraw_headers (); tabstrip_redraw (); @@ -869,7 +866,7 @@ on_tabstrip_mid_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.tabstrip_mid", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); redraw_headers (); tabstrip_redraw (); @@ -885,7 +882,7 @@ on_tabstrip_dark_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.tabstrip_dark", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); redraw_headers (); tabstrip_redraw (); @@ -900,12 +897,26 @@ on_tabstrip_base_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.tabstrip_base", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); redraw_headers (); tabstrip_redraw (); } +void +on_tabstrip_text_color_set (GtkColorButton *colorbutton, + gpointer user_data) +{ + GdkColor clr; + gtk_color_button_get_color (colorbutton, &clr); + char str[100]; + snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); + deadbeef->conf_set_str ("gtkui.color.tabstrip_text", str); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); + gtkui_init_theme_colors (); + redraw_headers (); + tabstrip_redraw (); +} void on_bar_foreground_color_set (GtkColorButton *colorbutton, @@ -916,7 +927,7 @@ on_bar_foreground_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.bar_foreground", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); seekbar_redraw (); volumebar_redraw (); @@ -933,7 +944,7 @@ on_bar_background_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.bar_background", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); seekbar_redraw (); volumebar_redraw (); @@ -947,7 +958,7 @@ on_override_listview_colors_toggled (GtkToggleButton *togglebutton, int active = gtk_toggle_button_get_active (togglebutton); deadbeef->conf_set_int ("gtkui.override_listview_colors", active); gtk_widget_set_sensitive (lookup_widget (prefwin, "listview_colors_group"), active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); prefwin_init_theme_colors (); playlist_refresh (); @@ -963,7 +974,7 @@ on_listview_even_row_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.listview_even_row", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); playlist_refresh (); } @@ -977,7 +988,7 @@ on_listview_odd_row_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.listview_odd_row", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); playlist_refresh (); } @@ -991,7 +1002,7 @@ on_listview_selected_row_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.listview_selection", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); playlist_refresh (); } @@ -1005,7 +1016,7 @@ on_listview_text_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.listview_text", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); playlist_refresh (); } @@ -1020,7 +1031,7 @@ on_listview_selected_text_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.listview_selected_text", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); playlist_refresh (); } @@ -1034,7 +1045,7 @@ on_listview_cursor_color_set (GtkColorButton *colorbutton, char str[100]; snprintf (str, sizeof (str), "%d %d %d", clr.red, clr.green, clr.blue); deadbeef->conf_set_str ("gtkui.color.listview_cursor", str); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); playlist_refresh (); } @@ -1047,7 +1058,7 @@ on_override_bar_colors_toggled (GtkToggleButton *togglebutton, int active = gtk_toggle_button_get_active (togglebutton); deadbeef->conf_set_int ("gtkui.override_bar_colors", active); gtk_widget_set_sensitive (lookup_widget (prefwin, "bar_colors_group"), active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); prefwin_init_theme_colors (); seekbar_redraw (); @@ -1062,7 +1073,7 @@ on_override_tabstrip_colors_toggled (GtkToggleButton *togglebutton, int active = gtk_toggle_button_get_active (togglebutton); deadbeef->conf_set_int ("gtkui.override_tabstrip_colors", active); gtk_widget_set_sensitive (lookup_widget (prefwin, "tabstrip_colors_group"), active); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); gtkui_init_theme_colors (); prefwin_init_theme_colors (); redraw_headers (); @@ -1070,137 +1081,6 @@ on_override_tabstrip_colors_toggled (GtkToggleButton *togglebutton, } void -on_write_id3v2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("mp3.write_id3v2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_write_id3v1_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("mp3.write_id3v1", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_write_apev2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("mp3.write_apev2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_strip_id3v2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("mp3.strip_id3v2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_strip_id3v1_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("mp3.strip_id3v1", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_strip_apev2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("mp3.strip_apev2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_id3v2_version_changed (GtkComboBox *combobox, - gpointer user_data) -{ - int version = 3; - int active = gtk_combo_box_get_active (combobox); - if (active == 1) { - version = 4; - } - deadbeef->conf_set_int ("mp3.id3v2_version", version); -} - - -void -on_id3v1_encoding_changed (GtkEditable *editable, - gpointer user_data) -{ - deadbeef->conf_set_str ("mp3.id3v1_encoding", gtk_entry_get_text (GTK_ENTRY (editable))); -} - - -void -on_ape_write_id3v2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("ape.write_id3v2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_ape_write_apev2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("ape.write_apev2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_ape_strip_id3v2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("ape.strip_id3v2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_ape_strip_apev2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("ape.strip_apev2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_wv_write_apev2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("wv.write_apev2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_wv_write_id3v1_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("wv.write_id3v1", gtk_toggle_button_get_active (togglebutton)); -} - -void -on_wv_strip_apev2_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("wv.strip_apev2", gtk_toggle_button_get_active (togglebutton)); -} - - -void -on_wv_strip_id3v1_toggled (GtkToggleButton *togglebutton, - gpointer user_data) -{ - deadbeef->conf_set_int ("wv.strip_id3v1", gtk_toggle_button_get_active (togglebutton)); -} - -void on_pref_network_proxyaddress_changed (GtkEditable *editable, gpointer user_data) { @@ -1344,3 +1224,115 @@ on_resume_last_session_toggled (GtkToggleButton *togglebutton, deadbeef->conf_set_int ("resume_last_session", active); } + +void +on_auto_name_playlist_from_folder_toggled + (GtkToggleButton *togglebutton, + gpointer user_data) +{ + int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (togglebutton)); + deadbeef->conf_set_int ("gtkui.name_playlist_from_folder", active); +} + +void +on_info_window_delete (GtkWidget *widget, GtkTextDirection previous_direction, GtkWidget **pwindow); + +static void +show_copyright_window (const char *text, const char *title, GtkWidget **pwindow) { + if (*pwindow) { + return; + } + GtkWidget *widget = *pwindow = create_helpwindow (); + g_object_set_data (G_OBJECT (widget), "pointer", pwindow); + g_signal_connect (widget, "delete_event", G_CALLBACK (on_info_window_delete), pwindow); + gtk_window_set_title (GTK_WINDOW (widget), title); + gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (prefwin)); + GtkWidget *txt = lookup_widget (widget, "helptext"); + GtkTextBuffer *buffer = gtk_text_buffer_new (NULL); + + gtk_text_buffer_set_text (buffer, text, strlen(text)); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (txt), buffer); + g_object_unref (buffer); + gtk_widget_show (widget); +} + +static GtkWidget *copyright_window; + +void +on_plug_copyright_clicked (GtkButton *button, + gpointer user_data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(lookup_widget (prefwin, "pref_pluginlist")); + GtkTreePath *path; + GtkTreeViewColumn *col; + gtk_tree_view_get_cursor (treeview, &path, &col); + if (!path || !col) { + // reset + return; + } + int *indices = gtk_tree_path_get_indices (path); + DB_plugin_t **plugins = deadbeef->plug_get_list (); + DB_plugin_t *p = plugins[*indices]; + g_free (indices); + assert (p); + + if (p->copyright) { + show_copyright_window (p->copyright, "Copyright", ©right_window); + } +} + +gboolean +on_prefwin_configure_event (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data) +{ + wingeom_save (widget, "prefwin"); + return FALSE; +} + + +gboolean +on_prefwin_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer user_data) +{ + wingeom_save_max (event, widget, "prefwin"); + return FALSE; +} + + +void +on_prefwin_realize (GtkWidget *widget, + gpointer user_data) +{ + wingeom_restore (widget, "prefwin", -1, -1, -1, -1, 0); +} + +void +on_gui_plugin_changed (GtkComboBox *combobox, + gpointer user_data) +{ + gchar *txt = gtk_combo_box_get_active_text (combobox); + if (txt) { + deadbeef->conf_set_str ("gui_plugin", txt); + g_free (txt); + } +} + +void +on_gui_fps_value_changed (GtkRange *range, + gpointer user_data) +{ + int val = gtk_range_get_value (range); + deadbeef->conf_set_int ("gtkui.refresh_rate", val); + gtkui_setup_gui_refresh (); +} + +void +on_ignore_archives_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + + deadbeef->conf_set_int ("ignore_archives", gtk_toggle_button_get_active (togglebutton)); +} + diff --git a/plugins/gtkui/progress.c b/plugins/gtkui/progress.c index d986a162..1941c40e 100644 --- a/plugins/gtkui/progress.c +++ b/plugins/gtkui/progress.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,9 +34,36 @@ static GtkWidget *progressdlg; static GtkWidget *progressitem; static int progress_aborted; +static void +on_progress_abort (GtkButton *button, + gpointer user_data) +{ + progress_aborted = 1; +} + +static gboolean +on_addprogress_delete_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + progress_aborted = 1; + return gtk_widget_hide_on_delete (widget); +} void progress_init (void) { - progressdlg = create_addprogress (); + progressdlg = create_progressdlg (); + + gtk_window_set_title (GTK_WINDOW (progressdlg), _("Adding files...")); + + g_signal_connect ((gpointer) progressdlg, "delete_event", + G_CALLBACK (on_addprogress_delete_event), + NULL); + + GtkWidget *cancelbtn = lookup_widget (progressdlg, "cancelbtn"); + g_signal_connect ((gpointer) cancelbtn, "clicked", + G_CALLBACK (on_progress_abort), + NULL); + gtk_window_set_transient_for (GTK_WINDOW (progressdlg), GTK_WINDOW (mainwin)); progressitem = lookup_widget (progressdlg, "progresstitle"); } @@ -81,24 +108,9 @@ progress_abort (void) { progress_aborted = 1; } -void -on_progress_abort (GtkButton *button, - gpointer user_data) -{ - progress_aborted = 1; -} - int progress_is_aborted (void) { return progress_aborted; } -gboolean -on_addprogress_delete_event (GtkWidget *widget, - GdkEvent *event, - gpointer user_data) -{ - progress_aborted = 1; - return gtk_widget_hide_on_delete (widget); -} diff --git a/plugins/gtkui/progress.h b/plugins/gtkui/progress.h index 49b20248..86558106 100644 --- a/plugins/gtkui/progress.h +++ b/plugins/gtkui/progress.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/plugins/gtkui/search.c b/plugins/gtkui/search.c index ee096930..4509d3d5 100644 --- a/plugins/gtkui/search.c +++ b/plugins/gtkui/search.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,6 +38,8 @@ #include "gtkui.h" +#include "wingeom.h" + #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) @@ -49,27 +51,8 @@ extern GtkWidget *searchwin; extern GtkWidget *mainwin; void -search_restore_attrs (void) { - int x = deadbeef->conf_get_int ("searchwin.geometry.x", -1); - int y = deadbeef->conf_get_int ("searchwin.geometry.y", -1); - int w = deadbeef->conf_get_int ("searchwin.geometry.w", 450); - int h = deadbeef->conf_get_int ("searchwin.geometry.h", 150); - gtk_widget_show (searchwin); - if (x != -1 && y != -1) { - gtk_window_move (GTK_WINDOW (searchwin), x, y); - gtk_window_resize (GTK_WINDOW (searchwin), w, h); - if (deadbeef->conf_get_int ("searchwin.geometry.maximized", 0)) { - gtk_window_maximize (GTK_WINDOW (searchwin)); - } - gtk_window_present (GTK_WINDOW (searchwin)); - } - else { - gtk_window_resize (GTK_WINDOW (searchwin), w, h); - } -} - -void search_start (void) { + wingeom_restore (searchwin, "searchwin", -1, -1, 450, 150, 0); gtk_entry_set_text (GTK_ENTRY (lookup_widget (searchwin, "searchentry")), ""); gtk_widget_show (searchwin); gtk_window_present (GTK_WINDOW (searchwin)); @@ -98,14 +81,14 @@ on_searchentry_changed (GtkEditable *editable, search_refresh (); // redraw main playlist to be in sync selection-wise - ddb_listview_refresh (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), DDB_REFRESH_LIST); } void search_refresh (void) { if (searchwin && gtk_widget_get_visible (searchwin)) { GtkWidget *pl = lookup_widget (searchwin, "searchlist"); - ddb_listview_refresh (DDB_LISTVIEW (pl), DDB_REFRESH_VSCROLL | DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (DDB_LISTVIEW (pl), DDB_REFRESH_VSCROLL | DDB_REFRESH_LIST); } } @@ -176,7 +159,7 @@ on_searchwin_key_press_event (GtkWidget *widget, int row = deadbeef->pl_get_cursor (PL_SEARCH); DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (max (row, 0), PL_SEARCH); if (it) { - deadbeef->sendmessage (M_PLAYSONGNUM, 0, deadbeef->pl_get_idx_of (it), 0); + deadbeef->sendmessage (M_PLAY_NUM, 0, deadbeef->pl_get_idx_of (it), 0); deadbeef->pl_item_unref (it); } } @@ -198,21 +181,7 @@ on_searchwin_configure_event (GtkWidget *widget, GdkEventConfigure *event, gpointer user_data) { -#if GTK_CHECK_VERSION(2,2,0) - GdkWindowState window_state = gdk_window_get_state (GDK_WINDOW (widget->window)); -#else - GdkWindowState window_state = gdk_window_get_state (G_OBJECT (widget)); -#endif - if (!(window_state & GDK_WINDOW_STATE_MAXIMIZED) && gtk_widget_get_visible (widget)) { - int x, y; - int w, h; - gtk_window_get_position (GTK_WINDOW (widget), &x, &y); - gtk_window_get_size (GTK_WINDOW (widget), &w, &h); - deadbeef->conf_set_int ("searchwin.geometry.x", x); - deadbeef->conf_set_int ("searchwin.geometry.y", y); - deadbeef->conf_set_int ("searchwin.geometry.w", w); - deadbeef->conf_set_int ("searchwin.geometry.h", h); - } + wingeom_save (widget, "searchwin"); return FALSE; } @@ -221,30 +190,7 @@ on_searchwin_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer user_data) { - if (!gtk_widget_get_visible (widget)) { - return FALSE; - } - // based on pidgin maximization handler -#if GTK_CHECK_VERSION(2,2,0) - if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { - if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { - deadbeef->conf_set_int ("searchwin.geometry.maximized", 1); - } - else { - deadbeef->conf_set_int ("searchwin.geometry.maximized", 0); - } - } -#else - GdkWindowState new_window_state = gdk_window_get_state(G_OBJECT(widget)); - - if () - if (new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { - deadbeef->conf_set_int ("searchwin.geometry.maximized", 1); - } - else { - deadbeef->conf_set_int ("searchwin.geometry.maximized", 0); - } -#endif + wingeom_save_max (event, widget, "searchwin"); return FALSE; } @@ -364,13 +310,13 @@ void search_col_free_user_data (void *data) { } void search_handle_doubleclick (DdbListview *listview, DdbListviewIter iter, int idx) { - deadbeef->sendmessage (M_PLAYSONGNUM, 0, deadbeef->pl_get_idx_of ((DB_playItem_t *)iter), 0); + deadbeef->sendmessage (M_PLAY_NUM, 0, deadbeef->pl_get_idx_of ((DB_playItem_t *)iter), 0); } void search_selection_changed (DdbListviewIter it, int idx) { DdbListview *main = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); if (idx == -1) { - ddb_listview_refresh (main, DDB_REFRESH_LIST | DDB_EXPOSE_LIST); + ddb_listview_refresh (main, DDB_REFRESH_LIST); } else { ddb_listview_draw_row (main, main_get_idx ((DB_playItem_t *)it), it); @@ -438,7 +384,7 @@ search_playlist_init (GtkWidget *widget) { if (!col) { add_column_helper (listview, _("Artist / Album"), 150, -1, "%a - %b", 0); add_column_helper (listview, _("Track No"), 50, -1, "%n", 1); - add_column_helper (listview, _("Title / Track Artist"), 150, -1, "%t", 0); + add_column_helper (listview, _("Title"), 150, -1, "%t", 0); add_column_helper (listview, _("Duration"), 50, -1, "%l", 0); } else { diff --git a/plugins/gtkui/search.h b/plugins/gtkui/search.h index 7e34d4db..98216d5c 100644 --- a/plugins/gtkui/search.h +++ b/plugins/gtkui/search.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,9 +30,6 @@ search_start (void); void search_refresh (void); -void -search_restore_attrs (void); - int search_get_idx (DdbListviewIter it); diff --git a/plugins/gtkui/support.h b/plugins/gtkui/support.h index a32649e5..b4b0ace3 100644 --- a/plugins/gtkui/support.h +++ b/plugins/gtkui/support.h @@ -27,7 +27,9 @@ # define dgettext(Domain,Message) (Message) # define dcgettext(Domain,Message,Type) (Message) # define bindtextdomain(Domain,Directory) (Domain) +#ifndef _ # define _(String) (String) +#endif # define Q_(String) g_strip_context ((String), (String)) # define N_(String) (String) #endif diff --git a/plugins/gtkui/tagwritersettings.c b/plugins/gtkui/tagwritersettings.c new file mode 100644 index 00000000..ede3d447 --- /dev/null +++ b/plugins/gtkui/tagwritersettings.c @@ -0,0 +1,204 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <gtk/gtk.h> +#include "../../gettext.h" +#include "interface.h" +#include "support.h" +#include "../../deadbeef.h" +#include "gtkui.h" + +void +run_tagwriter_settings (GtkWidget *parentwindow) { + GtkWidget *dlg = create_tagwritersettings (); + gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (parentwindow)); + + // tag writer + int strip_id3v2 = deadbeef->conf_get_int ("mp3.strip_id3v2", 0); + int strip_id3v1 = deadbeef->conf_get_int ("mp3.strip_id3v1", 0); + int strip_apev2 = deadbeef->conf_get_int ("mp3.strip_apev2", 0); + int write_id3v2 = deadbeef->conf_get_int ("mp3.write_id3v2", 1); + int write_id3v1 = deadbeef->conf_get_int ("mp3.write_id3v1", 1); + int write_apev2 = deadbeef->conf_get_int ("mp3.write_apev2", 0); + int id3v2_version = deadbeef->conf_get_int ("mp3.id3v2_version", 3); + char id3v1_encoding[50]; + deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1", id3v1_encoding, sizeof (id3v1_encoding)); + int ape_strip_id3v2 = deadbeef->conf_get_int ("ape.strip_id3v2", 0); + int ape_strip_apev2 = deadbeef->conf_get_int ("ape.strip_apev2", 0); + int ape_write_id3v2 = deadbeef->conf_get_int ("ape.write_id3v2", 0); + int ape_write_apev2 = deadbeef->conf_get_int ("ape.write_apev2", 1); + int wv_strip_apev2 = deadbeef->conf_get_int ("wv.strip_apev2", 0); + int wv_strip_id3v1 = deadbeef->conf_get_int ("wv.strip_id3v1", 0); + int wv_write_apev2 = deadbeef->conf_get_int ("wv.write_apev2", 1); + int wv_write_id3v1 = deadbeef->conf_get_int ("wv.write_id3v1", 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "strip_id3v2")), strip_id3v2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "strip_id3v1")), strip_id3v1); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "strip_apev2")), strip_apev2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "write_id3v2")), write_id3v2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "write_id3v1")), write_id3v1); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "write_apev2")), write_apev2); + gtk_combo_box_set_active (GTK_COMBO_BOX (lookup_widget (dlg, "id3v2_version")), id3v2_version != 4 ? 0 : 1); + gtk_entry_set_text (GTK_ENTRY (lookup_widget (dlg, "id3v1_encoding")), id3v1_encoding); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "ape_strip_id3v2")), ape_strip_id3v2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "ape_strip_apev2")), ape_strip_apev2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "ape_write_apev2")), ape_write_apev2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "ape_write_id3v2")), ape_write_id3v2); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "wv_strip_id3v1")), wv_strip_id3v1); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "wv_strip_apev2")), wv_strip_apev2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "wv_write_apev2")), wv_write_apev2); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (dlg, "wv_write_id3v1")), wv_write_id3v1); + + + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); +} + +void +on_write_id3v2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("mp3.write_id3v2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_write_id3v1_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("mp3.write_id3v1", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_write_apev2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("mp3.write_apev2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_strip_id3v2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("mp3.strip_id3v2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_strip_id3v1_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("mp3.strip_id3v1", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_strip_apev2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("mp3.strip_apev2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_id3v2_version_changed (GtkComboBox *combobox, + gpointer user_data) +{ + int version = 3; + int active = gtk_combo_box_get_active (combobox); + if (active == 1) { + version = 4; + } + deadbeef->conf_set_int ("mp3.id3v2_version", version); +} + + +void +on_id3v1_encoding_changed (GtkEditable *editable, + gpointer user_data) +{ + deadbeef->conf_set_str ("mp3.id3v1_encoding", gtk_entry_get_text (GTK_ENTRY (editable))); +} + + +void +on_ape_write_id3v2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("ape.write_id3v2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_ape_write_apev2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("ape.write_apev2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_ape_strip_id3v2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("ape.strip_id3v2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_ape_strip_apev2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("ape.strip_apev2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_wv_write_apev2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("wv.write_apev2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_wv_write_id3v1_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("wv.write_id3v1", gtk_toggle_button_get_active (togglebutton)); +} + +void +on_wv_strip_apev2_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("wv.strip_apev2", gtk_toggle_button_get_active (togglebutton)); +} + + +void +on_wv_strip_id3v1_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("wv.strip_id3v1", gtk_toggle_button_get_active (togglebutton)); +} diff --git a/plugins/gtkui/tagwritersettings.h b/plugins/gtkui/tagwritersettings.h new file mode 100644 index 00000000..a48aaacb --- /dev/null +++ b/plugins/gtkui/tagwritersettings.h @@ -0,0 +1,25 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __TAGWRITERSETTINGS_H +#define __TAGWRITERSETTINGS_H + +void +run_tagwriter_settings (GtkWidget *parentwindow); + +#endif diff --git a/plugins/gtkui/timeline.c b/plugins/gtkui/timeline.c index 4543f6c6..83fc7f0f 100644 --- a/plugins/gtkui/timeline.c +++ b/plugins/gtkui/timeline.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/timeline.h b/plugins/gtkui/timeline.h index 5325288e..4223a84b 100644 --- a/plugins/gtkui/timeline.h +++ b/plugins/gtkui/timeline.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index f60a7b0b..cc419d59 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,6 +23,7 @@ #include <gdk/gdkkeysyms.h> #include <string.h> #include <math.h> +#include <assert.h> #include "../../gettext.h" #include "ddblistview.h" #include "trkproperties.h" @@ -33,16 +34,121 @@ #include "mainplaylist.h" #include "search.h" #include "ddbcellrenderertextmultiline.h" +#include "tagwritersettings.h" +#include "wingeom.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) +#define min(x,y) ((x)<(y)?(x):(y)) + static GtkWidget *trackproperties; -static DB_playItem_t *track; static GtkCellRenderer *rend_text2; static GtkListStore *store; static GtkListStore *propstore; static int trkproperties_modified; +static DB_playItem_t **tracks; +static int numtracks; +static GtkWidget *progressdlg; +static int progress_aborted; + +static int +build_key_list (const char ***pkeys, int props) { + int sz = 20; + const char **keys = malloc (sizeof (const char *) * sz); + if (!keys) { + fprintf (stderr, "fatal: out of memory allocating key list\n"); + assert (0); + return 0; + } + + int n = 0; + + for (int i = 0; i < numtracks; i++) { + DB_metaInfo_t *meta = deadbeef->pl_get_metadata_head (tracks[i]); + while (meta) { + if ((props && meta->key[0] == ':') || (!props && meta->key[0] != ':')) { + int k = 0; + for (; k < n; k++) { + if (meta->key == keys[k]) { + break; + } + } + if (k == n) { + if (n >= sz) { + sz *= 2; + keys = realloc (keys, sizeof (const char *) * sz); + if (!keys) { + fprintf (stderr, "fatal: out of memory reallocating key list (%d keys)\n", sz); + assert (0); + } + } + keys[n++] = meta->key; + } + } + meta = meta->next; + } + } + + *pkeys = keys; + return n; +} + +static int +equals_ptr (const char *a, const char *b) { + return a == b; +} + +static int +get_field_value (char *out, int size, const char *key, const char *(*getter)(DB_playItem_t *it, const char *key), int (*equals)(const char *a, const char *b)) { + int multiple = 0; + *out = 0; + if (numtracks == 0) { + return 0; + } + char *p = out; + const char **prev = malloc (sizeof (const char *) * numtracks); + memset (prev, 0, sizeof (const char *) * numtracks); + for (int i = 0; i < numtracks; i++) { + const char *val = getter (tracks[i], key); + if (val && val[0] == 0) { + val = NULL; + } + if (i > 0) { + int n = 0; + for (; n < i; n++) { + if (equals (prev[n], val)) { + break; + } + } + if (n == i) { + multiple = 1; + if (val) { + size_t l = snprintf (out, size, out == p ? "%s" : "; %s", val ? val : ""); + l = min (l, size); + out += l; + size -= l; + } + } + } + else if (val) { + size_t l = snprintf (out, size, "%s", val ? val : ""); + l = min (l, size); + out += l; + size -= l; + } + prev[i] = val; + if (size <= 1) { + break; + } + } + if (size <= 1) { + gchar *prev = g_utf8_prev_char (out-4); + strcpy (prev, "..."); + } + free (prev); + return multiple; +} gboolean on_trackproperties_delete_event (GtkWidget *widget, @@ -50,7 +156,7 @@ on_trackproperties_delete_event (GtkWidget *widget, gpointer user_data) { if (trkproperties_modified) { - GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (mainwin), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("You've modified data for this track.")); + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (trackproperties), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("You've modified data for this track.")); gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (trackproperties)); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), _("Really close the window?")); gtk_window_set_title (GTK_WINDOW (dlg), _("Warning")); @@ -64,9 +170,13 @@ on_trackproperties_delete_event (GtkWidget *widget, gtk_widget_destroy (widget); rend_text2 = NULL; trackproperties = NULL; - if (track) { - deadbeef->pl_item_unref (track); - track = NULL; + if (tracks) { + for (int i = 0; i < numtracks; i++) { + deadbeef->pl_item_unref (tracks[i]); + } + free (tracks); + tracks = NULL; + numtracks = 0; } return TRUE; } @@ -108,7 +218,7 @@ on_metadata_edited (GtkCellRendererText *renderer, gchar *path, gchar *new_text, gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 1, &value); const char *svalue = g_value_get_string (&value); if (strcmp (svalue, new_text)) { - gtk_list_store_set (store, &iter, 1, new_text, -1); + gtk_list_store_set (store, &iter, 1, new_text, 3, 0, -1); trkproperties_modified = 1; } } @@ -117,8 +227,6 @@ on_metadata_edited (GtkCellRendererText *renderer, gchar *path, gchar *new_text, static const char *types[] = { "artist", "Artist", "title", "Track Title", - "performer", "Performer", - "band", "Band / Album Artist", "album", "Album", "year", "Date", "track", "Track Number", @@ -127,17 +235,36 @@ static const char *types[] = { "composer", "Composer", "disc", "Disc Number", "comment", "Comment", - "vendor", "Encoder / Vendor", - "copyright", "Copyright", - // nonstandard frames, let's hide them for now -// "<performer>", "<PERFORMER>", -// "<albumartist>", "<ALBUM ARTIST>", NULL }; -static inline float -amp_to_db (float amp) { - return 20*log10 (amp); +static const char *hc_props[] = { + ":URI", "Location", + ":TRACKNUM", "Subtrack Index", + ":DURATION", "Duration", + ":TAGS", "Tag Type(s)", + ":HAS_EMBEDDED_CUESHEET", "Embedded Cuesheet", + ":DECODER", "Codec", + NULL +}; + +void +add_field (GtkListStore *store, const char *key, const char *title, int is_prop) { + // get value to edit + const char *mult = is_prop ? "" : _("[Multiple values] "); + char val[1000]; + size_t ml = strlen (mult); + memcpy (val, mult, ml+1); + int n = get_field_value (val + ml, sizeof (val) - ml, key, deadbeef->pl_find_meta, equals_ptr); + + GtkTreeIter iter; + gtk_list_store_append (store, &iter); + if (!is_prop) { + gtk_list_store_set (store, &iter, 0, title, 1, n ? val : val + ml, 2, key, 3, n ? 1 : 0, -1); + } + else { + gtk_list_store_set (store, &iter, 0, title, 1, n ? val : val + ml, -1); + } } void @@ -148,105 +275,126 @@ trkproperties_fill_metadata (void) { trkproperties_modified = 0; gtk_list_store_clear (store); deadbeef->pl_lock (); - int i = 0; - while (types[i]) { - GtkTreeIter iter; - gtk_list_store_append (store, &iter); - const char *value = deadbeef->pl_find_meta (track, types[i]); - if (!value) { - value = ""; + + const char **keys = NULL; + int nkeys = build_key_list (&keys, 0); + + int k; + + // add "standard" fields + for (int i = 0; types[i]; i += 2) { + add_field (store, types[i], _(types[i+1]), 0); + } + + // add all other fields + for (int k = 0; k < nkeys; k++) { + int i; + for (i = 0; types[i]; i += 2) { + if (!strcasecmp (keys[k], types[i])) { + break; + } } - gtk_list_store_set (store, &iter, 0, _(types[i+1]), 1, value, 2, types[i], -1); - i += 2; + if (types[i]) { + continue; + } + + char title[1000]; + if (!types[i]) { + snprintf (title, sizeof (title), "<%s>", keys[k]); + } + add_field (store, keys[k], title, 0); + } + if (keys) { + free (keys); } - deadbeef->pl_unlock (); + // hardcoded properties + for (int i = 0; hc_props[i]; i += 2) { + add_field (propstore, hc_props[i], _(hc_props[i+1]), 1); + } // properties - char temp[200]; - GtkTreeIter iter; - gtk_list_store_clear (propstore); - gtk_list_store_append (propstore, &iter); - gtk_list_store_set (propstore, &iter, 0, _("Location"), 1, track->fname, -1); - gtk_list_store_append (propstore, &iter); - snprintf (temp, sizeof (temp), "%d", track->tracknum); - gtk_list_store_set (propstore, &iter, 0, _("Subtrack Index"), 1, temp, -1); - gtk_list_store_append (propstore, &iter); - deadbeef->pl_format_time (deadbeef->pl_get_item_duration (track), temp, sizeof (temp)); - gtk_list_store_set (propstore, &iter, 0, _("Duration"), 1, temp, -1); - gtk_list_store_append (propstore, &iter); - deadbeef->pl_format_title (track, -1, temp, sizeof (temp), -1, "%T"); - gtk_list_store_set (propstore, &iter, 0, _("Tag Type(s)"), 1, temp, -1); - gtk_list_store_append (propstore, &iter); - gtk_list_store_set (propstore, &iter, 0, _("Embedded Cuesheet"), 1, (deadbeef->pl_get_item_flags (track) & DDB_HAS_EMBEDDED_CUESHEET) ? _("Yes") : _("No"), -1); - gtk_list_store_append (propstore, &iter); - gtk_list_store_set (propstore, &iter, 0, _("Codec"), 1, track->decoder_id, -1); - - gtk_list_store_append (propstore, &iter); - snprintf (temp, sizeof (temp), "%0.2f dB", track->replaygain_album_gain); - gtk_list_store_set (propstore, &iter, 0, "REPLAYGAIN_ALBUM_GAIN", 1, temp, -1); - gtk_list_store_append (propstore, &iter); - snprintf (temp, sizeof (temp), "%0.6f", track->replaygain_album_peak); - gtk_list_store_set (propstore, &iter, 0, "REPLAYGAIN_ALBUM_PEAK", 1, temp, -1); - - gtk_list_store_append (propstore, &iter); - snprintf (temp, sizeof (temp), "%0.2f dB", track->replaygain_track_gain); - gtk_list_store_set (propstore, &iter, 0, "REPLAYGAIN_TRACK_GAIN", 1, temp, -1); - gtk_list_store_append (propstore, &iter); - snprintf (temp, sizeof (temp), "%0.6f", track->replaygain_track_peak); - gtk_list_store_set (propstore, &iter, 0, "REPLAYGAIN_TRACK_PEAK", 1, temp, -1); -} - -void -show_track_properties_dlg (DB_playItem_t *it) { - if (track) { - deadbeef->pl_item_unref (track); - track = NULL; + keys = NULL; + nkeys = build_key_list (&keys, 1); + for (int k = 0; k < nkeys; k++) { + int i; + for (i = 0; hc_props[i]; i += 2) { + if (!strcasecmp (keys[k], hc_props[i])) { + break; + } + } + if (hc_props[i]) { + continue; + } + char title[1000]; + snprintf (title, sizeof (title), "<%s>", keys[k]+1); + add_field (propstore, keys[k], title, 1); } - if (it) { - deadbeef->pl_item_ref (it); + if (keys) { + free (keys); } - track = it; + deadbeef->pl_unlock (); +} - int allow_editing = 0; +void +show_track_properties_dlg (void) { - int is_subtrack = deadbeef->pl_get_item_flags (it) & DDB_IS_SUBTRACK; + deadbeef->plt_lock (); + deadbeef->pl_lock (); - if (!is_subtrack && deadbeef->is_local_file (it->fname)) { - // get decoder plugin by id - DB_decoder_t *dec = NULL; - if (it->decoder_id) { - DB_decoder_t **decoders = deadbeef->plug_get_decoder_list (); - for (int i = 0; decoders[i]; i++) { - if (!strcmp (decoders[i]->plugin.id, it->decoder_id)) { - dec = decoders[i]; - break; + if (tracks) { + for (int i = 0; i < numtracks; i++) { + deadbeef->pl_item_unref (tracks[i]); + } + free (tracks); + tracks = NULL; + numtracks = 0; + } + + int nsel = deadbeef->pl_getselcount (); + if (0 < nsel) { + tracks = malloc (sizeof (DB_playItem_t *) * nsel); + if (tracks) { + int n = 0; + DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); + while (it) { + if (deadbeef->pl_is_selected (it)) { + assert (n < nsel); + deadbeef->pl_item_ref (it); + tracks[n++] = it; } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; } + numtracks = nsel; } - - if (dec && dec->write_metadata) { - allow_editing = 1; + else { + deadbeef->pl_unlock (); + deadbeef->plt_unlock (); + return; } } + deadbeef->pl_unlock (); + deadbeef->plt_unlock (); + GtkTreeView *tree; GtkTreeView *proptree; if (!trackproperties) { trackproperties = create_trackproperties (); gtk_window_set_transient_for (GTK_WINDOW (trackproperties), GTK_WINDOW (mainwin)); + wingeom_restore (trackproperties, "trkproperties", -1, -1, 300, 400, 0); // metadata tree tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); - store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); gtk_tree_view_set_model (tree, GTK_TREE_MODEL (store)); GtkCellRenderer *rend_text = gtk_cell_renderer_text_new (); - rend_text2 = GTK_CELL_RENDERER (ddb_cell_renderer_text_multiline_new ());//gtk_cell_renderer_text_new (); - if (allow_editing) { - g_signal_connect ((gpointer)rend_text2, "edited", - G_CALLBACK (on_metadata_edited), - store); - } + rend_text2 = GTK_CELL_RENDERER (ddb_cell_renderer_text_multiline_new ()); + g_signal_connect ((gpointer)rend_text2, "edited", + G_CALLBACK (on_metadata_edited), + store); GtkTreeViewColumn *col1 = gtk_tree_view_column_new_with_attributes (_("Key"), rend_text, "text", 0, NULL); GtkTreeViewColumn *col2 = gtk_tree_view_column_new_with_attributes (_("Value"), rend_text2, "text", 1, NULL); gtk_tree_view_append_column (tree, col1); @@ -273,12 +421,7 @@ show_track_properties_dlg (DB_playItem_t *it) { gtk_list_store_clear (propstore); } -// if (allow_editing) { - g_object_set (G_OBJECT (rend_text2), "editable", TRUE, NULL); -// } -// else { -// g_object_set (G_OBJECT (rend_text2), "editable", FALSE, NULL); -// } + g_object_set (G_OBJECT (rend_text2), "editable", TRUE, NULL); GtkWidget *widget = trackproperties; GtkWidget *w; @@ -286,12 +429,7 @@ show_track_properties_dlg (DB_playItem_t *it) { trkproperties_fill_metadata (); - if (allow_editing) { - gtk_widget_set_sensitive (lookup_widget (widget, "write_tags"), TRUE); - } - else { - gtk_widget_set_sensitive (lookup_widget (widget, "write_tags"), FALSE); - } + gtk_widget_set_sensitive (lookup_widget (widget, "write_tags"), TRUE); gtk_widget_show (widget); gtk_window_present (GTK_WINDOW (widget)); @@ -299,46 +437,322 @@ show_track_properties_dlg (DB_playItem_t *it) { static gboolean set_metadata_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { - GValue key = {0,}, value = {0,}; - gtk_tree_model_get_value (model, iter, 2, &key); - gtk_tree_model_get_value (model, iter, 1, &value); - const char *skey = g_value_get_string (&key); - const char *svalue = g_value_get_string (&value); + GValue mult = {0,}; + gtk_tree_model_get_value (model, iter, 3, &mult); + int smult = g_value_get_int (&mult); + if (!smult) { + GValue key = {0,}, value = {0,}; + gtk_tree_model_get_value (model, iter, 2, &key); + gtk_tree_model_get_value (model, iter, 1, &value); + const char *skey = g_value_get_string (&key); + const char *svalue = g_value_get_string (&value); + if (*svalue) { + for (int i = 0; i < numtracks; i++) { + deadbeef->pl_replace_meta (tracks[i], skey, svalue); + } + } + else { + for (int i = 0; i < numtracks; i++) { + deadbeef->pl_delete_meta (tracks[i], skey); + } + } + } + return FALSE; +} - for (int i = 0; types[i]; i += 2) { - if (!strcmp (skey, types[i])) { - deadbeef->pl_replace_meta (DB_PLAYITEM (data), types[i], svalue); +static gboolean +write_finished_cb (void *ctx) { + gtk_widget_destroy (progressdlg); + progressdlg = NULL; + main_refresh (); + search_refresh (); + trkproperties_modified = 0; + return FALSE; +} + +static gboolean +set_progress_cb (void *ctx) { + DB_playItem_t *track = ctx; + GtkWidget *progressitem = lookup_widget (progressdlg, "progresstitle"); + const char *fname = deadbeef->pl_find_meta (track, ":URI"); + gtk_entry_set_text (GTK_ENTRY (progressitem), fname); + deadbeef->pl_item_unref (track); +} + +static void +write_meta_worker (void *ctx) { + for (int t = 0; t < numtracks; t++) { + if (progress_aborted) { + break; + } + DB_playItem_t *track = tracks[t]; + const char *decoder_id = deadbeef->pl_find_meta (track, ":DECODER"); + if (track && decoder_id) { + int is_subtrack = deadbeef->pl_get_item_flags (track) & DDB_IS_SUBTRACK; + if (is_subtrack) { + continue; + } + deadbeef->pl_item_ref (track); + g_idle_add (set_progress_cb, track); + // find decoder + DB_decoder_t *dec = NULL; + DB_decoder_t **decoders = deadbeef->plug_get_decoder_list (); + for (int i = 0; decoders[i]; i++) { + if (!strcmp (decoders[i]->plugin.id, decoder_id)) { + dec = decoders[i]; + if (dec->write_metadata) { + dec->write_metadata (track); + } + break; + } + } } } + g_idle_add (write_finished_cb, ctx); +} - return FALSE; +static gboolean +on_progress_delete_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + progress_aborted = 1; + return gtk_widget_hide_on_delete (widget); +} + +static void +on_progress_abort (GtkButton *button, + gpointer user_data) +{ + progress_aborted = 1; } void on_write_tags_clicked (GtkButton *button, gpointer user_data) { - if (!track || !track->decoder_id) { - return; + deadbeef->pl_lock (); + GtkTreeView *tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); + GtkTreeModel *model = GTK_TREE_MODEL (gtk_tree_view_get_model (tree)); + + // delete all metadata properties that are not in the listview + for (int i = 0; i < numtracks; i++) { + DB_metaInfo_t *meta = deadbeef->pl_get_metadata_head (tracks[i]); + while (meta) { + DB_metaInfo_t *next = meta->next; + if (meta->key[0] != ':') { + GtkTreeIter iter; + gboolean res = gtk_tree_model_get_iter_first (model, &iter); + int mult = 0; + while (res) { + GValue key = {0,}; + gtk_tree_model_get_value (model, &iter, 2, &key); + const char *skey = g_value_get_string (&key); + + if (!strcasecmp (skey, meta->key)) { + // field found, don't delete + break; + } + res = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + } + if (!res) { + // field not found, delete + deadbeef->pl_delete_metadata (tracks[i], meta); + } + } + meta = next; + } } - // find decoder - DB_decoder_t *dec = NULL; - DB_decoder_t **decoders = deadbeef->plug_get_decoder_list (); - for (int i = 0; decoders[i]; i++) { - if (!strcmp (decoders[i]->plugin.id, track->decoder_id)) { - dec = decoders[i]; - if (dec->write_metadata) { - // put all metainfo into track - GtkTreeView *tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); - GtkTreeModel *model = GTK_TREE_MODEL (gtk_tree_view_get_model (tree)); - gtk_tree_model_foreach (model, set_metadata_cb, track); - dec->write_metadata (track); - main_refresh (); - search_refresh (); + // put all metainfo into track + gtk_tree_model_foreach (model, set_metadata_cb, NULL); + deadbeef->pl_unlock (); + + progress_aborted = 0; + progressdlg = create_progressdlg (); + gtk_window_set_title (GTK_WINDOW (progressdlg), _("Writing tags...")); + + g_signal_connect ((gpointer) progressdlg, "delete_event", + G_CALLBACK (on_progress_delete_event), + NULL); + GtkWidget *cancelbtn = lookup_widget (progressdlg, "cancelbtn"); + g_signal_connect ((gpointer) cancelbtn, "clicked", + G_CALLBACK (on_progress_abort), + NULL); + + gtk_widget_show_all (progressdlg); + gtk_window_present (GTK_WINDOW (progressdlg)); + gtk_window_set_transient_for (GTK_WINDOW (progressdlg), GTK_WINDOW (trackproperties)); + + // start new thread for writing metadata + intptr_t tid = deadbeef->thread_start (write_meta_worker, NULL); + deadbeef->thread_detach (tid); +} + +void +on_add_field_activate (GtkMenuItem *menuitem, + gpointer user_data) { + GtkWidget *dlg = create_entrydialog (); + gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK); + gtk_window_set_title (GTK_WINDOW (dlg), _("Edit playlist")); + GtkWidget *e; + e = lookup_widget (dlg, "title_label"); + gtk_label_set_text (GTK_LABEL(e), _("Name:")); + for (;;) { + int res = gtk_dialog_run (GTK_DIALOG (dlg)); + if (res == GTK_RESPONSE_OK) { + e = lookup_widget (dlg, "title"); + + const char *text = gtk_entry_get_text (GTK_ENTRY(e)); + + GtkTreeIter iter; + + // check for _ and : + if (text[0] == '_' || text[0] == ':') { + GtkWidget *d = gtk_message_dialog_new (GTK_WINDOW (dlg), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Field names must not start with : or _")); + gtk_window_set_title (GTK_WINDOW (d), _("Cannot add field")); + + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + continue; } + + // check if a field with the same name already exists + int dup = 0; + gboolean res = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + while (res) { + GValue value = {0,}; + gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 2, &value); + const char *svalue = g_value_get_string (&value); + if (!strcasecmp (svalue, text)) { + dup = 1; + break; + } + res = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + } + + if (!dup) { + int l = strlen (text); + char title[l+3]; + snprintf (title, sizeof (title), "<%s>", text); + const char *value = ""; + const char *key = text; + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, title, 1, value, 2, key, -1); + trkproperties_modified = 1; + } + else { + GtkWidget *d = gtk_message_dialog_new (GTK_WINDOW (dlg), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Field with such name already exists, please try different name.")); + gtk_window_set_title (GTK_WINDOW (d), _("Cannot add field")); + + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + continue; + } + } + break; + } + gtk_widget_destroy (dlg); +} + +void +on_remove_field_activate (GtkMenuItem *menuitem, + gpointer user_data) { + + GtkTreePath *path; + GtkTreeViewColumn *col; + GtkTreeView *treeview = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); + gtk_tree_view_get_cursor (treeview, &path, &col); + if (!path || !col) { + return; + } + + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (trackproperties), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Really remove selected field?")); + gtk_window_set_title (GTK_WINDOW (dlg), _("Warning")); + + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + if (response != GTK_RESPONSE_YES) { + return; + } + + GtkTreeIter iter; + gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path); + GValue value = {0,}; + gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 2, &value); + const char *svalue = g_value_get_string (&value); + + // delete unknown fields completely; otherwise just clear + int i = 0; + for (; types[i]; i += 2) { + if (!strcasecmp (svalue, types[i])) { break; } } - trkproperties_modified = 0; + if (types[i]) { // known val, clear + gtk_list_store_set (store, &iter, 1, "", 3, 0, -1); + } + else { + gtk_list_store_remove (store, &iter); + } + gtk_tree_path_free (path); + trkproperties_modified = 1; +} + +gboolean +on_metalist_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + if (event->button == 3) { + GtkWidget *menu; + GtkWidget *add; + GtkWidget *remove; + menu = gtk_menu_new (); + add = gtk_menu_item_new_with_mnemonic (_("Add field")); + gtk_widget_show (add); + gtk_container_add (GTK_CONTAINER (menu), add); + remove = gtk_menu_item_new_with_mnemonic (_("Remove field")); + gtk_widget_show (remove); + gtk_container_add (GTK_CONTAINER (menu), remove); + + g_signal_connect ((gpointer) add, "activate", + G_CALLBACK (on_add_field_activate), + NULL); + + g_signal_connect ((gpointer) remove, "activate", + G_CALLBACK (on_remove_field_activate), + NULL); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, event->button, gtk_get_current_event_time()); + } + return FALSE; +} + +void +on_tagwriter_settings_clicked (GtkButton *button, + gpointer user_data) +{ + run_tagwriter_settings (trackproperties); +} + +gboolean +on_trackproperties_configure_event (GtkWidget *widget, + GdkEventConfigure *event, + gpointer user_data) +{ + wingeom_save (widget, "trkproperties"); + return FALSE; } + + +gboolean +on_trackproperties_window_state_event (GtkWidget *widget, + GdkEventWindowState *event, + gpointer user_data) +{ + wingeom_save_max (event, widget, "trkproperties"); + return FALSE; +} + diff --git a/plugins/gtkui/trkproperties.h b/plugins/gtkui/trkproperties.h index 8bd1a903..ccdaa69f 100644 --- a/plugins/gtkui/trkproperties.h +++ b/plugins/gtkui/trkproperties.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -22,7 +22,7 @@ struct DB_playItem_s; void -show_track_properties_dlg (struct DB_playItem_s *it); +show_track_properties_dlg (void); void trkproperties_destroy (void); diff --git a/plugins/gtkui/wingeom.c b/plugins/gtkui/wingeom.c new file mode 100644 index 00000000..8591a5c6 --- /dev/null +++ b/plugins/gtkui/wingeom.c @@ -0,0 +1,101 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <gtk/gtk.h> +#include "wingeom.h" +#include "../../deadbeef.h" +#include "gtkui.h" + +void +wingeom_save (GtkWidget *widget, const char *name) { +#if GTK_CHECK_VERSION(2,2,0) + GdkWindowState window_state = gdk_window_get_state (GDK_WINDOW (widget->window)); +#else + GdkWindowState window_state = gdk_window_get_state (G_OBJECT (widget)); +#endif + if (!(window_state & GDK_WINDOW_STATE_MAXIMIZED) && gtk_widget_get_visible (widget)) { + int x, y; + int w, h; + char key[100]; + gtk_window_get_position (GTK_WINDOW (widget), &x, &y); + gtk_window_get_size (GTK_WINDOW (widget), &w, &h); + snprintf (key, sizeof (key), "%s.geometry.x", name); + deadbeef->conf_set_int (key, x); + snprintf (key, sizeof (key), "%s.geometry.y", name); + deadbeef->conf_set_int (key, y); + snprintf (key, sizeof (key), "%s.geometry.w", name); + deadbeef->conf_set_int (key, w); + snprintf (key, sizeof (key), "%s.geometry.h", name); + deadbeef->conf_set_int (key, h); + } +} + +void +wingeom_save_max (GdkEventWindowState *event, GtkWidget *widget, const char *name) { + if (!gtk_widget_get_visible (widget)) { + return; + } + char key[100]; + snprintf (key, sizeof (key), "%s.geometry.maximized", name); + // based on pidgin maximization handler +#if GTK_CHECK_VERSION(2,2,0) + if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { + if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { + deadbeef->conf_set_int (key, 1); + } + else { + deadbeef->conf_set_int (key, 0); + } + } +#else + GdkWindowState new_window_state = gdk_window_get_state(G_OBJECT(widget)); + + if (new_window_state & GDK_WINDOW_STATE_MAXIMIZED) { + deadbeef->conf_set_int (key, 1); + } + else { + deadbeef->conf_set_int (key, 0); + } +#endif +} + +void +wingeom_restore (GtkWidget *win, const char *name, int dx, int dy, int dw, int dh, int dmax) { + char key[100]; + snprintf (key, sizeof (key), "%s.geometry.x", name); + int x = deadbeef->conf_get_int (key, dx); + snprintf (key, sizeof (key), "%s.geometry.y", name); + int y = deadbeef->conf_get_int (key, dy); + snprintf (key, sizeof (key), "%s.geometry.w", name); + int w = deadbeef->conf_get_int (key, dw); + snprintf (key, sizeof (key), "%s.geometry.h", name); + int h = deadbeef->conf_get_int (key, dh); + if (x != -1 && y != -1) { + gtk_window_move (GTK_WINDOW (win), x, y); + } + if (w != -1 && h != -1) { + gtk_window_resize (GTK_WINDOW (win), w, h); + } + snprintf (key, sizeof (key), "%s.geometry.maximized", name); + if (deadbeef->conf_get_int (key, dmax)) { + gtk_window_maximize (GTK_WINDOW (win)); + } +} diff --git a/plugins/gtkui/wingeom.h b/plugins/gtkui/wingeom.h new file mode 100644 index 00000000..9b468846 --- /dev/null +++ b/plugins/gtkui/wingeom.h @@ -0,0 +1,31 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __WINGEOM_H +#define __WINGEOM_H + +void +wingeom_save (GtkWidget *widget, const char *name); + +void +wingeom_save_max (GdkEventWindowState *event, GtkWidget *widget, const char *name); + +void +wingeom_restore (GtkWidget *win, const char *name, int dx, int dy, int dw, int dh, int dmax); + +#endif diff --git a/plugins/hotkeys/hotkeys.c b/plugins/hotkeys/hotkeys.c index 2be24387..ec8f2e96 100644 --- a/plugins/hotkeys/hotkeys.c +++ b/plugins/hotkeys/hotkeys.c @@ -111,7 +111,7 @@ cmd_stop_after_current (void *unused) { int var = deadbeef->conf_get_int ("playlist.stop_after_current", 0); var = 1 - var; deadbeef->conf_set_int ("playlist.stop_after_current", var); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); } /* @@ -461,7 +461,7 @@ hotkeys_event_loop (void *unused) { } static int -hotkeys_start (void) { +hotkeys_connect (void) { finished = 0; loop_tid = 0; disp = XOpenDisplay (NULL); @@ -475,12 +475,11 @@ hotkeys_start (void) { read_config (disp); XSync (disp, 0); loop_tid = deadbeef->thread_start (hotkeys_event_loop, 0); - return 0; } static int -hotkeys_stop (void) { +hotkeys_disconnect (void) { if (loop_tid) { finished = 1; deadbeef->thread_join (loop_tid); @@ -508,31 +507,31 @@ hotkeys_reset (void) { int action_play_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { - deadbeef->playback_play (); + deadbeef->sendmessage (M_PLAY_CURRENT, 0, 0, 0); return 0; } int action_prev_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { - deadbeef->playback_prev (); + deadbeef->sendmessage (M_PREV, 0, 0, 0); return 0; } int action_next_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { - deadbeef->playback_next (); + deadbeef->sendmessage (M_NEXT, 0, 0, 0); return 0; } int action_stop_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { - deadbeef->playback_stop (); + deadbeef->sendmessage (M_STOP, 0, 0, 0); return 0; } int action_toggle_pause_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { - deadbeef->playback_pause (); + deadbeef->sendmessage (M_TOGGLE_PAUSE, 0, 0, 0); return 0; } @@ -540,17 +539,17 @@ int action_play_pause_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { int state = deadbeef->get_output ()->state (); if (state == OUTPUT_STATE_PLAYING) { - deadbeef->playback_pause (); + deadbeef->sendmessage (M_PAUSE, 0, 0, 0); } else { - deadbeef->playback_play (); + deadbeef->sendmessage (M_PLAY_CURRENT, 0, 0, 0); } return 0; } int action_play_random_cb (struct DB_plugin_action_s *action, DB_playItem_t *it) { - deadbeef->playback_random (); + deadbeef->sendmessage (M_PLAY_RANDOM, 0, 0, 0); return 0; } @@ -583,7 +582,7 @@ action_toggle_stop_after_current_cb (struct DB_plugin_action_s *action, DB_playI int var = deadbeef->conf_get_int ("playlist.stop_after_current", 0); var = 1 - var; deadbeef->conf_set_int ("playlist.stop_after_current", var); - deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); + deadbeef->sendmessage (M_CONFIG_CHANGED, 0, 0, 0); return 0; } @@ -693,16 +692,34 @@ hotkeys_get_actions (DB_playItem_t *it) static DB_hotkeys_plugin_t plugin = { .misc.plugin.api_vmajor = DB_API_VERSION_MAJOR, .misc.plugin.api_vminor = DB_API_VERSION_MINOR, + .misc.plugin.version_major = 1, + .misc.plugin.version_minor = 0, .misc.plugin.type = DB_PLUGIN_MISC, .misc.plugin.id = "hotkeys", .misc.plugin.name = "Global hotkeys support", .misc.plugin.descr = "Allows to control player with global hotkeys", - .misc.plugin.author = "Viktor Semykin", - .misc.plugin.email = "thesame.ml@gmail.com", + .misc.plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .misc.plugin.website = "http://deadbeef.sf.net", - .misc.plugin.start = hotkeys_start, - .misc.plugin.stop = hotkeys_stop, .misc.plugin.get_actions = hotkeys_get_actions, + .misc.plugin.connect = hotkeys_connect, + .misc.plugin.disconnect = hotkeys_disconnect, .get_name_for_keycode = hotkeys_get_name_for_keycode, .reset = hotkeys_reset, }; diff --git a/plugins/hotkeys/hotkeys.h b/plugins/hotkeys/hotkeys.h index f520aa41..010f0919 100644 --- a/plugins/hotkeys/hotkeys.h +++ b/plugins/hotkeys/hotkeys.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c index 4a234e8f..b29355b1 100644 --- a/plugins/lastfm/lastfm.c +++ b/plugins/lastfm/lastfm.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -67,13 +67,15 @@ static DB_playItem_t *lfm_subm_queue[LFM_SUBMISSION_QUEUE_SIZE]; static void lfm_update_auth (void) { - const char *user = deadbeef->conf_get_str ("lastfm.login", ""); - const char *pass = deadbeef->conf_get_str ("lastfm.password", ""); + deadbeef->conf_lock (); + const char *user = deadbeef->conf_get_str_fast ("lastfm.login", ""); + const char *pass = deadbeef->conf_get_str_fast ("lastfm.password", ""); if (strcmp (user, lfm_user) || strcmp (pass, lfm_pass)) { strcpy (lfm_user, user); strcpy (lfm_pass, pass); lfm_sess[0] = 0; } + deadbeef->conf_unlock (); } static size_t @@ -128,9 +130,10 @@ curl_req_send (const char *req, const char *post) { curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(post)); } if (deadbeef->conf_get_int ("network.proxy", 0)) { - curl_easy_setopt (curl, CURLOPT_PROXY, deadbeef->conf_get_str ("network.proxy.address", "")); + deadbeef->conf_lock (); + curl_easy_setopt (curl, CURLOPT_PROXY, deadbeef->conf_get_str_fast ("network.proxy.address", "")); curl_easy_setopt (curl, CURLOPT_PROXYPORT, deadbeef->conf_get_int ("network.proxy.port", 8080)); - const char *type = deadbeef->conf_get_str ("network.proxy.type", "HTTP"); + const char *type = deadbeef->conf_get_str_fast ("network.proxy.type", "HTTP"); int curlproxytype = CURLPROXY_HTTP; if (!strcasecmp (type, "HTTP")) { curlproxytype = CURLPROXY_HTTP; @@ -158,8 +161,8 @@ curl_req_send (const char *req, const char *post) { #endif curl_easy_setopt (curl, CURLOPT_PROXYTYPE, curlproxytype); - const char *proxyuser = deadbeef->conf_get_str ("network.proxy.username", ""); - const char *proxypass = deadbeef->conf_get_str ("network.proxy.password", ""); + const char *proxyuser = deadbeef->conf_get_str_fast ("network.proxy.username", ""); + const char *proxypass = deadbeef->conf_get_str_fast ("network.proxy.password", ""); if (*proxyuser || *proxypass) { #if LIBCURL_VERSION_MINOR >= 19 && LIBCURL_VERSION_PATCH >= 1 curl_easy_setopt (curl, CURLOPT_PROXYUSERNAME, proxyuser); @@ -170,6 +173,7 @@ curl_req_send (const char *req, const char *post) { curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, pwd); #endif } + deadbeef->conf_unlock (); } int status = curl_easy_perform(curl); curl_easy_cleanup (curl); @@ -207,12 +211,14 @@ auth (void) { deadbeef->md5 (sig, token, strlen (token)); deadbeef->md5_to_str (token, sig); - const char *scrobbler_url = deadbeef->conf_get_str ("lastfm.scrobbler_url", SCROBBLER_URL_LFM); + deadbeef->conf_lock (); + const char *scrobbler_url = deadbeef->conf_get_str_fast ("lastfm.scrobbler_url", SCROBBLER_URL_LFM); #if LFM_TESTMODE snprintf (req, sizeof (req), "%s/?hs=true&p=1.2.1&c=tst&v=1.0&u=%s&t=%d&a=%s", scrobbler_url, lfm_user, (int)timestamp, token); #else snprintf (req, sizeof (req), "%s/?hs=true&p=1.2.1&c=%s&v=%d.%d&u=%s&t=%d&a=%s", scrobbler_url, LFM_CLIENTID, plugin.plugin.version_major, plugin.plugin.version_minor, lfm_user, (int)timestamp, token); #endif + deadbeef->conf_unlock (); // handshake int status = curl_req_send (req, NULL); if (!status) { @@ -846,7 +852,6 @@ lfm_action_lookup (DB_plugin_action_t *action, DB_playItem_t *it) char *command = NULL; if (-1 == asprintf (&command, "xdg-open 'http://www.last.fm/music/%s/_/%s' &", eartist, etitle)) return 0; - printf ("executing %s\n", command); system (command); free (command); } @@ -902,13 +907,28 @@ static const char settings_dlg[] = // define plugin interface static DB_misc_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_MISC, .plugin.name = "last.fm scrobbler", - .plugin.descr = "sends played songs information to your last.fm account", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.descr = "Sends played songs information to your last.fm account, or other service that use AudioScrobbler protocol", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = lastfm_start, .plugin.stop = lastfm_stop, diff --git a/plugins/m3u/Makefile.am b/plugins/m3u/Makefile.am new file mode 100644 index 00000000..e77663c6 --- /dev/null +++ b/plugins/m3u/Makefile.am @@ -0,0 +1,11 @@ +if HAVE_M3U +pkglib_LTLIBRARIES = m3u.la + +m3u_la_SOURCES = m3u.c + +m3u_la_LDFLAGS = -module + +m3u_la_LIBADD = $(LIBADD) + +m3u_la_CFLAGS = $(CFLAGS) -std=c99 +endif diff --git a/plugins/m3u/m3u.c b/plugins/m3u/m3u.c new file mode 100644 index 00000000..4e5afe83 --- /dev/null +++ b/plugins/m3u/m3u.c @@ -0,0 +1,469 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <string.h> +#include <stdlib.h> +#include <math.h> +#include "../../deadbeef.h" + +//#define trace(...) { fprintf(stderr, __VA_ARGS__); } +#define trace(fmt,...) + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +static DB_functions_t *deadbeef; + +static const uint8_t * +skipspaces (const uint8_t *p, const uint8_t *end) { + while (p < end && *p <= ' ') { + p++; + } + return p; +} + +static DB_playItem_t * +load_m3u (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + const char *slash = strrchr (fname, '/'); + trace ("enter pl_insert_m3u\n"); + // skip all empty lines and comments + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + trace ("failed to open file %s\n", fname); + return NULL; + } + int sz = deadbeef->fgetlength (fp); + trace ("loading m3u...\n"); + uint8_t *buffer = malloc (sz); + if (!buffer) { + deadbeef->fclose (fp); + trace ("failed to allocate %d bytes to read the file %s\n", sz, fname); + return NULL; + } + deadbeef->fread (buffer, 1, sz, fp); + deadbeef->fclose (fp); + const uint8_t *p = buffer; + const uint8_t *end = buffer+sz; + deadbeef->pl_lock (); + while (p < end) { + p = skipspaces (p, end); + if (p >= end) { + break; + } + if (*p == '#') { + while (p < end && *p >= 0x20) { + p++; + } + if (p >= end) { + break; + } + continue; + } + const uint8_t *e = p; + while (e < end && *e >= 0x20) { + e++; + } + int n = e-p; + uint8_t nm[n+1]; + memcpy (nm, p, n); + nm[n] = 0; + + DB_playItem_t *it = NULL; + if (strrchr (nm, '/')) { + trace ("pl_insert_m3u: adding file %s\n", nm); + it = deadbeef->pl_insert_file (after, nm, pabort, cb, user_data); + } + else { + int l = strlen (nm); + char fullpath[slash - fname + l + 2]; + memcpy (fullpath, fname, slash - fname + 1); + strcpy (fullpath + (slash - fname + 1), nm); + trace ("pl_insert_m3u: adding file %s\n", fullpath); + it = deadbeef->pl_insert_file (after, fullpath, pabort, cb, user_data); + } + if (it) { + after = it; + } + if (pabort && *pabort) { + deadbeef->pl_unlock (); + free (buffer); + return after; + } + p = e; + if (p >= end) { + break; + } + } + deadbeef->pl_unlock (); + trace ("leave pl_insert_m3u\n"); + free (buffer); + return after; +} + +static DB_playItem_t * +pls_insert_file (DB_playItem_t *after, const char *fname, const char *uri, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data, const char *title, const char *length) { + trace ("pls_insert_file uri: %s\n", uri); + DB_playItem_t *it = NULL; + const char *slash = NULL; + + if (strrchr (uri, '/')) { + it = deadbeef->pl_insert_file (after, uri, pabort, cb, user_data); + } + else if (slash = strrchr (fname, '/')) { + int l = strlen (uri); + char fullpath[slash - fname + l + 2]; + memcpy (fullpath, fname, slash - fname + 1); + strcpy (fullpath + (slash - fname + 1), uri); + trace ("pls_insert_file: adding file %s\n", fullpath); + it = deadbeef->pl_insert_file (after, fullpath, pabort, cb, user_data); + } + if (length[0]) { + deadbeef->pl_set_item_duration (it, atoi (length)); + } + if (title[0]) { + deadbeef->pl_replace_meta (it, "title", title); + } + return it; +} + +static DB_playItem_t * +load_pls (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + const char *slash = strrchr (fname, '/'); + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + trace ("failed to open file %s\n", fname); + return NULL; + } + int sz = deadbeef->fgetlength (fp); + deadbeef->rewind (fp); + uint8_t *buffer = malloc (sz); + if (!buffer) { + deadbeef->fclose (fp); + trace ("failed to allocate %d bytes to read the file %s\n", sz, fname); + return NULL; + } + deadbeef->fread (buffer, 1, sz, fp); + deadbeef->fclose (fp); + // 1st line must be "[playlist]" + const uint8_t *p = buffer; + const uint8_t *end = buffer+sz; + if (strncasecmp (p, "[playlist]", 10)) { + trace ("file %s doesn't begin with [playlist]\n", fname); + free (buffer); + return NULL; + } + p += 10; + p = skipspaces (p, end); + if (p >= end) { + trace ("file %s finished before numberofentries had been read\n", fname); + free (buffer); + return NULL; + } + // fetch all tracks + char uri[1024] = ""; + char title[1024] = ""; + char length[20] = ""; + int lastidx = -1; + deadbeef->pl_lock (); + while (p < end) { + p = skipspaces (p, end); + if (p >= end) { + break; + } + if (end-p < 6) { + break; + } + const uint8_t *e; + int n; + if (!strncasecmp (p, "numberofentries=", 16) || !strncasecmp (p, "version=", 8)) { + while (p < end && *p >= 0x20) { + p++; + } + continue; + } + else if (!strncasecmp (p, "file", 4)) { + int idx = atoi (p + 4); + if (uri[0] && idx != lastidx && lastidx != -1) { + trace ("uri%d\n", idx); + DB_playItem_t *it = pls_insert_file (after, fname, uri, pabort, cb, user_data, title, length); + if (it) { + after = it; + } + if (pabort && *pabort) { + deadbeef->pl_unlock (); + free (buffer); + return after; + } + uri[0] = 0; + title[0] = 0; + length[0] = 0; + } + lastidx = idx; + p += 4; + while (p < end && *p != '=') { + p++; + } + p++; + if (p >= end) { + break; + } + e = p; + while (e < end && *e >= 0x20) { + e++; + } + n = e-p; + n = min (n, sizeof (uri)-1); + memcpy (uri, p, n); + uri[n] = 0; + trace ("uri: %s\n", uri); + trace ("uri%d=%s\n", idx, uri); + p = ++e; + } + else if (!strncasecmp (p, "title", 5)) { + int idx = atoi (p + 5); + if (uri[0] && idx != lastidx && lastidx != -1) { + trace ("title%d\n", idx); + DB_playItem_t *it = pls_insert_file (after, fname, uri, pabort, cb, user_data, title, length); + if (it) { + after = it; + } + if (pabort && *pabort) { + deadbeef->pl_unlock (); + free (buffer); + return after; + } + uri[0] = 0; + title[0] = 0; + length[0] = 0; + } + lastidx = idx; + p += 5; + while (p < end && *p != '=') { + p++; + } + p++; + if (p >= end) { + break; + } + e = p; + while (e < end && *e >= 0x20) { + e++; + } + n = e-p; + n = min (n, sizeof (title)-1); + memcpy (title, p, n); + title[n] = 0; + trace ("title%d=%s\n", idx, title); + p = ++e; + } + else if (!strncasecmp (p, "length", 6)) { + int idx = atoi (p + 6); + if (uri[0] && idx != lastidx && lastidx != -1) { + trace ("length%d\n", idx); + DB_playItem_t *it = pls_insert_file (after, fname, uri, pabort, cb, user_data, title, length); + if (it) { + after = it; + } + if (pabort && *pabort) { + deadbeef->pl_unlock (); + free (buffer); + return after; + } + uri[0] = 0; + title[0] = 0; + length[0] = 0; + } + lastidx = idx; + p += 6; + // skip = + while (p < end && *p != '=') { + p++; + } + p++; + if (p >= end) { + break; + } + e = p; + while (e < end && *e >= 0x20) { + e++; + } + n = e-p; + n = min (n, sizeof (length)-1); + memcpy (length, p, n); + trace ("length%d=%s\n", idx, length); + } + else { + trace ("invalid entry in pls file: %s\n", p); + break; + } + while (e < end && *e < 0x20) { + e++; + } + p = e; + } + if (uri[0]) { + DB_playItem_t *it = pls_insert_file (after, fname, uri, pabort, cb, user_data, title, length); + if (it) { + after = it; + } + } + deadbeef->pl_unlock (); + free (buffer); + return after; +} + +static DB_playItem_t * +m3uplug_load (DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data) { + const char *ext = strrchr (fname, '.'); + if (!ext) { + return NULL; + } + ext++; + + if (!strcasecmp (ext, "m3u") || !strcasecmp (ext, "m3u8")) { + return load_m3u (after, fname, pabort, cb, user_data); + } + else if (!strcasecmp (ext, "pls")) { + return load_pls (after, fname, pabort, cb, user_data); + } + + return NULL; +} + +int +m3uplug_save_m3u (const char *fname, DB_playItem_t *first, DB_playItem_t *last) { + FILE *fp = fopen (fname, "w+t"); + if (!fp) { + return -1; + } + DB_playItem_t *it = first; + deadbeef->pl_item_ref (it); + while (it) { + int dur = (int)ceil(deadbeef->pl_get_item_duration (it)); + char s[1000]; + deadbeef->pl_format_title (it, -1, s, sizeof (s), -1, "%a - %t"); + const char *fname = deadbeef->pl_find_meta (it, ":URI"); + fprintf (fp, "#EXTINF:%d,%s\n", dur, s); + fprintf (fp, "%s\n", fname); + + if (it == last) { + break; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + fclose (fp); + return 0; +} + +int +m3uplug_save_pls (const char *fname, DB_playItem_t *first, DB_playItem_t *last) { + FILE *fp = fopen (fname, "w+t"); + if (!fp) { + return -1; + } + + int n = 0; + DB_playItem_t *it = first; + deadbeef->pl_item_ref (it); + while (it) { + n++; + if (it == last) { + break; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + + fprintf (fp, "[playlist]\n"); + fprintf (fp, "NumberOfEntries=%d\n", n); + + it = first; + deadbeef->pl_item_ref (it); + int i = 1; + while (it) { + const char *fname = deadbeef->pl_find_meta (it, ":URI"); + fprintf (fp, "File%d=%s\n", i, fname); + + if (it == last) { + break; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + i++; + } + fclose (fp); + return 0; +} + +int +m3uplug_save (const char *fname, DB_playItem_t *first, DB_playItem_t *last) { + const char *e = strrchr (fname, '.'); + if (!e) { + return -1; + } + if (!strcasecmp (e, ".m3u") || !strcasecmp (e, ".m3u8")) { + return m3uplug_save_m3u (fname, first, last); + } + else if (!strcasecmp (e, ".pls")) { + return m3uplug_save_pls (fname, first, last); + } + return -1; +} + +static const char * exts[] = { "m3u", "m3u8", "pls", NULL }; +DB_playlist_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 1, + .plugin.version_minor = 0, + .plugin.type = DB_PLUGIN_PLAYLIST, + .plugin.id = "m3u", + .plugin.name = "M3U and PLS support", + .plugin.descr = "Importing and exporting M3U and PLS formats\nRecognizes .pls, .m3u and .m3u8 file types\n\nNOTE: only utf8 file names are currently supported", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.website = "http://deadbeef.sf.net", + .load = m3uplug_load, + .save = m3uplug_save, + .extensions = exts, +}; + +DB_plugin_t * +m3u_load (DB_functions_t *api) { + deadbeef = api; + return &plugin.plugin; +} diff --git a/plugins/mms/mmsplug.c b/plugins/mms/mmsplug.c index 4ad57832..787af70d 100644 --- a/plugins/mms/mmsplug.c +++ b/plugins/mms/mmsplug.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -108,15 +108,42 @@ mms_get_content_type (DB_FILE *stream) { static const char *scheme_names[] = { "mms://", "mmsh://", NULL }; +const char ** +mms_get_schemes (void) { + return scheme_names; +} + +int +mms_is_streaming (void) { + return 1; +} + static DB_vfs_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_VFS, .plugin.name = "mms vfs", .plugin.descr = "MMS streaming plugin based on libmms", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified libmms-0.6.0, http://sourceforge.net/projects/libmms/\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = mms_open, .close = mms_close, @@ -126,8 +153,8 @@ static DB_vfs_t plugin = { .rewind = mms_rewind, .getlength = mms_getlength, .get_content_type = mms_get_content_type, - .scheme_names = scheme_names, - .streaming = 1 + .get_schemes = mms_get_schemes, + .is_streaming = mms_is_streaming, }; DB_plugin_t * diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c index 13544238..3f8c4260 100644 --- a/plugins/mpgmad/mpgmad.c +++ b/plugins/mpgmad/mpgmad.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include <stdlib.h> #include <limits.h> #include <unistd.h> +#include <sys/time.h> #include "../../deadbeef.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } @@ -45,13 +46,15 @@ static DB_functions_t *deadbeef; #define READBUFFER 0x2800 // 10k is enough for single frame // vbrmethod constants -#define LAME_CBR 1 -#define LAME_CBR2 8 -#define LAME_ABR 2 -#define LAME_VBR1 3 -#define LAME_VBR2 4 -#define LAME_VBR3 5 -#define LAME_VBR4 6 +#define XING_CBR 1 +#define XING_ABR 2 +#define XING_VBR1 3 +#define XING_VBR2 4 +#define XING_VBR3 5 +#define XING_VBR4 6 +#define XING_CBR2 8 +#define XING_ABR2 9 +#define DETECTED_VBR 100 // xing header flags #define FRAMES_FLAG 0x0001 @@ -78,24 +81,34 @@ typedef struct { int bitrate; int samplerate; int packetlength; - float frameduration; int bitspersample; int channels; float duration; + + // currentsample and totalsamples are in the entire file scope (delay/padding inclusive) int currentsample; int totalsamples; + int skipsamples; - int startoffset; - int endoffset; + + int startoffset; // in bytes (id3v2, xing/lame) + int endoffset; // in bytes (apev2, id3v1) + + // startsample and endsample exclude delay/padding int startsample; int endsample; - int startdelay; - int enddelay; - int avg_packetlength; + + // number of samples to skip at the start/end of file + int delay; + int padding; + + float avg_packetlength; int avg_samplerate; int avg_samples_per_frame; int nframes; int last_comment_update; + int vbr; + int have_xing_header; } buffer_t; typedef struct { @@ -175,25 +188,29 @@ extract_f32 (unsigned char *buf) { static int cmp3_scan_stream (buffer_t *buffer, int sample) { trace ("cmp3_scan_stream %d\n", sample); + int initpos = deadbeef->ftell (buffer->file); trace ("initpos: %d\n", initpos); -// if (sample == 0) { -// sample = -1; -// } int packetlength = 0; int nframe = 0; - int got_xing_header = 0; - buffer->duration = 0; int scansamples = 0; buffer->currentsample = 0; buffer->skipsamples = 0; - int fsize = 0; - int avg_bitrate = 0; +// int avg_bitrate = 0; int valid_frames = 0; + int prev_bitrate = -1; + buffer->samplerate = 0; + int64_t fsize = deadbeef->fgetlength (buffer->file); if (sample <= 0) { buffer->totalsamples = 0; - fsize = deadbeef->fgetlength (buffer->file) - initpos; + if (fsize > 0) { + fsize -= initpos; + if (fsize < 0) { + trace ("cmp3_scan_stream: invalid file: bad header\n"); + return -1; + } + } } if (sample <= 0 && buffer->avg_packetlength == 0) { buffer->avg_packetlength = 0; @@ -204,37 +221,40 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { buffer->startoffset = initpos; } + int lastframe_valid = 0; + int64_t offs = -1; + for (;;) { - uint32_t hdr; - uint8_t sync; - //size_t pos = deadbeef->ftell (buffer->file); - if (deadbeef->fread (&sync, 1, 1, buffer->file) != 1) { + if (!lastframe_valid && offs >= 0) { + deadbeef->fseek (buffer->file, offs+1, SEEK_SET); + } + offs = deadbeef->ftell (buffer->file); + uint8_t fb[4]; + if (deadbeef->fread (fb, 1, 4, buffer->file) != 4) { break; // eof } + + uint32_t hdr; + uint8_t sync = fb[0]; if (sync != 0xff) { // trace ("[1]frame %d didn't seek to frame end\n", nframe); + lastframe_valid = 0; continue; // not an mpeg frame } else { // 2nd sync byte - if (deadbeef->fread (&sync, 1, 1, buffer->file) != 1) { - break; // eof - } + sync = fb[1]; if ((sync >> 5) != 7) { // trace ("[2]frame %d didn't seek to frame end\n", nframe); + lastframe_valid = 0; continue; } } // found frame hdr = (0xff<<24) | (sync << 16); - // read 2 bytes more - if (deadbeef->fread (&sync, 1, 1, buffer->file) != 1) { - break; // eof - } + sync = fb[2]; hdr |= sync << 8; - if (deadbeef->fread (&sync, 1, 1, buffer->file) != 1) { - break; // eof - } + sync = fb[3]; hdr |= sync; // parse header @@ -246,20 +266,22 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { } // mpeg version - static int vertbl[] = {3, -1, 2, 1}; // 3 is 2.5 + static const int vertbl[] = {3, -1, 2, 1}; // 3 is 2.5 int ver = (hdr & (3<<19)) >> 19; ver = vertbl[ver]; if (ver < 0) { trace ("frame %d bad mpeg version %d\n", nframe, (hdr & (3<<19)) >> 19); + lastframe_valid = 0; continue; // invalid frame } // layer info - static int ltbl[] = { -1, 3, 2, 1 }; + static const int ltbl[] = { -1, 3, 2, 1 }; int layer = (hdr & (3<<17)) >> 17; layer = ltbl[layer]; if (layer < 0) { trace ("frame %d bad layer %d\n", nframe, (hdr & (3<<17)) >> 17); + lastframe_valid = 0; continue; // invalid frame } @@ -282,6 +304,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { bitrate = brtable[idx][bitrate]; if (bitrate <= 0) { trace ("frame %d bad bitrate %d\n", nframe, (hdr & (0x0f<<12)) >> 12); + lastframe_valid = 0; continue; // invalid frame } @@ -295,13 +318,14 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { samplerate = srtable[ver-1][samplerate]; if (samplerate < 0) { trace ("frame %d bad samplerate %d\n", nframe, (hdr & (0x03<<10))>>10); + lastframe_valid = 0; continue; // invalid frame } // padding int padding = (hdr & (0x1 << 9)) >> 9; - static int chantbl[4] = { 2, 2, 2, 1 }; + static const int chantbl[4] = { 2, 2, 2, 1 }; int nchannels = (hdr & (0x3 << 6)) >> 6; nchannels = chantbl[nchannels]; @@ -309,10 +333,12 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { if (layer == 2) { if ((bitrate <= 56 || bitrate == 80) && nchannels != 1) { trace ("mpgmad: bad frame %d: layer %d, channels %d, bitrate %d\n", nframe, layer, nchannels, bitrate); + lastframe_valid = 0; continue; // bad frame } if (bitrate >= 224 && nchannels == 1) { trace ("mpgmad: bad frame %d: layer %d, channels %d, bitrate %d\n", nframe, layer, nchannels, bitrate); + lastframe_valid = 0; continue; // bad frame } } @@ -320,34 +346,35 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { // packetlength packetlength = 0; bitrate *= 1000; - float dur = 0; int samples_per_frame = 0; if (samplerate > 0 && bitrate > 0) { if (layer == 1) { samples_per_frame = 384; - dur = (float)384 / samplerate; } else if (layer == 2) { samples_per_frame = 1152; - dur = (float)1152 / samplerate; } else if (layer == 3) { if (ver == 1) { samples_per_frame = 1152; - dur = (float)1152 / samplerate; } else { samples_per_frame = 576; - dur = (float)576 / samplerate; } } packetlength = samples_per_frame / 8 * bitrate / samplerate + padding; } else { trace ("frame %d samplerate or bitrate is invalid\n", nframe); + lastframe_valid = 0; continue; } + if (!buffer->have_xing_header && prev_bitrate != -1 && prev_bitrate != bitrate) { + buffer->vbr = DETECTED_VBR; + } + prev_bitrate = bitrate; + valid_frames++; #if 0 if (nframe < 1000) { @@ -356,23 +383,26 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { #endif if (sample != 0 || nframe == 0) { + if (sample == 0 && lastframe_valid) { + return 0; + } buffer->version = ver; buffer->layer = layer; buffer->bitrate = bitrate; buffer->samplerate = samplerate; buffer->packetlength = packetlength; - buffer->frameduration = dur; if (nchannels > buffer->channels) { buffer->channels = nchannels; } buffer->bitspersample = 16; - //trace ("frame %d mpeg v%d layer %d bitrate %d samplerate %d packetlength %d framedur %f channels %d\n", nframe, ver, layer, bitrate, samplerate, packetlength, dur, nchannels); + trace ("frame %d mpeg v%d layer %d bitrate %d samplerate %d packetlength %d channels %d\n", nframe, ver, layer, bitrate, samplerate, packetlength, nchannels); } + lastframe_valid = 1; // try to read xing/info tag (only on initial scans) - if (sample <= 0 && !got_xing_header) + if (sample <= 0 && !buffer->have_xing_header) { size_t framepos = deadbeef->ftell (buffer->file); - if (!buffer->file->vfs->streaming) { + if (!buffer->file->vfs->is_streaming ()) { // trace ("trying to read xing header at pos %d\n", framepos); if (ver == 1) { deadbeef->fseek (buffer->file, 32, SEEK_CUR); @@ -408,7 +438,9 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { return -1; // EOF } uint32_t nframes = extract_i32 (buf); - buffer->duration = (float)nframes * (float)samples_per_frame / (float)samplerate; + if (sample == 0) { + buffer->duration = (((uint64_t)nframes * (uint64_t)samples_per_frame) - buffer->delay - buffer->padding)/ (uint64_t)samplerate; + } trace ("xing totalsamples: %d, nframes: %d, samples_per_frame: %d\n", nframes*samples_per_frame, nframes, samples_per_frame); if (nframes <= 0 || samples_per_frame <= 0) { trace ("bad xing header\n"); @@ -432,9 +464,23 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { return -1; // EOF } // trace ("tell=%x, %c%c%c%c\n", deadbeef->ftell(buffer->file), buf[0], buf[1], buf[2], buf[3]); + + deadbeef->fseek (buffer->file, 5, SEEK_CUR); + uint8_t rev = 0; + if (deadbeef->fread (&rev, 1, 1, buffer->file) != 1) { + trace ("cmp3_scan_stream: EOF while reading info tag revision / vbr method\n"); + } + switch (rev & 0x0f) { + case XING_ABR ... XING_VBR4: + case XING_ABR2: + buffer->vbr = rev & 0x0f; + break; + default: + buffer->vbr = DETECTED_VBR; + break; + } if (!memcmp (buf, "LAME", 4)) { trace ("lame header found\n"); - deadbeef->fseek (buffer->file, 6, SEEK_CUR); // FIXME: that can be optimized by single read uint8_t lpf; @@ -451,8 +497,8 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { // skip deadbeef->fseek (buffer->file, 2, SEEK_CUR); deadbeef->fread (buf, 1, 3, buffer->file); - uint32_t startdelay = (((uint32_t)buf[0]) << 4) | ((((uint32_t)buf[1]) & 0xf0)>>4); - uint32_t enddelay = ((((uint32_t)buf[1])&0x0f)<<8) | ((uint32_t)buf[2]); +// buffer->delay = (((uint32_t)buf[0]) << 4) | ((((uint32_t)buf[1]) & 0xf0)>>4); +// buffer->padding = ((((uint32_t)buf[1])&0x0f)<<8) | ((uint32_t)buf[2]); // skip deadbeef->fseek (buffer->file, 1, SEEK_CUR); // mp3gain @@ -464,43 +510,54 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { deadbeef->fread (buf, 1, 4, buffer->file); // uint32_t musiclen = extract_i32 (buf); - //trace ("lpf: %d, peaksignalamp: %f, radiogain: %d, audiophile: %d, startdelay: %d, enddelay: %d, mp3gain: %d, musiclen: %d\n", lpf, rg_peaksignalamp, rg_radio, rg_audiophile, startdelay, enddelay, mp3gain, musiclen); + //trace ("lpf: %d, peaksignalamp: %f, radiogain: %d, audiophile: %d, delay: %d, padding: %d, mp3gain: %d, musiclen: %d\n", lpf, rg_peaksignalamp, rg_radio, rg_audiophile, delay, padding, mp3gain, musiclen); // skip crc //deadbeef->fseek (buffer->file, 4, SEEK_CUR); - buffer->startdelay = startdelay; - buffer->enddelay = enddelay; trace ("lame totalsamples: %d\n", buffer->totalsamples); } if (sample <= 0 && (flags&FRAMES_FLAG)) { - buffer->totalsamples -= buffer->enddelay; + buffer->have_xing_header = 1; deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET); - return 0; + if (fsize >= 0) { + buffer->bitrate = (fsize - deadbeef->ftell (buffer->file))/ buffer->samplerate * 1000; + } + buffer->startoffset = deadbeef->ftell (buffer->file); } } } if (sample == 0) { - // xing header failed, calculate based on file size -// trace ("xing header failed\n"); + trace ("cmp3_scan_stream: trying to figure out duration from file size\n"); buffer->samplerate = samplerate; - if (buffer->file->vfs->streaming) { + if (buffer->file->vfs->is_streaming ()) { // only suitable for cbr files, used if streaming - int sz = deadbeef->fgetlength (buffer->file) - buffer->startoffset - buffer->endoffset; + int sz = deadbeef->fgetlength (buffer->file); + if (sz > 0) { + sz -= buffer->startoffset + buffer->endoffset; + if (sz < 0) { + trace ("cmp3_scan_stream: bad file headers\n"); + return -1; + } + } if (sz < 0) { // unable to determine duration buffer->duration = -1; buffer->totalsamples = -1; if (sample == 0) { - deadbeef->fseek (buffer->file, framepos/*+packetlength-4*/, SEEK_SET); + trace ("check validity of the next frame...\n"); + deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET); + continue; } + trace ("cmp3_scan_stream: unable to determine duration"); return 0; } buffer->nframes = sz / packetlength; buffer->avg_packetlength = packetlength; buffer->avg_samplerate = samplerate; buffer->avg_samples_per_frame = samples_per_frame; - buffer->duration = buffer->nframes * samples_per_frame / samplerate; + buffer->duration = (buffer->nframes * samples_per_frame - buffer->delay - buffer->padding) / samplerate; buffer->totalsamples = buffer->nframes * samples_per_frame; -// trace ("bitrate=%d, layer=%d, packetlength=%d, fsize=%d, nframes=%d, samples_per_frame=%d, samplerate=%d, duration=%f, totalsamples=%d\n", bitrate, layer, packetlength, sz, nframes, samples_per_frame, samplerate, buffer->duration, buffer->totalsamples); + trace ("totalsamples: %d, samplesperframe: %d, fsize=%lld\n", buffer->totalsamples, samples_per_frame, fsize); +// trace ("bitrate=%d, layer=%d, packetlength=%d, fsize=%d, nframes=%d, samples_per_frame=%d, samplerate=%d, duration=%f, totalsamples=%d\n", bitrate, layer, packetlength, sz, nframe, samples_per_frame, samplerate, buffer->duration, buffer->totalsamples); if (sample == 0) { deadbeef->fseek (buffer->file, framepos/*+packetlength-4*/, SEEK_SET); @@ -513,7 +570,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { } else { deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET); - got_xing_header = 1; + buffer->have_xing_header = 1; } } @@ -526,18 +583,9 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { buffer->avg_packetlength += packetlength; buffer->avg_samplerate += samplerate; buffer->avg_samples_per_frame += samples_per_frame; - avg_bitrate += bitrate; + //avg_bitrate += bitrate; if (nframe >= 100) { - buffer->avg_packetlength /= valid_frames; - buffer->avg_samplerate /= valid_frames; - buffer->avg_samples_per_frame /= valid_frames; - avg_bitrate /= valid_frames; - trace ("valid_frames=%d, avg_bitrate=%d, avg_packetlength=%d, avg_samplerate=%d, avg_samples_per_frame=%d\n", valid_frames, avg_bitrate, buffer->avg_packetlength, buffer->avg_samplerate, buffer->avg_samples_per_frame); - - buffer->nframes = fsize / buffer->avg_packetlength; - buffer->duration = buffer->nframes * buffer->avg_samples_per_frame / buffer->avg_samplerate; - buffer->totalsamples = buffer->nframes * buffer->avg_samples_per_frame; - return 0; + goto end_scan; } } else { @@ -549,37 +597,133 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { } } scansamples += samples_per_frame; - buffer->duration += dur; nframe++; if (packetlength > 0) { deadbeef->fseek (buffer->file, packetlength-4, SEEK_CUR); } } +end_scan: if (nframe == 0) { trace ("cmp3_scan_stream: couldn't find mpeg frames in file\n"); return -1; } + if (sample == 0) { + buffer->avg_packetlength /= (float)valid_frames; + buffer->avg_samplerate /= valid_frames; + buffer->avg_samples_per_frame /= valid_frames; +// avg_bitrate /= valid_frames; + //trace ("valid_frames=%d, avg_bitrate=%d, avg_packetlength=%f, avg_samplerate=%d, avg_samples_per_frame=%d\n", valid_frames, avg_bitrate, buffer->avg_packetlength, buffer->avg_samplerate, buffer->avg_samples_per_frame); + trace ("startoffs: %d, endoffs: %d\n", buffer->startoffset, buffer->endoffset); + + buffer->nframes = (fsize - buffer->startoffset - buffer->endoffset) / buffer->avg_packetlength; + if (!buffer->have_xing_header) { + buffer->totalsamples = buffer->nframes * buffer->avg_samples_per_frame; + buffer->duration = (buffer->totalsamples - buffer->delay - buffer->padding) / buffer->avg_samplerate; + } + buffer->bitrate = (fsize-buffer->startoffset-buffer->endoffset) / buffer->duration * 8; + trace ("nframes: %d, fsize: %lld, spf: %d, smp: %d, totalsamples: %d\n", buffer->nframes, fsize, buffer->avg_samples_per_frame, buffer->avg_samplerate, buffer->totalsamples); + return 0; + } + buffer->totalsamples = scansamples; -// buffer->duration = buffer->totalsamples / buffer->samplerate; + buffer->duration = (buffer->totalsamples - buffer->delay - buffer->padding) / buffer->samplerate; trace ("nframes=%d, totalsamples=%d, samplerate=%d, dur=%f\n", nframe, scansamples, buffer->samplerate, buffer->duration); return 0; } +int +cmp3_seek_stream (DB_fileinfo_t *_info, int sample) { + mpgmad_info_t *info = (mpgmad_info_t *)_info; + sample += info->buffer.delay; + if (sample == 0) { + _info->readpos = 0; + info->buffer.currentsample = 0; + return 0; + + } + return cmp3_scan_stream (&info->buffer, sample); +} + static DB_fileinfo_t * -cmp3_open (void) { +cmp3_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (mpgmad_info_t)); mpgmad_info_t *info = (mpgmad_info_t *)_info; memset (info, 0, sizeof (mpgmad_info_t)); return _info; } +void +cmp3_set_extra_properties (buffer_t *buffer) { + char s[100]; + int64_t size = deadbeef->fgetlength (buffer->file); + if (size >= 0) { + snprintf (s, sizeof (s), "%lld", size); + deadbeef->pl_replace_meta (buffer->it, ":FILE_SIZE", s); + } + else { + deadbeef->pl_replace_meta (buffer->it, ":FILE_SIZE", "∞"); + } + if (buffer->bitrate > 0) { + snprintf (s, sizeof (s), "%d", buffer->bitrate/1000); + deadbeef->pl_replace_meta (buffer->it, ":BITRATE", s); + } + deadbeef->pl_replace_meta (buffer->it, ":BPS", "16"); + snprintf (s, sizeof (s), "%d", buffer->channels); + deadbeef->pl_replace_meta (buffer->it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", buffer->samplerate); + deadbeef->pl_replace_meta (buffer->it, ":SAMPLERATE", s); + + // set codec profile (cbr or vbr) and mp3 vbr method (guessed, or from Xing/Info header) + + switch (buffer->vbr) { + case XING_ABR: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "ABR"); + break; + case XING_VBR1: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "full VBR method 1"); + break; + case XING_VBR2: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "full VBR method 2"); + break; + case XING_VBR3: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "full VBR method 3"); + break; + case XING_VBR4: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "full VBR method 4"); + break; + case XING_CBR2: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "CBR"); + break; + case XING_ABR2: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "ABR 2 pass"); + break; + case DETECTED_VBR: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "VBR"); + deadbeef->pl_replace_meta (buffer->it, ":MP3_VBR_METHOD", "unspecified"); + break; + default: + deadbeef->pl_replace_meta (buffer->it, ":CODEC_PROFILE", "CBR"); + break; + } + const char *versions[] = {"1", "2", "2.5"}; + snprintf (s, sizeof (s), "MPEG%s layer%d", versions[buffer->version-1], buffer->layer); + deadbeef->pl_replace_meta (buffer->it, ":MPEG_VERSION", s); + deadbeef->pl_replace_meta (buffer->it, ":XING_HEADER", buffer->have_xing_header ? "Yes" : "No"); +} + static int cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) { mpgmad_info_t *info = (mpgmad_info_t *)_info; _info->plugin = &plugin; memset (&info->buffer, 0, sizeof (info->buffer)); - info->buffer.file = deadbeef->fopen (it->fname); + info->buffer.file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->buffer.file) { return -1; } @@ -587,33 +731,35 @@ cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) { deadbeef->pl_item_ref (it); info->buffer.it = it; info->info.readpos = 0; - if (!info->buffer.file->vfs->streaming) { + if (!info->buffer.file->vfs->is_streaming ()) { int skip = deadbeef->junk_get_leading_size (info->buffer.file); if (skip > 0) { trace ("mpgmad: skipping %d(%xH) bytes of junk\n", skip, skip); deadbeef->fseek (info->buffer.file, skip, SEEK_SET); } - cmp3_scan_stream (&info->buffer, -1); // scan entire stream, calc duration + int res = cmp3_scan_stream (&info->buffer, -1); + if (res < 0) { + trace ("mpgmad: cmp3_init: initial cmp3_scan_stream failed\n"); + return -1; + } if (it->endsample > 0) { info->buffer.startsample = it->startsample; info->buffer.endsample = it->endsample; // that comes from cue, don't calc duration, just seek and play - plugin.seek_sample (_info, 0); } else { deadbeef->pl_set_item_duration (it, info->buffer.duration); info->buffer.startsample = 0; - info->buffer.endsample = info->buffer.totalsamples-1; - info->buffer.skipsamples = info->buffer.startdelay; - info->buffer.currentsample = info->buffer.startdelay; + info->buffer.endsample = info->buffer.totalsamples-info->buffer.delay-info->buffer.padding; trace ("mpgmad: seeking to %d(%xH) start offset\n", info->buffer.startoffset, info->buffer.startoffset); deadbeef->fseek (info->buffer.file, info->buffer.startoffset, SEEK_SET); } + plugin.seek_sample (_info, 0); } else { deadbeef->fset_track (info->buffer.file, it); - info->buffer.it->filetype = NULL; - int len = deadbeef->fgetlength (info->buffer.file); + deadbeef->pl_delete_meta (info->buffer.it, ":FILETYPE"); + int64_t len = deadbeef->fgetlength (info->buffer.file); if (len > 0) { deadbeef->pl_delete_all_meta (it); int v2err = deadbeef->junk_id3v2_read (it, info->buffer.file); @@ -627,6 +773,10 @@ cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("mpgmad: cmp3_init: initial cmp3_scan_stream failed\n"); return -1; } + deadbeef->fseek (info->buffer.file, 0, SEEK_SET); + + cmp3_set_extra_properties (&info->buffer); + deadbeef->pl_set_item_duration (it, info->buffer.duration); if (info->buffer.duration >= 0) { info->buffer.endsample = info->buffer.totalsamples - 1; @@ -648,12 +798,14 @@ cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("duration=%f, endsample=%d, totalsamples=%d\n", info->buffer.duration, info->buffer.endsample, info->buffer.totalsamples); } if (info->buffer.samplerate == 0) { - trace ("bad mpeg file: %f\n", it->fname); + trace ("bad mpeg file: %f\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } - _info->bps = info->buffer.bitspersample; - _info->samplerate = info->buffer.samplerate; - _info->channels = info->buffer.channels; + _info->fmt.bps = info->buffer.bitspersample; + _info->fmt.samplerate = info->buffer.samplerate; + _info->fmt.channels = info->buffer.channels; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); + trace ("mp3 format: bps:%d sr:%d channels:%d\n", _info->fmt.bps, _info->fmt.samplerate, _info->fmt.channels); mad_stream_init(&info->stream); mad_stream_options (&info->stream, MAD_OPTION_IGNORECRC); @@ -716,14 +868,6 @@ cmp3_decode_cut (mpgmad_info_t *info, int framesize) { trace ("mpgmad: got frame with invalid number of channels (%d)\n", info->buffer.channels); return 1; } - if (info->buffer.currentsample + info->buffer.readsize / (framesize * info->buffer.channels) > info->buffer.endsample) { - int sz = (info->buffer.endsample - info->buffer.currentsample + 1) * framesize * info->buffer.channels; - trace ("size truncated to %d bytes, cursample=%d, endsample=%d, totalsamples=%d\n", info->buffer.readsize, info->buffer.currentsample, info->buffer.endsample, info->buffer.totalsamples); - if (sz <= 0) { - return 1; - } - info->buffer.readsize = sz; - } } return 0; } @@ -732,6 +876,7 @@ static inline void cmp3_skip (mpgmad_info_t *info) { if (info->buffer.skipsamples > 0) { int skip = min (info->buffer.skipsamples, info->buffer.decode_remaining); +// printf ("skip %d / %d\n", skip, info->buffer.skipsamples); info->buffer.skipsamples -= skip; info->buffer.decode_remaining -= skip; } @@ -743,23 +888,43 @@ cmp3_decode_requested_int16 (mpgmad_info_t *info) { cmp3_skip (info); // copy synthesized samples into readbuffer int idx = info->synth.pcm.length-info->buffer.decode_remaining; - while (info->buffer.decode_remaining > 0 && info->buffer.readsize > 0) { - int16_t sample = MadFixedToSshort (info->synth.pcm.samples[0][idx]); - *((int16_t*)info->buffer.out) = sample; - info->buffer.readsize -= 2; - info->buffer.out += 2; - if (MAD_NCHANNELS(&info->frame.header) == 2 && info->info.channels == 2) { + + // stereo + if (MAD_NCHANNELS(&info->frame.header) == 2 && info->info.fmt.channels == 2) { + while (info->buffer.decode_remaining > 0 && info->buffer.readsize > 0) { + *((int16_t*)info->buffer.out) = MadFixedToSshort (info->synth.pcm.samples[0][idx]); + info->buffer.readsize -= 2; + info->buffer.out += 2; *((int16_t*)info->buffer.out) = MadFixedToSshort (info->synth.pcm.samples[1][idx]); info->buffer.readsize -= 2; info->buffer.out += 2; + info->buffer.decode_remaining--; + idx++; } - else if (MAD_NCHANNELS(&info->frame.header) == 1 && info->info.channels == 2) { + } + // mono + else if (MAD_NCHANNELS(&info->frame.header) == 1 && info->info.fmt.channels == 1){ + while (info->buffer.decode_remaining > 0 && info->buffer.readsize > 0) { + *((int16_t*)info->buffer.out) = MadFixedToSshort (info->synth.pcm.samples[0][idx]); + info->buffer.readsize -= 2; + info->buffer.out += 2; + info->buffer.decode_remaining--; + idx++; + } + } + // workaround for bad mp3s that have both mono and stereo frames + else if (MAD_NCHANNELS(&info->frame.header) == 1 && info->info.fmt.channels == 2) { + while (info->buffer.decode_remaining > 0 && info->buffer.readsize > 0) { + int16_t sample = MadFixedToSshort (info->synth.pcm.samples[0][idx]); *((int16_t*)info->buffer.out) = sample; info->buffer.readsize -= 2; info->buffer.out += 2; + *((int16_t*)info->buffer.out) = sample; + info->buffer.readsize -= 2; + info->buffer.out += 2; + info->buffer.decode_remaining--; + idx++; } - info->buffer.decode_remaining--; - idx++; } assert (info->buffer.readsize >= 0); } @@ -775,12 +940,12 @@ cmp3_decode_requested_float32 (mpgmad_info_t *info) { *((float*)info->buffer.out) = sample; info->buffer.readsize -= 4; info->buffer.out += 4; - if (MAD_NCHANNELS(&info->frame.header) == 2 && info->info.channels == 2) { + if (MAD_NCHANNELS(&info->frame.header) == 2 && info->info.fmt.channels == 2) { *((float*)info->buffer.out) = MadFixedToFloat (info->synth.pcm.samples[1][idx]); info->buffer.readsize -= 4; info->buffer.out += 4; } - else if (MAD_NCHANNELS(&info->frame.header) == 1 && info->info.channels == 2) { + else if (MAD_NCHANNELS(&info->frame.header) == 1 && info->info.fmt.channels == 2) { *((float*)info->buffer.out) = sample; info->buffer.readsize -= 4; info->buffer.out += 4; @@ -834,41 +999,43 @@ cmp3_stream_frame (mpgmad_info_t *info) { } } info->stream.error=0; + // decode next frame if(mad_frame_decode(&info->frame,&info->stream)) { if(MAD_RECOVERABLE(info->stream.error)) { if(info->stream.error!=MAD_ERROR_LOSTSYNC) { - trace ("mpgmad: recoverable frame level error (%s)\n", MadErrorString(&info->stream)); + //trace ("mpgmad: recoverable frame level error (%s)\n", MadErrorString(&info->stream)); } continue; } else { if(info->stream.error==MAD_ERROR_BUFLEN) { - trace ("mpgmad: recoverable frame level error (%s)\n", MadErrorString(&info->stream)); + //trace ("mpgmad: recoverable frame level error (%s)\n", MadErrorString(&info->stream)); continue; } else { - trace ("mpgmad: unrecoverable frame level error (%s).\n", MadErrorString(&info->stream)); + //trace ("mpgmad: unrecoverable frame level error (%s).\n", MadErrorString(&info->stream)); return -1; // fatal error } } } - if (!info->buffer.it->filetype) { - int layer = info->frame.header.layer; - if (layer >= 1 && layer <= 3) { - info->buffer.it->filetype = plugin.filetypes[layer-1]; - } - } +// const char *filetype = deadbeef->pl_find_meta (info->buffer.it, ":FILETYPE"); +// if (!filetype) { +// int layer = info->frame.header.layer; +// if (layer >= 1 && layer <= 3) { +// deadbeef->pl_replace_meta (info->buffer.it, ":FILETYPE", plugin.filetypes[layer-1]); +// } +// } - info->info.samplerate = info->frame.header.samplerate; + info->info.fmt.samplerate = info->frame.header.samplerate; #if 0 // don't switch number of channels on the fly - if (info->info.channels == 0) { - info->info.channels = MAD_NCHANNELS(&info->frame.header); + if (info->info.fmt.channels == 0) { + info->info.fmt.channels = MAD_NCHANNELS(&info->frame.header); } #endif @@ -937,36 +1104,36 @@ cmp3_free (DB_fileinfo_t *_info) { } static int -cmp3_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +cmp3_read (DB_fileinfo_t *_info, char *bytes, int size) { #if WRITE_DUMP if (!out) { out = fopen ("out.raw", "w+b"); } #endif mpgmad_info_t *info = (mpgmad_info_t *)_info; + if (info->buffer.duration >= 0) { + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + int curr = info->buffer.currentsample - info->buffer.delay; + if (size / samplesize + curr > info->buffer.endsample) { + size = (info->buffer.endsample - curr + 1) * samplesize; + trace ("mp3: size truncated to %d bytes (%d samples), cursample=%d, endsample=%d\n", size, info->buffer.endsample - curr + 1, curr, info->buffer.endsample); + if (size <= 0) { + return 0; + } + } + } + int initsize = size; info->buffer.readsize = size; info->buffer.out = bytes; cmp3_decode_int16 (info); info->buffer.currentsample += (size - info->buffer.readsize) / 4; - _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate; + _info->readpos = (float)(info->buffer.currentsample - info->buffer.delay - info->buffer.startsample) / info->buffer.samplerate; #if WRITE_DUMP if (size - info->buffer.readsize > 0) { fwrite (bytes, 1, size - info->buffer.readsize, out); } #endif - return size - info->buffer.readsize; -} - -static int -cmp3_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { - mpgmad_info_t *info = (mpgmad_info_t *)_info; -// trace ("cmp3_read_float32 readsize=%d, nchannels=%d\n", size, _info->channels); - info->buffer.readsize = size; - info->buffer.out = bytes; - cmp3_decode_float32 (info); - info->buffer.currentsample += (size - info->buffer.readsize) / 8; - _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate; - return size - info->buffer.readsize; + return initsize - info->buffer.readsize; } static int @@ -976,23 +1143,22 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) { return -1; } - if (info->buffer.file->vfs->streaming) { - if (info->buffer.totalsamples > 0 && info->buffer.avg_samples_per_frame && info->buffer.avg_packetlength) { // that means seekable remote stream, like podcast + if (info->buffer.file->vfs->is_streaming ()) { + if (info->buffer.totalsamples > 0 && info->buffer.avg_samples_per_frame > 0 && info->buffer.avg_packetlength > 0) { // that means seekable remote stream, like podcast trace ("seeking is possible!\n"); // get length excluding id3v2 - int64_t l = deadbeef->fgetlength (info->buffer.file) - info->buffer.startoffset; + int64_t l = deadbeef->fgetlength (info->buffer.file) - info->buffer.startoffset - info->buffer.endoffset; int r; // seek to beginning of the frame - int frm = sample / info->buffer.avg_samples_per_frame; - r = deadbeef->fseek (info->buffer.file, frm * info->buffer.avg_packetlength, SEEK_SET); + int64_t frm = sample / info->buffer.avg_samples_per_frame; + r = deadbeef->fseek (info->buffer.file, frm * info->buffer.avg_packetlength + info->buffer.startoffset, SEEK_SET); // l = l * sample / buffer.totalsamples; // r = deadbeef->fseek (buffer.file, l, SEEK_SET); if (!r) { - trace ("seek successful!\n"); info->buffer.skipsamples = sample - frm * info->buffer.avg_samples_per_frame; info->buffer.currentsample = sample; @@ -1018,17 +1184,14 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) { return 0; } - sample += info->buffer.startsample + info->buffer.startdelay; + sample += info->buffer.startsample; +// sample += info->buffer.delay; if (sample > info->buffer.endsample) { trace ("seek sample %d is beyond end of track (%d)\n", sample, info->buffer.endsample); return -1; // eof } // restart file, and load until we hit required pos - deadbeef->fseek (info->buffer.file, 0, SEEK_SET); - int skip = deadbeef->junk_get_leading_size (info->buffer.file); - if (skip > 0) { - deadbeef->fseek (info->buffer.file, skip, SEEK_SET); - } + deadbeef->fseek (info->buffer.file, info->buffer.startoffset, SEEK_SET); mad_synth_finish (&info->synth); mad_frame_finish (&info->frame); mad_stream_finish (&info->stream); @@ -1036,23 +1199,23 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) { info->buffer.readsize = 0; info->buffer.decode_remaining = 0; - if (sample == 0) { - _info->readpos = 0; - info->buffer.currentsample = 0; - info->buffer.skipsamples = info->buffer.startdelay; - return 0; - } - - if (cmp3_scan_stream (&info->buffer, sample) == -1) { +// struct timeval tm1; +// gettimeofday (&tm1, NULL); + if (cmp3_seek_stream (_info, sample) == -1) { trace ("failed to seek to sample %d\n", sample); _info->readpos = 0; return -1; } +// struct timeval tm2; +// gettimeofday (&tm2, NULL); +// int ms = (tm2.tv_sec*1000+tm2.tv_usec/1000) - (tm1.tv_sec*1000+tm1.tv_usec/1000); +// printf ("cmp3_scan_stream took %d ms\n", ms); mad_stream_init(&info->stream); mad_stream_options (&info->stream, MAD_OPTION_IGNORECRC); mad_frame_init(&info->frame); mad_synth_init(&info->synth); - _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate; + trace ("seeked to %d\n", info->buffer.currentsample-info->buffer.delay); + _info->readpos = (float)(info->buffer.currentsample - info->buffer.delay - info->buffer.startsample) / info->buffer.samplerate; return 0; } @@ -1075,14 +1238,11 @@ cmp3_insert (DB_playItem_t *after, const char *fname) { trace ("failed to open file %s\n", fname); return NULL; } - if (fp->vfs->streaming) { - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); + if (fp->vfs->is_streaming ()) { + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->fclose (fp); deadbeef->pl_add_meta (it, "title", NULL); deadbeef->pl_set_item_duration (it, -1); - it->filetype = NULL;//filetypes[0]; after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); return after; @@ -1143,9 +1303,7 @@ cmp3_insert (DB_playItem_t *after, const char *fname) { break; } } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->rewind (fp); // reset tags @@ -1155,13 +1313,18 @@ cmp3_insert (DB_playItem_t *after, const char *fname) { /*int apeerr = */deadbeef->junk_apev2_read (it, fp); /*int v2err = */deadbeef->junk_id3v2_read (it, fp); /*int v1err = */deadbeef->junk_id3v1_read (it, fp); - deadbeef->pl_add_meta (it, "title", NULL); + deadbeef->pl_set_meta_int (it, ":MP3_DELAY", buffer.delay); + deadbeef->pl_set_meta_int (it, ":MP3_PADDING", buffer.padding); + + buffer.it = it; + cmp3_set_extra_properties (&buffer); + deadbeef->pl_set_item_duration (it, buffer.duration); - it->filetype = ftype; + deadbeef->pl_replace_meta (it, ":FILETYPE", ftype); deadbeef->fclose (fp); // FIXME! bad numsamples passed to cue - DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, buffer.duration*buffer.samplerate, buffer.samplerate); + DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, buffer.totalsamples-buffer.delay-buffer.padding, buffer.samplerate); if (cue_after) { deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue_after); @@ -1175,7 +1338,7 @@ cmp3_insert (DB_playItem_t *after, const char *fname) { int cmp3_read_metadata (DB_playItem_t *it) { - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -1223,7 +1386,8 @@ cmp3_write_metadata (DB_playItem_t *it) { if (id3v2_version != 3 && id3v2_version != 4) { id3v2_version = 3; } - const char *id3v1_encoding = deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1"); + char id3v1_encoding[50]; + deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1", id3v1_encoding, sizeof (id3v1_encoding)); return deadbeef->junk_rewrite_tags (it, junk_flags, id3v2_version, id3v1_encoding); } @@ -1234,20 +1398,34 @@ static const char *exts[] = { // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "stdmpg", .plugin.name = "MPEG decoder", .plugin.descr = "MPEG v1/2 layer1/2/3 decoder based on libmad", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = cmp3_open, .init = cmp3_init, .free = cmp3_free, - .read_int16 = cmp3_read_int16, - .read_float32 = cmp3_read_float32, + .read = cmp3_read, .seek = cmp3_seek, .seek_sample = cmp3_seek_sample, .insert = cmp3_insert, diff --git a/plugins/musepack/Makefile.am b/plugins/musepack/Makefile.am index 213fee32..c82490aa 100644 --- a/plugins/musepack/Makefile.am +++ b/plugins/musepack/Makefile.am @@ -27,6 +27,6 @@ mpc/minimax.h musepack_la_LDFLAGS = -module musepack_la_LIBADD = $(LDADD) -lm -AM_CFLAGS = $(CFLAGS) -fPIC +AM_CFLAGS = $(CFLAGS) -fPIC -std=c99 endif diff --git a/plugins/musepack/mpc_decoder.c b/plugins/musepack/mpc_decoder.c index a7732bf2..952789fd 100644 --- a/plugins/musepack/mpc_decoder.c +++ b/plugins/musepack/mpc_decoder.c @@ -650,11 +650,11 @@ void mpc_decoder_read_bitstream_sv8(mpc_decoder * d, mpc_bits_reader * r, mpc_bo for ( ; k < 36; k += 2 ) { union { mpc_int8_t sym; - struct { mpc_int8_t s1:4, s2:4; }; + struct { mpc_int8_t s1:4, s2:4; } symf; } tmp; tmp.sym = mpc_bits_can_dec(r, Table); - q[k] = tmp.s1; - q[k + 1] = tmp.s2; + q[k] = tmp.symf.s1; + q[k + 1] = tmp.symf.s2; } } else if (Res <= 8) { Tables[0] = & mpc_can_Q [Res - 3][0]; diff --git a/plugins/musepack/musepack.c b/plugins/musepack/musepack.c index 69d18027..7f34c624 100644 --- a/plugins/musepack/musepack.c +++ b/plugins/musepack/musepack.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -80,7 +80,7 @@ mpc_bool_t musepack_vfs_canseek (mpc_reader *r) { } static DB_fileinfo_t * -musepack_open (void) { +musepack_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (musepack_info_t)); musepack_info_t *info = (musepack_info_t *)_info; memset (info, 0, sizeof (musepack_info_t)); @@ -97,7 +97,7 @@ musepack_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->reader.get_size = musepack_vfs_get_size; info->reader.canseek = musepack_vfs_canseek; - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -112,19 +112,17 @@ musepack_init (DB_fileinfo_t *_info, DB_playItem_t *it) { } mpc_demux_get_info (info->demux, &info->si); -// info->mpcdec = mpc_decoder_init (&info->si); -// if (!info->mpcdec) { -// deadbeef->fclose ((DB_FILE *)info->reader.data); -// info->reader.data = NULL; -// return -1; -// } info->vbr_update_acc = 0; info->vbr_update_bits = 0; info->remaining = 0; - _info->bps = 16; - _info->channels = info->si.channels; - _info->samplerate = info->si.sample_freq; + _info->fmt.is_float = 1; + _info->fmt.bps = 32; + _info->fmt.channels = info->si.channels; + _info->fmt.samplerate = info->si.sample_freq; + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } _info->readpos = 0; _info->plugin = &plugin; @@ -145,10 +143,6 @@ static void musepack_free (DB_fileinfo_t *_info) { musepack_info_t *info = (musepack_info_t *)_info; if (info) { -// if (info->mpcdec) { -// mpc_decoder_exit (info->mpcdec); -// info->decoder = NULL; -// } if (info->demux) { mpc_demux_exit (info->demux); info->demux = NULL; @@ -161,27 +155,24 @@ musepack_free (DB_fileinfo_t *_info) { } } +#if 0 static int -musepack_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +musepack_read (DB_fileinfo_t *_info, char *bytes, int size) { musepack_info_t *info = (musepack_info_t *)_info; - if (info->currentsample + size / (2 * _info->channels) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * _info->channels; + int samplesize = _info->fmt.bps / 8 * _info->fmt.channels; + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { return 0; } } int initsize = size; - int out_channels = _info->channels; - if (out_channels > 2) { - out_channels = 2; - } - int sample_size = ((_info->bps >> 3) * out_channels); while (size > 0) { if (info->remaining > 0) { - int n = size / sample_size; + int n = size / samplesize; n = min (n, info->remaining); int nn = n; float *p = info->buffer; @@ -195,7 +186,7 @@ musepack_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } *((int16_t *)bytes) = (int16_t)sample; bytes += 2; - if (_info->channels == 2) { + if (_info->fmt.channels == 2) { sample = (int)(*(p+1) * 32767.0f); if (sample > 32767) { sample = 32767; @@ -207,11 +198,11 @@ musepack_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { bytes += 2; } n--; - size -= sample_size; + size -= samplesize; p += info->si.channels; } if (info->remaining > nn) { - memmove (info->buffer, p, (info->remaining - nn) * sizeof (float) * _info->channels); + memmove (info->buffer, p, (info->remaining - nn) * sizeof (float) * _info->fmt.channels); } info->remaining -= nn; } @@ -227,48 +218,39 @@ musepack_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { info->remaining = frame.samples; } } - info->currentsample += (initsize-size) / sample_size; + info->currentsample += (initsize-size) / samplesize; return initsize-size; } +#endif static int -musepack_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { +musepack_read (DB_fileinfo_t *_info, char *bytes, int size) { musepack_info_t *info = (musepack_info_t *)_info; + int samplesize = _info->fmt.bps / 8 * _info->fmt.channels; - if (info->currentsample + size / (4 * _info->channels) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 4 * _info->channels; + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { return 0; } } int initsize = size; - int out_channels = _info->channels; - if (out_channels > 2) { - out_channels = 2; - } while (size > 0) { if (info->remaining > 0) { - int n = size / (out_channels * 4); + int n = size / samplesize; n = min (n, info->remaining); - int nn = n; - float *p = info->buffer; - while (n > 0) { - *((float *)bytes) = *p; - bytes += 4; - if (out_channels == 2) { - *((float *)bytes) = *(p+1); - bytes += 4; - } - n--; - size -= out_channels * 4; - p += info->si.channels; - } - if (info->remaining > nn) { - memmove (info->buffer, p, (info->remaining - nn) * 4 * _info->channels); + + memcpy (bytes, info->buffer, n * samplesize); + + size -= n * samplesize; + bytes += n * samplesize; + + if (info->remaining > n) { + memmove (info->buffer, ((char *)info->buffer) + n * samplesize, (info->remaining - n) * samplesize); } - info->remaining -= nn; + info->remaining -= n; } if (size > 0 && !info->remaining) { @@ -282,9 +264,10 @@ musepack_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { info->remaining = frame.samples; } } - info->currentsample += (initsize-size) / (4 * _info->channels); + info->currentsample += (initsize-size) / samplesize; return initsize-size; } + static int musepack_seek_sample (DB_fileinfo_t *_info, int sample) { musepack_info_t *info = (musepack_info_t *)_info; @@ -294,7 +277,7 @@ musepack_seek_sample (DB_fileinfo_t *_info, int sample) { return -1; } info->currentsample = sample + info->startsample; - _info->readpos = (float)sample / _info->samplerate; + _info->readpos = (float)sample / _info->fmt.samplerate; info->remaining = 0; return 0; } @@ -302,7 +285,37 @@ musepack_seek_sample (DB_fileinfo_t *_info, int sample) { static int musepack_seek (DB_fileinfo_t *_info, float time) { musepack_info_t *info = (musepack_info_t *)_info; - return musepack_seek_sample (_info, time * _info->samplerate); + return musepack_seek_sample (_info, time * _info->fmt.samplerate); +} + +void +mpc_set_trk_properties (DB_playItem_t *it, mpc_streaminfo *si, int64_t fsize) { + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + deadbeef->pl_add_meta (it, ":BPS", "32"); + snprintf (s, sizeof (s), "%d", si->channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", si->sample_freq); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + snprintf (s, sizeof (s), "%d", (int)(si->average_bitrate/1000)); + deadbeef->pl_add_meta (it, ":BITRATE", s); + snprintf (s, sizeof (s), "%f", si->profile); + deadbeef->pl_add_meta (it, ":MPC_QUALITY_PROFILE", s); + deadbeef->pl_add_meta (it, ":MPC_PROFILE_NAME", si->profile_name); + deadbeef->pl_add_meta (it, ":MPC_ENCODER", si->encoder); + snprintf (s, sizeof (s), "%d.%d", (si->encoder_version&0xff000000)>>24, (si->encoder_version&0x00ff0000)>>16); + deadbeef->pl_add_meta (it, ":MPC_ENCODER_VERSION", s); + deadbeef->pl_add_meta (it, ":MPC_PNS_USED", si->pns ? "1" : "0"); + deadbeef->pl_add_meta (it, ":MPC_TRUE_GAPLESS", si->is_true_gapless ? "1" : "0"); + snprintf (s, sizeof (s), "%d", si->beg_silence); + deadbeef->pl_add_meta (it, ":MPC_BEG_SILENCE", s); + snprintf (s, sizeof (s), "%d", si->stream_version); + deadbeef->pl_add_meta (it, ":MPC_STREAM_VERSION", s); + snprintf (s, sizeof (s), "%d", si->max_band); + deadbeef->pl_add_meta (it, ":MPC_MAX_BAND", s); + deadbeef->pl_add_meta (it, ":MPC_MS", si->ms ? "1" : "0"); + deadbeef->pl_add_meta (it, ":MPC_FAST_SEEK", si->fast_seek ? "1" : "0"); } static DB_playItem_t * @@ -321,6 +334,7 @@ musepack_insert (DB_playItem_t *after, const char *fname) { trace ("mpc: insert failed to open %s\n", fname); return NULL; } + int64_t fsize = deadbeef->fgetlength (fp); reader.data = fp; mpc_demux *demux = mpc_demux_init (&reader); @@ -363,11 +377,9 @@ musepack_insert (DB_playItem_t *after, const char *fname) { int i; for (i = 0; i < nchapters; i++) { const mpc_chap_info *ch = mpc_demux_chap (demux, i); - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "MusePack"; - it->tracknum = i; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "MusePack"); + deadbeef->pl_set_meta_int (it, ":TRACKNUM", i); it->startsample = ch->sample; it->endsample = totalsamples-1; float gain = gain_title, peak = peak_title; @@ -377,10 +389,10 @@ musepack_insert (DB_playItem_t *after, const char *fname) { if (ch->peak != 0) { peak = pow (10, ch->peak / (20.0 * 256.0)) / (1<<15); } - it->replaygain_album_gain = gain_album; - it->replaygain_album_peak = peak_album; - it->replaygain_track_gain = gain_title; - it->replaygain_track_peak = peak_title; + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, gain_album); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, peak_album); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, gain_title); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, peak_title); deadbeef->pl_set_item_flags (it, DDB_IS_SUBTRACK); if (!prev) { meta = deadbeef->pl_item_alloc (); @@ -402,6 +414,9 @@ musepack_insert (DB_playItem_t *after, const char *fname) { deadbeef->pl_items_copy_junk (meta, it, it); } } + + mpc_set_trk_properties (it, &si, fsize); + after = deadbeef->pl_insert_item (after, it); prev = it; deadbeef->pl_item_unref (it); @@ -415,17 +430,15 @@ musepack_insert (DB_playItem_t *after, const char *fname) { return after; } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "MusePack"; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "MusePack"); deadbeef->pl_set_item_duration (it, dur); /*int apeerr = */deadbeef->junk_apev2_read (it, fp); - it->replaygain_album_gain = gain_album; - it->replaygain_album_peak = peak_album; - it->replaygain_track_gain = gain_title; - it->replaygain_track_peak = peak_title; + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, gain_album); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, peak_album); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, gain_title); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, peak_title); deadbeef->fclose (fp); @@ -447,6 +460,7 @@ musepack_insert (DB_playItem_t *after, const char *fname) { } deadbeef->pl_unlock (); + mpc_set_trk_properties (it, &si, fsize); cue = deadbeef->pl_insert_cue (after, it, totalsamples, si.sample_freq); if (cue) { deadbeef->pl_item_unref (it); @@ -468,7 +482,7 @@ musepack_insert (DB_playItem_t *after, const char *fname) { } static int musepack_read_metadata (DB_playItem_t *it) { - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -512,22 +526,38 @@ static const char *filetypes[] = { "MusePack", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "musepack", .plugin.name = "MusePack decoder", .plugin.descr = "Musepack decoder using libmppdec", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses Musepack SV8 libs (r435), (C) 2005-2009, The Musepack Development Team\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = musepack_start, .plugin.stop = musepack_stop, .open = musepack_open, .init = musepack_init, .free = musepack_free, - .read_int16 = musepack_read_int16, - .read_float32 = musepack_read_float32, + .read = musepack_read, .seek = musepack_seek, .seek_sample = musepack_seek_sample, .insert = musepack_insert, diff --git a/plugins/notify/notify.c b/plugins/notify/notify.c index ba7daa92..c572b131 100644 --- a/plugins/notify/notify.c +++ b/plugins/notify/notify.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include "../../gettext.h" +#include "../artwork/artwork.h" #define E_NOTIFICATION_BUS_NAME "org.freedesktop.Notifications" #define E_NOTIFICATION_INTERFACE "org.freedesktop.Notifications" @@ -29,8 +30,62 @@ DB_functions_t *deadbeef; DB_misc_t plugin; +DB_artwork_plugin_t *artwork_plugin; -#define NOTIFY_DEFAULT_FORMAT "%a - %t" +static dbus_uint32_t replaces_id = 0; + +#define NOTIFY_DEFAULT_TITLE "%t" +#define NOTIFY_DEFAULT_CONTENT "%a - %b" + +static void +notify_thread (void *ctx) { + + DBusMessage *msg = (DBusMessage*) ctx; + DBusMessage *reply = NULL; + + DBusError error; + dbus_error_init (&error); + DBusConnection *conn = dbus_bus_get (DBUS_BUS_SESSION, &error); + if(dbus_error_is_set (&error)) { + fprintf(stderr, "connection failed: %s",error.message); + dbus_error_free(&error); + dbus_message_unref (msg); + deadbeef->thread_exit(NULL); + } + + reply = dbus_connection_send_with_reply_and_block (conn, msg, -1, &error); + if (dbus_error_is_set (&error)) { + fprintf(stderr, "send_with_reply_and_block error: (%s)\n", error.message); + dbus_error_free(&error); + dbus_message_unref (msg); + deadbeef->thread_exit(NULL); + } + + if (reply != NULL) { + // Process the reply message + DBusMessageIter args; + + dbus_uint32_t id = 0; + if (dbus_message_iter_init(reply, &args)) { + if (DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type(&args)) { + dbus_message_iter_get_basic(&args, &id); + if (id != replaces_id) { + replaces_id = id; + } + dbus_message_unref (reply); + } else { + fprintf(stderr, "Argument is not uint32\n"); + } + } else { + fprintf(stderr, "Reply has no arguments\n"); + } + } + + dbus_message_unref (msg); + dbus_connection_unref (conn); + deadbeef->thread_exit(NULL); + +} #if 0 static void @@ -62,107 +117,137 @@ notify_marshal_dict_string(DBusMessageIter *iter, const char *key, const char *v } #endif +static void +esc_xml (const char *cmd, char *esc, int size) { + const char *src = cmd; + char *dst = esc; + char *end = dst + size - 1; + while (*src && dst < end) { + if (*src == '&') { + if (end - dst < 5) { + break; + } + strcpy (dst, "&"); + dst += 5; + src++; + } + else if (*src == '<') { + if (end - dst < 4) { + break; + } + strcpy (dst, "<"); + dst += 4; + src++; + } + else if (*src == '>') { + if (end - dst < 4) { + break; + } + strcpy (dst, ">"); + dst += 4; + src++; + } + else if (*src == '\'') { + if (end - dst < 6) { + break; + } + strcpy (dst, "'"); + dst += 6; + src++; + } + else if (*src == '"') { + if (end - dst < 6) { + break; + } + strcpy (dst, """); + dst += 6; + src++; + } + else if (*src == '\\' && *(src+1) == 'n') { + strcpy (dst, "\n"); + dst++; + src+=2; + } + else { + *dst++ = *src++; + } + } + *dst = 0; +} + + +static void +cover_avail_callback (const char *fname, const char *artist, const char *album, void *user_data) { +// show_notification (track); +} + +static void show_notification (DB_playItem_t *track) { + char title[1024]; + char content[1024]; + deadbeef->conf_lock (); + deadbeef->pl_format_title (track, -1, title, sizeof (title), -1, deadbeef->conf_get_str_fast ("notify.format", NOTIFY_DEFAULT_TITLE)); + deadbeef->pl_format_title (track, -1, content, sizeof (content), -1, deadbeef->conf_get_str_fast ("notify.format_content", NOTIFY_DEFAULT_CONTENT)); + deadbeef->conf_unlock (); + + // escape & +// char esc_title[1024]; + char esc_content[1024]; +// esc_xml (title, esc_title, sizeof (esc_title)); + esc_xml (content, esc_content, sizeof (esc_content)); + DBusMessage *msg = dbus_message_new_method_call (E_NOTIFICATION_BUS_NAME, E_NOTIFICATION_PATH, E_NOTIFICATION_INTERFACE, "Notify"); + + const char *v_appname = "DeaDBeeF"; + dbus_uint32_t v_id = 0; + char *v_iconname = NULL; + if (deadbeef->conf_get_int("notify.albumart", 0) && artwork_plugin) { + const char *album = deadbeef->pl_find_meta (track, "album"); + const char *artist = deadbeef->pl_find_meta (track, "artist"); + v_iconname = artwork_plugin->get_album_art (deadbeef->pl_find_meta (track, ":URI"), artist, album, deadbeef->conf_get_int ("notify.albumart_size", 64), cover_avail_callback, NULL); + } + if (!v_iconname) { + v_iconname = strdup ("deadbeef"); + } + const char *v_summary = title; + const char *v_body = esc_content; + dbus_int32_t v_timeout = -1; + + dbus_message_append_args (msg + , DBUS_TYPE_STRING, &v_appname + , DBUS_TYPE_UINT32, &replaces_id + , DBUS_TYPE_STRING, &v_iconname + , DBUS_TYPE_STRING, &v_summary + , DBUS_TYPE_STRING, &v_body + , DBUS_TYPE_INVALID + ); + + DBusMessageIter iter, sub; + // actions + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub); + dbus_message_iter_close_container(&iter, &sub); + // hints + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub); + dbus_message_iter_close_container(&iter, &sub); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &v_timeout); + + intptr_t tid = 0; + if ((tid=deadbeef->thread_start(notify_thread, msg)) != 0) { + dbus_message_ref (msg); + deadbeef->thread_detach (tid); + } + dbus_message_unref (msg); + if (v_iconname) { + free (v_iconname); + } +} + static int on_songchanged (DB_event_trackchange_t *ev, uintptr_t data) { if (ev->to && deadbeef->conf_get_int ("notify.enable", 0)) { DB_playItem_t *track = ev->to; if (track) { - char cmd[1024]; - deadbeef->pl_format_title (track, -1, cmd, sizeof (cmd), -1, deadbeef->conf_get_str ("notify.format", NOTIFY_DEFAULT_FORMAT)); - - // escape & - char esc[1024]; - - char *src = cmd; - char *dst = esc; - char *end = dst + sizeof (esc) - 1; - while (*src && dst < end) { - if (*src == '&') { - if (end - dst < 5) { - break; - } - strcpy (dst, "&"); - dst += 5; - src++; - } - else if (*src == '<') { - if (end - dst < 4) { - break; - } - strcpy (dst, "<"); - dst += 4; - src++; - } - else if (*src == '>') { - if (end - dst < 4) { - break; - } - strcpy (dst, ">"); - dst += 4; - src++; - } - else if (*src == '\'') { - if (end - dst < 6) { - break; - } - strcpy (dst, "'"); - dst += 6; - src++; - } - else if (*src == '"') { - if (end - dst < 6) { - break; - } - strcpy (dst, """); - dst += 6; - src++; - } - else { - *dst++ = *src++; - } - } - *dst = 0; - - DBusError error; - dbus_error_init (&error); - DBusConnection *conn = dbus_bus_get (DBUS_BUS_SESSION, &error); - if(conn == NULL) { - printf("connection failed: %s",error.message); - exit(1); - } - DBusMessage *msg = dbus_message_new_method_call (E_NOTIFICATION_BUS_NAME, E_NOTIFICATION_PATH, E_NOTIFICATION_INTERFACE, "Notify"); - - const char *v_appname = "DeaDBeeF"; - dbus_uint32_t v_id = 0; - const char *v_iconname = "deadbeef"; - const char *v_summary = _("DeaDBeeF now playing"); - const char *v_body = esc; - dbus_int32_t v_timeout = -1; - - dbus_message_append_args (msg - , DBUS_TYPE_STRING, &v_appname - , DBUS_TYPE_UINT32, &v_id - , DBUS_TYPE_STRING, &v_iconname - , DBUS_TYPE_STRING, &v_summary - , DBUS_TYPE_STRING, &v_body - , DBUS_TYPE_INVALID - ); - - DBusMessageIter iter, sub; - // actions - dbus_message_iter_init_append(msg, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub); - dbus_message_iter_close_container(&iter, &sub); - // hints - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub); - dbus_message_iter_close_container(&iter, &sub); - - dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &v_timeout); - - int serial; - dbus_bool_t retval = dbus_connection_send(conn,msg,&serial); - dbus_connection_flush (conn); - dbus_message_unref (msg); + show_notification (track); } } return 0; @@ -180,9 +265,24 @@ notify_stop (void) { return 0; } +static int +notify_connect (void) { + artwork_plugin = (DB_artwork_plugin_t *)deadbeef->plug_get_for_id ("artwork"); + return 0; +} + +static int +notify_disconnect (void) { + artwork_plugin = NULL; + return 0; +} + static const char settings_dlg[] = "property \"Enable\" checkbox notify.enable 0;\n" - "property \"Notification format\" entry notify.format \"" NOTIFY_DEFAULT_FORMAT "\";\n" + "property \"Notification title format\" entry notify.format \"" NOTIFY_DEFAULT_TITLE "\";\n" + "property \"Notification content format\" entry notify.format_content \"" NOTIFY_DEFAULT_CONTENT "\";\n" + "property \"Show album art\" checkbox notify.albumart 1;\n" + "property \"Album art size (px)\" entry notify.albumart_size 64;\n" ; DB_misc_t plugin = { @@ -192,12 +292,29 @@ DB_misc_t plugin = { .plugin.version_minor = 0, .plugin.id = "notify", .plugin.name = "OSD Notify", - .plugin.descr = "notification daemon OSD", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.descr = "Displays notifications when new track starts.\nRequires dbus and notification daemon to be running.\nNotification daemon should be provided by your desktop environment.\n", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sourceforge.net", .plugin.start = notify_start, .plugin.stop = notify_stop, + .plugin.connect = notify_connect, + .plugin.disconnect = notify_disconnect, .plugin.configdialog = settings_dlg, }; diff --git a/plugins/nullout/nullout.c b/plugins/nullout/nullout.c index 8720de6d..c568f5d2 100644 --- a/plugins/nullout/nullout.c +++ b/plugins/nullout/nullout.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,7 +32,6 @@ DB_functions_t *deadbeef; static intptr_t null_tid; static int null_terminate; -static int null_rate; static int state; static void @@ -47,8 +46,8 @@ pnull_init (void); static int pnull_free (void); -static int -pnull_change_rate (int rate); +int +pnull_setformat (ddb_waveformat_t *fmt); static int pnull_play (void); @@ -62,32 +61,19 @@ pnull_pause (void); static int pnull_unpause (void); -static int -pnull_get_rate (void); - -static int -pnull_get_bps (void); - -static int -pnull_get_channels (void); - -static int -pnull_get_endianness (void); - int pnull_init (void) { trace ("pnull_init\n"); state = OUTPUT_STATE_STOPPED; - null_rate = 44100; null_terminate = 0; null_tid = deadbeef->thread_start (pnull_thread, NULL); return 0; } int -pnull_change_rate (int rate) { - null_rate = rate; - return null_rate; +pnull_setformat (ddb_waveformat_t *fmt) { + memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t)); + return 0; } int @@ -140,21 +126,6 @@ pnull_unpause (void) { return 0; } -int -pnull_get_rate (void) { - return null_rate; -} - -int -pnull_get_bps (void) { - return 16; -} - -int -pnull_get_channels (void) { - return 2; -} - static int pnull_get_endianness (void) { #if WORDS_BIGENDIAN @@ -220,27 +191,39 @@ nullout_load (DB_functions_t *api) { // define plugin interface static DB_output_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, - .plugin.nostop = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_OUTPUT, + .plugin.id = "nullout", .plugin.name = "null output plugin", .plugin.descr = "doesn't play anything", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = null_start, .plugin.stop = null_stop, .init = pnull_init, .free = pnull_free, - .change_rate = pnull_change_rate, + .setformat = pnull_setformat, .play = pnull_play, .stop = pnull_stop, .pause = pnull_pause, .unpause = pnull_unpause, .state = pnull_get_state, - .samplerate = pnull_get_rate, - .bitspersample = pnull_get_bps, - .channels = pnull_get_channels, - .endianness = pnull_get_endianness, + .fmt = {.samplerate = 44100, .channels = 2, .bps = 16, .channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT} }; diff --git a/plugins/oss/oss.c b/plugins/oss/oss.c index 52c417fa..953398af 100644 --- a/plugins/oss/oss.c +++ b/plugins/oss/oss.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -48,6 +48,8 @@ static int state; static int fd; static uintptr_t mutex; +static char oss_device[100]; + #define BLOCKSIZE 8192 static void @@ -56,70 +58,90 @@ oss_thread (void *context); static int oss_callback (char *stream, int len); -static int -oss_init (void) { - trace ("oss_init\n"); - state = OUTPUT_STATE_STOPPED; - oss_terminate = 0; - mutex = 0; - - // prepare oss for playback - const char *name = deadbeef->conf_get_str ("oss.device", "/dev/dsp"); - fd = open (name, O_WRONLY); - if (fd == -1) { - fprintf (stderr, "oss: failed to open file %s\n", name); - perror (name); - plugin.free (); - return -1; +int +oss_set_hwparams (ddb_waveformat_t *fmt) { + int samplefmt; + switch (fmt->bps) { + case 8: + samplefmt = AFMT_S8; + break; + case 16: + samplefmt = AFMT_S16_NE; + break; + default: + samplefmt = AFMT_S16_NE; + break; } - -#if OSS_VERSION>=0x040000 -/* - int cooked = 1; - ioctl (fd, SNDCTL_DSP_COOKEDMODE, &cooked); - trace ("oss: cooked_mode=%d\n", cooked); - - int policy = 3; - ioctl (fd, SNDCTL_DSP_POLICY, &policy); - trace ("oss: policy=%d\n", policy); -*/ -#endif - - int fmt = AFMT_S16_NE; - if (ioctl (fd, SNDCTL_DSP_SETFMT, &fmt) == -1) { + if (ioctl (fd, SNDCTL_DSP_SETFMT, &samplefmt) == -1) { fprintf (stderr, "oss: failed to set format\n"); perror ("SNDCTL_DSP_SETFMT"); - plugin.free (); - return -1; - } - - if (fmt != AFMT_S16_NE) { - fprintf (stderr, "oss: device doesn't support 16 bit sample format\n"); - plugin.free (); return -1; } - int channels = 2; + int channels = fmt->channels; if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { - fprintf (stderr, "oss: failed to set channels\n"); + if (channels != 2) { + fprintf (stderr, "oss: failed to set %d channels, trying fallback to stereo\n", fmt->channels); + channels = 2; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { + fprintf (stderr, "oss: stereo fallback failed\n"); + perror ("SNDCTL_DSP_CHANNELS"); + return -1; + } + } + else { + fprintf (stderr, "oss: failed to set %d channels\n", fmt->channels); + perror ("SNDCTL_DSP_CHANNELS"); + return -1; + } + } + int rate = fmt->samplerate; + if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) == -1) { + fprintf (stderr, "oss: can't switch to %d samplerate\n", rate); perror ("SNDCTL_DSP_CHANNELS"); - plugin.free (); return -1; } - if (channels != 2) { - fprintf (stderr, "oss: device doesn't support stereo output\n"); - plugin.free (); + + plugin.fmt.samplerate = rate; + plugin.fmt.channels = channels; + switch (samplefmt) { + case AFMT_S8: + plugin.fmt.bps = 8; + break; + case AFMT_S16_LE: + case AFMT_S16_BE: + plugin.fmt.bps = 16; + break; + default: + fprintf (stderr, "oss: unsupported output format: 0x%X\n", samplefmt); return -1; } + plugin.fmt.channelmask = 0; + for (int i = 0; i < plugin.fmt.channels; i++) { + plugin.fmt.channelmask |= 1 << i; + } - if (ioctl (fd, SNDCTL_DSP_SPEED, &oss_rate) == -1) { - fprintf (stderr, "oss: failed to set samplerate\n"); - perror ("SNDCTL_DSP_CHANNELS"); + return 0; +} + +static int +oss_init (void) { + trace ("oss_init\n"); + state = OUTPUT_STATE_STOPPED; + oss_terminate = 0; + mutex = 0; + + // prepare oss for playback + const char *name = oss_device; + fd = open (name, O_WRONLY); + if (fd == -1) { + fprintf (stderr, "oss: failed to open file %s\n", name); + perror (name); plugin.free (); return -1; } - trace ("oss: samplerate: %d\n", oss_rate); + oss_set_hwparams (&plugin.fmt); mutex = deadbeef->mutex_create (); @@ -128,28 +150,6 @@ oss_init (void) { } static int -oss_change_rate (int rate) { - if (!fd) { - oss_rate = rate; - return oss_rate; - } - if (rate == oss_rate) { - trace ("oss_change_rate %d: ignored\n", rate); - return rate; - } - deadbeef->mutex_lock (mutex); - if (ioctl (fd, SNDCTL_DSP_SPEED, &rate) == -1) { - fprintf (stderr, "oss: can't switch to %d samplerate\n", rate); - perror ("SNDCTL_DSP_CHANNELS"); - plugin.free (); - return -1; - } - oss_rate = rate; - deadbeef->mutex_unlock (mutex); - return oss_rate; -} - -static int oss_free (void) { trace ("oss_free\n"); if (!oss_terminate) { @@ -201,6 +201,41 @@ oss_pause (void) { return 0; } + +static int +oss_setformat (ddb_waveformat_t *fmt) { + trace ("oss_setformat\n"); + if (!fd) { + memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t)); + } + if (!memcmp (fmt, &plugin.fmt, sizeof (ddb_waveformat_t))) { + return 0; + } + deadbeef->mutex_lock (mutex); + + if (0 != oss_set_hwparams (fmt)) { + return -1; + } + + deadbeef->mutex_unlock (mutex); + + switch (state) { + case OUTPUT_STATE_STOPPED: + return oss_stop (); + case OUTPUT_STATE_PLAYING: + return oss_play (); + case OUTPUT_STATE_PAUSED: + if (0 != oss_play ()) { + return -1; + } + if (0 != oss_pause ()) { + return -1; + } + break; + } + return 0; +} + static int oss_unpause (void) { oss_play (); @@ -247,7 +282,14 @@ oss_thread (void *context) { int res = 0; - char buf[BLOCKSIZE]; + int sample_size = plugin.fmt.channels * (plugin.fmt.bps / 8); + int bs = BLOCKSIZE; + int mod = bs % sample_size; + if (mod > 0) { + bs -= mod; + } + char buf[bs]; + int write_size = oss_callback (buf, sizeof (buf)); deadbeef->mutex_lock (mutex); if ( write_size > 0 ) @@ -264,13 +306,7 @@ oss_thread (void *context) { static int oss_callback (char *stream, int len) { - int bytesread = deadbeef->streamer_read (stream, len); - int16_t ivolume = deadbeef->volume_get_amp () * 1000; - for (int i = 0; i < bytesread/2; i++) { - ((int16_t*)stream)[i] = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000); - } - - return bytesread; + return deadbeef->streamer_read (stream, len); } static int @@ -279,12 +315,28 @@ oss_get_state (void) { } static int +oss_configchanged (DB_event_t *ev, uintptr_t data) { + deadbeef->conf_lock (); + const char *dev = deadbeef->conf_get_str_fast ("oss.device", "/dev/dsp"); + if (strcmp (dev, oss_device)) { + strncpy (oss_device, dev, sizeof (oss_device)-1); + trace ("oss: config option changed, restarting\n"); + deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0); + } + deadbeef->conf_unlock (); + return 0; +} + +static int oss_plugin_start (void) { + deadbeef->conf_get_str ("oss.device", "/dev/dsp", oss_device, sizeof (oss_device)); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (oss_configchanged), 0); return 0; } static int oss_plugin_stop (void) { + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (oss_configchanged), 0); return 0; } @@ -294,31 +346,46 @@ oss_load (DB_functions_t *api) { return DB_PLUGIN (&plugin); } +static const char settings_dlg[] = + "property \"Device file\" entry oss.device /dev/dsp;\n"; + // define plugin interface static DB_output_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, - .plugin.nostop = 0, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_OUTPUT, .plugin.id = "oss", .plugin.name = "OSS output plugin", .plugin.descr = "plays sound via OSS API", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = oss_plugin_start, .plugin.stop = oss_plugin_stop, + .plugin.configdialog = settings_dlg, .init = oss_init, .free = oss_free, - .change_rate = oss_change_rate, + .setformat = oss_setformat, .play = oss_play, .stop = oss_stop, .pause = oss_pause, .unpause = oss_unpause, .state = oss_get_state, - .samplerate = oss_get_rate, - .bitspersample = oss_get_bps, - .channels = oss_get_channels, - .endianness = oss_get_endianness, + .fmt = {-1}, }; diff --git a/plugins/pulse/pulse.c b/plugins/pulse/pulse.c index 266ea107..9a08ff08 100644 --- a/plugins/pulse/pulse.c +++ b/plugins/pulse/pulse.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,7 +42,6 @@ static DB_output_t plugin; DB_functions_t * deadbeef; -#define CONFSTR_PULSE_SAMPLERATE "pulse.samplerate" #define CONFSTR_PULSE_SERVERADDR "pulse.serveraddr" #define CONFSTR_PULSE_BUFFERSIZE "pulse.buffersize" @@ -51,6 +50,8 @@ static int pulse_terminate; static pa_simple *s; static pa_sample_spec ss; +static pa_channel_map channel_map; +static ddb_waveformat_t requested_fmt; static int state; static uintptr_t mutex; @@ -60,29 +61,70 @@ static void pulse_thread(void *context); static void pulse_callback(char *stream, int len); -static int pulse_init(void) +static int pulse_init(); + +static int pulse_free(); + +static int pulse_setformat(ddb_waveformat_t *fmt); + +static int pulse_play(); + +static int pulse_stop(); + +static int pulse_pause(); + +static int pulse_unpause(); + +static int pulse_set_spec(ddb_waveformat_t *fmt) { - trace ("pulse_init\n"); - state = OUTPUT_STATE_STOPPED; - pulse_terminate = 0; + memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t)); + if (!plugin.fmt.channels) { + // generic format + plugin.fmt.bps = 16; + plugin.fmt.is_float = 0; + plugin.fmt.channels = 2; + plugin.fmt.samplerate = 44100; + plugin.fmt.channelmask = 3; + } - // Read serveraddr from config - const char * server = deadbeef->conf_get_str(CONFSTR_PULSE_SERVERADDR, NULL); + trace ("format %dbit %s %dch %dHz channelmask=%X\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int", plugin.fmt.channels, plugin.fmt.samplerate, plugin.fmt.channelmask); - if (server) - server = strcmp(server, "default") ? server : NULL; + ss.channels = plugin.fmt.channels; + // Try to auto-configure the channel map, see <pulse/channelmap.h> for details + pa_channel_map_init_extend(&channel_map, ss.channels, PA_CHANNEL_MAP_DEFAULT); + //pa_channel_map_init(&channel_map); + trace ("pulse: channels: %d\n", ss.channels); // Read samplerate from config - ss.rate = deadbeef->conf_get_int(CONFSTR_PULSE_SAMPLERATE, 44100); + //ss.rate = deadbeef->conf_get_int(CONFSTR_PULSE_SAMPLERATE, 44100); + ss.rate = plugin.fmt.samplerate; trace ("pulse: samplerate: %d\n", ss.rate); - // TODO: add config for this - pa_channel_map * map = NULL;//pa_channel_map_init_stereo(NULL); - ss.channels = 2; - ss.format = PA_SAMPLE_S16NE; + switch (plugin.fmt.bps) { + case 8: + ss.format = PA_SAMPLE_U8; + break; + case 16: + ss.format = PA_SAMPLE_S16LE; + break; + case 24: + ss.format = PA_SAMPLE_S24LE; + break; + case 32: + if (plugin.fmt.is_float) { + ss.format = PA_SAMPLE_FLOAT32LE; + } + else { + ss.format = PA_SAMPLE_S32LE; + } + break; + default: + return -1; + }; - // TODO: where list of all available devices? add this option to config too.. - char * dev = NULL; + if (s) { + pa_simple_free(s); + } pa_buffer_attr * attr = NULL; //attr->maxlength = Maximum length of the buffer. @@ -93,11 +135,42 @@ static int pulse_init(void) buffer_size = deadbeef->conf_get_int(CONFSTR_PULSE_BUFFERSIZE, 4096); + // TODO: where list of all available devices? add this option to config too.. + char * dev = NULL; + int error; - s = pa_simple_new(server, "Deadbeef", PA_STREAM_PLAYBACK, dev, "Music", &ss, map, attr, &error); + + // Read serveraddr from config + deadbeef->conf_lock (); + const char * server = deadbeef->conf_get_str_fast (CONFSTR_PULSE_SERVERADDR, NULL); + + if (server) { + server = strcmp(server, "default") ? server : NULL; + } + + s = pa_simple_new(server, "Deadbeef", PA_STREAM_PLAYBACK, dev, "Music", &ss, &channel_map, attr, &error); + deadbeef->conf_unlock (); if (!s) { + trace ("pulse_init failed (%d)\n", error); + return -1; + } + + return 0; +} + +static int pulse_init(void) +{ + trace ("pulse_init\n"); + state = OUTPUT_STATE_STOPPED; + pulse_terminate = 0; + + if (requested_fmt.samplerate != 0) { + memcpy (&plugin.fmt, &requested_fmt, sizeof (ddb_waveformat_t)); + } + + if (0 != pulse_set_spec(&plugin.fmt)) { return -1; } @@ -106,6 +179,42 @@ static int pulse_init(void) return 0; } +static int pulse_setformat (ddb_waveformat_t *fmt) +{ + memcpy (&requested_fmt, fmt, sizeof (ddb_waveformat_t)); + if (!s) { + return -1; + } + if (!memcmp (fmt, &plugin.fmt, sizeof (ddb_waveformat_t))) { + trace ("pulse_setformat ignored\n"); + return 0; + } + trace ("pulse_setformat %dbit %s %dch %dHz channelmask=%X\n", fmt->bps, fmt->is_float ? "float" : "int", fmt->channels, fmt->samplerate, fmt->channelmask); + + int prev_state = state; + pulse_stop (); + deadbeef->mutex_lock(mutex); + pulse_set_spec(fmt); + deadbeef->mutex_unlock(mutex); + trace ("new format %dbit %s %dch %dHz channelmask=%X\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int", plugin.fmt.channels, plugin.fmt.samplerate, plugin.fmt.channelmask); + + switch (prev_state) { + case OUTPUT_STATE_STOPPED: + return pulse_stop (); + case OUTPUT_STATE_PLAYING: + return pulse_play (); + case OUTPUT_STATE_PAUSED: + if (0 != pulse_play ()) { + return -1; + } + if (0 != pulse_pause ()) { + return -1; + } + break; + } + return 0; +} + static int pulse_free(void) { trace("pulse_free\n"); @@ -118,7 +227,7 @@ static int pulse_free(void) pulse_tid = 0; state = OUTPUT_STATE_STOPPED; - if (s != NULL) + if (s) { pa_simple_free(s); s = NULL; @@ -170,41 +279,6 @@ static int pulse_unpause(void) return 0; } -static int pulse_change_rate(int rate) -{ - pulse_free(); - ss.rate = rate; - - if (!pulse_init()) - return -1; - - return ss.rate; -} - -static int pulse_get_rate(void) -{ - return ss.rate; -} - -static int pulse_get_bps(void) -{ - return 16; -} - -static int pulse_get_channels(void) -{ - return ss.channels; -} - -static int pulse_get_endianness(void) -{ -#if WORDS_BIGENDIAN - return 1; -#else - return 0; -#endif -} - static void pulse_thread(void *context) { #ifdef __linux__ @@ -219,7 +293,14 @@ static void pulse_thread(void *context) continue; } - char buf[buffer_size]; + int sample_size = plugin.fmt.channels * (plugin.fmt.bps / 8); + int bs = buffer_size; + int mod = bs % sample_size; + if (mod > 0) { + bs -= mod; + } + + char buf[bs]; pulse_callback (buf, sizeof (buf)); int error; @@ -240,13 +321,6 @@ static void pulse_thread(void *context) static void pulse_callback(char *stream, int len) { int bytesread = deadbeef->streamer_read(stream, len); - int16_t ivolume = deadbeef->volume_get_amp() * 1000; - - for (int i = 0; i < bytesread/2; i++) - { - ((int16_t*)stream)[i] = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000); - } - if (bytesread < len) { memset (stream + bytesread, 0, len-bytesread); @@ -280,34 +354,45 @@ DB_plugin_t * pulse_load(DB_functions_t *api) static const char settings_dlg[] = "property \"PulseAudio server\" entry " CONFSTR_PULSE_SERVERADDR " default;\n" - "property \"Preferred buffer size\" entry " CONFSTR_PULSE_BUFFERSIZE " 4096;\n" - "property \"Samplerate\" entry " CONFSTR_PULSE_SAMPLERATE " 44100;\n"; + "property \"Preferred buffer size\" entry " CONFSTR_PULSE_BUFFERSIZE " 4096;\n"; static DB_output_t plugin = { DB_PLUGIN_SET_API_VERSION .plugin.version_major = 0, .plugin.version_minor = 1, - .plugin.nostop = 0, .plugin.type = DB_PLUGIN_OUTPUT, + .plugin.id = "pulseaudio", .plugin.name = "PulseAudio output plugin", - .plugin.descr = "plays sound via pulse API", - .plugin.author = "Anton Novikov", - .plugin.email = "tonn.post@gmail.com", + .plugin.descr = "At the moment of this writing, PulseAudio seems to be very unstable in many (or most) GNU/Linux distributions.\nIf you experience problems - please try switching to ALSA or OSS output.\nIf that doesn't help - please uninstall PulseAudio from your system, and try ALSA or OSS again.\nThanks for understanding", + .plugin.copyright = + "Copyright (C) 2011 Jan D. Behrens <zykure@web.de>\n" + "Copyright (C) 2010-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Copyright (C) 2010 Anton Novikov <tonn.post@gmail.com>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n", .plugin.website = "http://deadbeef.sf.net", .plugin.start = pulse_plugin_start, .plugin.stop = pulse_plugin_stop, .plugin.configdialog = settings_dlg, .init = pulse_init, .free = pulse_free, - .change_rate = pulse_change_rate, + .setformat = pulse_setformat, .play = pulse_play, .stop = pulse_stop, .pause = pulse_pause, .unpause = pulse_unpause, .state = pulse_get_state, - .samplerate = pulse_get_rate, - .bitspersample = pulse_get_bps, - .channels = pulse_get_channels, - .endianness = pulse_get_endianness, }; diff --git a/plugins/shellexec/Makefile.am b/plugins/shellexec/Makefile.am index 65d6a5b8..212f6a0b 100644 --- a/plugins/shellexec/Makefile.am +++ b/plugins/shellexec/Makefile.am @@ -4,6 +4,6 @@ pkglib_LTLIBRARIES = shellexec.la shellexec_la_SOURCES = shellexec.c shellexec_la_LDFLAGS = -module -shellexec_la_LIBADD = $(LDADD) $(HOTKEYS_LIBS) +shellexec_la_LIBADD = $(LDADD) AM_CFLAGS = $(CFLAGS) -std=c99 endif diff --git a/plugins/shellexec/shellexec.c b/plugins/shellexec/shellexec.c index e8298cab..a2c60af4 100644 --- a/plugins/shellexec/shellexec.c +++ b/plugins/shellexec/shellexec.c @@ -1,5 +1,6 @@ /* Shellexec plugin for DeaDBeeF + Copyright (C) 2010-2011 Alexey Yakovenko <waker@users.sf.net> Copyright (C) 2010 Viktor Semykin <thesame.ml@gmail.com> This program is free software: you can redistribute it and/or modify @@ -101,7 +102,7 @@ shx_callback (Shx_action_t *action, DB_playItem_t *it) static DB_plugin_action_t * shx_get_actions (DB_playItem_t *it) { - int is_local = it ? deadbeef->is_local_file (it->fname) : 1; + int is_local = it ? deadbeef->is_local_file (deadbeef->pl_find_meta (it, ":URI")) : 1; Shx_action_t *action; for (action = actions; action; action = (Shx_action_t *)action->parent.next) @@ -200,12 +201,30 @@ shx_start () static DB_misc_t plugin = { .plugin.api_vmajor = DB_API_VERSION_MAJOR, .plugin.api_vminor = DB_API_VERSION_MINOR, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_MISC, .plugin.id = "shellexec", .plugin.name = "Shell commands", .plugin.descr = "Executes configurable shell commands for tracks", - .plugin.author = "Viktor Semykin", - .plugin.email = "thesame.ml@gmail.com", + .plugin.copyright = + "Copyright (C) 2010-2011 Alexey Yakovenko <waker@users.sf.net>\n" + "Copyright (C) 2010 Viktor Semykin <thesame.ml@gmail.com>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = shx_start, .plugin.get_actions = shx_get_actions diff --git a/plugins/shn/Makefile b/plugins/shn/Makefile new file mode 100644 index 00000000..7ce0e430 --- /dev/null +++ b/plugins/shn/Makefile @@ -0,0 +1,22 @@ +OUT=shn.so + +CC=gcc + +CFLAGS+=-Wall -fPIC -std=c99 -D_GNU_SOURCE -DHAVE_CONFIG_H -I. -I../.. + +LDFLAGS+=-module -shared -lm + +SOURCES=array.c convert.c misc.c output.c seek.c shn.c shorten.c sulawalaw.c vario.c wave.c + +OBJECTS=$(SOURCES:.c=.o) + +all: $(SOURCES) $(OUT) + +$(OUT): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -c -o $@ + +clean: + rm $(OBJECTS) $(OUT) diff --git a/plugins/shn/Makefile.am b/plugins/shn/Makefile.am deleted file mode 100644 index 335dacff..00000000 --- a/plugins/shn/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -if HAVE_SHN -shndir = $(libdir)/$(PACKAGE) -pkglib_LTLIBRARIES = shn.la -shn_la_SOURCES = array.c convert.c misc.c output.c seek.c shn.c shn.h shorten.c shorten.h sulawalaw.c vario.c wave.c bitshift.h - -shn_la_LDFLAGS = -module - -shn_la_LIBADD = $(LDADD) -lm -AM_CFLAGS = $(CFLAGS) -std=c99 -endif diff --git a/plugins/shn/shn.c b/plugins/shn/shn.c index 1d44f6e8..4a90583c 100644 --- a/plugins/shn/shn.c +++ b/plugins/shn/shn.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -33,11 +33,6 @@ static DB_decoder_t plugin; DB_functions_t *deadbeef; shn_file *load_shn(const char *filename); -static void shn_play(char *); -static void shn_stop(void); -static int shn_get_time(void); -static void shn_get_file_info(char *,char **,int *); -static void shn_display_file_info(char *); typedef struct { DB_fileinfo_t info; @@ -67,7 +62,7 @@ typedef struct { shn_config shn_cfg; DB_fileinfo_t * -shn_open (void) { +shn_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (shn_fileinfo_t)); shn_fileinfo_t *info = (shn_fileinfo_t *)_info; memset (info, 0, sizeof (shn_fileinfo_t)); @@ -311,8 +306,9 @@ shn_init_decoder (shn_fileinfo_t *info) { static void shn_init_config (void) { shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL; - strncpy (shn_cfg.seek_tables_path, deadbeef->conf_get_str ("shn.seektable_path", ""), sizeof (shn_cfg.seek_tables_path)); - strncpy (shn_cfg.relative_seek_tables_path, deadbeef->conf_get_str ("shn.relative_seektable_path", "seektables"), sizeof (shn_cfg.relative_seek_tables_path)); + + deadbeef->conf_get_str ("shn.seektable_path", "", shn_cfg.seek_tables_path, sizeof (shn_cfg.seek_tables_path)); + deadbeef->conf_get_str ("shn.relative_seektable_path", "seektables", shn_cfg.relative_seek_tables_path, sizeof (shn_cfg.relative_seek_tables_path)); shn_cfg.verbose = 0; shn_cfg.swap_bytes = deadbeef->conf_get_int ("shn.swap_bytes", 0); } @@ -326,9 +322,9 @@ shn_init(DB_fileinfo_t *_info, DB_playItem_t *it) { char data[4]; DB_FILE *f; - f = deadbeef->fopen (it->fname); + f = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!f) { - trace ("shn: failed to open %s\n", it->fname); + trace ("shn: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } @@ -340,7 +336,7 @@ shn_init(DB_fileinfo_t *_info, DB_playItem_t *it) { if (deadbeef->fread((void *)data,1,4,f) != 4) { deadbeef->fclose(f); - trace ("shn: failed to read magic from %s\n", it->fname); + trace ("shn: failed to read magic from %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } deadbeef->fclose(f); @@ -350,14 +346,17 @@ shn_init(DB_fileinfo_t *_info, DB_playItem_t *it) { return -1; } - if (!(info->shnfile = load_shn(it->fname))) { + if (!(info->shnfile = load_shn(deadbeef->pl_find_meta (it, ":URI")))) { trace ("shn: load_shn failed\n"); return -1; } - _info->bps = info->shnfile->wave_header.bits_per_sample; - _info->channels = info->shnfile->wave_header.channels; - _info->samplerate = info->shnfile->wave_header.samples_per_sec; + _info->fmt.bps = info->shnfile->wave_header.bits_per_sample; + _info->fmt.channels = info->shnfile->wave_header.channels; + _info->fmt.samplerate = info->shnfile->wave_header.samples_per_sec; + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } _info->plugin = &plugin; int totalsamples = info->shnfile->wave_header.length * info->shnfile->wave_header.samples_per_sec; @@ -732,22 +731,21 @@ shn_decode (shn_fileinfo_t *info) { } int -shn_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +shn_read (DB_fileinfo_t *_info, char *bytes, int size) { shn_fileinfo_t *info = (shn_fileinfo_t *)_info; - if (info->currentsample + size / (2 * _info->channels) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * _info->channels; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { return 0; } } int initsize = size; - int ch = min (_info->channels, 2); - int sample_size = ch * (_info->bps >> 3); while (size > 0) { if (info->shnfile->vars.bytes_in_buf > 0) { - int n = size / sample_size; - int nsamples = info->shnfile->vars.bytes_in_buf / (_info->channels * 2); + int n = size / samplesize; + int nsamples = info->shnfile->vars.bytes_in_buf / samplesize; if (info->skipsamples > 0) { int nskip = min(nsamples, info->skipsamples); info->skipsamples -= nskip; @@ -756,26 +754,23 @@ shn_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { continue; } else { - memmove (info->shnfile->vars.buffer, info->shnfile->vars.buffer + nskip * (_info->channels * 2), info->shnfile->vars.bytes_in_buf - nskip * (_info->channels * 2)); + memmove (info->shnfile->vars.buffer, info->shnfile->vars.buffer + nskip * samplesize, info->shnfile->vars.bytes_in_buf - nskip * samplesize); nsamples -= nskip; continue; } } n = min (nsamples, n); - char *src = info->shnfile->vars.buffer; - for (int i = 0; i < n; i++) { - memcpy (bytes, src, sample_size); - bytes += sample_size; - src += _info->channels * 2; - } - - size -= n * sample_size; - if (n == info->shnfile->vars.bytes_in_buf / (_info->channels * 2)) { + char *src = (char *)info->shnfile->vars.buffer; + memcpy (bytes, src, samplesize * n); + src += samplesize * n; + bytes += samplesize * n; + size -= n * samplesize; + if (n == info->shnfile->vars.bytes_in_buf / samplesize) { info->shnfile->vars.bytes_in_buf = 0; } else { - memmove (info->shnfile->vars.buffer, src, info->shnfile->vars.bytes_in_buf - n * (_info->channels * 2)); - info->shnfile->vars.bytes_in_buf -= n * (_info->channels * 2); + memmove (info->shnfile->vars.buffer, src, info->shnfile->vars.bytes_in_buf - n * samplesize); + info->shnfile->vars.bytes_in_buf -= n * samplesize; } continue; } @@ -785,7 +780,7 @@ shn_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } } - info->currentsample += (initsize-size) / sample_size; + info->currentsample += (initsize-size) / samplesize; if (size != 0) { trace ("shn_read_int16 eof\n"); } @@ -798,7 +793,7 @@ shn_seek_sample (DB_fileinfo_t *_info, int sample) { sample += info->startsample; - info->shnfile->vars.seek_to = sample / _info->samplerate; + info->shnfile->vars.seek_to = sample / _info->fmt.samplerate; if (info->shnfile->vars.seek_table_entries == NO_SEEK_TABLE) { // seek by skipping samples from the start @@ -814,7 +809,7 @@ shn_seek_sample (DB_fileinfo_t *_info, int sample) { } info->skipsamples = sample; } - info->currentsample = info->shnfile->vars.seek_to * _info->samplerate; + info->currentsample = info->shnfile->vars.seek_to * _info->fmt.samplerate; _info->readpos = info->shnfile->vars.seek_to; return 0; } @@ -853,14 +848,14 @@ shn_seek_sample (DB_fileinfo_t *_info, int sample) { info->shnfile->vars.bytes_in_buf = 0; - info->currentsample = info->shnfile->vars.seek_to * _info->samplerate; + info->currentsample = info->shnfile->vars.seek_to * _info->fmt.samplerate; _info->readpos = info->shnfile->vars.seek_to; return 0; } int shn_seek (DB_fileinfo_t *_info, float time) { - return shn_seek_sample (_info, time * _info->samplerate); + return shn_seek_sample (_info, time * _info->fmt.samplerate); return 0; } @@ -874,6 +869,7 @@ shn_insert (DB_playItem_t *after, const char *fname) { if (!f) { return NULL; } + int64_t fsize = deadbeef->fgetlength (f); int id3v2_tag_size = deadbeef->junk_get_leading_size (f); if (id3v2_tag_size > 0) { @@ -898,10 +894,8 @@ shn_insert (DB_playItem_t *after, const char *fname) { return NULL; } - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "Shorten"; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "Shorten"); deadbeef->pl_set_item_duration (it, tmp_file->wave_header.length); int apeerr = deadbeef->junk_apev2_read (it, tmp_file->vars.fd); @@ -910,6 +904,18 @@ shn_insert (DB_playItem_t *after, const char *fname) { shn_unload(tmp_file); + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + snprintf (s, sizeof (s), "%d", tmp_file->wave_header.bits_per_sample); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", tmp_file->wave_header.channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", tmp_file->wave_header.samples_per_sec); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / (float)tmp_file->wave_header.length * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->pl_insert_item (after, it); @@ -1792,20 +1798,38 @@ static const char settings_dlg[] = // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "shn", - .plugin.name = "SHN player", - .plugin.descr = "SHN player based on xmms-shn", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.name = "Shorten player", + .plugin.descr = "decodes shn files", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Based on xmms-shn, http://www.etree.org/shnutils/xmms-shn/\n" + "Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.configdialog = settings_dlg, .open = shn_open, .init = shn_init, .free = shn_free, - .read_int16 = shn_read_int16, + .read = shn_read, .seek = shn_seek, .seek_sample = shn_seek_sample, .insert = shn_insert, diff --git a/plugins/sid/Makefile.am b/plugins/sid/Makefile.am index 38caa014..f5408088 100644 --- a/plugins/sid/Makefile.am +++ b/plugins/sid/Makefile.am @@ -103,9 +103,9 @@ sidplay-libs/libsidplay/src/kernal.bin\ sidplay-libs/libsidplay/src/psiddrv.bin\ sidplay-libs/libsidplay/src/poweron.bin -sid_la_LIBADD = -lstdc++ -sid_la_LDFLAGS = -module +sid_la_LDFLAGS = -module -nostdlib -lsupc++ AM_CFLAGS = $(CFLAGS) -std=c99 -I$(sidpath)/libsidplay/include -I$(sidpath)/builders/resid-builder/include -fPIC -AM_CPPFLAGS = $(CXXFLAGS) -DHAVE_UNIX -I$(sidpath) -I$(sidpath)/unix -I$(sidpath)/libsidplay -I$(sidpath)/libsidplay/include -I$(sidpath)/libsidplay/include/sidplay -I$(sidpath)/libsidutils/include/sidplay/utils -I$(sidpath)/builders/resid-builder/include/sidplay/builders -I$(sidpath)/builders/resid-builder/include -DHAVE_STRCASECMP -DHAVE_STRNCASECMP +AM_CPPFLAGS = $(CXXFLAGS) -DHAVE_UNIX -I$(sidpath) -I$(sidpath)/unix -I$(sidpath)/libsidplay -I$(sidpath)/libsidplay/include -I$(sidpath)/libsidplay/include/sidplay -I$(sidpath)/libsidutils/include/sidplay/utils -I$(sidpath)/builders/resid-builder/include/sidplay/builders -I$(sidpath)/builders/resid-builder/include -DHAVE_STRCASECMP -DHAVE_STRNCASECMP -fno-exceptions -fno-rtti -fno-unwind-tables + endif diff --git a/plugins/sid/csid.cpp b/plugins/sid/csid.cpp index ccb2ecb3..ffdd4548 100644 --- a/plugins/sid/csid.cpp +++ b/plugins/sid/csid.cpp @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -34,12 +34,26 @@ #include "../../deadbeef.h" #include "csid.h" +#ifndef ANDROID +int _Unwind_Resume_or_Rethrow; +int _Unwind_RaiseException; +int _Unwind_GetLanguageSpecificData; +int _Unwind_Resume; +int _Unwind_DeleteException; +int _Unwind_GetTextRelBase; +int _Unwind_SetIP; +int _Unwind_GetDataRelBase; +int _Unwind_GetRegionStart; +int _Unwind_SetGR; +int _Unwind_GetIPInfo; +#endif + extern DB_decoder_t sid_plugin; //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) -static DB_functions_t *deadbeef; +DB_functions_t *deadbeef; #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) @@ -94,8 +108,9 @@ sldb_load() sldb_disable = 1; return; } - const char *conf_hvsc_path = deadbeef->conf_get_str ("hvsc_path", NULL); - if (!conf_hvsc_path) { + char conf_hvsc_path[1000]; + deadbeef->conf_get_str ("hvsc_path", "", conf_hvsc_path, sizeof (conf_hvsc_path)); + if (!conf_hvsc_path[0]) { sldb_disable = 1; return; } @@ -276,7 +291,7 @@ sldb_find (const uint8_t *digest) { } DB_fileinfo_t * -csid_open (void) { +csid_open (uint32_t hints) { DB_fileinfo_t *_info = (DB_fileinfo_t *)malloc (sizeof (sid_info_t)); memset (_info, 0, sizeof (sid_info_t)); return _info; @@ -288,26 +303,28 @@ csid_init (DB_fileinfo_t *_info, DB_playItem_t *it) { // libsidplay crashes if file doesn't exist // so i have to check it here - FILE *fp = fopen (it->fname, "rb"); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp ){ return -1; } - fclose (fp); + deadbeef->fclose (fp); info->sidplay = new sidplay2; info->resid = new ReSIDBuilder ("wtf"); info->resid->create (info->sidplay->info ().maxsids); -// resid->create (1); info->resid->filter (true); int samplerate = deadbeef->conf_get_int ("sid.samplerate", 44100); - int bps = deadbeef->get_output ()->bitspersample (); + int bps = deadbeef->conf_get_int ("sid.bps", 16); + if (bps != 16 && bps != 8) { + bps = 16; + } info->resid->sampling (samplerate); info->duration = deadbeef->pl_get_item_duration (it); - info->tune = new SidTune (it->fname); + info->tune = new SidTune (deadbeef->pl_find_meta (it, ":URI")); - info->tune->selectSong (it->tracknum+1); + info->tune->selectSong (deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)+1); sid2_config_t conf; conf = info->sidplay->config (); conf.frequency = samplerate; @@ -319,9 +336,10 @@ csid_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->sidplay->load (info->tune); _info->plugin = &sid_plugin; - _info->channels = info->tune->isStereo () ? 2 : 1; - _info->bps = bps; - _info->samplerate = conf.frequency; + _info->fmt.channels = conf.playback == sid2_stereo ? 2 : 1; + _info->fmt.bps = bps; + _info->fmt.samplerate = conf.frequency; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; int maxsids = info->sidplay->info ().maxsids; @@ -354,24 +372,15 @@ csid_read (DB_fileinfo_t *_info, char *bytes, int size) { if (_info->readpos > info->duration) { return 0; } - int rd = info->sidplay->play (bytes, size/_info->channels); - _info->readpos += size/_info->channels/2 / (float)_info->samplerate; -#if 0 -#if WORDS_BIGENDIAN - // convert samples from le to be - int n = rd * _info->channels/2; - int16_t *ptr = (int16_t *)bytes; - while (n > 0) { - int16_t out; - le_int16 (*ptr, (unsigned char *)&out); - *ptr = out; - ptr++; - n--; - } -#endif -#endif - return rd * _info->channels; + int rd = info->sidplay->play (bytes, size); + + int samplesize = (_info->fmt.bps>>3) * _info->fmt.channels; + + _info->readpos += rd / samplesize / (float)_info->fmt.samplerate; + + return size; + } int @@ -386,11 +395,11 @@ csid_seek (DB_fileinfo_t *_info, float time) { t -= _info->readpos; } info->resid->filter (false); - int samples = t * _info->samplerate; - samples *= 2 * _info->channels; - uint16_t buffer[2048 * _info->channels]; + int samples = t * _info->fmt.samplerate; + samples *= (_info->fmt.bps>>3) * _info->fmt.channels; + uint16_t buffer[2048 * _info->fmt.channels]; while (samples > 0) { - int n = min (samples, 2048) * _info->channels; + int n = min (samples, 2048) * _info->fmt.channels; int done = info->sidplay->play (buffer, n); if (done < n) { trace ("sid seek failure\n"); @@ -478,15 +487,13 @@ csid_insert (DB_playItem_t *after, const char *fname) { // Include number of songs. endian_little16 (tmp,tune->getInfo ().songs); myMD5.append (tmp,sizeof(tmp)); - { // Include song speed for each song. - //uint_least16_t currentSong = tune->getInfo ().currentSong; + { + // Include song speed for each song. for (uint_least16_t s = 1; s <= tune->getInfo ().songs; s++) { tune->selectSong (s); myMD5.append (&tune->getInfo ().songSpeed,1); } - // Restore old song - //tune->selectSong (currentSong); } // Deal with PSID v2NG clock speed flags: Let only NTSC // clock speed change the MD5 fingerprint. That way the @@ -499,7 +506,6 @@ csid_insert (DB_playItem_t *after, const char *fname) { memcpy (sig, myMD5.getDigest (), 16); #endif -// sldb_load (); int song = -1; if (sldb_loaded) { song = sldb_find (sig); @@ -509,10 +515,8 @@ csid_insert (DB_playItem_t *after, const char *fname) { for (int s = 0; s < tunes; s++) { trace ("select %d...\n", s); if (tune->selectSong (s+1)) { - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (sid_plugin.plugin.id); - it->fname = strdup (fname); - it->tracknum = s; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, sid_plugin.plugin.id); + deadbeef->pl_set_meta_int (it, ":TRACKNUM", s); SidTuneInfo sidinfo; tune->getInfo (sidinfo); int i = sidinfo.numberOfInfoStrings; @@ -549,7 +553,7 @@ csid_insert (DB_playItem_t *after, const char *fname) { deadbeef->pl_add_meta (it, "title", NULL); } - float length = 120; + float length = deadbeef->conf_get_float ("sid.defaultlength", 180); if (sldb_loaded) { if (song >= 0 && sldb->sldb_lengths[song][s] >= 0) { length = sldb->sldb_lengths[song][s]; @@ -563,7 +567,7 @@ csid_insert (DB_playItem_t *after, const char *fname) { // } } deadbeef->pl_set_item_duration (it, length); - it->filetype = "SID"; + deadbeef->pl_add_meta (it, ":FILETYPE", "SID"); after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); diff --git a/plugins/sid/csid.h b/plugins/sid/csid.h index 273daa2c..c35be3d7 100644 --- a/plugins/sid/csid.h +++ b/plugins/sid/csid.h @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ extern "C" { #endif -DB_fileinfo_t *csid_open (void); +DB_fileinfo_t *csid_open (uint32_t hints); int csid_init (DB_fileinfo_t *_info, DB_playItem_t *it); void csid_free (DB_fileinfo_t *); int csid_read (DB_fileinfo_t *, char *bytes, int size); diff --git a/plugins/sid/plugin.c b/plugins/sid/plugin.c index 9a2e9a27..c8f1f7b0 100644 --- a/plugins/sid/plugin.c +++ b/plugins/sid/plugin.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,21 +22,42 @@ static const char *exts[] = { "sid",NULL }; const char *filetypes[] = { "SID", NULL }; static const char settings_dlg[] = - "property \"Enable HVSC\" checkbox hvsc_enable 0;\n" - "property \"HVSC path\" file hvsc_path \"\";\n" - "property \"Samplerate\" entry sid.samplerate 48000;\n" + "property \"Enable HVSC Songlength DB\" checkbox hvsc_enable 0;\n" + "property \"Songlengths.txt (from HVSC)\" file hvsc_path \"\";\n" + "property \"Samplerate\" entry sid.samplerate 44100;\n" + "property \"Bits per sample (8 or 16)\" entry sid.bps 16;\n" + "property \"Default song length (sec)\" entry sid.defaultlength 180;\n" ; // define plugin interface DB_decoder_t sid_plugin = { DB_PLUGIN_SET_API_VERSION .plugin.type = DB_PLUGIN_DECODER, - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.name = "SID decoder", .plugin.descr = "SID player based on libsidplay2", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified libsidplay2-2.1.0\n" + "Commodore 64 SID emulation library\n" + "Copyright (C) Simon White and other authors\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = csid_start, .plugin.stop = csid_stop, @@ -45,7 +66,7 @@ DB_decoder_t sid_plugin = { .open = csid_open, .init = csid_init, .free = csid_free, - .read_int16 = csid_read, + .read = csid_read, .seek = csid_seek, .seek_sample = NULL, .insert = csid_insert, diff --git a/plugins/sid/sidplay-libs/libsidplay/src/sidtune/SidTune.cpp b/plugins/sid/sidplay-libs/libsidplay/src/sidtune/SidTune.cpp index 4dfba1d7..fdbd64e1 100644 --- a/plugins/sid/sidplay-libs/libsidplay/src/sidtune/SidTune.cpp +++ b/plugins/sid/sidplay-libs/libsidplay/src/sidtune/SidTune.cpp @@ -25,6 +25,7 @@ #include "sidendian.h" #include "PP20.h" #include <stdio.h> +#include "../../../../../../../deadbeef.h" #ifdef HAVE_EXCEPTIONS # include <new> @@ -32,6 +33,8 @@ #include <string.h> #include <limits.h> +extern DB_functions_t *deadbeef; + #if defined(HAVE_IOS_OPENMODE) typedef std::ios::openmode openmode; #else @@ -252,7 +255,7 @@ bool SidTune::loadFile(const char* fileName, Buffer_sidtt<const uint_least8_t>& Buffer_sidtt<uint_least8_t> fileBuf; uint_least32_t fileLen = 0; - FILE *fp = fopen (fileName, "rb"); + DB_FILE *fp = deadbeef->fopen (fileName); if (!fp) { @@ -261,9 +264,7 @@ bool SidTune::loadFile(const char* fileName, Buffer_sidtt<const uint_least8_t>& } else { - fseek (fp, 0, SEEK_END); - fileLen = ftell (fp); - rewind (fp); + fileLen = deadbeef->fgetlength(fp); #ifdef HAVE_EXCEPTIONS if ( !fileBuf.assign(new(std::nothrow) uint_least8_t[fileLen],fileLen) ) #else @@ -274,7 +275,7 @@ bool SidTune::loadFile(const char* fileName, Buffer_sidtt<const uint_least8_t>& return false; } uint_least32_t restFileLen = fileLen; - int res = fread((char*)fileBuf.get()+(fileLen-restFileLen),1,restFileLen,fp); + int res = deadbeef->fread((char*)fileBuf.get()+(fileLen-restFileLen),1,restFileLen,fp); if ( res != restFileLen ) { info.statusString = SidTune::txt_cantLoadFile; @@ -285,7 +286,7 @@ bool SidTune::loadFile(const char* fileName, Buffer_sidtt<const uint_least8_t>& info.statusString = SidTune::txt_noErrors; } } - fclose(fp); + deadbeef->fclose(fp); if ( fileLen==0 ) { info.statusString = SidTune::txt_empty; diff --git a/plugins/sndfile/sndfile.c b/plugins/sndfile/sndfile.c index ab1d1ee9..3af4616f 100644 --- a/plugins/sndfile/sndfile.c +++ b/plugins/sndfile/sndfile.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -21,6 +21,8 @@ #endif #include <string.h> #include <sndfile.h> +#include <math.h> +#include <stdlib.h> #include "../../deadbeef.h" #define min(x,y) ((x)<(y)?(x):(y)) @@ -85,20 +87,78 @@ static SF_VIRTUAL_IO vfs = { }; static DB_fileinfo_t * -sndfile_open (void) { +sndfile_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (sndfile_info_t)); memset (_info, 0, sizeof (sndfile_info_t)); return _info; } + +// taken from libsndfile +#define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0]))) +/* This stores which bit in dwChannelMask maps to which channel */ +static const struct chanmap_s +{ int id ; + const char * name ; +} channel_mask_bits [] = +{ /* WAVEFORMATEXTENSIBLE doesn't distuingish FRONT_LEFT from LEFT */ + { SF_CHANNEL_MAP_LEFT, "L" }, + { SF_CHANNEL_MAP_RIGHT, "R" }, + { SF_CHANNEL_MAP_CENTER, "C" }, + { SF_CHANNEL_MAP_LFE, "LFE" }, + { SF_CHANNEL_MAP_REAR_LEFT, "Ls" }, + { SF_CHANNEL_MAP_REAR_RIGHT, "Rs" }, + { SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, "Lc" }, + { SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, "Rc" }, + { SF_CHANNEL_MAP_REAR_CENTER, "Cs" }, + { SF_CHANNEL_MAP_SIDE_LEFT, "Sl" }, + { SF_CHANNEL_MAP_SIDE_RIGHT, "Sr" }, + { SF_CHANNEL_MAP_TOP_CENTER, "Tc" }, + { SF_CHANNEL_MAP_TOP_FRONT_LEFT, "Tfl" }, + { SF_CHANNEL_MAP_TOP_FRONT_CENTER, "Tfc" }, + { SF_CHANNEL_MAP_TOP_FRONT_RIGHT, "Tfr" }, + { SF_CHANNEL_MAP_TOP_REAR_LEFT, "Trl" }, + { SF_CHANNEL_MAP_TOP_REAR_CENTER, "Trc" }, + { SF_CHANNEL_MAP_TOP_REAR_RIGHT, "Trr" }, +} ; + + +static int +wavex_gen_channel_mask (const int *chan_map, int channels) +{ int chan, mask = 0, bit = -1, last_bit = -1 ; + + if (chan_map == NULL) + return 0 ; + + for (chan = 0 ; chan < channels ; chan ++) + { int k ; + + for (k = bit + 1 ; k < ARRAY_LEN (channel_mask_bits) ; k++) + if (chan_map [chan] == channel_mask_bits [k].id) + { bit = k ; + break ; + } ; + + /* Check for bad sequence. */ + if (bit <= last_bit) + return 0 ; + + mask += 1 << bit ; + last_bit = bit ; + } ; + + return mask ; +} /* wavex_gen_channel_mask */ + + static int sndfile_init (DB_fileinfo_t *_info, DB_playItem_t *it) { sndfile_info_t *info = (sndfile_info_t*)_info; SF_INFO inf; - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { - trace ("sndfile: failed to open %s\n", it->fname); + trace ("sndfile: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } int fsize = deadbeef->fgetlength (fp); @@ -114,27 +174,45 @@ sndfile_init (DB_fileinfo_t *_info, DB_playItem_t *it) { switch (inf.format&0x000f) { case SF_FORMAT_PCM_S8: case SF_FORMAT_PCM_U8: - _info->bps = 8; + _info->fmt.bps = 8; break; case SF_FORMAT_PCM_16: - _info->bps = 16; + _info->fmt.bps = 16; break; case SF_FORMAT_PCM_24: - _info->bps = 24; + _info->fmt.bps = 24; break; - case SF_FORMAT_PCM_32: case SF_FORMAT_FLOAT: - _info->bps = 24; + _info->fmt.is_float = 1; + case SF_FORMAT_PCM_32: + _info->fmt.bps = 32; break; case SF_FORMAT_DOUBLE: - _info->bps = 64; + fprintf (stderr, "[sndfile] 64 bit float input format is not supported (yet)\n"); + return -1; +// _info->fmt.bps = 64; break; default: - _info->bps = 16; + fprintf (stderr, "[sndfile] unidentified input format: 0x%X\n", inf.format&0x000f); + return -1; + } + + _info->fmt.channels = inf.channels; + _info->fmt.samplerate = inf.samplerate; + + int channel_map [inf.channels]; + int cmdres = sf_command (info->ctx, SFC_GET_CHANNEL_MAP_INFO, channel_map, sizeof (channel_map)) ; + if (cmdres != SF_FALSE) { + // channel map found, convert to channel mask + _info->fmt.channelmask = wavex_gen_channel_mask (channel_map, inf.channels); + } + else { + // channel map not found, generate from channel number + for (int i = 0; i < inf.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } } - _info->channels = inf.channels; - _info->samplerate = inf.samplerate; _info->readpos = 0; if (it->endsample > 0) { info->startsample = it->startsample; @@ -148,7 +226,9 @@ sndfile_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->endsample = inf.frames-1; } // hack bitrate - float sec = (float)(info->endsample-info->startsample) / inf.samplerate; + + int totalsamples = inf.frames; + float sec = (float)totalsamples / inf.samplerate; if (sec > 0) { info->bitrate = fsize / sec * 8 / 1000; } @@ -172,11 +252,11 @@ sndfile_free (DB_fileinfo_t *_info) { } static int -sndfile_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +sndfile_read (DB_fileinfo_t *_info, char *bytes, int size) { sndfile_info_t *info = (sndfile_info_t*)_info; - int out_ch = min (_info->channels, 2); - if (size / (2 * out_ch) + info->currentsample > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * out_ch; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + if (size / samplesize + info->currentsample > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; trace ("sndfile: size truncated to %d bytes, cursample=%d, endsample=%d\n", size, info->currentsample, info->endsample); if (size <= 0) { return 0; @@ -184,61 +264,12 @@ sndfile_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } int n = 0; - if (out_ch != _info->channels) { - // read into temp buffer, and downmix - int nframes = size / out_ch / 2; - int16_t buf[nframes * _info->channels]; - n = sf_readf_short (info->ctx, buf, nframes); - for (int i = 0; i < n; i++) { - for (int j = 0; j < out_ch; j++) { - ((int16_t *)bytes)[i * out_ch + j] = buf[i * _info->channels + j]; - } - } - } - else { - n = sf_readf_short (info->ctx, (short *)bytes, size/(2*_info->channels)); - } + n = sf_read_raw (info->ctx, (short *)bytes, size); + n /= samplesize; info->currentsample += n; - - size = n * 2 * out_ch; - _info->readpos = (float)(info->currentsample-info->startsample)/_info->samplerate; - if (info->bitrate > 0) { - deadbeef->streamer_set_bitrate (info->bitrate); - } - return size; -} - -static int -sndfile_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { - sndfile_info_t *info = (sndfile_info_t*)_info; - int out_ch = min (_info->channels, 2); - if (size / (4 * out_ch) + info->currentsample > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 4 * out_ch; - trace ("sndfile: size truncated to %d bytes, cursample=%d, endsample=%d\n", size, info->currentsample, info->endsample); - if (size <= 0) { - return 0; - } - } - int n = 0; - if (out_ch != _info->channels) { - // read into temp buffer, and downmix - int nframes = size / out_ch / 4; - float buf[nframes * _info->channels]; - n = sf_readf_float (info->ctx, buf, nframes); - for (int i = 0; i < n; i++) { - for (int j = 0; j < out_ch; j++) { - ((float *)bytes)[i * out_ch + j] = buf[i * _info->channels + j]; - } - } - } - else { - n = sf_readf_float (info->ctx, (float *)bytes, size/(4*_info->channels)); - } - - info->currentsample += n; - size = n * 4 * out_ch; - _info->readpos = (float)(info->currentsample-info->startsample)/_info->samplerate; + size = n * samplesize; + _info->readpos = (float)(info->currentsample-info->startsample)/_info->fmt.samplerate; if (info->bitrate > 0) { deadbeef->streamer_set_bitrate (info->bitrate); } @@ -253,13 +284,13 @@ sndfile_seek_sample (DB_fileinfo_t *_info, int sample) { return -1; } info->currentsample = ret; - _info->readpos = (float)(info->currentsample - info->startsample) / _info->samplerate; + _info->readpos = (float)(info->currentsample - info->startsample) / _info->fmt.samplerate; return 0; } static int sndfile_seek (DB_fileinfo_t *_info, float sec) { - return sndfile_seek_sample (_info, sec * _info->samplerate); + return sndfile_seek_sample (_info, sec * _info->fmt.samplerate); } static DB_playItem_t * @@ -272,6 +303,7 @@ sndfile_insert (DB_playItem_t *after, const char *fname) { trace ("sndfile: failed to open %s\n", fname); return NULL; } + int64_t fsize = deadbeef->fgetlength (info.file); info.ctx = sf_open_virtual (&vfs, SFM_READ, &inf, &info); if (!info.ctx) { trace ("sndfile: sf_open failed"); @@ -284,14 +316,45 @@ sndfile_insert (DB_playItem_t *after, const char *fname) { deadbeef->fclose (info.file); float duration = (float)totalsamples / samplerate; - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "wav"; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "wav"); deadbeef->pl_set_item_duration (it, duration); trace ("sndfile: totalsamples=%d, samplerate=%d, duration=%f\n", totalsamples, samplerate, duration); + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + + int bps = -1; + switch (inf.format&0x000f) { + case SF_FORMAT_PCM_S8: + case SF_FORMAT_PCM_U8: + bps = 8; + break; + case SF_FORMAT_PCM_16: + bps = 16; + break; + case SF_FORMAT_PCM_24: + bps = 24; + break; + case SF_FORMAT_FLOAT: + case SF_FORMAT_PCM_32: + bps = 32; + break; + } + + snprintf (s, sizeof (s), "%d", bps); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", inf.channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / duration * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); + + DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, totalsamples, samplerate); if (cue_after) { deadbeef->pl_item_unref (it); @@ -306,31 +369,116 @@ sndfile_insert (DB_playItem_t *after, const char *fname) { return after; } -static const char * exts[] = { "wav", "aif", "aiff", "snd", "au", "paf", "svx", "nist", "voc", "ircam", "w64", "mat4", "mat5", "pvf", "xi", "htk", "sds", "avr", "wavex", "sd2", "caf", "wve", NULL }; +#define DEFAULT_EXTS "wav;aif;aiff;snd;au;paf;svx;nist;voc;ircam;w64;mat4;mat5;pvf;xi;htk;sds;avr;wavex;sd2;caf;wve" + +#define EXT_MAX 100 + +static char *exts[EXT_MAX] = {NULL}; static const char *filetypes[] = { "WAV", NULL }; + +static void +sndfile_init_exts (void) { + for (int i = 0; exts[i]; i++) { + free (exts[i]); + } + exts[0] = NULL; + + int n = 0; + deadbeef->conf_lock (); + const char *new_exts = deadbeef->conf_get_str_fast ("sndfile.extensions", DEFAULT_EXTS); + while (*new_exts) { + if (n >= EXT_MAX) { + fprintf (stderr, "sndfile: too many extensions, max is %d\n", EXT_MAX); + break; + } + const char *e = new_exts; + while (*e && *e != ';') { + e++; + } + if (e != new_exts) { + char *ext = malloc (e-new_exts+1); + memcpy (ext, new_exts, e-new_exts); + ext[e-new_exts] = 0; + exts[n++] = ext; + } + if (*e == 0) { + break; + } + new_exts = e+1; + } + deadbeef->conf_unlock (); + exts[n] = NULL; +} + + +static int +sndfile_on_configchanged (DB_event_t *ev, uintptr_t data) { + sndfile_init_exts (); + return 0; +} + +static int +sndfile_start (void) { + sndfile_init_exts (); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (sndfile_on_configchanged), 0); + return 0; +} + +static int +sndfile_stop (void) { + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (sndfile_on_configchanged), 0); + for (int i = 0; exts[i]; i++) { + free (exts[i]); + } + exts[0] = NULL; + return 0; +} + +static const char settings_dlg[] = + "property \"File Extensions (separate with ';')\" entry sndfile.extensions \"" DEFAULT_EXTS "\";\n" +; + + // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "sndfile", - .plugin.name = "pcm player", + .plugin.name = "WAV/PCM player", .plugin.descr = "wav/aiff player using libsndfile", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = sndfile_open, .init = sndfile_init, .free = sndfile_free, - .read_int16 = sndfile_read_int16, - .read_float32 = sndfile_read_float32, + .read = sndfile_read, .seek = sndfile_seek, .seek_sample = sndfile_seek_sample, .insert = sndfile_insert, - .exts = exts, - .filetypes = filetypes + .exts = (const char **)exts, + .filetypes = filetypes, + .plugin.start = sndfile_start, + .plugin.stop = sndfile_stop, + .plugin.configdialog = settings_dlg, }; DB_plugin_t * diff --git a/plugins/soundtouch/Makefile b/plugins/soundtouch/Makefile new file mode 100644 index 00000000..ce4a0ad0 --- /dev/null +++ b/plugins/soundtouch/Makefile @@ -0,0 +1,52 @@ +CC=gcc +CXX=g++ + +OUT=ddb_soundtouch.so + +soundtouch_path=soundtouch + +CFLAGS+=-Wall -g -D_GNU_SOURCE -I$(soundtouch_path)/include -I../../include -std=c99 -fPIC -msse2 +CXXFLAGS+=-Wall -g -D_GNU_SOURCE -I$(soundtouch_path)/include -I../../include -fPIC -msse2 + +LDFLAGS+=-shared -lm + +SOURCES=plugin.c + +CXX_SOURCES=st.cpp\ +$(soundtouch_path)/source/SoundTouch/AAFilter.cpp\ +$(soundtouch_path)/source/SoundTouch/BPMDetect.cpp\ +$(soundtouch_path)/source/SoundTouch/cpu_detect_x86_gcc.cpp\ +$(soundtouch_path)/source/SoundTouch/FIFOSampleBuffer.cpp\ +$(soundtouch_path)/source/SoundTouch/FIRFilter.cpp\ +$(soundtouch_path)/source/SoundTouch/mmx_optimized.cpp\ +$(soundtouch_path)/source/SoundTouch/PeakFinder.cpp\ +$(soundtouch_path)/source/SoundTouch/RateTransposer.cpp\ +$(soundtouch_path)/source/SoundTouch/SoundTouch.cpp\ +$(soundtouch_path)/source/SoundTouch/sse_optimized.cpp\ +$(soundtouch_path)/source/SoundTouch/TDStretch.cpp + +HEADERS=st.h\ +$(soundtouch_path)/include/BPMDetect.h\ +$(soundtouch_path)/include/FIFOSampleBuffer.h\ +$(soundtouch_path)/include/FIFOSamplePipe.h\ +$(soundtouch_path)/include/soundtouch_config.h\ +$(soundtouch_path)/include/SoundTouch.h\ +$(soundtouch_path)/include/STTypes.h + +CXX_OBJECTS=$(CXX_SOURCES:.cpp=.o) +OBJECTS=$(SOURCES:.c=.o) + +all: $(SOURCES) $(OUT) + +$(OUT): $(OBJECTS) $(CXX_OBJECTS) + $(CXX) $(LDFLAGS) $(OBJECTS) $(CXX_OBJECTS) -o $@ + +.c.o: $(HEADERS) + $(CC) $(CFLAGS) $< -c -o $@ + +.cpp.o: $(HEADERS) + $(CXX) $(CXXFLAGS) $< -c -o $@ + +clean: + rm $(OBJECTS) $(CXX_OBJECTS) $(OUT) + diff --git a/plugins/soundtouch/plugin.c b/plugins/soundtouch/plugin.c new file mode 100644 index 00000000..42b633ac --- /dev/null +++ b/plugins/soundtouch/plugin.c @@ -0,0 +1,308 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "../../deadbeef.h" +#include "st.h" + +enum { + ST_PARAM_TEMPO, + ST_PARAM_PITCH, + ST_PARAM_RATE, + ST_PARAM_USE_AA_FILTER, + ST_PARAM_AA_FILTER_LENGTH, + ST_PARAM_USE_QUICKSEEK, + ST_PARAM_SEQUENCE_MS, + ST_PARAM_SEEKWINDOW_MS, + ST_PARAM_SET_OUTPUT_SAMPLERATE, + ST_PARAM_COUNT +}; + +static DB_functions_t *deadbeef; +static DB_dsp_t plugin; + +typedef struct { + ddb_dsp_context_t ctx; + void *st; + float tempo; + float pitch; + float rate; + int use_aa_filter; + int aa_filter_length; + int use_quickseek; + int sequence_ms; + int seekwindow_ms; + int set_output_samplerate; + int changed; +} ddb_soundtouch_t; + +ddb_dsp_context_t* +st_open (void) { + ddb_soundtouch_t *st = malloc (sizeof (ddb_soundtouch_t)); + DDB_INIT_DSP_CONTEXT (st,ddb_soundtouch_t,&plugin); + st->st = st_alloc (); + st->changed = 1; + st->tempo = 0; + st->rate = 0; + st->pitch = 0; + st->use_aa_filter = 0; + st->aa_filter_length = 32; + st->use_quickseek = 0; + st->sequence_ms = 82; + st->seekwindow_ms = 28; + return (ddb_dsp_context_t *)st; +} + +void +st_close (ddb_dsp_context_t *_src) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; + if (st->st) { + st_free (st->st); + } + free (st); +} + +void +st_reset (ddb_dsp_context_t *_src) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; + st_clear (st->st); +} + +int +st_process (ddb_dsp_context_t *_src, float *samples, int nframes, int maxframes, ddb_waveformat_t *fmt, float *ratio) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; + if (st->changed) { + if (st->set_output_samplerate > 0) { + st_set_rate_change (st->st, 0); + st_set_rate (st->st, fmt->samplerate / (float)st->set_output_samplerate); + } + else { + st_set_rate (st->st, 1); + st_set_rate_change (st->st, st->rate); + } + st_set_pitch_semi_tones (st->st, st->pitch); + st_set_tempo_change (st->st, st->tempo); + st_set_setting (st->st, SETTING_USE_AA_FILTER, st->use_aa_filter); + st_set_setting (st->st, SETTING_AA_FILTER_LENGTH, st->aa_filter_length); + st_set_setting (st->st, SETTING_USE_QUICKSEEK, st->use_quickseek); + st_set_setting (st->st, SETTING_SEQUENCE_MS, st->sequence_ms); + st_set_setting (st->st, SETTING_SEEKWINDOW_MS, st->seekwindow_ms); + st->changed = 0; + } + *ratio = (1.f + 0.01f * st->tempo); + + if (st->set_output_samplerate > 0) { + fmt->samplerate = st->set_output_samplerate; + } + else { + *ratio *= (1.f + 0.01f * st->rate); + } + + st_set_sample_rate (st->st, fmt->samplerate); + st_set_channels (st->st, fmt->channels); + + st_put_samples (st->st, samples, nframes); + int nout = 0; + int n = 0; + do { + n = st_receive_samples (st->st, samples, maxframes); + maxframes -= n; + samples += n * fmt->channels; + nout += n; + } while (n != 0); + + return nout; +} + +const char * +st_get_param_name (int p) { + switch (p) { + case ST_PARAM_TEMPO: + return "Tempo"; + case ST_PARAM_PITCH: + return "Pitch"; + case ST_PARAM_RATE: + return "Playback Rate"; + case ST_PARAM_USE_AA_FILTER: + return "Use AA Filter"; + case ST_PARAM_AA_FILTER_LENGTH: + return "AA Filter Length"; + case ST_PARAM_USE_QUICKSEEK: + return "Use Quickseek"; + case ST_PARAM_SEQUENCE_MS: + return "Time Stretch Sequence Length (ms)"; + case ST_PARAM_SEEKWINDOW_MS: + return "Time Stretch Seek Window Length (ms)"; + case ST_PARAM_SET_OUTPUT_SAMPLERATE: + return "Set Output Samplerate"; + default: + fprintf (stderr, "st_param_name: invalid param index (%d)\n", p); + } + return NULL; +} + +int +st_num_params (void) { + return ST_PARAM_COUNT; +} + +void +st_set_param (ddb_dsp_context_t *ctx, int p, const char *val) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)ctx; + switch (p) { + case ST_PARAM_TEMPO: + st->tempo = atof (val); + st->changed = 1; + break; + case ST_PARAM_PITCH: + st->pitch = atof (val); + st->changed = 1; + break; + case ST_PARAM_RATE: + st->rate = atof (val); + st->changed = 1; + break; + case ST_PARAM_USE_AA_FILTER: + st->use_aa_filter = atoi (val); + st->changed = 1; + break; + case ST_PARAM_AA_FILTER_LENGTH: + st->aa_filter_length = atoi (val); + st->changed = 1; + break; + case ST_PARAM_USE_QUICKSEEK: + st->use_quickseek = atoi (val); + st->changed = 1; + break; + case ST_PARAM_SEQUENCE_MS: + st->sequence_ms = atoi (val); + st->changed = 1; + break; + case ST_PARAM_SEEKWINDOW_MS: + st->seekwindow_ms = atoi (val); + st->changed = 1; + break; + case ST_PARAM_SET_OUTPUT_SAMPLERATE: + st->set_output_samplerate = atoi (val); + if (st->set_output_samplerate < 8000) { + st->set_output_samplerate = 0; + } + else if (st->set_output_samplerate > 192000) { + st->set_output_samplerate = 192000; + } + break; + default: + fprintf (stderr, "st_param: invalid param index (%d)\n", p); + } +} + +void +st_get_param (ddb_dsp_context_t *ctx, int p, char *val, int sz) { + ddb_soundtouch_t *st = (ddb_soundtouch_t *)ctx; + switch (p) { + case ST_PARAM_TEMPO: + snprintf (val, sz, "%f", st->tempo); + break; + case ST_PARAM_PITCH: + snprintf (val, sz, "%f", st->pitch); + break; + case ST_PARAM_RATE: + snprintf (val, sz, "%f", st->rate); + break; + case ST_PARAM_USE_AA_FILTER: + snprintf (val, sz, "%d", st->use_aa_filter); + break; + case ST_PARAM_AA_FILTER_LENGTH: + snprintf (val, sz, "%d", st->aa_filter_length); + break; + case ST_PARAM_USE_QUICKSEEK: + snprintf (val, sz, "%d", st->use_quickseek); + break; + case ST_PARAM_SEQUENCE_MS: + snprintf (val, sz, "%d", st->sequence_ms); + break; + case ST_PARAM_SEEKWINDOW_MS: + snprintf (val, sz, "%d", st->seekwindow_ms); + break; + case ST_PARAM_SET_OUTPUT_SAMPLERATE: + snprintf (val, sz, "%d", st->set_output_samplerate); + break; + default: + fprintf (stderr, "st_get_param: invalid param index (%d)\n", p); + } +} + +static const char settings_dlg[] = + "property \"Tempo Change (%)\" spinbtn[-200,200,1] 0 0;\n" + "property \"Pitch Change (semi-tones)\" spinbtn[-24,24,1] 1 0;\n" + "property \"Playback Rate Change (%)\" spinbtn[-200,200,1] 2 0;\n" + "property \"Absolute Samplerate (overrides Rate Change if nonzero)\" spinbtn[0,192000,1] 8 0;\n" + "property \"Use AA Filter\" checkbox 3 0;\n" + "property \"AA Filter Length\" spinbtn[16,64,1] 4 32;\n" + "property \"Use Quickseek\" checkbox 5 0;\n" + "property \"Time Stretch Sequence Length (ms)\" spinbtn[10,500,1] 6 82;\n" + "property \"Time Stretch Seek Window Length (ms)\" spinbtn[10,500,1] 7 28;\n" +; + +static DB_dsp_t plugin = { + .plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.api_vminor = DB_API_VERSION_MINOR, + .open = st_open, + .close = st_close, + .process = st_process, + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DSP, + .plugin.id = "soundtouch", + .plugin.name = "Soundtouch", + .plugin.descr = "Tempo/Pitch/Rate changer using SoundTouch Library (http://www.surina.net/soundtouch)", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "uses SoundTouch Library, (C) Olli Parviainen" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.website = "http://deadbeef.sf.net", + .num_params = st_num_params, + .get_param_name = st_get_param_name, + .set_param = st_set_param, + .get_param = st_get_param, + .reset = st_reset, + .configdialog = settings_dlg, +}; + +DB_plugin_t * +ddb_soundtouch_load (DB_functions_t *f) { + deadbeef = f; + return &plugin.plugin; +} diff --git a/plugins/soundtouch/soundtouch/COPYING.TXT b/plugins/soundtouch/soundtouch/COPYING.TXT new file mode 100644 index 00000000..5b2161be --- /dev/null +++ b/plugins/soundtouch/soundtouch/COPYING.TXT @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authoried party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/plugins/soundtouch/soundtouch/README.html b/plugins/soundtouch/soundtouch/README.html new file mode 100644 index 00000000..15a34c86 --- /dev/null +++ b/plugins/soundtouch/soundtouch/README.html @@ -0,0 +1,752 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=windows-1252">
+ <meta http-equiv="Content-Language" content="en-us">
+ <meta name="author" content="Olli Parviainen">
+ <meta name="description"
+ content="Readme file for SoundTouch audio processing library">
+ <meta name="GENERATOR" content="Microsoft FrontPage 4.0">
+ <meta name="ProgId" content="FrontPage.Editor.Document">
+ <title>SoundTouch library README</title>
+ <style>
+<!--
+.normal { font-family: Arial }
+-->
+ </style>
+</head>
+<body class="normal">
+<hr>
+<h1>SoundTouch audio processing library v1.5.0
+</h1>
+<p class="normal">SoundTouch library Copyright (c) Olli
+Parviainen 2002-2009 </p>
+<hr>
+<h2>1. Introduction </h2>
+<p>SoundTouch is an open-source audio
+processing library that allows changing the sound tempo, pitch
+and playback rate parameters independently from each other, i.e.:</p>
+<ul>
+ <li>Sound tempo can be increased or decreased while
+maintaining the original pitch</li>
+ <li>Sound pitch can be increased or decreased while
+maintaining the original tempo </li>
+ <li>Change playback rate that affects both tempo
+and pitch at the same time </li>
+ <li>Choose any combination of tempo/pitch/rate</li>
+</ul>
+<h3>1.1 Contact information </h3>
+<p>Author email: oparviai 'at' iki.fi </p>
+<p>SoundTouch WWW page: <a href="http://www.surina.net/soundtouch">http://www.surina.net/soundtouch</a></p>
+<hr>
+<h2>2. Compiling SoundTouch</h2>
+<p>Before compiling, notice that you can choose the sample data format
+if it's desirable to use floating point sample
+data instead of 16bit integers. See section "sample data format"
+for more information.</p>
+<h3>2.1. Building in Microsoft Windows</h3>
+<p>Project files for Microsoft Visual C++ 6.0 and Visual C++ .NET are
+supplied with the source code package. </p>
+<p> Please notice that SoundTouch
+library uses processor-specific optimizations for Pentium III and AMD
+processors. Visual Studio .NET and later versions supports the required
+instructions by default, but Visual Studio 6.0 requires a processor pack upgrade
+to be installed in order to support these optimizations. The processor pack upgrade can be downloaded from
+Microsoft site at this URL:</p>
+<p><a href="http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx">http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx</a></p>
+<p>If the above URL is unavailable or removed, go
+to <a href="http://msdn.microsoft.com/">http://msdn.microsoft.com</a>
+and perform a search with keywords "processor pack". </p>
+<p>To build the binaries with Visual C++
+compiler, either run "make-win.bat" script, or open the
+appropriate project files in source code directories with Visual
+Studio. The final executable will appear under the "SoundTouch\bin"
+directory. If using the Visual Studio IDE instead of the make-win.bat script, directories bin and
+lib may need to be created manually to the SoundTouch
+package root for the final executables. The make-win.bat script
+creates these directories automatically.
+</p>
+<h3>2.2. Building in Gnu platforms</h3>
+<p>The SoundTouch library can be compiled in
+practically any platform supporting GNU compiler (GCC) tools.
+SoundTouch have been tested with gcc version 3.3.4., but it
+shouldn't be very specific about the gcc version. Assembler-level
+performance optimizations for GNU platform are currently available in
+x86 platforms only, they are automatically disabled and replaced with
+standard C routines in other processor platforms.</p>
+<p>To build and install the binaries, run the
+following commands in the SoundTouch/ directory:</p>
+<table border="0" cellpadding="0" cellspacing="4">
+ <tbody>
+ <tr valign="top">
+ <td>
+ <pre>./configure -</pre>
+ </td>
+ <td>
+ <p>Configures the SoundTouch package for the local
+environment.</p>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <pre>make -</pre>
+ </td>
+ <td>
+ <p>Builds the SoundTouch library &
+SoundStretch utility.</p>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <pre>make install -</pre>
+ </td>
+ <td>
+ <p>Installs the SoundTouch & BPM libraries
+to <b>/usr/local/lib</b> and SoundStretch utility to <b>/usr/local/bin</b>.
+Please notice that 'root' privileges may be required to install the
+binaries to the destination locations.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h4><b>2.2.1 Required GNU tools</b> </h4>
+<p> Bash shell, GNU C++ compiler, libtool, autoconf and automake tools are required
+for compiling
+the SoundTouch library. These are usually included with the GNU/Linux distribution, but if
+not, install these packages first. For example, in Ubuntu Linux these can be acquired and
+installed with the following command:</p>
+<pre><b>sudo apt-get install <font SIZE="2">automake autoconf libtool build-essential</font></b></pre>
+<h4><b>2.2.2 Problems with GCC compiler compatibility</b></h4>
+<p>At the release time the SoundTouch package has been tested to compile in
+GNU/Linux platform. However, in past it's happened that new gcc versions aren't
+necessarily compatible with the assembler settings used in the optimized
+routines. <b>If you have problems getting the
+SoundTouch library compiled, try the workaround of disabling the optimizations</b>
+by editing the file "include/STTypes.h" and removing the following
+definition there:</p>
+<blockquote>
+ <pre>#define ALLOW_OPTIMIZATIONS 1</pre>
+</blockquote>
+<h4><b>2.2.3 Problems with configure script or build process</b> </h4>
+<p>Incompatibilities between various GNU toolchain versions may cause errors when running the "configure" script or building the source
+codes, if your GNU tool versions are not compatible with the versions used for
+preparing the SoundTouch kit. </p>
+<p>To resolve the issue, regenerate the configure scripts with your local tool
+set by running
+the "<b>./bootstrap</b>" script included in the SoundTouch source code
+kit. After that, run the <b>configure</b> script and <b>make</b> as usually.</p>
+<h4><b>2.2.4 Compiler issues with non-x86 processors</b></h4>
+<p>SoundTouch library works also on non-x86 processors.</p>
+<p>However, in case that you get compiler errors when trying to compile for non-Intel processor, edit the file
+"<b>source\SoundTouch\Makefile.am</b>" and remove the "<b>-msse2</b>"
+flag on the <b>AM_CXXFLAGS </b>line:</p>
+<pre><b>AM_CXXFLAGS=-O3 -fcheck-new -I../../include # Note: -msse2 flag removed!</b></pre>
+<p>After that, run "<b>./bootstrap</b>" script, and then run <b>configure</b>
+and <b>make</b> again.</p>
+<hr>
+<h2>3. About implementation & Usage tips</h2>
+<h3>3.1. Supported sample data formats</h3>
+<p>The sample data format can be chosen
+between 16bit signed integer and 32bit floating point values, the
+default is 32bit floating point. </p>
+
+<p>
+In Windows environment, the sample data format is chosen
+in file "STTypes.h" by choosing one of the following
+defines:</p>
+<ul>
+ <li><span style="font-weight: bold;">#define INTEGER_SAMPLES</span>
+for 16bit signed
+integer</li>
+ <li><span style="font-weight: bold;">#define FLOAT_SAMPLES</span> for
+32bit floating point</li>
+</ul>
+<p>
+In GNU environment, the floating sample format is used by default, but
+integer sample format can be chosen by giving the
+following switch to the configure script:
+<blockquote>
+<pre>./configure --enable-integer-samples</pre>
+</blockquote>
+
+<p>The sample data can have either single (mono)
+or double (stereo) audio channel. Stereo data is interleaved so
+that every other data value is for left channel and every second
+for right channel. Notice that while it'd be possible in theory
+to process stereo sound as two separate mono channels, this isn't
+recommended because processing the channels separately would
+result in losing the phase coherency between the channels, which
+consequently would ruin the stereo effect.</p>
+<p>Sample rates between 8000-48000H are
+supported.</p>
+<h3>3.2. Processing latency</h3>
+<p>The processing and latency constraints of
+the SoundTouch library are:</p>
+<ul>
+ <li>Input/output processing latency for the
+SoundTouch processor is around 100 ms. This is when time-stretching is
+used. If the rate transposing effect alone is used, the latency
+requirement
+is much shorter, see section 'About algorithms'.</li>
+ <li>Processing CD-quality sound (16bit stereo
+sound with 44100H sample rate) in real-time or faster is possible
+starting from processors equivalent to Intel Pentium 133Mh or better,
+if using the "quick" processing algorithm. If not using the "quick"
+mode or
+if floating point sample data are being used, several times more CPU
+power is typically required.</li>
+</ul>
+<h3>3.3. About algorithms</h3>
+<p>SoundTouch provides three seemingly
+independent effects: tempo, pitch and playback rate control.
+These three controls are implemented as combination of two primary
+effects, <em>sample rate transposing</em> and <em>time-stretching</em>.</p>
+<p><em>Sample rate transposing</em> affects
+both the audio stream duration and pitch. It's implemented simply
+by converting the original audio sample stream to the desired
+duration by interpolating from the original audio samples. In SoundTouch, linear interpolation with anti-alias filtering is
+used. Theoretically a higher-order interpolation provide better
+result than 1st order linear interpolation, but in audio
+application linear interpolation together with anti-alias
+filtering performs subjectively about as well as higher-order
+filtering would.</p>
+<p><em>Time-stretching </em>means changing
+the audio stream duration without affecting it's pitch. SoundTouch
+uses WSOLA-like time-stretching routines that operate in the time
+domain. Compared to sample rate transposing, time-stretching is a
+much heavier operation and also requires a longer processing
+"window" of sound samples used by the
+processing algorithm, thus increasing the algorithm input/output
+latency. Typical i/o latency for the SoundTouch
+time-stretch algorithm is around 100 ms.</p>
+<p>Sample rate transposing and time-stretching
+are then used together to produce the tempo, pitch and rate
+controls:</p>
+<ul>
+ <li><strong>'Tempo'</strong> control is
+implemented purely by time-stretching.</li>
+ <li><strong>'Rate</strong>' control is implemented
+purely by sample rate transposing.</li>
+ <li><strong>'Pitch</strong>' control is
+implemented as a combination of time-stretching and sample rate
+transposing. For example, to increase pitch the audio stream is first
+time-stretched to longer duration (without affecting pitch) and then
+transposed back to original duration by sample rate transposing, which
+simultaneously reduces duration and increases pitch. The result is
+original duration but increased pitch.</li>
+</ul>
+<h3>3.4 Tuning the algorithm parameters</h3>
+<p>The time-stretch algorithm has few
+parameters that can be tuned to optimize sound quality for
+certain application. The current default parameters have been
+chosen by iterative if-then analysis (read: "trial and error")
+to obtain best subjective sound quality in pop/rock music
+processing, but in applications processing different kind of
+sound the default parameter set may result into a sub-optimal
+result.</p>
+<p>The time-stretch algorithm default
+parameter values are set by the following #defines in file "TDStretch.h":</p>
+<blockquote>
+ <pre>#define DEFAULT_SEQUENCE_MS AUTOMATIC
+#define DEFAULT_SEEKWINDOW_MS AUTOMATIC
+#define DEFAULT_OVERLAP_MS 8</pre>
+</blockquote>
+<p>These parameters affect to the time-stretch
+algorithm as follows:</p>
+<ul>
+ <li><strong>DEFAULT_SEQUENCE_MS</strong>: This is
+the default length of a single processing sequence in milliseconds
+which determines the how the original sound is chopped in
+the time-stretch algorithm. Larger values mean fewer sequences
+are used in processing. In principle a larger value sounds better when
+slowing down the tempo, but worse when increasing the tempo and vice
+versa. <br>
+ <br>
+ By default, this setting value is calculated automatically according to
+ tempo value.<br>
+ </li>
+ <li><strong>DEFAULT_SEEKWINDOW_MS</strong>: The seeking window
+default length in milliseconds is for the algorithm that seeks the best
+possible overlapping location. This determines from how
+wide a sample "window" the algorithm can use to find an optimal mixing
+location when the sound sequences are to be linked back together. <br>
+ <br>
+The bigger this window setting is, the higher the possibility to find a
+better mixing position becomes, but at the same time large values may
+cause a "drifting" sound artifact because neighboring sequences can be
+chosen at more uneven intervals. If there's a disturbing artifact that
+sounds as if a constant frequency was drifting around, try reducing
+this setting.<br>
+ <br>
+ By default, this setting value is calculated automatically according to
+ tempo value.<br>
+ </li>
+ <li><strong>DEFAULT_OVERLAP_MS</strong>: Overlap
+length in milliseconds. When the sound sequences are mixed back
+together to form again a continuous sound stream, this parameter
+defines how much the ends of the consecutive sequences will overlap with each other.<br>
+ <br>
+ This shouldn't be that critical parameter. If you reduce the
+DEFAULT_SEQUENCE_MS setting by a large amount, you might wish to try a
+smaller value on this.</li>
+</ul>
+<p>Notice that these parameters can also be
+set during execution time with functions "<strong>TDStretch::setParameters()</strong>"
+and "<strong>SoundTouch::setSetting()</strong>".</p>
+<p>The table below summaries how the
+parameters can be adjusted for different applications:</p>
+<table border="1">
+ <tbody>
+ <tr>
+ <td valign="top"><strong>Parameter name</strong></td>
+ <td valign="top"><strong>Default value
+magnitude</strong></td>
+ <td valign="top"><strong>Larger value
+affects...</strong></td>
+ <td valign="top"><strong>Smaller value
+affects...</strong></td>
+ <td valign="top"><strong>Effect to CPU burden</strong></td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>SEQUENCE_MS</pre>
+ </td>
+ <td valign="top">Default value is relatively
+large, chosen for slowing down music tempo</td>
+ <td valign="top">Larger value is usually
+better for slowing down tempo. Growing the value decelerates the
+"echoing" artifact when slowing down the tempo.</td>
+ <td valign="top">Smaller value might be better
+for speeding up tempo. Reducing the value accelerates the "echoing"
+artifact when slowing down the tempo </td>
+ <td valign="top">Increasing the parameter
+value reduces computation burden</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>SEEKWINDOW_MS</pre>
+ </td>
+ <td valign="top">Default value is relatively
+large, chosen for slowing down music tempo</td>
+ <td valign="top">Larger value eases finding a
+good mixing position, but may cause a "drifting" artifact</td>
+ <td valign="top">Smaller reduce possibility to
+find a good mixing position, but reduce the "drifting" artifact.</td>
+ <td valign="top">Increasing the parameter
+value increases computation burden</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>OVERLAP_MS</pre>
+ </td>
+ <td valign="top">Default value is relatively
+large, chosen to suit with above parameters.</td>
+ <td valign="top"> </td>
+ <td valign="top">If you reduce the "sequence
+ms" setting, you might wish to try a smaller value.</td>
+ <td valign="top">Increasing the parameter
+value increases computation burden</td>
+ </tr>
+ </tbody>
+</table>
+<h3>3.5 Performance Optimizations </h3>
+<p><strong>General optimizations:</strong></p>
+<p>The time-stretch routine has a 'quick' mode
+that substantially speeds up the algorithm but may degrade the
+sound quality by a small amount. This mode is activated by
+calling SoundTouch::setSetting() function with parameter id
+of SETTING_USE_QUICKSEEK and value "1", i.e. </p>
+<blockquote>
+ <p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
+</blockquote>
+<p><strong>CPU-specific optimizations:</strong></p>
+<ul>
+ <li>Intel MMX optimized routines are used with
+compatible CPUs when 16bit integer sample type is used. MMX optimizations are available both in Win32 and Gnu/x86 platforms.
+Compatible processors are Intel PentiumMMX and later; AMD K6-2, Athlon
+and later. </li>
+ <li>Intel SSE optimized routines are used with
+compatible CPUs when floating point sample type is used. SSE optimizations are currently implemented for Win32 platform only.
+Processors compatible with SSE extension are Intel processors starting
+from Pentium-III, and AMD processors starting from Athlon XP. </li>
+ <li>AMD 3DNow! optimized routines are used with
+compatible CPUs when floating point sample type is used, but SSE
+extension isn't supported . 3DNow! optimizations are currently
+implemented for Win32 platform only. These optimizations are used in
+AMD K6-2 and Athlon (classic) CPU's; better performing SSE routines are
+used with AMD processor starting from Athlon XP. </li>
+</ul>
+<hr>
+<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
+</h2>
+<p>SoundStretch audio processing utility<br>
+Copyright (c) Olli Parviainen 2002-2009</p>
+<p>SoundStretch is a simple command-line
+application that can change tempo, pitch and playback rates of
+WAV sound files. This program is intended primarily to
+demonstrate how the "SoundTouch" library can be used to
+process sound in your own program, but it can as well be used for
+processing sound files.</p>
+<h3>4.1. SoundStretch Usage Instructions</h3>
+<p>SoundStretch Usage syntax:</p>
+<blockquote>
+ <pre>soundstretch infilename outfilename [switches]</pre>
+</blockquote>
+<p>Where: </p>
+<table border="0" cellpadding="2" width="100%">
+ <tbody>
+ <tr>
+ <td valign="top">
+ <pre>"infilename"</pre>
+ </td>
+ <td valign="top">Name of the input sound
+data file (in .WAV audio file format). Give "stdin" as filename to use
+ standard input pipe. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>"outfilename"</pre>
+ </td>
+ <td valign="top">Name of the output sound
+file where the resulting sound is saved (in .WAV audio file format).
+This parameter may be omitted if you don't want to save the
+output
+(e.g. when only calculating BPM rate with '-bpm' switch). Give "stdout"
+ as filename to use standard output pipe.</td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre> [switches]</pre>
+ </td>
+ <td valign="top">Are one or more control
+switches.</td>
+ </tr>
+ </tbody>
+</table>
+<p>Available control switches are:</p>
+<table border="0" cellpadding="2" width="100%">
+ <tbody>
+ <tr>
+ <td valign="top">
+ <pre>-tempo=n </pre>
+ </td>
+ <td valign="top">Change the sound tempo by n
+percents (n = -95.0 .. +5000.0 %) </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-pitch=n</pre>
+ </td>
+ <td valign="top">Change the sound pitch by n
+semitones (n = -60.0 .. + 60.0 semitones) </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-rate=n</pre>
+ </td>
+ <td valign="top">Change the sound playback rate by
+n percents (n = -95.0 .. +5000.0 %) </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-bpm=n</pre>
+ </td>
+ <td valign="top">Detect the Beats-Per-Minute (BPM) rate of the sound and adjust the tempo to meet 'n'
+ BPMs. When this switch is
+ applied, the "-tempo" switch is ignored. If "=n" is
+omitted, i.e. switch "-bpm" is used alone, then the BPM rate is
+ estimated and displayed, but tempo not adjusted according to the BPM
+value. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-quick</pre>
+ </td>
+ <td valign="top">Use quicker tempo change
+algorithm. Gains speed but loses sound quality. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-naa</pre>
+ </td>
+ <td valign="top">Don't use anti-alias
+filtering in sample rate transposing. Gains speed but loses sound
+quality. </td>
+ </tr>
+ <tr>
+ <td valign="top">
+ <pre>-license</pre>
+ </td>
+ <td valign="top">Displays the program license
+text (LGPL)</td>
+ </tr>
+ </tbody>
+</table>
+<p>Notes:</p>
+<ul>
+ <li>To use standard input/output pipes for processing, give "stdin"
+ and "stdout" as input/output filenames correspondingly. The
+ standard input/output pipes will still carry the audio data in .wav audio
+ file format.</li>
+ <li>The numerical switches allow both integer (e.g. "-tempo=123") and decimal (e.g.
+"-tempo=123.45") numbers.</li>
+ <li>The "-naa" and/or "-quick" switches can be
+used to reduce CPU usage while compromising some sound quality </li>
+ <li>The BPM detection algorithm works by detecting
+repeating bass or drum patterns at low frequencies of <250Hz. A
+ lower-than-expected BPM figure may be reported for music with uneven or
+ complex bass patterns. </li>
+</ul>
+<h3>4.2. SoundStretch usage examples </h3>
+<p><strong>Example 1</strong></p>
+<p>The following command increases tempo of
+the sound file "originalfile.wav" by 12.5% and stores result to file "destinationfile.wav":</p>
+<blockquote>
+ <pre>soundstretch originalfile.wav destinationfile.wav -tempo=12.5</pre>
+</blockquote>
+<p><strong>Example 2</strong></p>
+<p>The following command decreases the sound
+pitch (key) of the sound file "orig.wav" by two
+semitones and stores the result to file "dest.wav":</p>
+<blockquote>
+ <pre>soundstretch orig.wav dest.wav -pitch=-2</pre>
+</blockquote>
+<p><strong>Example 3</strong></p>
+<p>The following command processes the file "orig.wav" by decreasing the sound tempo by 25.3% and
+increasing the sound pitch (key) by 1.5 semitones. Resulting .wav audio data is
+directed to standard output pipe:</p>
+<blockquote>
+ <pre>soundstretch orig.wav stdout -tempo=-25.3 -pitch=1.5</pre>
+</blockquote>
+<p><strong>Example 4</strong></p>
+<p>The following command detects the BPM rate
+of the file "orig.wav" and adjusts the tempo to match
+100 beats per minute. Result is stored to file "dest.wav":</p>
+<blockquote>
+ <pre>soundstretch orig.wav dest.wav -bpm=100</pre>
+</blockquote>
+<p><strong>Example 5</strong></p>
+<p>The following command reads .wav sound data from standard input pipe and
+estimates the BPM rate:</p>
+<blockquote>
+ <pre>soundstretch stdin -bpm</pre>
+</blockquote>
+<hr>
+<h2>5. Change History</h2>
+<h3>5.1. SoundTouch library Change History </h3>
+
+<p><strong>1.5.0:</strong></p>
+<ul>
+<li>Added normalization to correlation calculation and improvement automatic seek/sequence parameter calculation to improve sound quality</li>
+
+<li>Bugfixes:
+ <ul>
+ <li>Fixed negative array indexing in quick seek algorithm</li>
+ <li>FIR autoalias filter running too far in processing buffer</li>
+ <li>Check against zero sample count in rate transposing</li>
+ <li>Fix for x86-64 support: Removed pop/push instructions from the cpu detection algorithm. </li>
+ <li>Check against empty buffers in FIFOSampleBuffer</li>
+ <li>Other minor fixes & code cleanup</li>
+ </ul>
+</li>
+
+<li>Fixes in compilation scripts for non-Intel platforms</li>
+<li>Added Dynamic-Link-Library (DLL) version of SoundTouch library build,
+ provided with Delphi/Pascal wrapper for calling the dll routines</li>
+<li>Added #define PREVENT_CLICK_AT_RATE_CROSSOVER that prevents a click artifact
+ when crossing the nominal pitch from either positive to negative side or vice
+ versa</li>
+
+</ul>
+
+<p><strong>1.4.1:</strong></p>
+<ul>
+<li>Fixed a buffer overflow bug in BPM detect algorithm routines if processing
+ more than 2048 samples at one call </li>
+
+</ul>
+
+<p><strong>1.4.0:</strong></p>
+<ul>
+<li>Improved sound quality by automatic calculation of time stretch algorithm
+ processing parameters according to tempo setting</li>
+<li>Moved BPM detection routines from SoundStretch application into SoundTouch
+ library</li>
+<li>Bugfixes: Usage of uninitialied variables, GNU build scripts, compiler errors
+ due to 'const' keyword mismatch.</li>
+<li>Source code cleanup</li>
+
+</ul>
+
+<p><strong>v1.3.1:
+</strong></p>
+<ul>
+<li>Changed static class declaration to GCC 4.x compiler compatible syntax.</li>
+<li>Enabled MMX/SSE-optimized routines also for GCC compilers. Earlier
+the MMX/SSE-optimized routines were written in compiler-specific inline
+assembler, now these routines are migrated to use compiler intrinsic
+syntax which allows compiling the same MMX/SSE-optimized source code with
+both Visual C++ and GCC compilers. </li>
+<li>Set floating point as the default sample format and added switch to
+the GNU configure script for selecting the other sample format.</li>
+
+</ul>
+
+<p><strong>v1.3.0:
+</strong></p>
+<ul>
+ <li>Fixed tempo routine output duration inaccuracy due to rounding
+error </li>
+ <li>Implemented separate processing routines for integer and
+floating arithmetic to allow improvements to floating point routines
+(earlier used algorithms mostly optimized for integer arithmetic also
+for floating point samples) </li>
+ <li>Fixed a bug that distorts sound if sample rate changes during the
+sound stream </li>
+ <li>Fixed a memory leak that appeared in MMX/SSE/3DNow! optimized
+routines </li>
+ <li>Reduced redundant code pieces in MMX/SSE/3DNow! optimized
+routines vs. the standard C routines.</li>
+ <li>MMX routine incompatibility with new gcc compiler versions </li>
+ <li>Other miscellaneous bug fixes </li>
+</ul>
+<p><strong>v1.2.1: </strong></p>
+<ul>
+ <li>Added automake/autoconf scripts for GNU
+platforms (in courtesy of David Durham)</li>
+ <li>Fixed SCALE overflow bug in rate transposer
+routine.</li>
+ <li>Fixed 64bit address space bugs.</li>
+ <li>Created a 'soundtouch' namespace for
+SAMPLETYPE definitions.</li>
+</ul>
+<p><strong>v1.2.0: </strong></p>
+<ul>
+ <li>Added support for 32bit floating point sample
+data type with SSE/3DNow! optimizations for Win32 platform (SSE/3DNow! optimizations currently not supported in GCC environment)</li>
+ <li>Replaced 'make-gcc' script for GNU environment
+by master Makefile</li>
+ <li>Added time-stretch routine configurability to
+SoundTouch main class</li>
+ <li>Bugfixes</li>
+</ul>
+<p><strong>v1.1.1: </strong></p>
+<ul>
+ <li>Moved SoundTouch under lesser GPL license (LGPL). This allows using SoundTouch library in programs that aren't
+released under GPL license. </li>
+ <li>Changed MMX routine organiation so that MMX optimized routines are now implemented in classes that are derived from
+the basic classes having the standard non-mmx routines. </li>
+ <li>MMX routines to support gcc version 3. </li>
+ <li>Replaced windows makefiles by script using the .dsw files </li>
+</ul>
+<p><strong>v1.01: </strong></p>
+<ul>
+ <li>"mmx_gcc.cpp": Added "using namespace std" and
+removed "return 0" from a function with void return value to fix
+compiler errors when compiling the library in Solaris environment. </li>
+ <li>Moved file "FIFOSampleBuffer.h" to "include"
+directory to allow accessing the FIFOSampleBuffer class from external
+files. </li>
+</ul>
+<p><strong>v1.0: </strong></p>
+<ul>
+ <li>Initial release </li>
+</ul>
+<p> </p>
+<h3>5.2. SoundStretch application Change
+History </h3>
+
+<p><strong>1.4.0:</strong></p>
+<ul>
+<li>Moved BPM detection routines from SoundStretch application into SoundTouch
+ library</li>
+<li>Allow using standard input/output pipes as audio processing input/output
+ streams</li>
+
+</ul>
+
+<p><strong>v1.3.0:</strong></p>
+<ul>
+ <li>Simplified accessing WAV files with floating
+point sample format.
+ </li>
+</ul>
+<p><strong>v1.2.1: </strong></p>
+<ul>
+ <li>Fixed 64bit address space bugs.</li>
+</ul>
+<p><strong>v1.2.0: </strong></p>
+<ul>
+ <li>Added support for 32bit floating point sample
+data type</li>
+ <li>Restructured the BPM routines into separate
+library</li>
+ <li>Fixed big-endian conversion bugs in WAV file
+routines (hopefully :)</li>
+</ul>
+<p><strong>v1.1.1: </strong></p>
+<ul>
+ <li>Fixed bugs in WAV file reading & added
+byte-order conversion for big-endian processors. </li>
+ <li>Moved SoundStretch source code under 'example'
+directory to highlight difference from SoundTouch stuff. </li>
+ <li>Replaced windows makefiles by script using the .dsw files </li>
+ <li>Output file name isn't required if output
+isn't desired (e.g. if using the switch '-bpm' in plain format only) </li>
+</ul>
+<p><strong>v1.1:</strong></p>
+<ul>
+ <li>Fixed "Release" settings in Microsoft Visual
+C++ project file (.dsp) </li>
+ <li>Added beats-per-minute (BPM) detection routine
+and command-line switch "-bpm" </li>
+</ul>
+<p><strong>v1.01: </strong></p>
+<ul>
+ <li>Initial release </li>
+</ul>
+<hr>
+<h2 >6. Acknowledgements </h2>
+<p >Kudos for these people who have contributed to development or submitted
+bugfixes since
+SoundTouch v1.3.1: </p>
+<ul>
+ <li>Arthur A</li>
+ <li>Richard Ash</li>
+ <li>Stanislav Brabec</li>
+ <li>Christian Budde</li>
+ <li>Brian Cameron</li>
+ <li>Jason Champion</li>
+ <li>Patrick Colis</li>
+ <li>Justin Frankel</li>
+ <li>Jason Garland</li>
+ <li>Takashi Iwai</li>
+ <li>Paulo Pizarro</li>
+ <li>RJ Ryan</li>
+ <li>John Sheehy</li>
+</ul>
+<p >Moral greetings to all other contributors and users also!</p>
+<hr>
+<h2 >7. LICENSE </h2>
+<p>SoundTouch audio processing library<br>
+Copyright (c) Olli Parviainen</p>
+<p>This library is free software; you can
+redistribute it and/or modify it under the terms of the GNU
+Lesser General Public License version 2.1 as published by the Free Software
+Foundation.</p>
+<p>This library is distributed in the hope
+that it will be useful, but WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.</p>
+<p>You should have received a copy of the GNU
+Lesser General Public License along with this library; if not,
+write to the Free Software Foundation, Inc., 59 Temple Place,
+Suite 330, Boston, MA 02111-1307 USA</p>
+<hr>
+<!--
+$Id: README.html 81 2009-12-28 20:51:18Z oparviai $
+-->
+</body>
+</html>
diff --git a/plugins/soundtouch/soundtouch/include/BPMDetect.h b/plugins/soundtouch/soundtouch/include/BPMDetect.h new file mode 100644 index 00000000..4def43f1 --- /dev/null +++ b/plugins/soundtouch/soundtouch/include/BPMDetect.h @@ -0,0 +1,161 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Beats-per-minute (BPM) detection routine.
+///
+/// The beat detection algorithm works as follows:
+/// - Use function 'inputSamples' to input a chunks of samples to the class for
+/// analysis. It's a good idea to enter a large sound file or stream in smallish
+/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
+/// - Input sound data is decimated to approx 500 Hz to reduce calculation burden,
+/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
+/// Simple averaging is used for anti-alias filtering because the resulting signal
+/// quality isn't of that high importance.
+/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
+/// taking absolute value that's smoothed by sliding average. Signal levels that
+/// are below a couple of times the general RMS amplitude level are cut away to
+/// leave only notable peaks there.
+/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
+/// autocorrelation function of the enveloped signal.
+/// - After whole sound data file has been analyzed as above, the bpm level is
+/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
+/// function, calculates it's precise location and converts this reading to bpm's.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: BPMDetect.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _BPMDetect_H_
+#define _BPMDetect_H_
+
+#include "STTypes.h"
+#include "FIFOSampleBuffer.h"
+
+namespace soundtouch
+{
+
+/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
+#define MIN_BPM 29
+
+/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
+#define MAX_BPM 230
+
+
+/// Class for calculating BPM rate for audio data.
+class BPMDetect
+{
+protected:
+ /// Auto-correlation accumulator bins.
+ float *xcorr;
+
+ /// Amplitude envelope sliding average approximation level accumulator
+ float envelopeAccu;
+
+ /// RMS volume sliding average approximation level accumulator
+ float RMSVolumeAccu;
+
+ /// Sample average counter.
+ int decimateCount;
+
+ /// Sample average accumulator for FIFO-like decimation.
+ soundtouch::LONG_SAMPLETYPE decimateSum;
+
+ /// Decimate sound by this coefficient to reach approx. 500 Hz.
+ int decimateBy;
+
+ /// Auto-correlation window length
+ int windowLen;
+
+ /// Number of channels (1 = mono, 2 = stereo)
+ int channels;
+
+ /// sample rate
+ int sampleRate;
+
+ /// Beginning of auto-correlation window: Autocorrelation isn't being updated for
+ /// the first these many correlation bins.
+ int windowStart;
+
+ /// FIFO-buffer for decimated processing samples.
+ soundtouch::FIFOSampleBuffer *buffer;
+
+ /// Updates auto-correlation function for given number of decimated samples that
+ /// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
+ /// though).
+ void updateXCorr(int process_samples /// How many samples are processed.
+ );
+
+ /// Decimates samples to approx. 500 Hz.
+ ///
+ /// \return Number of output samples.
+ int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer
+ const soundtouch::SAMPLETYPE *src, ///< Source sample buffer
+ int numsamples ///< Number of source samples.
+ );
+
+ /// Calculates amplitude envelope for the buffer of samples.
+ /// Result is output to 'samples'.
+ void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer
+ int numsamples ///< Number of samples in buffer
+ );
+
+public:
+ /// Constructor.
+ BPMDetect(int numChannels, ///< Number of channels in sample data.
+ int sampleRate ///< Sample rate in Hz.
+ );
+
+ /// Destructor.
+ virtual ~BPMDetect();
+
+ /// Inputs a block of samples for analyzing: Envelopes the samples and then
+ /// updates the autocorrelation estimation. When whole song data has been input
+ /// in smaller blocks using this function, read the resulting bpm with 'getBpm'
+ /// function.
+ ///
+ /// Notice that data in 'samples' array can be disrupted in processing.
+ void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
+ int numSamples ///< Number of samples in buffer
+ );
+
+
+ /// Analyzes the results and returns the BPM rate. Use this function to read result
+ /// after whole song data has been input to the class by consecutive calls of
+ /// 'inputSamples' function.
+ ///
+ /// \return Beats-per-minute rate, or zero if detection failed.
+ float getBpm();
+};
+
+}
+
+#endif // _BPMDetect_H_
diff --git a/plugins/soundtouch/soundtouch/include/FIFOSampleBuffer.h b/plugins/soundtouch/soundtouch/include/FIFOSampleBuffer.h new file mode 100644 index 00000000..76cbf951 --- /dev/null +++ b/plugins/soundtouch/soundtouch/include/FIFOSampleBuffer.h @@ -0,0 +1,174 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// A buffer class for temporarily storaging sound samples, operates as a
+/// first-in-first-out pipe.
+///
+/// Samples are added to the end of the sample buffer with the 'putSamples'
+/// function, and are received from the beginning of the buffer by calling
+/// the 'receiveSamples' function. The class automatically removes the
+/// output samples from the buffer as well as grows the storage size
+/// whenever necessary.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIFOSampleBuffer.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef FIFOSampleBuffer_H
+#define FIFOSampleBuffer_H
+
+#include "FIFOSamplePipe.h"
+
+namespace soundtouch
+{
+
+/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
+/// care of storage size adjustment and data moving during input/output operations.
+///
+/// Notice that in case of stereo audio, one sample is considered to consist of
+/// both channel data.
+class FIFOSampleBuffer : public FIFOSamplePipe
+{
+private:
+ /// Sample buffer.
+ SAMPLETYPE *buffer;
+
+ // Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first
+ // 16-byte aligned location of this buffer
+ SAMPLETYPE *bufferUnaligned;
+
+ /// Sample buffer size in bytes
+ uint sizeInBytes;
+
+ /// How many samples are currently in buffer.
+ uint samplesInBuffer;
+
+ /// Channels, 1=mono, 2=stereo.
+ uint channels;
+
+ /// Current position pointer to the buffer. This pointer is increased when samples are
+ /// removed from the pipe so that it's necessary to actually rewind buffer (move data)
+ /// only new data when is put to the pipe.
+ uint bufferPos;
+
+ /// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
+ /// beginning of the buffer.
+ void rewind();
+
+ /// Ensures that the buffer has capacity for at least this many samples.
+ void ensureCapacity(uint capacityRequirement);
+
+ /// Returns current capacity.
+ uint getCapacity() const;
+
+public:
+
+ /// Constructor
+ FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
+ ///< Default is stereo.
+ );
+
+ /// destructor
+ ~FIFOSampleBuffer();
+
+ /// Returns a pointer to the beginning of the output samples.
+ /// This function is provided for accessing the output samples directly.
+ /// Please be careful for not to corrupt the book-keeping!
+ ///
+ /// When using this function to output samples, also remember to 'remove' the
+ /// output samples from the buffer by calling the
+ /// 'receiveSamples(numSamples)' function
+ virtual SAMPLETYPE *ptrBegin();
+
+ /// Returns a pointer to the end of the used part of the sample buffer (i.e.
+ /// where the new samples are to be inserted). This function may be used for
+ /// inserting new samples into the sample buffer directly. Please be careful
+ /// not corrupt the book-keeping!
+ ///
+ /// When using this function as means for inserting new samples, also remember
+ /// to increase the sample count afterwards, by calling the
+ /// 'putSamples(numSamples)' function.
+ SAMPLETYPE *ptrEnd(
+ uint slackCapacity ///< How much free capacity (in samples) there _at least_
+ ///< should be so that the caller can succesfully insert the
+ ///< desired samples to the buffer. If necessary, the function
+ ///< grows the buffer size to comply with this requirement.
+ );
+
+ /// Adds 'numSamples' pcs of samples from the 'samples' memory position to
+ /// the sample buffer.
+ virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
+ uint numSamples ///< Number of samples to insert.
+ );
+
+ /// Adjusts the book-keeping to increase number of samples in the buffer without
+ /// copying any actual samples.
+ ///
+ /// This function is used to update the number of samples in the sample buffer
+ /// when accessing the buffer directly with 'ptrEnd' function. Please be
+ /// careful though!
+ virtual void putSamples(uint numSamples ///< Number of samples been inserted.
+ );
+
+ /// Output samples from beginning of the sample buffer. Copies requested samples to
+ /// output buffer and removes them from the sample buffer. If there are less than
+ /// 'numsample' samples in the buffer, returns all that available.
+ ///
+ /// \return Number of samples returned.
+ virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
+ uint maxSamples ///< How many samples to receive at max.
+ );
+
+ /// Adjusts book-keeping so that given number of samples are removed from beginning of the
+ /// sample buffer without copying them anywhere.
+ ///
+ /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
+ /// with 'ptrBegin' function.
+ virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
+ );
+
+ /// Returns number of samples currently available.
+ virtual uint numSamples() const;
+
+ /// Sets number of channels, 1 = mono, 2 = stereo.
+ void setChannels(int numChannels);
+
+ /// Returns nonzero if there aren't any samples available for outputting.
+ virtual int isEmpty() const;
+
+ /// Clears all the samples.
+ virtual void clear();
+};
+
+}
+
+#endif
diff --git a/plugins/soundtouch/soundtouch/include/FIFOSamplePipe.h b/plugins/soundtouch/soundtouch/include/FIFOSamplePipe.h new file mode 100644 index 00000000..b5fc3b77 --- /dev/null +++ b/plugins/soundtouch/soundtouch/include/FIFOSamplePipe.h @@ -0,0 +1,221 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound
+/// samples by operating like a first-in-first-out pipe: New samples are fed
+/// into one end of the pipe with the 'putSamples' function, and the processed
+/// samples are received from the other end with the 'receiveSamples' function.
+///
+/// 'FIFOProcessor' : A base class for classes the do signal processing with
+/// the samples while operating like a first-in-first-out pipe. When samples
+/// are input with the 'putSamples' function, the class processes them
+/// and moves the processed samples to the given 'output' pipe object, which
+/// may be either another processing stage, or a fifo sample buffer object.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-04-13 16:18:48 +0300 (Mon, 13 Apr 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIFOSamplePipe.h 69 2009-04-13 13:18:48Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef FIFOSamplePipe_H
+#define FIFOSamplePipe_H
+
+#include <assert.h>
+#include <stdlib.h>
+#include "STTypes.h"
+
+namespace soundtouch
+{
+
+/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
+class FIFOSamplePipe
+{
+public:
+ // virtual default destructor
+ virtual ~FIFOSamplePipe() {}
+
+
+ /// Returns a pointer to the beginning of the output samples.
+ /// This function is provided for accessing the output samples directly.
+ /// Please be careful for not to corrupt the book-keeping!
+ ///
+ /// When using this function to output samples, also remember to 'remove' the
+ /// output samples from the buffer by calling the
+ /// 'receiveSamples(numSamples)' function
+ virtual SAMPLETYPE *ptrBegin() = 0;
+
+ /// Adds 'numSamples' pcs of samples from the 'samples' memory position to
+ /// the sample buffer.
+ virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
+ uint numSamples ///< Number of samples to insert.
+ ) = 0;
+
+
+ // Moves samples from the 'other' pipe instance to this instance.
+ void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
+ )
+ {
+ int oNumSamples = other.numSamples();
+
+ putSamples(other.ptrBegin(), oNumSamples);
+ other.receiveSamples(oNumSamples);
+ };
+
+ /// Output samples from beginning of the sample buffer. Copies requested samples to
+ /// output buffer and removes them from the sample buffer. If there are less than
+ /// 'numsample' samples in the buffer, returns all that available.
+ ///
+ /// \return Number of samples returned.
+ virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
+ uint maxSamples ///< How many samples to receive at max.
+ ) = 0;
+
+ /// Adjusts book-keeping so that given number of samples are removed from beginning of the
+ /// sample buffer without copying them anywhere.
+ ///
+ /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
+ /// with 'ptrBegin' function.
+ virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
+ ) = 0;
+
+ /// Returns number of samples currently available.
+ virtual uint numSamples() const = 0;
+
+ // Returns nonzero if there aren't any samples available for outputting.
+ virtual int isEmpty() const = 0;
+
+ /// Clears all the samples.
+ virtual void clear() = 0;
+};
+
+
+
+/// Base-class for sound processing routines working in FIFO principle. With this base
+/// class it's easy to implement sound processing stages that can be chained together,
+/// so that samples that are fed into beginning of the pipe automatically go through
+/// all the processing stages.
+///
+/// When samples are input to this class, they're first processed and then put to
+/// the FIFO pipe that's defined as output of this class. This output pipe can be
+/// either other processing stage or a FIFO sample buffer.
+class FIFOProcessor :public FIFOSamplePipe
+{
+protected:
+ /// Internal pipe where processed samples are put.
+ FIFOSamplePipe *output;
+
+ /// Sets output pipe.
+ void setOutPipe(FIFOSamplePipe *pOutput)
+ {
+ assert(output == NULL);
+ assert(pOutput != NULL);
+ output = pOutput;
+ }
+
+
+ /// Constructor. Doesn't define output pipe; it has to be set be
+ /// 'setOutPipe' function.
+ FIFOProcessor()
+ {
+ output = NULL;
+ }
+
+
+ /// Constructor. Configures output pipe.
+ FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
+ )
+ {
+ output = pOutput;
+ }
+
+
+ /// Destructor.
+ virtual ~FIFOProcessor()
+ {
+ }
+
+
+ /// Returns a pointer to the beginning of the output samples.
+ /// This function is provided for accessing the output samples directly.
+ /// Please be careful for not to corrupt the book-keeping!
+ ///
+ /// When using this function to output samples, also remember to 'remove' the
+ /// output samples from the buffer by calling the
+ /// 'receiveSamples(numSamples)' function
+ virtual SAMPLETYPE *ptrBegin()
+ {
+ return output->ptrBegin();
+ }
+
+public:
+
+ /// Output samples from beginning of the sample buffer. Copies requested samples to
+ /// output buffer and removes them from the sample buffer. If there are less than
+ /// 'numsample' samples in the buffer, returns all that available.
+ ///
+ /// \return Number of samples returned.
+ virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
+ uint maxSamples ///< How many samples to receive at max.
+ )
+ {
+ return output->receiveSamples(outBuffer, maxSamples);
+ }
+
+
+ /// Adjusts book-keeping so that given number of samples are removed from beginning of the
+ /// sample buffer without copying them anywhere.
+ ///
+ /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
+ /// with 'ptrBegin' function.
+ virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
+ )
+ {
+ return output->receiveSamples(maxSamples);
+ }
+
+
+ /// Returns number of samples currently available.
+ virtual uint numSamples() const
+ {
+ return output->numSamples();
+ }
+
+
+ /// Returns nonzero if there aren't any samples available for outputting.
+ virtual int isEmpty() const
+ {
+ return output->isEmpty();
+ }
+};
+
+}
+
+#endif
diff --git a/plugins/soundtouch/soundtouch/include/STTypes.h b/plugins/soundtouch/soundtouch/include/STTypes.h new file mode 100644 index 00000000..c09a45f9 --- /dev/null +++ b/plugins/soundtouch/soundtouch/include/STTypes.h @@ -0,0 +1,149 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Common type definitions for SoundTouch audio processing library.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-05-17 14:30:57 +0300 (Sun, 17 May 2009) $
+// File revision : $Revision: 3 $
+//
+// $Id: STTypes.h 70 2009-05-17 11:30:57Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef STTypes_H
+#define STTypes_H
+
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+#ifdef __GNUC__
+ // In GCC, include soundtouch_config.h made by config scritps
+ #include "soundtouch_config.h"
+#endif
+
+#ifndef _WINDEF_
+ // if these aren't defined already by Windows headers, define now
+
+ typedef int BOOL;
+
+ #define FALSE 0
+ #define TRUE 1
+
+#endif // _WINDEF_
+
+
+namespace soundtouch
+{
+
+/// Activate these undef's to overrule the possible sampletype
+/// setting inherited from some other header file:
+//#undef INTEGER_SAMPLES
+//#undef FLOAT_SAMPLES
+
+#if !(INTEGER_SAMPLES || FLOAT_SAMPLES)
+
+ /// Choose either 32bit floating point or 16bit integer sampletype
+ /// by choosing one of the following defines, unless this selection
+ /// has already been done in some other file.
+ ////
+ /// Notes:
+ /// - In Windows environment, choose the sample format with the
+ /// following defines.
+ /// - In GNU environment, the floating point samples are used by
+ /// default, but integer samples can be chosen by giving the
+ /// following switch to the configure script:
+ /// ./configure --enable-integer-samples
+ /// However, if you still prefer to select the sample format here
+ /// also in GNU environment, then please #undef the INTEGER_SAMPLE
+ /// and FLOAT_SAMPLE defines first as in comments above.
+ //#define INTEGER_SAMPLES 1 //< 16bit integer samples
+ #define FLOAT_SAMPLES 1 //< 32bit float samples
+
+ #endif
+
+ #if (WIN32 || __i386__ || __x86_64__)
+ /// Define this to allow X86-specific assembler/intrinsic optimizations.
+ /// Notice that library contains also usual C++ versions of each of these
+ /// these routines, so if you're having difficulties getting the optimized
+ /// routines compiled for whatever reason, you may disable these optimizations
+ /// to make the library compile.
+
+ #define ALLOW_X86_OPTIMIZATIONS 0
+
+ #endif
+
+ // If defined, allows the SIMD-optimized routines to take minor shortcuts
+ // for improved performance. Undefine to require faithfully similar SIMD
+ // calculations as in normal C implementation.
+ #define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
+
+
+ #ifdef INTEGER_SAMPLES
+ // 16bit integer sample type
+ typedef short SAMPLETYPE;
+ // data type for sample accumulation: Use 32bit integer to prevent overflows
+ typedef long LONG_SAMPLETYPE;
+
+ #ifdef FLOAT_SAMPLES
+ // check that only one sample type is defined
+ #error "conflicting sample types defined"
+ #endif // FLOAT_SAMPLES
+
+ #ifdef ALLOW_X86_OPTIMIZATIONS
+ // Allow MMX optimizations
+ #define ALLOW_MMX 1
+ #endif
+
+ #else
+
+ // floating point samples
+ typedef float SAMPLETYPE;
+ // data type for sample accumulation: Use double to utilize full precision.
+ typedef double LONG_SAMPLETYPE;
+
+ #ifdef ALLOW_X86_OPTIMIZATIONS
+ // Allow 3DNow! and SSE optimizations
+ #if WIN32
+ #define ALLOW_3DNOW 1
+ #endif
+
+ #define ALLOW_SSE 1
+ #endif
+
+ #endif // INTEGER_SAMPLES
+};
+
+
+// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
+// parameter setting crosses from value <1 to >=1 or vice versa during processing.
+// Default is off as such crossover is untypical case and involves a slight sound
+// quality compromise.
+//#define PREVENT_CLICK_AT_RATE_CROSSOVER 1
+
+#endif
diff --git a/plugins/soundtouch/soundtouch/include/SoundTouch.h b/plugins/soundtouch/soundtouch/include/SoundTouch.h new file mode 100644 index 00000000..0e042d3c --- /dev/null +++ b/plugins/soundtouch/soundtouch/include/SoundTouch.h @@ -0,0 +1,252 @@ +//////////////////////////////////////////////////////////////////////////////
+///
+/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
+///
+/// Notes:
+/// - Initialize the SoundTouch object instance by setting up the sound stream
+/// parameters with functions 'setSampleRate' and 'setChannels', then set
+/// desired tempo/pitch/rate settings with the corresponding functions.
+///
+/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
+/// samples that are to be processed are fed into one of the pipe by calling
+/// function 'putSamples', while the ready processed samples can be read
+/// from the other end of the pipeline with function 'receiveSamples'.
+///
+/// - The SoundTouch processing classes require certain sized 'batches' of
+/// samples in order to process the sound. For this reason the classes buffer
+/// incoming samples until there are enough of samples available for
+/// processing, then they carry out the processing step and consequently
+/// make the processed samples available for outputting.
+///
+/// - For the above reason, the processing routines introduce a certain
+/// 'latency' between the input and output, so that the samples input to
+/// SoundTouch may not be immediately available in the output, and neither
+/// the amount of outputtable samples may not immediately be in direct
+/// relationship with the amount of previously input samples.
+///
+/// - The tempo/pitch/rate control parameters can be altered during processing.
+/// Please notice though that they aren't currently protected by semaphores,
+/// so in multi-thread application external semaphore protection may be
+/// required.
+///
+/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
+/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
+/// tempo and pitch in the same ratio) of the sound. The third available control
+/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
+/// combining the two other controls.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-12-28 22:10:14 +0200 (Mon, 28 Dec 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: SoundTouch.h 78 2009-12-28 20:10:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SoundTouch_H
+#define SoundTouch_H
+
+#include "FIFOSamplePipe.h"
+#include "STTypes.h"
+
+namespace soundtouch
+{
+
+/// Soundtouch library version string
+#define SOUNDTOUCH_VERSION "1.5.0"
+
+/// SoundTouch library version id
+#define SOUNDTOUCH_VERSION_ID (10500)
+
+//
+// Available setting IDs for the 'setSetting' & 'get_setting' functions:
+
+/// Enable/disable anti-alias filter in pitch transposer (0 = disable)
+#define SETTING_USE_AA_FILTER 0
+
+/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
+#define SETTING_AA_FILTER_LENGTH 1
+
+/// Enable/disable quick seeking algorithm in tempo changer routine
+/// (enabling quick seeking lowers CPU utilization but causes a minor sound
+/// quality compromising)
+#define SETTING_USE_QUICKSEEK 2
+
+/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
+/// to how long sequences the original sound is chopped in the time-stretch algorithm.
+/// See "STTypes.h" or README for more information.
+#define SETTING_SEQUENCE_MS 3
+
+/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
+/// best possible overlapping location. This determines from how wide window the algorithm
+/// may look for an optimal joining location when mixing the sound sequences back together.
+/// See "STTypes.h" or README for more information.
+#define SETTING_SEEKWINDOW_MS 4
+
+/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
+/// are mixed back together, to form a continuous sound stream, this parameter defines over
+/// how long period the two consecutive sequences are let to overlap each other.
+/// See "STTypes.h" or README for more information.
+#define SETTING_OVERLAP_MS 5
+
+
+class SoundTouch : public FIFOProcessor
+{
+private:
+ /// Rate transposer class instance
+ class RateTransposer *pRateTransposer;
+
+ /// Time-stretch class instance
+ class TDStretch *pTDStretch;
+
+ /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
+ float virtualRate;
+
+ /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
+ float virtualTempo;
+
+ /// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
+ float virtualPitch;
+
+ /// Flag: Has sample rate been set?
+ BOOL bSrateSet;
+
+ /// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
+ /// 'virtualPitch' parameters.
+ void calcEffectiveRateAndTempo();
+
+protected :
+ /// Number of channels
+ uint channels;
+
+ /// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
+ float rate;
+
+ /// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
+ float tempo;
+
+public:
+ SoundTouch();
+ virtual ~SoundTouch();
+
+ /// Get SoundTouch library version string
+ static const char *getVersionString();
+
+ /// Get SoundTouch library version Id
+ static uint getVersionId();
+
+ /// Sets new rate control value. Normal rate = 1.0, smaller values
+ /// represent slower rate, larger faster rates.
+ void setRate(float newRate);
+
+ /// Sets new tempo control value. Normal tempo = 1.0, smaller values
+ /// represent slower tempo, larger faster tempo.
+ void setTempo(float newTempo);
+
+ /// Sets new rate control value as a difference in percents compared
+ /// to the original rate (-50 .. +100 %)
+ void setRateChange(float newRate);
+
+ /// Sets new tempo control value as a difference in percents compared
+ /// to the original tempo (-50 .. +100 %)
+ void setTempoChange(float newTempo);
+
+ /// Sets new pitch control value. Original pitch = 1.0, smaller values
+ /// represent lower pitches, larger values higher pitch.
+ void setPitch(float newPitch);
+
+ /// Sets pitch change in octaves compared to the original pitch
+ /// (-1.00 .. +1.00)
+ void setPitchOctaves(float newPitch);
+
+ /// Sets pitch change in semi-tones compared to the original pitch
+ /// (-12 .. +12)
+ void setPitchSemiTones(int newPitch);
+ void setPitchSemiTones(float newPitch);
+
+ /// Sets the number of channels, 1 = mono, 2 = stereo
+ void setChannels(uint numChannels);
+
+ /// Sets sample rate.
+ void setSampleRate(uint srate);
+
+ /// Flushes the last samples from the processing pipeline to the output.
+ /// Clears also the internal processing buffers.
+ //
+ /// Note: This function is meant for extracting the last samples of a sound
+ /// stream. This function may introduce additional blank samples in the end
+ /// of the sound stream, and thus it's not recommended to call this function
+ /// in the middle of a sound stream.
+ void flush();
+
+ /// Adds 'numSamples' pcs of samples from the 'samples' memory position into
+ /// the input of the object. Notice that sample rate _has_to_ be set before
+ /// calling this function, otherwise throws a runtime_error exception.
+ virtual void putSamples(
+ const SAMPLETYPE *samples, ///< Pointer to sample buffer.
+ uint numSamples ///< Number of samples in buffer. Notice
+ ///< that in case of stereo-sound a single sample
+ ///< contains data for both channels.
+ );
+
+ /// Clears all the samples in the object's output and internal processing
+ /// buffers.
+ virtual void clear();
+
+ /// Changes a setting controlling the processing system behaviour. See the
+ /// 'SETTING_...' defines for available setting ID's.
+ ///
+ /// \return 'TRUE' if the setting was succesfully changed
+ BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
+ int value ///< New setting value.
+ );
+
+ /// Reads a setting controlling the processing system behaviour. See the
+ /// 'SETTING_...' defines for available setting ID's.
+ ///
+ /// \return the setting value.
+ int getSetting(int settingId ///< Setting ID number, see SETTING_... defines.
+ ) const;
+
+ /// Returns number of samples currently unprocessed.
+ virtual uint numUnprocessedSamples() const;
+
+
+ /// Other handy functions that are implemented in the ancestor classes (see
+ /// classes 'FIFOProcessor' and 'FIFOSamplePipe')
+ ///
+ /// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
+ /// - numSamples() : Get number of 'ready' samples that can be received with
+ /// function 'receiveSamples()'
+ /// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
+ /// - clear() : Clears all samples from ready/processing buffers.
+};
+
+}
+#endif
diff --git a/plugins/soundtouch/soundtouch/include/soundtouch_config.h b/plugins/soundtouch/soundtouch/include/soundtouch_config.h new file mode 100644 index 00000000..4097691b --- /dev/null +++ b/plugins/soundtouch/soundtouch/include/soundtouch_config.h @@ -0,0 +1,88 @@ +/* include/soundtouch_config.h. Generated from soundtouch_config.h.in by configure. */ +/* include/soundtouch_config.h.in. Generated from configure.ac by autoheader. */ + +/* Use Float as Sample type */ +#define FLOAT_SAMPLES 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Use Integer as Sample type */ +/* #undef INTEGER_SAMPLES */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "soundtouch" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://www.surina.net/soundtouch" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "SoundTouch" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "SoundTouch 1.4.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "soundtouch" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.4.0" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.4.0" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/3dnow_win.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/3dnow_win.cpp new file mode 100644 index 00000000..f0a9d7ec --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/3dnow_win.cpp @@ -0,0 +1,349 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon
+/// processors. All 3DNow! optimized functions have been gathered into this
+/// single source code file, regardless to their class or original source code
+/// file, in order to ease porting the library to other compiler and processor
+/// platforms.
+///
+/// By the way; the performance gain depends heavily on the CPU generation: On
+/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the
+/// difference to the original routines stayed at unremarkable 8%! Such a small
+/// improvement on Athlon is due to 3DNow can perform only two operations in
+/// parallel, and obviously also the Athlon FPU is doing a very good job with
+/// the standard C floating point routines! Here these routines are anyway,
+/// although it might not be worth the effort to convert these to GCC platform,
+/// for Athlon CPU at least. The situation is different regarding the SSE
+/// optimizations though, thanks to the four parallel operations of SSE that
+/// already make a difference.
+///
+/// This file is to be compiled in Windows platform with Microsoft Visual C++
+/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all
+/// GNU platforms (if file supplied).
+///
+/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
+/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
+/// available for download at Microsoft Developers Network, see here:
+/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
+///
+/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
+/// perform a search with keywords "processor pack".
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: 3dnow_win.cpp 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "cpu_detect.h"
+#include "STTypes.h"
+
+#ifndef WIN32
+#error "wrong platform - this source code file is exclusively for Win32 platform"
+#endif
+
+using namespace soundtouch;
+
+#ifdef ALLOW_3DNOW
+// 3DNow! routines available only with float sample type
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of 3DNow! optimized functions of class 'TDStretch3DNow'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "TDStretch.h"
+
+
+// Calculates cross correlation of two buffers
+double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
+{
+ int overlapLengthLocal = overlapLength;
+ float corr = 0;
+
+ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
+ /*
+ c-pseudocode:
+
+ corr = 0;
+ for (i = 0; i < overlapLength / 4; i ++)
+ {
+ corr += pV1[0] * pV2[0];
+ pV1[1] * pV2[1];
+ pV1[2] * pV2[2];
+ pV1[3] * pV2[3];
+ pV1[4] * pV2[4];
+ pV1[5] * pV2[5];
+ pV1[6] * pV2[6];
+ pV1[7] * pV2[7];
+
+ pV1 += 8;
+ pV2 += 8;
+ }
+ */
+
+ _asm
+ {
+ // give prefetch hints to CPU of what data are to be needed soonish.
+ // give more aggressive hints on pV1 as that changes more between different calls
+ // while pV2 stays the same.
+ prefetch [pV1]
+ prefetch [pV2]
+ prefetch [pV1 + 32]
+
+ mov eax, dword ptr pV2
+ mov ebx, dword ptr pV1
+
+ pxor mm0, mm0
+
+ mov ecx, overlapLengthLocal
+ shr ecx, 2 // div by four
+
+ loop1:
+ movq mm1, [eax]
+ prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ pfmul mm1, [ebx]
+ prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movq mm2, [eax + 8]
+ pfadd mm0, mm1
+ pfmul mm2, [ebx + 8]
+
+ movq mm3, [eax + 16]
+ pfadd mm0, mm2
+ pfmul mm3, [ebx + 16]
+
+ movq mm4, [eax + 24]
+ pfadd mm0, mm3
+ pfmul mm4, [ebx + 24]
+
+ add eax, 32
+ pfadd mm0, mm4
+ add ebx, 32
+
+ dec ecx
+ jnz loop1
+
+ // add halfs of mm0 together and return the result.
+ // note: mm1 is used as a dummy parameter only, we actually don't care about it's value
+ pfacc mm0, mm1
+ movd corr, mm0
+ femms
+ }
+
+ return corr;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of 3DNow! optimized functions of class 'FIRFilter'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "FIRFilter.h"
+
+FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
+{
+ filterCoeffsUnalign = NULL;
+ filterCoeffsAlign = NULL;
+}
+
+
+FIRFilter3DNow::~FIRFilter3DNow()
+{
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = NULL;
+ filterCoeffsAlign = NULL;
+}
+
+
+// (overloaded) Calculates filter coefficients for 3DNow! routine
+void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
+{
+ uint i;
+ float fDivider;
+
+ FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
+
+ // Scale the filter coefficients so that it won't be necessary to scale the filtering result
+ // also rearrange coefficients suitably for 3DNow!
+ // Ensure that filter coeffs array is aligned to 16-byte boundary
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = new float[2 * newLength + 4];
+ filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & (uint)-16);
+
+ fDivider = (float)resultDivider;
+
+ // rearrange the filter coefficients for mmx routines
+ for (i = 0; i < newLength; i ++)
+ {
+ filterCoeffsAlign[2 * i + 0] =
+ filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
+ }
+}
+
+
+// 3DNow!-optimized version of the filter routine for stereo sound
+uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, uint numSamples) const
+{
+ float *filterCoeffsLocal = filterCoeffsAlign;
+ uint count = (numSamples - length) & (uint)-2;
+ uint lengthLocal = length / 4;
+
+ assert(length != 0);
+ assert(count % 2 == 0);
+
+ /* original code:
+
+ double suml1, suml2;
+ double sumr1, sumr2;
+ uint i, j;
+
+ for (j = 0; j < count; j += 2)
+ {
+ const float *ptr;
+
+ suml1 = sumr1 = 0.0;
+ suml2 = sumr2 = 0.0;
+ ptr = src;
+ filterCoeffsLocal = filterCoeffs;
+ for (i = 0; i < lengthLocal; i ++)
+ {
+ // unroll loop for efficiency.
+
+ suml1 += ptr[0] * filterCoeffsLocal[0] +
+ ptr[2] * filterCoeffsLocal[2] +
+ ptr[4] * filterCoeffsLocal[4] +
+ ptr[6] * filterCoeffsLocal[6];
+
+ sumr1 += ptr[1] * filterCoeffsLocal[1] +
+ ptr[3] * filterCoeffsLocal[3] +
+ ptr[5] * filterCoeffsLocal[5] +
+ ptr[7] * filterCoeffsLocal[7];
+
+ suml2 += ptr[8] * filterCoeffsLocal[0] +
+ ptr[10] * filterCoeffsLocal[2] +
+ ptr[12] * filterCoeffsLocal[4] +
+ ptr[14] * filterCoeffsLocal[6];
+
+ sumr2 += ptr[9] * filterCoeffsLocal[1] +
+ ptr[11] * filterCoeffsLocal[3] +
+ ptr[13] * filterCoeffsLocal[5] +
+ ptr[15] * filterCoeffsLocal[7];
+
+ ptr += 16;
+ filterCoeffsLocal += 8;
+ }
+ dest[0] = (float)suml1;
+ dest[1] = (float)sumr1;
+ dest[2] = (float)suml2;
+ dest[3] = (float)sumr2;
+
+ src += 4;
+ dest += 4;
+ }
+
+ */
+ _asm
+ {
+ mov eax, dword ptr dest
+ mov ebx, dword ptr src
+ mov edx, count
+ shr edx, 1
+
+ loop1:
+ // "outer loop" : during each round 2*2 output samples are calculated
+ prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish
+
+ mov esi, ebx
+ mov edi, filterCoeffsLocal
+ pxor mm0, mm0
+ pxor mm1, mm1
+ mov ecx, lengthLocal
+
+ loop2:
+ // "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples
+ movq mm2, [edi]
+ movq mm3, mm2
+ prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ pfmul mm2, [esi]
+ prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ pfmul mm3, [esi + 8]
+
+ movq mm4, [edi + 8]
+ movq mm5, mm4
+ pfadd mm0, mm2
+ pfmul mm4, [esi + 8]
+ pfadd mm1, mm3
+ pfmul mm5, [esi + 16]
+
+ movq mm2, [edi + 16]
+ movq mm6, mm2
+ pfadd mm0, mm4
+ pfmul mm2, [esi + 16]
+ pfadd mm1, mm5
+ pfmul mm6, [esi + 24]
+
+ movq mm3, [edi + 24]
+ movq mm7, mm3
+ pfadd mm0, mm2
+ pfmul mm3, [esi + 24]
+ pfadd mm1, mm6
+ pfmul mm7, [esi + 32]
+ add esi, 32
+ pfadd mm0, mm3
+ add edi, 32
+ pfadd mm1, mm7
+
+ dec ecx
+ jnz loop2
+
+ movq [eax], mm0
+ add ebx, 16
+ movq [eax + 8], mm1
+ add eax, 16
+
+ dec edx
+ jnz loop1
+
+ femms
+ }
+
+ return count;
+}
+
+
+#endif // ALLOW_3DNOW
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/AAFilter.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/AAFilter.cpp new file mode 100644 index 00000000..96abda49 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/AAFilter.cpp @@ -0,0 +1,184 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
+/// MMX optimization.
+///
+/// Anti-alias filter is used to prevent folding of high frequencies when
+/// transposing the sample rate with interpolation.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-01-11 13:34:24 +0200 (Sun, 11 Jan 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "AAFilter.h"
+#include "FIRFilter.h"
+
+using namespace soundtouch;
+
+#define PI 3.141592655357989
+#define TWOPI (2 * PI)
+
+/*****************************************************************************
+ *
+ * Implementation of the class 'AAFilter'
+ *
+ *****************************************************************************/
+
+AAFilter::AAFilter(uint len)
+{
+ pFIR = FIRFilter::newInstance();
+ cutoffFreq = 0.5;
+ setLength(len);
+}
+
+
+
+AAFilter::~AAFilter()
+{
+ delete pFIR;
+}
+
+
+
+// Sets new anti-alias filter cut-off edge frequency, scaled to
+// sampling frequency (nyquist frequency = 0.5).
+// The filter will cut frequencies higher than the given frequency.
+void AAFilter::setCutoffFreq(double newCutoffFreq)
+{
+ cutoffFreq = newCutoffFreq;
+ calculateCoeffs();
+}
+
+
+
+// Sets number of FIR filter taps
+void AAFilter::setLength(uint newLength)
+{
+ length = newLength;
+ calculateCoeffs();
+}
+
+
+
+// Calculates coefficients for a low-pass FIR filter using Hamming window
+void AAFilter::calculateCoeffs()
+{
+ uint i;
+ double cntTemp, temp, tempCoeff,h, w;
+ double fc2, wc;
+ double scaleCoeff, sum;
+ double *work;
+ SAMPLETYPE *coeffs;
+
+ assert(length >= 2);
+ assert(length % 4 == 0);
+ assert(cutoffFreq >= 0);
+ assert(cutoffFreq <= 0.5);
+
+ work = new double[length];
+ coeffs = new SAMPLETYPE[length];
+
+ fc2 = 2.0 * cutoffFreq;
+ wc = PI * fc2;
+ tempCoeff = TWOPI / (double)length;
+
+ sum = 0;
+ for (i = 0; i < length; i ++)
+ {
+ cntTemp = (double)i - (double)(length / 2);
+
+ temp = cntTemp * wc;
+ if (temp != 0)
+ {
+ h = fc2 * sin(temp) / temp; // sinc function
+ }
+ else
+ {
+ h = 1.0;
+ }
+ w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
+
+ temp = w * h;
+ work[i] = temp;
+
+ // calc net sum of coefficients
+ sum += temp;
+ }
+
+ // ensure the sum of coefficients is larger than zero
+ assert(sum > 0);
+
+ // ensure we've really designed a lowpass filter...
+ assert(work[length/2] > 0);
+ assert(work[length/2 + 1] > -1e-6);
+ assert(work[length/2 - 1] > -1e-6);
+
+ // Calculate a scaling coefficient in such a way that the result can be
+ // divided by 16384
+ scaleCoeff = 16384.0f / sum;
+
+ for (i = 0; i < length; i ++)
+ {
+ // scale & round to nearest integer
+ temp = work[i] * scaleCoeff;
+ temp += (temp >= 0) ? 0.5 : -0.5;
+ // ensure no overfloods
+ assert(temp >= -32768 && temp <= 32767);
+ coeffs[i] = (SAMPLETYPE)temp;
+ }
+
+ // Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
+ pFIR->setCoefficients(coeffs, length, 14);
+
+ delete[] work;
+ delete[] coeffs;
+}
+
+
+// Applies the filter to the given sequence of samples.
+// Note : The amount of outputted samples is by value of 'filter length'
+// smaller than the amount of input samples.
+uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
+{
+ return pFIR->evaluate(dest, src, numSamples, numChannels);
+}
+
+
+uint AAFilter::getLength() const
+{
+ return pFIR->getLength();
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/AAFilter.h b/plugins/soundtouch/soundtouch/source/SoundTouch/AAFilter.h new file mode 100644 index 00000000..d5c8ce4c --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/AAFilter.h @@ -0,0 +1,91 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
+/// while maintaining the original pitch by using a time domain WSOLA-like method
+/// with several performance-increasing tweaks.
+///
+/// Anti-alias filter is used to prevent folding of high frequencies when
+/// transposing the sample rate with interpolation.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
+// File revision : $Revision: 4 $
+//
+// $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef AAFilter_H
+#define AAFilter_H
+
+#include "STTypes.h"
+
+namespace soundtouch
+{
+
+class AAFilter
+{
+protected:
+ class FIRFilter *pFIR;
+
+ /// Low-pass filter cut-off frequency, negative = invalid
+ double cutoffFreq;
+
+ /// num of filter taps
+ uint length;
+
+ /// Calculate the FIR coefficients realizing the given cutoff-frequency
+ void calculateCoeffs();
+public:
+ AAFilter(uint length);
+
+ ~AAFilter();
+
+ /// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
+ /// frequency (nyquist frequency = 0.5). The filter will cut off the
+ /// frequencies than that.
+ void setCutoffFreq(double newCutoffFreq);
+
+ /// Sets number of FIR filter taps, i.e. ~filter complexity
+ void setLength(uint newLength);
+
+ uint getLength() const;
+
+ /// Applies the filter to the given sequence of samples.
+ /// Note : The amount of outputted samples is by value of 'filter length'
+ /// smaller than the amount of input samples.
+ uint evaluate(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples,
+ uint numChannels) const;
+};
+
+}
+
+#endif
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/BPMDetect.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/BPMDetect.cpp new file mode 100644 index 00000000..405f514b --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/BPMDetect.cpp @@ -0,0 +1,308 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Beats-per-minute (BPM) detection routine.
+///
+/// The beat detection algorithm works as follows:
+/// - Use function 'inputSamples' to input a chunks of samples to the class for
+/// analysis. It's a good idea to enter a large sound file or stream in smallish
+/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
+/// - Inputted sound data is decimated to approx 500 Hz to reduce calculation burden,
+/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
+/// Simple averaging is used for anti-alias filtering because the resulting signal
+/// quality isn't of that high importance.
+/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
+/// taking absolute value that's smoothed by sliding average. Signal levels that
+/// are below a couple of times the general RMS amplitude level are cut away to
+/// leave only notable peaks there.
+/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
+/// autocorrelation function of the enveloped signal.
+/// - After whole sound data file has been analyzed as above, the bpm level is
+/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
+/// function, calculates it's precise location and converts this reading to bpm's.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: BPMDetect.cpp 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include "FIFOSampleBuffer.h"
+#include "PeakFinder.h"
+#include "BPMDetect.h"
+
+using namespace soundtouch;
+
+#define INPUT_BLOCK_SAMPLES 2048
+#define DECIMATED_BLOCK_SAMPLES 256
+
+/// decay constant for calculating RMS volume sliding average approximation
+/// (time constant is about 10 sec)
+const float avgdecay = 0.99986f;
+
+/// Normalization coefficient for calculating RMS sliding average approximation.
+const float avgnorm = (1 - avgdecay);
+
+
+
+BPMDetect::BPMDetect(int numChannels, int aSampleRate)
+{
+ this->sampleRate = aSampleRate;
+ this->channels = numChannels;
+
+ decimateSum = 0;
+ decimateCount = 0;
+
+ envelopeAccu = 0;
+
+ // Initialize RMS volume accumulator to RMS level of 3000 (out of 32768) that's
+ // a typical RMS signal level value for song data. This value is then adapted
+ // to the actual level during processing.
+#ifdef INTEGER_SAMPLES
+ // integer samples
+ RMSVolumeAccu = (3000 * 3000) / avgnorm;
+#else
+ // float samples, scaled to range [-1..+1[
+ RMSVolumeAccu = (0.092f * 0.092f) / avgnorm;
+#endif
+
+ // choose decimation factor so that result is approx. 500 Hz
+ decimateBy = sampleRate / 500;
+ assert(decimateBy > 0);
+ assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
+
+ // Calculate window length & starting item according to desired min & max bpms
+ windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
+ windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
+
+ assert(windowLen > windowStart);
+
+ // allocate new working objects
+ xcorr = new float[windowLen];
+ memset(xcorr, 0, windowLen * sizeof(float));
+
+ // allocate processing buffer
+ buffer = new FIFOSampleBuffer();
+ // we do processing in mono mode
+ buffer->setChannels(1);
+ buffer->clear();
+}
+
+
+
+BPMDetect::~BPMDetect()
+{
+ delete[] xcorr;
+ delete buffer;
+}
+
+
+
+/// convert to mono, low-pass filter & decimate to about 500 Hz.
+/// return number of outputted samples.
+///
+/// Decimation is used to remove the unnecessary frequencies and thus to reduce
+/// the amount of data needed to be processed as calculating autocorrelation
+/// function is a very-very heavy operation.
+///
+/// Anti-alias filtering is done simply by averaging the samples. This is really a
+/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
+/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
+/// narrow band)
+int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
+{
+ int count, outcount;
+ LONG_SAMPLETYPE out;
+
+ assert(channels > 0);
+ assert(decimateBy > 0);
+ outcount = 0;
+ for (count = 0; count < numsamples; count ++)
+ {
+ int j;
+
+ // convert to mono and accumulate
+ for (j = 0; j < channels; j ++)
+ {
+ decimateSum += src[j];
+ }
+ src += j;
+
+ decimateCount ++;
+ if (decimateCount >= decimateBy)
+ {
+ // Store every Nth sample only
+ out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
+ decimateSum = 0;
+ decimateCount = 0;
+#ifdef INTEGER_SAMPLES
+ // check ranges for sure (shouldn't actually be necessary)
+ if (out > 32767)
+ {
+ out = 32767;
+ }
+ else if (out < -32768)
+ {
+ out = -32768;
+ }
+#endif // INTEGER_SAMPLES
+ dest[outcount] = (SAMPLETYPE)out;
+ outcount ++;
+ }
+ }
+ return outcount;
+}
+
+
+
+// Calculates autocorrelation function of the sample history buffer
+void BPMDetect::updateXCorr(int process_samples)
+{
+ int offs;
+ SAMPLETYPE *pBuffer;
+
+ assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
+
+ pBuffer = buffer->ptrBegin();
+ for (offs = windowStart; offs < windowLen; offs ++)
+ {
+ LONG_SAMPLETYPE sum;
+ int i;
+
+ sum = 0;
+ for (i = 0; i < process_samples; i ++)
+ {
+ sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
+ }
+// xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
+ // if it's desired that the system adapts automatically to
+ // various bpms, e.g. in processing continouos music stream.
+ // The 'xcorr_decay' should be a value that's smaller than but
+ // close to one, and should also depend on 'process_samples' value.
+
+ xcorr[offs] += (float)sum;
+ }
+}
+
+
+
+// Calculates envelope of the sample data
+void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
+{
+ const float decay = 0.7f; // decay constant for smoothing the envelope
+ const float norm = (1 - decay);
+
+ int i;
+ LONG_SAMPLETYPE out;
+ float val;
+
+ for (i = 0; i < numsamples; i ++)
+ {
+ // calc average RMS volume
+ RMSVolumeAccu *= avgdecay;
+ val = (float)fabs((float)samples[i]);
+ RMSVolumeAccu += val * val;
+
+ // cut amplitudes that are below 2 times average RMS volume
+ // (we're interested in peak values, not the silent moments)
+ val -= 2 * (float)sqrt(RMSVolumeAccu * avgnorm);
+ val = (val > 0) ? val : 0;
+
+ // smooth amplitude envelope
+ envelopeAccu *= decay;
+ envelopeAccu += val;
+ out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
+
+#ifdef INTEGER_SAMPLES
+ // cut peaks (shouldn't be necessary though)
+ if (out > 32767) out = 32767;
+#endif // INTEGER_SAMPLES
+ samples[i] = (SAMPLETYPE)out;
+ }
+}
+
+
+
+void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
+{
+ SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
+
+ // iterate so that max INPUT_BLOCK_SAMPLES processed per iteration
+ while (numSamples > 0)
+ {
+ int block;
+ int decSamples;
+
+ block = (numSamples > INPUT_BLOCK_SAMPLES) ? INPUT_BLOCK_SAMPLES : numSamples;
+
+ // decimate. note that converts to mono at the same time
+ decSamples = decimate(decimated, samples, block);
+ samples += block * channels;
+ numSamples -= block;
+
+ // envelope new samples and add them to buffer
+ calcEnvelope(decimated, decSamples);
+ buffer->putSamples(decimated, decSamples);
+ }
+
+ // when the buffer has enought samples for processing...
+ if ((int)buffer->numSamples() > windowLen)
+ {
+ int processLength;
+
+ // how many samples are processed
+ processLength = (int)buffer->numSamples() - windowLen;
+
+ // ... calculate autocorrelations for oldest samples...
+ updateXCorr(processLength);
+ // ... and remove them from the buffer
+ buffer->receiveSamples(processLength);
+ }
+}
+
+
+
+float BPMDetect::getBpm()
+{
+ double peakPos;
+ PeakFinder peakFinder;
+
+ // find peak position
+ peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
+
+ assert(decimateBy != 0);
+ if (peakPos < 1e-6) return 0.0; // detection failed.
+
+ // calculate BPM
+ return (float)(60.0 * (((double)sampleRate / (double)decimateBy) / peakPos));
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp new file mode 100644 index 00000000..01f64b08 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/FIFOSampleBuffer.cpp @@ -0,0 +1,262 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// A buffer class for temporarily storaging sound samples, operates as a
+/// first-in-first-out pipe.
+///
+/// Samples are added to the end of the sample buffer with the 'putSamples'
+/// function, and are received from the beginning of the buffer by calling
+/// the 'receiveSamples' function. The class automatically removes the
+/// outputted samples from the buffer, as well as grows the buffer size
+/// whenever necessary.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-27 19:24:42 +0200 (Fri, 27 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIFOSampleBuffer.cpp 68 2009-02-27 17:24:42Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h>
+#include <stdexcept>
+
+#include "FIFOSampleBuffer.h"
+
+using namespace soundtouch;
+
+// Constructor
+FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
+{
+ assert(numChannels > 0);
+ sizeInBytes = 0; // reasonable initial value
+ buffer = NULL;
+ bufferUnaligned = NULL;
+ samplesInBuffer = 0;
+ bufferPos = 0;
+ channels = (uint)numChannels;
+ ensureCapacity(32); // allocate initial capacity
+}
+
+
+// destructor
+FIFOSampleBuffer::~FIFOSampleBuffer()
+{
+ delete[] bufferUnaligned;
+ bufferUnaligned = NULL;
+ buffer = NULL;
+}
+
+
+// Sets number of channels, 1 = mono, 2 = stereo
+void FIFOSampleBuffer::setChannels(int numChannels)
+{
+ uint usedBytes;
+
+ assert(numChannels > 0);
+ usedBytes = channels * samplesInBuffer;
+ channels = (uint)numChannels;
+ samplesInBuffer = usedBytes / channels;
+}
+
+
+// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
+// zeroes this pointer by copying samples from the 'bufferPos' pointer
+// location on to the beginning of the buffer.
+void FIFOSampleBuffer::rewind()
+{
+ if (buffer && bufferPos)
+ {
+ memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
+ bufferPos = 0;
+ }
+}
+
+
+// Adds 'numSamples' pcs of samples from the 'samples' memory position to
+// the sample buffer.
+void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels);
+ samplesInBuffer += nSamples;
+}
+
+
+// Increases the number of samples in the buffer without copying any actual
+// samples.
+//
+// This function is used to update the number of samples in the sample buffer
+// when accessing the buffer directly with 'ptrEnd' function. Please be
+// careful though!
+void FIFOSampleBuffer::putSamples(uint nSamples)
+{
+ uint req;
+
+ req = samplesInBuffer + nSamples;
+ ensureCapacity(req);
+ samplesInBuffer += nSamples;
+}
+
+
+// Returns a pointer to the end of the used part of the sample buffer (i.e.
+// where the new samples are to be inserted). This function may be used for
+// inserting new samples into the sample buffer directly. Please be careful!
+//
+// Parameter 'slackCapacity' tells the function how much free capacity (in
+// terms of samples) there _at least_ should be, in order to the caller to
+// succesfully insert all the required samples to the buffer. When necessary,
+// the function grows the buffer size to comply with this requirement.
+//
+// When using this function as means for inserting new samples, also remember
+// to increase the sample count afterwards, by calling the
+// 'putSamples(numSamples)' function.
+SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
+{
+ ensureCapacity(samplesInBuffer + slackCapacity);
+ return buffer + samplesInBuffer * channels;
+}
+
+
+// Returns a pointer to the beginning of the currently non-outputted samples.
+// This function is provided for accessing the output samples directly.
+// Please be careful!
+//
+// When using this function to output samples, also remember to 'remove' the
+// outputted samples from the buffer by calling the
+// 'receiveSamples(numSamples)' function
+SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
+{
+ assert(buffer);
+ return buffer + bufferPos * channels;
+}
+
+
+// Ensures that the buffer has enought capacity, i.e. space for _at least_
+// 'capacityRequirement' number of samples. The buffer is grown in steps of
+// 4 kilobytes to eliminate the need for frequently growing up the buffer,
+// as well as to round the buffer size up to the virtual memory page size.
+void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
+{
+ SAMPLETYPE *tempUnaligned, *temp;
+
+ if (capacityRequirement > getCapacity())
+ {
+ // enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
+ sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
+ assert(sizeInBytes % 2 == 0);
+ tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
+ if (tempUnaligned == NULL)
+ {
+ throw std::runtime_error("Couldn't allocate memory!\n");
+ }
+ // Align the buffer to begin at 16byte cache line boundary for optimal performance
+ temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
+ if (samplesInBuffer)
+ {
+ memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
+ }
+ delete[] bufferUnaligned;
+ buffer = temp;
+ bufferUnaligned = tempUnaligned;
+ bufferPos = 0;
+ }
+ else
+ {
+ // simply rewind the buffer (if necessary)
+ rewind();
+ }
+}
+
+
+// Returns the current buffer capacity in terms of samples
+uint FIFOSampleBuffer::getCapacity() const
+{
+ return sizeInBytes / (channels * sizeof(SAMPLETYPE));
+}
+
+
+// Returns the number of samples currently in the buffer
+uint FIFOSampleBuffer::numSamples() const
+{
+ return samplesInBuffer;
+}
+
+
+// Output samples from beginning of the sample buffer. Copies demanded number
+// of samples to output and removes them from the sample buffer. If there
+// are less than 'numsample' samples in the buffer, returns all available.
+//
+// Returns number of samples copied.
+uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
+{
+ uint num;
+
+ num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
+
+ memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
+ return receiveSamples(num);
+}
+
+
+// Removes samples from the beginning of the sample buffer without copying them
+// anywhere. Used to reduce the number of samples in the buffer, when accessing
+// the sample buffer with the 'ptrBegin' function.
+uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
+{
+ if (maxSamples >= samplesInBuffer)
+ {
+ uint temp;
+
+ temp = samplesInBuffer;
+ samplesInBuffer = 0;
+ return temp;
+ }
+
+ samplesInBuffer -= maxSamples;
+ bufferPos += maxSamples;
+
+ return maxSamples;
+}
+
+
+// Returns nonzero if the sample buffer is empty
+int FIFOSampleBuffer::isEmpty() const
+{
+ return (samplesInBuffer == 0) ? 1 : 0;
+}
+
+
+// Clears the sample buffer
+void FIFOSampleBuffer::clear()
+{
+ samplesInBuffer = 0;
+ bufferPos = 0;
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/FIRFilter.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/FIRFilter.cpp new file mode 100644 index 00000000..231263ad --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/FIRFilter.cpp @@ -0,0 +1,269 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// General FIR digital filter routines with MMX optimization.
+///
+/// Note : MMX optimized functions reside in a separate, platform-specific file,
+/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIRFilter.cpp 67 2009-02-25 17:13:51Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdexcept>
+#include "FIRFilter.h"
+#include "cpu_detect.h"
+
+using namespace soundtouch;
+
+/*****************************************************************************
+ *
+ * Implementation of the class 'FIRFilter'
+ *
+ *****************************************************************************/
+
+FIRFilter::FIRFilter()
+{
+ resultDivFactor = 0;
+ resultDivider = 0;
+ length = 0;
+ lengthDiv8 = 0;
+ filterCoeffs = NULL;
+}
+
+
+FIRFilter::~FIRFilter()
+{
+ delete[] filterCoeffs;
+}
+
+// Usual C-version of the filter routine for stereo sound
+uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
+{
+ uint i, j, end;
+ LONG_SAMPLETYPE suml, sumr;
+#ifdef FLOAT_SAMPLES
+ // when using floating point samples, use a scaler instead of a divider
+ // because division is much slower operation than multiplying.
+ double dScaler = 1.0 / (double)resultDivider;
+#endif
+
+ assert(length != 0);
+ assert(src != NULL);
+ assert(dest != NULL);
+ assert(filterCoeffs != NULL);
+
+ end = 2 * (numSamples - length);
+
+ for (j = 0; j < end; j += 2)
+ {
+ const SAMPLETYPE *ptr;
+
+ suml = sumr = 0;
+ ptr = src + j;
+
+ for (i = 0; i < length; i += 4)
+ {
+ // loop is unrolled by factor of 4 here for efficiency
+ suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
+ ptr[2 * i + 2] * filterCoeffs[i + 1] +
+ ptr[2 * i + 4] * filterCoeffs[i + 2] +
+ ptr[2 * i + 6] * filterCoeffs[i + 3];
+ sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
+ ptr[2 * i + 3] * filterCoeffs[i + 1] +
+ ptr[2 * i + 5] * filterCoeffs[i + 2] +
+ ptr[2 * i + 7] * filterCoeffs[i + 3];
+ }
+
+#ifdef INTEGER_SAMPLES
+ suml >>= resultDivFactor;
+ sumr >>= resultDivFactor;
+ // saturate to 16 bit integer limits
+ suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
+ // saturate to 16 bit integer limits
+ sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
+#else
+ suml *= dScaler;
+ sumr *= dScaler;
+#endif // INTEGER_SAMPLES
+ dest[j] = (SAMPLETYPE)suml;
+ dest[j + 1] = (SAMPLETYPE)sumr;
+ }
+ return numSamples - length;
+}
+
+
+
+
+// Usual C-version of the filter routine for mono sound
+uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
+{
+ uint i, j, end;
+ LONG_SAMPLETYPE sum;
+#ifdef FLOAT_SAMPLES
+ // when using floating point samples, use a scaler instead of a divider
+ // because division is much slower operation than multiplying.
+ double dScaler = 1.0 / (double)resultDivider;
+#endif
+
+
+ assert(length != 0);
+
+ end = numSamples - length;
+ for (j = 0; j < end; j ++)
+ {
+ sum = 0;
+ for (i = 0; i < length; i += 4)
+ {
+ // loop is unrolled by factor of 4 here for efficiency
+ sum += src[i + 0] * filterCoeffs[i + 0] +
+ src[i + 1] * filterCoeffs[i + 1] +
+ src[i + 2] * filterCoeffs[i + 2] +
+ src[i + 3] * filterCoeffs[i + 3];
+ }
+#ifdef INTEGER_SAMPLES
+ sum >>= resultDivFactor;
+ // saturate to 16 bit integer limits
+ sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
+#else
+ sum *= dScaler;
+#endif // INTEGER_SAMPLES
+ dest[j] = (SAMPLETYPE)sum;
+ src ++;
+ }
+ return end;
+}
+
+
+// Set filter coeffiecients and length.
+//
+// Throws an exception if filter length isn't divisible by 8
+void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
+{
+ assert(newLength > 0);
+ if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
+
+ lengthDiv8 = newLength / 8;
+ length = lengthDiv8 * 8;
+ assert(length == newLength);
+
+ resultDivFactor = uResultDivFactor;
+ resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
+
+ delete[] filterCoeffs;
+ filterCoeffs = new SAMPLETYPE[length];
+ memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
+}
+
+
+uint FIRFilter::getLength() const
+{
+ return length;
+}
+
+
+
+// Applies the filter to the given sequence of samples.
+//
+// Note : The amount of outputted samples is by value of 'filter_length'
+// smaller than the amount of input samples.
+uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
+{
+ assert(numChannels == 1 || numChannels == 2);
+
+ assert(length > 0);
+ assert(lengthDiv8 * 8 == length);
+ if (numSamples < length) return 0;
+ if (numChannels == 2)
+ {
+ return evaluateFilterStereo(dest, src, numSamples);
+ } else {
+ return evaluateFilterMono(dest, src, numSamples);
+ }
+}
+
+
+
+// Operator 'new' is overloaded so that it automatically creates a suitable instance
+// depending on if we've a MMX-capable CPU available or not.
+void * FIRFilter::operator new(size_t s)
+{
+ // Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
+ throw std::runtime_error("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
+ return NULL;
+}
+
+
+FIRFilter * FIRFilter::newInstance()
+{
+ uint uExtensions;
+
+ uExtensions = detectCPUextensions();
+
+ // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
+
+#ifdef ALLOW_MMX
+ // MMX routines available only with integer sample types
+ if (uExtensions & SUPPORT_MMX)
+ {
+ return ::new FIRFilterMMX;
+ }
+ else
+#endif // ALLOW_MMX
+
+#ifdef ALLOW_SSE
+ if (uExtensions & SUPPORT_SSE)
+ {
+ // SSE support
+ return ::new FIRFilterSSE;
+ }
+ else
+#endif // ALLOW_SSE
+
+#ifdef ALLOW_3DNOW
+ if (uExtensions & SUPPORT_3DNOW)
+ {
+ // 3DNow! support
+ return ::new FIRFilter3DNow;
+ }
+ else
+#endif // ALLOW_3DNOW
+
+ {
+ // ISA optimizations not supported, use plain C version
+ return ::new FIRFilter;
+ }
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/FIRFilter.h b/plugins/soundtouch/soundtouch/source/SoundTouch/FIRFilter.h new file mode 100644 index 00000000..5713f7bb --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/FIRFilter.h @@ -0,0 +1,164 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// General FIR digital filter routines with MMX optimization.
+///
+/// Note : MMX optimized functions reside in a separate, platform-specific file,
+/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: FIRFilter.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef FIRFilter_H
+#define FIRFilter_H
+
+#include <stddef.h>
+#include "STTypes.h"
+
+namespace soundtouch
+{
+
+class FIRFilter
+{
+protected:
+ // Number of FIR filter taps
+ uint length;
+ // Number of FIR filter taps divided by 8
+ uint lengthDiv8;
+
+ // Result divider factor in 2^k format
+ uint resultDivFactor;
+
+ // Result divider value.
+ SAMPLETYPE resultDivider;
+
+ // Memory for filter coefficients
+ SAMPLETYPE *filterCoeffs;
+
+ virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples) const;
+ virtual uint evaluateFilterMono(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples) const;
+
+public:
+ FIRFilter();
+ virtual ~FIRFilter();
+
+ /// Operator 'new' is overloaded so that it automatically creates a suitable instance
+ /// depending on if we've a MMX-capable CPU available or not.
+ static void * operator new(size_t s);
+
+ static FIRFilter *newInstance();
+
+ /// Applies the filter to the given sequence of samples.
+ /// Note : The amount of outputted samples is by value of 'filter_length'
+ /// smaller than the amount of input samples.
+ ///
+ /// \return Number of samples copied to 'dest'.
+ uint evaluate(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples,
+ uint numChannels) const;
+
+ uint getLength() const;
+
+ virtual void setCoefficients(const SAMPLETYPE *coeffs,
+ uint newLength,
+ uint uResultDivFactor);
+};
+
+
+// Optional subclasses that implement CPU-specific optimizations:
+
+#ifdef ALLOW_MMX
+
+/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
+ class FIRFilterMMX : public FIRFilter
+ {
+ protected:
+ short *filterCoeffsUnalign;
+ short *filterCoeffsAlign;
+
+ virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
+ public:
+ FIRFilterMMX();
+ ~FIRFilterMMX();
+
+ virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
+ };
+
+#endif // ALLOW_MMX
+
+
+#ifdef ALLOW_3DNOW
+
+ /// Class that implements 3DNow! optimized functions exclusive for floating point samples type.
+ class FIRFilter3DNow : public FIRFilter
+ {
+ protected:
+ float *filterCoeffsUnalign;
+ float *filterCoeffsAlign;
+
+ virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
+ public:
+ FIRFilter3DNow();
+ ~FIRFilter3DNow();
+ virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
+ };
+
+#endif // ALLOW_3DNOW
+
+
+#ifdef ALLOW_SSE
+ /// Class that implements SSE optimized functions exclusive for floating point samples type.
+ class FIRFilterSSE : public FIRFilter
+ {
+ protected:
+ float *filterCoeffsUnalign;
+ float *filterCoeffsAlign;
+
+ virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
+ public:
+ FIRFilterSSE();
+ ~FIRFilterSSE();
+
+ virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
+ };
+
+#endif // ALLOW_SSE
+
+}
+
+#endif // FIRFilter_H
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/PeakFinder.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/PeakFinder.cpp new file mode 100644 index 00000000..03f60bfa --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/PeakFinder.cpp @@ -0,0 +1,239 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Peak detection routine.
+///
+/// The routine detects highest value on an array of values and calculates the
+/// precise peak location as a mass-center of the 'hump' around the peak value.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: PeakFinder.cpp 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include <assert.h>
+
+#include "PeakFinder.h"
+
+using namespace soundtouch;
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+
+
+PeakFinder::PeakFinder()
+{
+ minPos = maxPos = 0;
+}
+
+
+// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
+// to direction defined by 'direction' until next 'hump' after minimum value will
+// begin
+int PeakFinder::findGround(const float *data, int peakpos, int direction) const
+{
+ float refvalue;
+ int lowpos;
+ int pos;
+ int climb_count;
+ float delta;
+
+ climb_count = 0;
+ refvalue = data[peakpos];
+ lowpos = peakpos;
+
+ pos = peakpos;
+
+ while ((pos > minPos) && (pos < maxPos))
+ {
+ int prevpos;
+
+ prevpos = pos;
+ pos += direction;
+
+ // calculate derivate
+ delta = data[pos] - data[prevpos];
+ if (delta <= 0)
+ {
+ // going downhill, ok
+ if (climb_count)
+ {
+ climb_count --; // decrease climb count
+ }
+
+ // check if new minimum found
+ if (data[pos] < refvalue)
+ {
+ // new minimum found
+ lowpos = pos;
+ refvalue = data[pos];
+ }
+ }
+ else
+ {
+ // going uphill, increase climbing counter
+ climb_count ++;
+ if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
+ }
+ }
+ return lowpos;
+}
+
+
+// Find offset where the value crosses the given level, when starting from 'peakpos' and
+// proceeds to direction defined in 'direction'
+int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
+{
+ float peaklevel;
+ int pos;
+
+ peaklevel = data[peakpos];
+ assert(peaklevel >= level);
+ pos = peakpos;
+ while ((pos >= minPos) && (pos < maxPos))
+ {
+ if (data[pos + direction] < level) return pos; // crossing found
+ pos += direction;
+ }
+ return -1; // not found
+}
+
+
+// Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
+double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
+{
+ int i;
+ float sum;
+ float wsum;
+
+ sum = 0;
+ wsum = 0;
+ for (i = firstPos; i <= lastPos; i ++)
+ {
+ sum += (float)i * data[i];
+ wsum += data[i];
+ }
+
+ if (wsum < 1e-6) return 0;
+ return sum / wsum;
+}
+
+
+
+/// get exact center of peak near given position by calculating local mass of center
+double PeakFinder::getPeakCenter(const float *data, int peakpos) const
+{
+ float peakLevel; // peak level
+ int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
+ float cutLevel; // cutting value
+ float groundLevel; // ground level of the peak
+ int gp1, gp2; // bottom positions of the peak 'hump'
+
+ // find ground positions.
+ gp1 = findGround(data, peakpos, -1);
+ gp2 = findGround(data, peakpos, 1);
+
+ groundLevel = max(data[gp1], data[gp2]);
+ peakLevel = data[peakpos];
+
+ if (groundLevel < 1e-6) return 0; // ground level too small => detection failed
+ if ((peakLevel / groundLevel) < 1.3) return 0; // peak less than 30% of the ground level => no good peak detected
+
+ // calculate 70%-level of the peak
+ cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
+ // find mid-level crossings
+ crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
+ crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
+
+ if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
+
+ // calculate mass center of the peak surroundings
+ return calcMassCenter(data, crosspos1, crosspos2);
+}
+
+
+
+double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
+{
+
+ int i;
+ int peakpos; // position of peak level
+ double highPeak, peak;
+
+ this->minPos = aminPos;
+ this->maxPos = amaxPos;
+
+ // find absolute peak
+ peakpos = minPos;
+ peak = data[minPos];
+ for (i = minPos + 1; i < maxPos; i ++)
+ {
+ if (data[i] > peak)
+ {
+ peak = data[i];
+ peakpos = i;
+ }
+ }
+
+ // Calculate exact location of the highest peak mass center
+ highPeak = getPeakCenter(data, peakpos);
+ peak = highPeak;
+
+ // Now check if the highest peak were in fact harmonic of the true base beat peak
+ // - sometimes the highest peak can be Nth harmonic of the true base peak yet
+ // just a slightly higher than the true base
+ for (i = 2; i < 10; i ++)
+ {
+ double peaktmp, tmp;
+ int i1,i2;
+
+ peakpos = (int)(highPeak / (double)i + 0.5f);
+ if (peakpos < minPos) break;
+
+ // calculate mass-center of possible base peak
+ peaktmp = getPeakCenter(data, peakpos);
+
+ // now compare to highest detected peak
+ i1 = (int)(highPeak + 0.5);
+ i2 = (int)(peaktmp + 0.5);
+ tmp = 2 * (data[i2] - data[i1]) / (data[i2] + data[i1]);
+ if (fabs(tmp) < 0.1)
+ {
+ // The highest peak is harmonic of almost as high base peak,
+ // thus use the base peak instead
+ peak = peaktmp;
+ }
+ }
+
+ return peak;
+}
+
+
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/PeakFinder.h b/plugins/soundtouch/soundtouch/source/SoundTouch/PeakFinder.h new file mode 100644 index 00000000..e3640cc6 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/PeakFinder.h @@ -0,0 +1,93 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// The routine detects highest value on an array of values and calculates the
+/// precise peak location as a mass-center of the 'hump' around the peak value.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: PeakFinder.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _PeakFinder_H_
+#define _PeakFinder_H_
+
+namespace soundtouch
+{
+
+class PeakFinder
+{
+protected:
+ /// Min, max allowed peak positions within the data vector
+ int minPos, maxPos;
+
+ /// Calculates the mass center between given vector items.
+ double calcMassCenter(const float *data, ///< Data vector.
+ int firstPos, ///< Index of first vector item beloging to the peak.
+ int lastPos ///< Index of last vector item beloging to the peak.
+ ) const;
+
+ /// Finds the data vector index where the monotoniously decreasing signal crosses the
+ /// given level.
+ int findCrossingLevel(const float *data, ///< Data vector.
+ float level, ///< Goal crossing level.
+ int peakpos, ///< Peak position index within the data vector.
+ int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
+ ) const;
+
+ /// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
+ /// or left-hand side of the given peak position.
+ int findGround(const float *data, /// Data vector.
+ int peakpos, /// Peak position index within the data vector.
+ int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
+ ) const;
+
+ /// get exact center of peak near given position by calculating local mass of center
+ double getPeakCenter(const float *data, int peakpos) const;
+
+public:
+ /// Constructor.
+ PeakFinder();
+
+ /// Detect exact peak position of the data vector by finding the largest peak 'hump'
+ /// and calculating the mass-center location of the peak hump.
+ ///
+ /// \return The location of the largest base harmonic peak hump.
+ double detectPeak(const float *data, /// Data vector to be analyzed. The data vector has
+ /// to be at least 'maxPos' items long.
+ int minPos, ///< Min allowed peak location within the vector data.
+ int maxPos ///< Max allowed peak location within the vector data.
+ );
+};
+
+}
+
+#endif // _PeakFinder_H_
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/RateTransposer.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/RateTransposer.cpp new file mode 100644 index 00000000..7e0b277d --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/RateTransposer.cpp @@ -0,0 +1,628 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sample rate transposer. Changes sample rate by using linear interpolation
+/// together with anti-alias filtering (first order interpolation with anti-
+/// alias filtering should be quite adequate for this application)
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-10-31 16:37:24 +0200 (Sat, 31 Oct 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: RateTransposer.cpp 74 2009-10-31 14:37:24Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <memory.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdexcept>
+#include "RateTransposer.h"
+#include "AAFilter.h"
+
+using namespace std;
+using namespace soundtouch;
+
+
+/// A linear samplerate transposer class that uses integer arithmetics.
+/// for the transposing.
+class RateTransposerInteger : public RateTransposer
+{
+protected:
+ int iSlopeCount;
+ int iRate;
+ SAMPLETYPE sPrevSampleL, sPrevSampleR;
+
+ virtual void resetRegisters();
+
+ virtual uint transposeStereo(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+ virtual uint transposeMono(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+
+public:
+ RateTransposerInteger();
+ virtual ~RateTransposerInteger();
+
+ /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
+ /// rate, larger faster rates.
+ virtual void setRate(float newRate);
+
+};
+
+
+/// A linear samplerate transposer class that uses floating point arithmetics
+/// for the transposing.
+class RateTransposerFloat : public RateTransposer
+{
+protected:
+ float fSlopeCount;
+ SAMPLETYPE sPrevSampleL, sPrevSampleR;
+
+ virtual void resetRegisters();
+
+ virtual uint transposeStereo(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+ virtual uint transposeMono(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+
+public:
+ RateTransposerFloat();
+ virtual ~RateTransposerFloat();
+};
+
+
+
+
+// Operator 'new' is overloaded so that it automatically creates a suitable instance
+// depending on if we've a MMX/SSE/etc-capable CPU available or not.
+void * RateTransposer::operator new(size_t s)
+{
+ throw runtime_error("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
+ return NULL;
+}
+
+
+RateTransposer *RateTransposer::newInstance()
+{
+#ifdef INTEGER_SAMPLES
+ return ::new RateTransposerInteger;
+#else
+ return ::new RateTransposerFloat;
+#endif
+}
+
+
+// Constructor
+RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
+{
+ numChannels = 2;
+ bUseAAFilter = TRUE;
+ fRate = 0;
+
+ // Instantiates the anti-alias filter with default tap length
+ // of 32
+ pAAFilter = new AAFilter(32);
+}
+
+
+
+RateTransposer::~RateTransposer()
+{
+ delete pAAFilter;
+}
+
+
+
+/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
+void RateTransposer::enableAAFilter(BOOL newMode)
+{
+ bUseAAFilter = newMode;
+}
+
+
+/// Returns nonzero if anti-alias filter is enabled.
+BOOL RateTransposer::isAAFilterEnabled() const
+{
+ return bUseAAFilter;
+}
+
+
+AAFilter *RateTransposer::getAAFilter()
+{
+ return pAAFilter;
+}
+
+
+
+// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
+// iRate, larger faster iRates.
+void RateTransposer::setRate(float newRate)
+{
+ double fCutoff;
+
+ fRate = newRate;
+
+ // design a new anti-alias filter
+ if (newRate > 1.0f)
+ {
+ fCutoff = 0.5f / newRate;
+ }
+ else
+ {
+ fCutoff = 0.5f * newRate;
+ }
+ pAAFilter->setCutoffFreq(fCutoff);
+}
+
+
+// Outputs as many samples of the 'outputBuffer' as possible, and if there's
+// any room left, outputs also as many of the incoming samples as possible.
+// The goal is to drive the outputBuffer empty.
+//
+// It's allowed for 'output' and 'input' parameters to point to the same
+// memory position.
+/*
+void RateTransposer::flushStoreBuffer()
+{
+ if (storeBuffer.isEmpty()) return;
+
+ outputBuffer.moveSamples(storeBuffer);
+}
+*/
+
+
+// Adds 'nSamples' pcs of samples from the 'samples' memory position into
+// the input of the object.
+void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ processSamples(samples, nSamples);
+}
+
+
+
+// Transposes up the sample rate, causing the observed playback 'rate' of the
+// sound to decrease
+void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
+{
+ uint count, sizeTemp, num;
+
+ // If the parameter 'uRate' value is smaller than 'SCALE', first transpose
+ // the samples and then apply the anti-alias filter to remove aliasing.
+
+ // First check that there's enough room in 'storeBuffer'
+ // (+16 is to reserve some slack in the destination buffer)
+ sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
+
+ // Transpose the samples, store the result into the end of "storeBuffer"
+ count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
+ storeBuffer.putSamples(count);
+
+ // Apply the anti-alias filter to samples in "store output", output the
+ // result to "dest"
+ num = storeBuffer.numSamples();
+ count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
+ storeBuffer.ptrBegin(), num, (uint)numChannels);
+ outputBuffer.putSamples(count);
+
+ // Remove the processed samples from "storeBuffer"
+ storeBuffer.receiveSamples(count);
+}
+
+
+// Transposes down the sample rate, causing the observed playback 'rate' of the
+// sound to increase
+void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
+{
+ uint count, sizeTemp;
+
+ // If the parameter 'uRate' value is larger than 'SCALE', first apply the
+ // anti-alias filter to remove high frequencies (prevent them from folding
+ // over the lover frequencies), then transpose.
+
+ // Add the new samples to the end of the storeBuffer
+ storeBuffer.putSamples(src, nSamples);
+
+ // Anti-alias filter the samples to prevent folding and output the filtered
+ // data to tempBuffer. Note : because of the FIR filter length, the
+ // filtering routine takes in 'filter_length' more samples than it outputs.
+ assert(tempBuffer.isEmpty());
+ sizeTemp = storeBuffer.numSamples();
+
+ count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
+ storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
+
+ if (count == 0) return;
+
+ // Remove the filtered samples from 'storeBuffer'
+ storeBuffer.receiveSamples(count);
+
+ // Transpose the samples (+16 is to reserve some slack in the destination buffer)
+ sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
+ count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
+ outputBuffer.putSamples(count);
+}
+
+
+// Transposes sample rate by applying anti-alias filter to prevent folding.
+// Returns amount of samples returned in the "dest" buffer.
+// The maximum amount of samples that can be returned at a time is set by
+// the 'set_returnBuffer_size' function.
+void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
+{
+ uint count;
+ uint sizeReq;
+
+ if (nSamples == 0) return;
+ assert(pAAFilter);
+
+ // If anti-alias filter is turned off, simply transpose without applying
+ // the filter
+ if (bUseAAFilter == FALSE)
+ {
+ sizeReq = (uint)((float)nSamples / fRate + 1.0f);
+ count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
+ outputBuffer.putSamples(count);
+ return;
+ }
+
+ // Transpose with anti-alias filter
+ if (fRate < 1.0f)
+ {
+ upsample(src, nSamples);
+ }
+ else
+ {
+ downsample(src, nSamples);
+ }
+}
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// Returns the number of samples returned in the "dest" buffer
+inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ if (numChannels == 2)
+ {
+ return transposeStereo(dest, src, nSamples);
+ }
+ else
+ {
+ return transposeMono(dest, src, nSamples);
+ }
+}
+
+
+// Sets the number of channels, 1 = mono, 2 = stereo
+void RateTransposer::setChannels(int nChannels)
+{
+ assert(nChannels > 0);
+ if (numChannels == nChannels) return;
+
+ assert(nChannels == 1 || nChannels == 2);
+ numChannels = nChannels;
+
+ storeBuffer.setChannels(numChannels);
+ tempBuffer.setChannels(numChannels);
+ outputBuffer.setChannels(numChannels);
+
+ // Inits the linear interpolation registers
+ resetRegisters();
+}
+
+
+// Clears all the samples in the object
+void RateTransposer::clear()
+{
+ outputBuffer.clear();
+ storeBuffer.clear();
+}
+
+
+// Returns nonzero if there aren't any samples available for outputting.
+int RateTransposer::isEmpty() const
+{
+ int res;
+
+ res = FIFOProcessor::isEmpty();
+ if (res == 0) return 0;
+ return storeBuffer.isEmpty();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// RateTransposerInteger - integer arithmetic implementation
+//
+
+/// fixed-point interpolation routine precision
+#define SCALE 65536
+
+// Constructor
+RateTransposerInteger::RateTransposerInteger() : RateTransposer()
+{
+ // Notice: use local function calling syntax for sake of clarity,
+ // to indicate the fact that C++ constructor can't call virtual functions.
+ RateTransposerInteger::resetRegisters();
+ RateTransposerInteger::setRate(1.0f);
+}
+
+
+RateTransposerInteger::~RateTransposerInteger()
+{
+}
+
+
+void RateTransposerInteger::resetRegisters()
+{
+ iSlopeCount = 0;
+ sPrevSampleL =
+ sPrevSampleR = 0;
+}
+
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Mono' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int i, used;
+ LONG_SAMPLETYPE temp, vol1;
+
+ if (nSamples == 0) return 0; // no samples, no work
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the previous call first...
+ while (iSlopeCount <= SCALE)
+ {
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
+ dest[i] = (SAMPLETYPE)(temp / SCALE);
+ i++;
+ iSlopeCount += iRate;
+ }
+ // now always (iSlopeCount > SCALE)
+ iSlopeCount -= SCALE;
+
+ while (1)
+ {
+ while (iSlopeCount > SCALE)
+ {
+ iSlopeCount -= SCALE;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = src[used] * vol1 + iSlopeCount * src[used + 1];
+ dest[i] = (SAMPLETYPE)(temp / SCALE);
+
+ i++;
+ iSlopeCount += iRate;
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[nSamples - 1];
+
+ return i;
+}
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Stereo' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int srcPos, i, used;
+ LONG_SAMPLETYPE temp, vol1;
+
+ if (nSamples == 0) return 0; // no samples, no work
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the sPrevSampleLious call first...
+ while (iSlopeCount <= SCALE)
+ {
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
+ dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
+ temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
+ dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
+ i++;
+ iSlopeCount += iRate;
+ }
+ // now always (iSlopeCount > SCALE)
+ iSlopeCount -= SCALE;
+
+ while (1)
+ {
+ while (iSlopeCount > SCALE)
+ {
+ iSlopeCount -= SCALE;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ srcPos = 2 * used;
+ vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
+ temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2];
+ dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
+ temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3];
+ dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
+
+ i++;
+ iSlopeCount += iRate;
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[2 * nSamples - 2];
+ sPrevSampleR = src[2 * nSamples - 1];
+
+ return i;
+}
+
+
+// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
+// iRate, larger faster iRates.
+void RateTransposerInteger::setRate(float newRate)
+{
+ iRate = (int)(newRate * SCALE + 0.5f);
+ RateTransposer::setRate(newRate);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// RateTransposerFloat - floating point arithmetic implementation
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// Constructor
+RateTransposerFloat::RateTransposerFloat() : RateTransposer()
+{
+ // Notice: use local function calling syntax for sake of clarity,
+ // to indicate the fact that C++ constructor can't call virtual functions.
+ RateTransposerFloat::resetRegisters();
+ RateTransposerFloat::setRate(1.0f);
+}
+
+
+RateTransposerFloat::~RateTransposerFloat()
+{
+}
+
+
+void RateTransposerFloat::resetRegisters()
+{
+ fSlopeCount = 0;
+ sPrevSampleL =
+ sPrevSampleR = 0;
+}
+
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Mono' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int i, used;
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the previous call first...
+ while (fSlopeCount <= 1.0f)
+ {
+ dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
+ i++;
+ fSlopeCount += fRate;
+ }
+ fSlopeCount -= 1.0f;
+
+ if (nSamples > 1)
+ {
+ while (1)
+ {
+ while (fSlopeCount > 1.0f)
+ {
+ fSlopeCount -= 1.0f;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
+ i++;
+ fSlopeCount += fRate;
+ }
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[nSamples - 1];
+
+ return i;
+}
+
+
+// Transposes the sample rate of the given samples using linear interpolation.
+// 'Mono' version of the routine. Returns the number of samples returned in
+// the "dest" buffer
+uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
+{
+ unsigned int srcPos, i, used;
+
+ if (nSamples == 0) return 0; // no samples, no work
+
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the sPrevSampleLious call first...
+ while (fSlopeCount <= 1.0f)
+ {
+ dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
+ dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]);
+ i++;
+ fSlopeCount += fRate;
+ }
+ // now always (iSlopeCount > 1.0f)
+ fSlopeCount -= 1.0f;
+
+ if (nSamples > 1)
+ {
+ while (1)
+ {
+ while (fSlopeCount > 1.0f)
+ {
+ fSlopeCount -= 1.0f;
+ used ++;
+ if (used >= nSamples - 1) goto end;
+ }
+ srcPos = 2 * used;
+
+ dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
+ + fSlopeCount * src[srcPos + 2]);
+ dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
+ + fSlopeCount * src[srcPos + 3]);
+
+ i++;
+ fSlopeCount += fRate;
+ }
+ }
+end:
+ // Store the last sample for the next round
+ sPrevSampleL = src[2 * nSamples - 2];
+ sPrevSampleR = src[2 * nSamples - 1];
+
+ return i;
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/RateTransposer.h b/plugins/soundtouch/soundtouch/source/SoundTouch/RateTransposer.h new file mode 100644 index 00000000..f035af2c --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/RateTransposer.h @@ -0,0 +1,159 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sample rate transposer. Changes sample rate by using linear interpolation
+/// together with anti-alias filtering (first order interpolation with anti-
+/// alias filtering should be quite adequate for this application).
+///
+/// Use either of the derived classes of 'RateTransposerInteger' or
+/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
+/// algorithm implementation.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: RateTransposer.h 63 2009-02-21 16:00:14Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef RateTransposer_H
+#define RateTransposer_H
+
+#include <stddef.h>
+#include "AAFilter.h"
+#include "FIFOSamplePipe.h"
+#include "FIFOSampleBuffer.h"
+
+#include "STTypes.h"
+
+namespace soundtouch
+{
+
+/// A common linear samplerate transposer class.
+///
+/// Note: Use function "RateTransposer::newInstance()" to create a new class
+/// instance instead of the "new" operator; that function automatically
+/// chooses a correct implementation depending on if integer or floating
+/// arithmetics are to be used.
+class RateTransposer : public FIFOProcessor
+{
+protected:
+ /// Anti-alias filter object
+ AAFilter *pAAFilter;
+
+ float fRate;
+
+ int numChannels;
+
+ /// Buffer for collecting samples to feed the anti-alias filter between
+ /// two batches
+ FIFOSampleBuffer storeBuffer;
+
+ /// Buffer for keeping samples between transposing & anti-alias filter
+ FIFOSampleBuffer tempBuffer;
+
+ /// Output sample buffer
+ FIFOSampleBuffer outputBuffer;
+
+ BOOL bUseAAFilter;
+
+ virtual void resetRegisters() = 0;
+
+ virtual uint transposeStereo(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples) = 0;
+ virtual uint transposeMono(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples) = 0;
+ inline uint transpose(SAMPLETYPE *dest,
+ const SAMPLETYPE *src,
+ uint numSamples);
+
+ void downsample(const SAMPLETYPE *src,
+ uint numSamples);
+ void upsample(const SAMPLETYPE *src,
+ uint numSamples);
+
+ /// Transposes sample rate by applying anti-alias filter to prevent folding.
+ /// Returns amount of samples returned in the "dest" buffer.
+ /// The maximum amount of samples that can be returned at a time is set by
+ /// the 'set_returnBuffer_size' function.
+ void processSamples(const SAMPLETYPE *src,
+ uint numSamples);
+
+
+public:
+ RateTransposer();
+ virtual ~RateTransposer();
+
+ /// Operator 'new' is overloaded so that it automatically creates a suitable instance
+ /// depending on if we're to use integer or floating point arithmetics.
+ static void *operator new(size_t s);
+
+ /// Use this function instead of "new" operator to create a new instance of this class.
+ /// This function automatically chooses a correct implementation, depending on if
+ /// integer ot floating point arithmetics are to be used.
+ static RateTransposer *newInstance();
+
+ /// Returns the output buffer object
+ FIFOSamplePipe *getOutput() { return &outputBuffer; };
+
+ /// Returns the store buffer object
+ FIFOSamplePipe *getStore() { return &storeBuffer; };
+
+ /// Return anti-alias filter object
+ AAFilter *getAAFilter();
+
+ /// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
+ void enableAAFilter(BOOL newMode);
+
+ /// Returns nonzero if anti-alias filter is enabled.
+ BOOL isAAFilterEnabled() const;
+
+ /// Sets new target rate. Normal rate = 1.0, smaller values represent slower
+ /// rate, larger faster rates.
+ virtual void setRate(float newRate);
+
+ /// Sets the number of channels, 1 = mono, 2 = stereo
+ void setChannels(int channels);
+
+ /// Adds 'numSamples' pcs of samples from the 'samples' memory position into
+ /// the input of the object.
+ void putSamples(const SAMPLETYPE *samples, uint numSamples);
+
+ /// Clears all the samples in the object
+ void clear();
+
+ /// Returns nonzero if there aren't any samples available for outputting.
+ int isEmpty() const;
+};
+
+}
+
+#endif
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/SoundTouch.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/SoundTouch.cpp new file mode 100644 index 00000000..aa7ac028 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/SoundTouch.cpp @@ -0,0 +1,480 @@ +//////////////////////////////////////////////////////////////////////////////
+///
+/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
+///
+/// Notes:
+/// - Initialize the SoundTouch object instance by setting up the sound stream
+/// parameters with functions 'setSampleRate' and 'setChannels', then set
+/// desired tempo/pitch/rate settings with the corresponding functions.
+///
+/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
+/// samples that are to be processed are fed into one of the pipe by calling
+/// function 'putSamples', while the ready processed samples can be read
+/// from the other end of the pipeline with function 'receiveSamples'.
+///
+/// - The SoundTouch processing classes require certain sized 'batches' of
+/// samples in order to process the sound. For this reason the classes buffer
+/// incoming samples until there are enough of samples available for
+/// processing, then they carry out the processing step and consequently
+/// make the processed samples available for outputting.
+///
+/// - For the above reason, the processing routines introduce a certain
+/// 'latency' between the input and output, so that the samples input to
+/// SoundTouch may not be immediately available in the output, and neither
+/// the amount of outputtable samples may not immediately be in direct
+/// relationship with the amount of previously input samples.
+///
+/// - The tempo/pitch/rate control parameters can be altered during processing.
+/// Please notice though that they aren't currently protected by semaphores,
+/// so in multi-thread application external semaphore protection may be
+/// required.
+///
+/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
+/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
+/// tempo and pitch in the same ratio) of the sound. The third available control
+/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
+/// combining the two other controls.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-05-19 07:57:30 +0300 (Tue, 19 May 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: SoundTouch.cpp 73 2009-05-19 04:57:30Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <assert.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#include <stdexcept>
+#include <stdio.h>
+
+#include "SoundTouch.h"
+#include "TDStretch.h"
+#include "RateTransposer.h"
+#include "cpu_detect.h"
+
+using namespace soundtouch;
+
+/// test if two floating point numbers are equal
+#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
+
+
+/// Print library version string for autoconf
+extern "C" void soundtouch_ac_test()
+{
+ printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
+}
+
+
+SoundTouch::SoundTouch()
+{
+ // Initialize rate transposer and tempo changer instances
+
+ pRateTransposer = RateTransposer::newInstance();
+ pTDStretch = TDStretch::newInstance();
+
+ setOutPipe(pTDStretch);
+
+ rate = tempo = 0;
+
+ virtualPitch =
+ virtualRate =
+ virtualTempo = 1.0;
+
+ calcEffectiveRateAndTempo();
+
+ channels = 0;
+ bSrateSet = FALSE;
+}
+
+
+
+SoundTouch::~SoundTouch()
+{
+ delete pRateTransposer;
+ delete pTDStretch;
+}
+
+
+
+/// Get SoundTouch library version string
+const char *SoundTouch::getVersionString()
+{
+ static const char *_version = SOUNDTOUCH_VERSION;
+
+ return _version;
+}
+
+
+/// Get SoundTouch library version Id
+uint SoundTouch::getVersionId()
+{
+ return SOUNDTOUCH_VERSION_ID;
+}
+
+
+// Sets the number of channels, 1 = mono, 2 = stereo
+void SoundTouch::setChannels(uint numChannels)
+{
+ if (numChannels != 1 && numChannels != 2)
+ {
+ throw std::runtime_error("Illegal number of channels");
+ }
+ channels = numChannels;
+ pRateTransposer->setChannels((int)numChannels);
+ pTDStretch->setChannels((int)numChannels);
+}
+
+
+
+// Sets new rate control value. Normal rate = 1.0, smaller values
+// represent slower rate, larger faster rates.
+void SoundTouch::setRate(float newRate)
+{
+ virtualRate = newRate;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new rate control value as a difference in percents compared
+// to the original rate (-50 .. +100 %)
+void SoundTouch::setRateChange(float newRate)
+{
+ virtualRate = 1.0f + 0.01f * newRate;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new tempo control value. Normal tempo = 1.0, smaller values
+// represent slower tempo, larger faster tempo.
+void SoundTouch::setTempo(float newTempo)
+{
+ virtualTempo = newTempo;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new tempo control value as a difference in percents compared
+// to the original tempo (-50 .. +100 %)
+void SoundTouch::setTempoChange(float newTempo)
+{
+ virtualTempo = 1.0f + 0.01f * newTempo;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets new pitch control value. Original pitch = 1.0, smaller values
+// represent lower pitches, larger values higher pitch.
+void SoundTouch::setPitch(float newPitch)
+{
+ virtualPitch = newPitch;
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets pitch change in octaves compared to the original pitch
+// (-1.00 .. +1.00)
+void SoundTouch::setPitchOctaves(float newPitch)
+{
+ virtualPitch = (float)exp(0.69314718056f * newPitch);
+ calcEffectiveRateAndTempo();
+}
+
+
+
+// Sets pitch change in semi-tones compared to the original pitch
+// (-12 .. +12)
+void SoundTouch::setPitchSemiTones(int newPitch)
+{
+ setPitchOctaves((float)newPitch / 12.0f);
+}
+
+
+
+void SoundTouch::setPitchSemiTones(float newPitch)
+{
+ setPitchOctaves(newPitch / 12.0f);
+}
+
+
+// Calculates 'effective' rate and tempo values from the
+// nominal control values.
+void SoundTouch::calcEffectiveRateAndTempo()
+{
+ float oldTempo = tempo;
+ float oldRate = rate;
+
+ tempo = virtualTempo / virtualPitch;
+ rate = virtualPitch * virtualRate;
+
+ if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
+ if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
+
+#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
+ if (rate <= 1.0f)
+ {
+ if (output != pTDStretch)
+ {
+ FIFOSamplePipe *tempoOut;
+
+ assert(output == pRateTransposer);
+ // move samples in the current output buffer to the output of pTDStretch
+ tempoOut = pTDStretch->getOutput();
+ tempoOut->moveSamples(*output);
+ // move samples in pitch transposer's store buffer to tempo changer's input
+ pTDStretch->moveSamples(*pRateTransposer->getStore());
+
+ output = pTDStretch;
+ }
+ }
+ else
+#endif
+ {
+ if (output != pRateTransposer)
+ {
+ FIFOSamplePipe *transOut;
+
+ assert(output == pTDStretch);
+ // move samples in the current output buffer to the output of pRateTransposer
+ transOut = pRateTransposer->getOutput();
+ transOut->moveSamples(*output);
+ // move samples in tempo changer's input to pitch transposer's input
+ pRateTransposer->moveSamples(*pTDStretch->getInput());
+
+ output = pRateTransposer;
+ }
+ }
+}
+
+
+// Sets sample rate.
+void SoundTouch::setSampleRate(uint srate)
+{
+ bSrateSet = TRUE;
+ // set sample rate, leave other tempo changer parameters as they are.
+ pTDStretch->setParameters((int)srate);
+}
+
+
+// Adds 'numSamples' pcs of samples from the 'samples' memory position into
+// the input of the object.
+void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ if (bSrateSet == FALSE)
+ {
+ throw std::runtime_error("SoundTouch : Sample rate not defined");
+ }
+ else if (channels == 0)
+ {
+ throw std::runtime_error("SoundTouch : Number of channels not defined");
+ }
+
+ // Transpose the rate of the new samples if necessary
+ /* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
+ if (rate == 1.0f)
+ {
+ // The rate value is same as the original, simply evaluate the tempo changer.
+ assert(output == pTDStretch);
+ if (pRateTransposer->isEmpty() == 0)
+ {
+ // yet flush the last samples in the pitch transposer buffer
+ // (may happen if 'rate' changes from a non-zero value to zero)
+ pTDStretch->moveSamples(*pRateTransposer);
+ }
+ pTDStretch->putSamples(samples, nSamples);
+ }
+ */
+#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
+ else if (rate <= 1.0f)
+ {
+ // transpose the rate down, output the transposed sound to tempo changer buffer
+ assert(output == pTDStretch);
+ pRateTransposer->putSamples(samples, nSamples);
+ pTDStretch->moveSamples(*pRateTransposer);
+ }
+ else
+#endif
+ {
+ // evaluate the tempo changer, then transpose the rate up,
+ assert(output == pRateTransposer);
+ pTDStretch->putSamples(samples, nSamples);
+ pRateTransposer->moveSamples(*pTDStretch);
+ }
+}
+
+
+// Flushes the last samples from the processing pipeline to the output.
+// Clears also the internal processing buffers.
+//
+// Note: This function is meant for extracting the last samples of a sound
+// stream. This function may introduce additional blank samples in the end
+// of the sound stream, and thus it's not recommended to call this function
+// in the middle of a sound stream.
+void SoundTouch::flush()
+{
+ int i;
+ uint nOut;
+ SAMPLETYPE buff[128];
+
+ nOut = numSamples();
+
+ memset(buff, 0, 128 * sizeof(SAMPLETYPE));
+ // "Push" the last active samples out from the processing pipeline by
+ // feeding blank samples into the processing pipeline until new,
+ // processed samples appear in the output (not however, more than
+ // 8ksamples in any case)
+ for (i = 0; i < 128; i ++)
+ {
+ putSamples(buff, 64);
+ if (numSamples() != nOut) break; // new samples have appeared in the output!
+ }
+
+ // Clear working buffers
+ pRateTransposer->clear();
+ pTDStretch->clearInput();
+ // yet leave the 'tempoChanger' output intouched as that's where the
+ // flushed samples are!
+}
+
+
+// Changes a setting controlling the processing system behaviour. See the
+// 'SETTING_...' defines for available setting ID's.
+BOOL SoundTouch::setSetting(int settingId, int value)
+{
+ int sampleRate, sequenceMs, seekWindowMs, overlapMs;
+
+ // read current tdstretch routine parameters
+ pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
+
+ switch (settingId)
+ {
+ case SETTING_USE_AA_FILTER :
+ // enables / disabless anti-alias filter
+ pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
+ return TRUE;
+
+ case SETTING_AA_FILTER_LENGTH :
+ // sets anti-alias filter length
+ pRateTransposer->getAAFilter()->setLength(value);
+ return TRUE;
+
+ case SETTING_USE_QUICKSEEK :
+ // enables / disables tempo routine quick seeking algorithm
+ pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
+ return TRUE;
+
+ case SETTING_SEQUENCE_MS:
+ // change time-stretch sequence duration parameter
+ pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
+ return TRUE;
+
+ case SETTING_SEEKWINDOW_MS:
+ // change time-stretch seek window length parameter
+ pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
+ return TRUE;
+
+ case SETTING_OVERLAP_MS:
+ // change time-stretch overlap length parameter
+ pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
+ return TRUE;
+
+ default :
+ return FALSE;
+ }
+}
+
+
+// Reads a setting controlling the processing system behaviour. See the
+// 'SETTING_...' defines for available setting ID's.
+//
+// Returns the setting value.
+int SoundTouch::getSetting(int settingId) const
+{
+ int temp;
+
+ switch (settingId)
+ {
+ case SETTING_USE_AA_FILTER :
+ return (uint)pRateTransposer->isAAFilterEnabled();
+
+ case SETTING_AA_FILTER_LENGTH :
+ return pRateTransposer->getAAFilter()->getLength();
+
+ case SETTING_USE_QUICKSEEK :
+ return (uint) pTDStretch->isQuickSeekEnabled();
+
+ case SETTING_SEQUENCE_MS:
+ pTDStretch->getParameters(NULL, &temp, NULL, NULL);
+ return temp;
+
+ case SETTING_SEEKWINDOW_MS:
+ pTDStretch->getParameters(NULL, NULL, &temp, NULL);
+ return temp;
+
+ case SETTING_OVERLAP_MS:
+ pTDStretch->getParameters(NULL, NULL, NULL, &temp);
+ return temp;
+
+ default :
+ return 0;
+ }
+}
+
+
+// Clears all the samples in the object's output and internal processing
+// buffers.
+void SoundTouch::clear()
+{
+ pRateTransposer->clear();
+ pTDStretch->clear();
+}
+
+
+
+/// Returns number of samples currently unprocessed.
+uint SoundTouch::numUnprocessedSamples() const
+{
+ FIFOSamplePipe * psp;
+ if (pTDStretch)
+ {
+ psp = pTDStretch->getInput();
+ if (psp)
+ {
+ return psp->numSamples();
+ }
+ }
+ return 0;
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/TDStretch.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/TDStretch.cpp new file mode 100644 index 00000000..232133b5 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/TDStretch.cpp @@ -0,0 +1,1045 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
+/// while maintaining the original pitch by using a time domain WSOLA-like
+/// method with several performance-increasing tweaks.
+///
+/// Note : MMX optimized functions reside in a separate, platform-specific
+/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-12-28 21:27:04 +0200 (Mon, 28 Dec 2009) $
+// File revision : $Revision: 1.12 $
+//
+// $Id: TDStretch.cpp 77 2009-12-28 19:27:04Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <stdexcept>
+
+#include "STTypes.h"
+#include "cpu_detect.h"
+#include "TDStretch.h"
+
+#include <stdio.h>
+
+using namespace soundtouch;
+
+#define max(x, y) (((x) > (y)) ? (x) : (y))
+
+
+/*****************************************************************************
+ *
+ * Constant definitions
+ *
+ *****************************************************************************/
+
+// Table for the hierarchical mixing position seeking algorithm
+static const short _scanOffsets[5][24]={
+ { 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
+ 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
+ {-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ { -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ { -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
+ 116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
+
+/*****************************************************************************
+ *
+ * Implementation of the class 'TDStretch'
+ *
+ *****************************************************************************/
+
+
+TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
+{
+ bQuickSeek = FALSE;
+ channels = 2;
+
+ pMidBuffer = NULL;
+ pRefMidBufferUnaligned = NULL;
+ overlapLength = 0;
+
+ bAutoSeqSetting = TRUE;
+ bAutoSeekSetting = TRUE;
+
+// outDebt = 0;
+ skipFract = 0;
+
+ tempo = 1.0f;
+ setParameters(44100, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
+ setTempo(1.0f);
+
+ clear();
+}
+
+
+
+TDStretch::~TDStretch()
+{
+ delete[] pMidBuffer;
+ delete[] pRefMidBufferUnaligned;
+}
+
+
+
+// Sets routine control parameters. These control are certain time constants
+// defining how the sound is stretched to the desired duration.
+//
+// 'sampleRate' = sample rate of the sound
+// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
+// 'seekwindowMS' = seeking window length for scanning the best overlapping
+// position (default = 28 ms)
+// 'overlapMS' = overlapping length (default = 12 ms)
+
+void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
+ int aSeekWindowMS, int aOverlapMS)
+{
+ // accept only positive parameter values - if zero or negative, use old values instead
+ if (aSampleRate > 0) this->sampleRate = aSampleRate;
+ if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
+
+ if (aSequenceMS > 0)
+ {
+ this->sequenceMs = aSequenceMS;
+ bAutoSeqSetting = FALSE;
+ }
+ else if (aSequenceMS == 0)
+ {
+ // if zero, use automatic setting
+ bAutoSeqSetting = TRUE;
+ }
+
+ if (aSeekWindowMS > 0)
+ {
+ this->seekWindowMs = aSeekWindowMS;
+ bAutoSeekSetting = FALSE;
+ }
+ else if (aSeekWindowMS == 0)
+ {
+ // if zero, use automatic setting
+ bAutoSeekSetting = TRUE;
+ }
+
+ calcSeqParameters();
+
+ calculateOverlapLength(overlapMs);
+
+ // set tempo to recalculate 'sampleReq'
+ setTempo(tempo);
+
+}
+
+
+
+/// Get routine control parameters, see setParameters() function.
+/// Any of the parameters to this function can be NULL, in such case corresponding parameter
+/// value isn't returned.
+void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
+{
+ if (pSampleRate)
+ {
+ *pSampleRate = sampleRate;
+ }
+
+ if (pSequenceMs)
+ {
+ *pSequenceMs = (bAutoSeqSetting) ? (USE_AUTO_SEQUENCE_LEN) : sequenceMs;
+ }
+
+ if (pSeekWindowMs)
+ {
+ *pSeekWindowMs = (bAutoSeekSetting) ? (USE_AUTO_SEEKWINDOW_LEN) : seekWindowMs;
+ }
+
+ if (pOverlapMs)
+ {
+ *pOverlapMs = overlapMs;
+ }
+}
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'pInput'
+void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
+{
+ int i, itemp;
+
+ for (i = 0; i < overlapLength ; i ++)
+ {
+ itemp = overlapLength - i;
+ pOutput[i] = (pInput[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
+ }
+}
+
+
+
+void TDStretch::clearMidBuffer()
+{
+ memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
+}
+
+
+void TDStretch::clearInput()
+{
+ inputBuffer.clear();
+ clearMidBuffer();
+}
+
+
+// Clears the sample buffers
+void TDStretch::clear()
+{
+ outputBuffer.clear();
+ clearInput();
+}
+
+
+
+// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
+// to enable
+void TDStretch::enableQuickSeek(BOOL enable)
+{
+ bQuickSeek = enable;
+}
+
+
+// Returns nonzero if the quick seeking algorithm is enabled.
+BOOL TDStretch::isQuickSeekEnabled() const
+{
+ return bQuickSeek;
+}
+
+
+// Seeks for the optimal overlap-mixing position.
+int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
+{
+ if (channels == 2)
+ {
+ // stereo sound
+ if (bQuickSeek)
+ {
+ return seekBestOverlapPositionStereoQuick(refPos);
+ }
+ else
+ {
+ return seekBestOverlapPositionStereo(refPos);
+ }
+ }
+ else
+ {
+ // mono sound
+ if (bQuickSeek)
+ {
+ return seekBestOverlapPositionMonoQuick(refPos);
+ }
+ else
+ {
+ return seekBestOverlapPositionMono(refPos);
+ }
+ }
+}
+
+
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
+// of 'ovlPos'.
+inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
+{
+ if (channels == 2)
+ {
+ // stereo sound
+ overlapStereo(pOutput, pInput + 2 * ovlPos);
+ } else {
+ // mono sound.
+ overlapMono(pOutput, pInput + ovlPos);
+ }
+}
+
+
+
+
+// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
+{
+ int bestOffs;
+ double bestCorr, corr;
+ int i;
+
+ // Slopes the amplitudes of the 'midBuffer' samples
+ precalcCorrReferenceStereo();
+
+ bestCorr = FLT_MIN;
+ bestOffs = 0;
+
+ // Scans for the best correlation value by testing each possible position
+ // over the permitted range.
+ for (i = 0; i < seekLength; i ++)
+ {
+ // Calculates correlation value for the mixing position corresponding
+ // to 'i'
+ corr = (double)calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * i - seekLength) / (double)seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = i;
+ }
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
+{
+ int j;
+ int bestOffs;
+ double bestCorr, corr;
+ int scanCount, corrOffset, tempOffset;
+
+ // Slopes the amplitude of the 'midBuffer' samples
+ precalcCorrReferenceStereo();
+
+ bestCorr = FLT_MIN;
+ bestOffs = _scanOffsets[0][0];
+ corrOffset = 0;
+ tempOffset = 0;
+
+ // Scans for the best correlation value using four-pass hierarchical search.
+ //
+ // The look-up table 'scans' has hierarchical position adjusting steps.
+ // In first pass the routine searhes for the highest correlation with
+ // relatively coarse steps, then rescans the neighbourhood of the highest
+ // correlation with better resolution and so on.
+ for (scanCount = 0;scanCount < 4; scanCount ++)
+ {
+ j = 0;
+ while (_scanOffsets[scanCount][j])
+ {
+ tempOffset = corrOffset + _scanOffsets[scanCount][j];
+ if (tempOffset >= seekLength) break;
+
+ // Calculates correlation value for the mixing position corresponding
+ // to 'tempOffset'
+ corr = (double)calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = tempOffset;
+ }
+ j ++;
+ }
+ corrOffset = bestOffs;
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+
+// Seeks for the optimal overlap-mixing position. The 'mono' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
+{
+ int bestOffs;
+ double bestCorr, corr;
+ int tempOffset;
+ const SAMPLETYPE *compare;
+
+ // Slopes the amplitude of the 'midBuffer' samples
+ precalcCorrReferenceMono();
+
+ bestCorr = FLT_MIN;
+ bestOffs = 0;
+
+ // Scans for the best correlation value by testing each possible position
+ // over the permitted range.
+ for (tempOffset = 0; tempOffset < seekLength; tempOffset ++)
+ {
+ compare = refPos + tempOffset;
+
+ // Calculates correlation value for the mixing position corresponding
+ // to 'tempOffset'
+ corr = (double)calcCrossCorrMono(pRefMidBuffer, compare);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = tempOffset;
+ }
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+// Seeks for the optimal overlap-mixing position. The 'mono' version of the
+// routine
+//
+// The best position is determined as the position where the two overlapped
+// sample sequences are 'most alike', in terms of the highest cross-correlation
+// value over the overlapping period
+int TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
+{
+ int j;
+ int bestOffs;
+ double bestCorr, corr;
+ int scanCount, corrOffset, tempOffset;
+
+ // Slopes the amplitude of the 'midBuffer' samples
+ precalcCorrReferenceMono();
+
+ bestCorr = FLT_MIN;
+ bestOffs = _scanOffsets[0][0];
+ corrOffset = 0;
+ tempOffset = 0;
+
+ // Scans for the best correlation value using four-pass hierarchical search.
+ //
+ // The look-up table 'scans' has hierarchical position adjusting steps.
+ // In first pass the routine searhes for the highest correlation with
+ // relatively coarse steps, then rescans the neighbourhood of the highest
+ // correlation with better resolution and so on.
+ for (scanCount = 0;scanCount < 4; scanCount ++)
+ {
+ j = 0;
+ while (_scanOffsets[scanCount][j])
+ {
+ tempOffset = corrOffset + _scanOffsets[scanCount][j];
+ if (tempOffset >= seekLength) break;
+
+ // Calculates correlation value for the mixing position corresponding
+ // to 'tempOffset'
+ corr = (double)calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
+ // heuristic rule to slightly favour values close to mid of the range
+ double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
+ corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
+
+ // Checks for the highest correlation value
+ if (corr > bestCorr)
+ {
+ bestCorr = corr;
+ bestOffs = tempOffset;
+ }
+ j ++;
+ }
+ corrOffset = bestOffs;
+ }
+ // clear cross correlation routine state if necessary (is so e.g. in MMX routines).
+ clearCrossCorrState();
+
+ return bestOffs;
+}
+
+
+/// clear cross correlation routine state if necessary
+void TDStretch::clearCrossCorrState()
+{
+ // default implementation is empty.
+}
+
+
+/// Calculates processing sequence length according to tempo setting
+void TDStretch::calcSeqParameters()
+{
+ // Adjust tempo param according to tempo, so that variating processing sequence length is used
+ // at varius tempo settings, between the given low...top limits
+ #define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
+ #define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
+
+ // sequence-ms setting values at above low & top tempo
+ #define AUTOSEQ_AT_MIN 125.0
+ #define AUTOSEQ_AT_MAX 50.0
+ #define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
+ #define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
+
+ // seek-window-ms setting values at above low & top tempo
+ #define AUTOSEEK_AT_MIN 25.0
+ #define AUTOSEEK_AT_MAX 15.0
+ #define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
+ #define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
+
+ #define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
+
+ double seq, seek;
+
+ if (bAutoSeqSetting)
+ {
+ seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
+ seq = CHECK_LIMITS(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN);
+ sequenceMs = (int)(seq + 0.5);
+ }
+
+ if (bAutoSeekSetting)
+ {
+ seek = AUTOSEEK_C + AUTOSEEK_K * tempo;
+ seek = CHECK_LIMITS(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN);
+ seekWindowMs = (int)(seek + 0.5);
+ }
+
+ // Update seek window lengths
+ seekWindowLength = (sampleRate * sequenceMs) / 1000;
+ if (seekWindowLength < 2 * overlapLength)
+ {
+ seekWindowLength = 2 * overlapLength;
+ }
+ seekLength = (sampleRate * seekWindowMs) / 1000;
+}
+
+
+
+// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
+// tempo, larger faster tempo.
+void TDStretch::setTempo(float newTempo)
+{
+ int intskip;
+
+ tempo = newTempo;
+
+ // Calculate new sequence duration
+ calcSeqParameters();
+
+ // Calculate ideal skip length (according to tempo value)
+ nominalSkip = tempo * (seekWindowLength - overlapLength);
+ intskip = (int)(nominalSkip + 0.5f);
+
+ // Calculate how many samples are needed in the 'inputBuffer' to
+ // process another batch of samples
+ //sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
+ sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
+}
+
+
+
+// Sets the number of channels, 1 = mono, 2 = stereo
+void TDStretch::setChannels(int numChannels)
+{
+ assert(numChannels > 0);
+ if (channels == numChannels) return;
+ assert(numChannels == 1 || numChannels == 2);
+
+ channels = numChannels;
+ inputBuffer.setChannels(channels);
+ outputBuffer.setChannels(channels);
+}
+
+
+// nominal tempo, no need for processing, just pass the samples through
+// to outputBuffer
+/*
+void TDStretch::processNominalTempo()
+{
+ assert(tempo == 1.0f);
+
+ if (bMidBufferDirty)
+ {
+ // If there are samples in pMidBuffer waiting for overlapping,
+ // do a single sliding overlapping with them in order to prevent a
+ // clicking distortion in the output sound
+ if (inputBuffer.numSamples() < overlapLength)
+ {
+ // wait until we've got overlapLength input samples
+ return;
+ }
+ // Mix the samples in the beginning of 'inputBuffer' with the
+ // samples in 'midBuffer' using sliding overlapping
+ overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
+ outputBuffer.putSamples(overlapLength);
+ inputBuffer.receiveSamples(overlapLength);
+ clearMidBuffer();
+ // now we've caught the nominal sample flow and may switch to
+ // bypass mode
+ }
+
+ // Simply bypass samples from input to output
+ outputBuffer.moveSamples(inputBuffer);
+}
+*/
+
+#include <stdio.h>
+
+// Processes as many processing frames of the samples 'inputBuffer', store
+// the result into 'outputBuffer'
+void TDStretch::processSamples()
+{
+ int ovlSkip, offset;
+ int temp;
+
+ /* Removed this small optimization - can introduce a click to sound when tempo setting
+ crosses the nominal value
+ if (tempo == 1.0f)
+ {
+ // tempo not changed from the original, so bypass the processing
+ processNominalTempo();
+ return;
+ }
+ */
+
+ // Process samples as long as there are enough samples in 'inputBuffer'
+ // to form a processing frame.
+// while ((int)inputBuffer.numSamples() >= sampleReq - (outDebt / 4))
+ while ((int)inputBuffer.numSamples() >= sampleReq)
+ {
+ // If tempo differs from the normal ('SCALE'), scan for the best overlapping
+ // position
+ offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
+
+ // Mix the samples in the 'inputBuffer' at position of 'offset' with the
+ // samples in 'midBuffer' using sliding overlapping
+ // ... first partially overlap with the end of the previous sequence
+ // (that's in 'midBuffer')
+ overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
+ outputBuffer.putSamples((uint)overlapLength);
+
+ // ... then copy sequence samples from 'inputBuffer' to output:
+ temp = (seekLength / 2 - offset);
+
+ // compensate cumulated output length diff vs. ideal output
+// temp -= outDebt / 4;
+
+ // update ideal vs. true output difference
+// outDebt += temp;
+
+ // length of sequence
+// temp += (seekWindowLength - 2 * overlapLength);
+ temp = (seekWindowLength - 2 * overlapLength);
+
+ // crosscheck that we don't have buffer overflow...
+ if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
+ {
+ continue; // just in case, shouldn't really happen
+ }
+
+ outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
+
+ // Copies the end of the current sequence from 'inputBuffer' to
+ // 'midBuffer' for being mixed with the beginning of the next
+ // processing sequence and so on
+ assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
+ memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
+ channels * sizeof(SAMPLETYPE) * overlapLength);
+
+ // Remove the processed samples from the input buffer. Update
+ // the difference between integer & nominal skip step to 'skipFract'
+ // in order to prevent the error from accumulating over time.
+ skipFract += nominalSkip; // real skip size
+ ovlSkip = (int)skipFract; // rounded to integer skip
+ skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
+ inputBuffer.receiveSamples((uint)ovlSkip);
+ }
+}
+
+
+// Adds 'numsamples' pcs of samples from the 'samples' memory position into
+// the input of the object.
+void TDStretch::putSamples(const SAMPLETYPE *samples, uint nSamples)
+{
+ // Add the samples into the input buffer
+ inputBuffer.putSamples(samples, nSamples);
+ // Process the samples in input buffer
+ processSamples();
+}
+
+
+
+/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
+void TDStretch::acceptNewOverlapLength(int newOverlapLength)
+{
+ int prevOvl;
+
+ assert(newOverlapLength >= 0);
+ prevOvl = overlapLength;
+ overlapLength = newOverlapLength;
+
+ if (overlapLength > prevOvl)
+ {
+ delete[] pMidBuffer;
+ delete[] pRefMidBufferUnaligned;
+
+ pMidBuffer = new SAMPLETYPE[overlapLength * 2];
+ clearMidBuffer();
+
+ pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
+ // ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
+ pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & (ulong)-16);
+ }
+}
+
+
+// Operator 'new' is overloaded so that it automatically creates a suitable instance
+// depending on if we've a MMX/SSE/etc-capable CPU available or not.
+void * TDStretch::operator new(size_t s)
+{
+ // Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
+ throw std::runtime_error("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
+ return NULL;
+}
+
+
+TDStretch * TDStretch::newInstance()
+{
+ uint uExtensions;
+
+ uExtensions = detectCPUextensions();
+
+ // Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
+
+#ifdef ALLOW_MMX
+ // MMX routines available only with integer sample types
+ if (uExtensions & SUPPORT_MMX)
+ {
+ return ::new TDStretchMMX;
+ }
+ else
+#endif // ALLOW_MMX
+
+
+#ifdef ALLOW_SSE
+ if (uExtensions & SUPPORT_SSE)
+ {
+ // SSE support
+ return ::new TDStretchSSE;
+ }
+ else
+#endif // ALLOW_SSE
+
+
+#ifdef ALLOW_3DNOW
+ if (uExtensions & SUPPORT_3DNOW)
+ {
+ // 3DNow! support
+ return ::new TDStretch3DNow;
+ }
+ else
+#endif // ALLOW_3DNOW
+
+ {
+ // ISA optimizations not supported, use plain C version
+ return ::new TDStretch;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Integer arithmetics specific algorithm implementations.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef INTEGER_SAMPLES
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceStereo()
+{
+ int i, cnt2;
+ int temp, temp2;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = i * (overlapLength - i);
+ cnt2 = i * 2;
+
+ temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider;
+ pRefMidBuffer[cnt2] = (short)(temp2);
+ temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider;
+ pRefMidBuffer[cnt2 + 1] = (short)(temp2);
+ }
+}
+
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceMono()
+{
+ int i;
+ long temp;
+ long temp2;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = i * (overlapLength - i);
+ temp2 = (pMidBuffer[i] * temp) / slopingDivider;
+ pRefMidBuffer[i] = (short)temp2;
+ }
+}
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
+// version of the routine.
+void TDStretch::overlapStereo(short *poutput, const short *input) const
+{
+ int i;
+ short temp;
+ int cnt2;
+
+ for (i = 0; i < overlapLength ; i ++)
+ {
+ temp = (short)(overlapLength - i);
+ cnt2 = 2 * i;
+ poutput[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
+ poutput[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
+ }
+}
+
+// Calculates the x having the closest 2^x value for the given value
+static int _getClosest2Power(double value)
+{
+ return (int)(log(value) / log(2.0) + 0.5);
+}
+
+
+/// Calculates overlap period length in samples.
+/// Integer version rounds overlap length to closest power of 2
+/// for a divide scaling operation.
+void TDStretch::calculateOverlapLength(int aoverlapMs)
+{
+ int newOvl;
+
+ assert(aoverlapMs >= 0);
+
+ // calculate overlap length so that it's power of 2 - thus it's easy to do
+ // integer division by right-shifting. Term "-1" at end is to account for
+ // the extra most significatnt bit left unused in result by signed multiplication
+ overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
+ if (overlapDividerBits > 9) overlapDividerBits = 9;
+ if (overlapDividerBits < 3) overlapDividerBits = 3;
+ newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
+
+ acceptNewOverlapLength(newOvl);
+
+ // calculate sloping divider so that crosscorrelation operation won't
+ // overflow 32-bit register. Max. sum of the crosscorrelation sum without
+ // divider would be 2^30*(N^3-N)/3, where N = overlap length
+ slopingDivider = (newOvl * newOvl - 1) / 3;
+}
+
+
+long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
+{
+ long corr;
+ long norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 1; i < overlapLength; i ++)
+ {
+ corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
+ norm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
+ }
+
+ // Normalize result by dividing by sqrt(norm) - this step is easiest
+ // done using floating point operation
+ if (norm == 0) norm = 1; // to avoid div by zero
+ return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
+}
+
+
+long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
+{
+ long corr;
+ long norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 2; i < 2 * overlapLength; i += 2)
+ {
+ corr += (mixingPos[i] * compare[i] +
+ mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
+ norm += (mixingPos[i] * mixingPos[i] + mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits;
+ }
+
+ // Normalize result by dividing by sqrt(norm) - this step is easiest
+ // done using floating point operation
+ if (norm == 0) norm = 1; // to avoid div by zero
+ return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
+}
+
+#endif // INTEGER_SAMPLES
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Floating point arithmetics specific algorithm implementations.
+//
+
+#ifdef FLOAT_SAMPLES
+
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceStereo()
+{
+ int i, cnt2;
+ float temp;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = (float)i * (float)(overlapLength - i);
+ cnt2 = i * 2;
+ pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp);
+ pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp);
+ }
+}
+
+
+// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
+// is faster to calculate
+void TDStretch::precalcCorrReferenceMono()
+{
+ int i;
+ float temp;
+
+ for (i=0 ; i < (int)overlapLength ;i ++)
+ {
+ temp = (float)i * (float)(overlapLength - i);
+ pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp);
+ }
+}
+
+
+// Overlaps samples in 'midBuffer' with the samples in 'pInput'
+void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
+{
+ int i;
+ int cnt2;
+ float fTemp;
+ float fScale;
+ float fi;
+
+ fScale = 1.0f / (float)overlapLength;
+
+ for (i = 0; i < (int)overlapLength ; i ++)
+ {
+ fTemp = (float)(overlapLength - i) * fScale;
+ fi = (float)i * fScale;
+ cnt2 = 2 * i;
+ pOutput[cnt2 + 0] = pInput[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
+ pOutput[cnt2 + 1] = pInput[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
+ }
+}
+
+
+/// Calculates overlapInMsec period length in samples.
+void TDStretch::calculateOverlapLength(int overlapInMsec)
+{
+ int newOvl;
+
+ assert(overlapInMsec >= 0);
+ newOvl = (sampleRate * overlapInMsec) / 1000;
+ if (newOvl < 16) newOvl = 16;
+
+ // must be divisible by 8
+ newOvl -= newOvl % 8;
+
+ acceptNewOverlapLength(newOvl);
+}
+
+
+
+double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
+{
+ double corr;
+ double norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 1; i < overlapLength; i ++)
+ {
+ corr += mixingPos[i] * compare[i];
+ norm += mixingPos[i] * mixingPos[i];
+ }
+
+ if (norm < 1e-9) norm = 1.0; // to avoid div by zero
+ return corr / sqrt(norm);
+}
+
+
+double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
+{
+ double corr;
+ double norm;
+ int i;
+
+ corr = norm = 0;
+ for (i = 2; i < 2 * overlapLength; i += 2)
+ {
+ corr += mixingPos[i] * compare[i] +
+ mixingPos[i + 1] * compare[i + 1];
+ norm += mixingPos[i] * mixingPos[i] +
+ mixingPos[i + 1] * mixingPos[i + 1];
+ }
+
+ if (norm < 1e-9) norm = 1.0; // to avoid div by zero
+ return corr / sqrt(norm);
+}
+
+#endif // FLOAT_SAMPLES
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/TDStretch.h b/plugins/soundtouch/soundtouch/source/SoundTouch/TDStretch.h new file mode 100644 index 00000000..00d1f3e3 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/TDStretch.h @@ -0,0 +1,275 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
+/// while maintaining the original pitch by using a time domain WSOLA-like method
+/// with several performance-increasing tweaks.
+///
+/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
+/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-05-17 14:35:13 +0300 (Sun, 17 May 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: TDStretch.h 71 2009-05-17 11:35:13Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef TDStretch_H
+#define TDStretch_H
+
+#include <stddef.h>
+#include "STTypes.h"
+#include "RateTransposer.h"
+#include "FIFOSamplePipe.h"
+
+namespace soundtouch
+{
+
+/// Default values for sound processing parameters:
+/// Notice that the default parameters are tuned for contemporary popular music
+/// processing. For speech processing applications these parameters suit better:
+/// #define DEFAULT_SEQUENCE_MS 40
+/// #define DEFAULT_SEEKWINDOW_MS 15
+/// #define DEFAULT_OVERLAP_MS 8
+///
+
+/// Default length of a single processing sequence, in milliseconds. This determines to how
+/// long sequences the original sound is chopped in the time-stretch algorithm.
+///
+/// The larger this value is, the lesser sequences are used in processing. In principle
+/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
+/// and vice versa.
+///
+/// Increasing this value reduces computational burden & vice versa.
+//#define DEFAULT_SEQUENCE_MS 40
+#define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN
+
+/// Giving this value for the sequence length sets automatic parameter value
+/// according to tempo setting (recommended)
+#define USE_AUTO_SEQUENCE_LEN 0
+
+/// Seeking window default length in milliseconds for algorithm that finds the best possible
+/// overlapping location. This determines from how wide window the algorithm may look for an
+/// optimal joining location when mixing the sound sequences back together.
+///
+/// The bigger this window setting is, the higher the possibility to find a better mixing
+/// position will become, but at the same time large values may cause a "drifting" artifact
+/// because consequent sequences will be taken at more uneven intervals.
+///
+/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
+/// around, try reducing this setting.
+///
+/// Increasing this value increases computational burden & vice versa.
+//#define DEFAULT_SEEKWINDOW_MS 15
+#define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN
+
+/// Giving this value for the seek window length sets automatic parameter value
+/// according to tempo setting (recommended)
+#define USE_AUTO_SEEKWINDOW_LEN 0
+
+/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
+/// to form a continuous sound stream, this parameter defines over how long period the two
+/// consecutive sequences are let to overlap each other.
+///
+/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
+/// by a large amount, you might wish to try a smaller value on this.
+///
+/// Increasing this value increases computational burden & vice versa.
+#define DEFAULT_OVERLAP_MS 8
+
+
+/// Class that does the time-stretch (tempo change) effect for the processed
+/// sound.
+class TDStretch : public FIFOProcessor
+{
+protected:
+ int channels;
+ int sampleReq;
+ float tempo;
+
+ SAMPLETYPE *pMidBuffer;
+ SAMPLETYPE *pRefMidBuffer;
+ SAMPLETYPE *pRefMidBufferUnaligned;
+ int overlapLength;
+ int seekLength;
+ int seekWindowLength;
+ int overlapDividerBits;
+ int slopingDivider;
+ float nominalSkip;
+ float skipFract;
+ FIFOSampleBuffer outputBuffer;
+ FIFOSampleBuffer inputBuffer;
+ BOOL bQuickSeek;
+// int outDebt;
+// BOOL bMidBufferDirty;
+
+ int sampleRate;
+ int sequenceMs;
+ int seekWindowMs;
+ int overlapMs;
+ BOOL bAutoSeqSetting;
+ BOOL bAutoSeekSetting;
+
+ void acceptNewOverlapLength(int newOverlapLength);
+
+ virtual void clearCrossCorrState();
+ void calculateOverlapLength(int overlapMs);
+
+ virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
+ virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
+
+ virtual int seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
+ virtual int seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
+ virtual int seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
+ virtual int seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
+ int seekBestOverlapPosition(const SAMPLETYPE *refPos);
+
+ virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
+ virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
+
+ void clearMidBuffer();
+ void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
+
+ void precalcCorrReferenceMono();
+ void precalcCorrReferenceStereo();
+
+ void calcSeqParameters();
+
+ /// Changes the tempo of the given sound samples.
+ /// Returns amount of samples returned in the "output" buffer.
+ /// The maximum amount of samples that can be returned at a time is set by
+ /// the 'set_returnBuffer_size' function.
+ void processSamples();
+
+public:
+ TDStretch();
+ virtual ~TDStretch();
+
+ /// Operator 'new' is overloaded so that it automatically creates a suitable instance
+ /// depending on if we've a MMX/SSE/etc-capable CPU available or not.
+ static void *operator new(size_t s);
+
+ /// Use this function instead of "new" operator to create a new instance of this class.
+ /// This function automatically chooses a correct feature set depending on if the CPU
+ /// supports MMX/SSE/etc extensions.
+ static TDStretch *newInstance();
+
+ /// Returns the output buffer object
+ FIFOSamplePipe *getOutput() { return &outputBuffer; };
+
+ /// Returns the input buffer object
+ FIFOSamplePipe *getInput() { return &inputBuffer; };
+
+ /// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
+ /// tempo, larger faster tempo.
+ void setTempo(float newTempo);
+
+ /// Returns nonzero if there aren't any samples available for outputting.
+ virtual void clear();
+
+ /// Clears the input buffer
+ void clearInput();
+
+ /// Sets the number of channels, 1 = mono, 2 = stereo
+ void setChannels(int numChannels);
+
+ /// Enables/disables the quick position seeking algorithm. Zero to disable,
+ /// nonzero to enable
+ void enableQuickSeek(BOOL enable);
+
+ /// Returns nonzero if the quick seeking algorithm is enabled.
+ BOOL isQuickSeekEnabled() const;
+
+ /// Sets routine control parameters. These control are certain time constants
+ /// defining how the sound is stretched to the desired duration.
+ //
+ /// 'sampleRate' = sample rate of the sound
+ /// 'sequenceMS' = one processing sequence length in milliseconds
+ /// 'seekwindowMS' = seeking window length for scanning the best overlapping
+ /// position
+ /// 'overlapMS' = overlapping length
+ void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
+ int sequenceMS = -1, ///< Single processing sequence length (ms)
+ int seekwindowMS = -1, ///< Offset seeking window length (ms)
+ int overlapMS = -1 ///< Sequence overlapping length (ms)
+ );
+
+ /// Get routine control parameters, see setParameters() function.
+ /// Any of the parameters to this function can be NULL, in such case corresponding parameter
+ /// value isn't returned.
+ void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
+
+ /// Adds 'numsamples' pcs of samples from the 'samples' memory position into
+ /// the input of the object.
+ virtual void putSamples(
+ const SAMPLETYPE *samples, ///< Input sample data
+ uint numSamples ///< Number of samples in 'samples' so that one sample
+ ///< contains both channels if stereo
+ );
+};
+
+
+
+// Implementation-specific class declarations:
+
+#ifdef ALLOW_MMX
+ /// Class that implements MMX optimized routines for 16bit integer samples type.
+ class TDStretchMMX : public TDStretch
+ {
+ protected:
+ long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
+ virtual void overlapStereo(short *output, const short *input) const;
+ virtual void clearCrossCorrState();
+ };
+#endif /// ALLOW_MMX
+
+
+#ifdef ALLOW_3DNOW
+ /// Class that implements 3DNow! optimized routines for floating point samples type.
+ class TDStretch3DNow : public TDStretch
+ {
+ protected:
+ double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
+ };
+#endif /// ALLOW_3DNOW
+
+
+#ifdef ALLOW_SSE
+ /// Class that implements SSE optimized routines for floating point samples type.
+ class TDStretchSSE : public TDStretch
+ {
+ protected:
+ double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
+ };
+
+#endif /// ALLOW_SSE
+
+}
+#endif /// TDStretch_H
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect.h b/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect.h new file mode 100644 index 00000000..025781da --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect.h @@ -0,0 +1,62 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// A header file for detecting the Intel MMX instructions set extension.
+///
+/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
+/// routine implementations for x86 Windows, x86 gnu version and non-x86
+/// platforms, respectively.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
+// File revision : $Revision: 4 $
+//
+// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _CPU_DETECT_H_
+#define _CPU_DETECT_H_
+
+#include "STTypes.h"
+
+#define SUPPORT_MMX 0x0001
+#define SUPPORT_3DNOW 0x0002
+#define SUPPORT_ALTIVEC 0x0004
+#define SUPPORT_SSE 0x0008
+#define SUPPORT_SSE2 0x0010
+
+/// Checks which instruction set extensions are supported by the CPU.
+///
+/// \return A bitmask of supported extensions, see SUPPORT_... defines.
+uint detectCPUextensions(void);
+
+/// Disables given set of instruction extensions. See SUPPORT_... defines.
+void disableExtensions(uint wDisableMask);
+
+#endif // _CPU_DETECT_H_
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp new file mode 100644 index 00000000..b0d0a693 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_gcc.cpp @@ -0,0 +1,135 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Generic version of the x86 CPU extension detection routine.
+///
+/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
+/// for the Microsoft compiler version.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: cpu_detect_x86_gcc.cpp 67 2009-02-25 17:13:51Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include <stdexcept>
+#include <string>
+#include "cpu_detect.h"
+#include "STTypes.h"
+
+using namespace std;
+
+#include <stdio.h>
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// processor instructions extension detection routines
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// Flag variable indicating whick ISA extensions are disabled (for debugging)
+static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
+
+// Disables given set of instruction extensions. See SUPPORT_... defines.
+void disableExtensions(uint dwDisableMask)
+{
+ _dwDisabledISA = dwDisableMask;
+}
+
+
+
+/// Checks which instruction set extensions are supported by the CPU.
+uint detectCPUextensions(void)
+{
+#if (!(ALLOW_X86_OPTIMIZATIONS) || !(__GNUC__))
+
+ return 0; // always disable extensions on non-x86 platforms.
+
+#else
+ uint res = 0;
+
+ if (_dwDisabledISA == 0xffffffff) return 0;
+
+ asm volatile(
+ "\n\txor %%esi, %%esi" // clear %%esi = result register
+ // check if 'cpuid' instructions is available by toggling eflags bit 21
+
+ "\n\tpushf" // save eflags to stack
+ "\n\tmovl (%%esp), %%eax" // load eax from stack (with eflags)
+ "\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
+ "\n\txor $0x00200000, %%eax" // toggle bit 21
+ "\n\tmovl %%eax, (%%esp)" // store toggled eflags to stack
+ "\n\tpopf" // load eflags from stack
+ "\n\tpushf" // save updated eflags to stack
+ "\n\tmovl (%%esp), %%eax" // load eax from stack
+ "\n\tpopf" // pop stack to restore esp
+ "\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
+ "\n\tcmp %%ecx, %%eax" // compare to original eflags values
+ "\n\tjz end" // jumps to 'end' if cpuid not present
+ // cpuid instruction available, test for presence of mmx instructions
+
+ "\n\tmovl $1, %%eax"
+ "\n\tcpuid"
+ "\n\ttest $0x00800000, %%edx"
+ "\n\tjz end" // branch if MMX not available
+
+ "\n\tor $0x01, %%esi" // otherwise add MMX support bit
+
+ "\n\ttest $0x02000000, %%edx"
+ "\n\tjz test3DNow" // branch if SSE not available
+
+ "\n\tor $0x08, %%esi" // otherwise add SSE support bit
+
+ "\n\ttest3DNow:"
+ // test for precense of AMD extensions
+ "\n\tmov $0x80000000, %%eax"
+ "\n\tcpuid"
+ "\n\tcmp $0x80000000, %%eax"
+ "\n\tjbe end" // branch if no AMD extensions detected
+
+ // test for precense of 3DNow! extension
+ "\n\tmov $0x80000001, %%eax"
+ "\n\tcpuid"
+ "\n\ttest $0x80000000, %%edx"
+ "\n\tjz end" // branch if 3DNow! not detected
+
+ "\n\tor $0x02, %%esi" // otherwise add 3DNow support bit
+
+ "\n\tend:"
+
+ "\n\tmov %%esi, %0"
+
+ : "=r" (res)
+ : /* no inputs */
+ : "%edx", "%eax", "%ecx", "%esi" );
+
+ return res & ~_dwDisabledISA;
+#endif
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp new file mode 100644 index 00000000..c6c54246 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/cpu_detect_x86_win.cpp @@ -0,0 +1,129 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// Win32 version of the x86 CPU detect routine.
+///
+/// This file is to be compiled in Windows platform with Microsoft Visual C++
+/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
+/// for all GNU platforms.
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-02-13 18:22:48 +0200 (Fri, 13 Feb 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: cpu_detect_x86_win.cpp 62 2009-02-13 16:22:48Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "cpu_detect.h"
+
+#ifndef WIN32
+#error wrong platform - this source code file is exclusively for Win32 platform
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// processor instructions extension detection routines
+//
+//////////////////////////////////////////////////////////////////////////////
+
+// Flag variable indicating whick ISA extensions are disabled (for debugging)
+static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
+
+
+// Disables given set of instruction extensions. See SUPPORT_... defines.
+void disableExtensions(uint dwDisableMask)
+{
+ _dwDisabledISA = dwDisableMask;
+}
+
+
+
+/// Checks which instruction set extensions are supported by the CPU.
+uint detectCPUextensions(void)
+{
+ uint res = 0;
+
+ if (_dwDisabledISA == 0xffffffff) return 0;
+
+ _asm
+ {
+ ; check if 'cpuid' instructions is available by toggling eflags bit 21
+ ;
+ xor esi, esi ; clear esi = result register
+
+ pushfd ; save eflags to stack
+ mov eax,dword ptr [esp] ; load eax from stack (with eflags)
+ mov ecx, eax ; save the original eflags values to ecx
+ xor eax, 0x00200000 ; toggle bit 21
+ mov dword ptr [esp],eax ; store toggled eflags to stack
+ popfd ; load eflags from stack
+
+ pushfd ; save updated eflags to stack
+ mov eax,dword ptr [esp] ; load eax from stack
+ popfd ; pop stack to restore stack pointer
+
+ xor edx, edx ; clear edx for defaulting no mmx
+ cmp eax, ecx ; compare to original eflags values
+ jz end ; jumps to 'end' if cpuid not present
+
+ ; cpuid instruction available, test for presence of mmx instructions
+ mov eax, 1
+ cpuid
+ test edx, 0x00800000
+ jz end ; branch if MMX not available
+
+ or esi, SUPPORT_MMX ; otherwise add MMX support bit
+
+ test edx, 0x02000000
+ jz test3DNow ; branch if SSE not available
+
+ or esi, SUPPORT_SSE ; otherwise add SSE support bit
+
+ test3DNow:
+ ; test for precense of AMD extensions
+ mov eax, 0x80000000
+ cpuid
+ cmp eax, 0x80000000
+ jbe end ; branch if no AMD extensions detected
+
+ ; test for precense of 3DNow! extension
+ mov eax, 0x80000001
+ cpuid
+ test edx, 0x80000000
+ jz end ; branch if 3DNow! not detected
+
+ or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit
+
+ end:
+
+ mov res, esi
+ }
+
+ return res & ~_dwDisabledISA;
+}
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/mmx_optimized.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/mmx_optimized.cpp new file mode 100644 index 00000000..539ee57c --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/mmx_optimized.cpp @@ -0,0 +1,320 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// MMX optimized routines. All MMX optimized functions have been gathered into
+/// this single source code file, regardless to their class or original source
+/// code file, in order to ease porting the library to other compiler and
+/// processor platforms.
+///
+/// The MMX-optimizations are programmed using MMX compiler intrinsics that
+/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
+/// should compile with both toolsets.
+///
+/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
+/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
+/// is available for download at Microsoft Developers Network, see here:
+/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-10-31 16:53:23 +0200 (Sat, 31 Oct 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: mmx_optimized.cpp 75 2009-10-31 14:53:23Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "STTypes.h"
+
+#ifdef ALLOW_MMX
+// MMX routines available only with integer sample type
+
+#if !(WIN32 || __i386__ || __x86_64__)
+#error "wrong platform - this source code file is exclusively for x86 platforms"
+#endif
+
+using namespace soundtouch;
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of MMX optimized functions of class 'TDStretchMMX'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "TDStretch.h"
+#include <mmintrin.h>
+#include <limits.h>
+#include <math.h>
+
+
+// Calculates cross correlation of two buffers
+long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
+{
+ const __m64 *pVec1, *pVec2;
+ __m64 shifter;
+ __m64 accu, normaccu;
+ long corr, norm;
+ int i;
+
+ pVec1 = (__m64*)pV1;
+ pVec2 = (__m64*)pV2;
+
+ shifter = _m_from_int(overlapDividerBits);
+ normaccu = accu = _mm_setzero_si64();
+
+ // Process 4 parallel sets of 2 * stereo samples each during each
+ // round to improve CPU-level parallellization.
+ for (i = 0; i < overlapLength / 8; i ++)
+ {
+ __m64 temp, temp2;
+
+ // dictionary of instructions:
+ // _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
+ // _mm_add_pi32 : 2*32bit add
+ // _m_psrad : 32bit right-shift
+
+ temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
+ _mm_madd_pi16(pVec1[1], pVec2[1]));
+ temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
+ _mm_madd_pi16(pVec1[1], pVec1[1]));
+ accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
+ normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
+
+ temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
+ _mm_madd_pi16(pVec1[3], pVec2[3]));
+ temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
+ _mm_madd_pi16(pVec1[3], pVec1[3]));
+ accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
+ normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
+
+ pVec1 += 4;
+ pVec2 += 4;
+ }
+
+ // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
+ // and finally store the result into the variable "corr"
+
+ accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
+ corr = _m_to_int(accu);
+
+ normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32));
+ norm = _m_to_int(normaccu);
+
+ // Clear MMS state
+ _m_empty();
+
+ // Normalize result by dividing by sqrt(norm) - this step is easiest
+ // done using floating point operation
+ if (norm == 0) norm = 1; // to avoid div by zero
+ return (long)((double)corr * USHRT_MAX / sqrt((double)norm));
+ // Note: Warning about the missing EMMS instruction is harmless
+ // as it'll be called elsewhere.
+}
+
+
+
+void TDStretchMMX::clearCrossCorrState()
+{
+ // Clear MMS state
+ _m_empty();
+ //_asm EMMS;
+}
+
+
+
+// MMX-optimized version of the function overlapStereo
+void TDStretchMMX::overlapStereo(short *output, const short *input) const
+{
+ const __m64 *pVinput, *pVMidBuf;
+ __m64 *pVdest;
+ __m64 mix1, mix2, adder, shifter;
+ int i;
+
+ pVinput = (const __m64*)input;
+ pVMidBuf = (const __m64*)pMidBuffer;
+ pVdest = (__m64*)output;
+
+ // mix1 = mixer values for 1st stereo sample
+ // mix1 = mixer values for 2nd stereo sample
+ // adder = adder for updating mixer values after each round
+
+ mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
+ adder = _mm_set_pi16(1, -1, 1, -1);
+ mix2 = _mm_add_pi16(mix1, adder);
+ adder = _mm_add_pi16(adder, adder);
+
+ // Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
+ // overlapDividerBits calculation earlier.
+ shifter = _m_from_int(overlapDividerBits + 1);
+
+ for (i = 0; i < overlapLength / 4; i ++)
+ {
+ __m64 temp1, temp2;
+
+ // load & shuffle data so that input & mixbuffer data samples are paired
+ temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
+ temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
+
+ // temp = (temp .* mix) >> shifter
+ temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
+ temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
+ pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
+
+ // update mix += adder
+ mix1 = _mm_add_pi16(mix1, adder);
+ mix2 = _mm_add_pi16(mix2, adder);
+
+ // --- second round begins here ---
+
+ // load & shuffle data so that input & mixbuffer data samples are paired
+ temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
+ temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
+
+ // temp = (temp .* mix) >> shifter
+ temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
+ temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
+ pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
+
+ // update mix += adder
+ mix1 = _mm_add_pi16(mix1, adder);
+ mix2 = _mm_add_pi16(mix2, adder);
+
+ pVinput += 2;
+ pVMidBuf += 2;
+ pVdest += 2;
+ }
+
+ _m_empty(); // clear MMS state
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of MMX optimized functions of class 'FIRFilter'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "FIRFilter.h"
+
+
+FIRFilterMMX::FIRFilterMMX() : FIRFilter()
+{
+ filterCoeffsUnalign = NULL;
+}
+
+
+FIRFilterMMX::~FIRFilterMMX()
+{
+ delete[] filterCoeffsUnalign;
+}
+
+
+// (overloaded) Calculates filter coefficients for MMX routine
+void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
+{
+ uint i;
+ FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
+
+ // Ensure that filter coeffs array is aligned to 16-byte boundary
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = new short[2 * newLength + 8];
+ filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);
+
+ // rearrange the filter coefficients for mmx routines
+ for (i = 0;i < length; i += 4)
+ {
+ filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
+ filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
+ filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
+ filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
+
+ filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
+ filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
+ filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
+ filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
+ }
+}
+
+
+
+// mmx-optimized version of the filter routine for stereo sound
+uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
+{
+ // Create stack copies of the needed member variables for asm routines :
+ uint i, j;
+ __m64 *pVdest = (__m64*)dest;
+
+ if (length < 2) return 0;
+
+ for (i = 0; i < (numSamples - length) / 2; i ++)
+ {
+ __m64 accu1;
+ __m64 accu2;
+ const __m64 *pVsrc = (const __m64*)src;
+ const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
+
+ accu1 = accu2 = _mm_setzero_si64();
+ for (j = 0; j < lengthDiv8 * 2; j ++)
+ {
+ __m64 temp1, temp2;
+
+ temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
+ temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
+
+ accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
+ accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
+
+ temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
+
+ accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
+ accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
+
+ // accu1 += l2*f2+l0*f0 r2*f2+r0*f0
+ // += l3*f3+l1*f1 r3*f3+r1*f1
+
+ // accu2 += l3*f2+l1*f0 r3*f2+r1*f0
+ // l4*f3+l2*f1 r4*f3+r2*f1
+
+ pVfilter += 2;
+ pVsrc += 2;
+ }
+ // accu >>= resultDivFactor
+ accu1 = _mm_srai_pi32(accu1, resultDivFactor);
+ accu2 = _mm_srai_pi32(accu2, resultDivFactor);
+
+ // pack 2*2*32bits => 4*16 bits
+ pVdest[0] = _mm_packs_pi32(accu1, accu2);
+ src += 4;
+ pVdest ++;
+ }
+
+ _m_empty(); // clear emms state
+
+ return (numSamples & 0xfffffffe) - length;
+}
+
+#endif // ALLOW_MMX
diff --git a/plugins/soundtouch/soundtouch/source/SoundTouch/sse_optimized.cpp b/plugins/soundtouch/soundtouch/source/SoundTouch/sse_optimized.cpp new file mode 100644 index 00000000..7659be68 --- /dev/null +++ b/plugins/soundtouch/soundtouch/source/SoundTouch/sse_optimized.cpp @@ -0,0 +1,510 @@ +////////////////////////////////////////////////////////////////////////////////
+///
+/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
+/// optimized functions have been gathered into this single source
+/// code file, regardless to their class or original source code file, in order
+/// to ease porting the library to other compiler and processor platforms.
+///
+/// The SSE-optimizations are programmed using SSE compiler intrinsics that
+/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
+/// should compile with both toolsets.
+///
+/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
+/// 6.0 processor pack" update to support SSE instruction set. The update is
+/// available for download at Microsoft Developers Network, see here:
+/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
+///
+/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
+/// perform a search with keywords "processor pack".
+///
+/// Author : Copyright (c) Olli Parviainen
+/// Author e-mail : oparviai 'at' iki.fi
+/// SoundTouch WWW: http://www.surina.net/soundtouch
+///
+////////////////////////////////////////////////////////////////////////////////
+//
+// Last changed : $Date: 2009-12-28 22:32:57 +0200 (Mon, 28 Dec 2009) $
+// File revision : $Revision: 4 $
+//
+// $Id: sse_optimized.cpp 80 2009-12-28 20:32:57Z oparviai $
+//
+////////////////////////////////////////////////////////////////////////////////
+//
+// License :
+//
+// SoundTouch audio processing library
+// Copyright (c) Olli Parviainen
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "cpu_detect.h"
+#include "STTypes.h"
+
+using namespace soundtouch;
+
+#ifdef ALLOW_SSE
+
+// SSE routines available only with float sample type
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of SSE optimized functions of class 'TDStretchSSE'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "TDStretch.h"
+#include <xmmintrin.h>
+#include <math.h>
+
+// Calculates cross correlation of two buffers
+double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
+{
+ int i;
+ const float *pVec1;
+ const __m128 *pVec2;
+ __m128 vSum, vNorm;
+
+ // Note. It means a major slow-down if the routine needs to tolerate
+ // unaligned __m128 memory accesses. It's way faster if we can skip
+ // unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
+ // This can mean up to ~ 10-fold difference (incl. part of which is
+ // due to skipping every second round for stereo sound though).
+ //
+ // Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
+ // for choosing if this little cheating is allowed.
+
+#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION
+ // Little cheating allowed, return valid correlation only for
+ // aligned locations, meaning every second round for stereo sound.
+
+ #define _MM_LOAD _mm_load_ps
+
+ if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
+
+#else
+ // No cheating allowed, use unaligned load & take the resulting
+ // performance hit.
+ #define _MM_LOAD _mm_loadu_ps
+#endif
+
+ // ensure overlapLength is divisible by 8
+ assert((overlapLength % 8) == 0);
+
+ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
+ // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
+ pVec1 = (const float*)pV1;
+ pVec2 = (const __m128*)pV2;
+ vSum = vNorm = _mm_setzero_ps();
+
+ // Unroll the loop by factor of 4 * 4 operations
+ for (i = 0; i < overlapLength / 8; i ++)
+ {
+ __m128 vTemp;
+ // vSum += pV1[0..3] * pV2[0..3]
+ vTemp = _MM_LOAD(pVec1);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ // vSum += pV1[4..7] * pV2[4..7]
+ vTemp = _MM_LOAD(pVec1 + 4);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ // vSum += pV1[8..11] * pV2[8..11]
+ vTemp = _MM_LOAD(pVec1 + 8);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ // vSum += pV1[12..15] * pV2[12..15]
+ vTemp = _MM_LOAD(pVec1 + 12);
+ vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
+ vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
+
+ pVec1 += 16;
+ pVec2 += 4;
+ }
+
+ // return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
+ float *pvNorm = (float*)&vNorm;
+ double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
+ if (norm < 1e-9) norm = 1.0; // to avoid div by zero
+
+ float *pvSum = (float*)&vSum;
+ return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
+
+ /* This is approximately corresponding routine in C-language yet without normalization:
+ double corr, norm;
+ uint i;
+
+ // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
+ corr = norm = 0.0;
+ for (i = 0; i < overlapLength / 8; i ++)
+ {
+ corr += pV1[0] * pV2[0] +
+ pV1[1] * pV2[1] +
+ pV1[2] * pV2[2] +
+ pV1[3] * pV2[3] +
+ pV1[4] * pV2[4] +
+ pV1[5] * pV2[5] +
+ pV1[6] * pV2[6] +
+ pV1[7] * pV2[7] +
+ pV1[8] * pV2[8] +
+ pV1[9] * pV2[9] +
+ pV1[10] * pV2[10] +
+ pV1[11] * pV2[11] +
+ pV1[12] * pV2[12] +
+ pV1[13] * pV2[13] +
+ pV1[14] * pV2[14] +
+ pV1[15] * pV2[15];
+
+ for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
+
+ pV1 += 16;
+ pV2 += 16;
+ }
+ return corr / sqrt(norm);
+ */
+
+ /* This is a bit outdated, corresponding routine in assembler. This may be teeny-weeny bit
+ faster than intrinsic version, but more difficult to maintain & get compiled on multiple
+ platforms.
+
+ uint overlapLengthLocal = overlapLength;
+ float corr;
+
+ _asm
+ {
+ // Very important note: data in 'pV2' _must_ be aligned to
+ // 16-byte boundary!
+
+ // give prefetch hints to CPU of what data are to be needed soonish
+ // give more aggressive hints on pV1 as that changes while pV2 stays
+ // same between runs
+ prefetcht0 [pV1]
+ prefetcht0 [pV2]
+ prefetcht0 [pV1 + 32]
+
+ mov eax, dword ptr pV1
+ mov ebx, dword ptr pV2
+
+ xorps xmm0, xmm0
+
+ mov ecx, overlapLengthLocal
+ shr ecx, 3 // div by eight
+
+ loop1:
+ prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ movups xmm1, [eax]
+ mulps xmm1, [ebx]
+ addps xmm0, xmm1
+
+ movups xmm2, [eax + 16]
+ mulps xmm2, [ebx + 16]
+ addps xmm0, xmm2
+
+ prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movups xmm3, [eax + 32]
+ mulps xmm3, [ebx + 32]
+ addps xmm0, xmm3
+
+ movups xmm4, [eax + 48]
+ mulps xmm4, [ebx + 48]
+ addps xmm0, xmm4
+
+ add eax, 64
+ add ebx, 64
+
+ dec ecx
+ jnz loop1
+
+ // add the four floats of xmm0 together and return the result.
+
+ movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
+ addps xmm1, xmm0
+ movaps xmm2, xmm1
+ shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
+ addss xmm2, xmm1
+ movss corr, xmm2
+ }
+
+ return (double)corr;
+ */
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// implementation of SSE optimized functions of class 'FIRFilter'
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "FIRFilter.h"
+
+FIRFilterSSE::FIRFilterSSE() : FIRFilter()
+{
+ filterCoeffsAlign = NULL;
+ filterCoeffsUnalign = NULL;
+}
+
+
+FIRFilterSSE::~FIRFilterSSE()
+{
+ delete[] filterCoeffsUnalign;
+ filterCoeffsAlign = NULL;
+ filterCoeffsUnalign = NULL;
+}
+
+
+// (overloaded) Calculates filter coefficients for SSE routine
+void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
+{
+ uint i;
+ float fDivider;
+
+ FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
+
+ // Scale the filter coefficients so that it won't be necessary to scale the filtering result
+ // also rearrange coefficients suitably for 3DNow!
+ // Ensure that filter coeffs array is aligned to 16-byte boundary
+ delete[] filterCoeffsUnalign;
+ filterCoeffsUnalign = new float[2 * newLength + 4];
+ filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & (ulong)-16);
+
+ fDivider = (float)resultDivider;
+
+ // rearrange the filter coefficients for mmx routines
+ for (i = 0; i < newLength; i ++)
+ {
+ filterCoeffsAlign[2 * i + 0] =
+ filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
+ }
+}
+
+
+
+// SSE-optimized version of the filter routine for stereo sound
+uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
+{
+ int count = (int)((numSamples - length) & (uint)-2);
+ int j;
+
+ assert(count % 2 == 0);
+
+ if (count < 2) return 0;
+
+ assert(source != NULL);
+ assert(dest != NULL);
+ assert((length % 8) == 0);
+ assert(filterCoeffsAlign != NULL);
+ assert(((ulong)filterCoeffsAlign) % 16 == 0);
+
+ // filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
+ for (j = 0; j < count; j += 2)
+ {
+ const float *pSrc;
+ const __m128 *pFil;
+ __m128 sum1, sum2;
+ uint i;
+
+ pSrc = (const float*)source; // source audio data
+ pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
+ // are aligned to 16-byte boundary
+ sum1 = sum2 = _mm_setzero_ps();
+
+ for (i = 0; i < length / 8; i ++)
+ {
+ // Unroll loop for efficiency & calculate filter for 2*2 stereo samples
+ // at each pass
+
+ // sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
+ // sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
+
+ sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
+ sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
+
+ pSrc += 16;
+ pFil += 4;
+ }
+
+ // Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
+ // to sum the two hi- and lo-floats of these registers together.
+
+ // post-shuffle & add the filtered values and store to dest.
+ _mm_storeu_ps(dest, _mm_add_ps(
+ _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
+ _mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
+ ));
+ source += 4;
+ dest += 4;
+ }
+
+ // Ideas for further improvement:
+ // 1. If it could be guaranteed that 'source' were always aligned to 16-byte
+ // boundary, a faster aligned '_mm_load_ps' instruction could be used.
+ // 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
+ // boundary, a faster '_mm_store_ps' instruction could be used.
+
+ return (uint)count;
+
+ /* original routine in C-language. please notice the C-version has differently
+ organized coefficients though.
+ double suml1, suml2;
+ double sumr1, sumr2;
+ uint i, j;
+
+ for (j = 0; j < count; j += 2)
+ {
+ const float *ptr;
+ const float *pFil;
+
+ suml1 = sumr1 = 0.0;
+ suml2 = sumr2 = 0.0;
+ ptr = src;
+ pFil = filterCoeffs;
+ for (i = 0; i < lengthLocal; i ++)
+ {
+ // unroll loop for efficiency.
+
+ suml1 += ptr[0] * pFil[0] +
+ ptr[2] * pFil[2] +
+ ptr[4] * pFil[4] +
+ ptr[6] * pFil[6];
+
+ sumr1 += ptr[1] * pFil[1] +
+ ptr[3] * pFil[3] +
+ ptr[5] * pFil[5] +
+ ptr[7] * pFil[7];
+
+ suml2 += ptr[8] * pFil[0] +
+ ptr[10] * pFil[2] +
+ ptr[12] * pFil[4] +
+ ptr[14] * pFil[6];
+
+ sumr2 += ptr[9] * pFil[1] +
+ ptr[11] * pFil[3] +
+ ptr[13] * pFil[5] +
+ ptr[15] * pFil[7];
+
+ ptr += 16;
+ pFil += 8;
+ }
+ dest[0] = (float)suml1;
+ dest[1] = (float)sumr1;
+ dest[2] = (float)suml2;
+ dest[3] = (float)sumr2;
+
+ src += 4;
+ dest += 4;
+ }
+ */
+
+
+ /* Similar routine in assembly, again obsoleted due to maintainability
+ _asm
+ {
+ // Very important note: data in 'src' _must_ be aligned to
+ // 16-byte boundary!
+ mov edx, count
+ mov ebx, dword ptr src
+ mov eax, dword ptr dest
+ shr edx, 1
+
+ loop1:
+ // "outer loop" : during each round 2*2 output samples are calculated
+
+ // give prefetch hints to CPU of what data are to be needed soonish
+ prefetcht0 [ebx]
+ prefetcht0 [filterCoeffsLocal]
+
+ mov esi, ebx
+ mov edi, filterCoeffsLocal
+ xorps xmm0, xmm0
+ xorps xmm1, xmm1
+ mov ecx, lengthLocal
+
+ loop2:
+ // "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
+ prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movups xmm2, [esi] // possibly unaligned load
+ movups xmm3, [esi + 8] // possibly unaligned load
+ mulps xmm2, [edi]
+ mulps xmm3, [edi]
+ addps xmm0, xmm2
+ addps xmm1, xmm3
+
+ movups xmm4, [esi + 16] // possibly unaligned load
+ movups xmm5, [esi + 24] // possibly unaligned load
+ mulps xmm4, [edi + 16]
+ mulps xmm5, [edi + 16]
+ addps xmm0, xmm4
+ addps xmm1, xmm5
+
+ prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
+ prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
+
+ movups xmm6, [esi + 32] // possibly unaligned load
+ movups xmm7, [esi + 40] // possibly unaligned load
+ mulps xmm6, [edi + 32]
+ mulps xmm7, [edi + 32]
+ addps xmm0, xmm6
+ addps xmm1, xmm7
+
+ movups xmm4, [esi + 48] // possibly unaligned load
+ movups xmm5, [esi + 56] // possibly unaligned load
+ mulps xmm4, [edi + 48]
+ mulps xmm5, [edi + 48]
+ addps xmm0, xmm4
+ addps xmm1, xmm5
+
+ add esi, 64
+ add edi, 64
+ dec ecx
+ jnz loop2
+
+ // Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
+ // to sum the two hi- and lo-floats of these registers together.
+
+ movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
+ movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
+ shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
+ addps xmm0, xmm2
+
+ movaps [eax], xmm0
+ add ebx, 16
+ add eax, 16
+
+ dec edx
+ jnz loop1
+ }
+ */
+}
+
+#endif // ALLOW_SSE
diff --git a/plugins/soundtouch/st.cpp b/plugins/soundtouch/st.cpp new file mode 100644 index 00000000..458a5b44 --- /dev/null +++ b/plugins/soundtouch/st.cpp @@ -0,0 +1,112 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <SoundTouch.h> +#include "st.h" + +using namespace soundtouch; + +void* +st_alloc (void) { + return new SoundTouch (); +} + +void +st_free (void *st) { + delete (SoundTouch *)st; +} + +void +st_set_rate (void *st, float r) { + ((SoundTouch *)st)->setRate (r); +} + +void +st_set_tempo (void *st, float t) { + ((SoundTouch *)st)->setTempo (t); +} + +void +st_set_rate_change (void *st, float r) { + ((SoundTouch *)st)->setRateChange (r); +} + +void +st_set_tempo_change (void *st, float t) { + ((SoundTouch *)st)->setTempoChange (t); +} + +void +st_set_pitch (void *st, float p) { + ((SoundTouch *)st)->setPitch (p); +} + +void +st_set_pitch_octaves (void *st, float po) { + ((SoundTouch *)st)->setPitchOctaves (po); +} + +void +st_set_pitch_semi_tones (void *st, float p) { + ((SoundTouch *)st)->setPitchSemiTones (p); +} + +void +st_set_channels (void *st, int c) { + ((SoundTouch *)st)->setChannels (c); +} + +void +st_set_sample_rate (void *st, int r) { + ((SoundTouch *)st)->setSampleRate (r); +} + +void +st_flush (void *st) { + ((SoundTouch *)st)->flush (); +} + +void +st_put_samples (void *st, float *samples, int nsamples) { + ((SoundTouch *)st)->putSamples (samples, nsamples); +} + +void +st_clear (void *st) { + ((SoundTouch *)st)->clear (); +} + +void +st_set_setting (void *st, int id, int value) { + ((SoundTouch *)st)->setSetting (id, value); +} + +int +st_get_setting (void *st, int id) { + return ((SoundTouch *)st)->getSetting (id); +} + +unsigned int +st_num_unprocessed_samples (void *st) { + return ((SoundTouch *)st)->numUnprocessedSamples (); +} + +unsigned int +st_receive_samples (void *st, float *out, unsigned int max_samples) { + return ((SoundTouch *)st)->receiveSamples (out, max_samples); +} diff --git a/plugins/soundtouch/st.h b/plugins/soundtouch/st.h new file mode 100644 index 00000000..ae1f4f05 --- /dev/null +++ b/plugins/soundtouch/st.h @@ -0,0 +1,115 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __ST_H +#define __ST_H + +////////////////////////////////// +// C-wrapper for soundtouch class + +/// Enable/disable anti-alias filter in pitch transposer (0 = disable) +#define SETTING_USE_AA_FILTER 0 + +/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) +#define SETTING_AA_FILTER_LENGTH 1 + +/// Enable/disable quick seeking algorithm in tempo changer routine +/// (enabling quick seeking lowers CPU utilization but causes a minor sound +/// quality compromising) +#define SETTING_USE_QUICKSEEK 2 + +/// Time-stretch algorithm single processing sequence length in milliseconds. This determines +/// to how long sequences the original sound is chopped in the time-stretch algorithm. +/// See "STTypes.h" or README for more information. +#define SETTING_SEQUENCE_MS 3 + +/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the +/// best possible overlapping location. This determines from how wide window the algorithm +/// may look for an optimal joining location when mixing the sound sequences back together. +/// See "STTypes.h" or README for more information. +#define SETTING_SEEKWINDOW_MS 4 + +/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences +/// are mixed back together, to form a continuous sound stream, this parameter defines over +/// how long period the two consecutive sequences are let to overlap each other. +/// See "STTypes.h" or README for more information. +#define SETTING_OVERLAP_MS 5 + +#ifdef __cplusplus +extern "C" { +#endif + +void* +st_alloc (void); + +void +st_free (void *st); + +void +st_set_rate (void *st, float r); + +void +st_set_tempo (void *st, float t); + +void +st_set_rate_change (void *st, float r); + +void +st_set_tempo_change (void *st, float t); + +void +st_set_pitch (void *st, float p); + +void +st_set_pitch_octaves (void *st, float po); + +void +st_set_pitch_semi_tones (void *st, float p); + +void +st_set_channels (void *st, int c); + +void +st_set_sample_rate (void *st, int r); + +void +st_flush (void *st); + +void +st_put_samples (void *st, float *samples, int nsamples); + +void +st_clear (void *st); + +void +st_set_setting (void *st, int id, int value); + +int +st_get_setting (void *st, int id); + +unsigned int +st_num_unprocessed_samples (void *st); + +unsigned int +st_receive_samples (void *st, float *out, unsigned int max_samples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/supereq/Equ.cpp b/plugins/supereq/Equ.cpp index f53b99d1..0aff4f8a 100644 --- a/plugins/supereq/Equ.cpp +++ b/plugins/supereq/Equ.cpp @@ -1,37 +1,92 @@ +/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>
+ Original SuperEQ code (C) Naoki Shibata <shibatch@users.sf.net>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "paramlist.hpp"
+#include "Equ.h"
+
+int _Unwind_Resume_or_Rethrow;
+int _Unwind_RaiseException;
+int _Unwind_GetLanguageSpecificData;
+int _Unwind_Resume;
+int _Unwind_DeleteException;
+int _Unwind_GetTextRelBase;
+int _Unwind_SetIP;
+int _Unwind_GetDataRelBase;
+int _Unwind_GetRegionStart;
+int _Unwind_SetGR;
+int _Unwind_GetIPInfo;
+
+#ifdef USE_OOURA
+extern "C" void rdft(int, int, REAL *, int *, REAL *);
+void rfft(int n,int isign,REAL *x)
+{
+ static int ipsize = 0,wsize=0;
+ static int *ip = NULL;
+ static REAL *w = NULL;
+ int newipsize,newwsize;
+ if (n == 0) {
+ free(ip); ip = NULL; ipsize = 0;
+ free(w); w = NULL; wsize = 0;
+ return;
+ }
-typedef float REAL;
-void rfft(int n,int isign,REAL x[]);
+ n = 1 << n;
-#define M 15
-#define PI 3.1415926535897932384626433832795
+ newipsize = 2+sqrt(n/2);
+ if (newipsize > ipsize) {
+ ipsize = newipsize;
+ ip = (int *)realloc(ip,sizeof(int)*ipsize);
+ ip[0] = 0;
+ }
-#define RINT(x) ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) - 0.5)))
+ newwsize = n/2;
+ if (newwsize > wsize) {
+ wsize = newwsize;
+ w = (REAL *)realloc(w,sizeof(REAL)*wsize);
+ }
-#define DITHERLEN 65536
+ rdft(n,isign,x,ip,w);
+}
+#elif defined(USE_FFMPEG) || defined(USE_SHIBATCH)
+extern "C" void rfft(int n,int isign,REAL *x);
+#endif
-// play -c 2 -r 44100 -fs -sw
+#if defined(USE_SHIBATCH)
+extern "C" {
+#include "SIMDBase.h"
+}
+#endif
+
+
+#define PI 3.1415926535897932384626433832795
+
+#define DITHERLEN 65536
+#define M 15
static REAL fact[M+1];
static REAL aa = 96;
-static REAL iza;
-static REAL *lires,*lires1,*lires2,*rires,*rires1,*rires2,*irest;
-static REAL *fsamples;
-static REAL *ditherbuf;
-static int ditherptr = 0;
-static volatile int chg_ires,cur_ires;
-static int winlen,winlenbit,tabsize,nbufsamples;
-static short *inbuf;
-static REAL *outbuf;
-static int maxamp;
-int enable = 1, dither = 0;
-
-#define NCH 2
+static REAL iza = 0;
#define NBANDS 17
static REAL bands[] = {
@@ -62,49 +117,75 @@ static REAL izero(REAL x) return ret;
}
-extern "C" void equ_init(int wb)
+void *equ_malloc (int size) {
+#ifdef USE_SHIBATCH
+ return SIMDBase_alignedMalloc (size);
+#else
+ return malloc (size);
+#endif
+}
+
+void equ_free (void *mem) {
+#ifdef USE_SHIBATCH
+ SIMDBase_alignedFree (mem);
+#else
+ free (mem);
+#endif
+}
+
+extern "C" void equ_init(SuperEqState *state, int wb, int channels)
{
int i,j;
- if (lires1 != NULL) free(lires1);
- if (lires2 != NULL) free(lires2);
- if (rires1 != NULL) free(rires1);
- if (rires2 != NULL) free(rires2);
- if (irest != NULL) free(irest);
- if (fsamples != NULL) free(fsamples);
- if (inbuf != NULL) free(inbuf);
- if (outbuf != NULL) free(outbuf);
- if (ditherbuf != NULL) free(ditherbuf);
-
- winlen = (1 << (wb-1))-1;
- winlenbit = wb;
- tabsize = 1 << wb;
-
- lires1 = (REAL *)malloc(sizeof(REAL)*tabsize);
- lires2 = (REAL *)malloc(sizeof(REAL)*tabsize);
- rires1 = (REAL *)malloc(sizeof(REAL)*tabsize);
- rires2 = (REAL *)malloc(sizeof(REAL)*tabsize);
- irest = (REAL *)malloc(sizeof(REAL)*tabsize);
- fsamples = (REAL *)malloc(sizeof(REAL)*tabsize);
- inbuf = (short *)calloc(winlen*NCH,sizeof(int));
- outbuf = (REAL *)calloc(tabsize*NCH,sizeof(REAL));
- ditherbuf = (REAL *)malloc(sizeof(REAL)*DITHERLEN);
-
- lires = lires1;
- rires = rires1;
- cur_ires = 1;
- chg_ires = 1;
+ if (state->lires1 != NULL) free(state->lires1);
+ if (state->lires2 != NULL) free(state->lires2);
+ if (state->irest != NULL) free(state->irest);
+ if (state->fsamples != NULL) free(state->fsamples);
+ if (state->finbuf != NULL) free(state->finbuf);
+ if (state->outbuf != NULL) free(state->outbuf);
+ if (state->ditherbuf != NULL) free(state->ditherbuf);
+
+
+ memset (state, 0, sizeof (SuperEqState));
+ state->channels = channels;
+ state->enable = 1;
+
+ state->winlen = (1 << (wb-1))-1;
+ state->winlenbit = wb;
+ state->tabsize = 1 << wb;
+ state->fft_bits = wb;
+
+ state->lires1 = (REAL *)equ_malloc(sizeof(REAL)*state->tabsize * state->channels);
+ state->lires2 = (REAL *)equ_malloc(sizeof(REAL)*state->tabsize * state->channels);
+ state->irest = (REAL *)equ_malloc(sizeof(REAL)*state->tabsize);
+ state->fsamples = (REAL *)equ_malloc(sizeof(REAL)*state->tabsize);
+ state->finbuf = (REAL *)equ_malloc(state->winlen*state->channels*sizeof(REAL));
+ state->outbuf = (REAL *)equ_malloc(state->tabsize*state->channels*sizeof(REAL));
+ state->ditherbuf = (REAL *)equ_malloc(sizeof(REAL)*DITHERLEN);
+
+ memset (state->lires1, 0, sizeof(REAL)*state->tabsize * state->channels);
+ memset (state->lires2, 0, sizeof(REAL)*state->tabsize * state->channels);
+ memset (state->irest, 0, sizeof(REAL)*state->tabsize);
+ memset (state->fsamples, 0, sizeof(REAL)*state->tabsize);
+ memset (state->finbuf, 0, state->winlen*state->channels*sizeof(REAL));
+ memset (state->outbuf, 0, state->tabsize*state->channels*sizeof(REAL));
+ memset (state->ditherbuf, 0, sizeof(REAL)*DITHERLEN);
+
+ state->lires = state->lires1;
+ state->cur_ires = 1;
+ state->chg_ires = 1;
for(i=0;i<DITHERLEN;i++)
- ditherbuf[i] = (float(rand())/RAND_MAX-0.5);
-
- for(i=0;i<=M;i++)
- {
- fact[i] = 1;
- for(j=1;j<=i;j++) fact[i] *= j;
- }
-
- iza = izero(alpha(aa));
+ state->ditherbuf[i] = (float(rand())/RAND_MAX-0.5);
+
+ if (fact[0] < 1) {
+ for(i=0;i<=M;i++)
+ {
+ fact[i] = 1;
+ for(j=1;j<=i;j++) fact[i] *= j;
+ }
+ iza = izero(alpha(aa));
+ }
}
// -(N-1)/2 <= n <= (N-1)/2
@@ -168,7 +249,6 @@ void process_param(REAL *bc,paramlist *param,paramlist ¶m2,REAL fs,int ch) for(e = param->elm;e != NULL;e = e->next)
{
- if ((ch == 0 && !e->left) || (ch == 1 && !e->right)) continue;
if (e->lower >= e->upper) continue;
for(p=param2.elm;p != NULL;p = p->next)
@@ -231,414 +311,164 @@ void process_param(REAL *bc,paramlist *param,paramlist ¶m2,REAL fs,int ch) }
}
-extern "C" void equ_makeTable(REAL *lbc,REAL *rbc,paramlist *param,REAL fs)
+extern "C" void equ_makeTable(SuperEqState *state, REAL *lbc,void *_param,REAL fs)
{
- int i,cires = cur_ires;
+ paramlist *param = (paramlist *)_param;
+ int i,cires = state->cur_ires;
REAL *nires;
if (fs <= 0) return;
paramlist param2;
- // L
-
- process_param(lbc,param,param2,fs,0);
-
- for(i=0;i<winlen;i++)
- irest[i] = hn(i-winlen/2,param2,fs)*win(i-winlen/2,winlen);
-
- for(;i<tabsize;i++)
- irest[i] = 0;
+ for (int ch = 0; ch < state->channels; ch++) {
+ process_param(lbc,param,param2,fs,ch);
- rfft(tabsize,1,irest);
+ for(i=0;i<state->winlen;i++)
+ state->irest[i] = hn(i-state->winlen/2,param2,fs)*win(i-state->winlen/2,state->winlen);
- nires = cires == 1 ? lires2 : lires1;
+ for(;i<state->tabsize;i++)
+ state->irest[i] = 0;
- for(i=0;i<tabsize;i++)
- nires[i] = irest[i];
+ rfft(state->fft_bits,1,state->irest);
- process_param(rbc,param,param2,fs,1);
-
- // R
-
- for(i=0;i<winlen;i++)
- irest[i] = hn(i-winlen/2,param2,fs)*win(i-winlen/2,winlen);
+ nires = cires == 1 ? state->lires2 : state->lires1;
+ nires += ch * state->tabsize;
- for(;i<tabsize;i++)
- irest[i] = 0;
-
- rfft(tabsize,1,irest);
-
- nires = cires == 1 ? rires2 : rires1;
-
- for(i=0;i<tabsize;i++)
- nires[i] = irest[i];
-
- //
-
- chg_ires = cires == 1 ? 2 : 1;
+ for(i=0;i<state->tabsize;i++)
+ nires[i] = state->irest[i];
+ }
+ state->chg_ires = cires == 1 ? 2 : 1;
}
-extern "C" void equ_quit(void)
+extern "C" void equ_quit(SuperEqState *state)
{
- free(lires1);
- free(lires2);
- free(rires1);
- free(rires2);
- free(irest);
- free(fsamples);
- free(inbuf);
- free(outbuf);
- free(ditherbuf);
-
- lires1 = NULL;
- lires2 = NULL;
- rires1 = NULL;
- rires2 = NULL;
- irest = NULL;
- fsamples = NULL;
- inbuf = NULL;
- outbuf = NULL;
+ equ_free(state->lires1);
+ equ_free(state->lires2);
+ equ_free(state->irest);
+ equ_free(state->fsamples);
+ equ_free(state->finbuf);
+ equ_free(state->outbuf);
+ equ_free(state->ditherbuf);
+
+ state->lires1 = NULL;
+ state->lires2 = NULL;
+ state->irest = NULL;
+ state->fsamples = NULL;
+ state->finbuf = NULL;
+ state->outbuf = NULL;
rfft(0,0,NULL);
}
-extern "C" void equ_clearbuf(int bps,int srate)
+extern "C" void equ_clearbuf(SuperEqState *state)
{
int i;
- nbufsamples = 0;
- for(i=0;i<tabsize*NCH;i++) outbuf[i] = 0;
+ state->nbufsamples = 0;
+ for(i=0;i<state->tabsize*state->channels;i++) state->outbuf[i] = 0;
}
-extern "C" int equ_modifySamples(char *buf,int nsamples,int nch,int bps)
+extern "C" int equ_modifySamples_float (SuperEqState *state, char *buf,int nsamples,int nch)
{
int i,p,ch;
REAL *ires;
- int amax = (1 << (bps-1))-1;
- int amin = -(1 << (bps-1));
+ float amax = 1.0f;
+ float amin = -1.0f;
static float hm1 = 0, hm2 = 0;
- if (chg_ires) {
- cur_ires = chg_ires;
- lires = cur_ires == 1 ? lires1 : lires2;
- rires = cur_ires == 1 ? rires1 : rires2;
- chg_ires = 0;
+ if (state->chg_ires) {
+ state->cur_ires = state->chg_ires;
+ state->lires = state->cur_ires == 1 ? state->lires1 : state->lires2;
+ state->chg_ires = 0;
}
p = 0;
- while(nbufsamples+nsamples >= winlen)
+ while(state->nbufsamples+nsamples >= state->winlen)
{
- switch(bps)
- {
- case 8:
- for(i=0;i<(winlen-nbufsamples)*nch;i++)
- {
- inbuf[nbufsamples*nch+i] = ((unsigned char *)buf)[i+p*nch] - 0x80;
- float s = outbuf[nbufsamples*nch+i];
- if (dither) {
- float u;
- s -= hm1;
- u = s;
- s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- s = RINT(s);
- hm1 = s - u;
- ((unsigned char *)buf)[i+p*nch] = s + 0x80;
- } else {
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- ((unsigned char *)buf)[i+p*nch] = RINT(s) + 0x80;
- }
- }
- for(i=winlen*nch;i<tabsize*nch;i++)
- outbuf[i-winlen*nch] = outbuf[i];
-
- break;
-
- case 16:
- for(i=0;i<(winlen-nbufsamples)*nch;i++)
+ for(i=0;i<(state->winlen-state->nbufsamples)*nch;i++)
{
- inbuf[nbufsamples*nch+i] = ((short *)buf)[i+p*nch];
- float s = outbuf[nbufsamples*nch+i];
- if (dither) {
- float u;
- s -= hm1;
- u = s;
- s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- s = RINT(s);
- hm1 = s - u;
- ((short *)buf)[i+p*nch] = s;
- } else {
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- ((short *)buf)[i+p*nch] = RINT(s);
- }
- }
- for(i=winlen*nch;i<tabsize*nch;i++)
- outbuf[i-winlen*nch] = outbuf[i];
-
- break;
-
- case 24:
- for(i=0;i<(winlen-nbufsamples)*nch;i++)
- {
- ((int *)inbuf)[nbufsamples*nch+i] =
- (((unsigned char *)buf)[(i+p*nch)*3 ] ) +
- (((unsigned char *)buf)[(i+p*nch)*3+1] << 8) +
- ((( signed char *)buf)[(i+p*nch)*3+2] << 16) ;
-
- float s = outbuf[nbufsamples*nch+i];
+ state->finbuf[state->nbufsamples*nch+i] = ((float *)buf)[i+p*nch];
+ float s = state->outbuf[state->nbufsamples*nch+i];
//if (dither) s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
if (s < amin) s = amin;
if (amax < s) s = amax;
- int s2 = RINT(s);
- ((signed char *)buf)[(i+p*nch)*3 ] = s2 & 255; s2 >>= 8;
- ((signed char *)buf)[(i+p*nch)*3+1] = s2 & 255; s2 >>= 8;
- ((signed char *)buf)[(i+p*nch)*3+2] = s2 & 255;
+ ((float *)buf)[i+p*nch] = s;
}
- for(i=winlen*nch;i<tabsize*nch;i++)
- outbuf[i-winlen*nch] = outbuf[i];
+ for(i=state->winlen*nch;i<state->tabsize*nch;i++)
+ state->outbuf[i-state->winlen*nch] = state->outbuf[i];
- break;
- default:
- assert(0);
- }
-
- p += winlen-nbufsamples;
- nsamples -= winlen-nbufsamples;
- nbufsamples = 0;
+ p += state->winlen-state->nbufsamples;
+ nsamples -= state->winlen-state->nbufsamples;
+ state->nbufsamples = 0;
for(ch=0;ch<nch;ch++)
{
- ires = ch == 0 ? lires : rires;
+ ires = state->lires + ch * state->tabsize;
- if (bps == 24) {
- for(i=0;i<winlen;i++)
- fsamples[i] = ((int *)inbuf)[nch*i+ch];
- } else {
- for(i=0;i<winlen;i++)
- fsamples[i] = inbuf[nch*i+ch];
- }
+ for(i=0;i<state->winlen;i++)
+ state->fsamples[i] = state->finbuf[nch*i+ch];
- for(i=winlen;i<tabsize;i++)
- fsamples[i] = 0;
+ for(i=state->winlen;i<state->tabsize;i++)
+ state->fsamples[i] = 0;
- if (enable) {
- rfft(tabsize,1,fsamples);
+ if (state->enable) {
+ rfft(state->fft_bits,1,state->fsamples);
- fsamples[0] = ires[0]*fsamples[0];
- fsamples[1] = ires[1]*fsamples[1];
+ state->fsamples[0] = ires[0]*state->fsamples[0];
+ state->fsamples[1] = ires[1]*state->fsamples[1];
- for(i=1;i<tabsize/2;i++)
+ for(i=1;i<state->tabsize/2;i++)
{
REAL re,im;
- re = ires[i*2 ]*fsamples[i*2] - ires[i*2+1]*fsamples[i*2+1];
- im = ires[i*2+1]*fsamples[i*2] + ires[i*2 ]*fsamples[i*2+1];
+ re = ires[i*2 ]*state->fsamples[i*2] - ires[i*2+1]*state->fsamples[i*2+1];
+ im = ires[i*2+1]*state->fsamples[i*2] + ires[i*2 ]*state->fsamples[i*2+1];
- fsamples[i*2 ] = re;
- fsamples[i*2+1] = im;
+ state->fsamples[i*2 ] = re;
+ state->fsamples[i*2+1] = im;
}
- rfft(tabsize,-1,fsamples);
+ rfft(state->fft_bits,-1,state->fsamples);
} else {
- for(i=winlen-1+winlen/2;i>=winlen/2;i--) fsamples[i] = fsamples[i-winlen/2]*tabsize/2;
- for(;i>=0;i--) fsamples[i] = 0;
+ for(i=state->winlen-1+state->winlen/2;i>=state->winlen/2;i--) state->fsamples[i] = state->fsamples[i-state->winlen/2]*state->tabsize/2;
+ for(;i>=0;i--) state->fsamples[i] = 0;
}
- for(i=0;i<winlen;i++) outbuf[i*nch+ch] += fsamples[i]/tabsize*2;
+ for(i=0;i<state->winlen;i++) state->outbuf[i*nch+ch] += state->fsamples[i]/state->tabsize*2;
- for(i=winlen;i<tabsize;i++) outbuf[i*nch+ch] = fsamples[i]/tabsize*2;
+ for(i=state->winlen;i<state->tabsize;i++) state->outbuf[i*nch+ch] = state->fsamples[i]/state->tabsize*2;
}
}
- switch(bps)
- {
- case 8:
- for(i=0;i<nsamples*nch;i++)
- {
- inbuf[nbufsamples*nch+i] = ((unsigned char *)buf)[i+p*nch] - 0x80;
- float s = outbuf[nbufsamples*nch+i];
- if (dither) {
- float u;
- s -= hm1;
- u = s;
- s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- s = RINT(s);
- hm1 = s - u;
- ((unsigned char *)buf)[i+p*nch] = s + 0x80;
- } else {
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- ((unsigned char *)buf)[i+p*nch] = RINT(s) + 0x80;
- }
- }
- break;
-
- case 16:
for(i=0;i<nsamples*nch;i++)
{
- inbuf[nbufsamples*nch+i] = ((short *)buf)[i+p*nch];
- float s = outbuf[nbufsamples*nch+i];
- if (dither) {
+ state->finbuf[state->nbufsamples*nch+i] = ((float *)buf)[i+p*nch];
+ float s = state->outbuf[state->nbufsamples*nch+i];
+ if (state->dither) {
float u;
s -= hm1;
u = s;
- s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
+// s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
if (s < amin) s = amin;
if (amax < s) s = amax;
- s = RINT(s);
hm1 = s - u;
- ((short *)buf)[i+p*nch] = s;
+ ((float *)buf)[i+p*nch] = s;
} else {
if (s < amin) s = amin;
if (amax < s) s = amax;
- ((short *)buf)[i+p*nch] = RINT(s);
+ ((float *)buf)[i+p*nch] = s;
}
}
- break;
-
- case 24:
- for(i=0;i<nsamples*nch;i++)
- {
- ((int *)inbuf)[nbufsamples*nch+i] =
- (((unsigned char *)buf)[(i+p*nch)*3 ] ) +
- (((unsigned char *)buf)[(i+p*nch)*3+1] << 8) +
- ((( signed char *)buf)[(i+p*nch)*3+2] << 16) ;
-
- float s = outbuf[nbufsamples*nch+i];
- //if (dither) s += ditherbuf[(ditherptr++) & (DITHERLEN-1)];
- if (s < amin) s = amin;
- if (amax < s) s = amax;
- int s2 = RINT(s);
- ((signed char *)buf)[(i+p*nch)*3 ] = s2 & 255; s2 >>= 8;
- ((signed char *)buf)[(i+p*nch)*3+1] = s2 & 255; s2 >>= 8;
- ((signed char *)buf)[(i+p*nch)*3+2] = s2 & 255;
- }
- break;
-
- default:
- assert(0);
- }
p += nsamples;
- nbufsamples += nsamples;
+ state->nbufsamples += nsamples;
return p;
}
-#if 0
-void usage(void)
-{
- fprintf(stderr,"Ouch!\n");
-}
-
-int main(int argc,char **argv)
-{
- FILE *fpi,*fpo;
- char buf[576*2*2];
-
- static REAL bc[] =
- {1.0, 0,1.0, 0,1.0, 0,1.0, 0,1.0, 0,1.0, 0,1.0, 0,1.0, 0,1.0, 0};
-
- init(14);
- makeTable(bc,44100);
-
- if (argc != 3 && argc != 4) exit(-1);
-
- fpi = fopen(argv[1],"r");
- fpo = fopen(argv[2],"w");
-
- if (!fpi || !fpo) exit(-1);
-
- /* generate wav header */
-
- {
- short word;
- int dword;
-
- fwrite("RIFF",4,1,fpo);
- dword = 0;
- fwrite(&dword,4,1,fpo);
-
- fwrite("WAVEfmt ",8,1,fpo);
- dword = 16;
- fwrite(&dword,4,1,fpo);
- word = 1;
- fwrite(&word,2,1,fpo); /* format category, PCM */
- word = 2;
- fwrite(&word,2,1,fpo); /* channels */
- dword = 44100;
- fwrite(&dword,4,1,fpo); /* sampling rate */
- dword = 44100*2*2;
- fwrite(&dword,4,1,fpo); /* bytes per sec */
- word = 4;
- fwrite(&word,2,1,fpo); /* block alignment */
- word = 16;
- fwrite(&word,2,1,fpo); /* ??? */
-
- fwrite("data",4,1,fpo);
- dword = 0;
- fwrite(&dword,4,1,fpo);
- }
-
- preamp = 65536;
- maxamp = 0;
-
- if (argc == 4) {
- preamp = 32767*65536/atoi(argv[3]);
- fprintf(stderr,"preamp = %d\n",preamp);
- }
-
- for(;;)
- {
- int n,m;
-
- n = fread(buf,1,576*2*2,fpi);
- if (n <= 0) break;
- m = modifySamples((short *)buf,n/4,2);
- fwrite(buf,4,m,fpo);
- }
-
-#if 0
- for(;;)
- {
- int n = flushbuf((short *)buf,576);
- if (n == 0) break;
- fwrite(buf,4,n,fpo);
- }
-#endif
-
- {
- short word;
- int dword;
- int len = ftell(fpo);
-
- fseek(fpo,4,SEEK_SET);
- dword = len-8;
- fwrite(&dword,4,1,fpo);
-
- fseek(fpo,40,SEEK_SET);
- dword = len-44;
- fwrite(&dword,4,1,fpo);
- }
-
- if (maxamp != 0) {
- fprintf(stderr,"maxamp = %d\n",maxamp);
- }
-
- quit();
-}
-#endif
-
extern "C" void *paramlist_alloc (void) {
return (void *)(new paramlist);
}
diff --git a/plugins/supereq/Equ.h b/plugins/supereq/Equ.h new file mode 100644 index 00000000..a315741a --- /dev/null +++ b/plugins/supereq/Equ.h @@ -0,0 +1,56 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __EQU_H +#define __EQU_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef float REAL; +typedef struct { + REAL *lires,*lires1,*lires2; + REAL *irest; + REAL *fsamples; + REAL *ditherbuf; + int ditherptr; + volatile int chg_ires,cur_ires; + int winlen,winlenbit,tabsize,nbufsamples; + REAL *finbuf; + REAL *outbuf; + int dither; + int channels; + int enable; + int fft_bits; +} SuperEqState; + +void *paramlist_alloc (void); +void paramlist_free (void *); +void equ_makeTable(SuperEqState *state, float *lbc,void *param,float fs); +int equ_modifySamples(SuperEqState *state, char *buf,int nsamples,int nch,int bps); +int equ_modifySamples_float (SuperEqState *state, char *buf,int nsamples,int nch); +void equ_clearbuf(SuperEqState *state); +void equ_init(SuperEqState *state, int wb, int channels); +void equ_quit(SuperEqState *state); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugins/supereq/Fftsg_fl.cpp b/plugins/supereq/Fftsg_fl.cpp index d48debfe..636f8b8a 100644 --- a/plugins/supereq/Fftsg_fl.cpp +++ b/plugins/supereq/Fftsg_fl.cpp @@ -285,6 +285,7 @@ Appendix : w[] and ip[] are compatible with all routines.
*/
+extern "C" {
void cdft(int n, int isgn, REAL *a, int *ip, REAL *w)
{
@@ -2649,32 +2650,4 @@ void dstsub(int n, REAL *a, int nc, REAL *c) }
a[m] *= c[0];
}
-
-void rfft(int n,int isign,REAL x[])
-{
- static int ipsize = 0,wsize=0;
- static int *ip = NULL;
- static REAL *w = NULL;
- int newipsize,newwsize;
-
- if (n == 0) {
- free(ip); ip = NULL; ipsize = 0;
- free(w); w = NULL; wsize = 0;
- return;
- }
-
- newipsize = 2+sqrt(n/2);
- if (newipsize > ipsize) {
- ipsize = newipsize;
- ip = (int *)realloc(ip,sizeof(int)*ipsize);
- ip[0] = 0;
- }
-
- newwsize = n/2;
- if (newwsize > wsize) {
- wsize = newwsize;
- w = (REAL *)realloc(w,sizeof(REAL)*wsize);
- }
-
- rdft(n,isign,x,ip,w);
}
diff --git a/plugins/supereq/Makefile.am b/plugins/supereq/Makefile.am index 0fffd6d6..45010ec8 100644 --- a/plugins/supereq/Makefile.am +++ b/plugins/supereq/Makefile.am @@ -3,8 +3,51 @@ supereqdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = supereq.la supereq_la_SOURCES = supereq.c supereq.h Equ.cpp Fftsg_fl.cpp paramlist.hpp -supereq_la_LDFLAGS = -module +#nsfft-1.00/simd/SIMDBaseUndiff.c\ +#nsfft-1.00/simd/SIMDBase.c\ +#nsfft-1.00/dft/DFT.c\ +#nsfft-1.00/dft/DFTUndiff.c\ +#nsfft-1.00/simd/SIMDBase.h\ +#nsfft-1.00/simd/SIMDBaseUndiff.h\ +#nsfft-1.00/dft/DFTUndiff.h\ +#nsfft-1.00/dft/DFT.h\ +#shibatch_rdft.c + +#ffmpeg_fft/libavutil/mem.c\ +#ffmpeg_fft/libavutil/mathematics.c\ +#ffmpeg_fft/libavutil/rational.c\ +#ffmpeg_fft/libavutil/intfloat_readwrite.c\ +#ffmpeg_fft/libavcodec/dct.c\ +#ffmpeg_fft/libavcodec/avfft.c\ +#ffmpeg_fft/libavcodec/fft.c\ +#ffmpeg_fft/libavcodec/dct32.c\ +#ffmpeg_fft/libavcodec/rdft.c\ +#ffmpeg_fft/libavutil/intfloat_readwrite.h\ +#ffmpeg_fft/libavutil/avutil.h\ +#ffmpeg_fft/libavutil/common.h\ +#ffmpeg_fft/libavutil/attributes.h\ +#ffmpeg_fft/libavutil/mem.h\ +#ffmpeg_fft/libavutil/avconfig.h\ +#ffmpeg_fft/libavutil/mathematics.h\ +#ffmpeg_fft/libavutil/rational.h\ +#ffmpeg_fft/publik.h\ +#ffmpeg_fft/ffmpeg_fft.h\ +#ffmpeg_fft/libavcodec/dct32.h\ +#ffmpeg_fft/libavcodec/fft.h\ +#ffmpeg_fft/libavcodec/avfft.h\ +#ffmpeg_fft/config.h\ +#ff_rdft.c + +#AM_CFLAGS = $(CFLAGS) -I ffmpeg_fft -I ffmpeg_fft/libavcodec -I ffmpeg_fft/libavutil -std=c99 +#AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -I ffmpeg_fft -I ffmpeg_fft/libavcodec -I ffmpeg_fft/libavutil + +#AM_CFLAGS = $(CFLAGS) -I nsfft-1.00/dft -I nsfft-1.00/simd -std=c99 -msse -DENABLE_SSE_FLOAT -DUSE_SHIBATCH +#AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -I nsfft-1.00/dft -I nsfft-1.00/simd -msse -DENABLE_SSE_FLOAT -DUSE_SHIBATCH + +AM_CFLAGS = $(CFLAGS) -std=c99 -DUSE_OOURA +AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -DUSE_OOURA + +supereq_la_LDFLAGS = -module -nostdlib -lsupc++ supereq_la_LIBADD = $(LDADD) -AM_CFLAGS = -std=c99 endif diff --git a/plugins/supereq/ff_rdft.c b/plugins/supereq/ff_rdft.c new file mode 100644 index 00000000..70a09350 --- /dev/null +++ b/plugins/supereq/ff_rdft.c @@ -0,0 +1,63 @@ +#include <stdint.h> +#include <complex.h> +#include "libavcodec/avfft.h" +#include "libavutil/avutil.h" + +void rfft(int n,int isign,float *x) +{ + static int wsize=0; + static float *w = NULL; + static RDFTContext *s = NULL; + static RDFTContext *si = NULL; + int newwsize; + + if (n == 0) { + if (w) { + av_free(w); + w = NULL; + wsize = 0; + } + if (s) { + av_rdft_end (s); + s = NULL; + } + if (si) { + av_rdft_end (si); + si = NULL; + } + return; + } + + newwsize = n/2; + if (newwsize > wsize) { + wsize = newwsize; + if (s) { + av_rdft_end (s); + s = NULL; + } + if (si) { + av_rdft_end (si); + si = NULL; + } + if (w) { + av_free (w); + w = NULL; + } + w = (float *)av_malloc(sizeof(float)*wsize); + } + + if (!s) { + s = av_rdft_init(n,DFT_R2C); + } + if (!si) { + si = av_rdft_init(n,IDFT_C2R); + } + + if (isign == 1) { + av_rdft_calc (s, x); + } + else { + av_rdft_calc (si, x); + } +} + diff --git a/plugins/supereq/ffmpeg_fft/README b/plugins/supereq/ffmpeg_fft/README new file mode 100644 index 00000000..f53b2447 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/README @@ -0,0 +1,9 @@ +purpose: + +* compare fftw and ffmpeg fourier transforms using benchfft and / or libbench +* note: this is very specifically for neon. if you want to use ffmpeg_fft with + some other arch / fpu, then you will need to do some reorganization + +todo: + +1) fix benchees/ffmpeg/doitr.c diff --git a/plugins/supereq/ffmpeg_fft/config.h b/plugins/supereq/ffmpeg_fft/config.h new file mode 100644 index 00000000..0f36b47c --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/config.h @@ -0,0 +1,904 @@ +/* Automatically generated by configure - do not modify! */ +#ifndef FFMPEG_CONFIG_H +#define FFMPEG_CONFIG_H +#define FFMPEG_CONFIGURATION "--prefix=/usr --enable-neon --enable-pic --cpu=cortex-a8 --arch=arm --cross-prefix=arm-none-linux-gnueabi- --enable-cross-compile --target-os=linux --extra-cflags='-mfpu=neon -mcpu=cortex-a8 -mfloat-abi=softfp' --enable-shared --disable-debug" +#define FFMPEG_LICENSE "LGPL version 2.1 or later" +#define FFMPEG_DATADIR "/usr/share/ffmpeg" +#define CC_TYPE "gcc" +#define CC_VERSION __VERSION__ +#define restrict restrict +#define ASMALIGN(ZEROBITS) ".p2align " #ZEROBITS "\n\t" +#define EXTERN_PREFIX "" +#define EXTERN_ASM +#define ARCH_ALPHA 0 +#define ARCH_ARM 0 +#define ARCH_AVR32 0 +#define ARCH_AVR32_AP 0 +#define ARCH_AVR32_UC 0 +#define ARCH_BFIN 0 +#define ARCH_IA64 0 +#define ARCH_M68K 0 +#define ARCH_MIPS 0 +#define ARCH_MIPS64 0 +#define ARCH_PARISC 0 +#define ARCH_PPC 0 +#define ARCH_PPC64 0 +#define ARCH_S390 0 +#define ARCH_SH4 0 +#define ARCH_SPARC 0 +#define ARCH_SPARC64 0 +#define ARCH_TOMI 0 +#define ARCH_X86 1 +#define ARCH_X86_32 1 +#define ARCH_X86_64 0 +#define HAVE_ALTIVEC 0 +#define HAVE_AMD3DNOW 0 +#define HAVE_AMD3DNOWEXT 0 +#define HAVE_ARMV5TE 1 +#define HAVE_ARMV6 1 +#define HAVE_ARMV6T2 1 +#define HAVE_ARMVFP 1 +#define HAVE_IWMMXT 0 +#define HAVE_MMI 0 +#define HAVE_MMX 0 +#define HAVE_MMX2 0 +#define HAVE_NEON 1 +#define HAVE_PPC4XX 0 +#define HAVE_SSE 1 +#define HAVE_SSSE3 1 +#define HAVE_VIS 0 +#define HAVE_BIGENDIAN 0 +#define HAVE_PTHREADS 1 +#define HAVE_W32THREADS 0 +#define HAVE_ALSA_ASOUNDLIB_H 0 +#define HAVE_ALTIVEC_H 0 +#define HAVE_ARPA_INET_H 1 +#define HAVE_ATTRIBUTE_MAY_ALIAS 1 +#define HAVE_ATTRIBUTE_PACKED 1 +#define HAVE_BSWAP 0 +#define HAVE_CLOSESOCKET 0 +#define HAVE_CMOV 0 +#define HAVE_CONIO_H 0 +#define HAVE_DCBZL 0 +#define HAVE_DEV_BKTR_IOCTL_BT848_H 0 +#define HAVE_DEV_BKTR_IOCTL_METEOR_H 0 +#define HAVE_DEV_IC_BT8XX_H 0 +#define HAVE_DEV_VIDEO_METEOR_IOCTL_METEOR_H 0 +#define HAVE_DEV_VIDEO_BKTR_IOCTL_BT848_H 0 +#define HAVE_DLFCN_H 1 +#define HAVE_DLOPEN 1 +#define HAVE_DOS_PATHS 0 +#define HAVE_EBP_AVAILABLE 0 +#define HAVE_EBX_AVAILABLE 0 +#define HAVE_EXP2 1 +#define HAVE_EXP2F 1 +#define HAVE_FAST_64BIT 0 +#define HAVE_FAST_CLZ 1 +#define HAVE_FAST_CMOV 0 +#define HAVE_FAST_UNALIGNED 1 +#define HAVE_FCNTL 1 +#define HAVE_FORK 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETHRTIME 0 +#define HAVE_GETPROCESSMEMORYINFO 0 +#define HAVE_GETPROCESSTIMES 0 +#define HAVE_GETRUSAGE 1 +#define HAVE_GNU_AS 1 +#define HAVE_STRUCT_RUSAGE_RU_MAXRSS 1 +#define HAVE_IBM_ASM 0 +#define HAVE_INET_ATON 1 +#define HAVE_INLINE_ASM 1 +#define HAVE_ISATTY 1 +#define HAVE_LDBRX 0 +#define HAVE_LIBDC1394_1 0 +#define HAVE_LIBDC1394_2 0 +#define HAVE_LLRINT 1 +#define HAVE_LLRINTF 1 +#define HAVE_LOCAL_ALIGNED_16 0 +#define HAVE_LOCAL_ALIGNED_8 0 +#define HAVE_LOG2 1 +#define HAVE_LOG2F 1 +#define HAVE_LOONGSON 0 +#define HAVE_LRINT 1 +#define HAVE_LRINTF 1 +#define HAVE_LZO1X_999_COMPRESS 0 +#define HAVE_MACHINE_IOCTL_BT848_H 0 +#define HAVE_MACHINE_IOCTL_METEOR_H 0 +#define HAVE_MALLOC_H 1 +#define HAVE_MEMALIGN 1 +#define HAVE_MKSTEMP 1 +#define HAVE_PLD 1 +#define HAVE_POSIX_MEMALIGN 1 +#define HAVE_ROUND 1 +#define HAVE_ROUNDF 1 +#define HAVE_SDL 0 +#define HAVE_SDL_VIDEO_SIZE 0 +#define HAVE_SETMODE 0 +#define HAVE_SOCKLEN_T 1 +#define HAVE_SOUNDCARD_H 0 +#define HAVE_POLL_H 1 +#define HAVE_SETRLIMIT 1 +#define HAVE_STRERROR_R 1 +#define HAVE_STRUCT_ADDRINFO 1 +#define HAVE_STRUCT_IPV6_MREQ 1 +#define HAVE_STRUCT_SOCKADDR_IN6 1 +#define HAVE_STRUCT_SOCKADDR_SA_LEN 0 +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#define HAVE_SYMVER 1 +#define HAVE_SYMVER_GNU_ASM 1 +#define HAVE_SYMVER_ASM_LABEL 0 +#define HAVE_SYS_MMAN_H 1 +#define HAVE_SYS_RESOURCE_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_SOUNDCARD_H 1 +#define HAVE_SYS_VIDEOIO_H 0 +#define HAVE_TEN_OPERANDS 0 +#define HAVE_TERMIOS_H 1 +#define HAVE_THREADS 1 +#define HAVE_TRUNCF 1 +#define HAVE_VFP_ARGS 0 +#define HAVE_VIRTUALALLOC 0 +#define HAVE_WINSOCK2_H 0 +#define HAVE_XFORM_ASM 0 +#define HAVE_YASM 0 +#define CONFIG_BSFS 1 +#define CONFIG_DECODERS 1 +#define CONFIG_DEMUXERS 1 +#define CONFIG_ENCODERS 1 +#define CONFIG_FILTERS 1 +#define CONFIG_HWACCELS 0 +#define CONFIG_INDEVS 1 +#define CONFIG_MUXERS 1 +#define CONFIG_OUTDEVS 1 +#define CONFIG_PARSERS 1 +#define CONFIG_PROTOCOLS 1 +#define CONFIG_AANDCT 1 +#define CONFIG_AVCODEC 1 +#define CONFIG_AVDEVICE 1 +#define CONFIG_AVFILTER 1 +#define CONFIG_AVFILTER_LAVF 0 +#define CONFIG_AVFORMAT 1 +#define CONFIG_AVISYNTH 0 +#define CONFIG_BZLIB 0 +#define CONFIG_DCT 1 +#define CONFIG_DOC 0 +#define CONFIG_DWT 1 +#define CONFIG_DXVA2 0 +#define CONFIG_FASTDIV 1 +#define CONFIG_FFMPEG 1 +#define CONFIG_FFPLAY 0 +#define CONFIG_FFPROBE 1 +#define CONFIG_FFSERVER 1 +#define CONFIG_FFT 1 +#define CONFIG_GOLOMB 1 +#define CONFIG_GPL 0 +#define CONFIG_GRAY 0 +#define CONFIG_H264DSP 1 +#define CONFIG_HARDCODED_TABLES 0 +#define CONFIG_LIBDC1394 0 +#define CONFIG_LIBDIRAC 0 +#define CONFIG_LIBFAAC 0 +#define CONFIG_LIBGSM 0 +#define CONFIG_LIBMP3LAME 0 +#define CONFIG_LIBNUT 0 +#define CONFIG_LIBOPENCORE_AMRNB 0 +#define CONFIG_LIBOPENCORE_AMRWB 0 +#define CONFIG_LIBOPENJPEG 0 +#define CONFIG_LIBRTMP 0 +#define CONFIG_LIBSCHROEDINGER 0 +#define CONFIG_LIBSPEEX 0 +#define CONFIG_LIBTHEORA 0 +#define CONFIG_LIBVORBIS 0 +#define CONFIG_LIBVPX 0 +#define CONFIG_LIBX264 0 +#define CONFIG_LIBXVID 0 +#define CONFIG_LPC 1 +#define CONFIG_LSP 1 +//#define CONFIG_MDCT 1 +#define CONFIG_MEMALIGN_HACK 0 +#define CONFIG_MLIB 0 +#define CONFIG_MPEGAUDIO_HP 1 +#define CONFIG_NETWORK 1 +#define CONFIG_NONFREE 0 +#define CONFIG_PIC 1 +#define CONFIG_POSTPROC 0 +#define CONFIG_RDFT 1 +#define CONFIG_RUNTIME_CPUDETECT 0 +#define CONFIG_SHARED 1 +#define CONFIG_SMALL 0 +#define CONFIG_SRAM 0 +#define CONFIG_STATIC 1 +#define CONFIG_SWSCALE 1 +#define CONFIG_SWSCALE_ALPHA 1 +#define CONFIG_VAAPI 0 +#define CONFIG_VDPAU 0 +#define CONFIG_VERSION3 0 +#define CONFIG_X11GRAB 0 +#define CONFIG_ZLIB 0 +#define CONFIG_AVUTIL 1 +#define CONFIG_GPLV3 0 +#define CONFIG_LGPLV3 0 +#define CONFIG_AASC_DECODER 1 +#define CONFIG_AMV_DECODER 1 +#define CONFIG_ANM_DECODER 1 +#define CONFIG_ASV1_DECODER 1 +#define CONFIG_ASV2_DECODER 1 +#define CONFIG_AURA_DECODER 1 +#define CONFIG_AURA2_DECODER 1 +#define CONFIG_AVS_DECODER 1 +#define CONFIG_BETHSOFTVID_DECODER 1 +#define CONFIG_BFI_DECODER 1 +#define CONFIG_BINK_DECODER 1 +#define CONFIG_BMP_DECODER 1 +#define CONFIG_C93_DECODER 1 +#define CONFIG_CAVS_DECODER 1 +#define CONFIG_CDGRAPHICS_DECODER 1 +#define CONFIG_CINEPAK_DECODER 1 +#define CONFIG_CLJR_DECODER 1 +#define CONFIG_CSCD_DECODER 1 +#define CONFIG_CYUV_DECODER 1 +#define CONFIG_DNXHD_DECODER 1 +#define CONFIG_DPX_DECODER 1 +#define CONFIG_DSICINVIDEO_DECODER 1 +#define CONFIG_DVVIDEO_DECODER 1 +#define CONFIG_DXA_DECODER 0 +#define CONFIG_EACMV_DECODER 1 +#define CONFIG_EAMAD_DECODER 1 +#define CONFIG_EATGQ_DECODER 1 +#define CONFIG_EATGV_DECODER 1 +#define CONFIG_EATQI_DECODER 1 +#define CONFIG_EIGHTBPS_DECODER 1 +#define CONFIG_EIGHTSVX_EXP_DECODER 1 +#define CONFIG_EIGHTSVX_FIB_DECODER 1 +#define CONFIG_ESCAPE124_DECODER 1 +#define CONFIG_FFV1_DECODER 1 +#define CONFIG_FFVHUFF_DECODER 1 +#define CONFIG_FLASHSV_DECODER 0 +#define CONFIG_FLIC_DECODER 1 +#define CONFIG_FLV_DECODER 1 +#define CONFIG_FOURXM_DECODER 1 +#define CONFIG_FRAPS_DECODER 1 +#define CONFIG_FRWU_DECODER 1 +#define CONFIG_GIF_DECODER 1 +#define CONFIG_H261_DECODER 1 +#define CONFIG_H263_DECODER 1 +#define CONFIG_H263I_DECODER 1 +#define CONFIG_H264_DECODER 1 +#define CONFIG_H264_VDPAU_DECODER 0 +#define CONFIG_HUFFYUV_DECODER 1 +#define CONFIG_IDCIN_DECODER 1 +#define CONFIG_IFF_BYTERUN1_DECODER 1 +#define CONFIG_IFF_ILBM_DECODER 1 +#define CONFIG_INDEO2_DECODER 1 +#define CONFIG_INDEO3_DECODER 1 +#define CONFIG_INDEO5_DECODER 1 +#define CONFIG_INTERPLAY_VIDEO_DECODER 1 +#define CONFIG_JPEGLS_DECODER 1 +#define CONFIG_KGV1_DECODER 1 +#define CONFIG_KMVC_DECODER 1 +#define CONFIG_LOCO_DECODER 1 +#define CONFIG_MDEC_DECODER 1 +#define CONFIG_MIMIC_DECODER 1 +#define CONFIG_MJPEG_DECODER 1 +#define CONFIG_MJPEGB_DECODER 1 +#define CONFIG_MMVIDEO_DECODER 1 +#define CONFIG_MOTIONPIXELS_DECODER 1 +#define CONFIG_MPEG_XVMC_DECODER 0 +#define CONFIG_MPEG1VIDEO_DECODER 1 +#define CONFIG_MPEG2VIDEO_DECODER 1 +#define CONFIG_MPEG4_DECODER 1 +#define CONFIG_MPEG4_VDPAU_DECODER 0 +#define CONFIG_MPEGVIDEO_DECODER 1 +#define CONFIG_MPEG_VDPAU_DECODER 0 +#define CONFIG_MPEG1_VDPAU_DECODER 0 +#define CONFIG_MSMPEG4V1_DECODER 1 +#define CONFIG_MSMPEG4V2_DECODER 1 +#define CONFIG_MSMPEG4V3_DECODER 1 +#define CONFIG_MSRLE_DECODER 1 +#define CONFIG_MSVIDEO1_DECODER 1 +#define CONFIG_MSZH_DECODER 1 +#define CONFIG_NUV_DECODER 1 +#define CONFIG_PAM_DECODER 1 +#define CONFIG_PBM_DECODER 1 +#define CONFIG_PCX_DECODER 1 +#define CONFIG_PGM_DECODER 1 +#define CONFIG_PGMYUV_DECODER 1 +#define CONFIG_PICTOR_DECODER 1 +#define CONFIG_PNG_DECODER 0 +#define CONFIG_PPM_DECODER 1 +#define CONFIG_PTX_DECODER 1 +#define CONFIG_QDRAW_DECODER 1 +#define CONFIG_QPEG_DECODER 1 +#define CONFIG_QTRLE_DECODER 1 +#define CONFIG_R210_DECODER 1 +#define CONFIG_RAWVIDEO_DECODER 1 +#define CONFIG_RL2_DECODER 1 +#define CONFIG_ROQ_DECODER 1 +#define CONFIG_RPZA_DECODER 1 +#define CONFIG_RV10_DECODER 1 +#define CONFIG_RV20_DECODER 1 +#define CONFIG_RV30_DECODER 1 +#define CONFIG_RV40_DECODER 1 +#define CONFIG_SGI_DECODER 1 +#define CONFIG_SMACKER_DECODER 1 +#define CONFIG_SMC_DECODER 1 +#define CONFIG_SNOW_DECODER 1 +#define CONFIG_SP5X_DECODER 1 +#define CONFIG_SUNRAST_DECODER 1 +#define CONFIG_SVQ1_DECODER 1 +#define CONFIG_SVQ3_DECODER 1 +#define CONFIG_TARGA_DECODER 1 +#define CONFIG_THEORA_DECODER 1 +#define CONFIG_THP_DECODER 1 +#define CONFIG_TIERTEXSEQVIDEO_DECODER 1 +#define CONFIG_TIFF_DECODER 1 +#define CONFIG_TMV_DECODER 1 +#define CONFIG_TRUEMOTION1_DECODER 1 +#define CONFIG_TRUEMOTION2_DECODER 1 +#define CONFIG_TSCC_DECODER 0 +#define CONFIG_TXD_DECODER 1 +#define CONFIG_ULTI_DECODER 1 +#define CONFIG_V210_DECODER 1 +#define CONFIG_V210X_DECODER 1 +#define CONFIG_VB_DECODER 1 +#define CONFIG_VC1_DECODER 1 +#define CONFIG_VC1_VDPAU_DECODER 0 +#define CONFIG_VCR1_DECODER 1 +#define CONFIG_VMDVIDEO_DECODER 1 +#define CONFIG_VMNC_DECODER 1 +#define CONFIG_VP3_DECODER 1 +#define CONFIG_VP5_DECODER 1 +#define CONFIG_VP6_DECODER 1 +#define CONFIG_VP6A_DECODER 1 +#define CONFIG_VP6F_DECODER 1 +#define CONFIG_VP8_DECODER 1 +#define CONFIG_VQA_DECODER 1 +#define CONFIG_WMV1_DECODER 1 +#define CONFIG_WMV2_DECODER 1 +#define CONFIG_WMV3_DECODER 1 +#define CONFIG_WMV3_VDPAU_DECODER 0 +#define CONFIG_WNV1_DECODER 1 +#define CONFIG_XAN_WC3_DECODER 1 +#define CONFIG_XL_DECODER 1 +#define CONFIG_YOP_DECODER 1 +#define CONFIG_ZLIB_DECODER 0 +#define CONFIG_ZMBV_DECODER 0 +#define CONFIG_AAC_DECODER 1 +#define CONFIG_AC3_DECODER 1 +#define CONFIG_ALAC_DECODER 1 +#define CONFIG_ALS_DECODER 1 +#define CONFIG_AMRNB_DECODER 1 +#define CONFIG_APE_DECODER 1 +#define CONFIG_ATRAC1_DECODER 1 +#define CONFIG_ATRAC3_DECODER 1 +#define CONFIG_BINKAUDIO_DCT_DECODER 1 +#define CONFIG_BINKAUDIO_RDFT_DECODER 1 +#define CONFIG_COOK_DECODER 1 +/* #define CONFIG_DCA_DECODER 1 */ +#define CONFIG_DSICINAUDIO_DECODER 1 +#define CONFIG_EAC3_DECODER 1 +#define CONFIG_FLAC_DECODER 1 +#define CONFIG_GSM_DECODER 1 +#define CONFIG_GSM_MS_DECODER 1 +#define CONFIG_IMC_DECODER 1 +#define CONFIG_MACE3_DECODER 1 +#define CONFIG_MACE6_DECODER 1 +#define CONFIG_MLP_DECODER 1 +#define CONFIG_MP1_DECODER 1 +#define CONFIG_MP1FLOAT_DECODER 1 +#define CONFIG_MP2_DECODER 1 +#define CONFIG_MP2FLOAT_DECODER 1 +#define CONFIG_MP3_DECODER 1 +#define CONFIG_MP3FLOAT_DECODER 1 +#define CONFIG_MP3ADU_DECODER 1 +#define CONFIG_MP3ADUFLOAT_DECODER 1 +#define CONFIG_MP3ON4_DECODER 1 +#define CONFIG_MP3ON4FLOAT_DECODER 1 +#define CONFIG_MPC7_DECODER 1 +#define CONFIG_MPC8_DECODER 1 +#define CONFIG_NELLYMOSER_DECODER 1 +#define CONFIG_QCELP_DECODER 1 +#define CONFIG_QDM2_DECODER 1 +#define CONFIG_RA_144_DECODER 1 +#define CONFIG_RA_288_DECODER 1 +#define CONFIG_SHORTEN_DECODER 1 +#define CONFIG_SIPR_DECODER 1 +#define CONFIG_SMACKAUD_DECODER 1 +#define CONFIG_SONIC_DECODER 1 +#define CONFIG_TRUEHD_DECODER 1 +#define CONFIG_TRUESPEECH_DECODER 1 +#define CONFIG_TTA_DECODER 1 +#define CONFIG_TWINVQ_DECODER 1 +#define CONFIG_VMDAUDIO_DECODER 1 +#define CONFIG_VORBIS_DECODER 1 +#define CONFIG_WAVPACK_DECODER 1 +#define CONFIG_WMAPRO_DECODER 1 +#define CONFIG_WMAV1_DECODER 1 +#define CONFIG_WMAV2_DECODER 1 +#define CONFIG_WMAVOICE_DECODER 1 +#define CONFIG_WS_SND1_DECODER 1 +#define CONFIG_PCM_ALAW_DECODER 1 +#define CONFIG_PCM_BLURAY_DECODER 1 +#define CONFIG_PCM_DVD_DECODER 1 +#define CONFIG_PCM_F32BE_DECODER 1 +#define CONFIG_PCM_F32LE_DECODER 1 +#define CONFIG_PCM_F64BE_DECODER 1 +#define CONFIG_PCM_F64LE_DECODER 1 +#define CONFIG_PCM_MULAW_DECODER 1 +#define CONFIG_PCM_S8_DECODER 1 +#define CONFIG_PCM_S16BE_DECODER 1 +#define CONFIG_PCM_S16LE_DECODER 1 +#define CONFIG_PCM_S16LE_PLANAR_DECODER 1 +#define CONFIG_PCM_S24BE_DECODER 1 +#define CONFIG_PCM_S24DAUD_DECODER 1 +#define CONFIG_PCM_S24LE_DECODER 1 +#define CONFIG_PCM_S32BE_DECODER 1 +#define CONFIG_PCM_S32LE_DECODER 1 +#define CONFIG_PCM_U8_DECODER 1 +#define CONFIG_PCM_U16BE_DECODER 1 +#define CONFIG_PCM_U16LE_DECODER 1 +#define CONFIG_PCM_U24BE_DECODER 1 +#define CONFIG_PCM_U24LE_DECODER 1 +#define CONFIG_PCM_U32BE_DECODER 1 +#define CONFIG_PCM_U32LE_DECODER 1 +#define CONFIG_PCM_ZORK_DECODER 1 +#define CONFIG_INTERPLAY_DPCM_DECODER 1 +#define CONFIG_ROQ_DPCM_DECODER 1 +#define CONFIG_SOL_DPCM_DECODER 1 +#define CONFIG_XAN_DPCM_DECODER 1 +#define CONFIG_ADPCM_4XM_DECODER 1 +#define CONFIG_ADPCM_ADX_DECODER 1 +#define CONFIG_ADPCM_CT_DECODER 1 +#define CONFIG_ADPCM_EA_DECODER 1 +#define CONFIG_ADPCM_EA_MAXIS_XA_DECODER 1 +#define CONFIG_ADPCM_EA_R1_DECODER 1 +#define CONFIG_ADPCM_EA_R2_DECODER 1 +#define CONFIG_ADPCM_EA_R3_DECODER 1 +#define CONFIG_ADPCM_EA_XAS_DECODER 1 +#define CONFIG_ADPCM_G726_DECODER 1 +#define CONFIG_ADPCM_IMA_AMV_DECODER 1 +#define CONFIG_ADPCM_IMA_DK3_DECODER 1 +#define CONFIG_ADPCM_IMA_DK4_DECODER 1 +#define CONFIG_ADPCM_IMA_EA_EACS_DECODER 1 +#define CONFIG_ADPCM_IMA_EA_SEAD_DECODER 1 +#define CONFIG_ADPCM_IMA_ISS_DECODER 1 +#define CONFIG_ADPCM_IMA_QT_DECODER 1 +#define CONFIG_ADPCM_IMA_SMJPEG_DECODER 1 +#define CONFIG_ADPCM_IMA_WAV_DECODER 1 +#define CONFIG_ADPCM_IMA_WS_DECODER 1 +#define CONFIG_ADPCM_MS_DECODER 1 +#define CONFIG_ADPCM_SBPRO_2_DECODER 1 +#define CONFIG_ADPCM_SBPRO_3_DECODER 1 +#define CONFIG_ADPCM_SBPRO_4_DECODER 1 +#define CONFIG_ADPCM_SWF_DECODER 1 +#define CONFIG_ADPCM_THP_DECODER 1 +#define CONFIG_ADPCM_XA_DECODER 1 +#define CONFIG_ADPCM_YAMAHA_DECODER 1 +#define CONFIG_DVBSUB_DECODER 1 +#define CONFIG_DVDSUB_DECODER 1 +#define CONFIG_PGSSUB_DECODER 1 +#define CONFIG_XSUB_DECODER 1 +#define CONFIG_LIBDIRAC_DECODER 0 +#define CONFIG_LIBGSM_DECODER 0 +#define CONFIG_LIBGSM_MS_DECODER 0 +#define CONFIG_LIBOPENCORE_AMRNB_DECODER 0 +#define CONFIG_LIBOPENCORE_AMRWB_DECODER 0 +#define CONFIG_LIBOPENJPEG_DECODER 0 +#define CONFIG_LIBSCHROEDINGER_DECODER 0 +#define CONFIG_LIBSPEEX_DECODER 0 +#define CONFIG_LIBVPX_DECODER 0 +#define CONFIG_ASV1_ENCODER 1 +#define CONFIG_ASV2_ENCODER 1 +#define CONFIG_BMP_ENCODER 1 +#define CONFIG_DNXHD_ENCODER 1 +#define CONFIG_DVVIDEO_ENCODER 1 +#define CONFIG_FFV1_ENCODER 1 +#define CONFIG_FFVHUFF_ENCODER 1 +#define CONFIG_FLASHSV_ENCODER 0 +#define CONFIG_FLV_ENCODER 1 +#define CONFIG_GIF_ENCODER 1 +#define CONFIG_H261_ENCODER 1 +#define CONFIG_H263_ENCODER 1 +#define CONFIG_H263P_ENCODER 1 +#define CONFIG_HUFFYUV_ENCODER 1 +#define CONFIG_JPEGLS_ENCODER 1 +#define CONFIG_LJPEG_ENCODER 1 +#define CONFIG_MJPEG_ENCODER 1 +#define CONFIG_MPEG1VIDEO_ENCODER 1 +#define CONFIG_MPEG2VIDEO_ENCODER 1 +#define CONFIG_MPEG4_ENCODER 1 +#define CONFIG_MSMPEG4V1_ENCODER 1 +#define CONFIG_MSMPEG4V2_ENCODER 1 +#define CONFIG_MSMPEG4V3_ENCODER 1 +#define CONFIG_PAM_ENCODER 1 +#define CONFIG_PBM_ENCODER 1 +#define CONFIG_PCX_ENCODER 1 +#define CONFIG_PGM_ENCODER 1 +#define CONFIG_PGMYUV_ENCODER 1 +#define CONFIG_PNG_ENCODER 0 +#define CONFIG_PPM_ENCODER 1 +#define CONFIG_QTRLE_ENCODER 1 +#define CONFIG_RAWVIDEO_ENCODER 1 +#define CONFIG_ROQ_ENCODER 1 +#define CONFIG_RV10_ENCODER 1 +#define CONFIG_RV20_ENCODER 1 +#define CONFIG_SGI_ENCODER 1 +#define CONFIG_SNOW_ENCODER 1 +#define CONFIG_SVQ1_ENCODER 1 +#define CONFIG_TARGA_ENCODER 1 +#define CONFIG_TIFF_ENCODER 1 +#define CONFIG_V210_ENCODER 1 +#define CONFIG_WMV1_ENCODER 1 +#define CONFIG_WMV2_ENCODER 1 +#define CONFIG_ZLIB_ENCODER 0 +#define CONFIG_ZMBV_ENCODER 0 +#define CONFIG_AAC_ENCODER 1 +#define CONFIG_AC3_ENCODER 1 +#define CONFIG_ALAC_ENCODER 1 +#define CONFIG_FLAC_ENCODER 1 +#define CONFIG_MP2_ENCODER 1 +#define CONFIG_NELLYMOSER_ENCODER 1 +#define CONFIG_RA_144_ENCODER 1 +#define CONFIG_SONIC_ENCODER 1 +#define CONFIG_SONIC_LS_ENCODER 1 +#define CONFIG_VORBIS_ENCODER 1 +#define CONFIG_WMAV1_ENCODER 1 +#define CONFIG_WMAV2_ENCODER 1 +#define CONFIG_PCM_ALAW_ENCODER 1 +#define CONFIG_PCM_F32BE_ENCODER 1 +#define CONFIG_PCM_F32LE_ENCODER 1 +#define CONFIG_PCM_F64BE_ENCODER 1 +#define CONFIG_PCM_F64LE_ENCODER 1 +#define CONFIG_PCM_MULAW_ENCODER 1 +#define CONFIG_PCM_S8_ENCODER 1 +#define CONFIG_PCM_S16BE_ENCODER 1 +#define CONFIG_PCM_S16LE_ENCODER 1 +#define CONFIG_PCM_S24BE_ENCODER 1 +#define CONFIG_PCM_S24DAUD_ENCODER 1 +#define CONFIG_PCM_S24LE_ENCODER 1 +#define CONFIG_PCM_S32BE_ENCODER 1 +#define CONFIG_PCM_S32LE_ENCODER 1 +#define CONFIG_PCM_U8_ENCODER 1 +#define CONFIG_PCM_U16BE_ENCODER 1 +#define CONFIG_PCM_U16LE_ENCODER 1 +#define CONFIG_PCM_U24BE_ENCODER 1 +#define CONFIG_PCM_U24LE_ENCODER 1 +#define CONFIG_PCM_U32BE_ENCODER 1 +#define CONFIG_PCM_U32LE_ENCODER 1 +#define CONFIG_PCM_ZORK_ENCODER 1 +#define CONFIG_ROQ_DPCM_ENCODER 1 +#define CONFIG_ADPCM_ADX_ENCODER 1 +#define CONFIG_ADPCM_G726_ENCODER 1 +#define CONFIG_ADPCM_IMA_QT_ENCODER 1 +#define CONFIG_ADPCM_IMA_WAV_ENCODER 1 +#define CONFIG_ADPCM_MS_ENCODER 1 +#define CONFIG_ADPCM_SWF_ENCODER 1 +#define CONFIG_ADPCM_YAMAHA_ENCODER 1 +#define CONFIG_DVBSUB_ENCODER 1 +#define CONFIG_DVDSUB_ENCODER 1 +#define CONFIG_XSUB_ENCODER 1 +#define CONFIG_LIBDIRAC_ENCODER 0 +#define CONFIG_LIBFAAC_ENCODER 0 +#define CONFIG_LIBGSM_ENCODER 0 +#define CONFIG_LIBGSM_MS_ENCODER 0 +#define CONFIG_LIBMP3LAME_ENCODER 0 +#define CONFIG_LIBOPENCORE_AMRNB_ENCODER 0 +#define CONFIG_LIBSCHROEDINGER_ENCODER 0 +#define CONFIG_LIBTHEORA_ENCODER 0 +#define CONFIG_LIBVORBIS_ENCODER 0 +#define CONFIG_LIBVPX_ENCODER 0 +#define CONFIG_LIBX264_ENCODER 0 +#define CONFIG_LIBXVID_ENCODER 0 +#define CONFIG_H263_VAAPI_HWACCEL 0 +#define CONFIG_H264_DXVA2_HWACCEL 0 +#define CONFIG_H264_VAAPI_HWACCEL 0 +#define CONFIG_MPEG2_DXVA2_HWACCEL 0 +#define CONFIG_MPEG2_VAAPI_HWACCEL 0 +#define CONFIG_MPEG4_VAAPI_HWACCEL 0 +#define CONFIG_VC1_DXVA2_HWACCEL 0 +#define CONFIG_VC1_VAAPI_HWACCEL 0 +#define CONFIG_WMV3_DXVA2_HWACCEL 0 +#define CONFIG_WMV3_VAAPI_HWACCEL 0 +#define CONFIG_AAC_PARSER 1 +#define CONFIG_AC3_PARSER 1 +#define CONFIG_CAVSVIDEO_PARSER 1 +#define CONFIG_DCA_PARSER 1 +#define CONFIG_DIRAC_PARSER 1 +#define CONFIG_DNXHD_PARSER 1 +#define CONFIG_DVBSUB_PARSER 1 +#define CONFIG_DVDSUB_PARSER 1 +#define CONFIG_H261_PARSER 1 +#define CONFIG_H263_PARSER 1 +#define CONFIG_H264_PARSER 1 +#define CONFIG_MJPEG_PARSER 1 +#define CONFIG_MLP_PARSER 1 +#define CONFIG_MPEG4VIDEO_PARSER 1 +#define CONFIG_MPEGAUDIO_PARSER 1 +#define CONFIG_MPEGVIDEO_PARSER 1 +#define CONFIG_PNM_PARSER 1 +#define CONFIG_VC1_PARSER 1 +#define CONFIG_VP3_PARSER 1 +#define CONFIG_VP8_PARSER 1 +#define CONFIG_AAC_ADTSTOASC_BSF 1 +#define CONFIG_CHOMP_BSF 1 +#define CONFIG_DUMP_EXTRADATA_BSF 1 +#define CONFIG_H264_MP4TOANNEXB_BSF 1 +#define CONFIG_IMX_DUMP_HEADER_BSF 1 +#define CONFIG_MJPEGA_DUMP_HEADER_BSF 1 +#define CONFIG_MP3_HEADER_COMPRESS_BSF 1 +#define CONFIG_MP3_HEADER_DECOMPRESS_BSF 1 +#define CONFIG_MOV2TEXTSUB_BSF 1 +#define CONFIG_NOISE_BSF 1 +#define CONFIG_REMOVE_EXTRADATA_BSF 1 +#define CONFIG_TEXT2MOVSUB_BSF 1 +#define CONFIG_AAC_DEMUXER 1 +#define CONFIG_AC3_DEMUXER 1 +#define CONFIG_AEA_DEMUXER 1 +#define CONFIG_AIFF_DEMUXER 1 +#define CONFIG_AMR_DEMUXER 1 +#define CONFIG_ANM_DEMUXER 1 +#define CONFIG_APC_DEMUXER 1 +#define CONFIG_APE_DEMUXER 1 +#define CONFIG_ASF_DEMUXER 1 +#define CONFIG_ASS_DEMUXER 1 +#define CONFIG_AU_DEMUXER 1 +#define CONFIG_AVI_DEMUXER 1 +#define CONFIG_AVISYNTH_DEMUXER 0 +#define CONFIG_AVS_DEMUXER 1 +#define CONFIG_BETHSOFTVID_DEMUXER 1 +#define CONFIG_BFI_DEMUXER 1 +#define CONFIG_BINK_DEMUXER 1 +#define CONFIG_C93_DEMUXER 1 +#define CONFIG_CAF_DEMUXER 1 +#define CONFIG_CAVSVIDEO_DEMUXER 1 +#define CONFIG_CDG_DEMUXER 1 +#define CONFIG_DAUD_DEMUXER 1 +#define CONFIG_DIRAC_DEMUXER 1 +#define CONFIG_DNXHD_DEMUXER 1 +#define CONFIG_DSICIN_DEMUXER 1 +#define CONFIG_DTS_DEMUXER 1 +#define CONFIG_DV_DEMUXER 1 +#define CONFIG_DXA_DEMUXER 1 +#define CONFIG_EA_DEMUXER 1 +#define CONFIG_EA_CDATA_DEMUXER 1 +#define CONFIG_EAC3_DEMUXER 1 +#define CONFIG_FFM_DEMUXER 1 +#define CONFIG_FILMSTRIP_DEMUXER 1 +#define CONFIG_FLAC_DEMUXER 1 +#define CONFIG_FLIC_DEMUXER 1 +#define CONFIG_FLV_DEMUXER 1 +#define CONFIG_FOURXM_DEMUXER 1 +#define CONFIG_GSM_DEMUXER 1 +#define CONFIG_GXF_DEMUXER 1 +#define CONFIG_H261_DEMUXER 1 +#define CONFIG_H263_DEMUXER 1 +#define CONFIG_H264_DEMUXER 1 +#define CONFIG_IDCIN_DEMUXER 1 +#define CONFIG_IFF_DEMUXER 1 +#define CONFIG_IMAGE2_DEMUXER 1 +#define CONFIG_IMAGE2PIPE_DEMUXER 1 +#define CONFIG_INGENIENT_DEMUXER 1 +#define CONFIG_IPMOVIE_DEMUXER 1 +#define CONFIG_ISS_DEMUXER 1 +#define CONFIG_IV8_DEMUXER 1 +#define CONFIG_IVF_DEMUXER 1 +#define CONFIG_LMLM4_DEMUXER 1 +#define CONFIG_M4V_DEMUXER 1 +#define CONFIG_MATROSKA_DEMUXER 1 +#define CONFIG_MJPEG_DEMUXER 1 +#define CONFIG_MLP_DEMUXER 1 +#define CONFIG_MM_DEMUXER 1 +#define CONFIG_MMF_DEMUXER 1 +#define CONFIG_MOV_DEMUXER 1 +#define CONFIG_MP3_DEMUXER 1 +#define CONFIG_MPC_DEMUXER 1 +#define CONFIG_MPC8_DEMUXER 1 +#define CONFIG_MPEGPS_DEMUXER 1 +#define CONFIG_MPEGTS_DEMUXER 1 +#define CONFIG_MPEGTSRAW_DEMUXER 1 +#define CONFIG_MPEGVIDEO_DEMUXER 1 +#define CONFIG_MSNWC_TCP_DEMUXER 1 +#define CONFIG_MTV_DEMUXER 1 +#define CONFIG_MVI_DEMUXER 1 +#define CONFIG_MXF_DEMUXER 1 +#define CONFIG_NC_DEMUXER 1 +#define CONFIG_NSV_DEMUXER 1 +#define CONFIG_NUT_DEMUXER 1 +#define CONFIG_NUV_DEMUXER 1 +#define CONFIG_OGG_DEMUXER 1 +#define CONFIG_OMA_DEMUXER 1 +#define CONFIG_PCM_ALAW_DEMUXER 1 +#define CONFIG_PCM_MULAW_DEMUXER 1 +#define CONFIG_PCM_F64BE_DEMUXER 1 +#define CONFIG_PCM_F64LE_DEMUXER 1 +#define CONFIG_PCM_F32BE_DEMUXER 1 +#define CONFIG_PCM_F32LE_DEMUXER 1 +#define CONFIG_PCM_S32BE_DEMUXER 1 +#define CONFIG_PCM_S32LE_DEMUXER 1 +#define CONFIG_PCM_S24BE_DEMUXER 1 +#define CONFIG_PCM_S24LE_DEMUXER 1 +#define CONFIG_PCM_S16BE_DEMUXER 1 +#define CONFIG_PCM_S16LE_DEMUXER 1 +#define CONFIG_PCM_S8_DEMUXER 1 +#define CONFIG_PCM_U32BE_DEMUXER 1 +#define CONFIG_PCM_U32LE_DEMUXER 1 +#define CONFIG_PCM_U24BE_DEMUXER 1 +#define CONFIG_PCM_U24LE_DEMUXER 1 +#define CONFIG_PCM_U16BE_DEMUXER 1 +#define CONFIG_PCM_U16LE_DEMUXER 1 +#define CONFIG_PCM_U8_DEMUXER 1 +#define CONFIG_PVA_DEMUXER 1 +#define CONFIG_QCP_DEMUXER 1 +#define CONFIG_R3D_DEMUXER 1 +#define CONFIG_RAWVIDEO_DEMUXER 1 +#define CONFIG_RL2_DEMUXER 1 +#define CONFIG_RM_DEMUXER 1 +#define CONFIG_ROQ_DEMUXER 1 +#define CONFIG_RPL_DEMUXER 1 +#define CONFIG_RTSP_DEMUXER 1 +#define CONFIG_SDP_DEMUXER 1 +#define CONFIG_SEGAFILM_DEMUXER 1 +#define CONFIG_SHORTEN_DEMUXER 1 +#define CONFIG_SIFF_DEMUXER 1 +#define CONFIG_SMACKER_DEMUXER 1 +#define CONFIG_SOL_DEMUXER 1 +#define CONFIG_SOX_DEMUXER 1 +#define CONFIG_STR_DEMUXER 1 +#define CONFIG_SWF_DEMUXER 1 +#define CONFIG_THP_DEMUXER 1 +#define CONFIG_TIERTEXSEQ_DEMUXER 1 +#define CONFIG_TMV_DEMUXER 1 +#define CONFIG_TRUEHD_DEMUXER 1 +#define CONFIG_TTA_DEMUXER 1 +#define CONFIG_TXD_DEMUXER 1 +#define CONFIG_VC1_DEMUXER 1 +#define CONFIG_VC1T_DEMUXER 1 +#define CONFIG_VMD_DEMUXER 1 +#define CONFIG_VOC_DEMUXER 1 +#define CONFIG_VQF_DEMUXER 1 +#define CONFIG_W64_DEMUXER 1 +#define CONFIG_WAV_DEMUXER 1 +#define CONFIG_WC3_DEMUXER 1 +#define CONFIG_WSAUD_DEMUXER 1 +#define CONFIG_WSVQA_DEMUXER 1 +#define CONFIG_WV_DEMUXER 1 +#define CONFIG_XA_DEMUXER 1 +#define CONFIG_YOP_DEMUXER 1 +#define CONFIG_YUV4MPEGPIPE_DEMUXER 1 +#define CONFIG_LIBNUT_DEMUXER 0 +#define CONFIG_AC3_MUXER 1 +#define CONFIG_ADTS_MUXER 1 +#define CONFIG_AIFF_MUXER 1 +#define CONFIG_AMR_MUXER 1 +#define CONFIG_ASF_MUXER 1 +#define CONFIG_ASS_MUXER 1 +#define CONFIG_ASF_STREAM_MUXER 1 +#define CONFIG_AU_MUXER 1 +#define CONFIG_AVI_MUXER 1 +#define CONFIG_AVM2_MUXER 1 +#define CONFIG_CRC_MUXER 1 +#define CONFIG_DAUD_MUXER 1 +#define CONFIG_DIRAC_MUXER 1 +#define CONFIG_DNXHD_MUXER 1 +#define CONFIG_DTS_MUXER 1 +#define CONFIG_DV_MUXER 1 +#define CONFIG_EAC3_MUXER 1 +#define CONFIG_FFM_MUXER 1 +#define CONFIG_FILMSTRIP_MUXER 1 +#define CONFIG_FLAC_MUXER 1 +#define CONFIG_FLV_MUXER 1 +#define CONFIG_FRAMECRC_MUXER 1 +#define CONFIG_FRAMEMD5_MUXER 1 +#define CONFIG_GIF_MUXER 1 +#define CONFIG_GXF_MUXER 1 +#define CONFIG_H261_MUXER 1 +#define CONFIG_H263_MUXER 1 +#define CONFIG_H264_MUXER 1 +#define CONFIG_IMAGE2_MUXER 1 +#define CONFIG_IMAGE2PIPE_MUXER 1 +#define CONFIG_IPOD_MUXER 1 +#define CONFIG_M4V_MUXER 1 +#define CONFIG_MD5_MUXER 1 +#define CONFIG_MATROSKA_MUXER 1 +#define CONFIG_MATROSKA_AUDIO_MUXER 1 +#define CONFIG_MJPEG_MUXER 1 +#define CONFIG_MLP_MUXER 1 +#define CONFIG_MMF_MUXER 1 +#define CONFIG_MOV_MUXER 1 +#define CONFIG_MP2_MUXER 1 +#define CONFIG_MP3_MUXER 1 +#define CONFIG_MP4_MUXER 1 +#define CONFIG_MPEG1SYSTEM_MUXER 1 +#define CONFIG_MPEG1VCD_MUXER 1 +#define CONFIG_MPEG1VIDEO_MUXER 1 +#define CONFIG_MPEG2DVD_MUXER 1 +#define CONFIG_MPEG2SVCD_MUXER 1 +#define CONFIG_MPEG2VIDEO_MUXER 1 +#define CONFIG_MPEG2VOB_MUXER 1 +#define CONFIG_MPEGTS_MUXER 1 +#define CONFIG_MPJPEG_MUXER 1 +#define CONFIG_MXF_MUXER 1 +#define CONFIG_MXF_D10_MUXER 1 +#define CONFIG_NULL_MUXER 1 +#define CONFIG_NUT_MUXER 1 +#define CONFIG_OGG_MUXER 1 +#define CONFIG_PCM_ALAW_MUXER 1 +#define CONFIG_PCM_MULAW_MUXER 1 +#define CONFIG_PCM_F64BE_MUXER 1 +#define CONFIG_PCM_F64LE_MUXER 1 +#define CONFIG_PCM_F32BE_MUXER 1 +#define CONFIG_PCM_F32LE_MUXER 1 +#define CONFIG_PCM_S32BE_MUXER 1 +#define CONFIG_PCM_S32LE_MUXER 1 +#define CONFIG_PCM_S24BE_MUXER 1 +#define CONFIG_PCM_S24LE_MUXER 1 +#define CONFIG_PCM_S16BE_MUXER 1 +#define CONFIG_PCM_S16LE_MUXER 1 +#define CONFIG_PCM_S8_MUXER 1 +#define CONFIG_PCM_U32BE_MUXER 1 +#define CONFIG_PCM_U32LE_MUXER 1 +#define CONFIG_PCM_U24BE_MUXER 1 +#define CONFIG_PCM_U24LE_MUXER 1 +#define CONFIG_PCM_U16BE_MUXER 1 +#define CONFIG_PCM_U16LE_MUXER 1 +#define CONFIG_PCM_U8_MUXER 1 +#define CONFIG_PSP_MUXER 1 +#define CONFIG_RAWVIDEO_MUXER 1 +#define CONFIG_RM_MUXER 1 +#define CONFIG_ROQ_MUXER 1 +#define CONFIG_RTP_MUXER 1 +#define CONFIG_RTSP_MUXER 1 +#define CONFIG_SOX_MUXER 1 +#define CONFIG_SPDIF_MUXER 1 +#define CONFIG_SWF_MUXER 1 +#define CONFIG_TG2_MUXER 1 +#define CONFIG_TGP_MUXER 1 +#define CONFIG_TRUEHD_MUXER 1 +#define CONFIG_VC1T_MUXER 1 +#define CONFIG_VOC_MUXER 1 +#define CONFIG_WAV_MUXER 1 +#define CONFIG_WEBM_MUXER 1 +#define CONFIG_YUV4MPEGPIPE_MUXER 1 +#define CONFIG_LIBNUT_MUXER 0 +#define CONFIG_ASPECT_FILTER 1 +#define CONFIG_CROP_FILTER 1 +#define CONFIG_FORMAT_FILTER 1 +#define CONFIG_NOFORMAT_FILTER 1 +#define CONFIG_NULL_FILTER 1 +#define CONFIG_PAD_FILTER 1 +#define CONFIG_PIXDESCTEST_FILTER 1 +#define CONFIG_PIXELASPECT_FILTER 1 +#define CONFIG_SCALE_FILTER 1 +#define CONFIG_SLICIFY_FILTER 1 +#define CONFIG_UNSHARP_FILTER 1 +#define CONFIG_VFLIP_FILTER 1 +#define CONFIG_BUFFER_FILTER 1 +#define CONFIG_NULLSRC_FILTER 1 +#define CONFIG_NULLSINK_FILTER 1 +#define CONFIG_FILE_PROTOCOL 1 +#define CONFIG_GOPHER_PROTOCOL 1 +#define CONFIG_HTTP_PROTOCOL 1 +#define CONFIG_MMST_PROTOCOL 1 +#define CONFIG_PIPE_PROTOCOL 1 +#define CONFIG_RTMP_PROTOCOL 1 +#define CONFIG_RTMPT_PROTOCOL 1 +#define CONFIG_RTMPE_PROTOCOL 1 +#define CONFIG_RTMPTE_PROTOCOL 1 +#define CONFIG_RTMPS_PROTOCOL 1 +#define CONFIG_RTP_PROTOCOL 1 +#define CONFIG_TCP_PROTOCOL 1 +#define CONFIG_UDP_PROTOCOL 1 +#define CONFIG_CONCAT_PROTOCOL 1 +#define CONFIG_ALSA_INDEV 0 +#define CONFIG_BKTR_INDEV 0 +#define CONFIG_DV1394_INDEV 1 +#define CONFIG_JACK_INDEV 0 +#define CONFIG_OSS_INDEV 1 +#define CONFIG_V4L2_INDEV 1 +#define CONFIG_V4L_INDEV 1 +#define CONFIG_VFWCAP_INDEV 0 +#define CONFIG_X11_GRAB_DEVICE_INDEV 0 +#define CONFIG_LIBDC1394_INDEV 0 +#define CONFIG_ALSA_OUTDEV 0 +#define CONFIG_OSS_OUTDEV 1 +#endif /* FFMPEG_CONFIG_H */ diff --git a/plugins/supereq/ffmpeg_fft/ffmpeg_fft.h b/plugins/supereq/ffmpeg_fft/ffmpeg_fft.h new file mode 100644 index 00000000..b98313d2 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/ffmpeg_fft.h @@ -0,0 +1,95 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVFFT_H +#define AVCODEC_AVFFT_H + +typedef float FFTSample; + +typedef struct FFTComplex { + FFTSample re, im; +} FFTComplex; + +//#define FFTC_SZ 32 +typedef struct FFTContext FFTContext; + +/** + * Set up a complex FFT. + * @param nbits log2 of the length of the input array + * @param inverse if 0 perform the forward transform, if 1 perform the inverse + */ +FFTContext *av_fft_init(int nbits, int inverse); + +/** + * Do the permutation needed BEFORE calling ff_fft_calc(). + */ +void av_fft_permute(FFTContext *s, FFTComplex *z); + +/** + * Do a complex FFT with the parameters defined in av_fft_init(). The + * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + */ +void av_fft_calc(FFTContext *s, FFTComplex *z); + +void av_fft_end(FFTContext *s); + +/* Real Discrete Fourier Transform */ + +enum RDFTransformType { + DFT_R2C, + IDFT_C2R, + IDFT_R2C, + DFT_C2R, +}; + +//#define RDFTC_SZ 56 +typedef struct RDFTContext RDFTContext; + +/** + * Set up a real FFT. + * @param nbits log2 of the length of the input array + * @param trans the type of transform + */ +RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans); +void av_rdft_calc(RDFTContext *s, FFTSample *data); +void av_rdft_end(RDFTContext *s); + +/* Discrete Cosine Transform */ + +typedef struct DCTContext DCTContext; + +enum DCTTransformType { + DCT_II = 0, + DCT_III, + DCT_I, + DST_I, +}; + +/** + * Set up DCT. + * @param nbits size of the input array: + * (1 << nbits) for DCT-II, DCT-III and DST-I + * (1 << nbits) + 1 for DCT-I + * + * @note the first element of the input of DST-I is ignored + */ +DCTContext *av_dct_init(int nbits, enum DCTTransformType type); +void av_dct_calc(DCTContext *s, FFTSample *data); +void av_dct_end (DCTContext *s); + +#endif /* AVCODEC_AVFFT_H */ diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/arm/asm.S b/plugins/supereq/ffmpeg_fft/libavcodec/arm/asm.S new file mode 100644 index 00000000..6860f1cf --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/arm/asm.S @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008 Mans Rullgard <mans@mansr.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#ifdef __ELF__ +# define ELF +#else +# define ELF @ +#endif + +.macro require8 val=1 +ELF .eabi_attribute 24, \val +.endm + +.macro preserve8 val=1 +ELF .eabi_attribute 25, \val +.endm + +/* +.macro function name, export=0 + .macro endfunc +ELF .size \name, . - \name + .endfunc + .purgem endfunc + .endm + .text + .if \export + .global EXTERN_ASM\name +EXTERN_ASM\name: + .endif +ELF .type \name, %function + .func \name +\name: +.endm +*/ + +.macro function name, export=0 + .macro endfunc +ELF .size \name, . - \name + .endfunc + .purgem endfunc + .endm + .text + .if \export + .hidden EXTERN_ASM\name + .global EXTERN_ASM\name +EXTERN_ASM\name: + .endif +ELF .type \name, %function + .func \name +\name: +.endm + +.macro mov32 rd, val +#if HAVE_ARMV6T2 + movw \rd, #(\val) & 0xffff + .if (\val) >> 16 + movt \rd, #(\val) >> 16 + .endif +#else + ldr \rd, =\val +#endif +.endm + +.macro movrel rd, val +#if HAVE_ARMV6T2 && !CONFIG_PIC + movw \rd, #:lower16:\val + movt \rd, #:upper16:\val +#else + ldr \rd, =\val +#endif +.endm + +#if HAVE_VFP_ARGS + .eabi_attribute 28, 1 +# define VFP +# define NOVFP @ +#else +# define VFP @ +# define NOVFP +#endif + +#define GLUE(a, b) a ## b +#define JOIN(a, b) GLUE(a, b) +#define X(s) JOIN(EXTERN_ASM, s) + diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/arm/fft_init_arm.c b/plugins/supereq/ffmpeg_fft/libavcodec/arm/fft_init_arm.c new file mode 100644 index 00000000..28148e92 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/arm/fft_init_arm.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2009 Mans Rullgard <mans@mansr.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/fft.h" +#if CONFIG_DCA_DECODER +#include "libavcodec/synth_filter.h" +#endif + +void ff_fft_permute_neon(FFTContext *s, FFTComplex *z); +void ff_fft_calc_neon(FFTContext *s, FFTComplex *z); + +#if 0 +void ff_imdct_calc_neon(FFTContext *s, FFTSample *output, const FFTSample *input); +void ff_imdct_half_neon(FFTContext *s, FFTSample *output, const FFTSample *input); +void ff_mdct_calc_neon(FFTContext *s, FFTSample *output, const FFTSample *input); +#endif + +void ff_rdft_calc_neon(struct RDFTContext *s, FFTSample *z); + +void ff_synth_filter_float_neon(FFTContext *imdct, + float *synth_buf_ptr, int *synth_buf_offset, + float synth_buf2[32], const float window[512], + float out[32], const float in[32], + float scale, float bias); + +av_cold void ff_fft_init_arm(FFTContext *s) +{ + if (HAVE_NEON) { + s->fft_permute = ff_fft_permute_neon; + s->fft_calc = ff_fft_calc_neon; +#if 0 + s->imdct_calc = ff_imdct_calc_neon; + s->imdct_half = ff_imdct_half_neon; + s->mdct_calc = ff_mdct_calc_neon; + s->permutation = FF_MDCT_PERM_INTERLEAVE; +#endif + } +} + +#if CONFIG_RDFT +av_cold void ff_rdft_init_arm(RDFTContext *s) +{ + if (HAVE_NEON) + s->rdft_calc = ff_rdft_calc_neon; +} +#endif + +#if CONFIG_DCA_DECODER +av_cold void ff_synth_filter_init_arm(SynthFilterContext *s) +{ + if (HAVE_NEON) + s->synth_filter_float = ff_synth_filter_float_neon; +} +#endif diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/arm/fft_neon.S b/plugins/supereq/ffmpeg_fft/libavcodec/arm/fft_neon.S new file mode 100644 index 00000000..117f4fee --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/arm/fft_neon.S @@ -0,0 +1,372 @@ +/* + * ARM NEON optimised FFT + * + * Copyright (c) 2009 Mans Rullgard <mans@mansr.com> + * Copyright (c) 2009 Naotoshi Nojiri + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "asm.S" + +#define M_SQRT1_2 0.70710678118654752440 + + .text + +function fft4_neon + vld1.32 {d0-d3}, [r0,:128] + + vext.32 q8, q1, q1, #1 @ i2,r3 d3=i3,r2 + vsub.f32 d6, d0, d1 @ r0-r1,i0-i1 + vsub.f32 d7, d16, d17 @ r3-r2,i2-i3 + vadd.f32 d4, d0, d1 @ r0+r1,i0+i1 + vadd.f32 d5, d2, d3 @ i2+i3,r2+r3 + vadd.f32 d1, d6, d7 + vsub.f32 d3, d6, d7 + vadd.f32 d0, d4, d5 + vsub.f32 d2, d4, d5 + + vst1.32 {d0-d3}, [r0,:128] + + bx lr +endfunc + +function fft8_neon + mov r1, r0 + vld1.32 {d0-d3}, [r1,:128]! + vld1.32 {d16-d19}, [r1,:128] + + movw r2, #0x04f3 @ sqrt(1/2) + movt r2, #0x3f35 + eor r3, r2, #1<<31 + vdup.32 d31, r2 + + vext.32 q11, q1, q1, #1 @ i2,r3,i3,r2 + vadd.f32 d4, d16, d17 @ r4+r5,i4+i5 + vmov d28, r3, r2 + vadd.f32 d5, d18, d19 @ r6+r7,i6+i7 + vsub.f32 d17, d16, d17 @ r4-r5,i4-i5 + vsub.f32 d19, d18, d19 @ r6-r7,i6-i7 + vrev64.32 d29, d28 + vadd.f32 d20, d0, d1 @ r0+r1,i0+i1 + vadd.f32 d21, d2, d3 @ r2+r3,i2+i3 + vmul.f32 d26, d17, d28 @ -a2r*w,a2i*w + vext.32 q3, q2, q2, #1 + vmul.f32 d27, d19, d29 @ a3r*w,-a3i*w + vsub.f32 d23, d22, d23 @ i2-i3,r3-r2 + vsub.f32 d22, d0, d1 @ r0-r1,i0-i1 + vmul.f32 d24, d17, d31 @ a2r*w,a2i*w + vmul.f32 d25, d19, d31 @ a3r*w,a3i*w + vadd.f32 d0, d20, d21 + vsub.f32 d2, d20, d21 + vadd.f32 d1, d22, d23 + vrev64.32 q13, q13 + vsub.f32 d3, d22, d23 + vsub.f32 d6, d6, d7 + vadd.f32 d24, d24, d26 @ a2r+a2i,a2i-a2r t1,t2 + vadd.f32 d25, d25, d27 @ a3r-a3i,a3i+a3r t5,t6 + vadd.f32 d7, d4, d5 + vsub.f32 d18, d2, d6 + vext.32 q13, q12, q12, #1 + vadd.f32 d2, d2, d6 + vsub.f32 d16, d0, d7 + vadd.f32 d5, d25, d24 + vsub.f32 d4, d26, d27 + vadd.f32 d0, d0, d7 + vsub.f32 d17, d1, d5 + vsub.f32 d19, d3, d4 + vadd.f32 d3, d3, d4 + vadd.f32 d1, d1, d5 + + vst1.32 {d16-d19}, [r1,:128] + vst1.32 {d0-d3}, [r0,:128] + + bx lr +endfunc + +function fft16_neon + movrel r1, mppm + vld1.32 {d16-d19}, [r0,:128]! @ q8{r0,i0,r1,i1} q9{r2,i2,r3,i3} + pld [r0, #32] + vld1.32 {d2-d3}, [r1,:128] + vext.32 q13, q9, q9, #1 + vld1.32 {d22-d25}, [r0,:128]! @ q11{r4,i4,r5,i5} q12{r6,i5,r7,i7} + vadd.f32 d4, d16, d17 + vsub.f32 d5, d16, d17 + vadd.f32 d18, d18, d19 + vsub.f32 d19, d26, d27 + + vadd.f32 d20, d22, d23 + vsub.f32 d22, d22, d23 + vsub.f32 d23, d24, d25 + vadd.f32 q8, q2, q9 @ {r0,i0,r1,i1} + vadd.f32 d21, d24, d25 + vmul.f32 d24, d22, d2 + vsub.f32 q9, q2, q9 @ {r2,i2,r3,i3} + vmul.f32 d25, d23, d3 + vuzp.32 d16, d17 @ {r0,r1,i0,i1} + vmul.f32 q1, q11, d2[1] + vuzp.32 d18, d19 @ {r2,r3,i2,i3} + vrev64.32 q12, q12 + vadd.f32 q11, q12, q1 @ {t1a,t2a,t5,t6} + vld1.32 {d24-d27}, [r0,:128]! @ q12{r8,i8,r9,i9} q13{r10,i10,r11,i11} + vzip.32 q10, q11 + vld1.32 {d28-d31}, [r0,:128] @ q14{r12,i12,r13,i13} q15{r14,i14,r15,i15} + vadd.f32 d0, d22, d20 + vadd.f32 d1, d21, d23 + vsub.f32 d2, d21, d23 + vsub.f32 d3, d22, d20 + sub r0, r0, #96 + vext.32 q13, q13, q13, #1 + vsub.f32 q10, q8, q0 @ {r4,r5,i4,i5} + vadd.f32 q8, q8, q0 @ {r0,r1,i0,i1} + vext.32 q15, q15, q15, #1 + vsub.f32 q11, q9, q1 @ {r6,r7,i6,i7} + vswp d25, d26 @ q12{r8,i8,i10,r11} q13{r9,i9,i11,r10} + vadd.f32 q9, q9, q1 @ {r2,r3,i2,i3} + vswp d29, d30 @ q14{r12,i12,i14,r15} q15{r13,i13,i15,r14} + vadd.f32 q0, q12, q13 @ {t1,t2,t5,t6} + vadd.f32 q1, q14, q15 @ {t1a,t2a,t5a,t6a} + movrel r2, X(ff_cos_16) + vsub.f32 q13, q12, q13 @ {t3,t4,t7,t8} + vrev64.32 d1, d1 + vsub.f32 q15, q14, q15 @ {t3a,t4a,t7a,t8a} + vrev64.32 d3, d3 + movrel r3, pmmp + vswp d1, d26 @ q0{t1,t2,t3,t4} q13{t6,t5,t7,t8} + vswp d3, d30 @ q1{t1a,t2a,t3a,t4a} q15{t6a,t5a,t7a,t8a} + vadd.f32 q12, q0, q13 @ {r8,i8,r9,i9} + vadd.f32 q14, q1, q15 @ {r12,i12,r13,i13} + vld1.32 {d4-d5}, [r2,:64] + vsub.f32 q13, q0, q13 @ {r10,i10,r11,i11} + vsub.f32 q15, q1, q15 @ {r14,i14,r15,i15} + vswp d25, d28 @ q12{r8,i8,r12,i12} q14{r9,i9,r13,i13} + vld1.32 {d6-d7}, [r3,:128] + vrev64.32 q1, q14 + vmul.f32 q14, q14, d4[1] + vmul.f32 q1, q1, q3 + vmla.f32 q14, q1, d5[1] @ {t1a,t2a,t5a,t6a} + vswp d27, d30 @ q13{r10,i10,r14,i14} q15{r11,i11,r15,i15} + vzip.32 q12, q14 + vadd.f32 d0, d28, d24 + vadd.f32 d1, d25, d29 + vsub.f32 d2, d25, d29 + vsub.f32 d3, d28, d24 + vsub.f32 q12, q8, q0 @ {r8,r9,i8,i9} + vadd.f32 q8, q8, q0 @ {r0,r1,i0,i1} + vsub.f32 q14, q10, q1 @ {r12,r13,i12,i13} + mov r1, #32 + vadd.f32 q10, q10, q1 @ {r4,r5,i4,i5} + vrev64.32 q0, q13 + vmul.f32 q13, q13, d5[0] + vrev64.32 q1, q15 + vmul.f32 q15, q15, d5[1] + vst2.32 {d16-d17},[r0,:128], r1 + vmul.f32 q0, q0, q3 + vst2.32 {d20-d21},[r0,:128], r1 + vmul.f32 q1, q1, q3 + vmla.f32 q13, q0, d5[0] @ {t1,t2,t5,t6} + vmla.f32 q15, q1, d4[1] @ {t1a,t2a,t5a,t6a} + vst2.32 {d24-d25},[r0,:128], r1 + vst2.32 {d28-d29},[r0,:128] + vzip.32 q13, q15 + sub r0, r0, #80 + vadd.f32 d0, d30, d26 + vadd.f32 d1, d27, d31 + vsub.f32 d2, d27, d31 + vsub.f32 d3, d30, d26 + vsub.f32 q13, q9, q0 @ {r10,r11,i10,i11} + vadd.f32 q9, q9, q0 @ {r2,r3,i2,i3} + vsub.f32 q15, q11, q1 @ {r14,r15,i14,i15} + vadd.f32 q11, q11, q1 @ {r6,r7,i6,i7} + vst2.32 {d18-d19},[r0,:128], r1 + vst2.32 {d22-d23},[r0,:128], r1 + vst2.32 {d26-d27},[r0,:128], r1 + vst2.32 {d30-d31},[r0,:128] + bx lr +endfunc + +function fft_pass_neon + push {r4-r6,lr} + mov r6, r2 @ n + lsl r5, r2, #3 @ 2 * n * sizeof FFTSample + lsl r4, r2, #4 @ 2 * n * sizeof FFTComplex + lsl r2, r2, #5 @ 4 * n * sizeof FFTComplex + add r3, r2, r4 + add r4, r4, r0 @ &z[o1] + add r2, r2, r0 @ &z[o2] + add r3, r3, r0 @ &z[o3] + vld1.32 {d20-d21},[r2,:128] @ {z[o2],z[o2+1]} + movrel r12, pmmp + vld1.32 {d22-d23},[r3,:128] @ {z[o3],z[o3+1]} + add r5, r5, r1 @ wim + vld1.32 {d6-d7}, [r12,:128] @ pmmp + vswp d21, d22 + vld1.32 {d4}, [r1,:64]! @ {wre[0],wre[1]} + sub r5, r5, #4 @ wim-- + vrev64.32 q1, q11 + vmul.f32 q11, q11, d4[1] + vmul.f32 q1, q1, q3 + vld1.32 {d5[0]}, [r5,:32] @ d5[0] = wim[-1] + vmla.f32 q11, q1, d5[0] @ {t1a,t2a,t5a,t6a} + vld2.32 {d16-d17},[r0,:128] @ {z[0],z[1]} + sub r6, r6, #1 @ n-- + vld2.32 {d18-d19},[r4,:128] @ {z[o1],z[o1+1]} + vzip.32 q10, q11 + vadd.f32 d0, d22, d20 + vadd.f32 d1, d21, d23 + vsub.f32 d2, d21, d23 + vsub.f32 d3, d22, d20 + vsub.f32 q10, q8, q0 + vadd.f32 q8, q8, q0 + vsub.f32 q11, q9, q1 + vadd.f32 q9, q9, q1 + vst2.32 {d20-d21},[r2,:128]! @ {z[o2],z[o2+1]} + vst2.32 {d16-d17},[r0,:128]! @ {z[0],z[1]} + vst2.32 {d22-d23},[r3,:128]! @ {z[o3],z[o3+1]} + vst2.32 {d18-d19},[r4,:128]! @ {z[o1],z[o1+1]} + sub r5, r5, #8 @ wim -= 2 +1: + vld1.32 {d20-d21},[r2,:128] @ {z[o2],z[o2+1]} + vld1.32 {d22-d23},[r3,:128] @ {z[o3],z[o3+1]} + vswp d21, d22 + vld1.32 {d4}, [r1]! @ {wre[0],wre[1]} + vrev64.32 q0, q10 + vmul.f32 q10, q10, d4[0] + vrev64.32 q1, q11 + vmul.f32 q11, q11, d4[1] + vld1.32 {d5}, [r5] @ {wim[-1],wim[0]} + vmul.f32 q0, q0, q3 + sub r5, r5, #8 @ wim -= 2 + vmul.f32 q1, q1, q3 + vmla.f32 q10, q0, d5[1] @ {t1,t2,t5,t6} + vmla.f32 q11, q1, d5[0] @ {t1a,t2a,t5a,t6a} + vld2.32 {d16-d17},[r0,:128] @ {z[0],z[1]} + subs r6, r6, #1 @ n-- + vld2.32 {d18-d19},[r4,:128] @ {z[o1],z[o1+1]} + vzip.32 q10, q11 + vadd.f32 d0, d22, d20 + vadd.f32 d1, d21, d23 + vsub.f32 d2, d21, d23 + vsub.f32 d3, d22, d20 + vsub.f32 q10, q8, q0 + vadd.f32 q8, q8, q0 + vsub.f32 q11, q9, q1 + vadd.f32 q9, q9, q1 + vst2.32 {d20-d21}, [r2,:128]! @ {z[o2],z[o2+1]} + vst2.32 {d16-d17}, [r0,:128]! @ {z[0],z[1]} + vst2.32 {d22-d23}, [r3,:128]! @ {z[o3],z[o3+1]} + vst2.32 {d18-d19}, [r4,:128]! @ {z[o1],z[o1+1]} + bne 1b + + pop {r4-r6,pc} +endfunc + +.macro def_fft n, n2, n4 + .align 6 +function fft\n\()_neon + push {r4, lr} + mov r4, r0 + bl fft\n2\()_neon + add r0, r4, #\n4*2*8 + bl fft\n4\()_neon + add r0, r4, #\n4*3*8 + bl fft\n4\()_neon + mov r0, r4 + pop {r4, lr} + movrel r1, X(ff_cos_\n) + mov r2, #\n4/2 + b fft_pass_neon +endfunc +.endm + + def_fft 32, 16, 8 + def_fft 64, 32, 16 + def_fft 128, 64, 32 + def_fft 256, 128, 64 + def_fft 512, 256, 128 + def_fft 1024, 512, 256 + def_fft 2048, 1024, 512 + def_fft 4096, 2048, 1024 + def_fft 8192, 4096, 2048 + def_fft 16384, 8192, 4096 + def_fft 32768, 16384, 8192 + def_fft 65536, 32768, 16384 + +function ff_fft_calc_neon, export=1 + ldr r2, [r0] + sub r2, r2, #2 + movrel r3, fft_tab_neon + ldr r3, [r3, r2, lsl #2] + mov r0, r1 + bx r3 +endfunc + +function ff_fft_permute_neon, export=1 + push {r4,lr} + mov r12, #1 + ldr r2, [r0] @ nbits + ldr r3, [r0, #12] @ tmp_buf + ldr r0, [r0, #8] @ revtab + lsl r12, r12, r2 + mov r2, r12 +1: + vld1.32 {d0-d1}, [r1,:128]! + ldr r4, [r0], #4 + uxth lr, r4 + uxth r4, r4, ror #16 + add lr, r3, lr, lsl #3 + add r4, r3, r4, lsl #3 + vst1.32 {d0}, [lr,:64] + vst1.32 {d1}, [r4,:64] + subs r12, r12, #2 + bgt 1b + + sub r1, r1, r2, lsl #3 +1: + vld1.32 {d0-d3}, [r3,:128]! + vst1.32 {d0-d3}, [r1,:128]! + subs r2, r2, #4 + bgt 1b + + pop {r4,pc} +endfunc + + .section .rodata + .align 4 +fft_tab_neon: + .word fft4_neon + .word fft8_neon + .word fft16_neon + .word fft32_neon + .word fft64_neon + .word fft128_neon + .word fft256_neon + .word fft512_neon + .word fft1024_neon + .word fft2048_neon + .word fft4096_neon + .word fft8192_neon + .word fft16384_neon + .word fft32768_neon + .word fft65536_neon +ELF .size fft_tab_neon, . - fft_tab_neon + + .align 4 +pmmp: .float +1.0, -1.0, -1.0, +1.0 +mppm: .float -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2 + diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/arm/rdft_neon.S b/plugins/supereq/ffmpeg_fft/libavcodec/arm/rdft_neon.S new file mode 100644 index 00000000..4f8a1032 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/arm/rdft_neon.S @@ -0,0 +1,151 @@ +/* + * ARM NEON optimised RDFT + * Copyright (c) 2009 Mans Rullgard <mans@mansr.com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "asm.S" + + preserve8 + +function ff_rdft_calc_neon, export=1 + push {r4-r8,lr} + + ldr r6, [r0, #4] @ inverse + mov r4, r0 + mov r5, r1 + + lsls r6, r6, #31 + bne 1f + add r0, r4, #20 + bl X(ff_fft_permute_neon) + add r0, r4, #20 + mov r1, r5 + bl X(ff_fft_calc_neon) +1: + ldr r12, [r4, #0] @ nbits + mov r2, #1 + lsl r12, r2, r12 + add r0, r5, #8 + add r1, r5, r12, lsl #2 + lsr r12, r12, #2 + ldr r2, [r4, #12] @ tcos + sub r12, r12, #2 + ldr r3, [r4, #16] @ tsin + mov r7, r0 + sub r1, r1, #8 + mov lr, r1 + mov r8, #-8 + vld1.32 {d0}, [r0,:64]! @ d1[0,1] + vld1.32 {d1}, [r1,:64], r8 @ d2[0,1] + vld1.32 {d4}, [r2,:64]! @ tcos[i] + vld1.32 {d5}, [r3,:64]! @ tsin[i] + vmov.f32 d18, #0.5 @ k1 + vdup.32 d19, r6 + pld [r0, #32] + veor d19, d18, d19 @ k2 + vmov.i32 d16, #0 + vmov.i32 d17, #1<<31 + pld [r1, #-32] + vtrn.32 d16, d17 + pld [r2, #32] + vrev64.32 d16, d16 @ d16=1,0 d17=0,1 + pld [r3, #32] +2: + veor q1, q0, q8 @ -d1[0],d1[1], d2[0],-d2[1] + vld1.32 {d24}, [r0,:64]! @ d1[0,1] + vadd.f32 d0, d0, d3 @ d1[0]+d2[0], d1[1]-d2[1] + vld1.32 {d25}, [r1,:64], r8 @ d2[0,1] + vadd.f32 d1, d2, d1 @ -d1[0]+d2[0], d1[1]+d2[1] + veor q3, q12, q8 @ -d1[0],d1[1], d2[0],-d2[1] + pld [r0, #32] + vmul.f32 q10, q0, q9 @ ev.re, ev.im, od.im, od.re + pld [r1, #-32] + vadd.f32 d0, d24, d7 @ d1[0]+d2[0], d1[1]-d2[1] + vadd.f32 d1, d6, d25 @ -d1[0]+d2[0], d1[1]+d2[1] + vmul.f32 q11, q0, q9 @ ev.re, ev.im, od.im, od.re + veor d7, d21, d16 @ -od.im, od.re + vrev64.32 d3, d21 @ od.re, od.im + veor d6, d20, d17 @ ev.re,-ev.im + veor d2, d3, d16 @ -od.re, od.im + vmla.f32 d20, d3, d4[1] + vmla.f32 d20, d7, d5[1] + vmla.f32 d6, d2, d4[1] + vmla.f32 d6, d21, d5[1] + vld1.32 {d4}, [r2,:64]! @ tcos[i] + veor d7, d23, d16 @ -od.im, od.re + vld1.32 {d5}, [r3,:64]! @ tsin[i] + veor d24, d22, d17 @ ev.re,-ev.im + vrev64.32 d3, d23 @ od.re, od.im + pld [r2, #32] + veor d2, d3, d16 @ -od.re, od.im + pld [r3, #32] + vmla.f32 d22, d3, d4[0] + vmla.f32 d22, d7, d5[0] + vmla.f32 d24, d2, d4[0] + vmla.f32 d24, d23, d5[0] + vld1.32 {d0}, [r0,:64]! @ d1[0,1] + vld1.32 {d1}, [r1,:64], r8 @ d2[0,1] + vst1.32 {d20}, [r7,:64]! + vst1.32 {d6}, [lr,:64], r8 + vst1.32 {d22}, [r7,:64]! + vst1.32 {d24}, [lr,:64], r8 + subs r12, r12, #2 + bgt 2b + + veor q1, q0, q8 @ -d1[0],d1[1], d2[0],-d2[1] + vadd.f32 d0, d0, d3 @ d1[0]+d2[0], d1[1]-d2[1] + vadd.f32 d1, d2, d1 @ -d1[0]+d2[0], d1[1]+d2[1] + ldr r2, [r4, #8] @ sign_convention + vmul.f32 q10, q0, q9 @ ev.re, ev.im, od.im, od.re + add r0, r0, #4 + bfc r2, #0, #31 + vld1.32 {d0[0]}, [r0,:32] + veor d7, d21, d16 @ -od.im, od.re + vrev64.32 d3, d21 @ od.re, od.im + veor d6, d20, d17 @ ev.re,-ev.im + vld1.32 {d22}, [r5,:64] + vdup.32 d1, r2 + vmov d23, d22 + veor d2, d3, d16 @ -od.re, od.im + vtrn.32 d22, d23 + veor d0, d0, d1 + veor d23, d23, d17 + vmla.f32 d20, d3, d4[1] + vmla.f32 d20, d7, d5[1] + vmla.f32 d6, d2, d4[1] + vmla.f32 d6, d21, d5[1] + vadd.f32 d22, d22, d23 + vst1.32 {d20}, [r7,:64] + vst1.32 {d6}, [lr,:64] + vst1.32 {d0[0]}, [r0,:32] + vst1.32 {d22}, [r5,:64] + + cmp r6, #0 + popeq {r4-r8,pc} + + vmul.f32 d22, d22, d18 + vst1.32 {d22}, [r5,:64] + add r0, r4, #20 + mov r1, r5 + bl X(ff_fft_permute_neon) + add r0, r4, #20 + mov r1, r5 + pop {r4-r8,lr} + b X(ff_fft_calc_neon) +endfunc diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/arm/simple_idct_neon.S b/plugins/supereq/ffmpeg_fft/libavcodec/arm/simple_idct_neon.S new file mode 100644 index 00000000..17cde583 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/arm/simple_idct_neon.S @@ -0,0 +1,372 @@ +/* + * ARM NEON IDCT + * + * Copyright (c) 2008 Mans Rullgard <mans@mansr.com> + * + * Based on Simple IDCT + * Copyright (c) 2001 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "asm.S" + +#define W1 22725 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W2 21407 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W3 19266 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W4 16383 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W5 12873 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W6 8867 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W7 4520 //cos(i*M_PI/16)*sqrt(2)*(1<<14) + 0.5 +#define W4c ((1<<(COL_SHIFT-1))/W4) +#define ROW_SHIFT 11 +#define COL_SHIFT 20 + +#define w1 d0[0] +#define w2 d0[1] +#define w3 d0[2] +#define w4 d0[3] +#define w5 d1[0] +#define w6 d1[1] +#define w7 d1[2] +#define w4c d1[3] + + .macro idct_col4_top + vmull.s16 q7, d6, w2 /* q9 = W2 * col[2] */ + vmull.s16 q8, d6, w6 /* q10 = W6 * col[2] */ + vmull.s16 q9, d4, w1 /* q9 = W1 * col[1] */ + vadd.i32 q11, q15, q7 + vmull.s16 q10, d4, w3 /* q10 = W3 * col[1] */ + vadd.i32 q12, q15, q8 + vmull.s16 q5, d4, w5 /* q5 = W5 * col[1] */ + vsub.i32 q13, q15, q8 + vmull.s16 q6, d4, w7 /* q6 = W7 * col[1] */ + vsub.i32 q14, q15, q7 + + vmlal.s16 q9, d8, w3 /* q9 += W3 * col[3] */ + vmlsl.s16 q10, d8, w7 /* q10 -= W7 * col[3] */ + vmlsl.s16 q5, d8, w1 /* q5 -= W1 * col[3] */ + vmlsl.s16 q6, d8, w5 /* q6 -= W5 * col[3] */ + .endm + + .text + .align 6 + +function idct_row4_pld_neon + pld [r0] + add r3, r0, r1, lsl #2 + pld [r0, r1] + pld [r0, r1, lsl #1] + pld [r3, -r1] + pld [r3] + pld [r3, r1] + add r3, r3, r1, lsl #1 + pld [r3] + pld [r3, r1] +endfunc + +function idct_row4_neon + vmov.i32 q15, #(1<<(ROW_SHIFT-1)) + vld1.64 {d2-d5}, [r2,:128]! + vmlal.s16 q15, d2, w4 /* q15 += W4 * col[0] */ + vld1.64 {d6,d7}, [r2,:128]! + vorr d10, d3, d5 + vld1.64 {d8,d9}, [r2,:128]! + add r2, r2, #-64 + + vorr d11, d7, d9 + vorr d10, d10, d11 + vmov r3, r4, d10 + + idct_col4_top + + orrs r3, r3, r4 + beq 1f + + vmull.s16 q7, d3, w4 /* q7 = W4 * col[4] */ + vmlal.s16 q9, d5, w5 /* q9 += W5 * col[5] */ + vmlsl.s16 q10, d5, w1 /* q10 -= W1 * col[5] */ + vmull.s16 q8, d7, w2 /* q8 = W2 * col[6] */ + vmlal.s16 q5, d5, w7 /* q5 += W7 * col[5] */ + vadd.i32 q11, q11, q7 + vsub.i32 q12, q12, q7 + vsub.i32 q13, q13, q7 + vadd.i32 q14, q14, q7 + vmlal.s16 q6, d5, w3 /* q6 += W3 * col[5] */ + vmull.s16 q7, d7, w6 /* q7 = W6 * col[6] */ + vmlal.s16 q9, d9, w7 + vmlsl.s16 q10, d9, w5 + vmlal.s16 q5, d9, w3 + vmlsl.s16 q6, d9, w1 + vadd.i32 q11, q11, q7 + vsub.i32 q12, q12, q8 + vadd.i32 q13, q13, q8 + vsub.i32 q14, q14, q7 + +1: vadd.i32 q3, q11, q9 + vadd.i32 q4, q12, q10 + vshrn.i32 d2, q3, #ROW_SHIFT + vshrn.i32 d4, q4, #ROW_SHIFT + vadd.i32 q7, q13, q5 + vadd.i32 q8, q14, q6 + vtrn.16 d2, d4 + vshrn.i32 d6, q7, #ROW_SHIFT + vshrn.i32 d8, q8, #ROW_SHIFT + vsub.i32 q14, q14, q6 + vsub.i32 q11, q11, q9 + vtrn.16 d6, d8 + vsub.i32 q13, q13, q5 + vshrn.i32 d3, q14, #ROW_SHIFT + vtrn.32 d2, d6 + vsub.i32 q12, q12, q10 + vtrn.32 d4, d8 + vshrn.i32 d5, q13, #ROW_SHIFT + vshrn.i32 d7, q12, #ROW_SHIFT + vshrn.i32 d9, q11, #ROW_SHIFT + + vtrn.16 d3, d5 + vtrn.16 d7, d9 + vtrn.32 d3, d7 + vtrn.32 d5, d9 + + vst1.64 {d2-d5}, [r2,:128]! + vst1.64 {d6-d9}, [r2,:128]! + + bx lr +endfunc + +function idct_col4_neon + mov ip, #16 + vld1.64 {d2}, [r2,:64], ip /* d2 = col[0] */ + vdup.16 d30, w4c + vld1.64 {d4}, [r2,:64], ip /* d3 = col[1] */ + vadd.i16 d30, d30, d2 + vld1.64 {d6}, [r2,:64], ip /* d4 = col[2] */ + vmull.s16 q15, d30, w4 /* q15 = W4*(col[0]+(1<<COL_SHIFT-1)/W4)*/ + vld1.64 {d8}, [r2,:64], ip /* d5 = col[3] */ + + ldrd r4, [r2] + ldrd r6, [r2, #16] + orrs r4, r4, r5 + + idct_col4_top + addeq r2, r2, #16 + beq 1f + + vld1.64 {d3}, [r2,:64], ip /* d6 = col[4] */ + vmull.s16 q7, d3, w4 /* q7 = W4 * col[4] */ + vadd.i32 q11, q11, q7 + vsub.i32 q12, q12, q7 + vsub.i32 q13, q13, q7 + vadd.i32 q14, q14, q7 + +1: orrs r6, r6, r7 + ldrd r4, [r2, #16] + addeq r2, r2, #16 + beq 2f + + vld1.64 {d5}, [r2,:64], ip /* d7 = col[5] */ + vmlal.s16 q9, d5, w5 /* q9 += W5 * col[5] */ + vmlsl.s16 q10, d5, w1 /* q10 -= W1 * col[5] */ + vmlal.s16 q5, d5, w7 /* q5 += W7 * col[5] */ + vmlal.s16 q6, d5, w3 /* q6 += W3 * col[5] */ + +2: orrs r4, r4, r5 + ldrd r4, [r2, #16] + addeq r2, r2, #16 + beq 3f + + vld1.64 {d7}, [r2,:64], ip /* d8 = col[6] */ + vmull.s16 q7, d7, w6 /* q7 = W6 * col[6] */ + vmull.s16 q8, d7, w2 /* q8 = W2 * col[6] */ + vadd.i32 q11, q11, q7 + vsub.i32 q14, q14, q7 + vsub.i32 q12, q12, q8 + vadd.i32 q13, q13, q8 + +3: orrs r4, r4, r5 + addeq r2, r2, #16 + beq 4f + + vld1.64 {d9}, [r2,:64], ip /* d9 = col[7] */ + vmlal.s16 q9, d9, w7 + vmlsl.s16 q10, d9, w5 + vmlal.s16 q5, d9, w3 + vmlsl.s16 q6, d9, w1 + +4: vaddhn.i32 d2, q11, q9 + vaddhn.i32 d3, q12, q10 + vaddhn.i32 d4, q13, q5 + vaddhn.i32 d5, q14, q6 + vsubhn.i32 d9, q11, q9 + vsubhn.i32 d8, q12, q10 + vsubhn.i32 d7, q13, q5 + vsubhn.i32 d6, q14, q6 + + bx lr +endfunc + + .align 6 + +function idct_col4_st8_neon + vqshrun.s16 d2, q1, #COL_SHIFT-16 + vqshrun.s16 d3, q2, #COL_SHIFT-16 + vqshrun.s16 d4, q3, #COL_SHIFT-16 + vqshrun.s16 d5, q4, #COL_SHIFT-16 + vst1.32 {d2[0]}, [r0,:32], r1 + vst1.32 {d2[1]}, [r0,:32], r1 + vst1.32 {d3[0]}, [r0,:32], r1 + vst1.32 {d3[1]}, [r0,:32], r1 + vst1.32 {d4[0]}, [r0,:32], r1 + vst1.32 {d4[1]}, [r0,:32], r1 + vst1.32 {d5[0]}, [r0,:32], r1 + vst1.32 {d5[1]}, [r0,:32], r1 + + bx lr +endfunc + + .section .rodata + .align 4 +idct_coeff_neon: + .short W1, W2, W3, W4, W5, W6, W7, W4c + + .macro idct_start data + push {r4-r7, lr} + pld [\data] + pld [\data, #64] + vpush {d8-d15} + movrel r3, idct_coeff_neon + vld1.64 {d0,d1}, [r3,:128] + .endm + + .macro idct_end + vpop {d8-d15} + pop {r4-r7, pc} + .endm + +/* void ff_simple_idct_put_neon(uint8_t *dst, int line_size, DCTELEM *data); */ +function ff_simple_idct_put_neon, export=1 + idct_start r2 + + bl idct_row4_pld_neon + bl idct_row4_neon + add r2, r2, #-128 + bl idct_col4_neon + bl idct_col4_st8_neon + sub r0, r0, r1, lsl #3 + add r0, r0, #4 + add r2, r2, #-120 + bl idct_col4_neon + bl idct_col4_st8_neon + + idct_end +endfunc + + .align 6 + +function idct_col4_add8_neon + mov ip, r0 + + vld1.32 {d10[0]}, [r0,:32], r1 + vshr.s16 q1, q1, #COL_SHIFT-16 + vld1.32 {d10[1]}, [r0,:32], r1 + vshr.s16 q2, q2, #COL_SHIFT-16 + vld1.32 {d11[0]}, [r0,:32], r1 + vshr.s16 q3, q3, #COL_SHIFT-16 + vld1.32 {d11[1]}, [r0,:32], r1 + vshr.s16 q4, q4, #COL_SHIFT-16 + vld1.32 {d12[0]}, [r0,:32], r1 + vaddw.u8 q1, q1, d10 + vld1.32 {d12[1]}, [r0,:32], r1 + vaddw.u8 q2, q2, d11 + vld1.32 {d13[0]}, [r0,:32], r1 + vqmovun.s16 d2, q1 + vld1.32 {d13[1]}, [r0,:32], r1 + vaddw.u8 q3, q3, d12 + vst1.32 {d2[0]}, [ip,:32], r1 + vqmovun.s16 d3, q2 + vst1.32 {d2[1]}, [ip,:32], r1 + vaddw.u8 q4, q4, d13 + vst1.32 {d3[0]}, [ip,:32], r1 + vqmovun.s16 d4, q3 + vst1.32 {d3[1]}, [ip,:32], r1 + vqmovun.s16 d5, q4 + vst1.32 {d4[0]}, [ip,:32], r1 + vst1.32 {d4[1]}, [ip,:32], r1 + vst1.32 {d5[0]}, [ip,:32], r1 + vst1.32 {d5[1]}, [ip,:32], r1 + + bx lr +endfunc + +/* void ff_simple_idct_add_neon(uint8_t *dst, int line_size, DCTELEM *data); */ +function ff_simple_idct_add_neon, export=1 + idct_start r2 + + bl idct_row4_pld_neon + bl idct_row4_neon + add r2, r2, #-128 + bl idct_col4_neon + bl idct_col4_add8_neon + sub r0, r0, r1, lsl #3 + add r0, r0, #4 + add r2, r2, #-120 + bl idct_col4_neon + bl idct_col4_add8_neon + + idct_end +endfunc + + .align 6 + +function idct_col4_st16_neon + mov ip, #16 + + vshr.s16 q1, q1, #COL_SHIFT-16 + vshr.s16 q2, q2, #COL_SHIFT-16 + vst1.64 {d2}, [r2,:64], ip + vshr.s16 q3, q3, #COL_SHIFT-16 + vst1.64 {d3}, [r2,:64], ip + vshr.s16 q4, q4, #COL_SHIFT-16 + vst1.64 {d4}, [r2,:64], ip + vst1.64 {d5}, [r2,:64], ip + vst1.64 {d6}, [r2,:64], ip + vst1.64 {d7}, [r2,:64], ip + vst1.64 {d8}, [r2,:64], ip + vst1.64 {d9}, [r2,:64], ip + + bx lr +endfunc + +/* void ff_simple_idct_neon(DCTELEM *data); */ +function ff_simple_idct_neon, export=1 + idct_start r0 + + mov r2, r0 + bl idct_row4_neon + bl idct_row4_neon + add r2, r2, #-128 + bl idct_col4_neon + add r2, r2, #-128 + bl idct_col4_st16_neon + add r2, r2, #-120 + bl idct_col4_neon + add r2, r2, #-128 + bl idct_col4_st16_neon + + idct_end +endfunc diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/avfft.c b/plugins/supereq/ffmpeg_fft/libavcodec/avfft.c new file mode 100644 index 00000000..25fc4e09 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/avfft.c @@ -0,0 +1,142 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/mem.h" +#include "avfft.h" +#include "fft.h" + +/* FFT */ + +FFTContext *av_fft_init(int nbits, int inverse) +{ + FFTContext *s = av_malloc(sizeof(*s)); + + if (s) + ff_fft_init(s, nbits, inverse); + + return s; +} + +void av_fft_permute(FFTContext *s, FFTComplex *z) +{ + s->fft_permute(s, z); +} + +void av_fft_calc(FFTContext *s, FFTComplex *z) +{ + s->fft_calc(s, z); +} + +void av_fft_end(FFTContext *s) +{ + if (s) { + ff_fft_end(s); + av_free(s); + } +} + +#if CONFIG_MDCT + +FFTContext *av_mdct_init(int nbits, int inverse, double scale) +{ + FFTContext *s = av_malloc(sizeof(*s)); + + if (s) + ff_mdct_init(s, nbits, inverse, scale); + + return s; +} + +void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) +{ + s->imdct_calc(s, output, input); +} + +void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input) +{ + s->imdct_half(s, output, input); +} + +void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) +{ + s->mdct_calc(s, output, input); +} + +void av_mdct_end(FFTContext *s) +{ + if (s) { + ff_mdct_end(s); + av_free(s); + } +} + +#endif /* CONFIG_MDCT */ + +#if CONFIG_RDFT + +RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans) +{ + RDFTContext *s = av_malloc(sizeof(*s)); + + if (s) + ff_rdft_init(s, nbits, trans); + + return s; +} + +void av_rdft_calc(RDFTContext *s, FFTSample *data) +{ + ff_rdft_calc(s, data); +} + +void av_rdft_end(RDFTContext *s) +{ + if (s) { + ff_rdft_end(s); + av_free(s); + } +} + +#endif /* CONFIG_RDFT */ + +#if CONFIG_DCT + +DCTContext *av_dct_init(int nbits, enum DCTTransformType inverse) +{ + DCTContext *s = av_malloc(sizeof(*s)); + + if (s) + ff_dct_init(s, nbits, inverse); + + return s; +} + +void av_dct_calc(DCTContext *s, FFTSample *data) +{ + ff_dct_calc(s, data); +} + +void av_dct_end(DCTContext *s) +{ + if (s) { + ff_dct_end(s); + av_free(s); + } +} + +#endif /* CONFIG_DCT */ diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/avfft.h b/plugins/supereq/ffmpeg_fft/libavcodec/avfft.h new file mode 100644 index 00000000..fdf30237 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/avfft.h @@ -0,0 +1,103 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_AVFFT_H +#define AVCODEC_AVFFT_H + +#include "publik.h" + +typedef float FFTSample; + +typedef struct FFTComplex { + FFTSample re, im; +} FFTComplex; + +typedef struct FFTContext FFTContext; + +/** + * Set up a complex FFT. + * @param nbits log2 of the length of the input array + * @param inverse if 0 perform the forward transform, if 1 perform the inverse + */ +PUBLIK FFTContext *av_fft_init(int nbits, int inverse); + +/** + * Do the permutation needed BEFORE calling ff_fft_calc(). + */ +PUBLIK void av_fft_permute(FFTContext *s, FFTComplex *z); + +/** + * Do a complex FFT with the parameters defined in av_fft_init(). The + * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + */ +PUBLIK void av_fft_calc(FFTContext *s, FFTComplex *z); + +PUBLIK void av_fft_end(FFTContext *s); + +#if 0 +FFTContext *av_mdct_init(int nbits, int inverse, double scale); +void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +void av_mdct_end(FFTContext *s); +#endif + +/* Real Discrete Fourier Transform */ + +enum RDFTransformType { + DFT_R2C, + IDFT_C2R, + IDFT_R2C, + DFT_C2R, +}; + +typedef struct RDFTContext RDFTContext; + +/** + * Set up a real FFT. + * @param nbits log2 of the length of the input array + * @param trans the type of transform + */ +PUBLIK RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans); +PUBLIK void av_rdft_calc(RDFTContext *s, FFTSample *data); +PUBLIK void av_rdft_end(RDFTContext *s); + +/* Discrete Cosine Transform */ + +typedef struct DCTContext DCTContext; + +enum DCTTransformType { + DCT_II = 0, + DCT_III, + DCT_I, + DST_I, +}; + +/** + * Set up DCT. + * @param nbits size of the input array: + * (1 << nbits) for DCT-II, DCT-III and DST-I + * (1 << nbits) + 1 for DCT-I + * + * @note the first element of the input of DST-I is ignored + */ +PUBLIK DCTContext *av_dct_init(int nbits, enum DCTTransformType type); +PUBLIK void av_dct_calc(DCTContext *s, FFTSample *data); +PUBLIK void av_dct_end (DCTContext *s); + +#endif /* AVCODEC_AVFFT_H */ diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/dct.c b/plugins/supereq/ffmpeg_fft/libavcodec/dct.c new file mode 100644 index 00000000..6ea1936e --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/dct.c @@ -0,0 +1,228 @@ +/* + * (I)DCT Transforms + * Copyright (c) 2009 Peter Ross <pross@xvid.org> + * Copyright (c) 2010 Alex Converse <alex.converse@gmail.com> + * Copyright (c) 2010 Vitor Sessak + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * (Inverse) Discrete Cosine Transforms. These are also known as the + * type II and type III DCTs respectively. + */ + +#include <math.h> +#include "libavutil/mathematics.h" +#include "fft.h" +#ifndef ARCH_ARM +#include "x86/fft.h" +#endif + +#define DCT32_FLOAT +#include "dct32.h" + +/* sin((M_PI * x / (2*n)) */ +#define SIN(s,n,x) (s->costab[(n) - (x)]) + +/* cos((M_PI * x / (2*n)) */ +#define COS(s,n,x) (s->costab[x]) + +static void ff_dst_calc_I_c(DCTContext *ctx, FFTSample *data) +{ + int n = 1 << ctx->nbits; + int i; + + data[0] = 0; + for(i = 1; i < n/2; i++) { + float tmp1 = data[i ]; + float tmp2 = data[n - i]; + float s = SIN(ctx, n, 2*i); + + s *= tmp1 + tmp2; + tmp1 = (tmp1 - tmp2) * 0.5f; + data[i ] = s + tmp1; + data[n - i] = s - tmp1; + } + + data[n/2] *= 2; + ff_rdft_calc(&ctx->rdft, data); + + data[0] *= 0.5f; + + for(i = 1; i < n-2; i += 2) { + data[i + 1] += data[i - 1]; + data[i ] = -data[i + 2]; + } + + data[n-1] = 0; +} + +static void ff_dct_calc_I_c(DCTContext *ctx, FFTSample *data) +{ + int n = 1 << ctx->nbits; + int i; + float next = -0.5f * (data[0] - data[n]); + + for(i = 0; i < n/2; i++) { + float tmp1 = data[i ]; + float tmp2 = data[n - i]; + float s = SIN(ctx, n, 2*i); + float c = COS(ctx, n, 2*i); + + c *= tmp1 - tmp2; + s *= tmp1 - tmp2; + + next += c; + + tmp1 = (tmp1 + tmp2) * 0.5f; + data[i ] = tmp1 - s; + data[n - i] = tmp1 + s; + } + + ff_rdft_calc(&ctx->rdft, data); + data[n] = data[1]; + data[1] = next; + + for(i = 3; i <= n; i += 2) + data[i] = data[i - 2] - data[i]; +} + +static void ff_dct_calc_III_c(DCTContext *ctx, FFTSample *data) +{ + int n = 1 << ctx->nbits; + int i; + + float next = data[n - 1]; + float inv_n = 1.0f / n; + + for (i = n - 2; i >= 2; i -= 2) { + float val1 = data[i ]; + float val2 = data[i - 1] - data[i + 1]; + float c = COS(ctx, n, i); + float s = SIN(ctx, n, i); + + data[i ] = c * val1 + s * val2; + data[i + 1] = s * val1 - c * val2; + } + + data[1] = 2 * next; + + ff_rdft_calc(&ctx->rdft, data); + + for (i = 0; i < n / 2; i++) { + float tmp1 = data[i ] * inv_n; + float tmp2 = data[n - i - 1] * inv_n; + float csc = ctx->csc2[i] * (tmp1 - tmp2); + + tmp1 += tmp2; + data[i ] = tmp1 + csc; + data[n - i - 1] = tmp1 - csc; + } +} + +static void ff_dct_calc_II_c(DCTContext *ctx, FFTSample *data) +{ + int n = 1 << ctx->nbits; + int i; + float next; + + for (i=0; i < n/2; i++) { + float tmp1 = data[i ]; + float tmp2 = data[n - i - 1]; + float s = SIN(ctx, n, 2*i + 1); + + s *= tmp1 - tmp2; + tmp1 = (tmp1 + tmp2) * 0.5f; + + data[i ] = tmp1 + s; + data[n-i-1] = tmp1 - s; + } + + ff_rdft_calc(&ctx->rdft, data); + + next = data[1] * 0.5; + data[1] *= -1; + + for (i = n - 2; i >= 0; i -= 2) { + float inr = data[i ]; + float ini = data[i + 1]; + float c = COS(ctx, n, i); + float s = SIN(ctx, n, i); + + data[i ] = c * inr + s * ini; + + data[i+1] = next; + + next += s * inr - c * ini; + } +} + +static void dct32_func(DCTContext *ctx, FFTSample *data) +{ + ctx->dct32(data, data); +} + +void ff_dct_calc(DCTContext *s, FFTSample *data) +{ + s->dct_calc(s, data); +} + +av_cold int ff_dct_init(DCTContext *s, int nbits, enum DCTTransformType inverse) +{ + int n = 1 << nbits; + int i; + + s->nbits = nbits; + s->inverse = inverse; + + ff_init_ff_cos_tabs(nbits+2); + + s->costab = ff_cos_tabs[nbits+2]; + + s->csc2 = av_malloc(n/2 * sizeof(FFTSample)); + + if (ff_rdft_init(&s->rdft, nbits, inverse == DCT_III) < 0) { + av_free(s->csc2); + return -1; + } + + for (i = 0; i < n/2; i++) + s->csc2[i] = 0.5 / sin((M_PI / (2*n) * (2*i + 1))); + + switch(inverse) { + case DCT_I : s->dct_calc = ff_dct_calc_I_c; break; + case DCT_II : s->dct_calc = ff_dct_calc_II_c ; break; + case DCT_III: s->dct_calc = ff_dct_calc_III_c; break; + case DST_I : s->dct_calc = ff_dst_calc_I_c; break; + } + + if (inverse == DCT_II && nbits == 5) + s->dct_calc = dct32_func; + + s->dct32 = dct32; + if (HAVE_MMX) ff_dct_init_mmx(s); + + return 0; +} + +av_cold void ff_dct_end(DCTContext *s) +{ + ff_rdft_end(&s->rdft); + av_free(s->csc2); +} diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/dct32.c b/plugins/supereq/ffmpeg_fft/libavcodec/dct32.c new file mode 100644 index 00000000..3e6ad78d --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/dct32.c @@ -0,0 +1,262 @@ +/* + * Template for the Discrete Cosine Transform for 32 samples + * Copyright (c) 2001, 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "dct32.h" + +/* tab[i][j] = 1.0 / (2.0 * cos(pi*(2*k+1) / 2^(6 - j))) */ + +/* cos(i*pi/64) */ + +#define COS0_0 FIXHR(0.50060299823519630134/2) +#define COS0_1 FIXHR(0.50547095989754365998/2) +#define COS0_2 FIXHR(0.51544730992262454697/2) +#define COS0_3 FIXHR(0.53104259108978417447/2) +#define COS0_4 FIXHR(0.55310389603444452782/2) +#define COS0_5 FIXHR(0.58293496820613387367/2) +#define COS0_6 FIXHR(0.62250412303566481615/2) +#define COS0_7 FIXHR(0.67480834145500574602/2) +#define COS0_8 FIXHR(0.74453627100229844977/2) +#define COS0_9 FIXHR(0.83934964541552703873/2) +#define COS0_10 FIXHR(0.97256823786196069369/2) +#define COS0_11 FIXHR(1.16943993343288495515/4) +#define COS0_12 FIXHR(1.48416461631416627724/4) +#define COS0_13 FIXHR(2.05778100995341155085/8) +#define COS0_14 FIXHR(3.40760841846871878570/8) +#define COS0_15 FIXHR(10.19000812354805681150/32) + +#define COS1_0 FIXHR(0.50241928618815570551/2) +#define COS1_1 FIXHR(0.52249861493968888062/2) +#define COS1_2 FIXHR(0.56694403481635770368/2) +#define COS1_3 FIXHR(0.64682178335999012954/2) +#define COS1_4 FIXHR(0.78815462345125022473/2) +#define COS1_5 FIXHR(1.06067768599034747134/4) +#define COS1_6 FIXHR(1.72244709823833392782/4) +#define COS1_7 FIXHR(5.10114861868916385802/16) + +#define COS2_0 FIXHR(0.50979557910415916894/2) +#define COS2_1 FIXHR(0.60134488693504528054/2) +#define COS2_2 FIXHR(0.89997622313641570463/2) +#define COS2_3 FIXHR(2.56291544774150617881/8) + +#define COS3_0 FIXHR(0.54119610014619698439/2) +#define COS3_1 FIXHR(1.30656296487637652785/4) + +#define COS4_0 FIXHR(0.70710678118654752439/2) + +/* butterfly operator */ +#define BF(a, b, c, s)\ +{\ + tmp0 = val##a + val##b;\ + tmp1 = val##a - val##b;\ + val##a = tmp0;\ + val##b = MULH3(tmp1, c, 1<<(s));\ +} + +#define BF0(a, b, c, s)\ +{\ + tmp0 = tab[a] + tab[b];\ + tmp1 = tab[a] - tab[b];\ + val##a = tmp0;\ + val##b = MULH3(tmp1, c, 1<<(s));\ +} + +#define BF1(a, b, c, d)\ +{\ + BF(a, b, COS4_0, 1);\ + BF(c, d,-COS4_0, 1);\ + val##c += val##d;\ +} + +#define BF2(a, b, c, d)\ +{\ + BF(a, b, COS4_0, 1);\ + BF(c, d,-COS4_0, 1);\ + val##c += val##d;\ + val##a += val##c;\ + val##c += val##b;\ + val##b += val##d;\ +} + +#define ADD(a, b) val##a += val##b + +/* DCT32 without 1/sqrt(2) coef zero scaling. */ +void dct32(INTFLOAT *out, const INTFLOAT *tab) +{ + INTFLOAT tmp0, tmp1; + + INTFLOAT val0 , val1 , val2 , val3 , val4 , val5 , val6 , val7 , + val8 , val9 , val10, val11, val12, val13, val14, val15, + val16, val17, val18, val19, val20, val21, val22, val23, + val24, val25, val26, val27, val28, val29, val30, val31; + + /* pass 1 */ + BF0( 0, 31, COS0_0 , 1); + BF0(15, 16, COS0_15, 5); + /* pass 2 */ + BF( 0, 15, COS1_0 , 1); + BF(16, 31,-COS1_0 , 1); + /* pass 1 */ + BF0( 7, 24, COS0_7 , 1); + BF0( 8, 23, COS0_8 , 1); + /* pass 2 */ + BF( 7, 8, COS1_7 , 4); + BF(23, 24,-COS1_7 , 4); + /* pass 3 */ + BF( 0, 7, COS2_0 , 1); + BF( 8, 15,-COS2_0 , 1); + BF(16, 23, COS2_0 , 1); + BF(24, 31,-COS2_0 , 1); + /* pass 1 */ + BF0( 3, 28, COS0_3 , 1); + BF0(12, 19, COS0_12, 2); + /* pass 2 */ + BF( 3, 12, COS1_3 , 1); + BF(19, 28,-COS1_3 , 1); + /* pass 1 */ + BF0( 4, 27, COS0_4 , 1); + BF0(11, 20, COS0_11, 2); + /* pass 2 */ + BF( 4, 11, COS1_4 , 1); + BF(20, 27,-COS1_4 , 1); + /* pass 3 */ + BF( 3, 4, COS2_3 , 3); + BF(11, 12,-COS2_3 , 3); + BF(19, 20, COS2_3 , 3); + BF(27, 28,-COS2_3 , 3); + /* pass 4 */ + BF( 0, 3, COS3_0 , 1); + BF( 4, 7,-COS3_0 , 1); + BF( 8, 11, COS3_0 , 1); + BF(12, 15,-COS3_0 , 1); + BF(16, 19, COS3_0 , 1); + BF(20, 23,-COS3_0 , 1); + BF(24, 27, COS3_0 , 1); + BF(28, 31,-COS3_0 , 1); + + + + /* pass 1 */ + BF0( 1, 30, COS0_1 , 1); + BF0(14, 17, COS0_14, 3); + /* pass 2 */ + BF( 1, 14, COS1_1 , 1); + BF(17, 30,-COS1_1 , 1); + /* pass 1 */ + BF0( 6, 25, COS0_6 , 1); + BF0( 9, 22, COS0_9 , 1); + /* pass 2 */ + BF( 6, 9, COS1_6 , 2); + BF(22, 25,-COS1_6 , 2); + /* pass 3 */ + BF( 1, 6, COS2_1 , 1); + BF( 9, 14,-COS2_1 , 1); + BF(17, 22, COS2_1 , 1); + BF(25, 30,-COS2_1 , 1); + + /* pass 1 */ + BF0( 2, 29, COS0_2 , 1); + BF0(13, 18, COS0_13, 3); + /* pass 2 */ + BF( 2, 13, COS1_2 , 1); + BF(18, 29,-COS1_2 , 1); + /* pass 1 */ + BF0( 5, 26, COS0_5 , 1); + BF0(10, 21, COS0_10, 1); + /* pass 2 */ + BF( 5, 10, COS1_5 , 2); + BF(21, 26,-COS1_5 , 2); + /* pass 3 */ + BF( 2, 5, COS2_2 , 1); + BF(10, 13,-COS2_2 , 1); + BF(18, 21, COS2_2 , 1); + BF(26, 29,-COS2_2 , 1); + /* pass 4 */ + BF( 1, 2, COS3_1 , 2); + BF( 5, 6,-COS3_1 , 2); + BF( 9, 10, COS3_1 , 2); + BF(13, 14,-COS3_1 , 2); + BF(17, 18, COS3_1 , 2); + BF(21, 22,-COS3_1 , 2); + BF(25, 26, COS3_1 , 2); + BF(29, 30,-COS3_1 , 2); + + /* pass 5 */ + BF1( 0, 1, 2, 3); + BF2( 4, 5, 6, 7); + BF1( 8, 9, 10, 11); + BF2(12, 13, 14, 15); + BF1(16, 17, 18, 19); + BF2(20, 21, 22, 23); + BF1(24, 25, 26, 27); + BF2(28, 29, 30, 31); + + /* pass 6 */ + + ADD( 8, 12); + ADD(12, 10); + ADD(10, 14); + ADD(14, 9); + ADD( 9, 13); + ADD(13, 11); + ADD(11, 15); + + out[ 0] = val0; + out[16] = val1; + out[ 8] = val2; + out[24] = val3; + out[ 4] = val4; + out[20] = val5; + out[12] = val6; + out[28] = val7; + out[ 2] = val8; + out[18] = val9; + out[10] = val10; + out[26] = val11; + out[ 6] = val12; + out[22] = val13; + out[14] = val14; + out[30] = val15; + + ADD(24, 28); + ADD(28, 26); + ADD(26, 30); + ADD(30, 25); + ADD(25, 29); + ADD(29, 27); + ADD(27, 31); + + out[ 1] = val16 + val24; + out[17] = val17 + val25; + out[ 9] = val18 + val26; + out[25] = val19 + val27; + out[ 5] = val20 + val28; + out[21] = val21 + val29; + out[13] = val22 + val30; + out[29] = val23 + val31; + out[ 3] = val24 + val20; + out[19] = val25 + val21; + out[11] = val26 + val22; + out[27] = val27 + val23; + out[ 7] = val28 + val18; + out[23] = val29 + val19; + out[15] = val30 + val17; + out[31] = val31; +} diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/dct32.h b/plugins/supereq/ffmpeg_fft/libavcodec/dct32.h new file mode 100644 index 00000000..dc2d847a --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/dct32.h @@ -0,0 +1,10 @@ +#ifndef DCT_32_H +#define DCT_32_H + +#define FIXHR(x) ((float)(x)) +#define MULH3(x, y, s) ((s)*(y)*(x)) +#define INTFLOAT float + +void dct32(INTFLOAT *out, const INTFLOAT *tab); + +#endif diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/fft.c b/plugins/supereq/ffmpeg_fft/libavcodec/fft.c new file mode 100644 index 00000000..04082bf4 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/fft.c @@ -0,0 +1,300 @@ +/* + * FFT/IFFT transforms + * Copyright (c) 2008 Loren Merritt + * Copyright (c) 2002 Fabrice Bellard + * Partly based on libdjbfft by D. J. Bernstein + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * FFT/IFFT transforms. + */ + +#include <stdlib.h> +#include <string.h> +#include "libavutil/mathematics.h" +#include "fft.h" + +/* cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse */ +#if !CONFIG_HARDCODED_TABLES +COSTABLE(16); +COSTABLE(32); +COSTABLE(64); +COSTABLE(128); +COSTABLE(256); +COSTABLE(512); +COSTABLE(1024); +COSTABLE(2048); +COSTABLE(4096); +COSTABLE(8192); +COSTABLE(16384); +COSTABLE(32768); +COSTABLE(65536); +#endif +COSTABLE_CONST FFTSample * const ff_cos_tabs[] = { + NULL, NULL, NULL, NULL, + ff_cos_16, ff_cos_32, ff_cos_64, ff_cos_128, ff_cos_256, ff_cos_512, ff_cos_1024, + ff_cos_2048, ff_cos_4096, ff_cos_8192, ff_cos_16384, ff_cos_32768, ff_cos_65536, +}; + +static int split_radix_permutation(int i, int n, int inverse) +{ + int m; + if(n <= 2) return i&1; + m = n >> 1; + if(!(i&m)) return split_radix_permutation(i, m, inverse)*2; + m >>= 1; + if(inverse == !(i&m)) return split_radix_permutation(i, m, inverse)*4 + 1; + else return split_radix_permutation(i, m, inverse)*4 - 1; +} + +av_cold void ff_init_ff_cos_tabs(int index) +{ +#if !CONFIG_HARDCODED_TABLES + int i; + int m = 1<<index; + double freq = 2*M_PI/m; + FFTSample *tab = ff_cos_tabs[index]; + for(i=0; i<=m/4; i++) + tab[i] = cos(i*freq); + for(i=1; i<m/4; i++) + tab[m/2-i] = tab[i]; +#endif +} + +av_cold int ff_fft_init(FFTContext *s, int nbits, int inverse) +{ + int i, j, n; + + if (nbits < 2 || nbits > 16) + goto fail; + s->nbits = nbits; + n = 1 << nbits; + + s->revtab = av_malloc(n * sizeof(uint16_t)); + if (!s->revtab) + goto fail; + s->tmp_buf = av_malloc(n * sizeof(FFTComplex)); + if (!s->tmp_buf) + goto fail; + s->inverse = inverse; + + s->fft_permute = ff_fft_permute_c; + s->fft_calc = ff_fft_calc_c; +#if CONFIG_MDCT + s->imdct_calc = ff_imdct_calc_c; + s->imdct_half = ff_imdct_half_c; + s->mdct_calc = ff_mdct_calc_c; +#endif + +#if ARCH_ARM + ff_fft_init_arm(s); +#elif HAVE_ALTIVEC + if (HAVE_ALTIVEC) ff_fft_init_altivec(s); +#elif HAVE_MMX + if (HAVE_MMX) ff_fft_init_mmx(s); +#endif + + for(j=4; j<=nbits; j++) { + ff_init_ff_cos_tabs(j); + } + for(i=0; i<n; i++) + s->revtab[-split_radix_permutation(i, n, s->inverse) & (n-1)] = i; + + return 0; + fail: + av_freep(&s->revtab); + av_freep(&s->tmp_buf); + return -1; +} + +void ff_fft_permute_c(FFTContext *s, FFTComplex *z) +{ + int j, np; + const uint16_t *revtab = s->revtab; + np = 1 << s->nbits; + /* TODO: handle split-radix permute in a more optimal way, probably in-place */ + for(j=0;j<np;j++) s->tmp_buf[revtab[j]] = z[j]; + memcpy(z, s->tmp_buf, np * sizeof(FFTComplex)); +} + +av_cold void ff_fft_end(FFTContext *s) +{ + av_freep(&s->revtab); + av_freep(&s->tmp_buf); +} + +#define sqrthalf (float)M_SQRT1_2 + +#define BF(x,y,a,b) {\ + x = a - b;\ + y = a + b;\ +} + +#define BUTTERFLIES(a0,a1,a2,a3) {\ + BF(t3, t5, t5, t1);\ + BF(a2.re, a0.re, a0.re, t5);\ + BF(a3.im, a1.im, a1.im, t3);\ + BF(t4, t6, t2, t6);\ + BF(a3.re, a1.re, a1.re, t4);\ + BF(a2.im, a0.im, a0.im, t6);\ +} + +// force loading all the inputs before storing any. +// this is slightly slower for small data, but avoids store->load aliasing +// for addresses separated by large powers of 2. +#define BUTTERFLIES_BIG(a0,a1,a2,a3) {\ + FFTSample r0=a0.re, i0=a0.im, r1=a1.re, i1=a1.im;\ + BF(t3, t5, t5, t1);\ + BF(a2.re, a0.re, r0, t5);\ + BF(a3.im, a1.im, i1, t3);\ + BF(t4, t6, t2, t6);\ + BF(a3.re, a1.re, r1, t4);\ + BF(a2.im, a0.im, i0, t6);\ +} + +#define TRANSFORM(a0,a1,a2,a3,wre,wim) {\ + t1 = a2.re * wre + a2.im * wim;\ + t2 = a2.im * wre - a2.re * wim;\ + t5 = a3.re * wre - a3.im * wim;\ + t6 = a3.im * wre + a3.re * wim;\ + BUTTERFLIES(a0,a1,a2,a3)\ +} + +#define TRANSFORM_ZERO(a0,a1,a2,a3) {\ + t1 = a2.re;\ + t2 = a2.im;\ + t5 = a3.re;\ + t6 = a3.im;\ + BUTTERFLIES(a0,a1,a2,a3)\ +} + +/* z[0...8n-1], w[1...2n-1] */ +#define PASS(name)\ +static void name(FFTComplex *z, const FFTSample *wre, unsigned int n)\ +{\ + FFTSample t1, t2, t3, t4, t5, t6;\ + int o1 = 2*n;\ + int o2 = 4*n;\ + int o3 = 6*n;\ + const FFTSample *wim = wre+o1;\ + n--;\ +\ + TRANSFORM_ZERO(z[0],z[o1],z[o2],z[o3]);\ + TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\ + do {\ + z += 2;\ + wre += 2;\ + wim -= 2;\ + TRANSFORM(z[0],z[o1],z[o2],z[o3],wre[0],wim[0]);\ + TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\ + } while(--n);\ +} + +PASS(pass) +#undef BUTTERFLIES +#define BUTTERFLIES BUTTERFLIES_BIG +PASS(pass_big) + +#define DECL_FFT(n,n2,n4)\ +static void fft##n(FFTComplex *z)\ +{\ + fft##n2(z);\ + fft##n4(z+n4*2);\ + fft##n4(z+n4*3);\ + pass(z,ff_cos_##n,n4/2);\ +} + +static void fft4(FFTComplex *z) +{ + FFTSample t1, t2, t3, t4, t5, t6, t7, t8; + + BF(t3, t1, z[0].re, z[1].re); + BF(t8, t6, z[3].re, z[2].re); + BF(z[2].re, z[0].re, t1, t6); + BF(t4, t2, z[0].im, z[1].im); + BF(t7, t5, z[2].im, z[3].im); + BF(z[3].im, z[1].im, t4, t8); + BF(z[3].re, z[1].re, t3, t7); + BF(z[2].im, z[0].im, t2, t5); +} + +static void fft8(FFTComplex *z) +{ + FFTSample t1, t2, t3, t4, t5, t6, t7, t8; + + fft4(z); + + BF(t1, z[5].re, z[4].re, -z[5].re); + BF(t2, z[5].im, z[4].im, -z[5].im); + BF(t3, z[7].re, z[6].re, -z[7].re); + BF(t4, z[7].im, z[6].im, -z[7].im); + BF(t8, t1, t3, t1); + BF(t7, t2, t2, t4); + BF(z[4].re, z[0].re, z[0].re, t1); + BF(z[4].im, z[0].im, z[0].im, t2); + BF(z[6].re, z[2].re, z[2].re, t7); + BF(z[6].im, z[2].im, z[2].im, t8); + + TRANSFORM(z[1],z[3],z[5],z[7],sqrthalf,sqrthalf); +} + +#if !CONFIG_SMALL +static void fft16(FFTComplex *z) +{ + FFTSample t1, t2, t3, t4, t5, t6; + + fft8(z); + fft4(z+8); + fft4(z+12); + + TRANSFORM_ZERO(z[0],z[4],z[8],z[12]); + TRANSFORM(z[2],z[6],z[10],z[14],sqrthalf,sqrthalf); + TRANSFORM(z[1],z[5],z[9],z[13],ff_cos_16[1],ff_cos_16[3]); + TRANSFORM(z[3],z[7],z[11],z[15],ff_cos_16[3],ff_cos_16[1]); +} +#else +DECL_FFT(16,8,4) +#endif +DECL_FFT(32,16,8) +DECL_FFT(64,32,16) +DECL_FFT(128,64,32) +DECL_FFT(256,128,64) +DECL_FFT(512,256,128) +#if !CONFIG_SMALL +#define pass pass_big +#endif +DECL_FFT(1024,512,256) +DECL_FFT(2048,1024,512) +DECL_FFT(4096,2048,1024) +DECL_FFT(8192,4096,2048) +DECL_FFT(16384,8192,4096) +DECL_FFT(32768,16384,8192) +DECL_FFT(65536,32768,16384) + +static void (* const fft_dispatch[])(FFTComplex*) = { + fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024, + fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, +}; + +void ff_fft_calc_c(FFTContext *s, FFTComplex *z) +{ + fft_dispatch[s->nbits-2](z); +} + diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/fft.h b/plugins/supereq/ffmpeg_fft/libavcodec/fft.h new file mode 100644 index 00000000..b2e0f540 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/fft.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2000, 2001, 2002 Fabrice Bellard + * Copyright (c) 2002-2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_FFT_H +#define AVCODEC_FFT_H + +#include <stdint.h> +#include "../config.h" +#include "libavutil/mem.h" +#include "avfft.h" + +/* FFT computation */ + +struct FFTContext { + int nbits; + int inverse; + uint16_t *revtab; + FFTComplex *tmp_buf; + int mdct_size; /* size of MDCT (i.e. number of input data * 2) */ + int mdct_bits; /* n = 2^nbits */ + /* pre/post rotation tables */ + FFTSample *tcos; + FFTSample *tsin; + void (*fft_permute)(struct FFTContext *s, FFTComplex *z); + void (*fft_calc)(struct FFTContext *s, FFTComplex *z); + void (*imdct_calc)(struct FFTContext *s, FFTSample *output, const FFTSample *input); + void (*imdct_half)(struct FFTContext *s, FFTSample *output, const FFTSample *input); + void (*mdct_calc)(struct FFTContext *s, FFTSample *output, const FFTSample *input); + int permutation; +#define FF_MDCT_PERM_NONE 0 +#define FF_MDCT_PERM_INTERLEAVE 1 +}; + +#if CONFIG_HARDCODED_TABLES +#define COSTABLE_CONST const +#define SINTABLE_CONST const +#define SINETABLE_CONST const +#else +#define COSTABLE_CONST +#define SINTABLE_CONST +#define SINETABLE_CONST +#endif + +#define COSTABLE(size) \ + COSTABLE_CONST DECLARE_ALIGNED(16, FFTSample, ff_cos_##size)[size/2] +#define SINTABLE(size) \ + SINTABLE_CONST DECLARE_ALIGNED(16, FFTSample, ff_sin_##size)[size/2] +#define SINETABLE(size) \ + SINETABLE_CONST DECLARE_ALIGNED(16, float, ff_sine_##size)[size] +extern COSTABLE(16); +extern COSTABLE(32); +extern COSTABLE(64); +extern COSTABLE(128); +extern COSTABLE(256); +extern COSTABLE(512); +extern COSTABLE(1024); +extern COSTABLE(2048); +extern COSTABLE(4096); +extern COSTABLE(8192); +extern COSTABLE(16384); +extern COSTABLE(32768); +extern COSTABLE(65536); +extern COSTABLE_CONST FFTSample* const ff_cos_tabs[17]; + +/** + * Initialize the cosine table in ff_cos_tabs[index] + * \param index index in ff_cos_tabs array of the table to initialize + */ +void ff_init_ff_cos_tabs(int index); + +extern SINTABLE(16); +extern SINTABLE(32); +extern SINTABLE(64); +extern SINTABLE(128); +extern SINTABLE(256); +extern SINTABLE(512); +extern SINTABLE(1024); +extern SINTABLE(2048); +extern SINTABLE(4096); +extern SINTABLE(8192); +extern SINTABLE(16384); +extern SINTABLE(32768); +extern SINTABLE(65536); + +/** + * Set up a complex FFT. + * @param nbits log2 of the length of the input array + * @param inverse if 0 perform the forward transform, if 1 perform the inverse + */ +int ff_fft_init(FFTContext *s, int nbits, int inverse); +void ff_fft_permute_c(FFTContext *s, FFTComplex *z); +void ff_fft_calc_c(FFTContext *s, FFTComplex *z); + +void ff_fft_init_altivec(FFTContext *s); +void ff_fft_init_mmx(FFTContext *s); +void ff_fft_init_arm(FFTContext *s); +void ff_dct_init_mmx(DCTContext *s); + +/** + * Do the permutation needed BEFORE calling ff_fft_calc(). + */ +static inline void ff_fft_permute(FFTContext *s, FFTComplex *z) +{ + s->fft_permute(s, z); +} +/** + * Do a complex FFT with the parameters defined in ff_fft_init(). The + * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + */ +static inline void ff_fft_calc(FFTContext *s, FFTComplex *z) +{ + s->fft_calc(s, z); +} +void ff_fft_end(FFTContext *s); + +/* MDCT computation */ + +static inline void ff_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) +{ + s->imdct_calc(s, output, input); +} +static inline void ff_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input) +{ + s->imdct_half(s, output, input); +} + +static inline void ff_mdct_calc(FFTContext *s, FFTSample *output, + const FFTSample *input) +{ + s->mdct_calc(s, output, input); +} + +/** + * Maximum window size for ff_kbd_window_init. + */ +#define FF_KBD_WINDOW_MAX 1024 + +/** + * Generate a Kaiser-Bessel Derived Window. + * @param window pointer to half window + * @param alpha determines window shape + * @param n size of half window, max FF_KBD_WINDOW_MAX + */ +void ff_kbd_window_init(float *window, float alpha, int n); + +/** + * Generate a sine window. + * @param window pointer to half window + * @param n size of half window + */ +void ff_sine_window_init(float *window, int n); + +/** + * initialize the specified entry of ff_sine_windows + */ +void ff_init_ff_sine_windows(int index); +extern SINETABLE( 32); +extern SINETABLE( 64); +extern SINETABLE( 128); +extern SINETABLE( 256); +extern SINETABLE( 512); +extern SINETABLE(1024); +extern SINETABLE(2048); +extern SINETABLE(4096); +extern SINETABLE_CONST float * const ff_sine_windows[13]; + +int ff_mdct_init(FFTContext *s, int nbits, int inverse, double scale); +void ff_imdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input); +void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input); +void ff_mdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input); +void ff_mdct_end(FFTContext *s); + +/* Real Discrete Fourier Transform */ + +struct RDFTContext { + int nbits; + int inverse; + int sign_convention; + + /* pre/post rotation tables */ + const FFTSample *tcos; + SINTABLE_CONST FFTSample *tsin; + FFTContext fft; + void (*rdft_calc)(struct RDFTContext *s, FFTSample *z); +}; + +/** + * Set up a real FFT. + * @param nbits log2 of the length of the input array + * @param trans the type of transform + */ +int ff_rdft_init(RDFTContext *s, int nbits, enum RDFTransformType trans); +void ff_rdft_end(RDFTContext *s); + +void ff_rdft_init_arm(RDFTContext *s); + +static av_always_inline void ff_rdft_calc(RDFTContext *s, FFTSample *data) +{ + s->rdft_calc(s, data); +} + +/* Discrete Cosine Transform */ + +struct DCTContext { + int nbits; + int inverse; + RDFTContext rdft; + const float *costab; + FFTSample *csc2; + void (*dct_calc)(struct DCTContext *s, FFTSample *data); + void (*dct32)(FFTSample *out, const FFTSample *in); +}; + +/** + * Set up DCT. + * @param nbits size of the input array: + * (1 << nbits) for DCT-II, DCT-III and DST-I + * (1 << nbits) + 1 for DCT-I + * + * @note the first element of the input of DST-I is ignored + */ +int ff_dct_init(DCTContext *s, int nbits, enum DCTTransformType type); +void ff_dct_calc(DCTContext *s, FFTSample *data); +void ff_dct_end (DCTContext *s); + +#endif /* AVCODEC_FFT_H */ diff --git a/plugins/supereq/ffmpeg_fft/libavcodec/rdft.c b/plugins/supereq/ffmpeg_fft/libavcodec/rdft.c new file mode 100644 index 00000000..fe6014fb --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavcodec/rdft.c @@ -0,0 +1,137 @@ +/* + * (I)RDFT transforms + * Copyright (c) 2009 Alex Converse <alex dot converse at gmail dot com> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <stdlib.h> +#include <math.h> +#include "libavutil/mathematics.h" +#include "fft.h" + +/** + * @file + * (Inverse) Real Discrete Fourier Transforms. + */ + +/* sin(2*pi*x/n) for 0<=x<n/4, followed by n/2<=x<3n/4 */ +#if !CONFIG_HARDCODED_TABLES +SINTABLE(16); +SINTABLE(32); +SINTABLE(64); +SINTABLE(128); +SINTABLE(256); +SINTABLE(512); +SINTABLE(1024); +SINTABLE(2048); +SINTABLE(4096); +SINTABLE(8192); +SINTABLE(16384); +SINTABLE(32768); +SINTABLE(65536); +#endif +SINTABLE_CONST FFTSample * const ff_sin_tabs[] = { + NULL, NULL, NULL, NULL, + ff_sin_16, ff_sin_32, ff_sin_64, ff_sin_128, ff_sin_256, ff_sin_512, ff_sin_1024, + ff_sin_2048, ff_sin_4096, ff_sin_8192, ff_sin_16384, ff_sin_32768, ff_sin_65536, +}; + +/** Map one real FFT into two parallel real even and odd FFTs. Then interleave + * the two real FFTs into one complex FFT. Unmangle the results. + * ref: http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM + */ +static void ff_rdft_calc_c(RDFTContext* s, FFTSample* data) +{ + int i, i1, i2; + FFTComplex ev, od; + const int n = 1 << s->nbits; + const float k1 = 0.5; + const float k2 = 0.5 - s->inverse; + const FFTSample *tcos = s->tcos; + const FFTSample *tsin = s->tsin; + + if (!s->inverse) { + ff_fft_permute(&s->fft, (FFTComplex*)data); + ff_fft_calc(&s->fft, (FFTComplex*)data); + } + /* i=0 is a special case because of packing, the DC term is real, so we + are going to throw the N/2 term (also real) in with it. */ + ev.re = data[0]; + data[0] = ev.re+data[1]; + data[1] = ev.re-data[1]; + for (i = 1; i < (n>>2); i++) { + i1 = 2*i; + i2 = n-i1; + /* Separate even and odd FFTs */ + ev.re = k1*(data[i1 ]+data[i2 ]); + od.im = -k2*(data[i1 ]-data[i2 ]); + ev.im = k1*(data[i1+1]-data[i2+1]); + od.re = k2*(data[i1+1]+data[i2+1]); + /* Apply twiddle factors to the odd FFT and add to the even FFT */ + data[i1 ] = ev.re + od.re*tcos[i] - od.im*tsin[i]; + data[i1+1] = ev.im + od.im*tcos[i] + od.re*tsin[i]; + data[i2 ] = ev.re - od.re*tcos[i] + od.im*tsin[i]; + data[i2+1] = -ev.im + od.im*tcos[i] + od.re*tsin[i]; + } + data[2*i+1]=s->sign_convention*data[2*i+1]; + if (s->inverse) { + data[0] *= k1; + data[1] *= k1; + ff_fft_permute(&s->fft, (FFTComplex*)data); + ff_fft_calc(&s->fft, (FFTComplex*)data); + } +} + +av_cold int ff_rdft_init(RDFTContext *s, int nbits, enum RDFTransformType trans) +{ + int n = 1 << nbits; + int i; + const double theta = (trans == DFT_R2C || trans == DFT_C2R ? -1 : 1)*2*M_PI/n; + + s->nbits = nbits; + s->inverse = trans == IDFT_C2R || trans == DFT_C2R; + s->sign_convention = trans == IDFT_R2C || trans == DFT_C2R ? 1 : -1; + + if (nbits < 4 || nbits > 16) { + return -1; + } + + if (ff_fft_init(&s->fft, nbits-1, trans == IDFT_C2R || trans == IDFT_R2C) < 0) { + return -1; + } + + ff_init_ff_cos_tabs(nbits); + s->tcos = ff_cos_tabs[nbits]; + s->tsin = ff_sin_tabs[nbits]+(trans == DFT_R2C || trans == DFT_C2R)*(n>>2); +#if !CONFIG_HARDCODED_TABLES + for (i = 0; i < (n>>2); i++) { + s->tsin[i] = sin(i*theta); + } +#endif + s->rdft_calc = ff_rdft_calc_c; + +#if ARCH_ARM + ff_rdft_init_arm(s); +#endif + + return 0; +} + +av_cold void ff_rdft_end(RDFTContext *s) +{ + ff_fft_end(&s->fft); +} diff --git a/plugins/supereq/ffmpeg_fft/libavutil/attributes.h b/plugins/supereq/ffmpeg_fft/libavutil/attributes.h new file mode 100644 index 00000000..50fbfc31 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/attributes.h @@ -0,0 +1,122 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Macro definitions for various function/variable attributes + */ + +#ifndef AVUTIL_ATTRIBUTES_H +#define AVUTIL_ATTRIBUTES_H + +#ifdef __GNUC__ +# define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > x || __GNUC__ == x && __GNUC_MINOR__ >= y) +#else +# define AV_GCC_VERSION_AT_LEAST(x,y) 0 +#endif + +#ifndef av_always_inline +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_always_inline __attribute__((always_inline)) inline +#else +# define av_always_inline inline +#endif +#endif + +#ifndef av_noinline +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_noinline __attribute__((noinline)) +#else +# define av_noinline +#endif +#endif + +#ifndef av_pure +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define av_pure __attribute__((pure)) +#else +# define av_pure +#endif +#endif + +#ifndef av_const +#if AV_GCC_VERSION_AT_LEAST(2,6) +# define av_const __attribute__((const)) +#else +# define av_const +#endif +#endif + +#ifndef av_cold +#if (!defined(__ICC) || __ICC > 1110) && AV_GCC_VERSION_AT_LEAST(4,3) +# define av_cold __attribute__((cold)) +#else +# define av_cold +#endif +#endif + +#ifndef av_flatten +#if (!defined(__ICC) || __ICC > 1110) && AV_GCC_VERSION_AT_LEAST(4,1) +# define av_flatten __attribute__((flatten)) +#else +# define av_flatten +#endif +#endif + +#ifndef attribute_deprecated +#if AV_GCC_VERSION_AT_LEAST(3,1) +# define attribute_deprecated __attribute__((deprecated)) +#else +# define attribute_deprecated +#endif +#endif + +#ifndef av_unused +#if defined(__GNUC__) +# define av_unused __attribute__((unused)) +#else +# define av_unused +#endif +#endif + +#ifndef av_alias +#if (!defined(__ICC) || __ICC > 1110) && AV_GCC_VERSION_AT_LEAST(3,3) +# define av_alias __attribute__((may_alias)) +#else +# define av_alias +#endif +#endif + +#ifndef av_uninit +#if defined(__GNUC__) && !defined(__ICC) +# define av_uninit(x) x=x +#else +# define av_uninit(x) x +#endif +#endif + +#ifdef __GNUC__ +# define av_builtin_constant_p __builtin_constant_p +#else +# define av_builtin_constant_p(x) 0 +#endif + +#endif /* AVUTIL_ATTRIBUTES_H */ + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/avconfig.h b/plugins/supereq/ffmpeg_fft/libavutil/avconfig.h new file mode 100644 index 00000000..b028bb4f --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/avconfig.h @@ -0,0 +1,5 @@ +/* Generated by ffconf */ +#ifndef AVUTIL_AVCONFIG_H +#define AVUTIL_AVCONFIG_H +#define AV_HAVE_BIGENDIAN 0 +#endif /* AVUTIL_AVCONFIG_H */ diff --git a/plugins/supereq/ffmpeg_fft/libavutil/avutil.h b/plugins/supereq/ffmpeg_fft/libavutil/avutil.h new file mode 100644 index 00000000..f5d364be --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/avutil.h @@ -0,0 +1,90 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_AVUTIL_H +#define AVUTIL_AVUTIL_H + +/** + * @file + * external API header + */ + + +#define AV_STRINGIFY(s) AV_TOSTRING(s) +#define AV_TOSTRING(s) #s + +#define AV_GLUE(a, b) a ## b +#define AV_JOIN(a, b) AV_GLUE(a, b) + +#define AV_PRAGMA(s) _Pragma(#s) + +#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c) +#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c +#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c) + +#define LIBAVUTIL_VERSION_MAJOR 50 +#define LIBAVUTIL_VERSION_MINOR 21 +#define LIBAVUTIL_VERSION_MICRO 0 + +#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, \ + LIBAVUTIL_VERSION_MICRO) +#define LIBAVUTIL_VERSION AV_VERSION(LIBAVUTIL_VERSION_MAJOR, \ + LIBAVUTIL_VERSION_MINOR, \ + LIBAVUTIL_VERSION_MICRO) +#define LIBAVUTIL_BUILD LIBAVUTIL_VERSION_INT + +#define LIBAVUTIL_IDENT "Lavu" AV_STRINGIFY(LIBAVUTIL_VERSION) + +/** + * Return the LIBAVUTIL_VERSION_INT constant. + */ +unsigned avutil_version(void); + +/** + * Return the libavutil build-time configuration. + */ +const char *avutil_configuration(void); + +/** + * Return the libavutil license. + */ +const char *avutil_license(void); + +enum AVMediaType { + AVMEDIA_TYPE_UNKNOWN = -1, + AVMEDIA_TYPE_VIDEO, + AVMEDIA_TYPE_AUDIO, + AVMEDIA_TYPE_DATA, + AVMEDIA_TYPE_SUBTITLE, + AVMEDIA_TYPE_ATTACHMENT, + AVMEDIA_TYPE_NB +}; + +#include "common.h" +/* #include "error.h" */ +#include "mathematics.h" +#include "rational.h" +#include "intfloat_readwrite.h" +/* #include "log.h" */ +/* #include "pixfmt.h" */ + +#endif /* AVUTIL_AVUTIL_H */ + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/common.h b/plugins/supereq/ffmpeg_fft/libavutil/common.h new file mode 100644 index 00000000..9dff1435 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/common.h @@ -0,0 +1,347 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * common internal and external API header + */ + +#ifndef AVUTIL_COMMON_H +#define AVUTIL_COMMON_H + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "attributes.h" + +//rounded division & shift +#define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b)) +/* assume b>0 */ +#define ROUNDED_DIV(a,b) (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b)) +#define FFABS(a) ((a) >= 0 ? (a) : (-(a))) +#define FFSIGN(a) ((a) > 0 ? 1 : -1) + +#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) +#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c) +#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) +#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c) + +#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) +#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) +#define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1)) + +/* misc math functions */ +extern const uint8_t ff_log2_tab[256]; + +extern const uint8_t av_reverse[256]; + +static inline av_const int av_log2_c(unsigned int v) +{ + int n = 0; + if (v & 0xffff0000) { + v >>= 16; + n += 16; + } + if (v & 0xff00) { + v >>= 8; + n += 8; + } + n += ff_log2_tab[v]; + + return n; +} + +static inline av_const int av_log2_16bit_c(unsigned int v) +{ + int n = 0; + if (v & 0xff00) { + v >>= 8; + n += 8; + } + n += ff_log2_tab[v]; + + return n; +} + +#ifdef HAVE_AV_CONFIG_H +# include "config.h" +# include "intmath.h" +#endif + +/* Pull in unguarded fallback defines at the end of this file. */ +#include "common.h" + +/** + * Clip a signed integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static inline av_const int av_clip_c(int a, int amin, int amax) +{ + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** + * Clip a signed integer value into the 0-255 range. + * @param a value to clip + * @return clipped value + */ +static inline av_const uint8_t av_clip_uint8_c(int a) +{ + if (a&(~0xFF)) return (-a)>>31; + else return a; +} + +/** + * Clip a signed integer value into the -128,127 range. + * @param a value to clip + * @return clipped value + */ +static inline av_const int8_t av_clip_int8_c(int a) +{ + if ((a+0x80) & ~0xFF) return (a>>31) ^ 0x7F; + else return a; +} + +/** + * Clip a signed integer value into the 0-65535 range. + * @param a value to clip + * @return clipped value + */ +static inline av_const uint16_t av_clip_uint16_c(int a) +{ + if (a&(~0xFFFF)) return (-a)>>31; + else return a; +} + +/** + * Clip a signed integer value into the -32768,32767 range. + * @param a value to clip + * @return clipped value + */ +static inline av_const int16_t av_clip_int16_c(int a) +{ + if ((a+0x8000) & ~0xFFFF) return (a>>31) ^ 0x7FFF; + else return a; +} + +/** + * Clip a signed 64-bit integer value into the -2147483648,2147483647 range. + * @param a value to clip + * @return clipped value + */ +static inline av_const int32_t av_clipl_int32_c(int64_t a) +{ + if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (a>>63) ^ 0x7FFFFFFF; + else return a; +} + +/** + * Clip a float value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static inline av_const float av_clipf_c(float a, float amin, float amax) +{ + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +/** Compute ceil(log2(x)). + * @param x value used to compute ceil(log2(x)) + * @return computed ceiling of log2(x) + */ +static inline av_const int av_ceil_log2_c(int x) +{ + return av_log2((x - 1) << 1); +} + +#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) +#define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24)) + +/** + * Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form. + * + * @param val Output value, must be an lvalue of type uint32_t. + * @param GET_BYTE Expression reading one byte from the input. + * Evaluated up to 7 times (4 for the currently + * assigned Unicode range). With a memory buffer + * input, this could be *ptr++. + * @param ERROR Expression to be evaluated on invalid input, + * typically a goto statement. + */ +#define GET_UTF8(val, GET_BYTE, ERROR)\ + val= GET_BYTE;\ + {\ + int ones= 7 - av_log2(val ^ 255);\ + if(ones==1)\ + ERROR\ + val&= 127>>ones;\ + while(--ones > 0){\ + int tmp= GET_BYTE - 128;\ + if(tmp>>6)\ + ERROR\ + val= (val<<6) + tmp;\ + }\ + } + +/** + * Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form. + * + * @param val Output value, must be an lvalue of type uint32_t. + * @param GET_16BIT Expression returning two bytes of UTF-16 data converted + * to native byte order. Evaluated one or two times. + * @param ERROR Expression to be evaluated on invalid input, + * typically a goto statement. + */ +#define GET_UTF16(val, GET_16BIT, ERROR)\ + val = GET_16BIT;\ + {\ + unsigned int hi = val - 0xD800;\ + if (hi < 0x800) {\ + val = GET_16BIT - 0xDC00;\ + if (val > 0x3FFU || hi > 0x3FFU)\ + ERROR\ + val += (hi<<10) + 0x10000;\ + }\ + }\ + +/*! + * \def PUT_UTF8(val, tmp, PUT_BYTE) + * Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long). + * \param val is an input-only argument and should be of type uint32_t. It holds + * a UCS-4 encoded Unicode character that is to be converted to UTF-8. If + * val is given as a function it is executed only once. + * \param tmp is a temporary variable and should be of type uint8_t. It + * represents an intermediate value during conversion that is to be + * output by PUT_BYTE. + * \param PUT_BYTE writes the converted UTF-8 bytes to any proper destination. + * It could be a function or a statement, and uses tmp as the input byte. + * For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be + * executed up to 4 times for values in the valid UTF-8 range and up to + * 7 times in the general case, depending on the length of the converted + * Unicode character. + */ +#define PUT_UTF8(val, tmp, PUT_BYTE)\ + {\ + int bytes, shift;\ + uint32_t in = val;\ + if (in < 0x80) {\ + tmp = in;\ + PUT_BYTE\ + } else {\ + bytes = (av_log2(in) + 4) / 5;\ + shift = (bytes - 1) * 6;\ + tmp = (256 - (256 >> bytes)) | (in >> shift);\ + PUT_BYTE\ + while (shift >= 6) {\ + shift -= 6;\ + tmp = 0x80 | ((in >> shift) & 0x3f);\ + PUT_BYTE\ + }\ + }\ + } + +/*! + * \def PUT_UTF16(val, tmp, PUT_16BIT) + * Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes). + * \param val is an input-only argument and should be of type uint32_t. It holds + * a UCS-4 encoded Unicode character that is to be converted to UTF-16. If + * val is given as a function it is executed only once. + * \param tmp is a temporary variable and should be of type uint16_t. It + * represents an intermediate value during conversion that is to be + * output by PUT_16BIT. + * \param PUT_16BIT writes the converted UTF-16 data to any proper destination + * in desired endianness. It could be a function or a statement, and uses tmp + * as the input byte. For example, PUT_BYTE could be "*output++ = tmp;" + * PUT_BYTE will be executed 1 or 2 times depending on input character. + */ +#define PUT_UTF16(val, tmp, PUT_16BIT)\ + {\ + uint32_t in = val;\ + if (in < 0x10000) {\ + tmp = in;\ + PUT_16BIT\ + } else {\ + tmp = 0xD800 | ((in - 0x10000) >> 10);\ + PUT_16BIT\ + tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\ + PUT_16BIT\ + }\ + }\ + + + +#include "mem.h" + +#ifdef HAVE_AV_CONFIG_H +# include "internal.h" +#endif /* HAVE_AV_CONFIG_H */ + +#endif /* AVUTIL_COMMON_H */ + +/* + * The following definitions are outside the multiple inclusion guard + * to ensure they are immediately available in intmath.h. + */ + +#ifndef av_log2 +# define av_log2 av_log2_c +#endif +#ifndef av_log2_16bit +# define av_log2_16bit av_log2_16bit_c +#endif +#ifndef av_ceil_log2 +# define av_ceil_log2 av_ceil_log2_c +#endif +#ifndef av_clip +# define av_clip av_clip_c +#endif +#ifndef av_clip_uint8 +# define av_clip_uint8 av_clip_uint8_c +#endif +#ifndef av_clip_int8 +# define av_clip_int8 av_clip_int8_c +#endif +#ifndef av_clip_uint16 +# define av_clip_uint16 av_clip_uint16_c +#endif +#ifndef av_clip_int16 +# define av_clip_int16 av_clip_int16_c +#endif +#ifndef av_clipl_int32 +# define av_clipl_int32 av_clipl_int32_c +#endif +#ifndef av_clipf +# define av_clipf av_clipf_c +#endif + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/intfloat_readwrite.c b/plugins/supereq/ffmpeg_fft/libavutil/intfloat_readwrite.c new file mode 100644 index 00000000..79fe1867 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/intfloat_readwrite.c @@ -0,0 +1,98 @@ +/* + * portable IEEE float/double read/write functions + * + * Copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * portable IEEE float/double read/write functions + */ + +#include <stdint.h> +#include <math.h> +#include "intfloat_readwrite.h" + +double av_int2dbl(int64_t v){ + if(v+v > 0xFFEULL<<52) + return 0.0/0.0; + return ldexp(((v&((1LL<<52)-1)) + (1LL<<52)) * (v>>63|1), (v>>52&0x7FF)-1075); +} + +float av_int2flt(int32_t v){ + if(v+v > 0xFF000000U) + return 0.0/0.0; + return ldexp(((v&0x7FFFFF) + (1<<23)) * (v>>31|1), (v>>23&0xFF)-150); +} + +double av_ext2dbl(const AVExtFloat ext){ + uint64_t m = 0; + int e, i; + + for (i = 0; i < 8; i++) + m = (m<<8) + ext.mantissa[i]; + e = (((int)ext.exponent[0]&0x7f)<<8) | ext.exponent[1]; + if (e == 0x7fff && m) + return 0.0/0.0; + e -= 16383 + 63; /* In IEEE 80 bits, the whole (i.e. 1.xxxx) + * mantissa bit is written as opposed to the + * single and double precision formats. */ + if (ext.exponent[0]&0x80) + m= -m; + return ldexp(m, e); +} + +int64_t av_dbl2int(double d){ + int e; + if ( !d) return 0; + else if(d-d) return 0x7FF0000000000000LL + ((int64_t)(d<0)<<63) + (d!=d); + d= frexp(d, &e); + return (int64_t)(d<0)<<63 | (e+1022LL)<<52 | (int64_t)((fabs(d)-0.5)*(1LL<<53)); +} + +int32_t av_flt2int(float d){ + int e; + if ( !d) return 0; + else if(d-d) return 0x7F800000 + ((d<0)<<31) + (d!=d); + d= frexp(d, &e); + return (d<0)<<31 | (e+126)<<23 | (int64_t)((fabs(d)-0.5)*(1<<24)); +} + +AVExtFloat av_dbl2ext(double d){ + struct AVExtFloat ext= {{0}}; + int e, i; double f; uint64_t m; + + f = fabs(frexp(d, &e)); + if (f >= 0.5 && f < 1) { + e += 16382; + ext.exponent[0] = e>>8; + ext.exponent[1] = e; + m = (uint64_t)ldexp(f, 64); + for (i=0; i < 8; i++) + ext.mantissa[i] = m>>(56-(i<<3)); + } else if (f != 0.0) { + ext.exponent[0] = 0x7f; ext.exponent[1] = 0xff; + if (f != 1/0.0) + ext.mantissa[0] = ~0; + } + if (d < 0) + ext.exponent[0] |= 0x80; + return ext; +} + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/intfloat_readwrite.h b/plugins/supereq/ffmpeg_fft/libavutil/intfloat_readwrite.h new file mode 100644 index 00000000..644b3e64 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/intfloat_readwrite.h @@ -0,0 +1,41 @@ +/* + * copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_INTFLOAT_READWRITE_H +#define AVUTIL_INTFLOAT_READWRITE_H + +#include <stdint.h> +#include "attributes.h" + +/* IEEE 80 bits extended float */ +typedef struct AVExtFloat { + uint8_t exponent[2]; + uint8_t mantissa[8]; +} AVExtFloat; + +double av_int2dbl(int64_t v) av_const; +float av_int2flt(int32_t v) av_const; +double av_ext2dbl(const AVExtFloat ext) av_const; +int64_t av_dbl2int(double d) av_const; +int32_t av_flt2int(float d) av_const; +AVExtFloat av_dbl2ext(double d) av_const; + +#endif /* AVUTIL_INTFLOAT_READWRITE_H */ + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/mathematics.c b/plugins/supereq/ffmpeg_fft/libavutil/mathematics.c new file mode 100644 index 00000000..c6851cb7 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/mathematics.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * miscellaneous math routines and tables + */ + +#include <assert.h> +#include <stdint.h> +#include <limits.h> +#include "mathematics.h" + +const uint8_t ff_sqrt_tab[256]={ + 0, 16, 23, 28, 32, 36, 40, 43, 46, 48, 51, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 77, 79, 80, 82, 84, 85, 87, 88, 90, + 91, 92, 94, 95, 96, 98, 99,100,102,103,104,105,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,123,124,125,126,127, +128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,151,151,152,153,154,155,156,156, +157,158,159,160,160,161,162,163,164,164,165,166,167,168,168,169,170,171,171,172,173,174,174,175,176,176,177,178,179,179,180,181, +182,182,183,184,184,185,186,186,187,188,188,189,190,190,191,192,192,193,194,194,195,196,196,197,198,198,199,200,200,201,202,202, +203,204,204,205,205,206,207,207,208,208,209,210,210,211,212,212,213,213,214,215,215,216,216,217,218,218,219,219,220,220,221,222, +222,223,223,224,224,225,226,226,227,227,228,228,229,230,230,231,231,232,232,233,233,234,235,235,236,236,237,237,238,238,239,239, +240,240,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255,255,255 +}; + +const uint8_t ff_log2_tab[256]={ + 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +const uint8_t av_reverse[256]={ +0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, +0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, +0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, +0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, +0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, +0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, +0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, +0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, +0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, +0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, +0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, +0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, +0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, +0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, +0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, +0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF, +}; + +int64_t av_gcd(int64_t a, int64_t b){ + if(b) return av_gcd(b, a%b); + else return a; +} + +int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd){ + int64_t r=0; + assert(c > 0); + assert(b >=0); + assert((unsigned)rnd<=5 && rnd!=4); + + if(a<0 && a != INT64_MIN) return -av_rescale_rnd(-a, b, c, rnd ^ ((rnd>>1)&1)); + + if(rnd==AV_ROUND_NEAR_INF) r= c/2; + else if(rnd&1) r= c-1; + + if(b<=INT_MAX && c<=INT_MAX){ + if(a<=INT_MAX) + return (a * b + r)/c; + else + return a/c*b + (a%c*b + r)/c; + }else{ +#if 1 + uint64_t a0= a&0xFFFFFFFF; + uint64_t a1= a>>32; + uint64_t b0= b&0xFFFFFFFF; + uint64_t b1= b>>32; + uint64_t t1= a0*b1 + a1*b0; + uint64_t t1a= t1<<32; + int i; + + a0 = a0*b0 + t1a; + a1 = a1*b1 + (t1>>32) + (a0<t1a); + a0 += r; + a1 += a0<r; + + for(i=63; i>=0; i--){ +// int o= a1 & 0x8000000000000000ULL; + a1+= a1 + ((a0>>i)&1); + t1+=t1; + if(/*o || */c <= a1){ + a1 -= c; + t1++; + } + } + return t1; + } +#else + AVInteger ai; + ai= av_mul_i(av_int2i(a), av_int2i(b)); + ai= av_add_i(ai, av_int2i(r)); + + return av_i2int(av_div_i(ai, av_int2i(c))); + } +#endif +} + +int64_t av_rescale(int64_t a, int64_t b, int64_t c){ + return av_rescale_rnd(a, b, c, AV_ROUND_NEAR_INF); +} + +int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq){ + int64_t b= bq.num * (int64_t)cq.den; + int64_t c= cq.num * (int64_t)bq.den; + return av_rescale_rnd(a, b, c, AV_ROUND_NEAR_INF); +} + +int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b){ + int64_t a= tb_a.num * (int64_t)tb_b.den; + int64_t b= tb_b.num * (int64_t)tb_a.den; + if (av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b) return -1; + if (av_rescale_rnd(ts_b, b, a, AV_ROUND_DOWN) < ts_a) return 1; + return 0; +} + +int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod){ + int64_t c= (a-b) & (mod-1); + if(c > (mod>>1)) + c-= mod; + return c; +} + +#ifdef TEST +#include "integer.h" +#undef printf +int main(void){ + int64_t a,b,c,d,e; + + for(a=7; a<(1LL<<62); a+=a/3+1){ + for(b=3; b<(1LL<<62); b+=b/4+1){ + for(c=9; c<(1LL<<62); c+=(c*2)/5+3){ + int64_t r= c/2; + AVInteger ai; + ai= av_mul_i(av_int2i(a), av_int2i(b)); + ai= av_add_i(ai, av_int2i(r)); + + d= av_i2int(av_div_i(ai, av_int2i(c))); + + e= av_rescale(a,b,c); + + if((double)a * (double)b / (double)c > (1LL<<63)) + continue; + + if(d!=e) printf("%"PRId64"*%"PRId64"/%"PRId64"= %"PRId64"=%"PRId64"\n", a, b, c, d, e); + } + } + } + return 0; +} +#endif diff --git a/plugins/supereq/ffmpeg_fft/libavutil/mathematics.h b/plugins/supereq/ffmpeg_fft/libavutil/mathematics.h new file mode 100644 index 00000000..06d36e09 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/mathematics.h @@ -0,0 +1,110 @@ +/* + * copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_MATHEMATICS_H +#define AVUTIL_MATHEMATICS_H + +#include <stdint.h> +#include <math.h> +#include "attributes.h" +#include "rational.h" + +#ifndef M_E +#define M_E 2.7182818284590452354 /* e */ +#endif +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#endif +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#endif +#ifndef M_LOG2_10 +#define M_LOG2_10 3.32192809488736234787 /* log_2 10 */ +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#endif +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#endif +#ifndef NAN +#define NAN (0.0/0.0) +#endif +#ifndef INFINITY +#define INFINITY (1.0/0.0) +#endif + +enum AVRounding { + AV_ROUND_ZERO = 0, ///< Round toward zero. + AV_ROUND_INF = 1, ///< Round away from zero. + AV_ROUND_DOWN = 2, ///< Round toward -infinity. + AV_ROUND_UP = 3, ///< Round toward +infinity. + AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero. +}; + +/** + * Return the greatest common divisor of a and b. + * If both a and b are 0 or either or both are <0 then behavior is + * undefined. + */ +int64_t av_const av_gcd(int64_t a, int64_t b); + +/** + * Rescale a 64-bit integer with rounding to nearest. + * A simple a*b/c isn't possible as it can overflow. + */ +int64_t av_rescale(int64_t a, int64_t b, int64_t c) av_const; + +/** + * Rescale a 64-bit integer with specified rounding. + * A simple a*b/c isn't possible as it can overflow. + */ +int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding) av_const; + +/** + * Rescale a 64-bit integer by 2 rational numbers. + */ +int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const; + +/** + * Compare 2 timestamps each in its own timebases. + * The result of the function is undefined if one of the timestamps + * is outside the int64_t range when represented in the others timebase. + * @return -1 if ts_a is before ts_b, 1 if ts_a is after ts_b or 0 if they represent the same position + */ +int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b); + +/** + * Compare 2 integers modulo mod. + * That is we compare integers a and b for which only the least + * significant log2(mod) bits are known. + * + * @param mod must be a power of 2 + * @return a negative value if a is smaller than b + * a positive value if a is greater than b + * 0 if a equals b + */ +int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod); + +#endif /* AVUTIL_MATHEMATICS_H */ + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/mem.c b/plugins/supereq/ffmpeg_fft/libavutil/mem.c new file mode 100644 index 00000000..8cad089a --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/mem.c @@ -0,0 +1,176 @@ +/* + * default memory allocator for libavutil + * Copyright (c) 2002 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * default memory allocator for libavutil + */ + +#include "config.h" + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#if HAVE_MALLOC_H +#include <malloc.h> +#endif + +#include "avutil.h" +#include "mem.h" + +/* here we can use OS-dependent allocation functions */ +#undef free +#undef malloc +#undef realloc + +#ifdef MALLOC_PREFIX + +#define malloc AV_JOIN(MALLOC_PREFIX, malloc) +#define memalign AV_JOIN(MALLOC_PREFIX, memalign) +#define posix_memalign AV_JOIN(MALLOC_PREFIX, posix_memalign) +#define realloc AV_JOIN(MALLOC_PREFIX, realloc) +#define free AV_JOIN(MALLOC_PREFIX, free) + +void *malloc(size_t size); +void *memalign(size_t align, size_t size); +int posix_memalign(void **ptr, size_t align, size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); + +#endif /* MALLOC_PREFIX */ + +/* You can redefine av_malloc and av_free in your project to use your + memory allocator. You do not need to suppress this file because the + linker will do it automatically. */ + +void *av_malloc(unsigned int size) +{ + void *ptr = NULL; +#if CONFIG_MEMALIGN_HACK + long diff; +#endif + + /* let's disallow possible ambiguous cases */ + if(size > (INT_MAX-16) ) + return NULL; + +#if CONFIG_MEMALIGN_HACK + ptr = malloc(size+16); + if(!ptr) + return ptr; + diff= ((-(long)ptr - 1)&15) + 1; + ptr = (char*)ptr + diff; + ((char*)ptr)[-1]= diff; +#elif HAVE_POSIX_MEMALIGN + if (posix_memalign(&ptr,16,size)) + ptr = NULL; +#elif HAVE_MEMALIGN + ptr = memalign(16,size); + /* Why 64? + Indeed, we should align it: + on 4 for 386 + on 16 for 486 + on 32 for 586, PPro - K6-III + on 64 for K7 (maybe for P3 too). + Because L1 and L2 caches are aligned on those values. + But I don't want to code such logic here! + */ + /* Why 16? + Because some CPUs need alignment, for example SSE2 on P4, & most RISC CPUs + it will just trigger an exception and the unaligned load will be done in the + exception handler or it will just segfault (SSE2 on P4). + Why not larger? Because I did not see a difference in benchmarks ... + */ + /* benchmarks with P3 + memalign(64)+1 3071,3051,3032 + memalign(64)+2 3051,3032,3041 + memalign(64)+4 2911,2896,2915 + memalign(64)+8 2545,2554,2550 + memalign(64)+16 2543,2572,2563 + memalign(64)+32 2546,2545,2571 + memalign(64)+64 2570,2533,2558 + + BTW, malloc seems to do 8-byte alignment by default here. + */ +#else + ptr = malloc(size); +#endif + return ptr; +} + +void *av_realloc(void *ptr, unsigned int size) +{ +#if CONFIG_MEMALIGN_HACK + int diff; +#endif + + /* let's disallow possible ambiguous cases */ + if(size > (INT_MAX-16) ) + return NULL; + +#if CONFIG_MEMALIGN_HACK + //FIXME this isn't aligned correctly, though it probably isn't needed + if(!ptr) return av_malloc(size); + diff= ((char*)ptr)[-1]; + return (char*)realloc((char*)ptr - diff, size + diff) + diff; +#else + return realloc(ptr, size); +#endif +} + +void av_free(void *ptr) +{ + /* XXX: this test should not be needed on most libcs */ + if (ptr) +#if CONFIG_MEMALIGN_HACK + free((char*)ptr - ((char*)ptr)[-1]); +#else + free(ptr); +#endif +} + +void av_freep(void *arg) +{ + void **ptr= (void**)arg; + av_free(*ptr); + *ptr = NULL; +} + +void *av_mallocz(unsigned int size) +{ + void *ptr = av_malloc(size); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +char *av_strdup(const char *s) +{ + char *ptr= NULL; + if(s){ + int len = strlen(s) + 1; + ptr = av_malloc(len); + if (ptr) + memcpy(ptr, s, len); + } + return ptr; +} + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/mem.h b/plugins/supereq/ffmpeg_fft/libavutil/mem.h new file mode 100644 index 00000000..7da0a15f --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/mem.h @@ -0,0 +1,128 @@ +/* + * copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * memory handling functions + */ + +#ifndef AVUTIL_MEM_H +#define AVUTIL_MEM_H + +#include "attributes.h" +#include "avutil.h" +#include "publik.h" + +#if defined(__ICC) && _ICC < 1200 || defined(__SUNPRO_C) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_CONST(n,t,v) const t __attribute__ ((aligned (n))) v +#elif defined(__TI_COMPILER_VERSION__) + #define DECLARE_ALIGNED(n,t,v) \ + AV_PRAGMA(DATA_ALIGN(v,n)) \ + t __attribute__((aligned(n))) v + #define DECLARE_ASM_CONST(n,t,v) \ + AV_PRAGMA(DATA_ALIGN(v,n)) \ + static const t __attribute__((aligned(n))) v +#elif defined(__GNUC__) + #define DECLARE_ALIGNED(n,t,v) t __attribute__ ((aligned (n))) v + #define DECLARE_ASM_CONST(n,t,v) static const t attribute_used __attribute__ ((aligned (n))) v +#elif defined(_MSC_VER) + #define DECLARE_ALIGNED(n,t,v) __declspec(align(n)) t v + #define DECLARE_ASM_CONST(n,t,v) __declspec(align(n)) static const t v +#else + #define DECLARE_ALIGNED(n,t,v) t v + #define DECLARE_ASM_CONST(n,t,v) static const t v +#endif + +#if AV_GCC_VERSION_AT_LEAST(3,1) + #define av_malloc_attrib __attribute__((__malloc__)) +#else + #define av_malloc_attrib +#endif + +#if (!defined(__ICC) || __ICC > 1110) && AV_GCC_VERSION_AT_LEAST(4,3) + #define av_alloc_size(n) __attribute__((alloc_size(n))) +#else + #define av_alloc_size(n) +#endif + +/** + * Allocate a block of size bytes with alignment suitable for all + * memory accesses (including vectors if available on the CPU). + * @param size Size in bytes for the memory block to be allocated. + * @return Pointer to the allocated block, NULL if the block cannot + * be allocated. + * @see av_mallocz() + */ +PUBLIK void *av_malloc(unsigned int size) av_malloc_attrib av_alloc_size(1); + +/** + * Allocate or reallocate a block of memory. + * If ptr is NULL and size > 0, allocate a new block. If + * size is zero, free the memory block pointed to by ptr. + * @param size Size in bytes for the memory block to be allocated or + * reallocated. + * @param ptr Pointer to a memory block already allocated with + * av_malloc(z)() or av_realloc() or NULL. + * @return Pointer to a newly reallocated block or NULL if the block + * cannot be reallocated or the function is used to free the memory block. + * @see av_fast_realloc() + */ +void *av_realloc(void *ptr, unsigned int size) av_alloc_size(2); + +/** + * Free a memory block which has been allocated with av_malloc(z)() or + * av_realloc(). + * @param ptr Pointer to the memory block which should be freed. + * @note ptr = NULL is explicitly allowed. + * @note It is recommended that you use av_freep() instead. + * @see av_freep() + */ +PUBLIK void av_free(void *ptr); + +/** + * Allocate a block of size bytes with alignment suitable for all + * memory accesses (including vectors if available on the CPU) and + * zero all the bytes of the block. + * @param size Size in bytes for the memory block to be allocated. + * @return Pointer to the allocated block, NULL if it cannot be allocated. + * @see av_malloc() + */ +void *av_mallocz(unsigned int size) av_malloc_attrib av_alloc_size(1); + +/** + * Duplicate the string s. + * @param s string to be duplicated + * @return Pointer to a newly allocated string containing a + * copy of s or NULL if the string cannot be allocated. + */ +char *av_strdup(const char *s) av_malloc_attrib; + +/** + * Free a memory block which has been allocated with av_malloc(z)() or + * av_realloc() and set the pointer pointing to it to NULL. + * @param ptr Pointer to the pointer to the memory block which should + * be freed. + * @see av_free() + */ +void av_freep(void *ptr); + +#endif /* AVUTIL_MEM_H */ + diff --git a/plugins/supereq/ffmpeg_fft/libavutil/rational.c b/plugins/supereq/ffmpeg_fft/libavutil/rational.c new file mode 100644 index 00000000..3e8b885d --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/rational.c @@ -0,0 +1,131 @@ +/* + * rational numbers + * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * rational numbers + * @author Michael Niedermayer <michaelni@gmx.at> + */ + +#include <assert.h> +//#include <math.h> +#include <limits.h> + +#include "common.h" +#include "mathematics.h" +#include "rational.h" + +int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max){ + AVRational a0={0,1}, a1={1,0}; + int sign= (num<0) ^ (den<0); + int64_t gcd= av_gcd(FFABS(num), FFABS(den)); + + if(gcd){ + num = FFABS(num)/gcd; + den = FFABS(den)/gcd; + } + if(num<=max && den<=max){ + a1= (AVRational){num, den}; + den=0; + } + + while(den){ + uint64_t x = num / den; + int64_t next_den= num - den*x; + int64_t a2n= x*a1.num + a0.num; + int64_t a2d= x*a1.den + a0.den; + + if(a2n > max || a2d > max){ + if(a1.num) x= (max - a0.num) / a1.num; + if(a1.den) x= FFMIN(x, (max - a0.den) / a1.den); + + if (den*(2*x*a1.den + a0.den) > num*a1.den) + a1 = (AVRational){x*a1.num + a0.num, x*a1.den + a0.den}; + break; + } + + a0= a1; + a1= (AVRational){a2n, a2d}; + num= den; + den= next_den; + } + assert(av_gcd(a1.num, a1.den) <= 1U); + + *dst_num = sign ? -a1.num : a1.num; + *dst_den = a1.den; + + return den==0; +} + +AVRational av_mul_q(AVRational b, AVRational c){ + av_reduce(&b.num, &b.den, b.num * (int64_t)c.num, b.den * (int64_t)c.den, INT_MAX); + return b; +} + +AVRational av_div_q(AVRational b, AVRational c){ + return av_mul_q(b, (AVRational){c.den, c.num}); +} + +AVRational av_add_q(AVRational b, AVRational c){ + av_reduce(&b.num, &b.den, b.num * (int64_t)c.den + c.num * (int64_t)b.den, b.den * (int64_t)c.den, INT_MAX); + return b; +} + +AVRational av_sub_q(AVRational b, AVRational c){ + return av_add_q(b, (AVRational){-c.num, c.den}); +} + +AVRational av_d2q(double d, int max){ + AVRational a; +#define LOG2 0.69314718055994530941723212145817656807550013436025 + int exponent= FFMAX( (int)(log(fabs(d) + 1e-20)/LOG2), 0); + int64_t den= 1LL << (61 - exponent); + if (isnan(d)) + return (AVRational){0,0}; + av_reduce(&a.num, &a.den, (int64_t)(d * den + 0.5), den, max); + + return a; +} + +int av_nearer_q(AVRational q, AVRational q1, AVRational q2) +{ + /* n/d is q, a/b is the median between q1 and q2 */ + int64_t a = q1.num * (int64_t)q2.den + q2.num * (int64_t)q1.den; + int64_t b = 2 * (int64_t)q1.den * q2.den; + + /* rnd_up(a*d/b) > n => a*d/b > n */ + int64_t x_up = av_rescale_rnd(a, q.den, b, AV_ROUND_UP); + + /* rnd_down(a*d/b) < n => a*d/b < n */ + int64_t x_down = av_rescale_rnd(a, q.den, b, AV_ROUND_DOWN); + + return ((x_up > q.num) - (x_down < q.num)) * av_cmp_q(q2, q1); +} + +int av_find_nearest_q_idx(AVRational q, const AVRational* q_list) +{ + int i, nearest_q_idx = 0; + for(i=0; q_list[i].den; i++) + if (av_nearer_q(q, q_list[i], q_list[nearest_q_idx]) > 0) + nearest_q_idx = i; + + return nearest_q_idx; +} diff --git a/plugins/supereq/ffmpeg_fft/libavutil/rational.h b/plugins/supereq/ffmpeg_fft/libavutil/rational.h new file mode 100644 index 00000000..cd0a945a --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libavutil/rational.h @@ -0,0 +1,130 @@ +/* + * rational numbers + * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * rational numbers + * @author Michael Niedermayer <michaelni@gmx.at> + */ + +#ifndef AVUTIL_RATIONAL_H +#define AVUTIL_RATIONAL_H + +#include <stdint.h> +#include "attributes.h" + +/** + * rational number numerator/denominator + */ +typedef struct AVRational{ + int num; ///< numerator + int den; ///< denominator +} AVRational; + +/** + * Compare two rationals. + * @param a first rational + * @param b second rational + * @return 0 if a==b, 1 if a>b and -1 if a<b + */ +static inline int av_cmp_q(AVRational a, AVRational b){ + const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den; + + if(tmp) return (tmp>>63)|1; + else return 0; +} + +/** + * Convert rational to double. + * @param a rational to convert + * @return (double) a + */ +static inline double av_q2d(AVRational a){ + return a.num / (double) a.den; +} + +/** + * Reduce a fraction. + * This is useful for framerate calculations. + * @param dst_num destination numerator + * @param dst_den destination denominator + * @param num source numerator + * @param den source denominator + * @param max the maximum allowed for dst_num & dst_den + * @return 1 if exact, 0 otherwise + */ +int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max); + +/** + * Multiply two rationals. + * @param b first rational + * @param c second rational + * @return b*c + */ +AVRational av_mul_q(AVRational b, AVRational c) av_const; + +/** + * Divide one rational by another. + * @param b first rational + * @param c second rational + * @return b/c + */ +AVRational av_div_q(AVRational b, AVRational c) av_const; + +/** + * Add two rationals. + * @param b first rational + * @param c second rational + * @return b+c + */ +AVRational av_add_q(AVRational b, AVRational c) av_const; + +/** + * Subtract one rational from another. + * @param b first rational + * @param c second rational + * @return b-c + */ +AVRational av_sub_q(AVRational b, AVRational c) av_const; + +/** + * Convert a double precision floating point number to a rational. + * @param d double to convert + * @param max the maximum allowed numerator and denominator + * @return (AVRational) d + */ +AVRational av_d2q(double d, int max) av_const; + +/** + * @return 1 if q1 is nearer to q than q2, -1 if q2 is nearer + * than q1, 0 if they have the same distance. + */ +int av_nearer_q(AVRational q, AVRational q1, AVRational q2); + +/** + * Find the nearest value in q_list to q. + * @param q_list an array of rationals terminated by {0, 0} + * @return the index of the nearest value found in the array + */ +int av_find_nearest_q_idx(AVRational q, const AVRational* q_list); + +#endif /* AVUTIL_RATIONAL_H */ + diff --git a/plugins/supereq/ffmpeg_fft/libffmpeg_fft.ver b/plugins/supereq/ffmpeg_fft/libffmpeg_fft.ver new file mode 100644 index 00000000..07b44318 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/libffmpeg_fft.ver @@ -0,0 +1,4 @@ +LIBFFMPEG_FFT_52 { + global: *; +}; + diff --git a/plugins/supereq/ffmpeg_fft/publik.h b/plugins/supereq/ffmpeg_fft/publik.h new file mode 100644 index 00000000..bb044756 --- /dev/null +++ b/plugins/supereq/ffmpeg_fft/publik.h @@ -0,0 +1,6 @@ +#ifndef PUBLIK_H_ +#define PUBLIK_H_ + +#define PUBLIK __attribute__ ((visibility ("default"))) + +#endif /* PUBLIK_H_ */ diff --git a/plugins/supereq/nsfft-1.00/README b/plugins/supereq/nsfft-1.00/README new file mode 100644 index 00000000..1ca873b1 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/README @@ -0,0 +1,15 @@ + +NSFFT (Nonrestrictive SIMD FFT) is yet another FFT library for +performing 1-dimensional fast Fourier transforms. NSDFT is a simple, +small and portable library, and it is efficient since it can utilize +SIMD instruction sets in modern processors. It performs multiple +transforms simultaneously, and thus it is especially suitable for +digital signal processing. It does not need so much computation to +make a good execution plan. This library is in public domain, so that +you can incorporate this library into your product without any +obligation. + +Visit http://shibatch.sourceforge.net/ to get the latest version of +this library. + +Contact : Naoki Shibata shibatch@users.sourceforge.net diff --git a/plugins/supereq/nsfft-1.00/dft/DFT.c b/plugins/supereq/nsfft-1.00/dft/DFT.c new file mode 100644 index 00000000..d59e6ab8 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/DFT.c @@ -0,0 +1,327 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <stdint.h> +#include <sys/time.h> + +#include "SIMDBase.h" +#include "DFT.h" +#include "DFTUndiff.h" + +int32_t getModeParamInt_purec_float(int32_t paramId); +int32_t getModeParamInt_purec_double(int32_t paramId); +int32_t getModeParamInt_purec_longdouble(int32_t paramId); +int32_t getModeParamInt_sse_float(int32_t paramId); +int32_t getModeParamInt_sse2_double(int32_t paramId); +int32_t getModeParamInt_neon_float(int32_t paramId); +int32_t getModeParamInt_avx_float(int32_t paramId); +int32_t getModeParamInt_avx_double(int32_t paramId); +int32_t getModeParamInt_altivec_float(int32_t paramId); + +char * getModeParamString_purec_float(int32_t paramId); +char * getModeParamString_purec_double(int32_t paramId); +char * getModeParamString_purec_longdouble(int32_t paramId); +char * getModeParamString_sse_float(int32_t paramId); +char * getModeParamString_sse2_double(int32_t paramId); +char * getModeParamString_neon_float(int32_t paramId); +char * getModeParamString_avx_float(int32_t paramId); +char * getModeParamString_avx_double(int32_t paramId); +char * getModeParamString_altivec_float(int32_t paramId); + +void *makePlan_purec_float(uint64_t n, uint64_t flags); +void *makePlan_purec_double(uint64_t n, uint64_t flags); +void *makePlan_purec_longdouble(uint64_t n, uint64_t flags); +void *makePlan_sse_float(uint64_t n, uint64_t flags); +void *makePlan_sse2_double(uint64_t n, uint64_t flags); +void *makePlan_neon_float(uint64_t n, uint64_t flags); +void *makePlan_avx_float(uint64_t n, uint64_t flags); +void *makePlan_avx_double(uint64_t n, uint64_t flags); +void *makePlan_altivec_float(uint64_t n, uint64_t flags); + +void *makePlanSub_purec_float(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_purec_double(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_purec_longdouble(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_sse_float(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_sse2_double(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_neon_float(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_avx_float(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_avx_double(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); +void *makePlanSub_altivec_float(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags); + +void destroyPlan_purec_float(void *p); +void destroyPlan_purec_double(void *p); +void destroyPlan_purec_longdouble(void *p); +void destroyPlan_sse_float(void *p); +void destroyPlan_sse2_double(void *p); +void destroyPlan_neon_float(void *p); +void destroyPlan_avx_float(void *p); +void destroyPlan_avx_double(void *p); +void destroyPlan_altivec_float(void *p); + +void execute_purec_float(void *p, void *s, int32_t dir); +void execute_purec_double(void *p, void *s, int32_t dir); +void execute_purec_longdouble(void *p, void *s, int32_t dir); +void execute_sse_float(void *p, void *s, int32_t dir); +void execute_sse2_double(void *p, void *s, int32_t dir); +void execute_neon_float(void *p, void *s, int32_t dir); +void execute_avx_float(void *p, void *s, int32_t dir); +void execute_avx_double(void *p, void *s, int32_t dir); +void execute_altivec_float(void *p, void *s, int32_t dir); + +void *DFT_init(int32_t mode, uint64_t n, uint64_t flags) { + switch(mode) { +#if defined(ENABLE_PUREC_FLOAT) + case 1: return makePlan_purec_float(n, flags); break; +#endif +#if defined(ENABLE_PUREC_DOUBLE) + case 2: return makePlan_purec_double(n, flags); break; +#endif +#if defined(ENABLE_PUREC_LONGDOUBLE) + case 3: return makePlan_purec_longdouble(n, flags); break; +#endif +#if defined(ENABLE_SSE_FLOAT) + case 4: return makePlan_sse_float(n, flags); break; +#endif +#if defined(ENABLE_SSE2_DOUBLE) + case 5: return makePlan_sse2_double(n, flags); break; +#endif +#if defined(ENABLE_NEON_FLOAT) + case 6: return makePlan_neon_float(n, flags); break; +#endif +#if defined(ENABLE_AVX_FLOAT) + case 7: return makePlan_avx_float(n, flags); break; +#endif +#if defined(ENABLE_AVX_DOUBLE) + case 8: return makePlan_avx_double(n, flags); break; +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case 9: return makePlan_altivec_float(n, flags); break; +#endif + default: break; + } + + return NULL; +} + +void DFT_dispose(void *p, int32_t mode) { + switch(mode) { +#if defined(ENABLE_PUREC_FLOAT) + case 1: destroyPlan_purec_float(p); break; +#endif +#if defined(ENABLE_PUREC_DOUBLE) + case 2: destroyPlan_purec_double(p); break; +#endif +#if defined(ENABLE_PUREC_LONGDOUBLE) + case 3: destroyPlan_purec_longdouble(p); break; +#endif +#if defined(ENABLE_SSE_FLOAT) + case 4: destroyPlan_sse_float(p); break; +#endif +#if defined(ENABLE_SSE2_DOUBLE) + case 5: destroyPlan_sse2_double(p); break; +#endif +#if defined(ENABLE_NEON_FLOAT) + case 6: destroyPlan_neon_float(p); break; +#endif +#if defined(ENABLE_AVX_FLOAT) + case 7: destroyPlan_avx_float(p); break; +#endif +#if defined(ENABLE_AVX_DOUBLE) + case 8: destroyPlan_avx_double(p); break; +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case 9: destroyPlan_altivec_float(p); break; +#endif + default: break; + } +} + +void DFT_execute(void *p, int32_t mode, void *s, int32_t dir) { + switch(mode) { +#if defined(ENABLE_PUREC_FLOAT) + case 1: return execute_purec_float(p, s, dir); break; +#endif +#if defined(ENABLE_PUREC_DOUBLE) + case 2: return execute_purec_double(p, s, dir); break; +#endif +#if defined(ENABLE_PUREC_LONGDOUBLE) + case 3: return execute_purec_longdouble(p, s, dir); break; +#endif +#if defined(ENABLE_SSE_FLOAT) + case 4: return execute_sse_float(p, s, dir); break; +#endif +#if defined(ENABLE_SSE2_DOUBLE) + case 5: return execute_sse2_double(p, s, dir); break; +#endif +#if defined(ENABLE_NEON_FLOAT) + case 6: return execute_neon_float(p, s, dir); break; +#endif +#if defined(ENABLE_AVX_FLOAT) + case 7: return execute_avx_float(p, s, dir); break; +#endif +#if defined(ENABLE_AVX_DOUBLE) + case 8: return execute_avx_double(p, s, dir); break; +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case 9: return execute_altivec_float(p, s, dir); break; +#endif + default: break; + } +} + +#define FILE_FORMAT_VERSION 0 + +int32_t DFT_fwrite(void *p2, FILE *fp) { + DFTUndiff *p = (DFTUndiff *)p2; + if (p->magic != MAGIC_DFT) abort(); + + if (fprintf(fp, "nsfft file format : %d\n", FILE_FORMAT_VERSION) <= 0) return 0; + if (fprintf(fp, "arch : %s\n", SIMDBase_getProcessorNameString()) <= 0) return 0; + if (fprintf(fp, "computation mode : %d\n", p->mode) <= 0) return 0; + if (fprintf(fp, "length : %d\n", ((p->flags & DFT_FLAG_REAL) != 0 || (p->flags & DFT_FLAG_ALT_REAL) != 0)? p->length * 2 : p->length) <= 0) return 0; + if (fprintf(fp, "radix2 threshold : %d\n", p->radix2thres) <= 0) return 0; + if (fprintf(fp, "transpose : %d\n", p->flagTrans) <= 0) return 0; + if (fprintf(fp, "bit reversal : %d\n", p->useCobra) <= 0) return 0; + if (fprintf(fp, "flags : %llx\n", (unsigned long long int)p->flags) <= 0) return 0; + if (fprintf(fp, "%s\n", "end :") <= 0) return 0; + + return 1; +} + +static char *startsWith(char *str1, char *str2) { + if (strncmp(str1, str2, strlen(str2)) == 0) { + return str1 + strlen(str2); + } + + return NULL; +} + +DFT *DFT_fread(FILE *fp, int32_t *errcode) { + int length = -1, radix2thres = -1, flagTrans = -1, useCobra = -1; + int mode = -1, formatver = -1; + unsigned long long int flags = (1ULL << 63); + + if (errcode != NULL) *errcode = DFT_ERROR_NOERROR; + + for(;;) { + char buf[256], *q; + if (fgets(buf, 255, fp) == NULL) { if (errcode != NULL) *errcode = DFT_ERROR_UNEXPECTED_EOF; return NULL; } + + if ((q = startsWith(buf, "nsfft file format :")) != NULL) { + if (1 != sscanf(q, "%d", &formatver)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "computation mode :")) != NULL) { + if (1 != sscanf(q, "%d", &mode)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "length :")) != NULL) { + if (1 != sscanf(q, "%d", &length)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "radix2 threshold :")) != NULL) { + if (1 != sscanf(q, "%d", &radix2thres)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "transpose :")) != NULL) { + if (1 != sscanf(q, "%d", &flagTrans)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "bit reversal :")) != NULL) { + if (1 != sscanf(q, "%d", &useCobra)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "flags :")) != NULL) { + if (1 != sscanf(q, "%llx", &flags)) { if (errcode != NULL) *errcode = DFT_ERROR_FILE_IO; return NULL; } + } else if ((q = startsWith(buf, "end :")) != NULL) { + break; + } + } + + if (formatver > FILE_FORMAT_VERSION) { + if (errcode != NULL) *errcode = DFT_ERROR_FILE_VERSION; + return NULL; + } + + switch(SIMDBase_detect(mode)) { + case 1: + break; + case 0: + if (errcode != NULL) *errcode = DFT_ERROR_MODE_NOT_AVAILABLE; + return NULL; + case -1: + if (errcode != NULL) *errcode = DFT_ERROR_MODE_NOT_COMPILED_IN; + return NULL; + } + + switch(mode) { +#if defined(ENABLE_PUREC_FLOAT) + case 1: return makePlanSub_purec_float(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_PUREC_DOUBLE) + case 2: return makePlanSub_purec_double(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_PUREC_LONGDOUBLE) + case 3: return makePlanSub_purec_longdouble(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_SSE_FLOAT) + case 4: return makePlanSub_sse_float(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_SSE2_DOUBLE) + case 5: return makePlanSub_sse2_double(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_NEON_FLOAT) + case 6: return makePlanSub_neon_float(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_AVX_FLOAT) + case 7: return makePlanSub_avx_float(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_AVX_DOUBLE) + case 8: return makePlanSub_avx_double(length, radix2thres, useCobra, flags); +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case 9: return makePlanSub_altivec_float(length, radix2thres, useCobra, flags); +#endif + } + + if (errcode != NULL) *errcode = DFT_ERROR_UNKNOWN_MODE; + + return NULL; +} + +int32_t DFT_getPlanParamInt(int32_t paramId, void *p2) { + DFTUndiff *p = (DFTUndiff *)p2; + if (p->magic != MAGIC_DFT) abort(); + + switch(paramId) { + case DFT_PARAMID_MODE: return p->mode; + case DFT_PARAMID_FFT_LENGTH: + if ((p->flags & DFT_FLAG_REAL) != 0) return p->length * 2; + if ((p->flags & DFT_FLAG_ALT_REAL) != 0) return p->length * 2; + return p->length; + case DFT_PARAMID_IS_REAL_TRANSFORM: return (p->flags & DFT_FLAG_REAL) ? 1 : 0; + case DFT_PARAMID_IS_ALT_REAL_TRANSFORM: return (p->flags & DFT_FLAG_ALT_REAL) ? 1 : 0; + case DFT_PARAMID_NO_BIT_REVERSAL: return (p->flags & DFT_FLAG_NO_BITREVERSAL) ? 1 : 0; + case DFT_PARAMID_TEST_RUN: return p->flags & 3; + } + + return -1; +} + +#if 0 +char *DFT_getPlanParamString(int32_t paramId, void *p2) { + dft_t *p = (dft_t *)p2; + if (p->magic != MAGIC_NSDFT) abort(); + + return NULL; +} +#endif + +uint32_t DFT_ilog2(uint32_t q) { + static const uint32_t tab[] = {0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4}; + uint32_t r = 0,qq; + + if (q & 0xffff0000) r = 16; + + q >>= r; + qq = q | (q >> 1); + qq |= (qq >> 2); + qq = ((qq & 0x10) >> 4) | ((qq & 0x100) >> 7) | ((qq & 0x1000) >> 10); + + return r + tab[qq] * 4 + tab[q >> (tab[qq] * 4)] - 1; +} + +double DFT_timeofday(void) { + struct timeval tp; + gettimeofday(&tp, NULL); + return (double)tp.tv_sec+(1e-6)*tp.tv_usec; +} diff --git a/plugins/supereq/nsfft-1.00/dft/DFT.h b/plugins/supereq/nsfft-1.00/dft/DFT.h new file mode 100644 index 00000000..facb701a --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/DFT.h @@ -0,0 +1,56 @@ +#ifndef __DFT_H__ +#define __DFT_H__ + +#include <stdio.h> +#include <stdint.h> + +typedef void DFT; + +int32_t DFT_getParamInt(int32_t paramId); +char *DFT_getParamString(int32_t paramId); + +int32_t DFT_getModeParamInt(int32_t paramId, int32_t mode); +char *DFT_getModeParamString(int32_t paramId, int32_t mode); + +DFT *DFT_init(int32_t mode, uint64_t n, uint64_t flags); +void DFT_dispose(DFT *p, int32_t mode); + +int32_t DFT_fwrite(DFT *p, FILE *fp); +DFT *DFT_fread(FILE *fp, int32_t *errcode); + +int32_t DFT_getPlanParamInt(int32_t paramId, void *p); + +void DFT_execute(DFT *p, int32_t mode, void *s, int32_t dir); + +uint32_t DFT_ilog2(uint32_t q); +double DFT_timeofday(void); + +#define DFT_FLAG_NO_TEST_RUN ( 0ULL << 0) +#define DFT_FLAG_LIGHT_TEST_RUN ( 1ULL << 0) +#define DFT_FLAG_HEAVY_TEST_RUN ( 2ULL << 0) +#define DFT_FLAG_EXHAUSTIVE_TEST_RUN ( 3ULL << 0) + +#define DFT_FLAG_REAL (1ULL << 2) +#define DFT_FLAG_ALT_REAL (1ULL << 3) +#define DFT_FLAG_VERBOSE (1ULL << 4) +#define DFT_FLAG_NO_BITREVERSAL (1ULL << 5) +#define DFT_FLAG_FORCE_RECURSIVE (1ULL << 6) +#define DFT_FLAG_FORCE_COBRA (1ULL << 7) + +#define DFT_PARAMID_TYPE ( 1 | ( 3 << 24 )) +#define DFT_PARAMID_MODE ( 2 | ( 3 << 24 )) +#define DFT_PARAMID_FFT_LENGTH ( 3 | ( 3 << 24 )) +#define DFT_PARAMID_IS_REAL_TRANSFORM ( 4 | ( 3 << 24 )) +#define DFT_PARAMID_IS_ALT_REAL_TRANSFORM ( 5 | ( 3 << 24 )) +#define DFT_PARAMID_NO_BIT_REVERSAL ( 6 | ( 3 << 24 )) +#define DFT_PARAMID_TEST_RUN ( 7 | ( 3 << 24 )) + +#define DFT_ERROR_NOERROR 0 +#define DFT_ERROR_FILE_VERSION 1 +#define DFT_ERROR_FILE_IO 2 +#define DFT_ERROR_UNEXPECTED_EOF 3 +#define DFT_ERROR_MODE_NOT_COMPILED_IN 4 +#define DFT_ERROR_MODE_NOT_AVAILABLE 5 +#define DFT_ERROR_UNKNOWN_MODE 6 + +#endif diff --git a/plugins/supereq/nsfft-1.00/dft/DFTUndiff.c b/plugins/supereq/nsfft-1.00/dft/DFTUndiff.c new file mode 100644 index 00000000..4985da33 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/DFTUndiff.c @@ -0,0 +1,1807 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> + +#include "SIMDBase.h" +#include "SIMDBaseUndiff.h" +#include "DFT.h" +#include "DFTUndiff.h" + +// + +#define SIN(x) sin(x) +#define COS(x) cos(x) + +#define SQRT2_2 .7071067811865475244008443621048490392848359376884740365883398689953L + +#ifndef M_PIl +#define M_PIl 3.141592653589793238462643383279502884197169399375105820974944592307L +#endif + +// + +static inline void srBut2(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + int32_t o = p->offset1; + SIMDBase_VECT t0, t1; + + t0 = SIMDBase_ADDm(&s[o ], &s[o+2]); t1 = SIMDBase_SUBm(&s[o ], &s[o+2]); + SIMDBase_STOR(&s[o ], t0); SIMDBase_STOR(&s[o+2], t1); + t0 = SIMDBase_ADDm(&s[o+1], &s[o+3]); t1 = SIMDBase_SUBm(&s[o+1], &s[o+3]); + SIMDBase_STOR(&s[o+1], t0); SIMDBase_STOR(&s[o+3], t1); +} + +static inline void srButForward4(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + int32_t o = p->offset1; + SIMDBase_VECT t0r, t0i, t1r, t1i, t2r, t2i, t3r, t3i; + + t0r = SIMDBase_ADDm(&s[o+0], &s[o+4]); t2r = SIMDBase_SUBm(&s[o+0], &s[o+4]); + t0i = SIMDBase_ADDm(&s[o+1], &s[o+5]); t2i = SIMDBase_SUBm(&s[o+1], &s[o+5]); + t1r = SIMDBase_ADDm(&s[o+2], &s[o+6]); t3i = SIMDBase_SUBm(&s[o+2], &s[o+6]); + t1i = SIMDBase_ADDm(&s[o+7], &s[o+3]); t3r = SIMDBase_SUBm(&s[o+7], &s[o+3]); + + SIMDBase_STOR(&s[o+0], SIMDBase_ADDi(t0r, t1r)); SIMDBase_STOR(&s[o+1], SIMDBase_ADDi(t0i, t1i)); + SIMDBase_STOR(&s[o+2], SIMDBase_SUBi(t0r, t1r)); SIMDBase_STOR(&s[o+3], SIMDBase_SUBi(t0i, t1i)); + SIMDBase_STOR(&s[o+4], SIMDBase_SUBi(t2r, t3r)); SIMDBase_STOR(&s[o+5], SIMDBase_SUBi(t2i, t3i)); + SIMDBase_STOR(&s[o+6], SIMDBase_ADDi(t2r, t3r)); SIMDBase_STOR(&s[o+7], SIMDBase_ADDi(t2i, t3i)); +} + +static inline void srButBackward4(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + int32_t o = p->offset1; + + SIMDBase_VECT t0r, t0i, t1r, t1i; + SIMDBase_VECT s0 = SIMDBase_LOAD(&s[o+0]), s1 = SIMDBase_LOAD(&s[o+1]), s2 = SIMDBase_LOAD(&s[o+2]), s3 = SIMDBase_LOAD(&s[o+3]); + + t0r = SIMDBase_ADDi(s0, s2); t0i = SIMDBase_SUBi(s0, s2); s0 = t0r; s2 = t0i; + t0r = SIMDBase_ADDi(s1, s3); t0i = SIMDBase_SUBi(s1, s3); s1 = t0r; s3 = t0i; + t0r = SIMDBase_ADDm(&s[o+4], &s[o+6]); t1i = SIMDBase_SUBm(&s[o+4], &s[o+6]); + t0i = SIMDBase_ADDm(&s[o+7], &s[o+5]); t1r = SIMDBase_SUBm(&s[o+7], &s[o+5]); + + SIMDBase_STOR(&s[o+4], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[o+5], SIMDBase_SUBi(s1, t0i)); + SIMDBase_STOR(&s[o+6], SIMDBase_SUBi(s2, t1r)); SIMDBase_STOR(&s[o+7], SIMDBase_SUBi(s3, t1i)); + SIMDBase_STOR(&s[o+0], SIMDBase_ADDi(s0, t0r)); SIMDBase_STOR(&s[o+1], SIMDBase_ADDi(s1, t0i)); + SIMDBase_STOR(&s[o+2], SIMDBase_ADDi(s2, t1r)); SIMDBase_STOR(&s[o+3], SIMDBase_ADDi(s3, t1i)); +} + +static inline void srButForward8(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + int32_t o = p->offset1; + SIMDBase_VECT t0r, t0i, t1r, t1i, t2r, t2i, t3r, t3i; + + SIMDBase_VECT s0 = SIMDBase_LOAD(&s[o+ 0]), s1 = SIMDBase_LOAD(&s[o+ 1]), s2 = SIMDBase_LOAD(&s[o+ 2]), s3 = SIMDBase_LOAD(&s[o+ 3]); + SIMDBase_VECT s4 = SIMDBase_LOAD(&s[o+ 4]), s5 = SIMDBase_LOAD(&s[o+ 5]), s6 = SIMDBase_LOAD(&s[o+ 6]), s7 = SIMDBase_LOAD(&s[o+ 7]); + SIMDBase_VECT s8 = SIMDBase_LOAD(&s[o+ 8]), s9 = SIMDBase_LOAD(&s[o+ 9]), sa = SIMDBase_LOAD(&s[o+10]) ,sb = SIMDBase_LOAD(&s[o+11]); + SIMDBase_VECT sc = SIMDBase_LOAD(&s[o+12]), sd = SIMDBase_LOAD(&s[o+13]), se = SIMDBase_LOAD(&s[o+14]), sf = SIMDBase_LOAD(&s[o+15]); + + t2r = SIMDBase_SUBi(s0, s8); t2i = SIMDBase_SUBi(s1, s9); + t3r = SIMDBase_SUBi(sd, s5); t3i = SIMDBase_SUBi(s4, sc); + + s0 = SIMDBase_ADDi(s0, s8); s1 = SIMDBase_ADDi(s1, s9); + s4 = SIMDBase_ADDi(s4, sc); s5 = SIMDBase_ADDi(s5, sd); + + s8 = SIMDBase_SUBi(t2r, t3r); s9 = SIMDBase_SUBi(t2i, t3i); + sc = SIMDBase_ADDi(t2r, t3r); sd = SIMDBase_ADDi(t2i, t3i); + + t2r = SIMDBase_SUBi(s2, sa); t2i = SIMDBase_SUBi(s3, sb); + t3r = SIMDBase_SUBi(sf, s7); t3i = SIMDBase_SUBi(s6, se); + + s2 = SIMDBase_ADDi(s2, sa); s3 = SIMDBase_ADDi(s3, sb); + s6 = SIMDBase_ADDi(s6, se); s7 = SIMDBase_ADDi(s7, sf); + + t0r = SIMDBase_SUBi(t2r, t3r); t1r = SIMDBase_ADDi(t2r, t3r); + t0i = SIMDBase_SUBi(t2i, t3i); t1i = SIMDBase_ADDi(t2i, t3i); + + sa = SIMDBase_MULi(SIMDBase_ADDi(t0r, t0i), SIMDBase_SET1( SQRT2_2)); + sb = SIMDBase_MULi(SIMDBase_SUBi(t0i, t0r), SIMDBase_SET1( SQRT2_2)); + se = SIMDBase_MULi(SIMDBase_SUBi(t1i, t1r), SIMDBase_SET1( SQRT2_2)); + sf = SIMDBase_MULi(SIMDBase_ADDi(t1r, t1i), SIMDBase_SET1(-SQRT2_2)); + + SIMDBase_STOR(&s[o+ 8], SIMDBase_ADDi(s8, sa)); SIMDBase_STOR(&s[o+ 9], SIMDBase_ADDi(s9, sb)); + SIMDBase_STOR(&s[o+10], SIMDBase_SUBi(s8, sa)); SIMDBase_STOR(&s[o+11], SIMDBase_SUBi(s9, sb)); + + SIMDBase_STOR(&s[o+12], SIMDBase_ADDi(sc, se)); SIMDBase_STOR(&s[o+13], SIMDBase_ADDi(sd, sf)); + SIMDBase_STOR(&s[o+14], SIMDBase_SUBi(sc, se)); SIMDBase_STOR(&s[o+15], SIMDBase_SUBi(sd, sf)); + + t0r = SIMDBase_ADDi(s0, s4); t2r = SIMDBase_SUBi(s0, s4); + t0i = SIMDBase_ADDi(s1, s5); t2i = SIMDBase_SUBi(s1, s5); + + t1r = SIMDBase_ADDi(s2, s6); t3i = SIMDBase_SUBi(s2, s6); + t1i = SIMDBase_ADDi(s3, s7); t3r = SIMDBase_SUBi(s7, s3); + + SIMDBase_STOR(&s[o+0], SIMDBase_ADDi(t0r, t1r)); SIMDBase_STOR(&s[o+1], SIMDBase_ADDi(t0i, t1i)); + SIMDBase_STOR(&s[o+2], SIMDBase_SUBi(t0r, t1r)); SIMDBase_STOR(&s[o+3], SIMDBase_SUBi(t0i, t1i)); + SIMDBase_STOR(&s[o+4], SIMDBase_SUBi(t2r, t3r)); SIMDBase_STOR(&s[o+5], SIMDBase_SUBi(t2i, t3i)); + SIMDBase_STOR(&s[o+6], SIMDBase_ADDi(t2r, t3r)); SIMDBase_STOR(&s[o+7], SIMDBase_ADDi(t2i, t3i)); +} + +static void srButBackward8(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + int32_t o = p->offset1; + SIMDBase_VECT t0r, t0i, t1r, t1i; + + SIMDBase_VECT s0 = SIMDBase_LOAD(&s[o+ 0]), s1 = SIMDBase_LOAD(&s[o+ 1]), s2 = SIMDBase_LOAD(&s[o+ 2]), s3 = SIMDBase_LOAD(&s[o+ 3]); + SIMDBase_VECT s4 = SIMDBase_LOAD(&s[o+ 4]), s5 = SIMDBase_LOAD(&s[o+ 5]), s6 = SIMDBase_LOAD(&s[o+ 6]), s7 = SIMDBase_LOAD(&s[o+ 7]); + SIMDBase_VECT s8 = SIMDBase_LOAD(&s[o+ 8]), s9 = SIMDBase_LOAD(&s[o+ 9]), sa = SIMDBase_LOAD(&s[o+10]) ,sb = SIMDBase_LOAD(&s[o+11]); + SIMDBase_VECT sc = SIMDBase_LOAD(&s[o+12]), sd = SIMDBase_LOAD(&s[o+13]), se = SIMDBase_LOAD(&s[o+14]), sf = SIMDBase_LOAD(&s[o+15]); + + t0r = SIMDBase_ADDi(s8, sa); t0i = SIMDBase_SUBi(s8, sa); s8 = t0r; sa = t0i; + t0r = SIMDBase_ADDi(s9, sb); t0i = SIMDBase_SUBi(s9, sb); s9 = t0r; sb = t0i; + t0r = SIMDBase_ADDi(sc, se); t0i = SIMDBase_SUBi(sc, se); sc = t0r; se = t0i; + t0r = SIMDBase_ADDi(sd, sf); t0i = SIMDBase_SUBi(sd, sf); sd = t0r; sf = t0i; + t0r = SIMDBase_ADDi(s0, s2); t0i = SIMDBase_SUBi(s0, s2); s0 = t0r; s2 = t0i; + t0r = SIMDBase_ADDi(s1, s3); t0i = SIMDBase_SUBi(s1, s3); s1 = t0r; s3 = t0i; + + t0r = SIMDBase_ADDi(s4, s6); t0i = SIMDBase_ADDi(s7, s5); + t1r = SIMDBase_SUBi(s7, s5); t1i = SIMDBase_SUBi(s4, s6); + + s4 = SIMDBase_SUBi(s0, t0r); s5 = SIMDBase_SUBi(s1, t0i); + s6 = SIMDBase_SUBi(s2, t1r); s7 = SIMDBase_SUBi(s3, t1i); + s0 = SIMDBase_ADDi(s0, t0r); s1 = SIMDBase_ADDi(s1, t0i); + s2 = SIMDBase_ADDi(s2, t1r); s3 = SIMDBase_ADDi(s3, t1i); + + t0r = SIMDBase_ADDi(s8, sc); t0i = SIMDBase_ADDi(s9, sd); + t1r = SIMDBase_SUBi(sd, s9); t1i = SIMDBase_SUBi(s8, sc); + + s8 = SIMDBase_SUBi(s0, t0r); s9 = SIMDBase_SUBi(s1, t0i); + sc = SIMDBase_SUBi(s4, t1r); sd = SIMDBase_SUBi(s5, t1i); + s0 = SIMDBase_ADDi(s0, t0r); s1 = SIMDBase_ADDi(s1, t0i); + s4 = SIMDBase_ADDi(s4, t1r); s5 = SIMDBase_ADDi(s5, t1i); + + t0r = SIMDBase_MULi(SIMDBase_SUBi(sa, sb), SIMDBase_SET1( SQRT2_2)); + t0i = SIMDBase_MULi(SIMDBase_ADDi(sa, sb), SIMDBase_SET1( SQRT2_2)); + t1r = SIMDBase_MULi(SIMDBase_ADDi(se, sf), SIMDBase_SET1(-SQRT2_2)); + t1i = SIMDBase_MULi(SIMDBase_SUBi(se, sf), SIMDBase_SET1( SQRT2_2)); + + sa = t0r; sb = t0i; se = t1r; sf = t1i; + + t0r = SIMDBase_ADDi(sa, se); t0i = SIMDBase_ADDi(sb, sf); + t1r = SIMDBase_SUBi(sf, sb); t1i = SIMDBase_SUBi(sa, se); + + sa = SIMDBase_SUBi(s2, t0r); sb = SIMDBase_SUBi(s3, t0i); + se = SIMDBase_SUBi(s6, t1r); sf = SIMDBase_SUBi(s7, t1i); + s2 = SIMDBase_ADDi(s2, t0r); s3 = SIMDBase_ADDi(s3, t0i); + s6 = SIMDBase_ADDi(s6, t1r); s7 = SIMDBase_ADDi(s7, t1i); + + SIMDBase_STOR(&s[o+ 0], s0); SIMDBase_STOR(&s[o+ 1], s1); SIMDBase_STOR(&s[o+ 2], s2); SIMDBase_STOR(&s[o+ 3], s3); + SIMDBase_STOR(&s[o+ 4], s4); SIMDBase_STOR(&s[o+ 5], s5); SIMDBase_STOR(&s[o+ 6], s6); SIMDBase_STOR(&s[o+ 7], s7); + SIMDBase_STOR(&s[o+ 8], s8); SIMDBase_STOR(&s[o+ 9], s9); SIMDBase_STOR(&s[o+10], sa); SIMDBase_STOR(&s[o+11], sb); + SIMDBase_STOR(&s[o+12], sc); SIMDBase_STOR(&s[o+13], sd); SIMDBase_STOR(&s[o+14], se); SIMDBase_STOR(&s[o+15], sf); +} + +#if 0 +static inline void srButForwardSub(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + SIMDBase_REAL *tbl = p->ptTable[p->log2butlen]; + + int32_t i0 = p->offset1; + int32_t i1 = i0 + p->stride; + int32_t i2 = i1 + p->stride; + int32_t i3 = i2 + p->stride; + int32_t im = i1; + + int32_t p0 = p->offset2 & (p->butlen*4-1); + + while(i0 < im) { + SIMDBase_VECT t0r, t0i, t1r, t1i; + SIMDBase_VECT s00, s01, s10, s11, s20, s21, s30, s31; + SIMDBase_VECT a0, a1, a2, a3; + + s00 = SIMDBase_LOAD(&s[i0+0]), s01 = SIMDBase_LOAD(&s[i0+1]); + s10 = SIMDBase_LOAD(&s[i1+0]), s11 = SIMDBase_LOAD(&s[i1+1]); + s20 = SIMDBase_LOAD(&s[i2+0]), s21 = SIMDBase_LOAD(&s[i2+1]); + s30 = SIMDBase_LOAD(&s[i3+0]), s31 = SIMDBase_LOAD(&s[i3+1]); + + t0r = SIMDBase_SUBi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t0i = SIMDBase_SUBi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + t1r = SIMDBase_ADDi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t1i = SIMDBase_ADDi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + a0 = SIMDBase_LOAD1(&tbl[p0+0]); a1 = SIMDBase_LOAD1(&tbl[p0+1]); + a2 = SIMDBase_LOAD1(&tbl[p0+2]); a3 = SIMDBase_LOAD1(&tbl[p0+3]); + + SIMDBase_STOR(&s[i0 ], SIMDBase_ADDi(s00, s20)); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s01, s21)); + SIMDBase_STOR(&s[i1 ], SIMDBase_ADDi(s10, s30)); SIMDBase_STOR(&s[i1+1], SIMDBase_ADDi(s11, s31)); + +#ifndef SIMDBase_FMADD_AVAILABLE + SIMDBase_STOR(&s[i2 ], SIMDBase_SUBi(SIMDBase_MULi(t0r, a0), SIMDBase_MULi(t0i, a1))); + SIMDBase_STOR(&s[i2+1], SIMDBase_ADDi(SIMDBase_MULi(t0r, a1), SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3 ], SIMDBase_SUBi(SIMDBase_MULi(t1r, a2), SIMDBase_MULi(t1i, a3))); + SIMDBase_STOR(&s[i3+1], SIMDBase_ADDi(SIMDBase_MULi(t1r, a3), SIMDBase_MULi(t1i, a2))); +#else + SIMDBase_STOR(&s[i2 ], SIMDBase_FMSUBi(t0i, a1, SIMDBase_MULi(t0r, a0))); + SIMDBase_STOR(&s[i2+1], SIMDBase_FMADDi(t0r, a1, SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3 ], SIMDBase_FMSUBi(t1i, a3, SIMDBase_MULi(t1r, a2))); + SIMDBase_STOR(&s[i3+1], SIMDBase_FMADDi(t1r, a3, SIMDBase_MULi(t1i, a2))); +#endif + + i0 += 2; i1 += 2; i2 += 2; i3 += 2; + p0 += 4; + } +} +#endif + +#if 0 +static inline void srButBackwardSub(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + SIMDBase_REAL *tbl = p->ptTable[p->log2butlen]; + + int32_t i0 = p->offset1; + int32_t i1 = i0 + p->stride; + int32_t i2 = i1 + p->stride; + int32_t i3 = i2 + p->stride; + int32_t im = i1; + + int32_t p0 = p->offset2 & (p->butlen*4-1); + + while(i0 < im) { + SIMDBase_VECT t0r, t0i, t1r, t1i, u, v; + SIMDBase_VECT s00, s01, s10, s11, s20, s21, s30, s31; + SIMDBase_VECT a0, a1, a2, a3; + + s20 = SIMDBase_LOAD(&s[i2+0]); s21 = SIMDBase_LOAD(&s[i2+1]); + a0 = SIMDBase_LOAD1(&tbl[p0+0]); a1 = SIMDBase_LOAD1(&tbl[p0+1]); + u = SIMDBase_ADDi(SIMDBase_MULi(s20, a0), SIMDBase_MULi(s21, a1)); + + s30 = SIMDBase_LOAD(&s[i3+0]); s31 = SIMDBase_LOAD(&s[i3+1]); + a2 = SIMDBase_LOAD1(&tbl[p0+2]); a3 = SIMDBase_LOAD1(&tbl[p0+3]); + v = SIMDBase_ADDi(SIMDBase_MULi(s30, a2), SIMDBase_MULi(s31, a3)); + + t0r = SIMDBase_ADDi(u, v); t1i = SIMDBase_SUBi(u, v); + u = SIMDBase_SUBi(SIMDBase_MULi(s31, a2), SIMDBase_MULi(s30, a3)); + v = SIMDBase_SUBi(SIMDBase_MULi(s21, a0), SIMDBase_MULi(s20, a1)); + t0i = SIMDBase_ADDi(u, v); t1r = SIMDBase_SUBi(u, v); + + s00 = SIMDBase_LOAD(&s[i0+0]); s01 = SIMDBase_LOAD(&s[i0+1]); + s10 = SIMDBase_LOAD(&s[i1+0]); s11 = SIMDBase_LOAD(&s[i1+1]); + + SIMDBase_STOR(&s[i2+0], SIMDBase_SUBi(s00, t0r)); SIMDBase_STOR(&s[i0+0], SIMDBase_ADDi(s00, t0r)); + SIMDBase_STOR(&s[i2+1], SIMDBase_SUBi(s01, t0i)); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s01, t0i)); + SIMDBase_STOR(&s[i3+0], SIMDBase_SUBi(s10, t1r)); SIMDBase_STOR(&s[i1+0], SIMDBase_ADDi(s10, t1r)); + SIMDBase_STOR(&s[i3+1], SIMDBase_SUBi(s11, t1i)); SIMDBase_STOR(&s[i1+1], SIMDBase_ADDi(s11, t1i)); + + i0 += 2; i1 += 2; i2 += 2; i3 += 2; + p0 += 4; + } +} + +static void srButBackwardSubUnrolled(DFTUndiff *p) { + srButBackwardSub(p); +} +#endif + +static inline void srButForwardSubUnrolled(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + SIMDBase_REAL *tbl = p->ptTable[p->log2butlen]; + + int32_t i0 = p->offset1; + int32_t i1 = i0 + p->stride; + int32_t i2 = i1 + p->stride; + int32_t i3 = i2 + p->stride; + int32_t im = i1; + + int32_t p0 = p->offset2 & (p->butlen*4-1); + + while(i0 < im) { + SIMDBase_VECT t0r, t0i, t1r, t1i; + SIMDBase_VECT s00, s01, s10, s11, s20, s21, s30, s31; + SIMDBase_VECT a0, a1, a2, a3; + + // + + s00 = SIMDBase_LOAD(&s[i0+0]); s01 = SIMDBase_LOAD(&s[i0+1]); + s10 = SIMDBase_LOAD(&s[i1+0]); s11 = SIMDBase_LOAD(&s[i1+1]); + s20 = SIMDBase_LOAD(&s[i2+0]); s21 = SIMDBase_LOAD(&s[i2+1]); + s30 = SIMDBase_LOAD(&s[i3+0]); s31 = SIMDBase_LOAD(&s[i3+1]); + + t0r = SIMDBase_SUBi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t0i = SIMDBase_SUBi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + t1r = SIMDBase_ADDi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t1i = SIMDBase_ADDi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + a0 = SIMDBase_LOAD1(&tbl[p0+0]); a1 = SIMDBase_LOAD1(&tbl[p0+1]); + a2 = SIMDBase_LOAD1(&tbl[p0+2]); a3 = SIMDBase_LOAD1(&tbl[p0+3]); + + SIMDBase_STOR(&s[i0 ], SIMDBase_ADDi(s00, s20)); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s01, s21)); + SIMDBase_STOR(&s[i1 ], SIMDBase_ADDi(s10, s30)); SIMDBase_STOR(&s[i1+1], SIMDBase_ADDi(s11, s31)); + +#ifndef SIMDBase_FMADD_AVAILABLE + SIMDBase_STOR(&s[i2 ], SIMDBase_SUBi(SIMDBase_MULi(t0r, a0), SIMDBase_MULi(t0i, a1))); + SIMDBase_STOR(&s[i2+1], SIMDBase_ADDi(SIMDBase_MULi(t0r, a1), SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3 ], SIMDBase_SUBi(SIMDBase_MULi(t1r, a2), SIMDBase_MULi(t1i, a3))); + SIMDBase_STOR(&s[i3+1], SIMDBase_ADDi(SIMDBase_MULi(t1r, a3), SIMDBase_MULi(t1i, a2))); +#else + SIMDBase_STOR(&s[i2 ], SIMDBase_FMSUBi(t0i, a1, SIMDBase_MULi(t0r, a0))); + SIMDBase_STOR(&s[i2+1], SIMDBase_FMADDi(t0r, a1, SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3 ], SIMDBase_FMSUBi(t1i, a3, SIMDBase_MULi(t1r, a2))); + SIMDBase_STOR(&s[i3+1], SIMDBase_FMADDi(t1r, a3, SIMDBase_MULi(t1i, a2))); +#endif + + // + + s00 = SIMDBase_LOAD(&s[i0+2]); s01 = SIMDBase_LOAD(&s[i0+3]); + s10 = SIMDBase_LOAD(&s[i1+2]); s11 = SIMDBase_LOAD(&s[i1+3]); + s20 = SIMDBase_LOAD(&s[i2+2]); s21 = SIMDBase_LOAD(&s[i2+3]); + s30 = SIMDBase_LOAD(&s[i3+2]); s31 = SIMDBase_LOAD(&s[i3+3]); + + t0r = SIMDBase_SUBi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t0i = SIMDBase_SUBi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + t1r = SIMDBase_ADDi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t1i = SIMDBase_ADDi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + a0 = SIMDBase_LOAD1(&tbl[p0+4]); a1 = SIMDBase_LOAD1(&tbl[p0+5]); + a2 = SIMDBase_LOAD1(&tbl[p0+6]); a3 = SIMDBase_LOAD1(&tbl[p0+7]); + + SIMDBase_STOR(&s[i0+2], SIMDBase_ADDi(s00, s20)); SIMDBase_STOR(&s[i0+3], SIMDBase_ADDi(s01, s21)); + SIMDBase_STOR(&s[i1+2], SIMDBase_ADDi(s10, s30)); SIMDBase_STOR(&s[i1+3], SIMDBase_ADDi(s11, s31)); + +#ifndef SIMDBase_FMADD_AVAILABLE + SIMDBase_STOR(&s[i2+2], SIMDBase_SUBi(SIMDBase_MULi(t0r, a0), SIMDBase_MULi(t0i, a1))); + SIMDBase_STOR(&s[i2+3], SIMDBase_ADDi(SIMDBase_MULi(t0r, a1), SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3+2], SIMDBase_SUBi(SIMDBase_MULi(t1r, a2), SIMDBase_MULi(t1i, a3))); + SIMDBase_STOR(&s[i3+3], SIMDBase_ADDi(SIMDBase_MULi(t1r, a3), SIMDBase_MULi(t1i, a2))); +#else + SIMDBase_STOR(&s[i2+2], SIMDBase_FMSUBi(t0i, a1, SIMDBase_MULi(t0r, a0))); + SIMDBase_STOR(&s[i2+3], SIMDBase_FMADDi(t0r, a1, SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3+2], SIMDBase_FMSUBi(t1i, a3, SIMDBase_MULi(t1r, a2))); + SIMDBase_STOR(&s[i3+3], SIMDBase_FMADDi(t1r, a3, SIMDBase_MULi(t1i, a2))); +#endif + + // + + s00 = SIMDBase_LOAD(&s[i0+4]); s01 = SIMDBase_LOAD(&s[i0+5]); + s10 = SIMDBase_LOAD(&s[i1+4]); s11 = SIMDBase_LOAD(&s[i1+5]); + s20 = SIMDBase_LOAD(&s[i2+4]); s21 = SIMDBase_LOAD(&s[i2+5]); + s30 = SIMDBase_LOAD(&s[i3+4]); s31 = SIMDBase_LOAD(&s[i3+5]); + + t0r = SIMDBase_SUBi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t0i = SIMDBase_SUBi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + t1r = SIMDBase_ADDi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t1i = SIMDBase_ADDi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + a0 = SIMDBase_LOAD1(&tbl[p0+ 8]); a1 = SIMDBase_LOAD1(&tbl[p0+ 9]); + a2 = SIMDBase_LOAD1(&tbl[p0+10]); a3 = SIMDBase_LOAD1(&tbl[p0+11]); + + SIMDBase_STOR(&s[i0+4], SIMDBase_ADDi(s00, s20)); SIMDBase_STOR(&s[i0+5], SIMDBase_ADDi(s01, s21)); + SIMDBase_STOR(&s[i1+4], SIMDBase_ADDi(s10, s30)); SIMDBase_STOR(&s[i1+5], SIMDBase_ADDi(s11, s31)); + +#ifndef SIMDBase_FMADD_AVAILABLE + SIMDBase_STOR(&s[i2+4], SIMDBase_SUBi(SIMDBase_MULi(t0r, a0), SIMDBase_MULi(t0i, a1))); + SIMDBase_STOR(&s[i2+5], SIMDBase_ADDi(SIMDBase_MULi(t0r, a1), SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3+4], SIMDBase_SUBi(SIMDBase_MULi(t1r, a2), SIMDBase_MULi(t1i, a3))); + SIMDBase_STOR(&s[i3+5], SIMDBase_ADDi(SIMDBase_MULi(t1r, a3), SIMDBase_MULi(t1i, a2))); +#else + SIMDBase_STOR(&s[i2+4], SIMDBase_FMSUBi(t0i, a1, SIMDBase_MULi(t0r, a0))); + SIMDBase_STOR(&s[i2+5], SIMDBase_FMADDi(t0r, a1, SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3+4], SIMDBase_FMSUBi(t1i, a3, SIMDBase_MULi(t1r, a2))); + SIMDBase_STOR(&s[i3+5], SIMDBase_FMADDi(t1r, a3, SIMDBase_MULi(t1i, a2))); +#endif + + // + + s00 = SIMDBase_LOAD(&s[i0+6]); s01 = SIMDBase_LOAD(&s[i0+7]); + s10 = SIMDBase_LOAD(&s[i1+6]); s11 = SIMDBase_LOAD(&s[i1+7]); + s20 = SIMDBase_LOAD(&s[i2+6]); s21 = SIMDBase_LOAD(&s[i2+7]); + s30 = SIMDBase_LOAD(&s[i3+6]); s31 = SIMDBase_LOAD(&s[i3+7]); + + t0r = SIMDBase_SUBi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t0i = SIMDBase_SUBi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + t1r = SIMDBase_ADDi(SIMDBase_SUBi(s00, s20), SIMDBase_SUBi(s31, s11)); + t1i = SIMDBase_ADDi(SIMDBase_SUBi(s01, s21), SIMDBase_SUBi(s10, s30)); + + a0 = SIMDBase_LOAD1(&tbl[p0+12]); a1 = SIMDBase_LOAD1(&tbl[p0+13]); + a2 = SIMDBase_LOAD1(&tbl[p0+14]); a3 = SIMDBase_LOAD1(&tbl[p0+15]); + + SIMDBase_STOR(&s[i0+6], SIMDBase_ADDi(s00, s20)); SIMDBase_STOR(&s[i0+7], SIMDBase_ADDi(s01, s21)); + SIMDBase_STOR(&s[i1+6], SIMDBase_ADDi(s10, s30)); SIMDBase_STOR(&s[i1+7], SIMDBase_ADDi(s11, s31)); + +#ifndef SIMDBase_FMADD_AVAILABLE + SIMDBase_STOR(&s[i2+6], SIMDBase_SUBi(SIMDBase_MULi(t0r, a0), SIMDBase_MULi(t0i, a1))); + SIMDBase_STOR(&s[i2+7], SIMDBase_ADDi(SIMDBase_MULi(t0r, a1), SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3+6], SIMDBase_SUBi(SIMDBase_MULi(t1r, a2), SIMDBase_MULi(t1i, a3))); + SIMDBase_STOR(&s[i3+7], SIMDBase_ADDi(SIMDBase_MULi(t1r, a3), SIMDBase_MULi(t1i, a2))); +#else + SIMDBase_STOR(&s[i2+6], SIMDBase_FMSUBi(t0i, a1, SIMDBase_MULi(t0r, a0))); + SIMDBase_STOR(&s[i2+7], SIMDBase_FMADDi(t0r, a1, SIMDBase_MULi(t0i, a0))); + SIMDBase_STOR(&s[i3+6], SIMDBase_FMSUBi(t1i, a3, SIMDBase_MULi(t1r, a2))); + SIMDBase_STOR(&s[i3+7], SIMDBase_FMADDi(t1r, a3, SIMDBase_MULi(t1i, a2))); +#endif + + // + + i0 += 8; i1 += 8; i2 += 8; i3 += 8; + p0 += 16; + } +} + +#if 1 +static void srButBackwardSubUnrolled(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + SIMDBase_REAL *tbl = p->ptTable[p->log2butlen]; + + int32_t i0 = p->offset1; + int32_t i1 = i0 + p->stride; + int32_t i2 = i1 + p->stride; + int32_t i3 = i2 + p->stride; + int32_t im = i1; + + int32_t p0 = p->offset2 & (p->butlen*4-1); + + while(i0 < im) { + SIMDBase_VECT t0r, t0i, t1r, t1i, u, v; + SIMDBase_VECT s00, s01, s10, s11, s20, s21, s30, s31; + SIMDBase_VECT a0, a1, a2, a3; + + // + + s20 = SIMDBase_LOAD(&s[i2+ 0]); s21 = SIMDBase_LOAD(&s[i2+ 1]); + a0 = SIMDBase_LOAD1(&tbl[p0+ 0]); a1 = SIMDBase_LOAD1(&tbl[p0+ 1]); +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_ADDi(SIMDBase_MULi(s20, a0), SIMDBase_MULi(s21, a1)); +#else + u = SIMDBase_FMADDi(s20, a0, SIMDBase_MULi(s21, a1)); +#endif + + s30 = SIMDBase_LOAD(&s[i3+ 0]); s31 = SIMDBase_LOAD(&s[i3+ 1]); + a2 = SIMDBase_LOAD1(&tbl[p0+ 2]); a3 = SIMDBase_LOAD1(&tbl[p0+ 3]); +#ifndef SIMDBase_FMADD_AVAILABLE + v = SIMDBase_ADDi(SIMDBase_MULi(s30, a2), SIMDBase_MULi(s31, a3)); +#else + v = SIMDBase_FMADDi(s30, a2, SIMDBase_MULi(s31, a3)); +#endif + + t0r = SIMDBase_ADDi(u, v); t1i = SIMDBase_SUBi(u, v); + +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_SUBi(SIMDBase_MULi(s31, a2), SIMDBase_MULi(s30, a3)); + v = SIMDBase_SUBi(SIMDBase_MULi(s21, a0), SIMDBase_MULi(s20, a1)); +#else + u = SIMDBase_FMSUBi(s30, a3, SIMDBase_MULi(s31, a2)); + v = SIMDBase_FMSUBi(s20, a1, SIMDBase_MULi(s21, a0)); +#endif + t0i = SIMDBase_ADDi(u, v); t1r = SIMDBase_SUBi(u, v); + + s00 = SIMDBase_LOAD(&s[i0+ 0]); s01 = SIMDBase_LOAD(&s[i0+ 1]); + s10 = SIMDBase_LOAD(&s[i1+ 0]); s11 = SIMDBase_LOAD(&s[i1+ 1]); + + SIMDBase_STOR(&s[i2+ 0], SIMDBase_SUBi(s00, t0r)); SIMDBase_STOR(&s[i0+ 0], SIMDBase_ADDi(s00, t0r)); + SIMDBase_STOR(&s[i2+ 1], SIMDBase_SUBi(s01, t0i)); SIMDBase_STOR(&s[i0+ 1], SIMDBase_ADDi(s01, t0i)); + SIMDBase_STOR(&s[i3+ 0], SIMDBase_SUBi(s10, t1r)); SIMDBase_STOR(&s[i1+ 0], SIMDBase_ADDi(s10, t1r)); + SIMDBase_STOR(&s[i3+ 1], SIMDBase_SUBi(s11, t1i)); SIMDBase_STOR(&s[i1+ 1], SIMDBase_ADDi(s11, t1i)); + + // + + s20 = SIMDBase_LOAD(&s[i2+ 2]); s21 = SIMDBase_LOAD(&s[i2+ 3]); + a0 = SIMDBase_LOAD1(&tbl[p0+ 4]); a1 = SIMDBase_LOAD1(&tbl[p0+ 5]); +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_ADDi(SIMDBase_MULi(s20, a0), SIMDBase_MULi(s21, a1)); +#else + u = SIMDBase_FMADDi(s20, a0, SIMDBase_MULi(s21, a1)); +#endif + + s30 = SIMDBase_LOAD(&s[i3+ 2]); s31 = SIMDBase_LOAD(&s[i3+ 3]); + a2 = SIMDBase_LOAD1(&tbl[p0+ 6]); a3 = SIMDBase_LOAD1(&tbl[p0+ 7]); +#ifndef SIMDBase_FMADD_AVAILABLE + v = SIMDBase_ADDi(SIMDBase_MULi(s30, a2), SIMDBase_MULi(s31, a3)); +#else + v = SIMDBase_FMADDi(s30, a2, SIMDBase_MULi(s31, a3)); +#endif + + t0r = SIMDBase_ADDi(u, v); t1i = SIMDBase_SUBi(u, v); + +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_SUBi(SIMDBase_MULi(s31, a2), SIMDBase_MULi(s30, a3)); + v = SIMDBase_SUBi(SIMDBase_MULi(s21, a0), SIMDBase_MULi(s20, a1)); +#else + u = SIMDBase_FMSUBi(s30, a3, SIMDBase_MULi(s31, a2)); + v = SIMDBase_FMSUBi(s20, a1, SIMDBase_MULi(s21, a0)); +#endif + t0i = SIMDBase_ADDi(u, v); t1r = SIMDBase_SUBi(u, v); + + s00 = SIMDBase_LOAD(&s[i0+ 2]); s01 = SIMDBase_LOAD(&s[i0+ 3]); + s10 = SIMDBase_LOAD(&s[i1+ 2]); s11 = SIMDBase_LOAD(&s[i1+ 3]); + + SIMDBase_STOR(&s[i2+ 2], SIMDBase_SUBi(s00, t0r)); SIMDBase_STOR(&s[i0+ 2], SIMDBase_ADDi(s00, t0r)); + SIMDBase_STOR(&s[i2+ 3], SIMDBase_SUBi(s01, t0i)); SIMDBase_STOR(&s[i0+ 3], SIMDBase_ADDi(s01, t0i)); + SIMDBase_STOR(&s[i3+ 2], SIMDBase_SUBi(s10, t1r)); SIMDBase_STOR(&s[i1+ 2], SIMDBase_ADDi(s10, t1r)); + SIMDBase_STOR(&s[i3+ 3], SIMDBase_SUBi(s11, t1i)); SIMDBase_STOR(&s[i1+ 3], SIMDBase_ADDi(s11, t1i)); + + // + + s20 = SIMDBase_LOAD(&s[i2+ 4]); s21 = SIMDBase_LOAD(&s[i2+ 5]); + a0 = SIMDBase_LOAD1(&tbl[p0+ 8]); a1 = SIMDBase_LOAD1(&tbl[p0+ 9]); +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_ADDi(SIMDBase_MULi(s20, a0), SIMDBase_MULi(s21, a1)); +#else + u = SIMDBase_FMADDi(s20, a0, SIMDBase_MULi(s21, a1)); +#endif + + s30 = SIMDBase_LOAD(&s[i3+ 4]); s31 = SIMDBase_LOAD(&s[i3+ 5]); + a2 = SIMDBase_LOAD1(&tbl[p0+10]); a3 = SIMDBase_LOAD1(&tbl[p0+11]); +#ifndef SIMDBase_FMADD_AVAILABLE + v = SIMDBase_ADDi(SIMDBase_MULi(s30, a2), SIMDBase_MULi(s31, a3)); +#else + v = SIMDBase_FMADDi(s30, a2, SIMDBase_MULi(s31, a3)); +#endif + + t0r = SIMDBase_ADDi(u, v); t1i = SIMDBase_SUBi(u, v); + +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_SUBi(SIMDBase_MULi(s31, a2), SIMDBase_MULi(s30, a3)); + v = SIMDBase_SUBi(SIMDBase_MULi(s21, a0), SIMDBase_MULi(s20, a1)); +#else + u = SIMDBase_FMSUBi(s30, a3, SIMDBase_MULi(s31, a2)); + v = SIMDBase_FMSUBi(s20, a1, SIMDBase_MULi(s21, a0)); +#endif + t0i = SIMDBase_ADDi(u, v); t1r = SIMDBase_SUBi(u, v); + + s00 = SIMDBase_LOAD(&s[i0+ 4]); s01 = SIMDBase_LOAD(&s[i0+ 5]); + s10 = SIMDBase_LOAD(&s[i1+ 4]); s11 = SIMDBase_LOAD(&s[i1+ 5]); + + SIMDBase_STOR(&s[i2+ 4], SIMDBase_SUBi(s00, t0r)); SIMDBase_STOR(&s[i0+ 4], SIMDBase_ADDi(s00, t0r)); + SIMDBase_STOR(&s[i2+ 5], SIMDBase_SUBi(s01, t0i)); SIMDBase_STOR(&s[i0+ 5], SIMDBase_ADDi(s01, t0i)); + SIMDBase_STOR(&s[i3+ 4], SIMDBase_SUBi(s10, t1r)); SIMDBase_STOR(&s[i1+ 4], SIMDBase_ADDi(s10, t1r)); + SIMDBase_STOR(&s[i3+ 5], SIMDBase_SUBi(s11, t1i)); SIMDBase_STOR(&s[i1+ 5], SIMDBase_ADDi(s11, t1i)); + + // + + s20 = SIMDBase_LOAD(&s[i2+ 6]); s21 = SIMDBase_LOAD(&s[i2+ 7]); + a0 = SIMDBase_LOAD1(&tbl[p0+12]); a1 = SIMDBase_LOAD1(&tbl[p0+13]); +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_ADDi(SIMDBase_MULi(s20, a0), SIMDBase_MULi(s21, a1)); +#else + u = SIMDBase_FMADDi(s20, a0, SIMDBase_MULi(s21, a1)); +#endif + + s30 = SIMDBase_LOAD(&s[i3+ 6]); s31 = SIMDBase_LOAD(&s[i3+ 7]); + a2 = SIMDBase_LOAD1(&tbl[p0+14]); a3 = SIMDBase_LOAD1(&tbl[p0+15]); +#ifndef SIMDBase_FMADD_AVAILABLE + v = SIMDBase_ADDi(SIMDBase_MULi(s30, a2), SIMDBase_MULi(s31, a3)); +#else + v = SIMDBase_FMADDi(s30, a2, SIMDBase_MULi(s31, a3)); +#endif + + t0r = SIMDBase_ADDi(u, v); t1i = SIMDBase_SUBi(u, v); + +#ifndef SIMDBase_FMADD_AVAILABLE + u = SIMDBase_SUBi(SIMDBase_MULi(s31, a2), SIMDBase_MULi(s30, a3)); + v = SIMDBase_SUBi(SIMDBase_MULi(s21, a0), SIMDBase_MULi(s20, a1)); +#else + u = SIMDBase_FMSUBi(s30, a3, SIMDBase_MULi(s31, a2)); + v = SIMDBase_FMSUBi(s20, a1, SIMDBase_MULi(s21, a0)); +#endif + t0i = SIMDBase_ADDi(u, v); t1r = SIMDBase_SUBi(u, v); + + s00 = SIMDBase_LOAD(&s[i0+ 6]); s01 = SIMDBase_LOAD(&s[i0+ 7]); + s10 = SIMDBase_LOAD(&s[i1+ 6]); s11 = SIMDBase_LOAD(&s[i1+ 7]); + + SIMDBase_STOR(&s[i2+ 6], SIMDBase_SUBi(s00, t0r)); SIMDBase_STOR(&s[i0+ 6], SIMDBase_ADDi(s00, t0r)); + SIMDBase_STOR(&s[i2+ 7], SIMDBase_SUBi(s01, t0i)); SIMDBase_STOR(&s[i0+ 7], SIMDBase_ADDi(s01, t0i)); + SIMDBase_STOR(&s[i3+ 6], SIMDBase_SUBi(s10, t1r)); SIMDBase_STOR(&s[i1+ 6], SIMDBase_ADDi(s10, t1r)); + SIMDBase_STOR(&s[i3+ 7], SIMDBase_SUBi(s11, t1i)); SIMDBase_STOR(&s[i1+ 7], SIMDBase_ADDi(s11, t1i)); + + // + + i0 += 8; i1 += 8; i2 += 8; i3 += 8; + p0 += 16; + } +} +#endif + +static void r2ButForwardSub(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + + SIMDBase_REAL *tbl = p->ptTable[p->log2butlen]; + + int32_t i0 = p->offset1; + int32_t i2 = i0 + p->stride*2; + int32_t cp = 0, sp = p->butlen/4; + + do { + SIMDBase_VECT t0r, t0i, s0, s1, s2, s3, t0, t1; + + s0 = SIMDBase_LOAD(&s[i0+0]); s2 = SIMDBase_LOAD(&s[i0+1]); + s1 = SIMDBase_LOAD(&s[i2+0]); s3 = SIMDBase_LOAD(&s[i2+1]); + t0 = SIMDBase_LOAD1(&tbl[cp+0]); t1 = SIMDBase_LOAD1(&tbl[sp-0]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+0], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+0], SIMDBase_ADDi(SIMDBase_MULi(t0r, t0), SIMDBase_MULi(t0i, t1))); + SIMDBase_STOR(&s[i2+1], SIMDBase_SUBi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1))); + + s0 = SIMDBase_LOAD(&s[i0+2]); s2 = SIMDBase_LOAD(&s[i0+3]); + s1 = SIMDBase_LOAD(&s[i2+2]); s3 = SIMDBase_LOAD(&s[i2+3]); + t0 = SIMDBase_LOAD1(&tbl[cp+1]); t1 = SIMDBase_LOAD1(&tbl[sp-1]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+2], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+3], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+2], SIMDBase_ADDi(SIMDBase_MULi(t0r, t0), SIMDBase_MULi(t0i, t1))); + SIMDBase_STOR(&s[i2+3], SIMDBase_SUBi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1))); + + s0 = SIMDBase_LOAD(&s[i0+4]); s2 = SIMDBase_LOAD(&s[i0+5]); + s1 = SIMDBase_LOAD(&s[i2+4]); s3 = SIMDBase_LOAD(&s[i2+5]); + t0 = SIMDBase_LOAD1(&tbl[cp+2]); t1 = SIMDBase_LOAD1(&tbl[sp-2]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+4], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+5], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+4], SIMDBase_ADDi(SIMDBase_MULi(t0r, t0), SIMDBase_MULi(t0i, t1))); + SIMDBase_STOR(&s[i2+5], SIMDBase_SUBi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1))); + + s0 = SIMDBase_LOAD(&s[i0+6]); s2 = SIMDBase_LOAD(&s[i0+7]); + s1 = SIMDBase_LOAD(&s[i2+6]); s3 = SIMDBase_LOAD(&s[i2+7]); + t0 = SIMDBase_LOAD1(&tbl[cp+3]); t1 = SIMDBase_LOAD1(&tbl[sp-3]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+6], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+7], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+6], SIMDBase_ADDi(SIMDBase_MULi(t0r, t0), SIMDBase_MULi(t0i, t1))); + SIMDBase_STOR(&s[i2+7], SIMDBase_SUBi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1))); + + // + + i0 += 8; i2 += 8; cp += 4; sp -= 4; + } while(sp > 0); + + do { + SIMDBase_VECT t0r, t0i, s0, s1, s2, s3, t0, t1; + + s0 = SIMDBase_LOAD(&s[i0+0]); s2 = SIMDBase_LOAD(&s[i0+1]); + s1 = SIMDBase_LOAD(&s[i2+0]); s3 = SIMDBase_LOAD(&s[i2+1]); + t0 = SIMDBase_LOAD1(&tbl[cp-0]); t1 = SIMDBase_LOAD1(&tbl[sp+0]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+0], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+0], SIMDBase_SUBi(SIMDBase_MULi(t0i, t1), SIMDBase_MULi(t0r, t0))); + SIMDBase_STOR(&s[i2+1], SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1)))); + + s0 = SIMDBase_LOAD(&s[i0+2]); s2 = SIMDBase_LOAD(&s[i0+3]); + s1 = SIMDBase_LOAD(&s[i2+2]); s3 = SIMDBase_LOAD(&s[i2+3]); + t0 = SIMDBase_LOAD1(&tbl[cp-1]); t1 = SIMDBase_LOAD1(&tbl[sp+1]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+2], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+3], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+2], SIMDBase_SUBi(SIMDBase_MULi(t0i, t1), SIMDBase_MULi(t0r, t0))); + SIMDBase_STOR(&s[i2+3], SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1)))); + + s0 = SIMDBase_LOAD(&s[i0+4]); s2 = SIMDBase_LOAD(&s[i0+5]); + s1 = SIMDBase_LOAD(&s[i2+4]); s3 = SIMDBase_LOAD(&s[i2+5]); + t0 = SIMDBase_LOAD1(&tbl[cp-2]); t1 = SIMDBase_LOAD1(&tbl[sp+2]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+4], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+5], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+4], SIMDBase_SUBi(SIMDBase_MULi(t0i, t1), SIMDBase_MULi(t0r, t0))); + SIMDBase_STOR(&s[i2+5], SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1)))); + + s0 = SIMDBase_LOAD(&s[i0+6]); s2 = SIMDBase_LOAD(&s[i0+7]); + s1 = SIMDBase_LOAD(&s[i2+6]); s3 = SIMDBase_LOAD(&s[i2+7]); + t0 = SIMDBase_LOAD1(&tbl[cp-3]); t1 = SIMDBase_LOAD1(&tbl[sp+3]); + t0r = SIMDBase_SUBi(s0, s1); SIMDBase_STOR(&s[i0+6], SIMDBase_ADDi(s0, s1)); + t0i = SIMDBase_SUBi(s2, s3); SIMDBase_STOR(&s[i0+7], SIMDBase_ADDi(s2, s3)); + SIMDBase_STOR(&s[i2+6], SIMDBase_SUBi(SIMDBase_MULi(t0i, t1), SIMDBase_MULi(t0r, t0))); + SIMDBase_STOR(&s[i2+7], SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(t0i, t0), SIMDBase_MULi(t0r, t1)))); + + // + + i0 += 8; i2 += 8; cp -= 4; sp += 4; + } while(cp > 0); +} + +static void r2ButBackwardSub(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + + SIMDBase_REAL *tbl = p->ptTable[p->log2butlen]; + + int i0 = p->offset1; + int i2 = i0 + p->stride*2; + + int cp = 0, sp = p->butlen/4; + + do { + SIMDBase_VECT t0r, t0i, s0, s1, s2, s3, t0, t1; + + s0 = SIMDBase_LOAD(&s[i0+0]); s2 = SIMDBase_LOAD(&s[i0+1]); + s1 = SIMDBase_LOAD(&s[i2+0]); s3 = SIMDBase_LOAD(&s[i2+1]); + t0 = SIMDBase_LOAD1(&tbl[cp+0]); t1 = SIMDBase_LOAD1(&tbl[sp-0]); + t0r = SIMDBase_SUBi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1)); + t0i = SIMDBase_ADDi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+0], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+0], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+1], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s2, t0i)); + + s0 = SIMDBase_LOAD(&s[i0+2]); s2 = SIMDBase_LOAD(&s[i0+3]); + s1 = SIMDBase_LOAD(&s[i2+2]); s3 = SIMDBase_LOAD(&s[i2+3]); + t0 = SIMDBase_LOAD1(&tbl[cp+1]); t1 = SIMDBase_LOAD1(&tbl[sp-1]); + t0r = SIMDBase_SUBi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1)); + t0i = SIMDBase_ADDi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+2], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+2], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+3], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+3], SIMDBase_ADDi(s2, t0i)); + + s0 = SIMDBase_LOAD(&s[i0+4]); s2 = SIMDBase_LOAD(&s[i0+5]); + s1 = SIMDBase_LOAD(&s[i2+4]); s3 = SIMDBase_LOAD(&s[i2+5]); + t0 = SIMDBase_LOAD1(&tbl[cp+2]); t1 = SIMDBase_LOAD1(&tbl[sp-2]); + t0r = SIMDBase_SUBi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1)); + t0i = SIMDBase_ADDi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+4], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+4], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+5], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+5], SIMDBase_ADDi(s2, t0i)); + + s0 = SIMDBase_LOAD(&s[i0+6]); s2 = SIMDBase_LOAD(&s[i0+7]); + s1 = SIMDBase_LOAD(&s[i2+6]); s3 = SIMDBase_LOAD(&s[i2+7]); + t0 = SIMDBase_LOAD1(&tbl[cp+3]); t1 = SIMDBase_LOAD1(&tbl[sp-3]); + t0r = SIMDBase_SUBi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1)); + t0i = SIMDBase_ADDi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+6], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+6], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+7], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+7], SIMDBase_ADDi(s2, t0i)); + + i0 += 8; i2 += 8; cp += 4; sp -= 4; + } while(sp > 0); + + do { + SIMDBase_VECT t0r, t0i, s0, s1, s2, s3, t0, t1; + + s0 = SIMDBase_LOAD(&s[i0+0]); s2 = SIMDBase_LOAD(&s[i0+1]); + s1 = SIMDBase_LOAD(&s[i2+0]); s3 = SIMDBase_LOAD(&s[i2+1]); + t0 = SIMDBase_LOAD1(&tbl[cp-0]); t1 = SIMDBase_LOAD1(&tbl[sp+0]); + t0r = SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1))); + t0i = SIMDBase_SUBi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+0], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+0], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+1], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+1], SIMDBase_ADDi(s2, t0i)); + + s0 = SIMDBase_LOAD(&s[i0+2]); s2 = SIMDBase_LOAD(&s[i0+3]); + s1 = SIMDBase_LOAD(&s[i2+2]); s3 = SIMDBase_LOAD(&s[i2+3]); + t0 = SIMDBase_LOAD1(&tbl[cp-1]); t1 = SIMDBase_LOAD1(&tbl[sp+1]); + t0r = SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1))); + t0i = SIMDBase_SUBi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+2], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+2], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+3], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+3], SIMDBase_ADDi(s2, t0i)); + + s0 = SIMDBase_LOAD(&s[i0+4]); s2 = SIMDBase_LOAD(&s[i0+5]); + s1 = SIMDBase_LOAD(&s[i2+4]); s3 = SIMDBase_LOAD(&s[i2+5]); + t0 = SIMDBase_LOAD1(&tbl[cp-2]); t1 = SIMDBase_LOAD1(&tbl[sp+2]); + t0r = SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1))); + t0i = SIMDBase_SUBi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+4], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+4], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+5], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+5], SIMDBase_ADDi(s2, t0i)); + + s0 = SIMDBase_LOAD(&s[i0+6]); s2 = SIMDBase_LOAD(&s[i0+7]); + s1 = SIMDBase_LOAD(&s[i2+6]); s3 = SIMDBase_LOAD(&s[i2+7]); + t0 = SIMDBase_LOAD1(&tbl[cp-3]); t1 = SIMDBase_LOAD1(&tbl[sp+3]); + t0r = SIMDBase_NEGi(SIMDBase_ADDi(SIMDBase_MULi(s1, t0), SIMDBase_MULi(s3, t1))); + t0i = SIMDBase_SUBi(SIMDBase_MULi(s1, t1), SIMDBase_MULi(s3, t0)); + SIMDBase_STOR(&s[i2+6], SIMDBase_SUBi(s0, t0r)); SIMDBase_STOR(&s[i0+6], SIMDBase_ADDi(s0, t0r)); + SIMDBase_STOR(&s[i2+7], SIMDBase_SUBi(s2, t0i)); SIMDBase_STOR(&s[i0+7], SIMDBase_ADDi(s2, t0i)); + + i0 += 8; i2 += 8; cp -= 4; sp += 4; + } while(cp > 0); +} + +static void srButForward16(DFTUndiff *p) { + int32_t o = p->offset1; + + p->butlen = 16; p->log2butlen = 4; p->stride = p->butlen/2; + srButForwardSubUnrolled(p); + + p->offset1 = o + 16*6/4; + srButForward4(p); + + p->offset1 = o + 16*4/4; + srButForward4(p); + + p->offset1 = o; + srButForward8(p); +} + +static void srButBackward16(DFTUndiff *p) { + int32_t o = p->offset1; + + p->offset1 = o + 16*6/4; + srButBackward4(p); + + p->offset1 = o + 16*4/4; + srButBackward4(p); + + p->offset1 = o; + srButBackward8(p); + + p->butlen = 16; p->log2butlen = 4; p->stride = p->butlen/2; + srButBackwardSubUnrolled(p); +} + +static void srButForward32(DFTUndiff *p) { + int32_t o = p->offset1; + + p->butlen = 32; p->log2butlen = 5; p->stride = p->butlen/2; + srButForwardSubUnrolled(p); + + p->offset1 = o + 32*6/4; + srButForward8 (p); + + p->offset1 = o + 32*4/4; + srButForward8 (p); + + p->offset1 = o; + srButForward16(p); +} + +static void srButBackward32(DFTUndiff *p) { + int32_t o = p->offset1; + + p->offset1 = o + 32*6/4; + srButBackward8 (p); + + p->offset1 = o + 32*4/4; + srButBackward8 (p); + + p->offset1 = o; + srButBackward16(p); + + p->butlen = 32; p->log2butlen = 5; p->stride = p->butlen/2; + srButBackwardSubUnrolled(p); +} + +// + +#if 1 +static inline void bitReversalUnit(SIMDBase_VECT *p, SIMDBase_VECT *q) { + SIMDBase_VECT w, x, y, z; + + w = SIMDBase_LOAD(p); x = SIMDBase_LOAD(p+1); + y = SIMDBase_LOAD(q); z = SIMDBase_LOAD(q+1); + + SIMDBase_STOR(q, w); SIMDBase_STOR(q+1, x); + SIMDBase_STOR(p, y); SIMDBase_STOR(p+1, z); +} +#else +#define bitReversalUnit(p0, q0) { \ + SIMDBase_VECT *px = (p0), *qx = (q0); \ + SIMDBase_VECT wx, xx, yx, zx; \ + \ + wx = SIMDBase_LOAD(px); xx = SIMDBase_LOAD(px+1); \ + yx = SIMDBase_LOAD(qx); zx = SIMDBase_LOAD(qx+1); \ + \ + SIMDBase_STOR(qx, wx); SIMDBase_STOR(qx+1, xx); \ + SIMDBase_STOR(px, yx); SIMDBase_STOR(px+1, zx); \ +} +#endif + +static inline void bitReversal4s(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + SIMDBase_VECT *p = &s[o1*2], *q = &s[o2*2]; + int b1 = sc*2*1, b2 = b1*2; + p += b1; q += b2; + bitReversalUnit(p, q); +} + +static inline void bitReversal8s(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + SIMDBase_VECT *p = &s[o1*2], *q = &s[o2*2]; + int b1 = sc*2*1, b2 = b1*2, b4 = b2*2; + p += b1; q += b4; + bitReversalUnit(p, q); p += b2; q += b2; + bitReversalUnit(p, q); +} + +static inline void bitReversal8d(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + SIMDBase_VECT *p = &s[o1*2], *q = &s[o2*2]; + int32_t b1 = sc*2*1, b2 = b1*2, b4 = b2*2; + bitReversalUnit(p, q); p += b1; q += b4; + bitReversalUnit(p, q); p += b2; q += b2; + bitReversalUnit(p, q); p -= b1; q -= b4; + bitReversalUnit(p, q); p += b4; q += b1; + bitReversalUnit(p, q); p += b1; q += b4; + bitReversalUnit(p, q); p -= b2; q -= b2; + bitReversalUnit(p, q); p -= b1; q -= b4; + bitReversalUnit(p, q); +} + +static inline void bitReversal16s(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + SIMDBase_VECT *p = &s[o1*2], *q = &s[o2*2]; + int32_t b1 = sc*2*1, b2 = b1*2, b4 = b2*2, b8 = b4*2; + p += b1; q += b8; + bitReversalUnit(p, q); p += b2; q += b4; + bitReversalUnit(p, q); p -= b1; q -= b8; + bitReversalUnit(p, q); p += b1 + b4; q += b2 + b8; + bitReversalUnit(p, q); p -= b2; q -= b4; + bitReversalUnit(p, q); p += b2 + b4; q += b1 + b2; + bitReversalUnit(p, q); +} + +static inline void bitReversal16d(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + SIMDBase_VECT *p = &s[o1*2], *q = &s[o2*2]; + int32_t b1 = sc*2*1, b2 = b1*2, b4 = b2*2, b8 = b4*2; + bitReversalUnit(p, q); p += b1; q += b8; + bitReversalUnit(p, q); p += b2; q += b4; + bitReversalUnit(p, q); p -= b1; q -= b8; + bitReversalUnit(p, q); p += b4; q += b2; + bitReversalUnit(p, q); p += b1; q += b8; + bitReversalUnit(p, q); p -= b2; q -= b4; + bitReversalUnit(p, q); p -= b1; q -= b8; + bitReversalUnit(p, q); p += b8; q += b1; + bitReversalUnit(p, q); p += b1; q += b8; + bitReversalUnit(p, q); p += b2; q += b4; + bitReversalUnit(p, q); p -= b1; q -= b8; + bitReversalUnit(p, q); p -= b4; q -= b2; + bitReversalUnit(p, q); p += b1; q += b8; + bitReversalUnit(p, q); p -= b2; q -= b4; + bitReversalUnit(p, q); p -= b1; q -= b8; + bitReversalUnit(p, q); +} + +static inline void bitReversal32s(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + SIMDBase_VECT *p = &s[o1*2], *q = &s[o2*2]; + int32_t b1 = sc*2*1, b2 = b1*2, b4 = b2*2, b8 = b4*2, b16 = b8*2; + p += b1; q += b16; + bitReversalUnit(p, q); p += b2; q += b8; + bitReversalUnit(p, q); p -= b1; q -= b16; + bitReversalUnit(p, q); p += b4; q += b4; + bitReversalUnit(p, q); p += b1; q += b16; + bitReversalUnit(p, q); p -= b2; q -= b8; + bitReversalUnit(p, q); p += b8; q += b2; + bitReversalUnit(p, q); p += b2; q += b8; + bitReversalUnit(p, q); p -= b4; q -= b4; + bitReversalUnit(p, q); p -= b2; q -= b8; + bitReversalUnit(p, q); p += b16 - b2; q += b1 + b2 + b8; + bitReversalUnit(p, q); p -= b4; q -= b4; + bitReversalUnit(p, q); +} + +static void bitReversal32d(SIMDBase_VECT *s, int32_t sc, int32_t o1, int32_t o2) { + const int32_t k = 32; + + bitReversal8d(s,2*sc, sc*(k/2 )+o1, sc* 1 +o2); + bitReversal8d(s,2*sc, sc* 0 +o1, sc* 0 +o2); + bitReversal8d(s,2*sc, sc* 1 +o1, sc*(k/2 )+o2); + bitReversal8d(s,2*sc, sc*(k/2+1)+o1, sc*(k/2+1)+o2); +} + +static void bitReversalRecursive(SIMDBase_VECT *s, int32_t n, int32_t sc, int32_t o1, int32_t o2) { + if (n >= 64) { + if (o1 != o2) bitReversalRecursive(s, n/4, 2*sc, sc*(n/2)+o1, sc*1+o2); + + bitReversalRecursive(s, n/4, 2*sc, sc* 0 +o1, sc* 0 +o2); + bitReversalRecursive(s, n/4, 2*sc, sc* 1 +o1, sc*(n/2 )+o2); + bitReversalRecursive(s, n/4, 2*sc, sc*(n/2+1)+o1, sc*(n/2+1)+o2); + } else { + if (o1 == o2) { + switch(n) { + case 4: bitReversal4s (s,sc,o1,o2); return; + case 8: bitReversal8s (s,sc,o1,o2); return; + case 16: bitReversal16s(s,sc,o1,o2); return; + case 32: bitReversal32s(s,sc,o1,o2); return; + } + } else { + switch(n) { + case 8: bitReversal8d (s,sc,o1,o2); return; + case 16: bitReversal16d(s,sc,o1,o2); return; + case 32: bitReversal32d(s,sc,o1,o2); return; + } + } + } +} + +// + +static int bitR(int a, int logN) { + int ret = 0; + int i,j,k; + for(i=0,j=1,k=1<<(logN-1);i<logN;i++,j=j<<1,k=k>>1) { + if ((a & j) != 0) ret |= k; + } + return ret; +} + +static void bitReversalCobraInplace(DFTUndiff *p) { + SIMDBase_VECT *s = p->s; + int cobraQ = p->cobraQ; + SIMDBase_VECT *cobraT = p->cobraT; + int *cobraR = p->cobraR; + int logN = p->log2len; + + int b; + + for(b=0;b<(1 << (logN-2*cobraQ));b++) { + int a,c; + int b2 = bitR(b, logN-2*cobraQ); + + if (b2 < b) continue; + + if (b2 == b) { + for(a=0;a<(1 << cobraQ);a++) { + int abc = ((a << (logN-2*cobraQ)) | b) << (cobraQ + 1); + + int a2c = (cobraR[a] << cobraQ) << 1, a2cm = a2c+(1 << cobraQ)*2; + + while(a2c < a2cm) { + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + } + } + + for(c=0;c<(1 << cobraQ);c++) { + int c2 = cobraR[c]; + int c2b2a2 = ((c2 << (logN-2*cobraQ)) | b2) << (cobraQ+1); + + int a2c = c << 1; + int a2ci = 1 << (cobraQ+1); + int c2b2a2m = c2b2a2 + (1 << cobraQ)*2; + + while(c2b2a2 < c2b2a2m) { + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); a2c += a2ci; + } + } + } else { + for(a=0;a<(1 << cobraQ);a++) { + int a2c = (cobraR[a] << cobraQ) << 1, a2cm = a2c+(1 << cobraQ)*2; + int abc = ((a << (logN-2*cobraQ)) | b) << (cobraQ + 1); + + while(a2c < a2cm) { + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); SIMDBase_STOR(&cobraT[a2c++], SIMDBase_LOAD(&s[abc++])); + } + } + + for(c=0;c<(1 << cobraQ);c++) { + int c2 = cobraR[c]; + int c2b2a2 = ((c2 << (logN-2*cobraQ)) | b2) << (cobraQ+1); + + int a2c = c << 1; + int a2ci = 1 << (cobraQ+1); + int c2b2a2m = c2b2a2 + (1 << cobraQ)*2; + + while(c2b2a2 < c2b2a2m) { + SIMDBase_VECT t0, t1, t2, t3, t4, t5, t6, t7; + + t0 = SIMDBase_LOAD(&s[c2b2a2+0]); t1 = SIMDBase_LOAD(&s[c2b2a2+1]); + t2 = SIMDBase_LOAD(&s[c2b2a2+2]); t3 = SIMDBase_LOAD(&s[c2b2a2+3]); + t4 = SIMDBase_LOAD(&s[c2b2a2+4]); t5 = SIMDBase_LOAD(&s[c2b2a2+5]); + t6 = SIMDBase_LOAD(&s[c2b2a2+6]); t7 = SIMDBase_LOAD(&s[c2b2a2+7]); + + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t0); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t1); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t2); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t3); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t4); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t5); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t6); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t7); a2c += a2ci; + + t0 = SIMDBase_LOAD(&s[c2b2a2+0]); t1 = SIMDBase_LOAD(&s[c2b2a2+1]); + t2 = SIMDBase_LOAD(&s[c2b2a2+2]); t3 = SIMDBase_LOAD(&s[c2b2a2+3]); + t4 = SIMDBase_LOAD(&s[c2b2a2+4]); t5 = SIMDBase_LOAD(&s[c2b2a2+5]); + t6 = SIMDBase_LOAD(&s[c2b2a2+6]); t7 = SIMDBase_LOAD(&s[c2b2a2+7]); + + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t0); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t1); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t2); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t3); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t4); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t5); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t6); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t7); a2c += a2ci; + + t0 = SIMDBase_LOAD(&s[c2b2a2+0]); t1 = SIMDBase_LOAD(&s[c2b2a2+1]); + t2 = SIMDBase_LOAD(&s[c2b2a2+2]); t3 = SIMDBase_LOAD(&s[c2b2a2+3]); + t4 = SIMDBase_LOAD(&s[c2b2a2+4]); t5 = SIMDBase_LOAD(&s[c2b2a2+5]); + t6 = SIMDBase_LOAD(&s[c2b2a2+6]); t7 = SIMDBase_LOAD(&s[c2b2a2+7]); + + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t0); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t1); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t2); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t3); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t4); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t5); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t6); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t7); a2c += a2ci; + + t0 = SIMDBase_LOAD(&s[c2b2a2+0]); t1 = SIMDBase_LOAD(&s[c2b2a2+1]); + t2 = SIMDBase_LOAD(&s[c2b2a2+2]); t3 = SIMDBase_LOAD(&s[c2b2a2+3]); + t4 = SIMDBase_LOAD(&s[c2b2a2+4]); t5 = SIMDBase_LOAD(&s[c2b2a2+5]); + t6 = SIMDBase_LOAD(&s[c2b2a2+6]); t7 = SIMDBase_LOAD(&s[c2b2a2+7]); + + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t0); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t1); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t2); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t3); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t4); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t5); a2c += a2ci; + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c ])); SIMDBase_STOR(&cobraT[a2c ], t6); + SIMDBase_STOR(&s[c2b2a2++], SIMDBase_LOAD(&cobraT[a2c+1])); SIMDBase_STOR(&cobraT[a2c+1], t7); a2c += a2ci; + } + } + + for(a=0;a<(1 << cobraQ);a++) { + int a2c = (cobraR[a] << cobraQ) << 1, a2cm = a2c+(1 << cobraQ)*2; + int abc = ((a << (logN-2*cobraQ)) | b) << (cobraQ + 1); + + while(a2c < a2cm) { + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); SIMDBase_STOR(&s[abc++], SIMDBase_LOAD(&cobraT[a2c++])); + } + } + } + } +} + +// + +static void srForwardMain2(DFTUndiff *p) { + int32_t o = p->offset1; + int32_t butlen = p->butlen; + int32_t log2butlen = p->log2butlen; + + if (butlen >= p->radix2thres) { + p->stride = p->butlen/2; + r2ButForwardSub(p); + + p->offset1 = o + butlen*4/4; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srForwardMain2(p); + + p->offset1 = o; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srForwardMain2(p); + + return; + } + + if (butlen >= 256) { + p->stride = p->butlen/2; + srButForwardSubUnrolled(p); + + p->offset1 = o + butlen*6/4; + p->butlen = butlen/4; + p->log2butlen = log2butlen-2; + srForwardMain2(p); + + p->offset1 = o + butlen*4/4; + p->butlen = butlen/4; + p->log2butlen = log2butlen-2; + srForwardMain2(p); + + p->offset1 = o; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srForwardMain2(p); + + return; + } + + if (butlen == 128) { + p->stride = p->butlen/2; + srButForwardSubUnrolled(p); + + p->offset1 = o + butlen*6/4; + srButForward32(p); + + p->offset1 = o + butlen*4/4; + srButForward32(p); + + p->offset1 = o; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srForwardMain2 (p); + + return; + } + + // butlen == 64 + + p->stride = p->butlen/2; + srButForwardSubUnrolled(p); + + p->offset1 = o + butlen*6/4; + srButForward16(p); + + p->offset1 = o + butlen*4/4; + srButForward16(p); + + p->offset1 = o; + srButForward32(p); +} + +static void srBackwardMain2(DFTUndiff *p) { + int32_t o = p->offset1; + int32_t butlen = p->butlen; + int32_t log2butlen = p->log2butlen; + + if (butlen >= p->radix2thres) { + p->offset1 = o + butlen*4/4; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srBackwardMain2(p); + + p->offset1 = o; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srBackwardMain2(p); + + p->butlen = butlen; + p->stride = p->butlen/2; + p->log2butlen = log2butlen; + r2ButBackwardSub(p); + + return; + } + + if (butlen >= 256) { + p->offset1 = o + butlen*6/4; + p->butlen = butlen/4; + p->log2butlen = log2butlen-2; + srBackwardMain2(p); + + p->offset1 = o + butlen*4/4; + p->butlen = butlen/4; + p->log2butlen = log2butlen-2; + srBackwardMain2(p); + + p->offset1 = o; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srBackwardMain2(p); + + p->butlen = butlen; + p->stride = p->butlen/2; + p->log2butlen = log2butlen; + srButBackwardSubUnrolled(p); + + return; + } + + if (butlen == 128) { + p->offset1 = o + butlen*6/4; + srButBackward32(p); + + p->offset1 = o + butlen*4/4; + srButBackward32(p); + + p->offset1 = o; + p->butlen = butlen/2; + p->log2butlen = log2butlen-1; + srBackwardMain2 (p); + + p->butlen = butlen; + p->stride = p->butlen/2; + p->log2butlen = log2butlen; + srButBackwardSubUnrolled(p); + + return; + } + + // butlen == 64 + + p->offset1 = o + butlen*6/4; + srButBackward16(p); + + p->offset1 = o + butlen*4/4; + srButBackward16(p); + + p->offset1 = o; + srButBackward32(p); + + p->butlen = butlen; + p->stride = p->butlen/2; + p->log2butlen = log2butlen; + srButBackwardSubUnrolled(p); +} + +static void srForwardMain(DFTUndiff *p) { + if (p->length >= 64) { + p->butlen = p->length; + p->log2butlen = p->log2len; + p->offset1 = p->offset2 = 0; + + srForwardMain2(p); + } else { + switch(p->length) { + case 32: + srButForward32(p); + break; + case 16: + srButForward16(p); + break; + case 8: + srButForward8(p); + break; + case 4: + srButForward4(p); + break; + case 2: + srBut2(p); + break; + } + } +} + +static void srBackwardMain(DFTUndiff *p) { + if (p->length >= 64) { + p->butlen = p->length; + p->log2butlen = p->log2len; + p->offset1 = p->offset2 = 0; + + srBackwardMain2(p); + } else { + switch(p->length) { + case 32: + srButBackward32(p); + break; + case 16: + srButBackward16(p); + break; + case 8: + srButBackward8(p); + break; + case 4: + srButBackward4(p); + break; + case 2: + srBut2(p); + break; + } + } +} + +static void realSub0(DFTUndiff *p, SIMDBase_VECT *s, int32_t ts) { + SIMDBase_VECT tr, ti, ur, ui, mr, mi; + int32_t n = p->length*2; + int32_t k; + + for(k=1;k<n/4;k++) { + SIMDBase_VECT s00 = SIMDBase_LOAD(&s[k*2+0]), s01 = SIMDBase_LOAD(&s[k*2+1]); + SIMDBase_VECT s10 = SIMDBase_LOAD(&s[(n/2-k)*2+0]), s11 = SIMDBase_LOAD(&s[(n/2-k)*2+1]); + + tr = SIMDBase_SUBi(s00, s10); ti = SIMDBase_ADDi(s01, s11); + ur = SIMDBase_LOAD1(&(p->rtTable[ts][k*2+0])); + ui = SIMDBase_LOAD1(&(p->rtTable[ts][k*2+1])); + mr = SIMDBase_SUBi(SIMDBase_MULi(tr, ur), SIMDBase_MULi(ti, ui)); + mi = SIMDBase_ADDi(SIMDBase_MULi(tr, ui), SIMDBase_MULi(ti, ur)); + SIMDBase_STOR(&s[k*2+0], SIMDBase_SUBi(s00, mr)); + SIMDBase_STOR(&s[k*2+1], SIMDBase_SUBi(s01, mi)); + SIMDBase_STOR(&s[(n/2-k)*2+0], SIMDBase_ADDi(s10, mr)); + SIMDBase_STOR(&s[(n/2-k)*2+1], SIMDBase_SUBi(s11, mi)); + } + + tr = SIMDBase_LOAD(&s[0]); ti = SIMDBase_LOAD(&s[1]); + SIMDBase_STOR(&s[0], SIMDBase_ADDi(tr, ti)); + SIMDBase_STOR(&s[1], SIMDBase_SUBi(tr, ti)); +} + +static void realSub1(DFTUndiff *p, SIMDBase_VECT *s, int32_t ts) { + SIMDBase_VECT tr, ti, ur, ui, mr, mi; + int32_t n = p->length*2; + int32_t k; + + tr = SIMDBase_LOAD(&s[0]); ti = SIMDBase_LOAD(&s[1]); + SIMDBase_STOR(&s[0], SIMDBase_MULi(SIMDBase_ADDi(tr, ti), SIMDBase_SET1(0.5))); + SIMDBase_STOR(&s[1], SIMDBase_MULi(SIMDBase_SUBi(tr, ti), SIMDBase_SET1(0.5))); + + for(k=1;k<n/4;k++) { + SIMDBase_VECT s00 = SIMDBase_LOAD(&s[k*2+0]), s01 = SIMDBase_LOAD(&s[k*2+1]); + SIMDBase_VECT s10 = SIMDBase_LOAD(&s[(n/2-k)*2+0]), s11 = SIMDBase_LOAD(&s[(n/2-k)*2+1]); + + tr = SIMDBase_SUBi(s00, s10); ti = SIMDBase_ADDi(s01, s11); + ur = SIMDBase_LOAD1(&(p->rtTable[ts][k*2+0])); + ui = SIMDBase_LOAD1(&(p->rtTable[ts][k*2+1])); + mr = SIMDBase_SUBi(SIMDBase_MULi(tr, ur), SIMDBase_MULi(ti, ui)); + mi = SIMDBase_ADDi(SIMDBase_MULi(tr, ui), SIMDBase_MULi(ti, ur)); + tr = SIMDBase_SUBi(s00, mr); ti = SIMDBase_SUBi(mi, s01); + SIMDBase_STOR(&s[k*2+0], SIMDBase_ADDi(mr, s10)); + SIMDBase_STOR(&s[k*2+1], SIMDBase_SUBi(mi, s11)); + SIMDBase_STOR(&s[(n/2-k)*2+0], tr); + SIMDBase_STOR(&s[(n/2-k)*2+1], ti); + } +} + +void DFTUndiff_EXECUTE(void *p2, void *s2, int32_t dir) { + DFTUndiff *p = (DFTUndiff *)p2; + SIMDBase_VECT *s = (SIMDBase_VECT *)s2; + + if (p->magic != MAGIC_DFT) abort(); + + p->s = s; + + if (dir == -1) { + if ((p->flags & DFT_FLAG_ALT_REAL) != 0) { + realSub1(p, s, 0); + } + + srForwardMain(p); + + if ((p->flags & DFT_FLAG_NO_BITREVERSAL) == 0) { + if (p->useCobra) { + bitReversalCobraInplace(p); + } else { + bitReversalRecursive(p->s, p->length, 1, 0, 0); + } + } + + if ((p->flags & DFT_FLAG_REAL) != 0) { + realSub0(p, s, 0); + s[p->length+1] = SIMDBase_NEGi(s[p->length+1]); + } + } else { + if ((p->flags & DFT_FLAG_REAL) != 0) { + s[p->length+1] = SIMDBase_NEGi(s[p->length+1]); + realSub1(p, s, 1); + } + + if ((p->flags & DFT_FLAG_NO_BITREVERSAL) == 0) { + if (p->useCobra) { + bitReversalCobraInplace(p); + } else { + bitReversalRecursive(p->s, p->length, 1, 0, 0); + } + } + + srBackwardMain(p); + + if ((p->flags & DFT_FLAG_ALT_REAL) != 0) { + realSub0(p, s, 1); + } + } +} + +void DFTUndiff_DESTROYPLAN(void *p2) { + DFTUndiff *plan = (DFTUndiff *)p2; + if (plan->magic != MAGIC_DFT) abort(); + + free(*(plan->ptTable)); + free(plan->ptTable); + free(plan->cobraT); + free(plan->cobraR); + //free(plan->t); + if (plan->rtTable != NULL) { + free(plan->rtTable[0]); + free(plan->rtTable[1]); + free(plan->rtTable); + } + + plan->magic = 0; + free(plan); +} + +DFTUndiff *DFTUndiff_MAKEPLANSUB(uint64_t n, int32_t radix2thres, int32_t useCobra, uint64_t flags) { + int32_t i, j, k; + + uint32_t linesize = SIMDBase_sizeOfCachelineInByte(); + uint32_t cachesize = SIMDBase_sizeOfDataCacheInByte(); + + // + + if ((flags & DFT_FLAG_REAL) != 0 || (flags & DFT_FLAG_ALT_REAL) != 0) n /= 2; + + DFTUndiff *d = calloc(1, sizeof(DFTUndiff)); + + d->magic = MAGIC_DFT; + d->mode = SIMDBase_MODE; + d->flags = flags; + + d->radix2thres = radix2thres; + d->useCobra = useCobra; + + d->length = (uint32_t) n; + d->log2len = DFT_ilog2((uint32_t) n); + + // + + SIMDBase_REAL *trigTable = SIMDBase_alignedMalloc(sizeof(SIMDBase_REAL)*n*2); + d->ptTable = malloc(sizeof(SIMDBase_REAL *) * (d->log2len+1)); + + SIMDBase_REAL *p = trigTable, **pp = d->ptTable; + + for(j=0;j<(int32_t)d->log2len+1;j++) { + *pp++ = p; + + if ((1 << j) >= d->radix2thres) { + for(i=0;i<(1 << j)/4+1;i++) { + *p++ = (SIMDBase_REAL)COS(-2*M_PIl*i/(1 << j)); + } + const int32_t step = linesize / sizeof(SIMDBase_REAL); + p += (step - (p - trigTable) % step) % step; + } else { + for(i=0;i<(1 << j)/4;i++) { + *p++ = (SIMDBase_REAL)COS(-2*M_PIl*i/(1 << j)); + *p++ = (SIMDBase_REAL)SIN(-2*M_PIl*i/(1 << j)); + *p++ = (SIMDBase_REAL)COS(-6*M_PIl*i/(1 << j)); + *p++ = (SIMDBase_REAL)SIN(-6*M_PIl*i/(1 << j)); + } + } + } + + // + + int32_t cobraQ; + + cobraQ = linesize / (sizeof(SIMDBase_VECT) * 2); + + for(;;) { + if (1 << (cobraQ*2) > + (cachesize / (sizeof(SIMDBase_VECT) * 2)/2)) + break; + + cobraQ++; + } + cobraQ--; + + d->cobraQ = cobraQ; + + if (cobraQ >= 4 && d->log2len >= 2*cobraQ) { + SIMDBase_VECT *cobraT; + int32_t *cobraR; + + if (d->log2len <= 2*cobraQ) cobraQ = d->log2len / 2; + + cobraT = SIMDBase_alignedMalloc(sizeof(SIMDBase_VECT)*2 * (1 << (cobraQ*2))); + cobraR = (int32_t *)SIMDBase_alignedMalloc(sizeof(int32_t) * (1 << cobraQ)); + + for(i=0;i<(1 << cobraQ);i++) cobraR[i] = bitR(i, cobraQ); + + d->cobraT = cobraT; d->cobraR = cobraR; + } else { + d->useCobra = 0; + } + + // + + if ((d->flags & DFT_FLAG_REAL) != 0 || (d->flags & DFT_FLAG_ALT_REAL) != 0) { + int32_t m = n*2; + + d->rtTable = malloc(sizeof(SIMDBase_REAL *)*2); + d->rtTable[0] = SIMDBase_alignedMalloc(sizeof(SIMDBase_REAL)*m/2); + d->rtTable[1] = SIMDBase_alignedMalloc(sizeof(SIMDBase_REAL)*m/2); + + for(k=0;k<m/4;k++) { + d->rtTable[0][k*2+0] = 0.5-0.5*SIN(-2*M_PIl*k/m); + d->rtTable[0][k*2+1] = 0.5*COS(-2*M_PIl*k/m); + d->rtTable[1][k*2+0] = 0.5-0.5*SIN( 2*M_PIl*k/m); + d->rtTable[1][k*2+1] = 0.5*COS( 2*M_PIl*k/m); + } + } + + // + + return (void *)d; +} + +void *DFTUndiff_MAKEPLAN(uint64_t n, uint64_t flags) { + if (flags & DFT_FLAG_VERBOSE) { + printf("\n--------------------------------\n"); + printf("Making plan, mode = %s, dft length = %d\n", SIMDBase_NAME, (int)n); + printf("Processor : %s\n", SIMDBase_getProcessorNameString()); + printf("Cache size (L2 + L3) : %d kbytes / thread\n", SIMDBase_sizeOfDataCacheInByte() / 1024); + printf("Cache Line Size : %d bytes\n", SIMDBase_sizeOfCachelineInByte()); + } + + if (n <= 256 || (flags & 3) == 0) { + return DFTUndiff_MAKEPLANSUB(n, n*2, (flags & DFT_FLAG_FORCE_COBRA) != 0, flags); + } + + SIMDBase_REAL *s1 = SIMDBase_alignedMalloc(sizeof(SIMDBase_VECT)*n*2); + + int32_t i, j, ts, tsbest, useCobra = 0; + double tick, tickmin; + + if (flags & DFT_FLAG_VERBOSE) { + printf("\nWarming up before calibration ..."); + fflush(stdout); + } + + // warming up + tick = DFT_timeofday(); + while(DFT_timeofday() - tick < 0.5) + ; + + if (flags & DFT_FLAG_VERBOSE) { + printf(" done\n"); + } + + int32_t ntimes = 20000000.0 / n / DFT_ilog2(n); + if (ntimes == 0) ntimes = 1; + + if (flags & DFT_FLAG_VERBOSE) { + printf("nTimes = %d\n", ntimes); + } + + // + + DFTUndiff *plan = DFTUndiff_MAKEPLANSUB(n, n*2, 0, flags); + + for(i=0;i<n*2*SIMDBase_VECTLEN;i++) { + s1[i] = 0; + } + + plan->s = (SIMDBase_VECT *)s1; + + if (plan->cobraT != NULL) { + double tcobra = 0, trecur = 0; + + if (flags & DFT_FLAG_VERBOSE) { + printf("\nChecking which bit-reversal method is faster\n"); + } + + // + + bitReversalCobraInplace(plan); + + tick = DFT_timeofday(); + + for(j=0;j<ntimes*4;j++) { + bitReversalCobraInplace(plan); + } + + tcobra += DFT_timeofday() - tick; + + // + + bitReversalRecursive(plan->s, plan->length, 1, 0, 0); + + tick = DFT_timeofday(); + + for(j=0;j<ntimes*4;j++) { + bitReversalRecursive(plan->s, plan->length, 1, 0, 0); + } + + trecur += DFT_timeofday() - tick; + + // + + bitReversalCobraInplace(plan); + + tick = DFT_timeofday(); + + for(j=0;j<ntimes*4;j++) { + bitReversalCobraInplace(plan); + } + + tcobra += DFT_timeofday() - tick; + + // + + bitReversalRecursive(plan->s, plan->length, 1, 0, 0); + + tick = DFT_timeofday(); + + for(j=0;j<ntimes*4;j++) { + bitReversalRecursive(plan->s, plan->length, 1, 0, 0); + } + + trecur += DFT_timeofday() - tick; + + // + + useCobra = tcobra < trecur; + + if ((flags & DFT_FLAG_FORCE_RECURSIVE) != 0) useCobra = 0; + if ((flags & DFT_FLAG_FORCE_COBRA) != 0) useCobra = 1; + + if (flags & DFT_FLAG_VERBOSE) { + printf("cobra : %g\n", tcobra); + printf("recur : %g\n", trecur); + if (useCobra) { + printf("will use Cobra\n"); + } else { + printf("will use the recursive reverser\n"); + } + } + } + + DFTUndiff_DESTROYPLAN(plan); + + // + + if (flags & DFT_FLAG_VERBOSE) { + printf("\nDetermining radix 2 threshold\n"); + } + + plan = DFTUndiff_MAKEPLANSUB(n, n*2, useCobra, flags); + + for(j=0;j<ntimes;j++) { + DFTUndiff_EXECUTE(plan, s1, -1); + DFTUndiff_EXECUTE(plan, s1, 1); + } + + DFTUndiff_DESTROYPLAN(plan); + + tsbest = -1; + tickmin = 0; + + for(ts = 1024;ts <= n*2;ts *= 2) { + plan = DFTUndiff_MAKEPLANSUB(n, ts, useCobra, flags); + + tick = DFT_timeofday(); + + for(j=0;j<ntimes;j++) { + DFTUndiff_EXECUTE(plan, s1, -1); + DFTUndiff_EXECUTE(plan, s1, 1); + } + + tick = DFT_timeofday() - tick; + + DFTUndiff_DESTROYPLAN(plan); + + if (tickmin == 0) tickmin = tick; + + if (flags & DFT_FLAG_VERBOSE) { + printf("%d : %g\n",ts, (double)tick); + } + + if (tick < tickmin) { + tickmin = tick; + tsbest = ts; + } + } + + if (tsbest == -1) tsbest = n*2;; + + if (flags & DFT_FLAG_VERBOSE) { + //printf("forcing tsbest = 1024\n"); + //tsbest = 1024; + printf("radix 2 threshold : %d\n\n", tsbest); + + double t = tickmin / ntimes / 2; + double nf = 5 * n * log(n) / log(2) / (t * 1000000); + + printf("nFlops = %d x %g\n", SIMDBase_VECTLEN, nf); + } + + plan = DFTUndiff_MAKEPLANSUB(n, tsbest, useCobra, flags); + + if (flags & DFT_FLAG_VERBOSE) { + printf("\nDone making plan\n--------------------------------\n"); + } + + return plan; +} diff --git a/plugins/supereq/nsfft-1.00/dft/DFTUndiff.h b/plugins/supereq/nsfft-1.00/dft/DFTUndiff.h new file mode 100644 index 00000000..d26b0d9b --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/DFTUndiff.h @@ -0,0 +1,114 @@ +#ifndef __DFTIMPL_H__ +#define __DFTIMPL_H__ + +#include "SIMDBaseUndiff.h" + +#define MAGIC_DFT 0x18839f6d82bb02b6ULL + +typedef struct { + uint64_t magic; + + SIMDBase_VECT *s; + uint32_t offset1, offset2; + uint32_t butlen, log2butlen; + uint32_t stride; + + SIMDBase_REAL **ptTable; + uint32_t length, log2len; + + int32_t radix2thres, flagTrans, useCobra; + + int32_t cobraQ; + SIMDBase_VECT *cobraT; + int32_t *cobraR; + + SIMDBase_REAL **rtTable; + + uint64_t flags; + int32_t mode; +} DFTUndiff; + +#if defined(ENABLE_PUREC_FLOAT) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_purec_float +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_purec_float +#define DFTUndiff_EXECUTE execute_purec_float +#define DFTUndiff_MAKEPLAN makePlan_purec_float +#define DFTUndiff_MAKEPLANSUB makePlanSub_purec_float +#define DFTUndiff_DESTROYPLAN destroyPlan_purec_float + +#elif defined(ENABLE_PUREC_DOUBLE) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_purec_double +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_purec_double +#define DFTUndiff_EXECUTE execute_purec_double +#define DFTUndiff_MAKEPLAN makePlan_purec_double +#define DFTUndiff_MAKEPLANSUB makePlanSub_purec_double +#define DFTUndiff_DESTROYPLAN destroyPlan_purec_double + +#elif defined(ENABLE_PUREC_LONGDOUBLE) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_purec_longdouble +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_purec_longdouble +#define DFTUndiff_EXECUTE execute_purec_longdouble +#define DFTUndiff_MAKEPLAN makePlan_purec_longdouble +#define DFTUndiff_MAKEPLANSUB makePlanSub_purec_longdouble +#define DFTUndiff_DESTROYPLAN destroyPlan_purec_longdouble + +#elif defined(ENABLE_SSE_FLOAT) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_sse_float +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_sse_float +#define DFTUndiff_EXECUTE execute_sse_float +#define DFTUndiff_MAKEPLAN makePlan_sse_float +#define DFTUndiff_MAKEPLANSUB makePlanSub_sse_float +#define DFTUndiff_DESTROYPLAN destroyPlan_sse_float + +#elif defined(ENABLE_SSE2_DOUBLE) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_sse2_double +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_sse2_double +#define DFTUndiff_EXECUTE execute_sse2_double +#define DFTUndiff_MAKEPLAN makePlan_sse2_double +#define DFTUndiff_MAKEPLANSUB makePlanSub_sse2_double +#define DFTUndiff_DESTROYPLAN destroyPlan_sse2_double + +#elif defined(ENABLE_NEON_FLOAT) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_neon_float +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_neon_float +#define DFTUndiff_EXECUTE execute_neon_float +#define DFTUndiff_MAKEPLAN makePlan_neon_float +#define DFTUndiff_MAKEPLANSUB makePlanSub_neon_float +#define DFTUndiff_DESTROYPLAN destroyPlan_neon_float + +#elif defined(ENABLE_AVX_FLOAT) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_avx_float +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_avx_float +#define DFTUndiff_EXECUTE execute_avx_float +#define DFTUndiff_MAKEPLAN makePlan_avx_float +#define DFTUndiff_MAKEPLANSUB makePlanSub_avx_float +#define DFTUndiff_DESTROYPLAN destroyPlan_avx_float + +#elif defined(ENABLE_AVX_DOUBLE) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_avx_double +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_avx_double +#define DFTUndiff_EXECUTE execute_avx_double +#define DFTUndiff_MAKEPLAN makePlan_avx_double +#define DFTUndiff_MAKEPLANSUB makePlanSub_avx_double +#define DFTUndiff_DESTROYPLAN destroyPlan_avx_double + +#elif defined(ENABLE_ALTIVEC_FLOAT) //////////////////////////////////////////// + +#define DFTUndiff_GETMODEPARAMINT getModeParamInt_altivec_float +#define DFTUndiff_GETMODEPARAMSTRING getModeParamString_altivec_float +#define DFTUndiff_EXECUTE execute_altivec_float +#define DFTUndiff_MAKEPLAN makePlan_altivec_float +#define DFTUndiff_MAKEPLANSUB makePlanSub_altivec_float +#define DFTUndiff_DESTROYPLAN destroyPlan_altivec_float + +#endif //////////////////////////////////////////////////////////////////// + +#endif diff --git a/plugins/supereq/nsfft-1.00/dft/Makefile b/plugins/supereq/nsfft-1.00/dft/Makefile new file mode 120000 index 00000000..fc484116 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/Makefile @@ -0,0 +1 @@ +Makefile.x86
\ No newline at end of file diff --git a/plugins/supereq/nsfft-1.00/dft/Makefile.altivec b/plugins/supereq/nsfft-1.00/dft/Makefile.altivec new file mode 100644 index 00000000..fe7fc993 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/Makefile.altivec @@ -0,0 +1,26 @@ +CC=gcc +BASEOPT=-Wall -I ../simd -maltivec -mabi=altivec +OPT=$(BASEOPT) -O3 + +all : libDFT.a + +DFTpurecfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT DFTUndiff.c -c -o DFTpurecfloat.o + +DFTpurecdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE DFTUndiff.c -c -o DFTpurecdouble.o + +DFTpureclongdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE DFTUndiff.c -c -o DFTpureclongdouble.o + +DFTaltivecfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_ALTIVEC_FLOAT DFTUndiff.c -c -o DFTaltivecfloat.o + +DFT.o : DFT.c DFT.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_ALTIVEC_FLOAT DFT.c -c -o DFT.o + +libDFT.a : DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTaltivecfloat.o DFT.o + rm -f libDFT.a; ar -cvq libDFT.a DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTaltivecfloat.o DFT.o + +clean : + rm -f *~ *.o *.s *.a diff --git a/plugins/supereq/nsfft-1.00/dft/Makefile.neon b/plugins/supereq/nsfft-1.00/dft/Makefile.neon new file mode 100644 index 00000000..111a04ae --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/Makefile.neon @@ -0,0 +1,26 @@ +CC=gcc +BASEOPT=-Wall -I ../simd -mfloat-abi=softfp +OPT=$(BASEOPT) -O3 + +all : libDFT.a + +DFTpurecfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT DFTUndiff.c -c -o DFTpurecfloat.o + +DFTpurecdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE DFTUndiff.c -c -o DFTpurecdouble.o + +DFTpureclongdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE DFTUndiff.c -c -o DFTpureclongdouble.o + +DFTneonfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h + $(CC) $(OPT) -mfpu=neon -DENABLE_NEON_FLOAT DFTUndiff.c -c -o DFTneonfloat.o + +DFT.o : DFT.c DFT.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_NEON_FLOAT DFT.c -c -o DFT.o + +libDFT.a : DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTneonfloat.o DFT.o + rm -f libDFT.a; ar -cvq libDFT.a DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTneonfloat.o DFT.o + +clean : + rm -f *~ *.o *.s *.a diff --git a/plugins/supereq/nsfft-1.00/dft/Makefile.purec b/plugins/supereq/nsfft-1.00/dft/Makefile.purec new file mode 100644 index 00000000..2c8b04f1 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/Makefile.purec @@ -0,0 +1,35 @@ +CC=gcc +BASEOPT=-Wall +OPT=$(BASEOPT) -O3 + +all : libDFT.a + +DFTpurecfloat.o : DFTUndiff.c DFT.h SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT DFTUndiff.c -c -o DFTpurecfloat.o + +DFTpurecdouble.o : DFTUndiff.c DFT.h SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE DFTUndiff.c -c -o DFTpurecdouble.o + +DFTpureclongdouble.o : DFTUndiff.c DFT.h SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE DFTUndiff.c -c -o DFTpureclongdouble.o + +SIMDBaseUndiff_purecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecfloat.o + +SIMDBaseUndiff_purecdouble.o : SIMDBaseUndiff.c DFT.h SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecdouble.o + +SIMDBaseUndiff_pureclongdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_pureclongdouble.o + +SIMDBase.o : SIMDBase.c SIMDBase.h + $(CC) $(BASEOPT) -O -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE SIMDBase.c -c -o SIMDBase.o + +DFT.o : DFT.c DFT.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE DFT.c -c -o DFT.o + +libDFT.a : DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFT.o SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o + rm -f libDFT.a; ar -cvq libDFT.a DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFT.o SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o + +clean : + rm -f *~ *.o *.s *.a diff --git a/plugins/supereq/nsfft-1.00/dft/Makefile.x86 b/plugins/supereq/nsfft-1.00/dft/Makefile.x86 new file mode 100644 index 00000000..6ecbacec --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/Makefile.x86 @@ -0,0 +1,29 @@ +CC=gcc +BASEOPT=-Wall -I ../simd +OPT=$(BASEOPT) -O3 + +all : libDFT.a + +DFTpurecfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT DFTUndiff.c -c -o DFTpurecfloat.o + +DFTpurecdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE DFTUndiff.c -c -o DFTpurecdouble.o + +DFTpureclongdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE DFTUndiff.c -c -o DFTpureclongdouble.o + +DFTssefloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -msse -DENABLE_SSE_FLOAT DFTUndiff.c -c -o DFTssefloat.o + +DFTsse2double.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -msse2 -DENABLE_SSE2_DOUBLE DFTUndiff.c -c -o DFTsse2double.o + +DFT.o : DFT.c DFT.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_SSE_FLOAT -DENABLE_SSE2_DOUBLE DFT.c -c -o DFT.o + +libDFT.a : DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTssefloat.o DFTsse2double.o DFT.o + rm -f libDFT.a; ar -cvq libDFT.a DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTssefloat.o DFTsse2double.o DFT.o + +clean : + rm -f *~ *.o *.s *.a a.out diff --git a/plugins/supereq/nsfft-1.00/dft/Makefile.x86avx b/plugins/supereq/nsfft-1.00/dft/Makefile.x86avx new file mode 100644 index 00000000..b38909cb --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dft/Makefile.x86avx @@ -0,0 +1,35 @@ +CC=gcc +BASEOPT=-Wall -I ../simd +OPT=$(BASEOPT) -O3 + +all : libDFT.a + +DFTpurecfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT DFTUndiff.c -c -o DFTpurecfloat.o + +DFTpurecdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE DFTUndiff.c -c -o DFTpurecdouble.o + +DFTpureclongdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE DFTUndiff.c -c -o DFTpureclongdouble.o + +DFTssefloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -msse -DENABLE_SSE_FLOAT DFTUndiff.c -c -o DFTssefloat.o + +DFTsse2double.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -msse2 -DENABLE_SSE2_DOUBLE DFTUndiff.c -c -o DFTsse2double.o + +DFTavxfloat.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -mavx -DENABLE_AVX_FLOAT DFTUndiff.c -c -o DFTavxfloat.o + +DFTavxdouble.o : DFTUndiff.c DFT.h ../simd/SIMDBase.h ../simd/SIMDBaseUndiff.h + $(CC) $(OPT) -mavx -DENABLE_AVX_DOUBLE DFTUndiff.c -c -o DFTavxdouble.o + +DFT.o : DFT.c DFT.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_SSE_FLOAT -DENABLE_SSE2_DOUBLE -DENABLE_AVX_FLOAT -DENABLE_AVX_DOUBLE DFT.c -c -o DFT.o + +libDFT.a : DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTssefloat.o DFTsse2double.o DFTavxfloat.o DFTavxdouble.o DFT.o + rm -f libDFT.a; ar -cvq libDFT.a DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFTssefloat.o DFTsse2double.o DFTavxfloat.o DFTavxdouble.o DFT.o + +clean : + rm -f *~ *.o *.s *.a a.out diff --git a/plugins/supereq/nsfft-1.00/dfttest/DFTExample.c b/plugins/supereq/nsfft-1.00/dfttest/DFTExample.c new file mode 100644 index 00000000..78ff14dc --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dfttest/DFTExample.c @@ -0,0 +1,88 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> +#include <complex.h> + +#include "SIMDBase.h" +#include "DFT.h" + +typedef float REAL; +#define TYPE SIMDBase_TYPE_FLOAT + +#define THRES 1e-3 + +double complex omega(double n, double kn) { + return cexp((-2 * M_PI * _Complex_I / n) * kn); +} + +void forward(double complex *ts, double complex *fs, int len) { + int k, n; + + for(k=0;k<len;k++) { + fs[k] = 0; + + for(n=0;n<len;n++) { + fs[k] += ts[n] * omega(len, n*k); + } + } +} + +int main(int argc, char **argv) { + const int n = 256; + + int mode = SIMDBase_chooseBestMode(TYPE); + printf("mode : %d, %s\n", mode, SIMDBase_getModeParamString(SIMDBase_PARAMID_MODE_NAME, mode)); + + int veclen = SIMDBase_getModeParamInt(SIMDBase_PARAMID_VECTOR_LEN, mode); + int sizeOfVect = SIMDBase_getModeParamInt(SIMDBase_PARAMID_SIZE_OF_VECT, mode); + + // + + int i, j; + + DFT *p = DFT_init(mode, n, 0); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + double complex ts[veclen][n], fs[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + ts[j][i] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + sx[(i*2+0)*veclen+j] = creal(ts[j][i]); + sx[(i*2+1)*veclen+j] = cimag(ts[j][i]); + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + forward(ts[j], fs[j], n); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if ((fabs(sx[(i*2+0)*veclen+j] - creal(fs[j][i])) > THRES) || + (fabs(sx[(i*2+1)*veclen+j] - cimag(fs[j][i])) > THRES)) { + success = 0; + } + } + } + + printf("%s\n", success ? "OK" : "NG"); + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + exit(0); +} diff --git a/plugins/supereq/nsfft-1.00/dfttest/DFTTestFFTW.c b/plugins/supereq/nsfft-1.00/dfttest/DFTTestFFTW.c new file mode 100644 index 00000000..42825ed9 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dfttest/DFTTestFFTW.c @@ -0,0 +1,317 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> +#include <time.h> +#include <complex.h> + +#include <fftw3.h> + +#include "SIMDBase.h" +#include "DFT.h" + +#if 1 +typedef float REAL; +#define TYPE SIMDBase_TYPE_FLOAT +#else +typedef double REAL; +#define TYPE SIMDBase_TYPE_DOUBLE +#endif + +#define THRES 1e-3 + +// complex forward +int check_cf(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, 0); + fftw_plan w[n]; + + fftw_complex *in[sizeOfVect], *out[sizeOfVect]; + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + for(j=0;j<veclen;j++) { + in[j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n); + out[j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n); + w[j] = fftw_plan_dft_1d(n, in[j], out[j], FFTW_FORWARD, FFTW_ESTIMATE); + + for(i=0;i<n;i++) { + double re = random() / (double)RAND_MAX; + double im = random() / (double)RAND_MAX; + sx[(i*2+0)*veclen+j] = re; + sx[(i*2+1)*veclen+j] = im; + in[j][i] = re + im * _Complex_I; + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + fftw_execute(w[j]); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if (fabs(sx[(i*2+0)*veclen+j] - creal(out[j][i])) > THRES) success = 0; + if (fabs(sx[(i*2+1)*veclen+j] - cimag(out[j][i])) > THRES) success = 0; + } + } + + // + + for(j=0;j<veclen;j++) { + fftw_destroy_plan(w[j]); + fftw_free(in[j]); + fftw_free(out[j]); + } + + SIMDBase_alignedFree(sx); + + DFT_dispose(p, mode); + + // + + return success; +} + +// complex backward +int check_cb(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, 0); + fftw_plan w[n]; + + fftw_complex *in[sizeOfVect], *out[sizeOfVect]; + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + for(j=0;j<veclen;j++) { + in[j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n); + out[j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * n); + w[j] = fftw_plan_dft_1d(n, in[j], out[j], FFTW_BACKWARD, FFTW_ESTIMATE); + + for(i=0;i<n;i++) { + double re = random() / (double)RAND_MAX; + double im = random() / (double)RAND_MAX; + sx[(i*2+0)*veclen+j] = re; + sx[(i*2+1)*veclen+j] = im; + in[j][i] = re + im * _Complex_I; + } + } + + // + + DFT_execute(p, mode, sx, 1); + + for(j=0;j<veclen;j++) { + fftw_execute(w[j]); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if (fabs(sx[(i*2+0)*veclen+j] - creal(out[j][i])) > THRES) success = 0; + if (fabs(sx[(i*2+1)*veclen+j] - cimag(out[j][i])) > THRES) success = 0; + } + } + + // + + for(j=0;j<veclen;j++) { + fftw_destroy_plan(w[j]); + fftw_free(in[j]); + fftw_free(out[j]); + } + + SIMDBase_alignedFree(sx); + + DFT_dispose(p, mode); + + // + + return success; +} + +// real forward +int check_rf(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_REAL); + fftw_plan w[n]; + + double *in[sizeOfVect]; + fftw_complex *out[sizeOfVect]; + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + for(j=0;j<veclen;j++) { + in[j] = (double *) fftw_malloc(sizeof(double) * n); + out[j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (n/2+1)); + w[j] = fftw_plan_dft_r2c_1d(n, in[j], out[j], FFTW_ESTIMATE); + + for(i=0;i<n;i++) { + double re = random() / (double)RAND_MAX; + sx[i*veclen+j] = re; + in[j][i] = re; + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + fftw_execute(w[j]); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + if (fabs(sx[(i*2+0)*veclen+j] - creal(out[j][0])) > THRES) success = 0; + if (fabs(sx[(i*2+1)*veclen+j] - creal(out[j][n/2])) > THRES) success = 0; + } else { + if (fabs(sx[(i*2+0)*veclen+j] - creal(out[j][i])) > THRES) success = 0; + if (fabs(sx[(i*2+1)*veclen+j] - cimag(out[j][i])) > THRES) success = 0; + } + } + } + + // + + for(j=0;j<veclen;j++) { + fftw_destroy_plan(w[j]); + fftw_free(in[j]); + fftw_free(out[j]); + } + + SIMDBase_alignedFree(sx); + + DFT_dispose(p, mode); + + // + + return success; +} + +// real backward +int check_rb(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_REAL); + fftw_plan w[n]; + + fftw_complex *in[sizeOfVect]; + double *out[sizeOfVect]; + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + for(j=0;j<veclen;j++) { + in[j] = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (n/2+1)); + out[j] = (double *) fftw_malloc(sizeof(double) * n); + w[j] = fftw_plan_dft_c2r_1d(n, in[j], out[j], FFTW_ESTIMATE); + + for(i=0;i<n/2;i++) { + if (i == 0) { + in[j][0 ] = (random() / (double)RAND_MAX); + in[j][n/2] = (random() / (double)RAND_MAX); + } else { + in[j][i ] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + } + } + + for(i=0;i<n/2;i++) { + if (i == 0) { + sx[(2*0+0) * veclen + j] = creal(in[j][0 ]); + sx[(2*0+1) * veclen + j] = creal(in[j][n/2]); + } else { + sx[(2*i+0) * veclen + j] = creal(in[j][i]); + sx[(2*i+1) * veclen + j] = cimag(in[j][i]); + } + } + } + + // + + DFT_execute(p, mode, sx, 1); + + for(j=0;j<veclen;j++) { + fftw_execute(w[j]); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if ((fabs(sx[i * veclen + j]*2 - out[j][i]) > THRES)) { + success = 0; + } + } + } + + // + + for(j=0;j<veclen;j++) { + fftw_destroy_plan(w[j]); + fftw_free(in[j]); + fftw_free(out[j]); + } + + SIMDBase_alignedFree(sx); + + DFT_dispose(p, mode); + + // + + return success; +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "%s <log2n>\n", argv[0]); + exit(-1); + } + + const int n = 1 << atoi(argv[1]); + + srandom(time(NULL)); + + // + + int mode = SIMDBase_chooseBestMode(TYPE); + + printf("mode : %d, %s\n", mode, SIMDBase_getModeParamString(SIMDBase_PARAMID_MODE_NAME, mode)); + + int veclen = SIMDBase_getModeParamInt(SIMDBase_PARAMID_VECTOR_LEN, mode); + int sizeOfVect = SIMDBase_getModeParamInt(SIMDBase_PARAMID_SIZE_OF_VECT, mode); + + printf("complex forward : %s\n", check_cf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("complex backward : %s\n", check_cb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("real forward : %s\n", check_rf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("real backward : %s\n", check_rb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + + exit(0); +} diff --git a/plugins/supereq/nsfft-1.00/dfttest/DFTTestNaive.c b/plugins/supereq/nsfft-1.00/dfttest/DFTTestNaive.c new file mode 100644 index 00000000..9d4bdaae --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dfttest/DFTTestNaive.c @@ -0,0 +1,419 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> +#include <time.h> +#include <complex.h> + +#include "SIMDBase.h" +#include "DFT.h" + +#if 1 +typedef float REAL; +#define TYPE SIMDBase_TYPE_FLOAT +#else +typedef double REAL; +#define TYPE SIMDBase_TYPE_DOUBLE +#endif + +#define THRES 1e-3 + +double complex omega(double n, double kn) { + return cexp((-2 * M_PI * _Complex_I / n) * kn); +} + +void forward(double complex *ts, double complex *fs, int len) { + int k, n; + + for(k=0;k<len;k++) { + fs[k] = 0; + + for(n=0;n<len;n++) { + fs[k] += ts[n] * omega(len, n*k); + } + } +} + +void backward(double complex *fs, double complex *ts, int len) { + int k, n; + + for(k=0;k<len;k++) { + ts[k] = 0; + + for(n=0;n<len;n++) { + ts[k] += fs[n] * omega(-len, n*k); + } + } +} + +// complex forward +int check_cf(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, 0); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + double complex ts[veclen][n], fs[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + ts[j][i] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + sx[(i*2+0)*veclen+j] = creal(ts[j][i]); + sx[(i*2+1)*veclen+j] = cimag(ts[j][i]); + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + forward(ts[j], fs[j], n); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if ((fabs(sx[(i*2+0)*veclen+j] - creal(fs[j][i])) > THRES) || + (fabs(sx[(i*2+1)*veclen+j] - cimag(fs[j][i])) > THRES)) { + success = 0; + } + } + } + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + // + + return success; +} + +// complex backward +int check_cb(int n, int mode, int veclen, int sizeOfVect) { + int i,j; + + DFT *p = DFT_init(mode, n, 0); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + double complex fs[veclen][n], ts[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + fs[j][i] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + + sx[(i*2+0)*veclen+j] = creal(fs[j][i]); + sx[(i*2+1)*veclen+j] = cimag(fs[j][i]); + } + } + + // + + DFT_execute(p, mode, sx, 1); + + for(j=0;j<veclen;j++) { + backward(fs[j], ts[j], n); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if ((fabs(sx[(i*2+0)*veclen+j] - creal(ts[j][i])) > THRES) || + (fabs(sx[(i*2+1)*veclen+j] - cimag(ts[j][i])) > THRES)) { + success = 0; + } + } + } + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + // + + return success; +} + +// real forward +int check_rf(int n, int mode, int veclen, int sizeOfVect) { + int i,j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_REAL); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n); + + // + + double complex ts[veclen][n], fs[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + ts[j][i] = (random() / (double)RAND_MAX); + sx[i*veclen+j] = creal(ts[j][i]); + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + forward(ts[j], fs[j], n); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + if (fabs(sx[(2*0+0) * veclen + j] - creal(fs[j][0 ])) > THRES) success = 0; + if (fabs(sx[(2*0+1) * veclen + j] - creal(fs[j][n/2])) > THRES) success = 0; + } else { + if (fabs(sx[(2*i+0) * veclen + j] - creal(fs[j][i])) > THRES) success = 0; + if (fabs(sx[(2*i+1) * veclen + j] - cimag(fs[j][i])) > THRES) success = 0; + } + } + } + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + // + + return success; +} + +// real backward +int check_rb(int n, int mode, int veclen, int sizeOfVect) { + int i,j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_REAL); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n); + + // + + double complex fs[veclen][n], ts[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + fs[j][0 ] = (random() / (double)RAND_MAX); + fs[j][n/2] = (random() / (double)RAND_MAX); + } else { + fs[j][i ] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + fs[j][n-i] = conj(fs[j][i]); + } + } + } + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + sx[(2*0+0) * veclen + j] = creal(fs[j][0 ]); + sx[(2*0+1) * veclen + j] = creal(fs[j][n/2]); + } else { + sx[(2*i+0) * veclen + j] = creal(fs[j][i]); + sx[(2*i+1) * veclen + j] = cimag(fs[j][i]); + } + } + } + + // + + for(j=0;j<veclen;j++) { + backward(fs[j], ts[j], n); + } + + DFT_execute(p, mode, sx, 1); + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if (fabs(cimag(ts[j][i])) > THRES) { + success = 0; + } + + if ((fabs(sx[i * veclen + j]*2 - creal(ts[j][i])) > THRES)) { + success = 0; + } + } + } + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + // + + return success; +} + +// alt real forward +int check_arf(int n, int mode, int veclen, int sizeOfVect) { + int i,j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_ALT_REAL); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n); + + // + + double complex ts[veclen][n], fs[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + ts[j][i] = (random() / (double)RAND_MAX); + sx[i*veclen+j] = creal(ts[j][i]); + } + } + + // + + DFT_execute(p, mode, sx, 1); + + for(j=0;j<veclen;j++) { + backward(ts[j], fs[j], n); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + if (fabs(sx[(2*0+0) * veclen + j] - creal(fs[j][0 ])) > THRES) success = 0; + if (fabs(sx[(2*0+1) * veclen + j] - creal(fs[j][n/2])) > THRES) success = 0; + } else { + if (fabs(sx[(2*i+0) * veclen + j] - creal(fs[j][i])) > THRES) success = 0; + if (fabs(sx[(2*i+1) * veclen + j] - cimag(fs[j][i])) > THRES) success = 0; + } + } + } + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + // + + return success; +} + +// alt real backward +int check_arb(int n, int mode, int veclen, int sizeOfVect) { + int i,j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_ALT_REAL); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n); + + // + + double complex fs[veclen][n], ts[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + fs[j][0 ] = (random() / (double)RAND_MAX); + fs[j][n/2] = (random() / (double)RAND_MAX); + } else { + fs[j][i ] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + fs[j][n-i] = conj(fs[j][i]); + } + } + } + + for(j=0;j<veclen;j++) { + for(i=0;i<n/2;i++) { + if (i == 0) { + sx[(2*0+0) * veclen + j] = creal(fs[j][0 ]); + sx[(2*0+1) * veclen + j] = creal(fs[j][n/2]); + } else { + sx[(2*i+0) * veclen + j] = creal(fs[j][i]); + sx[(2*i+1) * veclen + j] = cimag(fs[j][i]); + } + } + } + + // + + for(j=0;j<veclen;j++) { + forward(fs[j], ts[j], n); + } + + DFT_execute(p, mode, sx, -1); + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if (fabs(cimag(ts[j][i])) > THRES) { + success = 0; + } + + if ((fabs(sx[i * veclen + j]*2 - creal(ts[j][i])) > THRES)) { + success = 0; + } + } + } + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + // + + return success; +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "%s <log2n>\n", argv[0]); + exit(-1); + } + + const int n = 1 << atoi(argv[1]); + + srandom(time(NULL)); + + // + + int mode = SIMDBase_chooseBestMode(TYPE); + + printf("mode : %d, %s\n", mode, SIMDBase_getModeParamString(SIMDBase_PARAMID_MODE_NAME, mode)); + + int veclen = SIMDBase_getModeParamInt(SIMDBase_PARAMID_VECTOR_LEN, mode); + int sizeOfVect = SIMDBase_getModeParamInt(SIMDBase_PARAMID_SIZE_OF_VECT, mode); + + printf("complex forward : %s\n", check_cf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("complex backward : %s\n", check_cb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("real forward : %s\n", check_rf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("real backward : %s\n", check_rb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("alt real forward : %s\n", check_arf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("alt real backward : %s\n", check_arb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + + exit(0); +} diff --git a/plugins/supereq/nsfft-1.00/dfttest/DFTTestOoura.c b/plugins/supereq/nsfft-1.00/dfttest/DFTTestOoura.c new file mode 100644 index 00000000..08c8315f --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dfttest/DFTTestOoura.c @@ -0,0 +1,260 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> +#include <time.h> + +#include "SIMDBase.h" +#include "DFT.h" + +void cdft(int, int, double *, int *, double *); +void rdft(int, int, double *, int *, double *); + +#if 1 +typedef float REAL; +#define TYPE SIMDBase_TYPE_FLOAT +#else +typedef double REAL; +#define TYPE SIMDBase_TYPE_DOUBLE +#endif + +#define THRES 1e-3 + +// complex forward +int check_cf(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, 0); + + int *ip = calloc(n, sizeof(int)); + double *trigTable = SIMDBase_alignedMalloc(sizeof(double)*n/2); + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + double *sy = SIMDBase_alignedMalloc(veclen * sizeof(double) *n*2); + + // + + for(j=0;j<veclen;j++) { + for(i=0;i<n*2;i++) { + sx[i*veclen + j] = random() / (double)RAND_MAX; + sy[j*n*2 + i] = sx[i*veclen + j]; + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + cdft(n*2, -1, &sy[j*n*2], ip, trigTable); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n*2;i++) { + if (fabs(sx[i*veclen+j] - sy[j*n*2 + i]) > THRES) success = 0; + } + } + + // + + SIMDBase_alignedFree(sy); + SIMDBase_alignedFree(sx); + SIMDBase_alignedFree(trigTable); + free(ip); + + DFT_dispose(p, mode); + + // + + return success; +} + +// complex backward +int check_cb(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, 0); + + int *ip = calloc(n, sizeof(int)); + double *trigTable = SIMDBase_alignedMalloc(sizeof(double)*n/2); + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + double *sy = SIMDBase_alignedMalloc(veclen * sizeof(double) *n*2); + + // + + for(j=0;j<veclen;j++) { + for(i=0;i<n*2;i++) { + sx[i*veclen + j] = random() / (double)RAND_MAX; + sy[j*n*2 + i] = sx[i*veclen + j]; + } + } + + // + + DFT_execute(p, mode, sx, 1); + + for(j=0;j<veclen;j++) { + cdft(n*2, 1, &sy[j*n*2], ip, trigTable); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n*2;i++) { + if (fabs(sx[i*veclen+j] - sy[j*n*2 + i]) > THRES) success = 0; + } + } + + // + + SIMDBase_alignedFree(sy); + SIMDBase_alignedFree(sx); + SIMDBase_alignedFree(trigTable); + free(ip); + + DFT_dispose(p, mode); + + // + + return success; +} + +// real forward +int check_rf(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_ALT_REAL); + + int *ip = calloc(n, sizeof(int)); + double *trigTable = SIMDBase_alignedMalloc(sizeof(double)*n/2); + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n); + double *sy = SIMDBase_alignedMalloc(veclen * sizeof(double) *n); + + // + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + sx[i*veclen + j] = random() / (double)RAND_MAX; + sy[j*n + i] = sx[i*veclen + j]; + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + rdft(n, -1, &sy[j*n], ip, trigTable); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if (fabs(sx[i*veclen+j] - sy[j*n + i]) > THRES) success = 0; + } + } + + // + + SIMDBase_alignedFree(sy); + SIMDBase_alignedFree(sx); + SIMDBase_alignedFree(trigTable); + free(ip); + + DFT_dispose(p, mode); + + // + + return success; +} + +// real backward +int check_rb(int n, int mode, int veclen, int sizeOfVect) { + int i, j; + + DFT *p = DFT_init(mode, n, DFT_FLAG_ALT_REAL); + + int *ip = calloc(n, sizeof(int)); + double *trigTable = SIMDBase_alignedMalloc(sizeof(double)*n/2); + + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n); + double *sy = SIMDBase_alignedMalloc(veclen * sizeof(double) *n); + + // + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + sx[i*veclen + j] = random() / (double)RAND_MAX; + sy[j*n + i] = sx[i*veclen + j]; + } + } + + // + + DFT_execute(p, mode, sx, 1); + + for(j=0;j<veclen;j++) { + rdft(n, 1, &sy[j*n], ip, trigTable); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if (fabs(sx[i*veclen+j] - sy[j*n + i]) > THRES) success = 0; + } + } + + // + + SIMDBase_alignedFree(sy); + SIMDBase_alignedFree(sx); + SIMDBase_alignedFree(trigTable); + free(ip); + + DFT_dispose(p, mode); + + // + + return success; +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "%s <log2n>\n", argv[0]); + exit(-1); + } + + const int n = 1 << atoi(argv[1]); + + srandom(time(NULL)); + + // + + int mode = SIMDBase_chooseBestMode(TYPE); + + printf("mode : %d, %s\n", mode, SIMDBase_getModeParamString(SIMDBase_PARAMID_MODE_NAME, mode)); + + int veclen = SIMDBase_getModeParamInt(SIMDBase_PARAMID_VECTOR_LEN, mode); + int sizeOfVect = SIMDBase_getModeParamInt(SIMDBase_PARAMID_SIZE_OF_VECT, mode); + + printf("complex forward : %s\n", check_cf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("complex backward : %s\n", check_cb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("real forward : %s\n", check_rf(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + printf("real backward : %s\n", check_rb(n, mode, veclen, sizeOfVect) ? "OK" : "NG"); + + exit(0); +} diff --git a/plugins/supereq/nsfft-1.00/dfttest/Makefile b/plugins/supereq/nsfft-1.00/dfttest/Makefile new file mode 100644 index 00000000..924b8656 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dfttest/Makefile @@ -0,0 +1,35 @@ +CC=gcc +BASEOPT=-Wall -g -I ../simd -I ../dft -L../simd -L../dft +OPT=$(BASEOPT) -O + +all : DFTExample DFTTestNaive + +clean : + rm -f *~ *.o nsfftplan.*.txt *.log *.dat a.out DFTExample DFTTestNaive DFTTestOoura DFTTestFFTW pi_fft_mod pi_fft_mod.c + +../simd/libSIMD.a : + @cd ../simd; make + +../dft/libDFT.a : + @cd ../dft; make + +../ooura/fftsg.o : + @cd ../ooura; make + +DFTExample : DFTExample.c ../simd/libSIMD.a ../dft/libDFT.a + $(CC) $(OPT) DFTExample.c -lDFT -lSIMD -lm -o DFTExample + +DFTTestNaive : DFTTestNaive.c ../simd/libSIMD.a ../dft/libDFT.a + $(CC) $(OPT) DFTTestNaive.c -lDFT -lSIMD -lm -o DFTTestNaive + +DFTTestOoura : DFTTestOoura.c ../ooura/fftsg.o ../simd/libSIMD.a ../dft/libDFT.a + $(CC) $(OPT) DFTTestOoura.c ../ooura/fftsg.o -lDFT -lSIMD -lm -o DFTTestOoura + +DFTTestFFTW : DFTTestFFTW.c ../simd/libSIMD.a ../dft/libDFT.a + $(CC) $(OPT) DFTTestFFTW.c -lDFT -lSIMD -lfftw3 -lm -o DFTTestFFTW + +pi_fft_mod.c : ../ooura/pi_fft.c pi_fft.c.patch + patch -o pi_fft_mod.c ../ooura/pi_fft.c pi_fft.c.patch + +pi_fft_mod : ../simd/libSIMD.a ../dft/libDFT.a pi_fft_mod.c + $(CC) $(OPT) pi_fft_mod.c -I ../dft -I ../simd -L../dft -L../simd -lm -lDFT -lSIMD -o pi_fft_mod diff --git a/plugins/supereq/nsfft-1.00/dfttest/pi_fft.c.patch b/plugins/supereq/nsfft-1.00/dfttest/pi_fft.c.patch new file mode 100644 index 00000000..c50133cc --- /dev/null +++ b/plugins/supereq/nsfft-1.00/dfttest/pi_fft.c.patch @@ -0,0 +1,131 @@ +--- pi_fft.c 2010-07-30 13:04:25.000000000 +0900 ++++ pi_fft_mod.c 2010-07-31 20:50:11.000000000 +0900 +@@ -25,7 +25,75 @@ + #include <stdio.h> + #include <stdlib.h> + #include <time.h> ++#include <sys/time.h> ++#include <unistd.h> + ++/****/ ++ ++#include <stdint.h> ++#include "SIMDBase.h" ++#include "DFT.h" ++ ++DFT* dft[64]; ++ ++void initdft(int n) { ++ int i, logn = 31 - __builtin_clz(n), writeflag = 0; ++ char buf[20], fn[256]; ++ gethostname(buf, 19); ++ sprintf(fn, "nsfftplan.%s.txt", buf); ++ FILE *fp = fopen(fn, "r"); ++ if (fp != NULL) { ++ for(i=1;i<=logn;i++) { ++ int err; ++ dft[i] = DFT_fread(fp, &err); ++ if (err != DFT_ERROR_NOERROR) { ++ printf("error when reading plan %d : %d\n", i, err); ++ break; ++ } ++ if (DFT_getPlanParamInt(DFT_PARAMID_MODE, dft[i]) != SIMDBase_MODE_PUREC_DOUBLE || ++ DFT_getPlanParamInt(DFT_PARAMID_FFT_LENGTH, dft[i]) != (1 << i) || ++ DFT_getPlanParamInt(DFT_PARAMID_IS_ALT_REAL_TRANSFORM, dft[i]) != 1) { ++ fprintf(stderr, "plan not compatible : %d\n", i); ++ break; ++ } ++ } ++ } ++ if (fp != NULL) fclose(fp); ++ ++ for(i=1;i<=logn;i++) { ++ if (dft[i] == NULL) { ++ dft[i] = DFT_init(SIMDBase_MODE_PUREC_DOUBLE, 1 << i, DFT_FLAG_ALT_REAL | DFT_FLAG_LIGHT_TEST_RUN | DFT_FLAG_VERBOSE); ++ if (dft[i] == NULL) { ++ printf("dft[%d] == NULL\n", i); ++ exit(-1); ++ } ++ writeflag = 1; ++ } ++ } ++ ++ if (writeflag) { ++ fp = fopen(fn, "w"); ++ if (fp != NULL) { ++ for(i=1;i<=logn;i++) { ++ DFT_fwrite(dft[i], fp); ++ } ++ fclose(fp); ++ } ++ } ++} ++ ++void rdft(int n, int isgn, double *a, int *ip, double *w) { ++ int logn = 31 - __builtin_clz(n); ++ DFT_execute(dft[logn], SIMDBase_MODE_PUREC_DOUBLE, a, isgn); ++} ++ ++double timeofday(void) { ++ struct timeval tp; ++ gettimeofday(&tp, NULL); ++ return (double)tp.tv_sec+(1e-6)*tp.tv_usec; ++} ++ ++/****/ + + void mp_load_0(int n, int radix, int out[]); + void mp_load_1(int n, int radix, int out[]); +@@ -67,7 +135,7 @@ + double err, d_time, n_op; + int *a, *b, *c, *e, *i1, *i2, *ip; + double *d1, *d2, *d3, *w; +- time_t t_1, t_2; ++ double t_1, t_2; + FILE *f_log, *f_out; + + f_log = fopen("pi.log", "w"); +@@ -96,6 +164,8 @@ + exit(1); + } + ip[0] = 0; ++ ++ initdft(nfft); + /* ---- radix test ---- */ + log10_radix = 1; + radix = 10; +@@ -111,7 +181,7 @@ + printf("calculating %d digits of PI...\n", log10_radix * (n - 2)); + fprintf(f_log, "calculating %d digits of PI...\n", log10_radix * (n - 2)); + /* ---- time check ---- */ +- time(&t_1); ++ t_1 = timeofday(); + /* + * ---- a formula based on the AGM (Arithmetic-Geometric Mean) ---- + * c = sqrt(0.125); +@@ -216,10 +286,10 @@ + mp_mul(n, radix, a, b, a, i1, nfft, d1, d2, d3, ip, w); + mp_idiv(n, radix, a, npow, a); + /* ---- time check ---- */ +- time(&t_2); ++ t_2 = timeofday(); + /* ---- output ---- */ + f_out = fopen("pi_mod.dat", "w"); +- printf("writing pi.dat...\n"); ++ printf("writing pi_mod.dat...\n"); + mp_fprintf(n - 1, log10_radix, a, f_out); + fclose(f_out); + free(d3); +@@ -238,9 +308,9 @@ + printf("floating point operation: %g op.\n", n_op); + fprintf(f_log, "floating point operation: %g op.\n", n_op); + /* ---- difftime ---- */ +- d_time = difftime(t_2, t_1); +- printf("execution time: %g sec. (real time)\n", d_time); +- fprintf(f_log, "execution time: %g sec. (real time)\n", d_time); ++ d_time = t_2 - t_1; ++ printf("execution time: %.5g sec. (real time)\n", d_time); ++ fprintf(f_log, "execution time: %.5g sec. (real time)\n", d_time); + fclose(f_log); + return 0; + } diff --git a/plugins/supereq/nsfft-1.00/doc/default.css b/plugins/supereq/nsfft-1.00/doc/default.css new file mode 100644 index 00000000..09721163 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/doc/default.css @@ -0,0 +1,34 @@ +body {margin-left: 1.5cm; padding-left: 0.1cm; margin-right: 1.5cm; padding-right: 0.1cm; margin-top: 2.5cm; padding-top: 0.5cm; margin-bottom: 1cm; padding-bottom: 1.0cm; border-top-style:solid; border-bottom-style:solid; } +h1 {font-family: arial, sansserif; font-weight: bold; font-style: italic; margin-top: 0.8cm; } +h2 {font-family: arial, sansserif; font-weight: bold; font-style: italic; margin-top: 0.8cm; } +h3 {font-family: arial, sansserif; font-weight: bold; margin-top: 1.2cm; margin-bottom: 0.8cm; } +h4 {font-family: arial, sansserif; font-weight: bold; margin-top: 1.2cm; margin-bottom: 0.8cm; } +p {font-family: Georgia, "Times New Roman", times, serif; margin-top: 0.3cm; margin-left: 0.5cm; margin-bottom: 0.3cm;} +p.dir {font-family: arial, sansserif; margin-top: 0cm; margin-bottom: 0cm;} +dl { margin-left: 0.5cm; } +dt { font-weight: bold; } +a:link {color: black;} +a:visited {color: black;} +ul.disc {list-style-type: disc; font-family: times, serif;} +ul.circle {list-style-type: circle; font-family: times, serif;} +ul.square {list-style-type: square; font-family: times, serif;} +ul.none {list-style-type: none; font-family: times, serif;} +pre.code { margin-top: 1.0cm; margin-bottom: 1.0cm; margin-left: 1.0cm; margin-right: 1.0cm; border:3px solid #c0c0c0; padding: 0.5cm; font-family: tahoma, sansserif; font-weight: normal; background-color:#f8f8f8; } +pre.command { margin-top: 1.0cm; margin-bottom: 1.0cm; margin-left: 1.5cm; margin-right: 0.0cm; border:0px; padding:0.0cm; font-family: tahoma, sansserif; font-weight: bold; background-color:#f8fffc; } +ol.level1 { font-family: arial, sansserif; font-weight: bold; font-style: italic; font-size:1.5em; } +ol.level2 { font-family: "Times New Roman", serif; font-weight: normal; font-style: normal; font-size:0.85em; margin-top: 0.2cm; margin-bottom: 0.5cm; } +table.figure { margin-left:auto; margin-right:auto; margin-top:1.0cm; margin-bottom:1.0cm; } + +td.caption { font-family: arial, sansserif; font-size: 75%; color: black; } +td { font-family: times, serif; } + +table.lt { border-collapse: collapse; border-style: none; } +td.lt- { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-width: 1px; border-style: none; padding-left=0.2cm; padding-right=0.2cm; } +td.lt-r { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-right-style: solid; border-width: 1px; border-color: black; } +td.lt-l { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-left-style: solid; border-width: 1px; border-color: black; } +td.lt-lr { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-right-style: solid; border-left-style: solid; border-width: 1px; border-color: black; } +td.lt-b { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-bottom-style: solid; border-width: 1px; border-color: black; } +td.lt-hl { margin: 0px; border-style: none; border-bottom-style: solid; border-width: 1px; border-color: black; height: 2px; } +td.lt-bl { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-bottom-style: solid; border-left-style: solid; border-width: 1px; border-color: black; } +td.lt-br { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-bottom-style: solid; border-right-style: solid; border-width: 1px; border-color: black; } +td.lt-blr { margin: 0px; padding: 4px; padding-left:0.3cm; padding-right:0.3cm; border-style: none; border-bottom-style: solid; border-left-style: solid; border-right-style: solid; border-width: 1px; border-color: black; } diff --git a/plugins/supereq/nsfft-1.00/doc/index.xhtml b/plugins/supereq/nsfft-1.00/doc/index.xhtml new file mode 100644 index 00000000..8b7e2c97 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/doc/index.xhtml @@ -0,0 +1,2016 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> +<link rel="stylesheet" type="text/css" href="default.css"/> +<title>NSFFT Reference Manual</title> +</head> +<body> +<h1>NSFFT Reference Manual</h1> + +<h3>Introduction</h3> + +<p> +This is a library for performing 1-dimensional discrete Fourier +transforms. NSDFT is a simple, small and portable library, and it is +efficient since it can utilize SIMD instruction sets in modern +processors. It performs multiple transforms simultaneously, and thus +it is especially suitable for digital signal processing. It does not +need so much computation to make a good execution plan. This library +is in public domain, so that you can incorporate this library into +your product without any obligation. +</p> + +<h3>API Reference</h3> + +<p> +In this section, the API functions are explained. +</p> + +<h4>Include files</h4> + +<p> +You have to include two include files in dft directory. +</p> + +<pre class="code"> +#include <stdint.h> +#include "SIMDBase.h" +#include "DFT.h" +</pre> + +<h4>Data types</h4> + +<p> +First, you have to choose a data type to represent an element in the +input and output sequence of numbers. You can choose from the +following three types. +</p> + +<table class="figure"> + <tr align="center"> + <td> + <table class="lt"> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-br" align="center">Symbol</td> + <td class="lt-b" align="center">Data Type</td> + </tr> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_TYPE_FLOAT</td> + <td class="lt-" align="left">float type in C language</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_TYPE_DOUBLE</td> + <td class="lt-" align="left">double type in C language</td> + </tr> + <tr> + <td class="lt-br" align="left">SIMDBase_TYPE_LONGDOUBLE</td> + <td class="lt-b" align="left">long double type in C language</td> + </tr> + </table> + </td> + </tr> + <tr align="center"> + <td class="caption">Table 1 Data types</td> + </tr> +</table> + + +<h4>Computation modes</h4> + +<p> +Next, a compuation mode have to be chosen. You can choose from the +following modes. +</p> + +<table class="figure"> + <tr align="center"> + <td> + <table class="lt"> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-br" align="center">Symbol</td> + <td class="lt-br" align="center">Type</td> + <td class="lt-br" align="center">Vector Length</td> + <td class="lt-b" align="center">Computation Mode</td> + </tr> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_PUREC_FLOAT</td> + <td class="lt-r" align="center">float</td> + <td class="lt-r" align="center">1</td> + <td class="lt-" align="center">Scalar float</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_PUREC_DOUBLE</td> + <td class="lt-r" align="center">double</td> + <td class="lt-r" align="center">1</td> + <td class="lt-" align="center">Scalar double</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_PUREC_LONGDOUBLE</td> + <td class="lt-r" align="center">long double</td> + <td class="lt-r" align="center">1</td> + <td class="lt-" align="center">Scalar long double</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_SSE_FLOAT</td> + <td class="lt-r" align="center">float</td> + <td class="lt-r" align="center">4</td> + <td class="lt-" align="center">x86 SSE</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_SSE2_DOUBLE</td> + <td class="lt-r" align="center">double</td> + <td class="lt-r" align="center">2</td> + <td class="lt-" align="center">x86 SSE2</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_NEON_FLOAT</td> + <td class="lt-r" align="center">float</td> + <td class="lt-r" align="center">4</td> + <td class="lt-" align="center">ARM NEON</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_AVX_FLOAT</td> + <td class="lt-r" align="center">float</td> + <td class="lt-r" align="center">8</td> + <td class="lt-" align="center">x86 AVX (float)</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_MODE_AVX_DOUBLE</td> + <td class="lt-r" align="center">double</td> + <td class="lt-r" align="center">4</td> + <td class="lt-" align="center">x86 AVX (double)</td> + </tr> + <tr> + <td class="lt-br" align="left">SIMDBase_MODE_ALTIVEC_FLOAT</td> + <td class="lt-br" align="center">float</td> + <td class="lt-br" align="center">4</td> + <td class="lt-b" align="center">PowerPC Altivec</td> + </tr> + </table> + </td> + </tr> + <tr align="center"> + <td class="caption">Table 2 Computation modes</td> + </tr> +</table> + +<p> +The following function automatically checks the availability of each +instruction set on your computer, and chooses the best computation +mode. +</p> + +<pre class="code"> +int32_t SIMDBase_chooseBestMode(int32_t type); +</pre> + +<p> +The return value is the best mode chosen by this routine. +<i>type</i> is the data type you chose. +</p> + + +<h4>Retrieving parameters</h4> + +<p> +You can make queries for any mode using the following function. +</p> + +<pre class="code"> +int32_t SIMDBase_getModeParamInt(int32_t paramId, int32_t mode); +</pre> + +<p> +<i>mode</i> is the computation mode you chose. <i>paramId</i> is one +of the following. +</p> + +<table class="figure"> + <tr align="center"> + <td> + <table class="lt"> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-br" align="center">Symbol</td> + <td class="lt-b" align="center">Meaning</td> + </tr> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_PARAMID_SIZE_OF_REAL</td> + <td class="lt-" align="left">Size of an element in a vector in byte</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_PARAMID_SIZE_OF_VECT</td> + <td class="lt-" align="left">Size of the vector in byte</td> + </tr> + <tr> + <td class="lt-r" align="left">SIMDBase_PARAMID_VECTOR_LEN</td> + <td class="lt-" align="left">Number of elements in a vector</td> + </tr> + <tr> + <td class="lt-br" align="left">SIMDBase_PARAMID_MODE_AVAILABILITY</td> + <td class="lt-b" align="left">Whether the given mode is available or not</td> + </tr> + </table> + </td> + </tr> + <tr align="center"> + <td class="caption">Table 3 Querying parameter for computation mode</td> + </tr> +</table> + +<p> +Here, a vector is a set of multiple primitive data element (single or +double precision FP number) which can be stored in one SIMD register, +and can be processed by one SIMD instruction at the same time. +</p> + +<p> +You can get the mode name in string data type. In this +case, <i>paramId</i> must be SIMDBase_PARAMID_MODE_NAME. +</p> + +<pre class="code"> +char *SIMDBase_getModeParamString(int32_t paramId, int32_t mode); +</pre> + +<p> +You should not modify the data returned by the above function. +</p> + + +<h4>Making and destroying execution plan</h4> + +<p> +An execution plan can be made by the following function. +</p> + +<pre class="code"> +DFT *DFT_init(int32_t mode, int32_t n, int32_t flags); +</pre> + +<p> +The return value is a pointer to a newly made plan. +<i>mode</i> is the mode you chose above. <i>n</i> is the length of a +transform. You can specify a bitwise OR of the following symbols +as <i>flags</i>. You should not specify more than one flags regarding +to test run. You should not specify DFT_FLAG_FORCE_RECURSIVE and +DFT_FLAG_FORCE_COBRA at the same time. If neither DFT_FLAG_REAL nor +DFT_FLAG_ALT_REAL is specified, an execution plan for complex +transforms are made. +</p> + +<table class="figure"> + <tr align="center"> + <td> + <table class="lt"> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-br" align="center">Symbol</td> + <td class="lt-b" align="center">Meaning</td> + </tr> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_NO_TEST_RUN</td> + <td class="lt-" align="left">Make execution plan without performing a test run</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_LIGHT_TEST_RUN</td> + <td class="lt-" align="left">Perform small amount of test run to make an execution plan</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_HEAVY_TEST_RUN</td> + <td class="lt-" align="left">Perform large amount of test run to make an execution plan</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_EXHAUSTIVE_TEST_RUN</td> + <td class="lt-" align="left">Perform exhaustive search of parameters and find the optimal execution plan</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_REAL</td> + <td class="lt-" align="left">Make an execution plan for a real transform</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_ALT_REAL</td> + <td class="lt-" align="left">Make an execution plan for an alternative real transform</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_VERBOSE</td> + <td class="lt-" align="left">Make some noise during making an execution plan</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_NOBITREVERSAL</td> + <td class="lt-" align="left">Does not perforam bitreversal operation during a transform</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_FLAG_FORCE_RECURSIVE</td> + <td class="lt-" align="left">Force using the recursive bit-reveral routine. This routine is suited for small transforms.</td> + </tr> + <tr> + <td class="lt-br" align="left">DFT_FLAG_FORCE_COBRA</td> + <td class="lt-b" align="left">Force using the Cobra bit-reveral routine. This routine is suited for large transforms.</td> + </tr> + </table> + </td> + </tr> + <tr align="center"> + <td class="caption">Table 4 Options for making execution plan</td> + </tr> +</table> + +<p> +You can destroy the plan you made by the following function. +</p> + +<pre class="code"> +void DFT_dispose(DFT *p, int32_t mode); +</pre> + +<p> +<i>p</i> is a pointer to the execution plan. <i>mode</i> is the +corresponding execution mode. +</p> + +<p> +You can retrieve parameters of a plan using the following function. +</p> + +<pre class="code"> +int32_t DFT_getPlanParamInt(int32_t paramId, void *p); +</pre> + +<p> +<i>p</i> is a pointer to an execution plan. <i>paramId</i> is one +of the following. +</p> + +<table class="figure"> + <tr align="center"> + <td> + <table class="lt"> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-br" align="center">Symbol</td> + <td class="lt-b" align="center">Meaning</td> + </tr> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_PARAMID_TYPE</td> + <td class="lt-" align="left">Data type</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_PARAMID_MODE</td> + <td class="lt-" align="left">Computation mode</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_PARAMID_FFT_LENGTH</td> + <td class="lt-" align="left">Length of the transform</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_PARAMID_IS_REAL_TRANSFORM</td> + <td class="lt-" align="left">Whether the plan is for real transforms</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_PARAMID_NO_BIT_REVERSAL</td> + <td class="lt-" align="left">Whether the plan does not perform bit reversal operation</td> + </tr> + <tr> + <td class="lt-br" align="left">DFT_PARAMID_TEST_RUN</td> + <td class="lt-b" align="left">How much test run is performed when making this plan</td> + </tr> + </table> + </td> + </tr> + <tr align="center"> + <td class="caption">Table 5 Querying parameter for execution plan</td> + </tr> +</table> + +<h4>Writing and reading execution plan to/from file</h4> + +<p> +You can write or read an execution plan to/from a file using the following functions. +</p> + +<pre class="code"> +int32_t DFT_fwrite(DFT *p, FILE *fp); +DFT *DFT_fread(FILE *fp, int32_t *errcode); +</pre> + +<p> +<i>p</i> is a pointer to a plan. <i>fp</i> is a file +pointer. DFT_fwrite returns 1 if the plan is successfully written, and +0 if an error occurs. DFT_fread returns the pointer to the read plan +if the plan is successfully read, and NULL if an error occurs. If an +error occurs, an error code is returned to a variable whose pointer is +specified by <i>errcode</i>. The interpretation of error codes is +given below. +</p> + +<table class="figure"> + <tr align="center"> + <td> + <table class="lt"> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-br" align="center">Symbol</td> + <td class="lt-b" align="center">Meaning</td> + </tr> + <tr> + <td class="lt-hl"></td> + <td class="lt-hl"></td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_ERROR_NOERROR</td> + <td class="lt-" align="left">No error</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_ERROR_FILE_VERSION</td> + <td class="lt-" align="left">File format version mismatch</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_ERROR_FILE_IO</td> + <td class="lt-" align="left">I/O error</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_ERROR_UNEXPECTED_EOF</td> + <td class="lt-" align="left">Unexpected EOF</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_ERROR_MODE_NOT_COMPILED_IN</td> + <td class="lt-" align="left">Tried to read a plan with mode that is not compiled in</td> + </tr> + <tr> + <td class="lt-r" align="left">DFT_ERROR_MODE_NOT_AVAILABLE</td> + <td class="lt-" align="left">Tried to read a plan with mode that is not supported by hardware</td> + </tr> + <tr> + <td class="lt-br" align="left">DFT_ERROR_UNKNOWN_MODE</td> + <td class="lt-b" align="left">Tried to read a plan with mode that is unknown by library</td> + </tr> + </table> + </td> + </tr> + <tr align="center"> + <td class="caption">Table 6 Errors that may happen during file I/O</td> + </tr> +</table> + + +<h4>Allocating and freeing buffers for transforms</h4> + +<p> +In order to allocate word-aligned buffers for storing data which is +fed to the FFT routine, you have to use the following function. +</p> + +<pre class="code"> +void *DFT_alignedMalloc(uint64_t size); +</pre> + +<p> +This function allocates <i>size</i> bytes of word-aligned memory and +returns the pointer. In order to free this memory, you have to use the +following function. +</p> + +<pre class="code"> +void DFT_alignedFree(void *ptr); +</pre> + +<p> +<i>ptr</i> is the pointer returned from DFT_alignedMalloc function. +</p> + +<h4>Executing transform</h4> + +<p> +By the following function, the planned transform can be executed. +</p> + +<pre class="code"> +void DFT_execute(DFT *p, int32_t mode, void *s, int32_t dir); +</pre> + +<p> +<i>p</i> is a pointer to the plan. <i>mode</i> is the computation +mode. <i>s</i> is the pointer to the buffer in which the sequence of +input values is stored. This pointer must be a pointer returned from +DFT_alignedMalloc function. +<i>dir</i> specifies the direction of transform. +</p> + +<p> +The forward and backward discrete Fourier transforms are defined by +the following formula (1) and (2), respectively. +</p> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <msub><mi>X</mi><mi>k</mi></msub> + <mo>=</mo> + <munderover> + <mo style="font-size:140%;">∑</mo> + <mrow><mi>n</mi><mo>=</mo><mn>0</mn></mrow> + <mrow><mi>N</mi><mo>-</mo><mn>1</mn></mrow> + </munderover> + <msub><mi>x</mi><mi>n</mi></msub> + <msup> + <mi>e</mi> + <mrow> + <mo>-</mo> + <mfrac> + <mrow><mn>2</mn><mi>π</mi><mi>i</mi></mrow> + <mi>N</mi> + </mfrac> + <mi>k</mi><mi>n</mi> + </mrow> + </msup> + + <mo> </mo> + + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </math> + </td> + <td> + <p>(1)</p> + </td> + </tr> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <msub><mi>x</mi><mi>n</mi></msub> + <mo>=</mo> + <mfrac> + <mn>1</mn> + <mi>N</mi> + </mfrac> + <munderover> + <mo style="font-size:140%;">∑</mo> + <mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow> + <mrow><mi>N</mi><mo>-</mo><mn>1</mn></mrow> + </munderover> + <msub><mi>X</mi><mi>k</mi></msub> + <msup> + <mi>e</mi> + <mrow> + <mfrac> + <mrow><mn>2</mn><mi>π</mi><mi>i</mi></mrow> + <mi>N</mi> + </mfrac> + <mi>k</mi><mi>n</mi> + </mrow> + </msup> + + <mo> </mo> + + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + </mrow> + </math> + </td> + <td> + <p>(2)</p> + </td> + </tr> +</table> + +<p> +The complex forward and backward transforms perform the transforms +defined by the following formula (3) and (4), respectively. <i>V</i> +is the vector length mentioned above. Again, calling DFT_execute once +performs <i>V</i> forward or backward transforms at a time. Please +note that (4) gives values multiplied by <i>N</i> compared to +(2). Specifying -1 as the direction of transform performs the +transform defined by (3). In this case, the input should be given as +in (5) , and the output is given as in (6). Specifying 1 as the +direction of transform performs the transform defined by (4), and in +this case, the input should be given as in (6) , and the output is +given as in (5). +</p> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>=</mo> + <munderover> + <mo style="font-size:140%;">∑</mo> + <mrow><mi>n</mi><mo>=</mo><mn>0</mn></mrow> + <mrow><mi>N</mi><mo>-</mo><mn>1</mn></mrow> + </munderover> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <msup> + <mi>e</mi> + <mrow> + <mo>-</mo> + <mfrac> + <mrow><mn>2</mn><mi>π</mi><mi>i</mi></mrow> + <mi>N</mi> + </mfrac> + <mi>k</mi><mi>n</mi> + </mrow> + </msup> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(3)</p> + </td> + </tr> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>=</mo> + <munderover> + <mo style="font-size:140%;">∑</mo> + <mrow><mi>k</mi><mo>=</mo><mn>0</mn></mrow> + <mrow><mi>N</mi><mo>-</mo><mn>1</mn></mrow> + </munderover> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <msup> + <mi>e</mi> + <mrow> + <mfrac> + <mrow><mn>2</mn><mi>π</mi><mi>i</mi></mrow> + <mi>N</mi> + </mfrac> + <mi>k</mi><mi>n</mi> + </mrow> + </msup> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(4)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mfenced open="{" close=""> + <mtable> + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>n</mi> + <mo>+</mo> + <mn>0</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Re</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>n</mi> + <mo>+</mo> + <mn>1</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Im</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + </mtr> + + </mtable> + </mfenced> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(5)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mfenced open="{" close=""> + <mtable> + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>k</mi> + <mo>+</mo> + <mn>0</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Re</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>k</mi> + <mo>+</mo> + <mn>1</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Im</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + </mtr> + + </mtable> + </mfenced> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(6)</p> + </td> + </tr> +</table> + +<p> +The real forward transform performs the transform defined by (3) when +the condition (7) is satisfied. In this case, the output satisfies +(8). You should specify -1 as the direction of transform, and the +input should be given as in (9), and the output is given as in (10). +The real backward transform is the opposite of the real forward +transform. The input should satisfy (8) and the output satisfies (7). +You should specify 1 as the direction of transform, and the input +should be given as in (10), and the output is given as in (11). +</p> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mi>Im</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + <mo>=</mo> + <mn>0</mn> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + + </mrow> + </math> + </td> + <td> + <p>(7)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mfenced open="{" close=""> + <mtable> + <mtr> + <mtd> + <mrow> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>=</mo> + <msubsup> + <mi>X</mi> + <mrow><mi>N</mi><mo>-</mo><mi>k</mi><mo>,</mo><mi>v</mi></mrow> + <mo>*</mo> + </msubsup> + </mrow> + </mtd> + + <mtd> + <mo> </mo> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>1</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>Im</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + <mo>=</mo> + <mn>0</mn> + </mrow> + </mtd> + + <mtd> + <mo> </mo> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + </mrow> + </mtd> + </mtr> + + </mtable> + </mfenced> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(8)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mi>n</mi> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Re</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(9)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mfenced open="{" close=""> + <mtable> + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>k</mi> + <mo>+</mo> + <mn>0</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + </mrow> + </mtd> + + <mtd> + <mo>=</mo> + </mtd> + + <mtd> + <mrow> + <mi>Re</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + </mrow> + </mtd> + + <mtd> + <mo>=</mo> + </mtd> + + <mtd> + <mrow> + <mi>Re</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>N</mi><mo>/</mo><mn>2</mn><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + + <mtd> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>k</mi> + <mo>+</mo> + <mn>1</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + </mrow> + </mtd> + + <mtd> + <mo>=</mo> + </mtd> + + <mtd> + <mrow> + <mi>Im</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>1</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mtd> + </mtr> + + </mtable> + </mfenced> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(10)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mrow> + <mn>2</mn> + <mo> </mo> + <mi>s</mi> + <mo>[</mo> + <mi>n</mi> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Re</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(11)</p> + </td> + </tr> +</table> + +<p> +The alternative real transforms are defined by (12) to (16), similarly +to the real transforms. The alternative transforms are handy if you +are migrating from the FFT library made by Prof. Takuya Ooura. You +should specify 1 as the direction in order to perform a forward +transform, and -1 when you perform a backward transform. +</p> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mi>Im</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + <mo>=</mo> + <mn>0</mn> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + + </mrow> + </math> + </td> + <td> + <p>(12)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mfenced open="{" close=""> + <mtable> + <mtr> + <mtd> + <mrow> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>=</mo> + <msubsup> + <mi>x</mi> + <mrow><mi>N</mi><mo>-</mo><mi>n</mi><mo>,</mo><mi>v</mi></mrow> + <mo>*</mo> + </msubsup> + </mrow> + </mtd> + + <mtd> + <mo> </mo> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>1</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>Im</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + <mo>=</mo> + <mn>0</mn> + </mrow> + </mtd> + + <mtd> + <mo> </mo> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + </mrow> + </mtd> + </mtr> + + </mtable> + </mfenced> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(13)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mi>n</mi> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Re</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(14)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mfenced open="{" close=""> + <mtable> + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>n</mi> + <mo>+</mo> + <mn>0</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + </mrow> + </mtd> + + <mtd> + <mo>=</mo> + </mtd> + + <mtd> + <mrow> + <mi>Re</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + </mrow> + </mtd> + + <mtd> + <mo>=</mo> + </mtd> + + <mtd> + <mrow> + <mi>Re</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>N</mi><mo>/</mo><mn>2</mn><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + + <mtd> + </mtd> + </mtr> + + <mtr> + <mtd> + <mrow> + <mi>s</mi> + <mo>[</mo> + <mo>(</mo> + <mn>2</mn> + <mi>n</mi> + <mo>+</mo> + <mn>1</mn> + <mo>)</mo> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + </mrow> + </mtd> + + <mtd> + <mo>=</mo> + </mtd> + + <mtd> + <mrow> + <mi>Im</mi> + <mo>(</mo> + <msub><mi>x</mi><mrow><mi>n</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + </mtd> + + <mtd> + <mrow style="font-size:100%;"> + <mi>n</mi> + <mo>=</mo> + <mn>1</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mfrac> + <mi>N</mi> + <mn>2</mn> + </mfrac> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mtd> + </mtr> + + </mtable> + </mfenced> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(15)</p> + </td> + </tr> +</table> + +<table border="0" style="margin-right:1.0cm; margin-left:1.0cm; margin-top:0.5cm; margin-bottom:0.5cm;"> + <tr> + <td align="center" style="width:100%;"> + <math mode="display" style="font-size:1.2em;" xmlns="http://www.w3.org/1998/Math/MathML"> + <mrow> + <mrow> + <mn>2</mn> + <mo> </mo> + <mi>s</mi> + <mo>[</mo> + <mi>n</mi> + <mi>V</mi> + <mo>+</mo> + <mi>v</mi> + <mo>]</mo> + + <mo>=</mo> + + <mi>Re</mi> + <mo>(</mo> + <msub><mi>X</mi><mrow><mi>k</mi><mo>,</mo><mi>v</mi></mrow></msub> + <mo>)</mo> + </mrow> + + <mo> </mo> + + <mrow style="font-size:100%;"> + <mi>k</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>N</mi> + <mo>-</mo> + <mn>1</mn> + + <mo> </mo> + <mo>,</mo> + <mo> </mo> + + <mi>v</mi> + <mo>=</mo> + <mn>0</mn> + <mo>,</mo> + <mo>·</mo> + <mo>·</mo> + <mo>·</mo> + <mo>,</mo> + <mi>V</mi> + <mo>-</mo> + <mn>1</mn> + </mrow> + </mrow> + </math> + </td> + <td> + <p>(16)</p> + </td> + </tr> +</table> + + +<h3>Examples</h3> + +<p> +Below is an example code using nsfft library. +</p> + +<pre class="code"> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> +#include <complex.h> + +#include "SIMDBase.h" +#include "DFT.h" + +typedef float REAL; +#define TYPE SIMDBase_TYPE_FLOAT + +#define THRES 1e-3 + +double complex omega(double n, double kn) { + return cexp((-2 * M_PI * _Complex_I / n) * kn); +} + +void forward(double complex *ts, double complex *fs, int len) { + int k, n; + + for(k=0;k<len;k++) { + fs[k] = 0; + + for(n=0;n<len;n++) { + fs[k] += ts[n] * omega(len, n*k); + } + } +} + +int main(int argc, char **argv) { + const int n = 256; + + int mode = SIMDBase_chooseBestMode(TYPE); + printf("mode : %d, %s\n", mode, SIMDBase_getModeParamString(SIMDBase_PARAMID_MODE_NAME, mode)); + + int veclen = SIMDBase_getModeParamInt(SIMDBase_PARAMID_VECTOR_LEN, mode); + int sizeOfVect = SIMDBase_getModeParamInt(SIMDBase_PARAMID_SIZE_OF_VECT, mode); + + // + + int i, j; + + DFT *p = DFT_init(mode, n, 0); + REAL *sx = SIMDBase_alignedMalloc(sizeOfVect*n*2); + + // + + double complex ts[veclen][n], fs[veclen][n]; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + ts[j][i] = (random() / (double)RAND_MAX) + (random() / (double)RAND_MAX) * _Complex_I; + sx[(i*2+0)*veclen+j] = creal(ts[j][i]); + sx[(i*2+1)*veclen+j] = cimag(ts[j][i]); + } + } + + // + + DFT_execute(p, mode, sx, -1); + + for(j=0;j<veclen;j++) { + forward(ts[j], fs[j], n); + } + + // + + int success = 1; + + for(j=0;j<veclen;j++) { + for(i=0;i<n;i++) { + if ((fabs(sx[(i*2+0)*veclen+j] - creal(fs[j][i])) > THRES) || + (fabs(sx[(i*2+1)*veclen+j] - cimag(fs[j][i])) > THRES)) { + success = 0; + } + } + } + + printf("%s\n", success ? "OK" : "NG"); + + // + + SIMDBase_alignedFree(sx); + DFT_dispose(p, mode); + + exit(0); +} +</pre> + +<p> +You should put this code under a directory in the root directory of +the library, and then you can compile this code with the following +command. +</p> + +<pre class="code"> +gcc -Wall -g -I ../simd -I ../dft -L../simd -L../dft -O DFTExample.c -lDFT -lSIMD -lm -o DFTExample +</pre> + +<h3>Compilation</h3> + +<p> +The nsfft source package include a few makefiles for various +architectures. You should make symbolic links to makefiles suited for +your computer under <i>dft</i> and <i>simd</i> directories. +</p> + +</body> +</html> diff --git a/plugins/supereq/nsfft-1.00/doc/nsfft.pdf b/plugins/supereq/nsfft-1.00/doc/nsfft.pdf Binary files differnew file mode 100644 index 00000000..ed4ad5db --- /dev/null +++ b/plugins/supereq/nsfft-1.00/doc/nsfft.pdf diff --git a/plugins/supereq/nsfft-1.00/ooura/Makefile b/plugins/supereq/nsfft-1.00/ooura/Makefile new file mode 100644 index 00000000..bad1679e --- /dev/null +++ b/plugins/supereq/nsfft-1.00/ooura/Makefile @@ -0,0 +1,11 @@ +CC=gcc +BASEOPT=-Wall -g +OPT=$(BASEOPT) -O3 + +all : fftsg.o + +clean : + rm -f *~ *.o a.out + +fftsg.o : fftsg.c + $(CC) $(OPT) -c fftsg.c diff --git a/plugins/supereq/nsfft-1.00/ooura/README b/plugins/supereq/nsfft-1.00/ooura/README new file mode 100644 index 00000000..d7ddefc2 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/ooura/README @@ -0,0 +1,2 @@ +Please put fftsg.c and pi_fft.c which is included in Prof. Takuya +Ooura's FFT package. diff --git a/plugins/supereq/nsfft-1.00/ooura/fftsg.c b/plugins/supereq/nsfft-1.00/ooura/fftsg.c new file mode 100644 index 00000000..43d75344 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/ooura/fftsg.c @@ -0,0 +1,3314 @@ +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :split-radix + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, double *, int *, double *); + void rdft(int, int, double *, int *, double *); + void ddct(int, int, double *, int *, double *); + void ddst(int, int, double *, int *, double *); + void dfct(int, double *, double *, int *, double *); + void dfst(int, double *, double *, int *, double *); +macro definitions + USE_CDFT_PTHREADS : default=not defined + CDFT_THREADS_BEGIN_N : must be >= 512, default=8192 + CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536 + USE_CDFT_WINTHREADS : default=not defined + CDFT_THREADS_BEGIN_N : must be >= 512, default=32768 + CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288 + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + <case1> + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k<n + <case2> + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k<n + (notes: sum_j=0^n-1 is a summation from j=0 to n-1) + [usage] + <case1> + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (double *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j<n + output data + a[2*k] = Re(X[k]), + a[2*k+1] = Im(X[k]), 0<=k<n + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + <case1> RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2 + <case2> IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n + [usage] + <case1> + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + <case1> + output data + a[2*k] = R[k], 0<=k<n/2 + a[2*k+1] = I[k], 0<k<n/2 + a[1] = R[n/2] + <case2> + input data + a[2*j] = R[j], 0<=j<n/2 + a[2*j+1] = I[j], 0<j<n/2 + a[1] = R[n/2] + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + <case1> IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n + <case2> DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k<n + [usage] + <case1> + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + output data + a[k] = C[k], 0<=k<n + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + <case1> IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n + <case2> DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0<k<=n + [usage] + <case1> + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + <case1> + input data + a[j] = A[j], 0<j<n + a[0] = A[n] + output data + a[k] = S[k], 0<=k<n + <case2> + output data + a[k] = S[k], 0<k<n + a[0] = S[n] + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (double *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (double *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n + [usage] + ip[0] = 0; // first time only + dfst(n, a, t, ip, w); + [parameters] + n :data length + 1 (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + output data + a[k] = S[k], 0<k<n + (a[0] is used for work area) + t[0...n/2-1] :work area (double *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + + +void cdft(int n, int isgn, double *a, int *ip, double *w) +{ + void makewt(int nw, int *ip, double *w); + void cftfsub(int n, double *a, int *ip, int nw, double *w); + void cftbsub(int n, double *a, int *ip, int nw, double *w); + int nw; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + if (isgn >= 0) { + cftfsub(n, a, ip, nw, w); + } else { + cftbsub(n, a, ip, nw, w); + } +} + + +void rdft(int n, int isgn, double *a, int *ip, double *w) +{ + void makewt(int nw, int *ip, double *w); + void makect(int nc, int *ip, double *c); + void cftfsub(int n, double *a, int *ip, int nw, double *w); + void cftbsub(int n, double *a, int *ip, int nw, double *w); + void rftfsub(int n, double *a, int nc, double *c); + void rftbsub(int n, double *a, int nc, double *c); + int nw, nc; + double xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } +} + + +void ddct(int n, int isgn, double *a, int *ip, double *w) +{ + void makewt(int nw, int *ip, double *w); + void makect(int nc, int *ip, double *c); + void cftfsub(int n, double *a, int *ip, int nw, double *w); + void cftbsub(int n, double *a, int *ip, int nw, double *w); + void rftfsub(int n, double *a, int nc, double *c); + void rftbsub(int n, double *a, int nc, double *c); + void dctsub(int n, double *a, int nc, double *c); + int j, nw, nc; + double xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = a[j] - a[j - 1]; + a[j] += a[j - 1]; + } + a[1] = a[0] - xr; + a[0] += xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } + dctsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = a[j] - a[j + 1]; + a[j] += a[j + 1]; + } + a[n - 1] = xr; + } +} + + +void ddst(int n, int isgn, double *a, int *ip, double *w) +{ + void makewt(int nw, int *ip, double *w); + void makect(int nc, int *ip, double *c); + void cftfsub(int n, double *a, int *ip, int nw, double *w); + void cftbsub(int n, double *a, int *ip, int nw, double *w); + void rftfsub(int n, double *a, int nc, double *c); + void rftbsub(int n, double *a, int nc, double *c); + void dstsub(int n, double *a, int nc, double *c); + int j, nw, nc; + double xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = -a[j] - a[j - 1]; + a[j] -= a[j - 1]; + } + a[1] = a[0] + xr; + a[0] -= xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } + dstsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = -a[j] - a[j + 1]; + a[j] -= a[j + 1]; + } + a[n - 1] = -xr; + } +} + + +void dfct(int n, double *a, double *t, int *ip, double *w) +{ + void makewt(int nw, int *ip, double *w); + void makect(int nc, int *ip, double *c); + void cftfsub(int n, double *a, int *ip, int nw, double *w); + void rftfsub(int n, double *a, int nc, double *c); + void dctsub(int n, double *a, int nc, double *c); + int j, k, l, m, mh, nw, nc; + double xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + m = n >> 1; + yi = a[m]; + xi = a[0] + a[n]; + a[0] -= a[n]; + t[0] = xi - yi; + t[m] = xi + yi; + if (n > 2) { + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] - a[n - j]; + xi = a[j] + a[n - j]; + yr = a[k] - a[n - k]; + yi = a[k] + a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi - yi; + t[k] = xi + yi; + } + t[mh] = a[mh] + a[n - mh]; + a[mh] -= a[n - mh]; + dctsub(m, a, nc, w + nw); + if (m > 4) { + cftfsub(m, a, ip, nw, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, ip, nw, w); + } + a[n - 1] = a[0] - a[1]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] + a[j + 1]; + a[2 * j - 1] = a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dctsub(m, t, nc, w + nw); + if (m > 4) { + cftfsub(m, t, ip, nw, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, ip, nw, w); + } + a[n - l] = t[0] - t[1]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = t[j] - t[j + 1]; + a[k + l] = t[j] + t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 0; j < mh; j++) { + k = m - j; + t[j] = t[m + k] - t[m + j]; + t[k] = t[m + k] + t[m + j]; + } + t[mh] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + a[n] = t[2] - t[1]; + a[0] = t[2] + t[1]; + } else { + a[1] = a[0]; + a[2] = t[0]; + a[0] = t[1]; + } +} + + +void dfst(int n, double *a, double *t, int *ip, double *w) +{ + void makewt(int nw, int *ip, double *w); + void makect(int nc, int *ip, double *c); + void cftfsub(int n, double *a, int *ip, int nw, double *w); + void rftfsub(int n, double *a, int nc, double *c); + void dstsub(int n, double *a, int nc, double *c); + int j, k, l, m, mh, nw, nc; + double xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + if (n > 2) { + m = n >> 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] + a[n - j]; + xi = a[j] - a[n - j]; + yr = a[k] + a[n - k]; + yi = a[k] - a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi + yi; + t[k] = xi - yi; + } + t[0] = a[mh] - a[n - mh]; + a[mh] += a[n - mh]; + a[0] = a[m]; + dstsub(m, a, nc, w + nw); + if (m > 4) { + cftfsub(m, a, ip, nw, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, ip, nw, w); + } + a[n - 1] = a[1] - a[0]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] - a[j + 1]; + a[2 * j - 1] = -a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dstsub(m, t, nc, w + nw); + if (m > 4) { + cftfsub(m, t, ip, nw, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, ip, nw, w); + } + a[n - l] = t[1] - t[0]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = -t[j] - t[j + 1]; + a[k + l] = t[j] - t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + t[j] = t[m + k] + t[m + j]; + t[k] = t[m + k] - t[m + j]; + } + t[0] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + } + a[0] = 0; +} + + +/* -------- initializing routines -------- */ + + +#include <math.h> + +void makewt(int nw, int *ip, double *w) +{ + void makeipt(int nw, int *ip); + int j, nwh, nw0, nw1; + double delta, wn4r, wk1r, wk1i, wk3r, wk3i; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + wn4r = cos(delta * nwh); + w[0] = 1; + w[1] = wn4r; + if (nwh == 4) { + w[2] = cos(delta * 2); + w[3] = sin(delta * 2); + } else if (nwh > 4) { + makeipt(nw, ip); + w[2] = 0.5 / cos(delta * 2); + w[3] = 0.5 / cos(delta * 6); + for (j = 4; j < nwh; j += 4) { + w[j] = cos(delta * j); + w[j + 1] = sin(delta * j); + w[j + 2] = cos(3 * delta * j); + w[j + 3] = -sin(3 * delta * j); + } + } + nw0 = 0; + while (nwh > 2) { + nw1 = nw0 + nwh; + nwh >>= 1; + w[nw1] = 1; + w[nw1 + 1] = wn4r; + if (nwh == 4) { + wk1r = w[nw0 + 4]; + wk1i = w[nw0 + 5]; + w[nw1 + 2] = wk1r; + w[nw1 + 3] = wk1i; + } else if (nwh > 4) { + wk1r = w[nw0 + 4]; + wk3r = w[nw0 + 6]; + w[nw1 + 2] = 0.5 / wk1r; + w[nw1 + 3] = 0.5 / wk3r; + for (j = 4; j < nwh; j += 4) { + wk1r = w[nw0 + 2 * j]; + wk1i = w[nw0 + 2 * j + 1]; + wk3r = w[nw0 + 2 * j + 2]; + wk3i = w[nw0 + 2 * j + 3]; + w[nw1 + j] = wk1r; + w[nw1 + j + 1] = wk1i; + w[nw1 + j + 2] = wk3r; + w[nw1 + j + 3] = wk3i; + } + } + nw0 = nw1; + } + } +} + + +void makeipt(int nw, int *ip) +{ + int j, l, m, m2, p, q; + + ip[2] = 0; + ip[3] = 16; + m = 2; + for (l = nw; l > 32; l >>= 2) { + m2 = m << 1; + q = m2 << 3; + for (j = m; j < m2; j++) { + p = ip[j] << 2; + ip[m + j] = p; + ip[m2 + j] = p + q; + } + m = m2; + } +} + + +void makect(int nc, int *ip, double *c) +{ + int j, nch; + double delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atan(1.0) / nch; + c[0] = cos(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cos(delta * j); + c[nc - j] = 0.5 * sin(delta * j); + } + } +} + + +/* -------- child routines -------- */ + + +#ifdef USE_CDFT_PTHREADS +#define USE_CDFT_THREADS +#ifndef CDFT_THREADS_BEGIN_N +#define CDFT_THREADS_BEGIN_N 8192 +#endif +#ifndef CDFT_4THREADS_BEGIN_N +#define CDFT_4THREADS_BEGIN_N 65536 +#endif +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#define cdft_thread_t pthread_t +#define cdft_thread_create(thp,func,argp) { \ + if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#define cdft_thread_wait(th) { \ + if (pthread_join(th, NULL) != 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#endif /* USE_CDFT_PTHREADS */ + + +#ifdef USE_CDFT_WINTHREADS +#define USE_CDFT_THREADS +#ifndef CDFT_THREADS_BEGIN_N +#define CDFT_THREADS_BEGIN_N 32768 +#endif +#ifndef CDFT_4THREADS_BEGIN_N +#define CDFT_4THREADS_BEGIN_N 524288 +#endif +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#define cdft_thread_t HANDLE +#define cdft_thread_create(thp,func,argp) { \ + DWORD thid; \ + *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \ + if (*(thp) == 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#define cdft_thread_wait(th) { \ + WaitForSingleObject(th, INFINITE); \ + CloseHandle(th); \ +} +#endif /* USE_CDFT_WINTHREADS */ + + +void cftfsub(int n, double *a, int *ip, int nw, double *w) +{ + void bitrv2(int n, int *ip, double *a); + void bitrv216(double *a); + void bitrv208(double *a); + void cftf1st(int n, double *a, double *w); + void cftrec4(int n, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftfx41(int n, double *a, int nw, double *w); + void cftf161(double *a, double *w); + void cftf081(double *a, double *w); + void cftf040(double *a); + void cftx020(double *a); +#ifdef USE_CDFT_THREADS + void cftrec4_th(int n, double *a, int nw, double *w); +#endif /* USE_CDFT_THREADS */ + + if (n > 8) { + if (n > 32) { + cftf1st(n, a, &w[nw - (n >> 2)]); +#ifdef USE_CDFT_THREADS + if (n > CDFT_THREADS_BEGIN_N) { + cftrec4_th(n, a, nw, w); + } else +#endif /* USE_CDFT_THREADS */ + if (n > 512) { + cftrec4(n, a, nw, w); + } else if (n > 128) { + cftleaf(n, 1, a, nw, w); + } else { + cftfx41(n, a, nw, w); + } + bitrv2(n, ip, a); + } else if (n == 32) { + cftf161(a, &w[nw - 8]); + bitrv216(a); + } else { + cftf081(a, w); + bitrv208(a); + } + } else if (n == 8) { + cftf040(a); + } else if (n == 4) { + cftx020(a); + } +} + + +void cftbsub(int n, double *a, int *ip, int nw, double *w) +{ + void bitrv2conj(int n, int *ip, double *a); + void bitrv216neg(double *a); + void bitrv208neg(double *a); + void cftb1st(int n, double *a, double *w); + void cftrec4(int n, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftfx41(int n, double *a, int nw, double *w); + void cftf161(double *a, double *w); + void cftf081(double *a, double *w); + void cftb040(double *a); + void cftx020(double *a); +#ifdef USE_CDFT_THREADS + void cftrec4_th(int n, double *a, int nw, double *w); +#endif /* USE_CDFT_THREADS */ + + if (n > 8) { + if (n > 32) { + cftb1st(n, a, &w[nw - (n >> 2)]); +#ifdef USE_CDFT_THREADS + if (n > CDFT_THREADS_BEGIN_N) { + cftrec4_th(n, a, nw, w); + } else +#endif /* USE_CDFT_THREADS */ + if (n > 512) { + cftrec4(n, a, nw, w); + } else if (n > 128) { + cftleaf(n, 1, a, nw, w); + } else { + cftfx41(n, a, nw, w); + } + bitrv2conj(n, ip, a); + } else if (n == 32) { + cftf161(a, &w[nw - 8]); + bitrv216neg(a); + } else { + cftf081(a, w); + bitrv208neg(a); + } + } else if (n == 8) { + cftb040(a); + } else if (n == 4) { + cftx020(a); + } +} + + +void bitrv2(int n, int *ip, double *a) +{ + int j, j1, k, k1, l, m, nh, nm; + double xr, xi, yr, yi; + + m = 1; + for (l = n >> 2; l > 8; l >>= 2) { + m <<= 1; + } + nh = n >> 1; + nm = 4 * m; + if (l == 8) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + 2 * ip[m + k]; + k1 = 4 * k + 2 * ip[m + j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + 2 * ip[m + k]; + j1 = k1 + 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= 2; + k1 -= nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh + 2; + k1 += nh + 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh - nm; + k1 += 2 * nm - 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + ip[m + k]; + k1 = 4 * k + ip[m + j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + ip[m + k]; + j1 = k1 + 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } +} + + +void bitrv2conj(int n, int *ip, double *a) +{ + int j, j1, k, k1, l, m, nh, nm; + double xr, xi, yr, yi; + + m = 1; + for (l = n >> 2; l > 8; l >>= 2) { + m <<= 1; + } + nh = n >> 1; + nm = 4 * m; + if (l == 8) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + 2 * ip[m + k]; + k1 = 4 * k + 2 * ip[m + j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + 2 * ip[m + k]; + j1 = k1 + 2; + k1 += nh; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= 2; + k1 -= nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh + 2; + k1 += nh + 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh - nm; + k1 += 2 * nm - 2; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + } + } else { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + ip[m + k]; + k1 = 4 * k + ip[m + j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + ip[m + k]; + j1 = k1 + 2; + k1 += nh; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + j1 += nm; + k1 += nm; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + } + } +} + + +void bitrv216(double *a) +{ + double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, + x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x7r = a[14]; + x7i = a[15]; + x8r = a[16]; + x8i = a[17]; + x10r = a[20]; + x10i = a[21]; + x11r = a[22]; + x11i = a[23]; + x12r = a[24]; + x12i = a[25]; + x13r = a[26]; + x13i = a[27]; + x14r = a[28]; + x14i = a[29]; + a[2] = x8r; + a[3] = x8i; + a[4] = x4r; + a[5] = x4i; + a[6] = x12r; + a[7] = x12i; + a[8] = x2r; + a[9] = x2i; + a[10] = x10r; + a[11] = x10i; + a[14] = x14r; + a[15] = x14i; + a[16] = x1r; + a[17] = x1i; + a[20] = x5r; + a[21] = x5i; + a[22] = x13r; + a[23] = x13i; + a[24] = x3r; + a[25] = x3i; + a[26] = x11r; + a[27] = x11i; + a[28] = x7r; + a[29] = x7i; +} + + +void bitrv216neg(double *a) +{ + double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, + x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, + x13r, x13i, x14r, x14i, x15r, x15i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x6r = a[12]; + x6i = a[13]; + x7r = a[14]; + x7i = a[15]; + x8r = a[16]; + x8i = a[17]; + x9r = a[18]; + x9i = a[19]; + x10r = a[20]; + x10i = a[21]; + x11r = a[22]; + x11i = a[23]; + x12r = a[24]; + x12i = a[25]; + x13r = a[26]; + x13i = a[27]; + x14r = a[28]; + x14i = a[29]; + x15r = a[30]; + x15i = a[31]; + a[2] = x15r; + a[3] = x15i; + a[4] = x7r; + a[5] = x7i; + a[6] = x11r; + a[7] = x11i; + a[8] = x3r; + a[9] = x3i; + a[10] = x13r; + a[11] = x13i; + a[12] = x5r; + a[13] = x5i; + a[14] = x9r; + a[15] = x9i; + a[16] = x1r; + a[17] = x1i; + a[18] = x14r; + a[19] = x14i; + a[20] = x6r; + a[21] = x6i; + a[22] = x10r; + a[23] = x10i; + a[24] = x2r; + a[25] = x2i; + a[26] = x12r; + a[27] = x12i; + a[28] = x4r; + a[29] = x4i; + a[30] = x8r; + a[31] = x8i; +} + + +void bitrv208(double *a) +{ + double x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i; + + x1r = a[2]; + x1i = a[3]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x6r = a[12]; + x6i = a[13]; + a[2] = x4r; + a[3] = x4i; + a[6] = x6r; + a[7] = x6i; + a[8] = x1r; + a[9] = x1i; + a[12] = x3r; + a[13] = x3i; +} + + +void bitrv208neg(double *a) +{ + double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x6r, x6i, x7r, x7i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x6r = a[12]; + x6i = a[13]; + x7r = a[14]; + x7i = a[15]; + a[2] = x7r; + a[3] = x7i; + a[4] = x3r; + a[5] = x3i; + a[6] = x5r; + a[7] = x5i; + a[8] = x1r; + a[9] = x1i; + a[10] = x6r; + a[11] = x6i; + a[12] = x2r; + a[13] = x2i; + a[14] = x4r; + a[15] = x4i; +} + + +void cftf1st(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, + wd1r, wd1i, wd3r, wd3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = a[1] + a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = a[1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j2] = x1r - x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + csc1 = w[2]; + csc3 = w[3]; + wd1r = 1; + wd1i = 0; + wd3r = 1; + wd3i = 0; + k = 0; + for (j = 2; j < mh - 2; j += 4) { + k += 4; + wk1r = csc1 * (wd1r + w[k]); + wk1i = csc1 * (wd1i + w[k + 1]); + wk3r = csc3 * (wd3r + w[k + 2]); + wk3i = csc3 * (wd3i + w[k + 3]); + wd1r = w[k]; + wd1i = w[k + 1]; + wd3r = w[k + 2]; + wd3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = a[j + 1] + a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = a[j + 1] - a[j2 + 1]; + y0r = a[j + 2] + a[j2 + 2]; + y0i = a[j + 3] + a[j2 + 3]; + y1r = a[j + 2] - a[j2 + 2]; + y1i = a[j + 3] - a[j2 + 3]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 + 2] + a[j3 + 2]; + y2i = a[j1 + 3] + a[j3 + 3]; + y3r = a[j1 + 2] - a[j3 + 2]; + y3i = a[j1 + 3] - a[j3 + 3]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j + 2] = y0r + y2r; + a[j + 3] = y0i + y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j1 + 2] = y0r - y2r; + a[j1 + 3] = y0i - y2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r - y3i; + x0i = y1i + y3r; + a[j2 + 2] = wd1r * x0r - wd1i * x0i; + a[j2 + 3] = wd1r * x0i + wd1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + x0r = y1r + y3i; + x0i = y1i - y3r; + a[j3 + 2] = wd3r * x0r + wd3i * x0i; + a[j3 + 3] = wd3r * x0i - wd3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + y0r = a[j0 - 2] + a[j2 - 2]; + y0i = a[j0 - 1] + a[j2 - 1]; + y1r = a[j0 - 2] - a[j2 - 2]; + y1i = a[j0 - 1] - a[j2 - 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 - 2] + a[j3 - 2]; + y2i = a[j1 - 1] + a[j3 - 1]; + y3r = a[j1 - 2] - a[j3 - 2]; + y3i = a[j1 - 1] - a[j3 - 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j0 - 2] = y0r + y2r; + a[j0 - 1] = y0i + y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j1 - 2] = y0r - y2r; + a[j1 - 1] = y0i - y2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = y1r - y3i; + x0i = y1i + y3r; + a[j2 - 2] = wd1i * x0r - wd1r * x0i; + a[j2 - 1] = wd1i * x0i + wd1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + x0r = y1r + y3i; + x0i = y1i - y3r; + a[j3 - 2] = wd3i * x0r + wd3r * x0i; + a[j3 - 1] = wd3i * x0i - wd3r * x0r; + } + wk1r = csc1 * (wd1r + wn4r); + wk1i = csc1 * (wd1i + wn4r); + wk3r = csc3 * (wd3r - wn4r); + wk3i = csc3 * (wd3i - wn4r); + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0 - 2] + a[j2 - 2]; + x0i = a[j0 - 1] + a[j2 - 1]; + x1r = a[j0 - 2] - a[j2 - 2]; + x1i = a[j0 - 1] - a[j2 - 1]; + x2r = a[j1 - 2] + a[j3 - 2]; + x2i = a[j1 - 1] + a[j3 - 1]; + x3r = a[j1 - 2] - a[j3 - 2]; + x3i = a[j1 - 1] - a[j3 - 1]; + a[j0 - 2] = x0r + x2r; + a[j0 - 1] = x0i + x2i; + a[j1 - 2] = x0r - x2r; + a[j1 - 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2 - 2] = wk1r * x0r - wk1i * x0i; + a[j2 - 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 - 2] = wk3r * x0r + wk3i * x0i; + a[j3 - 1] = wk3r * x0i - wk3i * x0r; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); + x0r = a[j0 + 2] + a[j2 + 2]; + x0i = a[j0 + 3] + a[j2 + 3]; + x1r = a[j0 + 2] - a[j2 + 2]; + x1i = a[j0 + 3] - a[j2 + 3]; + x2r = a[j1 + 2] + a[j3 + 2]; + x2i = a[j1 + 3] + a[j3 + 3]; + x3r = a[j1 + 2] - a[j3 + 2]; + x3i = a[j1 + 3] - a[j3 + 3]; + a[j0 + 2] = x0r + x2r; + a[j0 + 3] = x0i + x2i; + a[j1 + 2] = x0r - x2r; + a[j1 + 3] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2 + 2] = wk1i * x0r - wk1r * x0i; + a[j2 + 3] = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 + 2] = wk3i * x0r + wk3r * x0i; + a[j3 + 3] = wk3i * x0i - wk3r * x0r; +} + + +void cftb1st(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, + wd1r, wd1i, wd3r, wd3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = -a[1] - a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = -a[1] + a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i - x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j2] = x1r + x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r - x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + csc1 = w[2]; + csc3 = w[3]; + wd1r = 1; + wd1i = 0; + wd3r = 1; + wd3i = 0; + k = 0; + for (j = 2; j < mh - 2; j += 4) { + k += 4; + wk1r = csc1 * (wd1r + w[k]); + wk1i = csc1 * (wd1i + w[k + 1]); + wk3r = csc3 * (wd3r + w[k + 2]); + wk3i = csc3 * (wd3i + w[k + 3]); + wd1r = w[k]; + wd1i = w[k + 1]; + wd3r = w[k + 2]; + wd3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = -a[j + 1] - a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = -a[j + 1] + a[j2 + 1]; + y0r = a[j + 2] + a[j2 + 2]; + y0i = -a[j + 3] - a[j2 + 3]; + y1r = a[j + 2] - a[j2 + 2]; + y1i = -a[j + 3] + a[j2 + 3]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 + 2] + a[j3 + 2]; + y2i = a[j1 + 3] + a[j3 + 3]; + y3r = a[j1 + 2] - a[j3 + 2]; + y3i = a[j1 + 3] - a[j3 + 3]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j + 2] = y0r + y2r; + a[j + 3] = y0i - y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j1 + 2] = y0r - y2r; + a[j1 + 3] = y0i + y2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r + y3i; + x0i = y1i + y3r; + a[j2 + 2] = wd1r * x0r - wd1i * x0i; + a[j2 + 3] = wd1r * x0i + wd1i * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + x0r = y1r - y3i; + x0i = y1i - y3r; + a[j3 + 2] = wd3r * x0r + wd3i * x0i; + a[j3 + 3] = wd3r * x0i - wd3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = -a[j0 + 1] - a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = -a[j0 + 1] + a[j2 + 1]; + y0r = a[j0 - 2] + a[j2 - 2]; + y0i = -a[j0 - 1] - a[j2 - 1]; + y1r = a[j0 - 2] - a[j2 - 2]; + y1i = -a[j0 - 1] + a[j2 - 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 - 2] + a[j3 - 2]; + y2i = a[j1 - 1] + a[j3 - 1]; + y3r = a[j1 - 2] - a[j3 - 2]; + y3i = a[j1 - 1] - a[j3 - 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i - x2i; + a[j0 - 2] = y0r + y2r; + a[j0 - 1] = y0i - y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j1 - 2] = y0r - y2r; + a[j1 - 1] = y0i + y2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = y1r + y3i; + x0i = y1i + y3r; + a[j2 - 2] = wd1i * x0r - wd1r * x0i; + a[j2 - 1] = wd1i * x0i + wd1r * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + x0r = y1r - y3i; + x0i = y1i - y3r; + a[j3 - 2] = wd3i * x0r + wd3r * x0i; + a[j3 - 1] = wd3i * x0i - wd3r * x0r; + } + wk1r = csc1 * (wd1r + wn4r); + wk1i = csc1 * (wd1i + wn4r); + wk3r = csc3 * (wd3r - wn4r); + wk3i = csc3 * (wd3i - wn4r); + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0 - 2] + a[j2 - 2]; + x0i = -a[j0 - 1] - a[j2 - 1]; + x1r = a[j0 - 2] - a[j2 - 2]; + x1i = -a[j0 - 1] + a[j2 - 1]; + x2r = a[j1 - 2] + a[j3 - 2]; + x2i = a[j1 - 1] + a[j3 - 1]; + x3r = a[j1 - 2] - a[j3 - 2]; + x3i = a[j1 - 1] - a[j3 - 1]; + a[j0 - 2] = x0r + x2r; + a[j0 - 1] = x0i - x2i; + a[j1 - 2] = x0r - x2r; + a[j1 - 1] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2 - 2] = wk1r * x0r - wk1i * x0i; + a[j2 - 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3 - 2] = wk3r * x0r + wk3i * x0i; + a[j3 - 1] = wk3r * x0i - wk3i * x0r; + x0r = a[j0] + a[j2]; + x0i = -a[j0 + 1] - a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = -a[j0 + 1] + a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i - x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); + x0r = a[j0 + 2] + a[j2 + 2]; + x0i = -a[j0 + 3] - a[j2 + 3]; + x1r = a[j0 + 2] - a[j2 + 2]; + x1i = -a[j0 + 3] + a[j2 + 3]; + x2r = a[j1 + 2] + a[j3 + 2]; + x2i = a[j1 + 3] + a[j3 + 3]; + x3r = a[j1 + 2] - a[j3 + 2]; + x3i = a[j1 + 3] - a[j3 + 3]; + a[j0 + 2] = x0r + x2r; + a[j0 + 3] = x0i - x2i; + a[j1 + 2] = x0r - x2r; + a[j1 + 3] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2 + 2] = wk1i * x0r - wk1r * x0i; + a[j2 + 3] = wk1i * x0i + wk1r * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3 + 2] = wk3i * x0r + wk3r * x0i; + a[j3 + 3] = wk3i * x0i - wk3r * x0r; +} + + +#ifdef USE_CDFT_THREADS +struct cdft_arg_st { + int n0; + int n; + double *a; + int nw; + double *w; +}; +typedef struct cdft_arg_st cdft_arg_t; + + +void cftrec4_th(int n, double *a, int nw, double *w) +{ + void *cftrec1_th(void *p); + void *cftrec2_th(void *p); + int i, idiv4, m, nthread; + cdft_thread_t th[4]; + cdft_arg_t ag[4]; + + nthread = 2; + idiv4 = 0; + m = n >> 1; + if (n > CDFT_4THREADS_BEGIN_N) { + nthread = 4; + idiv4 = 1; + m >>= 1; + } + for (i = 0; i < nthread; i++) { + ag[i].n0 = n; + ag[i].n = m; + ag[i].a = &a[i * m]; + ag[i].nw = nw; + ag[i].w = w; + if (i != idiv4) { + cdft_thread_create(&th[i], cftrec1_th, &ag[i]); + } else { + cdft_thread_create(&th[i], cftrec2_th, &ag[i]); + } + } + for (i = 0; i < nthread; i++) { + cdft_thread_wait(th[i]); + } +} + + +void *cftrec1_th(void *p) +{ + int cfttree(int n, int j, int k, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftmdl1(int n, double *a, double *w); + int isplt, j, k, m, n, n0, nw; + double *a, *w; + + n0 = ((cdft_arg_t *) p)->n0; + n = ((cdft_arg_t *) p)->n; + a = ((cdft_arg_t *) p)->a; + nw = ((cdft_arg_t *) p)->nw; + w = ((cdft_arg_t *) p)->w; + m = n0; + while (m > 512) { + m >>= 2; + cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); + } + cftleaf(m, 1, &a[n - m], nw, w); + k = 0; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } + return (void *) 0; +} + + +void *cftrec2_th(void *p) +{ + int cfttree(int n, int j, int k, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftmdl2(int n, double *a, double *w); + int isplt, j, k, m, n, n0, nw; + double *a, *w; + + n0 = ((cdft_arg_t *) p)->n0; + n = ((cdft_arg_t *) p)->n; + a = ((cdft_arg_t *) p)->a; + nw = ((cdft_arg_t *) p)->nw; + w = ((cdft_arg_t *) p)->w; + k = 1; + m = n0; + while (m > 512) { + m >>= 2; + k <<= 2; + cftmdl2(m, &a[n - m], &w[nw - m]); + } + cftleaf(m, 0, &a[n - m], nw, w); + k >>= 1; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } + return (void *) 0; +} +#endif /* USE_CDFT_THREADS */ + + +void cftrec4(int n, double *a, int nw, double *w) +{ + int cfttree(int n, int j, int k, double *a, int nw, double *w); + void cftleaf(int n, int isplt, double *a, int nw, double *w); + void cftmdl1(int n, double *a, double *w); + int isplt, j, k, m; + + m = n; + while (m > 512) { + m >>= 2; + cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); + } + cftleaf(m, 1, &a[n - m], nw, w); + k = 0; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } +} + + +int cfttree(int n, int j, int k, double *a, int nw, double *w) +{ + void cftmdl1(int n, double *a, double *w); + void cftmdl2(int n, double *a, double *w); + int i, isplt, m; + + if ((k & 3) != 0) { + isplt = k & 1; + if (isplt != 0) { + cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]); + } else { + cftmdl2(n, &a[j - n], &w[nw - n]); + } + } else { + m = n; + for (i = k; (i & 3) == 0; i >>= 2) { + m <<= 2; + } + isplt = i & 1; + if (isplt != 0) { + while (m > 128) { + cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]); + m >>= 2; + } + } else { + while (m > 128) { + cftmdl2(m, &a[j - m], &w[nw - m]); + m >>= 2; + } + } + } + return isplt; +} + + +void cftleaf(int n, int isplt, double *a, int nw, double *w) +{ + void cftmdl1(int n, double *a, double *w); + void cftmdl2(int n, double *a, double *w); + void cftf161(double *a, double *w); + void cftf162(double *a, double *w); + void cftf081(double *a, double *w); + void cftf082(double *a, double *w); + + if (n == 512) { + cftmdl1(128, a, &w[nw - 64]); + cftf161(a, &w[nw - 8]); + cftf162(&a[32], &w[nw - 32]); + cftf161(&a[64], &w[nw - 8]); + cftf161(&a[96], &w[nw - 8]); + cftmdl2(128, &a[128], &w[nw - 128]); + cftf161(&a[128], &w[nw - 8]); + cftf162(&a[160], &w[nw - 32]); + cftf161(&a[192], &w[nw - 8]); + cftf162(&a[224], &w[nw - 32]); + cftmdl1(128, &a[256], &w[nw - 64]); + cftf161(&a[256], &w[nw - 8]); + cftf162(&a[288], &w[nw - 32]); + cftf161(&a[320], &w[nw - 8]); + cftf161(&a[352], &w[nw - 8]); + if (isplt != 0) { + cftmdl1(128, &a[384], &w[nw - 64]); + cftf161(&a[480], &w[nw - 8]); + } else { + cftmdl2(128, &a[384], &w[nw - 128]); + cftf162(&a[480], &w[nw - 32]); + } + cftf161(&a[384], &w[nw - 8]); + cftf162(&a[416], &w[nw - 32]); + cftf161(&a[448], &w[nw - 8]); + } else { + cftmdl1(64, a, &w[nw - 32]); + cftf081(a, &w[nw - 8]); + cftf082(&a[16], &w[nw - 8]); + cftf081(&a[32], &w[nw - 8]); + cftf081(&a[48], &w[nw - 8]); + cftmdl2(64, &a[64], &w[nw - 64]); + cftf081(&a[64], &w[nw - 8]); + cftf082(&a[80], &w[nw - 8]); + cftf081(&a[96], &w[nw - 8]); + cftf082(&a[112], &w[nw - 8]); + cftmdl1(64, &a[128], &w[nw - 32]); + cftf081(&a[128], &w[nw - 8]); + cftf082(&a[144], &w[nw - 8]); + cftf081(&a[160], &w[nw - 8]); + cftf081(&a[176], &w[nw - 8]); + if (isplt != 0) { + cftmdl1(64, &a[192], &w[nw - 32]); + cftf081(&a[240], &w[nw - 8]); + } else { + cftmdl2(64, &a[192], &w[nw - 64]); + cftf082(&a[240], &w[nw - 8]); + } + cftf081(&a[192], &w[nw - 8]); + cftf082(&a[208], &w[nw - 8]); + cftf081(&a[224], &w[nw - 8]); + } +} + + +void cftmdl1(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + double wn4r, wk1r, wk1i, wk3r, wk3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = a[1] + a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = a[1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j2] = x1r - x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + k = 0; + for (j = 2; j < mh; j += 2) { + k += 4; + wk1r = w[k]; + wk1i = w[k + 1]; + wk3r = w[k + 2]; + wk3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = a[j + 1] + a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = a[j + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + } + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); +} + + +void cftmdl2(int n, double *a, double *w) +{ + int j, j0, j1, j2, j3, k, kr, m, mh; + double wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i; + + mh = n >> 3; + m = 2 * mh; + wn4r = w[1]; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] - a[j2 + 1]; + x0i = a[1] + a[j2]; + x1r = a[0] + a[j2 + 1]; + x1i = a[1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wn4r * (x2r - x2i); + y0i = wn4r * (x2i + x2r); + a[0] = x0r + y0r; + a[1] = x0i + y0i; + a[j1] = x0r - y0r; + a[j1 + 1] = x0i - y0i; + y0r = wn4r * (x3r - x3i); + y0i = wn4r * (x3i + x3r); + a[j2] = x1r - y0i; + a[j2 + 1] = x1i + y0r; + a[j3] = x1r + y0i; + a[j3 + 1] = x1i - y0r; + k = 0; + kr = 2 * m; + for (j = 2; j < mh; j += 2) { + k += 4; + wk1r = w[k]; + wk1i = w[k + 1]; + wk3r = w[k + 2]; + wk3i = w[k + 3]; + kr -= 4; + wd1i = w[kr]; + wd1r = w[kr + 1]; + wd3i = w[kr + 2]; + wd3r = w[kr + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] - a[j2 + 1]; + x0i = a[j + 1] + a[j2]; + x1r = a[j] + a[j2 + 1]; + x1i = a[j + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wk1r * x0r - wk1i * x0i; + y0i = wk1r * x0i + wk1i * x0r; + y2r = wd1r * x2r - wd1i * x2i; + y2i = wd1r * x2i + wd1i * x2r; + a[j] = y0r + y2r; + a[j + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wk3r * x1r + wk3i * x1i; + y0i = wk3r * x1i - wk3i * x1r; + y2r = wd3r * x3r + wd3i * x3i; + y2i = wd3r * x3i - wd3i * x3r; + a[j2] = y0r + y2r; + a[j2 + 1] = y0i + y2i; + a[j3] = y0r - y2r; + a[j3 + 1] = y0i - y2i; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] - a[j2 + 1]; + x0i = a[j0 + 1] + a[j2]; + x1r = a[j0] + a[j2 + 1]; + x1i = a[j0 + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wd1i * x0r - wd1r * x0i; + y0i = wd1i * x0i + wd1r * x0r; + y2r = wk1i * x2r - wk1r * x2i; + y2i = wk1i * x2i + wk1r * x2r; + a[j0] = y0r + y2r; + a[j0 + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wd3i * x1r + wd3r * x1i; + y0i = wd3i * x1i - wd3r * x1r; + y2r = wk3i * x3r + wk3r * x3i; + y2i = wk3i * x3i - wk3r * x3r; + a[j2] = y0r + y2r; + a[j2 + 1] = y0i + y2i; + a[j3] = y0r - y2r; + a[j3 + 1] = y0i - y2i; + } + wk1r = w[m]; + wk1i = w[m + 1]; + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] - a[j2 + 1]; + x0i = a[j0 + 1] + a[j2]; + x1r = a[j0] + a[j2 + 1]; + x1i = a[j0 + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wk1r * x0r - wk1i * x0i; + y0i = wk1r * x0i + wk1i * x0r; + y2r = wk1i * x2r - wk1r * x2i; + y2i = wk1i * x2i + wk1r * x2r; + a[j0] = y0r + y2r; + a[j0 + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wk1i * x1r - wk1r * x1i; + y0i = wk1i * x1i + wk1r * x1r; + y2r = wk1r * x3r - wk1i * x3i; + y2i = wk1r * x3i + wk1i * x3r; + a[j2] = y0r - y2r; + a[j2 + 1] = y0i - y2i; + a[j3] = y0r + y2r; + a[j3 + 1] = y0i + y2i; +} + + +void cftfx41(int n, double *a, int nw, double *w) +{ + void cftf161(double *a, double *w); + void cftf162(double *a, double *w); + void cftf081(double *a, double *w); + void cftf082(double *a, double *w); + + if (n == 128) { + cftf161(a, &w[nw - 8]); + cftf162(&a[32], &w[nw - 32]); + cftf161(&a[64], &w[nw - 8]); + cftf161(&a[96], &w[nw - 8]); + } else { + cftf081(a, &w[nw - 8]); + cftf082(&a[16], &w[nw - 8]); + cftf081(&a[32], &w[nw - 8]); + cftf081(&a[48], &w[nw - 8]); + } +} + + +void cftf161(double *a, double *w) +{ + double wn4r, wk1r, wk1i, + x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, + y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, + y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; + + wn4r = w[1]; + wk1r = w[2]; + wk1i = w[3]; + x0r = a[0] + a[16]; + x0i = a[1] + a[17]; + x1r = a[0] - a[16]; + x1i = a[1] - a[17]; + x2r = a[8] + a[24]; + x2i = a[9] + a[25]; + x3r = a[8] - a[24]; + x3i = a[9] - a[25]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y4r = x0r - x2r; + y4i = x0i - x2i; + y8r = x1r - x3i; + y8i = x1i + x3r; + y12r = x1r + x3i; + y12i = x1i - x3r; + x0r = a[2] + a[18]; + x0i = a[3] + a[19]; + x1r = a[2] - a[18]; + x1i = a[3] - a[19]; + x2r = a[10] + a[26]; + x2i = a[11] + a[27]; + x3r = a[10] - a[26]; + x3i = a[11] - a[27]; + y1r = x0r + x2r; + y1i = x0i + x2i; + y5r = x0r - x2r; + y5i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y9r = wk1r * x0r - wk1i * x0i; + y9i = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + y13r = wk1i * x0r - wk1r * x0i; + y13i = wk1i * x0i + wk1r * x0r; + x0r = a[4] + a[20]; + x0i = a[5] + a[21]; + x1r = a[4] - a[20]; + x1i = a[5] - a[21]; + x2r = a[12] + a[28]; + x2i = a[13] + a[29]; + x3r = a[12] - a[28]; + x3i = a[13] - a[29]; + y2r = x0r + x2r; + y2i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y10r = wn4r * (x0r - x0i); + y10i = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + y14r = wn4r * (x0r + x0i); + y14i = wn4r * (x0i - x0r); + x0r = a[6] + a[22]; + x0i = a[7] + a[23]; + x1r = a[6] - a[22]; + x1i = a[7] - a[23]; + x2r = a[14] + a[30]; + x2i = a[15] + a[31]; + x3r = a[14] - a[30]; + x3i = a[15] - a[31]; + y3r = x0r + x2r; + y3i = x0i + x2i; + y7r = x0r - x2r; + y7i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y11r = wk1i * x0r - wk1r * x0i; + y11i = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + y15r = wk1r * x0r - wk1i * x0i; + y15i = wk1r * x0i + wk1i * x0r; + x0r = y12r - y14r; + x0i = y12i - y14i; + x1r = y12r + y14r; + x1i = y12i + y14i; + x2r = y13r - y15r; + x2i = y13i - y15i; + x3r = y13r + y15r; + x3i = y13i + y15i; + a[24] = x0r + x2r; + a[25] = x0i + x2i; + a[26] = x0r - x2r; + a[27] = x0i - x2i; + a[28] = x1r - x3i; + a[29] = x1i + x3r; + a[30] = x1r + x3i; + a[31] = x1i - x3r; + x0r = y8r + y10r; + x0i = y8i + y10i; + x1r = y8r - y10r; + x1i = y8i - y10i; + x2r = y9r + y11r; + x2i = y9i + y11i; + x3r = y9r - y11r; + x3i = y9i - y11i; + a[16] = x0r + x2r; + a[17] = x0i + x2i; + a[18] = x0r - x2r; + a[19] = x0i - x2i; + a[20] = x1r - x3i; + a[21] = x1i + x3r; + a[22] = x1r + x3i; + a[23] = x1i - x3r; + x0r = y5r - y7i; + x0i = y5i + y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + x0r = y5r + y7i; + x0i = y5i - y7r; + x3r = wn4r * (x0r - x0i); + x3i = wn4r * (x0i + x0r); + x0r = y4r - y6i; + x0i = y4i + y6r; + x1r = y4r + y6i; + x1i = y4i - y6r; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[10] = x0r - x2r; + a[11] = x0i - x2i; + a[12] = x1r - x3i; + a[13] = x1i + x3r; + a[14] = x1r + x3i; + a[15] = x1i - x3r; + x0r = y0r + y2r; + x0i = y0i + y2i; + x1r = y0r - y2r; + x1i = y0i - y2i; + x2r = y1r + y3r; + x2i = y1i + y3i; + x3r = y1r - y3r; + x3i = y1i - y3i; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x0r - x2r; + a[3] = x0i - x2i; + a[4] = x1r - x3i; + a[5] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; +} + + +void cftf162(double *a, double *w) +{ + double wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, + x0r, x0i, x1r, x1i, x2r, x2i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, + y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, + y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; + + wn4r = w[1]; + wk1r = w[4]; + wk1i = w[5]; + wk3r = w[6]; + wk3i = -w[7]; + wk2r = w[8]; + wk2i = w[9]; + x1r = a[0] - a[17]; + x1i = a[1] + a[16]; + x0r = a[8] - a[25]; + x0i = a[9] + a[24]; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + y0r = x1r + x2r; + y0i = x1i + x2i; + y4r = x1r - x2r; + y4i = x1i - x2i; + x1r = a[0] + a[17]; + x1i = a[1] - a[16]; + x0r = a[8] + a[25]; + x0i = a[9] - a[24]; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + y8r = x1r - x2i; + y8i = x1i + x2r; + y12r = x1r + x2i; + y12i = x1i - x2r; + x0r = a[2] - a[19]; + x0i = a[3] + a[18]; + x1r = wk1r * x0r - wk1i * x0i; + x1i = wk1r * x0i + wk1i * x0r; + x0r = a[10] - a[27]; + x0i = a[11] + a[26]; + x2r = wk3i * x0r - wk3r * x0i; + x2i = wk3i * x0i + wk3r * x0r; + y1r = x1r + x2r; + y1i = x1i + x2i; + y5r = x1r - x2r; + y5i = x1i - x2i; + x0r = a[2] + a[19]; + x0i = a[3] - a[18]; + x1r = wk3r * x0r - wk3i * x0i; + x1i = wk3r * x0i + wk3i * x0r; + x0r = a[10] + a[27]; + x0i = a[11] - a[26]; + x2r = wk1r * x0r + wk1i * x0i; + x2i = wk1r * x0i - wk1i * x0r; + y9r = x1r - x2r; + y9i = x1i - x2i; + y13r = x1r + x2r; + y13i = x1i + x2i; + x0r = a[4] - a[21]; + x0i = a[5] + a[20]; + x1r = wk2r * x0r - wk2i * x0i; + x1i = wk2r * x0i + wk2i * x0r; + x0r = a[12] - a[29]; + x0i = a[13] + a[28]; + x2r = wk2i * x0r - wk2r * x0i; + x2i = wk2i * x0i + wk2r * x0r; + y2r = x1r + x2r; + y2i = x1i + x2i; + y6r = x1r - x2r; + y6i = x1i - x2i; + x0r = a[4] + a[21]; + x0i = a[5] - a[20]; + x1r = wk2i * x0r - wk2r * x0i; + x1i = wk2i * x0i + wk2r * x0r; + x0r = a[12] + a[29]; + x0i = a[13] - a[28]; + x2r = wk2r * x0r - wk2i * x0i; + x2i = wk2r * x0i + wk2i * x0r; + y10r = x1r - x2r; + y10i = x1i - x2i; + y14r = x1r + x2r; + y14i = x1i + x2i; + x0r = a[6] - a[23]; + x0i = a[7] + a[22]; + x1r = wk3r * x0r - wk3i * x0i; + x1i = wk3r * x0i + wk3i * x0r; + x0r = a[14] - a[31]; + x0i = a[15] + a[30]; + x2r = wk1i * x0r - wk1r * x0i; + x2i = wk1i * x0i + wk1r * x0r; + y3r = x1r + x2r; + y3i = x1i + x2i; + y7r = x1r - x2r; + y7i = x1i - x2i; + x0r = a[6] + a[23]; + x0i = a[7] - a[22]; + x1r = wk1i * x0r + wk1r * x0i; + x1i = wk1i * x0i - wk1r * x0r; + x0r = a[14] + a[31]; + x0i = a[15] - a[30]; + x2r = wk3i * x0r - wk3r * x0i; + x2i = wk3i * x0i + wk3r * x0r; + y11r = x1r + x2r; + y11i = x1i + x2i; + y15r = x1r - x2r; + y15i = x1i - x2i; + x1r = y0r + y2r; + x1i = y0i + y2i; + x2r = y1r + y3r; + x2i = y1i + y3i; + a[0] = x1r + x2r; + a[1] = x1i + x2i; + a[2] = x1r - x2r; + a[3] = x1i - x2i; + x1r = y0r - y2r; + x1i = y0i - y2i; + x2r = y1r - y3r; + x2i = y1i - y3i; + a[4] = x1r - x2i; + a[5] = x1i + x2r; + a[6] = x1r + x2i; + a[7] = x1i - x2r; + x1r = y4r - y6i; + x1i = y4i + y6r; + x0r = y5r - y7i; + x0i = y5i + y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[8] = x1r + x2r; + a[9] = x1i + x2i; + a[10] = x1r - x2r; + a[11] = x1i - x2i; + x1r = y4r + y6i; + x1i = y4i - y6r; + x0r = y5r + y7i; + x0i = y5i - y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[12] = x1r - x2i; + a[13] = x1i + x2r; + a[14] = x1r + x2i; + a[15] = x1i - x2r; + x1r = y8r + y10r; + x1i = y8i + y10i; + x2r = y9r - y11r; + x2i = y9i - y11i; + a[16] = x1r + x2r; + a[17] = x1i + x2i; + a[18] = x1r - x2r; + a[19] = x1i - x2i; + x1r = y8r - y10r; + x1i = y8i - y10i; + x2r = y9r + y11r; + x2i = y9i + y11i; + a[20] = x1r - x2i; + a[21] = x1i + x2r; + a[22] = x1r + x2i; + a[23] = x1i - x2r; + x1r = y12r - y14i; + x1i = y12i + y14r; + x0r = y13r + y15i; + x0i = y13i - y15r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[24] = x1r + x2r; + a[25] = x1i + x2i; + a[26] = x1r - x2r; + a[27] = x1i - x2i; + x1r = y12r + y14i; + x1i = y12i - y14r; + x0r = y13r - y15i; + x0i = y13i + y15r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[28] = x1r - x2i; + a[29] = x1i + x2r; + a[30] = x1r + x2i; + a[31] = x1i - x2r; +} + + +void cftf081(double *a, double *w) +{ + double wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[1]; + x0r = a[0] + a[8]; + x0i = a[1] + a[9]; + x1r = a[0] - a[8]; + x1i = a[1] - a[9]; + x2r = a[4] + a[12]; + x2i = a[5] + a[13]; + x3r = a[4] - a[12]; + x3i = a[5] - a[13]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[2] + a[10]; + x0i = a[3] + a[11]; + x1r = a[2] - a[10]; + x1i = a[3] - a[11]; + x2r = a[6] + a[14]; + x2i = a[7] + a[15]; + x3r = a[6] - a[14]; + x3i = a[7] - a[15]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + a[8] = y1r + y5r; + a[9] = y1i + y5i; + a[10] = y1r - y5r; + a[11] = y1i - y5i; + a[12] = y3r - y7i; + a[13] = y3i + y7r; + a[14] = y3r + y7i; + a[15] = y3i - y7r; + a[0] = y0r + y4r; + a[1] = y0i + y4i; + a[2] = y0r - y4r; + a[3] = y0i - y4i; + a[4] = y2r - y6i; + a[5] = y2i + y6r; + a[6] = y2r + y6i; + a[7] = y2i - y6r; +} + + +void cftf082(double *a, double *w) +{ + double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[1]; + wk1r = w[2]; + wk1i = w[3]; + y0r = a[0] - a[9]; + y0i = a[1] + a[8]; + y1r = a[0] + a[9]; + y1i = a[1] - a[8]; + x0r = a[4] - a[13]; + x0i = a[5] + a[12]; + y2r = wn4r * (x0r - x0i); + y2i = wn4r * (x0i + x0r); + x0r = a[4] + a[13]; + x0i = a[5] - a[12]; + y3r = wn4r * (x0r - x0i); + y3i = wn4r * (x0i + x0r); + x0r = a[2] - a[11]; + x0i = a[3] + a[10]; + y4r = wk1r * x0r - wk1i * x0i; + y4i = wk1r * x0i + wk1i * x0r; + x0r = a[2] + a[11]; + x0i = a[3] - a[10]; + y5r = wk1i * x0r - wk1r * x0i; + y5i = wk1i * x0i + wk1r * x0r; + x0r = a[6] - a[15]; + x0i = a[7] + a[14]; + y6r = wk1i * x0r - wk1r * x0i; + y6i = wk1i * x0i + wk1r * x0r; + x0r = a[6] + a[15]; + x0i = a[7] - a[14]; + y7r = wk1r * x0r - wk1i * x0i; + y7i = wk1r * x0i + wk1i * x0r; + x0r = y0r + y2r; + x0i = y0i + y2i; + x1r = y4r + y6r; + x1i = y4i + y6i; + a[0] = x0r + x1r; + a[1] = x0i + x1i; + a[2] = x0r - x1r; + a[3] = x0i - x1i; + x0r = y0r - y2r; + x0i = y0i - y2i; + x1r = y4r - y6r; + x1i = y4i - y6i; + a[4] = x0r - x1i; + a[5] = x0i + x1r; + a[6] = x0r + x1i; + a[7] = x0i - x1r; + x0r = y1r - y3i; + x0i = y1i + y3r; + x1r = y5r - y7r; + x1i = y5i - y7i; + a[8] = x0r + x1r; + a[9] = x0i + x1i; + a[10] = x0r - x1r; + a[11] = x0i - x1i; + x0r = y1r + y3i; + x0i = y1i - y3r; + x1r = y5r + y7r; + x1i = y5i + y7i; + a[12] = x0r - x1i; + a[13] = x0i + x1r; + a[14] = x0r + x1i; + a[15] = x0i - x1r; +} + + +void cftf040(double *a) +{ + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[4]; + x0i = a[1] + a[5]; + x1r = a[0] - a[4]; + x1i = a[1] - a[5]; + x2r = a[2] + a[6]; + x2i = a[3] + a[7]; + x3r = a[2] - a[6]; + x3i = a[3] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[6] = x1r + x3i; + a[7] = x1i - x3r; +} + + +void cftb040(double *a) +{ + double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[4]; + x0i = a[1] + a[5]; + x1r = a[0] - a[4]; + x1i = a[1] - a[5]; + x2r = a[2] + a[6]; + x2i = a[3] + a[7]; + x3r = a[2] - a[6]; + x3i = a[3] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x1r + x3i; + a[3] = x1i - x3r; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[6] = x1r - x3i; + a[7] = x1i + x3r; +} + + +void cftx020(double *a) +{ + double x0r, x0i; + + x0r = a[0] - a[2]; + x0i = a[1] - a[3]; + a[0] += a[2]; + a[1] += a[3]; + a[2] = x0r; + a[3] = x0i; +} + + +void rftfsub(int n, double *a, int nc, double *c) +{ + int j, k, kk, ks, m; + double wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void rftbsub(int n, double *a, int nc, double *c) +{ + int j, k, kk, ks, m; + double wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void dctsub(int n, double *a, int nc, double *c) +{ + int j, k, kk, ks, m; + double wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[j] - wkr * a[k]; + a[j] = wkr * a[j] + wki * a[k]; + a[k] = xr; + } + a[m] *= c[0]; +} + + +void dstsub(int n, double *a, int nc, double *c) +{ + int j, k, kk, ks, m; + double wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[k] - wkr * a[j]; + a[k] = wkr * a[k] + wki * a[j]; + a[j] = xr; + } + a[m] *= c[0]; +} + diff --git a/plugins/supereq/nsfft-1.00/ooura/pi_fft.c b/plugins/supereq/nsfft-1.00/ooura/pi_fft.c new file mode 100644 index 00000000..c9a76bf8 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/ooura/pi_fft.c @@ -0,0 +1,1616 @@ +/* +---- calculation of PI(= 3.14159...) using FFT ---- + by T.Ooura, ver. LG1.1.2-MP1.5a Sep. 2001. + +This is a test program to estimate the performance of +the FFT routines: fft*g.c. + +Example compilation: + GNU : gcc -O6 -ffast-math pi_fft.c fftsg.c -lm -o pi_fftsg + SUN : cc -fast -xO5 pi_fft.c fft8g.c -lm -o pi_fft8g + Microsoft: cl /O2 /G6 pi_fft.c fft4g.c /Fepi_fft4g.exe + ... + etc. +*/ + +/* Please check the following macros before compiling */ +#ifndef DBL_ERROR_MARGIN +#define DBL_ERROR_MARGIN 0.3 /* must be < 0.5 */ +#endif + + +#include <math.h> +#include <limits.h> +#include <float.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + + +void mp_load_0(int n, int radix, int out[]); +void mp_load_1(int n, int radix, int out[]); +void mp_copy(int n, int radix, int in[], int out[]); +void mp_round(int n, int radix, int m, int inout[]); +int mp_cmp(int n, int radix, int in1[], int in2[]); +void mp_add(int n, int radix, int in1[], int in2[], int out[]); +void mp_sub(int n, int radix, int in1[], int in2[], int out[]); +void mp_imul(int n, int radix, int in1[], int in2, int out[]); +int mp_idiv(int n, int radix, int in1[], int in2, int out[]); +void mp_idiv_2(int n, int radix, int in[], int out[]); +double mp_mul_radix_test(int n, int radix, int nfft, + double tmpfft[], int ip[], double w[]); +void mp_mul(int n, int radix, int in1[], int in2[], int out[], + int tmp[], int nfft, double tmp1fft[], double tmp2fft[], + double tmp3fft[], int ip[], double w[]); +void mp_squ(int n, int radix, int in[], int out[], int tmp[], + int nfft, double tmp1fft[], double tmp2fft[], + int ip[], double w[]); +void mp_mulh(int n, int radix, int in1[], int in2[], int out[], + int nfft, double in1fft[], double outfft[], + int ip[], double w[]); +void mp_squh(int n, int radix, int in[], int out[], + int nfft, double inoutfft[], int ip[], double w[]); +int mp_inv(int n, int radix, int in[], int out[], + int tmp1[], int tmp2[], int nfft, + double tmp1fft[], double tmp2fft[], int ip[], double w[]); +int mp_sqrt(int n, int radix, int in[], int out[], + int tmp1[], int tmp2[], int nfft, + double tmp1fft[], double tmp2fft[], int ip[], double w[]); +void mp_sprintf(int n, int log10_radix, int in[], char out[]); +void mp_sscanf(int n, int log10_radix, char in[], int out[]); +void mp_fprintf(int n, int log10_radix, int in[], FILE *fout); + + +int main() +{ + int nfft, log2_nfft, radix, log10_radix, n, npow, nprc; + double err, d_time, n_op; + int *a, *b, *c, *e, *i1, *i2, *ip; + double *d1, *d2, *d3, *w; + time_t t_1, t_2; + FILE *f_log, *f_out; + + f_log = fopen("pi.log", "w"); + printf("PI calculation to estimate the FFT benchmarks\n"); + fprintf(f_log, "PI calculation to estimate the FFT benchmarks\n"); + printf("length of FFT =?\n"); + scanf("%d", &nfft); + + printf("initializing...\n"); + for (log2_nfft = 1; (1 << log2_nfft) < nfft; log2_nfft++); + nfft = 1 << log2_nfft; + n = nfft + 2; + ip = (int *) malloc((3 + (int) sqrt(0.5 * nfft)) * sizeof(int)); + w = (double *) malloc(nfft / 2 * sizeof(double)); + a = (int *) malloc((n + 2) * sizeof(int)); + b = (int *) malloc((n + 2) * sizeof(int)); + c = (int *) malloc((n + 2) * sizeof(int)); + e = (int *) malloc((n + 2) * sizeof(int)); + i1 = (int *) malloc((n + 2) * sizeof(int)); + i2 = (int *) malloc((n + 2) * sizeof(int)); + d1 = (double *) malloc((nfft + 2) * sizeof(double)); + d2 = (double *) malloc((nfft + 2) * sizeof(double)); + d3 = (double *) malloc((nfft + 2) * sizeof(double)); + if (d3 == NULL) { + printf("Allocation Failure!\n"); + exit(1); + } + ip[0] = 0; + /* ---- radix test ---- */ + log10_radix = 1; + radix = 10; + err = mp_mul_radix_test(n, radix, nfft, d1, ip, w); + err += DBL_EPSILON * (n * radix * radix / 4); + while (100 * err < DBL_ERROR_MARGIN && radix <= INT_MAX / 20) { + err *= 100; + log10_radix++; + radix *= 10; + } + printf("nfft= %d\nradix= %d\nerror_margin= %g\n", nfft, radix, err); + fprintf(f_log, "nfft= %d\nradix= %d\nerror_margin= %g\n", nfft, radix, err); + printf("calculating %d digits of PI...\n", log10_radix * (n - 2)); + fprintf(f_log, "calculating %d digits of PI...\n", log10_radix * (n - 2)); + /* ---- time check ---- */ + time(&t_1); + /* + * ---- a formula based on the AGM (Arithmetic-Geometric Mean) ---- + * c = sqrt(0.125); + * a = 1 + 3 * c; + * b = sqrt(a); + * e = b - 0.625; + * b = 2 * b; + * c = e - c; + * a = a + e; + * npow = 4; + * do { + * npow = 2 * npow; + * e = (a + b) / 2; + * b = sqrt(a * b); + * e = e - b; + * b = 2 * b; + * c = c - e; + * a = e + b; + * } while (e > SQRT_SQRT_EPSILON); + * e = e * e / 4; + * a = a + b; + * pi = (a * a - e - e / 2) / (a * c - e) / npow; + * ---- modification ---- + * This is a modified version of Gauss-Legendre formula + * (by T.Ooura). It is faster than original version. + * ---- reference ---- + * 1. E.Salamin, + * Computation of PI Using Arithmetic-Geometric Mean, + * Mathematics of Computation, Vol.30 1976. + * 2. R.P.Brent, + * Fast Multiple-Precision Evaluation of Elementary Functions, + * J. ACM 23 1976. + * 3. D.Takahasi, Y.Kanada, + * Calculation of PI to 51.5 Billion Decimal Digits on + * Distributed Memoriy Parallel Processors, + * Transactions of Information Processing Society of Japan, + * Vol.39 No.7 1998. + * 4. T.Ooura, + * Improvement of the PI Calculation Algorithm and + * Implementation of Fast Multiple-Precision Computation, + * Information Processing Society of Japan SIG Notes, + * 98-HPC-74, 1998. + */ + /* ---- c = sqrt(0.125) ---- */ + mp_sscanf(n, log10_radix, "0.125", a); + mp_sqrt(n, radix, a, c, i1, i2, nfft, d1, d2, ip, w); + /* ---- a = 1 + 3 * c ---- */ + mp_imul(n, radix, c, 3, e); + mp_sscanf(n, log10_radix, "1", a); + mp_add(n, radix, a, e, a); + /* ---- b = sqrt(a) ---- */ + mp_sqrt(n, radix, a, b, i1, i2, nfft, d1, d2, ip, w); + /* ---- e = b - 0.625 ---- */ + mp_sscanf(n, log10_radix, "0.625", e); + mp_sub(n, radix, b, e, e); + /* ---- b = 2 * b ---- */ + mp_add(n, radix, b, b, b); + /* ---- c = e - c ---- */ + mp_sub(n, radix, e, c, c); + /* ---- a = a + e ---- */ + mp_add(n, radix, a, e, a); + printf("AGM iteration\n"); + fprintf(f_log, "AGM iteration\n"); + npow = 4; + do { + npow *= 2; + /* ---- e = (a + b) / 2 ---- */ + mp_add(n, radix, a, b, e); + mp_idiv_2(n, radix, e, e); + /* ---- b = sqrt(a * b) ---- */ + mp_mul(n, radix, a, b, a, i1, nfft, d1, d2, d3, ip, w); + mp_sqrt(n, radix, a, b, i1, i2, nfft, d1, d2, ip, w); + /* ---- e = e - b ---- */ + mp_sub(n, radix, e, b, e); + /* ---- b = 2 * b ---- */ + mp_add(n, radix, b, b, b); + /* ---- c = c - e ---- */ + mp_sub(n, radix, c, e, c); + /* ---- a = e + b ---- */ + mp_add(n, radix, e, b, a); + /* ---- convergence check ---- */ + nprc = -e[1]; + if (e[0] == 0) { + nprc = n; + } + printf("precision= %d\n", 4 * nprc * log10_radix); + fprintf(f_log, "precision= %d\n", 4 * nprc * log10_radix); + } while (4 * nprc <= n); + /* ---- e = e * e / 4 (half precision) ---- */ + mp_idiv_2(n, radix, e, e); + mp_squh(n, radix, e, e, nfft, d1, ip, w); + /* ---- a = a + b ---- */ + mp_add(n, radix, a, b, a); + /* ---- a = (a * a - e - e / 2) / (a * c - e) / npow ---- */ + mp_mul(n, radix, a, c, c, i1, nfft, d1, d2, d3, ip, w); + mp_sub(n, radix, c, e, c); + mp_inv(n, radix, c, b, i1, i2, nfft, d1, d2, ip, w); + mp_squ(n, radix, a, a, i1, nfft, d1, d2, ip, w); + mp_sub(n, radix, a, e, a); + mp_idiv_2(n, radix, e, e); + mp_sub(n, radix, a, e, a); + mp_mul(n, radix, a, b, a, i1, nfft, d1, d2, d3, ip, w); + mp_idiv(n, radix, a, npow, a); + /* ---- time check ---- */ + time(&t_2); + /* ---- output ---- */ + f_out = fopen("pi.dat", "w"); + printf("writing pi.dat...\n"); + mp_fprintf(n - 1, log10_radix, a, f_out); + fclose(f_out); + free(d3); + free(d2); + free(d1); + free(i2); + free(i1); + free(e); + free(c); + free(b); + free(a); + free(w); + free(ip); + /* ---- benchmark ---- */ + n_op = 50.0 * nfft * log2_nfft * log2_nfft; + printf("floating point operation: %g op.\n", n_op); + fprintf(f_log, "floating point operation: %g op.\n", n_op); + /* ---- difftime ---- */ + d_time = difftime(t_2, t_1); + printf("execution time: %g sec. (real time)\n", d_time); + fprintf(f_log, "execution time: %g sec. (real time)\n", d_time); + fclose(f_log); + return 0; +} + + +/* -------- multiple precision routines -------- */ + + +#include <math.h> +#include <float.h> +#include <stdio.h> + +/* ---- floating point format ---- + data := data[0] * pow(radix, data[1]) * + (data[2] + data[3]/radix + data[4]/radix/radix + ...), + data[0] : sign (1;data>0, -1;data<0, 0;data==0) + data[1] : exponent (0;data==0) + data[2...n+1] : digits + ---- function prototypes ---- + void mp_load_0(int n, int radix, int out[]); + void mp_load_1(int n, int radix, int out[]); + void mp_copy(int n, int radix, int in[], int out[]); + void mp_round(int n, int radix, int m, int inout[]); + int mp_cmp(int n, int radix, int in1[], int in2[]); + void mp_add(int n, int radix, int in1[], int in2[], int out[]); + void mp_sub(int n, int radix, int in1[], int in2[], int out[]); + void mp_imul(int n, int radix, int in1[], int in2, int out[]); + int mp_idiv(int n, int radix, int in1[], int in2, int out[]); + void mp_idiv_2(int n, int radix, int in[], int out[]); + double mp_mul_radix_test(int n, int radix, int nfft, + double tmpfft[], int ip[], double w[]); + void mp_mul(int n, int radix, int in1[], int in2[], int out[], + int tmp[], int nfft, double tmp1fft[], double tmp2fft[], + double tmp3fft[], int ip[], double w[]); + void mp_squ(int n, int radix, int in[], int out[], int tmp[], + int nfft, double tmp1fft[], double tmp2fft[], + int ip[], double w[]); + void mp_mulh(int n, int radix, int in1[], int in2[], int out[], + int nfft, double in1fft[], double outfft[], + int ip[], double w[]); + void mp_squh(int n, int radix, int in[], int out[], + int nfft, double inoutfft[], int ip[], double w[]); + int mp_inv(int n, int radix, int in[], int out[], + int tmp1[], int tmp2[], int nfft, + double tmp1fft[], double tmp2fft[], int ip[], double w[]); + int mp_sqrt(int n, int radix, int in[], int out[], + int tmp1[], int tmp2[], int nfft, + double tmp1fft[], double tmp2fft[], int ip[], double w[]); + void mp_sprintf(int n, int log10_radix, int in[], char out[]); + void mp_sscanf(int n, int log10_radix, char in[], int out[]); + void mp_fprintf(int n, int log10_radix, int in[], FILE *fout); + ---- +*/ + + +/* -------- mp_load routines -------- */ + + +void mp_load_0(int n, int radix, int out[]) +{ + int j; + + for (j = 0; j <= n + 1; j++) { + out[j] = 0; + } +} + + +void mp_load_1(int n, int radix, int out[]) +{ + int j; + + out[0] = 1; + out[1] = 0; + out[2] = 1; + for (j = 3; j <= n + 1; j++) { + out[j] = 0; + } +} + + +void mp_copy(int n, int radix, int in[], int out[]) +{ + int j; + + for (j = 0; j <= n + 1; j++) { + out[j] = in[j]; + } +} + + +void mp_round(int n, int radix, int m, int inout[]) +{ + int j, x; + + if (m < n) { + for (j = n + 1; j > m + 2; j--) { + inout[j] = 0; + } + x = 2 * inout[m + 2]; + inout[m + 2] = 0; + if (x >= radix) { + for (j = m + 1; j >= 2; j--) { + x = inout[j] + 1; + if (x < radix) { + inout[j] = x; + break; + } + inout[j] = 0; + } + if (x >= radix) { + inout[2] = 1; + inout[1]++; + } + } + } +} + + +/* -------- mp_add routines -------- */ + + +int mp_cmp(int n, int radix, int in1[], int in2[]) +{ + int mp_unsgn_cmp(int n, int in1[], int in2[]); + + if (in1[0] > in2[0]) { + return 1; + } else if (in1[0] < in2[0]) { + return -1; + } + return in1[0] * mp_unsgn_cmp(n, &in1[1], &in2[1]); +} + + +void mp_add(int n, int radix, int in1[], int in2[], int out[]) +{ + int mp_unsgn_cmp(int n, int in1[], int in2[]); + int mp_unexp_add(int n, int radix, int expdif, + int in1[], int in2[], int out[]); + int mp_unexp_sub(int n, int radix, int expdif, + int in1[], int in2[], int out[]); + int outsgn, outexp, expdif; + + expdif = in1[1] - in2[1]; + outexp = in1[1]; + if (expdif < 0) { + outexp = in2[1]; + } + outsgn = in1[0] * in2[0]; + if (outsgn >= 0) { + if (outsgn > 0) { + outsgn = in1[0]; + } else { + outsgn = in1[0] + in2[0]; + outexp = in1[1] + in2[1]; + expdif = 0; + } + if (expdif >= 0) { + outexp += mp_unexp_add(n, radix, expdif, + &in1[2], &in2[2], &out[2]); + } else { + outexp += mp_unexp_add(n, radix, -expdif, + &in2[2], &in1[2], &out[2]); + } + } else { + outsgn = mp_unsgn_cmp(n, &in1[1], &in2[1]); + if (outsgn >= 0) { + expdif = mp_unexp_sub(n, radix, expdif, + &in1[2], &in2[2], &out[2]); + } else { + expdif = mp_unexp_sub(n, radix, -expdif, + &in2[2], &in1[2], &out[2]); + } + outexp -= expdif; + outsgn *= in1[0]; + if (expdif == n) { + outsgn = 0; + } + } + if (outsgn == 0) { + outexp = 0; + } + out[0] = outsgn; + out[1] = outexp; +} + + +void mp_sub(int n, int radix, int in1[], int in2[], int out[]) +{ + int mp_unsgn_cmp(int n, int in1[], int in2[]); + int mp_unexp_add(int n, int radix, int expdif, + int in1[], int in2[], int out[]); + int mp_unexp_sub(int n, int radix, int expdif, + int in1[], int in2[], int out[]); + int outsgn, outexp, expdif; + + expdif = in1[1] - in2[1]; + outexp = in1[1]; + if (expdif < 0) { + outexp = in2[1]; + } + outsgn = in1[0] * in2[0]; + if (outsgn <= 0) { + if (outsgn < 0) { + outsgn = in1[0]; + } else { + outsgn = in1[0] - in2[0]; + outexp = in1[1] + in2[1]; + expdif = 0; + } + if (expdif >= 0) { + outexp += mp_unexp_add(n, radix, expdif, + &in1[2], &in2[2], &out[2]); + } else { + outexp += mp_unexp_add(n, radix, -expdif, + &in2[2], &in1[2], &out[2]); + } + } else { + outsgn = mp_unsgn_cmp(n, &in1[1], &in2[1]); + if (outsgn >= 0) { + expdif = mp_unexp_sub(n, radix, expdif, + &in1[2], &in2[2], &out[2]); + } else { + expdif = mp_unexp_sub(n, radix, -expdif, + &in2[2], &in1[2], &out[2]); + } + outexp -= expdif; + outsgn *= in1[0]; + if (expdif == n) { + outsgn = 0; + } + } + if (outsgn == 0) { + outexp = 0; + } + out[0] = outsgn; + out[1] = outexp; +} + + +/* -------- mp_add child routines -------- */ + + +int mp_unsgn_cmp(int n, int in1[], int in2[]) +{ + int j, cmp; + + cmp = 0; + for (j = 0; j <= n && cmp == 0; j++) { + cmp = in1[j] - in2[j]; + } + if (cmp > 0) { + cmp = 1; + } else if (cmp < 0) { + cmp = -1; + } + return cmp; +} + + +int mp_unexp_add(int n, int radix, int expdif, + int in1[], int in2[], int out[]) +{ + int j, x, carry; + + carry = 0; + if (expdif == 0 && in1[0] + in2[0] >= radix) { + x = in1[n - 1] + in2[n - 1]; + carry = x >= radix ? -1 : 0; + for (j = n - 1; j > 0; j--) { + x = in1[j - 1] + in2[j - 1] - carry; + carry = x >= radix ? -1 : 0; + out[j] = x - (radix & carry); + } + out[0] = -carry; + } else { + if (expdif > n) { + expdif = n; + } + for (j = n - 1; j >= expdif; j--) { + x = in1[j] + in2[j - expdif] - carry; + carry = x >= radix ? -1 : 0; + out[j] = x - (radix & carry); + } + for (j = expdif - 1; j >= 0; j--) { + x = in1[j] - carry; + carry = x >= radix ? -1 : 0; + out[j] = x - (radix & carry); + } + if (carry != 0) { + for (j = n - 1; j > 0; j--) { + out[j] = out[j - 1]; + } + out[0] = -carry; + } + } + return -carry; +} + + +int mp_unexp_sub(int n, int radix, int expdif, + int in1[], int in2[], int out[]) +{ + int j, x, borrow, ncancel; + + if (expdif > n) { + expdif = n; + } + borrow = 0; + for (j = n - 1; j >= expdif; j--) { + x = in1[j] - in2[j - expdif] + borrow; + borrow = x < 0 ? -1 : 0; + out[j] = x + (radix & borrow); + } + for (j = expdif - 1; j >= 0; j--) { + x = in1[j] + borrow; + borrow = x < 0 ? -1 : 0; + out[j] = x + (radix & borrow); + } + ncancel = 0; + for (j = 0; j < n && out[j] == 0; j++) { + ncancel = j + 1; + } + if (ncancel > 0 && ncancel < n) { + for (j = 0; j < n - ncancel; j++) { + out[j] = out[j + ncancel]; + } + for (j = n - ncancel; j < n; j++) { + out[j] = 0; + } + } + return ncancel; +} + + +/* -------- mp_imul routines -------- */ + + +void mp_imul(int n, int radix, int in1[], int in2, int out[]) +{ + void mp_unsgn_imul(int n, double dradix, int in1[], double din2, + int out[]); + + if (in2 > 0) { + out[0] = in1[0]; + } else if (in2 < 0) { + out[0] = -in1[0]; + in2 = -in2; + } else { + out[0] = 0; + } + mp_unsgn_imul(n, radix, &in1[1], in2, &out[1]); + if (out[0] == 0) { + out[1] = 0; + } +} + + +int mp_idiv(int n, int radix, int in1[], int in2, int out[]) +{ + void mp_load_0(int n, int radix, int out[]); + void mp_unsgn_idiv(int n, double dradix, int in1[], double din2, + int out[]); + + if (in2 == 0) { + return -1; + } + if (in2 > 0) { + out[0] = in1[0]; + } else { + out[0] = -in1[0]; + in2 = -in2; + } + if (in1[0] == 0) { + mp_load_0(n, radix, out); + return 0; + } + mp_unsgn_idiv(n, radix, &in1[1], in2, &out[1]); + return 0; +} + + +void mp_idiv_2(int n, int radix, int in[], int out[]) +{ + int j, ix, carry, shift; + + out[0] = in[0]; + shift = 0; + if (in[2] == 1) { + shift = 1; + } + out[1] = in[1] - shift; + carry = -shift; + for (j = 2; j <= n + 1 - shift; j++) { + ix = in[j + shift] + (radix & carry); + carry = -(ix & 1); + out[j] = ix >> 1; + } + if (shift > 0) { + out[n + 1] = (radix & carry) >> 1; + } +} + + +/* -------- mp_imul child routines -------- */ + + +void mp_unsgn_imul(int n, double dradix, int in1[], double din2, + int out[]) +{ + int j, carry, shift; + double x, d1_radix; + + d1_radix = 1.0 / dradix; + carry = 0; + for (j = n; j >= 1; j--) { + x = din2 * in1[j] + carry + 0.5; + carry = (int) (d1_radix * x); + out[j] = (int) (x - dradix * carry); + } + shift = 0; + x = carry + 0.5; + while (x > 1) { + x *= d1_radix; + shift++; + } + out[0] = in1[0] + shift; + if (shift > 0) { + while (shift > n) { + carry = (int) (d1_radix * carry + 0.5); + shift--; + } + for (j = n; j >= shift + 1; j--) { + out[j] = out[j - shift]; + } + for (j = shift; j >= 1; j--) { + x = carry + 0.5; + carry = (int) (d1_radix * x); + out[j] = (int) (x - dradix * carry); + } + } +} + + +void mp_unsgn_idiv(int n, double dradix, int in1[], double din2, + int out[]) +{ + int j, ix, carry, shift; + double x, d1_in2; + + d1_in2 = 1.0 / din2; + shift = 0; + x = 0; + do { + shift++; + x *= dradix; + if (shift <= n) { + x += in1[shift]; + } + } while (x < din2 - 0.5); + x += 0.5; + ix = (int) (d1_in2 * x); + carry = (int) (x - din2 * ix); + out[1] = ix; + shift--; + out[0] = in1[0] - shift; + if (shift >= n) { + shift = n - 1; + } + for (j = 2; j <= n - shift; j++) { + x = in1[j + shift] + dradix * carry + 0.5; + ix = (int) (d1_in2 * x); + carry = (int) (x - din2 * ix); + out[j] = ix; + } + for (j = n - shift + 1; j <= n; j++) { + x = dradix * carry + 0.5; + ix = (int) (d1_in2 * x); + carry = (int) (x - din2 * ix); + out[j] = ix; + } +} + + +/* -------- mp_mul routines -------- */ + + +double mp_mul_radix_test(int n, int radix, int nfft, + double tmpfft[], int ip[], double w[]) +{ + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_csqu(int nfft, double dinout[]); + double mp_mul_d2i_test(int radix, int nfft, double din[]); + int j, ndata, radix_2; + + ndata = (nfft >> 1) + 1; + if (ndata > n) { + ndata = n; + } + tmpfft[nfft + 1] = radix - 1; + for (j = nfft; j > ndata; j--) { + tmpfft[j] = 0; + } + radix_2 = (radix + 1) / 2; + for (j = ndata; j > 2; j--) { + tmpfft[j] = radix_2; + } + tmpfft[2] = radix; + tmpfft[1] = radix - 1; + tmpfft[0] = 0; + rdft(nfft, 1, &tmpfft[1], ip, w); + mp_mul_csqu(nfft, tmpfft); + rdft(nfft, -1, &tmpfft[1], ip, w); + return 2 * mp_mul_d2i_test(radix, nfft, tmpfft); +} + + +void mp_mul(int n, int radix, int in1[], int in2[], int out[], + int tmp[], int nfft, double tmp1fft[], double tmp2fft[], + double tmp3fft[], int ip[], double w[]) +{ + void mp_copy(int n, int radix, int in[], int out[]); + void mp_add(int n, int radix, int in1[], int in2[], int out[]); + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_i2d(int n, int radix, int nfft, int shift, + int in[], double dout[]); + void mp_mul_cmul(int nfft, double din[], double dinout[]); + void mp_mul_cmuladd(int nfft, double din1[], double din2[], + double dinout[]); + void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]); + int n_h, shift; + + shift = (nfft >> 1) + 1; + while (n > shift) { + if (in1[shift + 2] + in2[shift + 2] != 0) { + break; + } + shift++; + } + n_h = n / 2 + 1; + if (n_h < n - shift) { + n_h = n - shift; + } + /* ---- tmp3fft = (upper) in1 * (lower) in2 ---- */ + mp_mul_i2d(n, radix, nfft, 0, in1, tmp1fft); + rdft(nfft, 1, &tmp1fft[1], ip, w); + mp_mul_i2d(n, radix, nfft, shift, in2, tmp3fft); + rdft(nfft, 1, &tmp3fft[1], ip, w); + mp_mul_cmul(nfft, tmp1fft, tmp3fft); + /* ---- tmp = (upper) in1 * (upper) in2 ---- */ + mp_mul_i2d(n, radix, nfft, 0, in2, tmp2fft); + rdft(nfft, 1, &tmp2fft[1], ip, w); + mp_mul_cmul(nfft, tmp2fft, tmp1fft); + rdft(nfft, -1, &tmp1fft[1], ip, w); + mp_mul_d2i(n, radix, nfft, tmp1fft, tmp); + /* ---- tmp3fft += (upper) in2 * (lower) in1 ---- */ + mp_mul_i2d(n, radix, nfft, shift, in1, tmp1fft); + rdft(nfft, 1, &tmp1fft[1], ip, w); + mp_mul_cmuladd(nfft, tmp1fft, tmp2fft, tmp3fft); + /* ---- out = tmp + tmp3fft ---- */ + rdft(nfft, -1, &tmp3fft[1], ip, w); + mp_mul_d2i(n_h, radix, nfft, tmp3fft, out); + if (out[0] != 0) { + mp_add(n, radix, out, tmp, out); + } else { + mp_copy(n, radix, tmp, out); + } +} + + +void mp_squ(int n, int radix, int in[], int out[], int tmp[], + int nfft, double tmp1fft[], double tmp2fft[], + int ip[], double w[]) +{ + void mp_add(int n, int radix, int in1[], int in2[], int out[]); + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_i2d(int n, int radix, int nfft, int shift, + int in[], double dout[]); + void mp_mul_cmul(int nfft, double din[], double dinout[]); + void mp_mul_csqu(int nfft, double dinout[]); + void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]); + int n_h, shift; + + shift = (nfft >> 1) + 1; + while (n > shift) { + if (in[shift + 2] != 0) { + break; + } + shift++; + } + n_h = n / 2 + 1; + if (n_h < n - shift) { + n_h = n - shift; + } + /* ---- tmp = (upper) in * (lower) in ---- */ + mp_mul_i2d(n, radix, nfft, 0, in, tmp1fft); + rdft(nfft, 1, &tmp1fft[1], ip, w); + mp_mul_i2d(n, radix, nfft, shift, in, tmp2fft); + rdft(nfft, 1, &tmp2fft[1], ip, w); + mp_mul_cmul(nfft, tmp1fft, tmp2fft); + rdft(nfft, -1, &tmp2fft[1], ip, w); + mp_mul_d2i(n_h, radix, nfft, tmp2fft, tmp); + /* ---- out = 2 * tmp + ((upper) in)^2 ---- */ + mp_mul_csqu(nfft, tmp1fft); + rdft(nfft, -1, &tmp1fft[1], ip, w); + mp_mul_d2i(n, radix, nfft, tmp1fft, out); + if (tmp[0] != 0) { + mp_add(n_h, radix, tmp, tmp, tmp); + mp_add(n, radix, out, tmp, out); + } +} + + +void mp_mulh(int n, int radix, int in1[], int in2[], int out[], + int nfft, double in1fft[], double outfft[], int ip[], double w[]) +{ + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_i2d(int n, int radix, int nfft, int shift, + int in[], double dout[]); + void mp_mul_cmul(int nfft, double din[], double dinout[]); + void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]); + + mp_mul_i2d(n, radix, nfft, 0, in1, in1fft); + rdft(nfft, 1, &in1fft[1], ip, w); + mp_mul_i2d(n, radix, nfft, 0, in2, outfft); + rdft(nfft, 1, &outfft[1], ip, w); + mp_mul_cmul(nfft, in1fft, outfft); + rdft(nfft, -1, &outfft[1], ip, w); + mp_mul_d2i(n, radix, nfft, outfft, out); +} + + +void mp_mulh_use_in1fft(int n, int radix, double in1fft[], + int shift, int in2[], int out[], int nfft, double outfft[], + int ip[], double w[]) +{ + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_i2d(int n, int radix, int nfft, int shift, + int in[], double dout[]); + void mp_mul_cmul(int nfft, double din[], double dinout[]); + void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]); + int n_h; + + while (n > shift) { + if (in2[shift + 2] != 0) { + break; + } + shift++; + } + n_h = n / 2 + 1; + if (n_h < n - shift) { + n_h = n - shift; + } + mp_mul_i2d(n, radix, nfft, shift, in2, outfft); + rdft(nfft, 1, &outfft[1], ip, w); + mp_mul_cmul(nfft, in1fft, outfft); + rdft(nfft, -1, &outfft[1], ip, w); + mp_mul_d2i(n_h, radix, nfft, outfft, out); +} + + +void mp_squh(int n, int radix, int in[], int out[], + int nfft, double inoutfft[], int ip[], double w[]) +{ + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_i2d(int n, int radix, int nfft, int shift, + int in[], double dout[]); + void mp_mul_csqu(int nfft, double dinout[]); + void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]); + + mp_mul_i2d(n, radix, nfft, 0, in, inoutfft); + rdft(nfft, 1, &inoutfft[1], ip, w); + mp_mul_csqu(nfft, inoutfft); + rdft(nfft, -1, &inoutfft[1], ip, w); + mp_mul_d2i(n, radix, nfft, inoutfft, out); +} + + +void mp_squh_use_in1fft(int n, int radix, double inoutfft[], int out[], + int nfft, int ip[], double w[]) +{ + void rdft(int n, int isgn, double *a, int *ip, double *w); + void mp_mul_csqu(int nfft, double dinout[]); + void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]); + + mp_mul_csqu(nfft, inoutfft); + rdft(nfft, -1, &inoutfft[1], ip, w); + mp_mul_d2i(n, radix, nfft, inoutfft, out); +} + + +/* -------- mp_mul child routines -------- */ + + +void mp_mul_i2d(int n, int radix, int nfft, int shift, + int in[], double dout[]) +{ + int j, x, carry, ndata, radix_2, topdgt; + + ndata = 0; + topdgt = 0; + if (n > shift) { + topdgt = in[shift + 2]; + ndata = (nfft >> 1) + 1; + if (ndata > n - shift) { + ndata = n - shift; + } + } + dout[nfft + 1] = in[0] * topdgt; + for (j = nfft; j > ndata; j--) { + dout[j] = 0; + } + /* ---- abs(dout[j]) <= radix/2 (to keep FFT precision) ---- */ + if (ndata > 1) { + radix_2 = radix / 2; + carry = 0; + for (j = ndata + 1; j > 3; j--) { + x = in[j + shift] - carry; + carry = x >= radix_2 ? -1 : 0; + dout[j - 1] = x - (radix & carry); + } + dout[2] = in[shift + 3] - carry; + } + dout[1] = topdgt; + dout[0] = in[1] - shift; +} + + +void mp_mul_cmul(int nfft, double din[], double dinout[]) +{ + int j; + double xr, xi, yr, yi; + + dinout[0] += din[0]; + dinout[1] *= din[1]; + dinout[2] *= din[2]; + for (j = 3; j < nfft; j += 2) { + xr = din[j]; + xi = din[j + 1]; + yr = dinout[j]; + yi = dinout[j + 1]; + dinout[j] = xr * yr - xi * yi; + dinout[j + 1] = xr * yi + xi * yr; + } + dinout[nfft + 1] *= din[nfft + 1]; +} + + +void mp_mul_cmuladd(int nfft, double din1[], double din2[], + double dinout[]) +{ + int j; + double xr, xi, yr, yi; + + dinout[1] += din1[1] * din2[1]; + dinout[2] += din1[2] * din2[2]; + for (j = 3; j < nfft; j += 2) { + xr = din1[j]; + xi = din1[j + 1]; + yr = din2[j]; + yi = din2[j + 1]; + dinout[j] += xr * yr - xi * yi; + dinout[j + 1] += xr * yi + xi * yr; + } + dinout[nfft + 1] += din1[nfft + 1] * din2[nfft + 1]; +} + + +void mp_mul_csqu(int nfft, double dinout[]) +{ + int j; + double xr, xi; + + dinout[0] *= 2; + dinout[1] *= dinout[1]; + dinout[2] *= dinout[2]; + for (j = 3; j < nfft; j += 2) { + xr = dinout[j]; + xi = dinout[j + 1]; + dinout[j] = xr * xr - xi * xi; + dinout[j + 1] = 2 * xr * xi; + } + dinout[nfft + 1] *= dinout[nfft + 1]; +} + + +void mp_mul_d2i(int n, int radix, int nfft, double din[], int out[]) +{ + int j, carry, carry1, carry2, shift, ndata; + double x, scale, d1_radix, d1_radix2, pow_radix, topdgt; + + scale = 2.0 / nfft; + d1_radix = 1.0 / radix; + d1_radix2 = d1_radix * d1_radix; + topdgt = din[nfft + 1]; + x = topdgt < 0 ? -topdgt : topdgt; + shift = x + 0.5 >= radix ? 1 : 0; + /* ---- correction of cyclic convolution of din[1] ---- */ + x *= nfft * 0.5; + din[nfft + 1] = din[1] - x; + din[1] = x; + /* ---- output of digits ---- */ + ndata = n; + if (n > nfft + 1 + shift) { + ndata = nfft + 1 + shift; + for (j = n + 1; j > ndata + 1; j--) { + out[j] = 0; + } + } + x = 0; + pow_radix = 1; + for (j = ndata + 1 - shift; j <= nfft + 1; j++) { + x += pow_radix * din[j]; + pow_radix *= d1_radix; + if (pow_radix < DBL_EPSILON) { + break; + } + } + x = d1_radix2 * (scale * x + 0.5); + carry2 = ((int) x) - 1; + carry = (int) (radix * (x - carry2) + 0.5); + for (j = ndata; j > 1; j--) { + x = d1_radix2 * (scale * din[j - shift] + carry + 0.5); + carry = carry2; + carry2 = ((int) x) - 1; + x = radix * (x - carry2); + carry1 = (int) x; + out[j + 1] = (int) (radix * (x - carry1)); + carry += carry1; + } + x = carry + ((double) radix) * carry2 + 0.5; + if (shift == 0) { + x += scale * din[1]; + } + carry = (int) (d1_radix * x); + out[2] = (int) (x - ((double) radix) * carry); + if (carry > 0) { + for (j = n + 1; j > 2; j--) { + out[j] = out[j - 1]; + } + out[2] = carry; + shift++; + } + /* ---- output of exp, sgn ---- */ + x = din[0] + shift + 0.5; + shift = ((int) x) - 1; + out[1] = shift + ((int) (x - shift)); + out[0] = topdgt > 0.5 ? 1 : -1; + if (out[2] == 0) { + out[0] = 0; + out[1] = 0; + } +} + + +double mp_mul_d2i_test(int radix, int nfft, double din[]) +{ + int j, carry, carry1, carry2; + double x, scale, d1_radix, d1_radix2, err; + + scale = 2.0 / nfft; + d1_radix = 1.0 / radix; + d1_radix2 = d1_radix * d1_radix; + /* ---- correction of cyclic convolution of din[1] ---- */ + x = din[nfft + 1] * nfft * 0.5; + if (x < 0) { + x = -x; + } + din[nfft + 1] = din[1] - x; + /* ---- check of digits ---- */ + err = 0; + carry = 0; + carry2 = 0; + for (j = nfft + 1; j > 1; j--) { + x = d1_radix2 * (scale * din[j] + carry + 0.5); + carry = carry2; + carry2 = ((int) x) - 1; + x = radix * (x - carry2); + carry1 = (int) x; + x = radix * (x - carry1); + carry += carry1; + x = x - 0.5 - ((int) x); + if (x > err) { + err = x; + } else if (-x > err) { + err = -x; + } + } + return err; +} + + +/* -------- mp_inv routines -------- */ + + +int mp_inv(int n, int radix, int in[], int out[], + int tmp1[], int tmp2[], int nfft, + double tmp1fft[], double tmp2fft[], int ip[], double w[]) +{ + int mp_get_nfft_init(int radix, int nfft_max); + void mp_inv_init(int n, int radix, int in[], int out[]); + int mp_inv_newton(int n, int radix, int in[], int inout[], + int tmp1[], int tmp2[], int nfft, double tmp1fft[], + double tmp2fft[], int ip[], double w[]); + int n_nwt, nfft_nwt, thr, prc; + + if (in[0] == 0) { + return -1; + } + nfft_nwt = mp_get_nfft_init(radix, nfft); + n_nwt = nfft_nwt + 2; + if (n_nwt > n) { + n_nwt = n; + } + mp_inv_init(n_nwt, radix, in, out); + thr = 8; + do { + n_nwt = nfft_nwt + 2; + if (n_nwt > n) { + n_nwt = n; + } + prc = mp_inv_newton(n_nwt, radix, in, out, + tmp1, tmp2, nfft_nwt, tmp1fft, tmp2fft, ip, w); + if (thr * nfft_nwt >= nfft) { + thr = 0; + if (2 * prc <= n_nwt - 2) { + nfft_nwt >>= 1; + } + } else { + if (3 * prc < n_nwt - 2) { + nfft_nwt >>= 1; + } + } + nfft_nwt <<= 1; + } while (nfft_nwt <= nfft); + return 0; +} + + +int mp_sqrt(int n, int radix, int in[], int out[], + int tmp1[], int tmp2[], int nfft, + double tmp1fft[], double tmp2fft[], int ip[], double w[]) +{ + void mp_load_0(int n, int radix, int out[]); + int mp_get_nfft_init(int radix, int nfft_max); + void mp_sqrt_init(int n, int radix, int in[], int out[], int out_rev[]); + int mp_sqrt_newton(int n, int radix, int in[], int inout[], + int inout_rev[], int tmp[], int nfft, double tmp1fft[], + double tmp2fft[], int ip[], double w[], int *n_tmp1fft); + int n_nwt, nfft_nwt, thr, prc, n_tmp1fft; + + if (in[0] < 0) { + return -1; + } else if (in[0] == 0) { + mp_load_0(n, radix, out); + return 0; + } + nfft_nwt = mp_get_nfft_init(radix, nfft); + n_nwt = nfft_nwt + 2; + if (n_nwt > n) { + n_nwt = n; + } + mp_sqrt_init(n_nwt, radix, in, out, tmp1); + n_tmp1fft = 0; + thr = 8; + do { + n_nwt = nfft_nwt + 2; + if (n_nwt > n) { + n_nwt = n; + } + prc = mp_sqrt_newton(n_nwt, radix, in, out, + tmp1, tmp2, nfft_nwt, tmp1fft, tmp2fft, + ip, w, &n_tmp1fft); + if (thr * nfft_nwt >= nfft) { + thr = 0; + if (2 * prc <= n_nwt - 2) { + nfft_nwt >>= 1; + } + } else { + if (3 * prc < n_nwt - 2) { + nfft_nwt >>= 1; + } + } + nfft_nwt <<= 1; + } while (nfft_nwt <= nfft); + return 0; +} + + +/* -------- mp_inv child routines -------- */ + + +int mp_get_nfft_init(int radix, int nfft_max) +{ + int nfft_init; + double r; + + r = radix; + nfft_init = 1; + do { + r *= r; + nfft_init <<= 1; + } while (DBL_EPSILON * r < 1 && nfft_init < nfft_max); + return nfft_init; +} + + +void mp_inv_init(int n, int radix, int in[], int out[]) +{ + void mp_unexp_d2mp(int n, int radix, double din, int out[]); + double mp_unexp_mp2d(int n, int radix, int in[]); + int outexp; + double din; + + out[0] = in[0]; + outexp = -in[1]; + din = 1.0 / mp_unexp_mp2d(n, radix, &in[2]); + while (din < 1) { + din *= radix; + outexp--; + } + out[1] = outexp; + mp_unexp_d2mp(n, radix, din, &out[2]); +} + + +void mp_sqrt_init(int n, int radix, int in[], int out[], int out_rev[]) +{ + void mp_unexp_d2mp(int n, int radix, double din, int out[]); + double mp_unexp_mp2d(int n, int radix, int in[]); + int outexp; + double din; + + out[0] = 1; + out_rev[0] = 1; + outexp = in[1]; + din = mp_unexp_mp2d(n, radix, &in[2]); + if (outexp % 2 != 0) { + din *= radix; + outexp--; + } + outexp /= 2; + din = sqrt(din); + if (din < 1) { + din *= radix; + outexp--; + } + out[1] = outexp; + mp_unexp_d2mp(n, radix, din, &out[2]); + outexp = -outexp; + din = 1.0 / din; + while (din < 1) { + din *= radix; + outexp--; + } + out_rev[1] = outexp; + mp_unexp_d2mp(n, radix, din, &out_rev[2]); +} + + +void mp_unexp_d2mp(int n, int radix, double din, int out[]) +{ + int j, x; + + for (j = 0; j < n; j++) { + x = (int) din; + if (x >= radix) { + x = radix - 1; + din = radix; + } + din = radix * (din - x); + out[j] = x; + } +} + + +double mp_unexp_mp2d(int n, int radix, int in[]) +{ + int j; + double d1_radix, dout; + + d1_radix = 1.0 / radix; + dout = 0; + for (j = n - 1; j >= 0; j--) { + dout = d1_radix * dout + in[j]; + } + return dout; +} + + +int mp_inv_newton(int n, int radix, int in[], int inout[], + int tmp1[], int tmp2[], int nfft, double tmp1fft[], + double tmp2fft[], int ip[], double w[]) +{ + void mp_load_1(int n, int radix, int out[]); + void mp_round(int n, int radix, int m, int inout[]); + void mp_add(int n, int radix, int in1[], int in2[], int out[]); + void mp_sub(int n, int radix, int in1[], int in2[], int out[]); + void mp_mulh(int n, int radix, int in1[], int in2[], int out[], + int nfft, double in1fft[], double outfft[], + int ip[], double w[]); + void mp_mulh_use_in1fft(int n, int radix, double in1fft[], + int shift, int in2[], int out[], int nfft, double outfft[], + int ip[], double w[]); + int n_h, shift, prc; + + shift = (nfft >> 1) + 1; + n_h = n / 2 + 1; + if (n_h < n - shift) { + n_h = n - shift; + } + /* ---- tmp1 = inout * (upper) in (half to normal precision) ---- */ + mp_round(n, radix, shift, inout); + mp_mulh(n, radix, inout, in, tmp1, + nfft, tmp1fft, tmp2fft, ip, w); + /* ---- tmp2 = 1 - tmp1 ---- */ + mp_load_1(n, radix, tmp2); + mp_sub(n, radix, tmp2, tmp1, tmp2); + /* ---- tmp2 -= inout * (lower) in (half precision) ---- */ + mp_mulh_use_in1fft(n, radix, tmp1fft, shift, in, tmp1, + nfft, tmp2fft, ip, w); + mp_sub(n_h, radix, tmp2, tmp1, tmp2); + /* ---- get precision ---- */ + prc = -tmp2[1]; + if (tmp2[0] == 0) { + prc = nfft + 1; + } + /* ---- tmp2 *= inout (half precision) ---- */ + mp_mulh_use_in1fft(n_h, radix, tmp1fft, 0, tmp2, tmp2, + nfft, tmp2fft, ip, w); + /* ---- inout += tmp2 ---- */ + if (tmp2[0] != 0) { + mp_add(n, radix, inout, tmp2, inout); + } + return prc; +} + + +int mp_sqrt_newton(int n, int radix, int in[], int inout[], + int inout_rev[], int tmp[], int nfft, double tmp1fft[], + double tmp2fft[], int ip[], double w[], int *n_tmp1fft) +{ + void mp_round(int n, int radix, int m, int inout[]); + void mp_add(int n, int radix, int in1[], int in2[], int out[]); + void mp_sub(int n, int radix, int in1[], int in2[], int out[]); + void mp_idiv_2(int n, int radix, int in[], int out[]); + void mp_mulh(int n, int radix, int in1[], int in2[], int out[], + int nfft, double in1fft[], double outfft[], + int ip[], double w[]); + void mp_squh(int n, int radix, int in[], int out[], + int nfft, double inoutfft[], int ip[], double w[]); + void mp_squh_use_in1fft(int n, int radix, double inoutfft[], int out[], + int nfft, int ip[], double w[]); + int n_h, nfft_h, shift, prc; + + nfft_h = nfft >> 1; + shift = nfft_h + 1; + if (nfft_h < 2) { + nfft_h = 2; + } + n_h = n / 2 + 1; + if (n_h < n - shift) { + n_h = n - shift; + } + /* ---- tmp = inout_rev^2 (1/4 to half precision) ---- */ + mp_round(n_h, radix, (nfft_h >> 1) + 1, inout_rev); + if (*n_tmp1fft != nfft_h) { + mp_squh(n_h, radix, inout_rev, tmp, + nfft_h, tmp1fft, ip, w); + } else { + mp_squh_use_in1fft(n_h, radix, tmp1fft, tmp, + nfft_h, ip, w); + } + /* ---- tmp = inout_rev - inout * tmp (half precision) ---- */ + mp_round(n, radix, shift, inout); + mp_mulh(n_h, radix, inout, tmp, tmp, + nfft, tmp1fft, tmp2fft, ip, w); + mp_sub(n_h, radix, inout_rev, tmp, tmp); + /* ---- inout_rev += tmp ---- */ + mp_add(n_h, radix, inout_rev, tmp, inout_rev); + /* ---- tmp = in - inout^2 (half to normal precision) ---- */ + mp_squh_use_in1fft(n, radix, tmp1fft, tmp, + nfft, ip, w); + mp_sub(n, radix, in, tmp, tmp); + /* ---- get precision ---- */ + prc = in[1] - tmp[1]; + if (in[2] > tmp[2]) { + prc++; + } + if (tmp[0] == 0) { + prc = nfft + 1; + } + /* ---- tmp = tmp * inout_rev / 2 (half precision) ---- */ + mp_round(n_h, radix, shift, inout_rev); + mp_mulh(n_h, radix, inout_rev, tmp, tmp, + nfft, tmp1fft, tmp2fft, ip, w); + *n_tmp1fft = nfft; + mp_idiv_2(n_h, radix, tmp, tmp); + /* ---- inout += tmp ---- */ + if (tmp[0] != 0) { + mp_add(n, radix, inout, tmp, inout); + } + return prc; +} + + +/* -------- mp_io routines -------- */ + + +void mp_sprintf(int n, int log10_radix, int in[], char out[]) +{ + int j, k, x, y, outexp, shift; + + if (in[0] < 0) { + *out++ = '-'; + } + x = in[2]; + shift = log10_radix; + for (k = log10_radix; k > 0; k--) { + y = x % 10; + x /= 10; + out[k] = '0' + y; + if (y != 0) { + shift = k; + } + } + out[0] = out[shift]; + out[1] = '.'; + for (k = 1; k <= log10_radix - shift; k++) { + out[k + 1] = out[k + shift]; + } + outexp = log10_radix - shift; + out += outexp + 2; + for (j = 3; j <= n + 1; j++) { + x = in[j]; + for (k = log10_radix - 1; k >= 0; k--) { + y = x % 10; + x /= 10; + out[k] = '0' + y; + } + out += log10_radix; + } + *out++ = 'e'; + outexp += log10_radix * in[1]; + sprintf(out, "%d", outexp); +} + + +void mp_sscanf(int n, int log10_radix, char in[], int out[]) +{ + char *s; + int j, x, outexp, outexp_mod; + + while (*in == ' ') { + in++; + } + out[0] = 1; + if (*in == '-') { + out[0] = -1; + in++; + } else if (*in == '+') { + in++; + } + while (*in == ' ' || *in == '0') { + in++; + } + outexp = 0; + for (s = in; *s != '\0'; s++) { + if (*s == 'e' || *s == 'E' || *s == 'd' || *s == 'D') { + if (sscanf(++s, "%d", &outexp) != 1) { + outexp = 0; + } + break; + } + } + if (*in == '.') { + do { + outexp--; + while (*++in == ' '); + } while (*in == '0' && *in != '\0'); + } else if (*in != '\0') { + s = in; + while (*++s == ' '); + while (*s >= '0' && *s <= '9' && *s != '\0') { + outexp++; + while (*++s == ' '); + } + } + x = outexp / log10_radix; + outexp_mod = outexp - log10_radix * x; + if (outexp_mod < 0) { + x--; + outexp_mod += log10_radix; + } + out[1] = x; + x = 0; + j = 2; + for (s = in; *s != '\0'; s++) { + if (*s == '.' || *s == ' ') { + continue; + } + if (*s < '0' || *s > '9') { + break; + } + x = 10 * x + (*s - '0'); + if (--outexp_mod < 0) { + if (j > n + 1) { + break; + } + out[j++] = x; + x = 0; + outexp_mod = log10_radix - 1; + } + } + while (outexp_mod-- >= 0) { + x *= 10; + } + while (j <= n + 1) { + out[j++] = x; + x = 0; + } + if (out[2] == 0) { + out[0] = 0; + out[1] = 0; + } +} + + +void mp_fprintf(int n, int log10_radix, int in[], FILE *fout) +{ + int j, k, x, y, outexp, shift; + char out[256]; + + if (in[0] < 0) { + putc('-', fout); + } + x = in[2]; + shift = log10_radix; + for (k = log10_radix; k > 0; k--) { + y = x % 10; + x /= 10; + out[k] = '0' + y; + if (y != 0) { + shift = k; + } + } + putc(out[shift], fout); + putc('.', fout); + for (k = 1; k <= log10_radix - shift; k++) { + putc(out[k + shift], fout); + } + outexp = log10_radix - shift; + for (j = 3; j <= n + 1; j++) { + x = in[j]; + for (k = log10_radix - 1; k >= 0; k--) { + y = x % 10; + x /= 10; + out[k] = '0' + y; + } + for (k = 0; k < log10_radix; k++) { + putc(out[k], fout); + } + } + putc('e', fout); + outexp += log10_radix * in[1]; + sprintf(out, "%d", outexp); + for (k = 0; out[k] != '\0'; k++) { + putc(out[k], fout); + } +} + + diff --git a/plugins/supereq/nsfft-1.00/simd/Makefile b/plugins/supereq/nsfft-1.00/simd/Makefile new file mode 120000 index 00000000..fc484116 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/Makefile @@ -0,0 +1 @@ +Makefile.x86
\ No newline at end of file diff --git a/plugins/supereq/nsfft-1.00/simd/Makefile.altivec b/plugins/supereq/nsfft-1.00/simd/Makefile.altivec new file mode 100644 index 00000000..eeaed6a1 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/Makefile.altivec @@ -0,0 +1,26 @@ +CC=gcc +BASEOPT=-Wall -maltivec -mabi=altivec +OPT=$(BASEOPT) -O3 + +all : libSIMD.a + +SIMDBaseUndiff_purecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecfloat.o + +SIMDBaseUndiff_purecdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecdouble.o + +SIMDBaseUndiff_pureclongdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_pureclongdouble.o + +SIMDBaseUndiff_altivecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_ALTIVEC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_altivecfloat.o + +SIMDBase.o : SIMDBase.c SIMDBase.h + $(CC) $(BASEOPT) -O -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_ALTIVEC_FLOAT SIMDBase.c -c -o SIMDBase.o + +libSIMD.a : SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o SIMDBaseUndiff_altivecfloat.o + rm -f libSIMD.a; ar -cvq libSIMD.a SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o SIMDBaseUndiff_altivecfloat.o + +clean : + rm -f *~ *.o *.s *.a diff --git a/plugins/supereq/nsfft-1.00/simd/Makefile.neon b/plugins/supereq/nsfft-1.00/simd/Makefile.neon new file mode 100644 index 00000000..ace704f1 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/Makefile.neon @@ -0,0 +1,26 @@ +CC=gcc +BASEOPT=-Wall -mfloat-abi=softfp +OPT=$(BASEOPT) -O3 + +all : libSIMD.a + +SIMDBaseUndiff_purecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecfloat.o + +SIMDBaseUndiff_purecdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecdouble.o + +SIMDBaseUndiff_pureclongdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_pureclongdouble.o + +SIMDBaseUndiff_neonfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -mfpu=neon -DENABLE_NEON_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_neonfloat.o + +SIMDBase.o : SIMDBase.c SIMDBase.h + $(CC) $(BASEOPT) -O -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_NEON_FLOAT SIMDBase.c -c -o SIMDBase.o + +libSIMD.a : SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o SIMDBaseUndiff_neonfloat.o + rm -f libSIMD.a; ar -cvq libSIMD.a SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o SIMDBaseUndiff_neonfloat.o + +clean : + rm -f *~ *.o *.s *.a diff --git a/plugins/supereq/nsfft-1.00/simd/Makefile.purec b/plugins/supereq/nsfft-1.00/simd/Makefile.purec new file mode 100644 index 00000000..2c8b04f1 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/Makefile.purec @@ -0,0 +1,35 @@ +CC=gcc +BASEOPT=-Wall +OPT=$(BASEOPT) -O3 + +all : libDFT.a + +DFTpurecfloat.o : DFTUndiff.c DFT.h SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT DFTUndiff.c -c -o DFTpurecfloat.o + +DFTpurecdouble.o : DFTUndiff.c DFT.h SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE DFTUndiff.c -c -o DFTpurecdouble.o + +DFTpureclongdouble.o : DFTUndiff.c DFT.h SIMDBase.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE DFTUndiff.c -c -o DFTpureclongdouble.o + +SIMDBaseUndiff_purecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecfloat.o + +SIMDBaseUndiff_purecdouble.o : SIMDBaseUndiff.c DFT.h SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecdouble.o + +SIMDBaseUndiff_pureclongdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_pureclongdouble.o + +SIMDBase.o : SIMDBase.c SIMDBase.h + $(CC) $(BASEOPT) -O -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE SIMDBase.c -c -o SIMDBase.o + +DFT.o : DFT.c DFT.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE DFT.c -c -o DFT.o + +libDFT.a : DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFT.o SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o + rm -f libDFT.a; ar -cvq libDFT.a DFTpurecfloat.o DFTpurecdouble.o DFTpureclongdouble.o DFT.o SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o + +clean : + rm -f *~ *.o *.s *.a diff --git a/plugins/supereq/nsfft-1.00/simd/Makefile.x86 b/plugins/supereq/nsfft-1.00/simd/Makefile.x86 new file mode 100644 index 00000000..02f49610 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/Makefile.x86 @@ -0,0 +1,35 @@ +CC=gcc +BASEOPT=-Wall +OPT=$(BASEOPT) -O3 + +all : libSIMD.a + +SIMDBaseUndiff_purecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecfloat.o + +SIMDBase_purecdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE SIMDBaseUndiff.c -c -o SIMDBase_purecdouble.o + +SIMDBase_pureclongdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE SIMDBaseUndiff.c -c -o SIMDBase_pureclongdouble.o + +SIMDBase_ssefloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -msse -DENABLE_SSE_FLOAT SIMDBaseUndiff.c -c -o SIMDBase_ssefloat.o + +SIMDBase_sse2double.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -msse2 -DENABLE_SSE2_DOUBLE SIMDBaseUndiff.c -c -o SIMDBase_sse2double.o + +SIMDBase_avxfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -mavx -DENABLE_AVX_FLOAT SIMDBaseUndiff.c -c -o SIMDBase_avxfloat.o + +SIMDBase_avxdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -mavx -DENABLE_AVX_DOUBLE SIMDBaseUndiff.c -c -o SIMDBase_avxdouble.o + +SIMDBase.o : SIMDBase.c SIMDBase.h + $(CC) $(BASEOPT) -O -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_SSE_FLOAT -DENABLE_SSE2_DOUBLE SIMDBase.c -c -o SIMDBase.o + +libSIMD.a : SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBase_purecdouble.o SIMDBase_pureclongdouble.o SIMDBase_ssefloat.o SIMDBase_sse2double.o + rm -f libSIMD.a; ar -cvq libSIMD.a SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBase_purecdouble.o SIMDBase_pureclongdouble.o SIMDBase_ssefloat.o SIMDBase_sse2double.o + +clean : + rm -f *~ *.o *.s *.a a.out diff --git a/plugins/supereq/nsfft-1.00/simd/Makefile.x86avx b/plugins/supereq/nsfft-1.00/simd/Makefile.x86avx new file mode 100644 index 00000000..d9d27a2e --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/Makefile.x86avx @@ -0,0 +1,35 @@ +CC=gcc +BASEOPT=-Wall +OPT=$(BASEOPT) -O3 + +all : libSIMD.a + +SIMDBaseUndiff_purecfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecfloat.o + +SIMDBaseUndiff_purecdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_purecdouble.o + +SIMDBaseUndiff_pureclongdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -DENABLE_PUREC_LONGDOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_pureclongdouble.o + +SIMDBaseUndiff_ssefloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -msse -DENABLE_SSE_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_ssefloat.o + +SIMDBaseUndiff_sse2double.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -msse2 -DENABLE_SSE2_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_sse2double.o + +SIMDBaseUndiff_avxfloat.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -mavx -DENABLE_AVX_FLOAT SIMDBaseUndiff.c -c -o SIMDBaseUndiff_avxfloat.o + +SIMDBaseUndiff_avxdouble.o : SIMDBaseUndiff.c SIMDBase.h SIMDBaseUndiff.h + $(CC) $(OPT) -mavx -DENABLE_AVX_DOUBLE SIMDBaseUndiff.c -c -o SIMDBaseUndiff_avxdouble.o + +SIMDBase.o : SIMDBase.c SIMDBase.h + $(CC) $(BASEOPT) -O -DENABLE_PUREC_FLOAT -DENABLE_PUREC_DOUBLE -DENABLE_PUREC_LONGDOUBLE -DENABLE_SSE_FLOAT -DENABLE_SSE2_DOUBLE -DENABLE_AVX_FLOAT -DENABLE_AVX_DOUBLE SIMDBase.c -c -o SIMDBase.o + +libSIMD.a : SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o SIMDBaseUndiff_ssefloat.o SIMDBaseUndiff_sse2double.o SIMDBaseUndiff_avxfloat.o SIMDBaseUndiff_avxdouble.o + rm -f libSIMD.a; ar -cvq libSIMD.a SIMDBase.o SIMDBaseUndiff_purecfloat.o SIMDBaseUndiff_purecdouble.o SIMDBaseUndiff_pureclongdouble.o SIMDBaseUndiff_ssefloat.o SIMDBaseUndiff_sse2double.o SIMDBaseUndiff_avxfloat.o SIMDBaseUndiff_avxdouble.o + +clean : + rm -f *~ *.o *.s *.a a.out diff --git a/plugins/supereq/nsfft-1.00/simd/SIMDBase.c b/plugins/supereq/nsfft-1.00/simd/SIMDBase.c new file mode 100644 index 00000000..eb51ee10 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/SIMDBase.c @@ -0,0 +1,454 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> +#include <signal.h> +#include <setjmp.h> +#include <string.h> + +#include "SIMDBase.h" + +void detect_purec_float(void); +void detect_purec_double(void); +void detect_purec_longdouble(void); +void detect_sse_float(void); +void detect_sse2_double(void); +void detect_neon_float(void); +void detect_avx_float(void); +void detect_avx_double(void); +void detect_altivec_float(void); + +int32_t getModeParamInt_purec_float(int32_t paramId); +int32_t getModeParamInt_purec_double(int32_t paramId); +int32_t getModeParamInt_purec_longdouble(int32_t paramId); +int32_t getModeParamInt_sse_float(int32_t paramId); +int32_t getModeParamInt_sse2_double(int32_t paramId); +int32_t getModeParamInt_neon_float(int32_t paramId); +int32_t getModeParamInt_avx_float(int32_t paramId); +int32_t getModeParamInt_avx_double(int32_t paramId); +int32_t getModeParamInt_altivec_float(int32_t paramId); + +char * getModeParamString_purec_float(int32_t paramId); +char * getModeParamString_purec_double(int32_t paramId); +char * getModeParamString_purec_longdouble(int32_t paramId); +char * getModeParamString_sse_float(int32_t paramId); +char * getModeParamString_sse2_double(int32_t paramId); +char * getModeParamString_neon_float(int32_t paramId); +char * getModeParamString_avx_float(int32_t paramId); +char * getModeParamString_avx_double(int32_t paramId); +char * getModeParamString_altivec_float(int32_t paramId); + +uint8_t detectBuffer[256]; +char SIMDBase_processorNameString[256]; + +static char *startsWith(char *str1, char *str2) { + if (strncmp(str1, str2, strlen(str2)) == 0) { + return str1 + strlen(str2); + } + + return NULL; +} + +#if defined(__linux__) +static char *tryReadingProcCpuinfo(char *entry) { + int i; + + FILE *fp = fopen("/proc/cpuinfo", "r"); + if (fp == NULL) return NULL; + + for(i=0;i<100;i++) { + char *q; + bzero(SIMDBase_processorNameString, 256); + if (fgets(SIMDBase_processorNameString, 255, fp) == NULL) break; + + if ((q = startsWith(SIMDBase_processorNameString, entry)) != NULL) { + int j; + fclose(fp); + + for(j=0;j<256;j++) { + if (SIMDBase_processorNameString[j] == '\n') SIMDBase_processorNameString[j] = ' '; + } + while(*q != '\0' && *q != ':' && q - SIMDBase_processorNameString < 200) q++; + if (q - SIMDBase_processorNameString >= 200) return NULL; + if (*q == ':' && *(q+1) == ' ') return q + 2; + return NULL; + } + } + + fclose(fp); + return NULL; +} +#else +static char *tryReadingProcCpuinfo(char *entry) { return NULL; } +#endif + +#if defined(__i386__) +static void SIMDBase_x86cpuid(uint32_t out[4], uint32_t eax, uint32_t ecx) { + uint32_t a, b, c, d; + __asm__ __volatile__("pushl %%eax; \n\t" + "pushl %%ebx; \n\t" + "pushl %%ecx; \n\t" + "pushl %%edx; \n\t" + "cpuid; \n\t" + "movl %%eax, %0; \n\t" + "movl %%ebx, %1; \n\t" + "movl %%ecx, %2; \n\t" + "movl %%edx, %3; \n\t" + "popl %%edx; \n\t" + "popl %%ecx; \n\t" + "popl %%ebx; \n\t" + "popl %%eax; \n\t" + : "=m"(a), "=m"(b), "=m"(c), "=m"(d) + : "a"(eax), "c"(ecx) + : "cc"); + out[0] = a; out[1] = b; out[2] = c; out[3] = d; +} +#endif + +#if defined(__x86_64__) +static void SIMDBase_x86cpuid(uint32_t out[4], uint32_t eax, uint32_t ecx) { + uint32_t a, b, c, d; + __asm__ __volatile__ ("cpuid" : "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (eax), "c"(ecx)); + out[0] = a; out[1] = b; out[2] = c; out[3] = d; +} +#endif + +#if defined(__i386__) || defined(__x86_64__) +static void getCacheParam(CacheParam *p) { + static int l2assoc[] = {0,1,2,0,4,0,8,0,16,0,32,48,64,96,128,-1}; + int32_t i; + uint32_t out[4]; + + for(i=0;i<8;i++) { + p->size[i] = p->assoc[i] = 0; + } + + SIMDBase_x86cpuid(out, 4, 0); + + if ((out[0] & 0xf) != 0) { + p->linesize = ((out[1] >> 0) & 2047)+1; + for(i=0;i<8;i++) { + SIMDBase_x86cpuid(out, 4, i); + if ((out[0] & 0xf) == 0) break; + int level = (out[0] >> 5) & 0x7; + int type = (out[0] >> 0) & 0xf; + int assoc = ((out[1] >> 22) & 1023)+1; + int part = ((out[1] >> 12) & 1023)+1; + int lsize = ((out[1] >> 0) & 2047)+1; + int nsets = ((out[2] >> 0))+1; + int nthre = ((out[0] >> 14) & 1023)+1; + + if (type != 1 && type != 3) continue; + p->assoc[level-1] = assoc; + p->size[level-1] = (uint64_t)assoc * part * lsize * nsets / nthre; + } + } else { + SIMDBase_x86cpuid(out, 0x80000008U, 0); + int ncores = (out[2] & 0xff) + 1; + + SIMDBase_x86cpuid(out, 0x80000005U, 0); + p->linesize = out[2] & 255; + p->size[0] = (out[2] >> 24) * 1024 / ncores; + p->assoc[0] = (out[2] >> 16) & 0xff; + + SIMDBase_x86cpuid(out, 0x80000006U, 0); + p->size[1] = (out[2] >> 16) * 1024 / ncores; + p->assoc[1] = l2assoc[(out[2] >> 12) & 0xf]; + p->size[2] = (out[3] >> 18) * 512 * 1024 / ncores; + p->assoc[2] = l2assoc[(out[3] >> 12) & 0xf]; + } + + if (p->size[0] == 0) { + p->size[0] = 16 * 1024; + p->assoc[0] = 4; + } + + if (p->size[1] == 0) { + p->size[1] = 256 * 1024; + p->assoc[1] = 4; + } +} + +char *SIMDBase_getProcessorNameString() { + union { + uint32_t info[4]; + uint8_t str[16]; + } u; + int i,j; + char *p; + + p = SIMDBase_processorNameString; + + SIMDBase_x86cpuid(u.info, 0, 0); + + for(i=0;i<4;i++) *p++ = u.str[i+4]; + for(i=0;i<4;i++) *p++ = u.str[i+12]; + for(i=0;i<4;i++) *p++ = u.str[i+8]; + + *p++ = ' '; + + for(i=0;i<3;i++) { + SIMDBase_x86cpuid(u.info, i + 0x80000002, 0); + + for(j=0;j<16;j++) { + *p++ = u.str[j]; + } + } + + *p++ = '\n'; + + return SIMDBase_processorNameString; +} +#else +char *SIMDBase_getProcessorNameString() { + char *p = "Unknown"; +#if defined(__powerpc__) + if ((p = tryReadingProcCpuinfo("cpu")) == NULL) p = "PowerPC"; +#elif defined(__arm__) + if ((p = tryReadingProcCpuinfo("Processor")) == NULL) p = "ARM"; +#endif + + return p; +} +#endif + +int32_t SIMDBase_sizeOfCachelineInByte() { +#if defined(__i386__) || defined(__x86_64__) + CacheParam p; + getCacheParam(&p); + return p.linesize; +#else + return 64; +#endif +} + +int32_t SIMDBase_sizeOfDataCacheInByte() { +#if defined(__i386__) || defined(__x86_64__) + CacheParam p; + getCacheParam(&p); + return p.size[1] + p.size[2]; // L2 + L3 +#else + return 256 * 1024; +#endif +} + +static jmp_buf sigjmp; + +static void sighandler(int signum) { + longjmp(sigjmp, 1); +} + +int32_t SIMDBase_detect(int32_t paramId) { +#if defined(__i386__) || defined(__x86_64__) + uint32_t reg[4]; +#endif + + switch(paramId) { + case SIMDBase_MODE_PUREC_FLOAT: +#if defined(ENABLE_PUREC_FLOAT) + return 1; +#else + return -1; +#endif + case SIMDBase_MODE_PUREC_DOUBLE: +#if defined(ENABLE_PUREC_DOUBLE) + return 1; +#else + return -1; +#endif + case SIMDBase_MODE_PUREC_LONGDOUBLE: +#if defined(ENABLE_PUREC_LONGDOUBLE) + return 1; +#else + return -1; +#endif + case SIMDBase_MODE_SSE_FLOAT: +#if defined(ENABLE_SSE_FLOAT) + SIMDBase_x86cpuid(reg, 1, 0); + return (reg[3] & (1 << 25)) != 0; +#else + return -1; +#endif + case SIMDBase_MODE_SSE2_DOUBLE: +#if defined(ENABLE_SSE2_DOUBLE) + SIMDBase_x86cpuid(reg, 1, 0); + return (reg[3] & (1 << 26)) != 0; +#else + return -1; +#endif + case SIMDBase_MODE_AVX_FLOAT: +#if defined(ENABLE_AVX_FLOAT) + SIMDBase_x86cpuid(reg, 1, 0); + return (reg[2] & (1 << 28)) != 0; +#else + return -1; +#endif + case SIMDBase_MODE_AVX_DOUBLE: +#if defined(ENABLE_AVX_DOUBLE) + SIMDBase_x86cpuid(reg, 1, 0); + return (reg[2] & (1 << 28)) != 0; +#else + return -1; +#endif + default: + break; + } + + signal(SIGILL, sighandler); + + if (setjmp(sigjmp) == 0) { + switch(paramId) { +#if defined(ENABLE_NEON_FLOAT) + case SIMDBase_MODE_NEON_FLOAT: + detect_neon_float(); + break; +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case SIMDBase_MODE_ALTIVEC_FLOAT: + detect_altivec_float(); + break; +#endif + default: + signal(SIGILL, SIG_DFL); + return -1; + } + signal(SIGILL, SIG_DFL); + return 1; + } else { + signal(SIGILL, SIG_DFL); + return 0; + } +} + +int32_t SIMDBase_chooseBestMode(int32_t typeId) { + switch(typeId) { + case SIMDBase_TYPE_HALF: + break; + case SIMDBase_TYPE_FLOAT: + if (SIMDBase_detect(SIMDBase_MODE_AVX_FLOAT) == 1) return SIMDBase_MODE_AVX_FLOAT; + if (SIMDBase_detect(SIMDBase_MODE_SSE_FLOAT) == 1) return SIMDBase_MODE_SSE_FLOAT; + if (SIMDBase_detect(SIMDBase_MODE_NEON_FLOAT) == 1) return SIMDBase_MODE_NEON_FLOAT; + if (SIMDBase_detect(SIMDBase_MODE_ALTIVEC_FLOAT) == 1) return SIMDBase_MODE_ALTIVEC_FLOAT; + if (SIMDBase_detect(SIMDBase_MODE_PUREC_FLOAT) == 1) return SIMDBase_MODE_PUREC_FLOAT; + break; + + case SIMDBase_TYPE_DOUBLE: + if (SIMDBase_detect(SIMDBase_MODE_AVX_DOUBLE) == 1) return SIMDBase_MODE_AVX_DOUBLE; + if (SIMDBase_detect(SIMDBase_MODE_SSE2_DOUBLE) == 1) return SIMDBase_MODE_SSE2_DOUBLE; + if (SIMDBase_detect(SIMDBase_MODE_PUREC_DOUBLE) == 1) return SIMDBase_MODE_PUREC_DOUBLE; + break; + + case SIMDBase_TYPE_LONGDOUBLE: + if (SIMDBase_detect(SIMDBase_MODE_PUREC_LONGDOUBLE) == 1) return SIMDBase_MODE_PUREC_LONGDOUBLE; + break; + + case SIMDBase_TYPE_EXTENDED: + break; + + case SIMDBase_TYPE_QUAD: + break; + } + + return SIMDBase_MODE_NONE; +} + +int32_t SIMDBase_getModeParamInt(int32_t paramId, int32_t mode) { + switch(mode) { +#if defined(ENABLE_PUREC_FLOAT) + case 1: return getModeParamInt_purec_float(paramId); break; +#endif +#if defined(ENABLE_PUREC_DOUBLE) + case 2: return getModeParamInt_purec_double(paramId); break; +#endif +#if defined(ENABLE_PUREC_LONGDOUBLE) + case 3: return getModeParamInt_purec_longdouble(paramId); break; +#endif +#if defined(ENABLE_SSE_FLOAT) + case 4: return getModeParamInt_sse_float(paramId); break; +#endif +#if defined(ENABLE_SSE2_DOUBLE) + case 5: return getModeParamInt_sse2_double(paramId); break; +#endif +#if defined(ENABLE_NEON_FLOAT) + case 6: return getModeParamInt_neon_float(paramId); break; +#endif +#if defined(ENABLE_AVX_FLOAT) + case 7: return getModeParamInt_avx_float(paramId); break; +#endif +#if defined(ENABLE_AVX_DOUBLE) + case 8: return getModeParamInt_avx_double(paramId); break; +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case 9: return getModeParamInt_altivec_float(paramId); break; +#endif + } + + return -1; +} + +char *SIMDBase_getModeParamString(int32_t paramId, int32_t mode) { + switch(mode) { +#if defined(ENABLE_PUREC_FLOAT) + case 1: return getModeParamString_purec_float(paramId); break; +#endif +#if defined(ENABLE_PUREC_DOUBLE) + case 2: return getModeParamString_purec_double(paramId); break; +#endif +#if defined(ENABLE_PUREC_LONGDOUBLE) + case 3: return getModeParamString_purec_longdouble(paramId); break; +#endif +#if defined(ENABLE_SSE_FLOAT) + case 4: return getModeParamString_sse_float(paramId); break; +#endif +#if defined(ENABLE_SSE2_DOUBLE) + case 5: return getModeParamString_sse2_double(paramId); break; +#endif +#if defined(ENABLE_NEON_FLOAT) + case 6: return getModeParamString_neon_float(paramId); break; +#endif +#if defined(ENABLE_AVX_FLOAT) + case 7: return getModeParamString_avx_float(paramId); break; +#endif +#if defined(ENABLE_AVX_DOUBLE) + case 8: return getModeParamString_avx_double(paramId); break; +#endif +#if defined(ENABLE_ALTIVEC_FLOAT) + case 9: return getModeParamString_altivec_float(paramId); break; +#endif + } + + return NULL; +} + +#ifdef ANDROID +int posix_memalign (void **memptr, size_t alignment, size_t size) { + *memptr = malloc (size); + return *memptr ? 0 : -1; +} +#endif + +void *SIMDBase_alignedMalloc(uint64_t size) { + void *p; + if (posix_memalign(&p, SIMDBase_sizeOfCachelineInByte(), size) != 0) abort(); + return p; +} + +void SIMDBase_alignedFree(void *ptr) { + free(ptr); +} + +int32_t SIMDBase_getParamInt(int32_t paramId) { + switch(paramId) { + case SIMDBase_PARAMID_MODE_MAX: + return SIMDBase_LAST_MODE + 1; + } + + return -1; +} + +int32_t SIMDBase_getTypeParamInt(int32_t paramId, int32_t typeId) { + switch(typeId) { + } + + return -1; +} diff --git a/plugins/supereq/nsfft-1.00/simd/SIMDBase.h b/plugins/supereq/nsfft-1.00/simd/SIMDBase.h new file mode 100644 index 00000000..10cdeb81 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/SIMDBase.h @@ -0,0 +1,53 @@ +#ifndef _SIMDBase_H_ +#define _SIMDBase_H_ + +#include <stdint.h> + +#define SIMDBase_TYPE_FLOAT ( 1 | ( 1 << 24 )) +#define SIMDBase_TYPE_DOUBLE ( 2 | ( 1 << 24 )) +#define SIMDBase_TYPE_LONGDOUBLE ( 3 | ( 1 << 24 )) +#define SIMDBase_TYPE_EXTENDED ( 4 | ( 1 << 24 )) +#define SIMDBase_TYPE_QUAD ( 5 | ( 1 << 24 )) +#define SIMDBase_TYPE_HALF ( 6 | ( 1 << 24 )) + +#define SIMDBase_MODE_NONE 0 +#define SIMDBase_MODE_PUREC_FLOAT 1 +#define SIMDBase_MODE_PUREC_DOUBLE 2 +#define SIMDBase_MODE_PUREC_LONGDOUBLE 3 +#define SIMDBase_MODE_SSE_FLOAT 4 +#define SIMDBase_MODE_SSE2_DOUBLE 5 +#define SIMDBase_MODE_NEON_FLOAT 6 +#define SIMDBase_MODE_AVX_FLOAT 7 +#define SIMDBase_MODE_AVX_DOUBLE 8 +#define SIMDBase_MODE_ALTIVEC_FLOAT 9 + +#define SIMDBase_LAST_MODE SIMDBase_MODE_ALTIVEC_FLOAT + +#define SIMDBase_PARAMID_MODE_MAX ( 1 | ( 2 << 24 )) +#define SIMDBase_PARAMID_TYPE_AVAILABILITY ( 2 | ( 2 << 24 )) +#define SIMDBase_PARAMID_SIZE_OF_REAL ( 3 | ( 2 << 24 )) +#define SIMDBase_PARAMID_SIZE_OF_VECT ( 4 | ( 2 << 24 )) +#define SIMDBase_PARAMID_VECTOR_LEN ( 5 | ( 2 << 24 )) +#define SIMDBase_PARAMID_MODE_AVAILABILITY ( 6 | ( 2 << 24 )) +#define SIMDBase_PARAMID_MODE_NAME ( 7 | ( 2 << 24 )) + +// + +typedef struct { + uint32_t linesize; + uint32_t size[8], assoc[8]; +} CacheParam; + +void *SIMDBase_alignedMalloc(uint64_t size); +void SIMDBase_alignedFree(void *ptr); +int32_t SIMDBase_sizeOfCachelineInByte(); +int32_t SIMDBase_sizeOfDataCacheInByte(); +int32_t SIMDBase_chooseBestMode(int32_t typeId); +char *SIMDBase_getProcessorNameString(); +int32_t SIMDBase_detect(int32_t paramId); +int32_t SIMDBase_getParamInt(int32_t paramId); +int32_t SIMDBase_getTypeParamInt(int32_t paramId, int32_t typeId); +int32_t SIMDBase_getModeParamInt(int32_t paramId, int32_t mode); +char *SIMDBase_getModeParamString(int32_t paramId, int32_t mode); + +#endif diff --git a/plugins/supereq/nsfft-1.00/simd/SIMDBaseUndiff.c b/plugins/supereq/nsfft-1.00/simd/SIMDBaseUndiff.c new file mode 100644 index 00000000..257a5ff0 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/SIMDBaseUndiff.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "SIMDBase.h" +#include "SIMDBaseUndiff.h" + +void SIMDBaseUndiff_DETECT() { + extern uint8_t detectBuffer[256]; + SIMDBase_VECT a = SIMDBase_LOAD((SIMDBase_VECT *)&detectBuffer[0]); + SIMDBase_VECT b = SIMDBase_LOAD((SIMDBase_VECT *)&detectBuffer[64]); + SIMDBase_VECT c = SIMDBase_ADDi(a, b); + SIMDBase_STOR((SIMDBase_VECT *)&detectBuffer[128], c); +} + +int32_t SIMDBaseUndiff_GETMODEPARAMINT(int32_t paramId) { + switch(paramId) { + case SIMDBase_PARAMID_SIZE_OF_REAL: + return sizeof(SIMDBase_REAL); + case SIMDBase_PARAMID_SIZE_OF_VECT: + return sizeof(SIMDBase_VECT); + case SIMDBase_PARAMID_VECTOR_LEN: + return SIMDBase_VECTLEN; + case SIMDBase_PARAMID_MODE_AVAILABILITY: + return SIMDBase_detect(paramId); + } + + return -1; +} + +char * SIMDBaseUndiff_GETMODEPARAMSTRING(int32_t paramId) { + switch(paramId) { + case SIMDBase_PARAMID_MODE_NAME: + return SIMDBase_NAME; + } + + return NULL; +} diff --git a/plugins/supereq/nsfft-1.00/simd/SIMDBaseUndiff.h b/plugins/supereq/nsfft-1.00/simd/SIMDBaseUndiff.h new file mode 100644 index 00000000..1af849a8 --- /dev/null +++ b/plugins/supereq/nsfft-1.00/simd/SIMDBaseUndiff.h @@ -0,0 +1,231 @@ +#ifndef _SIMDBaseUndiff_H_ +#define _SIMDBaseUndiff_H_ + +#if defined(ENABLE_PUREC_FLOAT) //////////////////////////////////////////// + +typedef float SIMDBase_REAL; +typedef float SIMDBase_VECT; + +#define SIMDBase_MODE 1 +#define SIMDBase_TYPE SIMDBase_TYPE_FLOAT +#define SIMDBase_VECTLEN 1 +#define SIMDBase_NAME "Pure C float" +#define SIMDBaseUndiff_DETECT detect_purec_float +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_purec_float +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_purec_float + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return *p; } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { *p = u; } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return f; } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return *p; } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return u + v; } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return u - v; } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return u * v; } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return -u; } + +#elif defined(ENABLE_PUREC_DOUBLE) //////////////////////////////////////////// + +typedef double SIMDBase_REAL; +typedef double SIMDBase_VECT; + +#define SIMDBase_MODE 2 +#define SIMDBase_TYPE SIMDBase_TYPE_DOUBLE +#define SIMDBase_VECTLEN 1 +#define SIMDBase_NAME "Pure C double" +#define SIMDBaseUndiff_DETECT detect_purec_double +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_purec_double +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_purec_double + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return *p; } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { *p = u; } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return f; } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return *p; } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return u + v; } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return u - v; } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return u * v; } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return -u; } + +#elif defined(ENABLE_PUREC_LONGDOUBLE) //////////////////////////////////////////// + +typedef long double SIMDBase_REAL; +typedef long double SIMDBase_VECT; + +#define SIMDBase_MODE 3 +#define SIMDBase_TYPE SIMDBase_TYPE_LONGDOUBLE +#define SIMDBase_VECTLEN 1 +#define SIMDBase_NAME "Pure C long double" +#define SIMDBaseUndiff_DETECT detect_purec_longdouble +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_purec_longdouble +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_purec_longdouble + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return *p; } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { *p = u; } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return f; } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return *p; } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return u + v; } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return u - v; } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return u * v; } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return -u; } + +#elif defined(ENABLE_SSE_FLOAT) //////////////////////////////////////////// + +#include <xmmintrin.h> + +typedef float SIMDBase_REAL; +typedef __m128 SIMDBase_VECT; + +#define SIMDBase_MODE 4 +#define SIMDBase_TYPE SIMDBase_TYPE_FLOAT +#define SIMDBase_VECTLEN 4 +#define SIMDBase_NAME "x86 SSE float" +#define SIMDBaseUndiff_DETECT detect_sse_float +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_sse_float +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_sse_float + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return _mm_load_ps((float *)p); } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { _mm_store_ps((float *)p, u); } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return _mm_set1_ps(f); } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return _mm_load1_ps(p); } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm_add_ps(u, v); } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm_sub_ps(u, v); } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm_mul_ps(u, v); } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return _mm_xor_ps(u, _mm_set_ps(-0.0f, -0.0f, -0.0f, -0.0f)); } + +#elif defined(ENABLE_SSE2_DOUBLE) //////////////////////////////////////////// + +#include <emmintrin.h> + +typedef double SIMDBase_REAL; +typedef __m128d SIMDBase_VECT; + +#define SIMDBase_MODE 5 +#define SIMDBase_TYPE SIMDBase_TYPE_DOUBLE +#define SIMDBase_VECTLEN 2 +#define SIMDBase_NAME "x86 SSE2 double" +#define SIMDBaseUndiff_DETECT detect_sse2_double +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_sse2_double +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_sse2_double + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return _mm_load_pd((double *)p); } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { _mm_store_pd((double *)p, u); } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return _mm_set1_pd(f); } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return _mm_load1_pd(p); } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm_add_pd(u, v); } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm_sub_pd(u, v); } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm_mul_pd(u, v); } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return _mm_xor_pd(u, _mm_set_pd(-0.0, -0.0)); } + +#elif defined(ENABLE_NEON_FLOAT) //////////////////////////////////////////// + +#include <arm_neon.h> + +typedef float32_t SIMDBase_REAL; +typedef float32x4_t SIMDBase_VECT; + +#define SIMDBase_MODE 6 +#define SIMDBase_TYPE SIMDBase_TYPE_FLOAT +#define SIMDBase_VECTLEN 4 +#define SIMDBase_NAME "ARM NEON float" +#define SIMDBaseUndiff_DETECT detect_neon_float +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_neon_float +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_neon_float + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return vld1q_f32((float32_t *)p); } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { vst1q_f32((float32_t *)p, u); } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return vdupq_n_f32(f); } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return vdupq_n_f32(*p); } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return vaddq_f32(u, v); } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return vsubq_f32(u, v); } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return vmulq_f32(u, v); } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { + return vreinterpretq_f32_u32( veorq_u32(vreinterpretq_u32_f32(u), vdupq_n_u32(0x80000000U))); +} + +#define SIMDBase_FMADD_AVAILABLE + +static inline SIMDBase_VECT SIMDBase_FMADDi(SIMDBase_VECT u, SIMDBase_VECT v, SIMDBase_VECT w) { return vmlaq_f32(w, u, v); } // w + u * v +static inline SIMDBase_VECT SIMDBase_FMSUBi(SIMDBase_VECT u, SIMDBase_VECT v, SIMDBase_VECT w) { return vmlsq_f32(w, u, v); } // w - u * v + +#elif defined(ENABLE_AVX_FLOAT) //////////////////////////////////////////// + +#include <immintrin.h> + +typedef float SIMDBase_REAL; +typedef __m256 SIMDBase_VECT; + +#define SIMDBase_MODE 7 +#define SIMDBase_TYPE SIMDBase_TYPE_FLOAT +#define SIMDBase_VECTLEN 8 +#define SIMDBase_NAME "x86 AVX float" +#define SIMDBaseUndiff_DETECT detect_avx_float +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_avx_float +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_avx_float + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return _mm256_load_ps((float *)p); } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { _mm256_store_ps((float *)p, u); } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return _mm256_set1_ps(f); } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return _mm256_set1_ps(*p); } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm256_add_ps(u, v); } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm256_sub_ps(u, v); } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm256_mul_ps(u, v); } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return _mm256_xor_ps(u, _mm256_set1_ps(-0.0f)); } + +#elif defined(ENABLE_AVX_DOUBLE) //////////////////////////////////////////// + +#include <immintrin.h> + +typedef double SIMDBase_REAL; +typedef __m256d SIMDBase_VECT; + +#define SIMDBase_MODE 8 +#define SIMDBase_TYPE SIMDBase_TYPE_DOUBLE +#define SIMDBase_VECTLEN 4 +#define SIMDBase_NAME "x86 AVX double" +#define SIMDBaseUndiff_DETECT detect_avx_double +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_avx_double +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_avx_double + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return _mm256_load_pd((double *)p); } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { _mm256_store_pd((double *)p, u); } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return _mm256_set1_pd(f); } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return _mm256_set1_pd(*p); } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm256_add_pd(u, v); } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm256_sub_pd(u, v); } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return _mm256_mul_pd(u, v); } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return _mm256_xor_pd(u, _mm256_set1_pd(-0.0)); } + +#elif defined(ENABLE_ALTIVEC_FLOAT) //////////////////////////////////////////// + +#include <altivec.h> + +typedef float SIMDBase_REAL; +typedef vector float SIMDBase_VECT; + +#define SIMDBase_MODE 9 +#define SIMDBase_TYPE SIMDBase_TYPE_FLOAT +#define SIMDBase_VECTLEN 4 +#define SIMDBase_NAME "PowerPC AltiVec float" +#define SIMDBaseUndiff_DETECT detect_altivec_float +#define SIMDBaseUndiff_GETMODEPARAMINT getModeParamInt_altivec_float +#define SIMDBaseUndiff_GETMODEPARAMSTRING getModeParamString_altivec_float + +static inline SIMDBase_VECT SIMDBase_LOAD(SIMDBase_VECT *p) { return vec_ld(0, p); } +static inline void SIMDBase_STOR(SIMDBase_VECT *p, SIMDBase_VECT u) { vec_st(u, 0, p); } +static inline SIMDBase_VECT SIMDBase_SET1(SIMDBase_REAL f) { return (vector float){f, f, f, f}; } +static inline SIMDBase_VECT SIMDBase_LOAD1(SIMDBase_REAL *p) { return (vector float){*p, *p, *p, *p}; } +static inline SIMDBase_VECT SIMDBase_ADDi(SIMDBase_VECT u, SIMDBase_VECT v) { return vec_add(u, v); } +static inline SIMDBase_VECT SIMDBase_SUBi(SIMDBase_VECT u, SIMDBase_VECT v) { return vec_sub(u, v); } +static inline SIMDBase_VECT SIMDBase_MULi(SIMDBase_VECT u, SIMDBase_VECT v) { return vec_madd(u, v, (vector float){0, 0, 0, 0}); } +static inline SIMDBase_VECT SIMDBase_NEGi(SIMDBase_VECT u) { return vec_xor(u, (vector float){-0.0f, -0.0f, -0.0f, -0.0f}); } + +#define SIMDBase_FMADD_AVAILABLE + +static inline SIMDBase_VECT SIMDBase_FMADDi(SIMDBase_VECT u, SIMDBase_VECT v, SIMDBase_VECT w) { return vec_madd(u, v, w); } // w + u * v +static inline SIMDBase_VECT SIMDBase_FMSUBi(SIMDBase_VECT u, SIMDBase_VECT v, SIMDBase_VECT w) { return vec_nmsub(u, v, w); } // w - u * v + +#endif //////////////////////////////////////////////////////////////////// + +static inline SIMDBase_VECT SIMDBase_ADDm(SIMDBase_VECT *p, SIMDBase_VECT *q) { return SIMDBase_ADDi(SIMDBase_LOAD(p), SIMDBase_LOAD(q)); } +static inline SIMDBase_VECT SIMDBase_SUBm(SIMDBase_VECT *p, SIMDBase_VECT *q) { return SIMDBase_SUBi(SIMDBase_LOAD(p), SIMDBase_LOAD(q)); } + +#endif diff --git a/plugins/supereq/paramlist.hpp b/plugins/supereq/paramlist.hpp index 0c513b78..9c5b09c4 100644 --- a/plugins/supereq/paramlist.hpp +++ b/plugins/supereq/paramlist.hpp @@ -1,4 +1,22 @@ -//#include <iostream.h>
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>
+ Original SuperEQ code (C) Naoki Shibata <shibatch@users.sf.net>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -7,12 +25,10 @@ class paramlistelm { public:
class paramlistelm *next;
- char left,right;
float lower,upper,gain,gain2;
int sortindex;
paramlistelm(void) {
- left = right = 1;
lower = upper = gain = 0;
next = NULL;
};
@@ -21,13 +37,6 @@ public: delete next;
next = NULL;
};
-
- char *getString(void) {
- static char str[64];
- sprintf(str,"%gHz to %gHz, %gdB %c%c",
- (double)lower,(double)upper,(double)gain,left?'L':' ',right?'R':' ');
- return str;
- }
};
class paramlist {
@@ -52,8 +61,6 @@ public: for(p=&elm,q=src.elm;q != NULL;q = q->next,p = &(*p)->next)
{
*p = new paramlistelm;
- (*p)->left = q->left;
- (*p)->right = q->right;
(*p)->lower = q->lower;
(*p)->upper = q->upper;
(*p)->gain = q->gain;
diff --git a/plugins/supereq/shibatch_rdft.c b/plugins/supereq/shibatch_rdft.c new file mode 100644 index 00000000..db453eb8 --- /dev/null +++ b/plugins/supereq/shibatch_rdft.c @@ -0,0 +1,71 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <stdint.h> + +#include "SIMDBase.h" +#include "DFT.h" + +#define TYPE SIMDBase_TYPE_FLOAT + +void rfft(int n,int isign,float *x) { + static DFT *p = NULL; + static float *buf = NULL; + static int ipsize = 0; + static int mode = 0; + static int veclen = 0; + int newipsize; + if (n == 0) { + if (buf) { + SIMDBase_alignedFree (buf); + buf = NULL; + } + if (p) { + DFT_dispose(p, mode); + p = NULL; + } + return; + } + int nn = n; + n = 1<<n; + newipsize = n; + if (newipsize != ipsize) { + ipsize = newipsize; + + if (buf) { + SIMDBase_alignedFree (buf); + buf = NULL; + } + + if (p) { + DFT_dispose(p, mode); + p = NULL; + } + + buf = SIMDBase_alignedMalloc (n * sizeof (float)); + + mode = SIMDBase_chooseBestMode(TYPE); + veclen = SIMDBase_getModeParamInt(SIMDBase_PARAMID_VECTOR_LEN, mode); + int sizeOfVect = SIMDBase_getModeParamInt(SIMDBase_PARAMID_SIZE_OF_VECT, mode); + printf ("n: %d, veclen: %d, sizeOfVect: %d\n", n, veclen, sizeOfVect); + p = DFT_init(mode, n/veclen, DFT_FLAG_REAL); + } + + // store in simd order + int asize = n / veclen; + int i, j; + for(j=0;j<veclen;j++) { + for (i = 0; i < asize; i++) { + buf[i * veclen + j] = x[j * asize + i]; + } + } + + DFT_execute(p, mode, buf, isign); + +#define THRES 1e-3 + for(j=0;j<veclen;j++) { + for (i = 0; i < asize; i++) { + x[j * asize + i] = buf[i * veclen + j]; + } + } +} diff --git a/plugins/supereq/supereq.c b/plugins/supereq/supereq.c index af4000fd..a773b4ef 100644 --- a/plugins/supereq/supereq.c +++ b/plugins/supereq/supereq.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -18,217 +18,301 @@ */ #include <stdio.h> #include <string.h> +#include <stdlib.h> +#include <math.h> #include "../../deadbeef.h" -#include "supereq.h" +#include "Equ.h" static DB_functions_t *deadbeef; -static DB_supereq_dsp_t plugin; - -void *paramlist_alloc (void); -void paramlist_free (void *); -void equ_makeTable(float *lbc,float *rbc,void *param,float fs); -int equ_modifySamples(char *buf,int nsamples,int nch,int bps); -void equ_clearbuf(int bps,int srate); -void equ_init(int wb); -void equ_quit(void); - -void supereq_reset (void); - -static float last_srate = 0; -static int last_nch = 0, last_bps = 0; -static float lbands[18] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; -static float rbands[18] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; -static float preamp = 1; -static void *paramsroot; - -static int params_changed = 0; -static intptr_t tid = 0; -static uintptr_t mutex = 0; -static int enabled = 0; - -static int -supereq_on_configchanged (DB_event_t *ev, uintptr_t data) { - int e = deadbeef->conf_get_int ("supereq.enable", 0); - if (e != enabled) { - if (e) { - supereq_reset (); - } - enabled = e; - } - - return 0; -} +static DB_dsp_t plugin; + +typedef struct { + ddb_dsp_context_t ctx; + float last_srate; + int last_nch; + float bands[18]; + float preamp; + void *paramsroot; + int params_changed; + uintptr_t mutex; + SuperEqState state; + int enabled; +} ddb_supereq_ctx_t; + +void supereq_reset (ddb_dsp_context_t *ctx); void -recalc_table (void) { +recalc_table (ddb_supereq_ctx_t *eq) { void *params = paramlist_alloc (); - deadbeef->mutex_lock (mutex); - float lbands_copy[18]; - float rbands_copy[18]; - float srate = last_srate; - memcpy (lbands_copy, lbands, sizeof (lbands)); - memcpy (rbands_copy, rbands, sizeof (rbands)); + deadbeef->mutex_lock (eq->mutex); + float bands_copy[18]; + float srate = eq->last_srate; + memcpy (bands_copy, eq->bands, sizeof (eq->bands)); for (int i = 0; i < 18; i++) { - lbands_copy[i] *= preamp; - rbands_copy[i] *= preamp; + bands_copy[i] *= eq->preamp; } - deadbeef->mutex_unlock (mutex); + deadbeef->mutex_unlock (eq->mutex); - equ_makeTable (lbands_copy, rbands_copy, params, srate); + equ_makeTable (&eq->state, bands_copy, params, srate); - deadbeef->mutex_lock (mutex); - paramlist_free (paramsroot); - paramsroot = params; - deadbeef->mutex_unlock (mutex); + deadbeef->mutex_lock (eq->mutex); + paramlist_free (eq->paramsroot); + eq->paramsroot = params; + deadbeef->mutex_unlock (eq->mutex); } int supereq_plugin_start (void) { - enabled = deadbeef->conf_get_int ("supereq.enable", 0); - // load bands from config - preamp = deadbeef->conf_get_float ("eq.preamp", 1); - for (int i = 0; i < 18; i++) { - char key[100]; - snprintf (key, sizeof (key), "eq.band%d", i); - lbands[i] = rbands[i] = deadbeef->conf_get_float (key, 1); - } - - equ_init (14); - paramsroot = paramlist_alloc (); - last_srate = 44100; - last_nch = 2; - last_bps = 16; - mutex = deadbeef->mutex_create (); - recalc_table (); - equ_clearbuf (last_bps,last_srate); - deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (supereq_on_configchanged), 0); return 0; } int supereq_plugin_stop (void) { - deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (supereq_on_configchanged), 0); - if (tid) { - deadbeef->thread_join (tid); - tid = 0; - } - if (mutex) { - deadbeef->mutex_free (mutex); - mutex = 0; - } - equ_quit (); - paramlist_free (paramsroot); return 0; } -void -supereq_regen_table_thread (void *param) { - recalc_table (); - tid = 0; -} - int -supereq_process_int16 (int16_t *samples, int nsamples, int nch, int bps, int srate) { - if ((nch != 1 && nch != 2) || (bps != 8 && bps != 16 && bps != 24)) return nsamples; - if (params_changed && !tid) { - tid = deadbeef->thread_start (supereq_regen_table_thread, NULL); - params_changed = 0; +supereq_process (ddb_dsp_context_t *ctx, float *samples, int frames, int maxframes, ddb_waveformat_t *fmt, float *r) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + if (supereq->enabled != ctx->enabled) { + if (ctx->enabled && !supereq->enabled) { + supereq_reset (ctx); + } + supereq->enabled = ctx->enabled; + +// this causes a glitch on 1st track +// DB_playItem_t *it = deadbeef->streamer_get_playing_track (); +// if (it) { +// float playpos = deadbeef->streamer_get_playpos (); +// deadbeef->streamer_seek (playpos); +// deadbeef->pl_item_unref (it); +// } } - if (last_srate != srate) { - deadbeef->mutex_lock (mutex); - //equ_makeTable (lbands, rbands, paramsroot, srate); - last_srate = srate; - last_nch = nch; - last_bps = bps; - recalc_table (); - deadbeef->mutex_unlock (mutex); - equ_clearbuf(bps,srate); + if (supereq->params_changed) { + recalc_table (supereq); + supereq->params_changed = 0; } - else if (last_nch != nch || last_bps != bps) { - deadbeef->mutex_lock (mutex); - last_nch = nch; - last_bps = bps; - deadbeef->mutex_unlock (mutex); - equ_clearbuf(bps,srate); + if (supereq->last_srate != fmt->samplerate || supereq->last_nch != fmt->channels) { + deadbeef->mutex_lock (supereq->mutex); + supereq->last_srate = fmt->samplerate; + supereq->last_nch = fmt->channels; + equ_init (&supereq->state, 10, fmt->channels); + recalc_table (supereq); + equ_clearbuf(&supereq->state); + deadbeef->mutex_unlock (supereq->mutex); } - equ_modifySamples((char *)samples,nsamples,nch,bps); - return nsamples; + equ_modifySamples_float(&supereq->state, (char *)samples,frames,fmt->channels); + return frames; } float -supereq_get_band (int band) { - return lbands[band]; +supereq_get_band (ddb_dsp_context_t *ctx, int band) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + return supereq->bands[band]; } void -supereq_set_band (int band, float value) { - deadbeef->mutex_lock (mutex); - lbands[band] = rbands[band] = value; - deadbeef->mutex_unlock (mutex); - params_changed = 1; - char key[100]; - snprintf (key, sizeof (key), "eq.band%d", band); - deadbeef->conf_set_float (key, value); +supereq_set_band (ddb_dsp_context_t *ctx, int band, float value) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + deadbeef->mutex_lock (supereq->mutex); + supereq->bands[band] = value; + deadbeef->mutex_unlock (supereq->mutex); + supereq->params_changed = 1; } float -supereq_get_preamp (void) { - return preamp; +supereq_get_preamp (ddb_dsp_context_t *ctx) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + return supereq->preamp; } void -supereq_set_preamp (float value) { - deadbeef->mutex_lock (mutex); - preamp = value; - deadbeef->mutex_unlock (mutex); - params_changed = 1; - deadbeef->conf_set_float ("eq.preamp", value); +supereq_set_preamp (ddb_dsp_context_t *ctx, float value) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + deadbeef->mutex_lock (supereq->mutex); + supereq->preamp = value; + deadbeef->mutex_unlock (supereq->mutex); + supereq->params_changed = 1; } void -supereq_reset (void) { - deadbeef->mutex_lock (mutex); - equ_clearbuf(last_bps,last_srate); - deadbeef->mutex_unlock (mutex); +supereq_reset (ddb_dsp_context_t *ctx) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + deadbeef->mutex_lock (supereq->mutex); + equ_clearbuf(&supereq->state); + deadbeef->mutex_unlock (supereq->mutex); +} + +int +supereq_num_params (void) { + return 19; +} + +static const char *bandnames[] = { + "Preamp", + "55 Hz", + "77 Hz", + "110 Hz", + "156 Hz", + "220 Hz", + "311 Hz", + "440 Hz", + "622 Hz", + "880 Hz", + "1.2 kHz", + "1.8 kHz", + "2.5 kHz", + "3.5 kHz", + "5 kHz", + "7 kHz", + "10 kHz", + "14 kHz", + "20 kHz" +}; + +const char * +supereq_get_param_name (int p) { + return bandnames[p]; +} + + +static inline float +db_to_amp (float dB) { + const float ln10=2.3025850929940002f; + return exp(ln10*dB/20.f); +} + +static inline float +amp_to_db (float amp) { + return 20*log10 (amp); } void -supereq_enable (int e) { - if (e != enabled) { - deadbeef->conf_set_int ("supereq.enable", e); - if (e && !enabled) { - supereq_reset (); - } - enabled = e; +supereq_set_param (ddb_dsp_context_t *ctx, int p, const char *val) { + switch (p) { + case 0: + supereq_set_preamp (ctx, db_to_amp (atof (val))); + break; + case 1 ... 18: + supereq_set_band (ctx, p-1, db_to_amp (atof (val))); + break; + default: + fprintf (stderr, "supereq_set_param: invalid param index (%d)\n", p); } } -int -supereq_enabled (void) { - return enabled; -} - -static DB_supereq_dsp_t plugin = { - .dsp.plugin.api_vmajor = DB_API_VERSION_MAJOR, - .dsp.plugin.api_vminor = DB_API_VERSION_MINOR, - .dsp.plugin.type = DB_PLUGIN_DSP, - .dsp.plugin.id = "supereq", - .dsp.plugin.name = "SuperEQ", - .dsp.plugin.descr = "equalizer plugin using SuperEQ library by Naoki Shibata", - .dsp.plugin.author = "Alexey Yakovenko", - .dsp.plugin.email = "waker@users.sourceforge.net", - .dsp.plugin.website = "http://deadbeef.sf.net", - .dsp.plugin.start = supereq_plugin_start, - .dsp.plugin.stop = supereq_plugin_stop, - .dsp.process_int16 = supereq_process_int16, - .dsp.reset = supereq_reset, - .dsp.enable = supereq_enable, - .dsp.enabled = supereq_enabled, - .get_band = supereq_get_band, - .set_band = supereq_set_band, - .get_preamp = supereq_get_preamp, - .set_preamp = supereq_set_preamp, +void +supereq_get_param (ddb_dsp_context_t *ctx, int p, char *v, int sz) { + switch (p) { + case 0: + snprintf (v, sz, "%f", amp_to_db (supereq_get_preamp (ctx))); + break; + case 1 ... 18: + snprintf (v, sz, "%f", amp_to_db (supereq_get_band (ctx, p-1))); + break; + default: + fprintf (stderr, "supereq_get_param: invalid param index (%d)\n", p); + } +} + + +ddb_dsp_context_t* +supereq_open (void) { + ddb_supereq_ctx_t *supereq = malloc (sizeof (ddb_supereq_ctx_t)); + DDB_INIT_DSP_CONTEXT (supereq,ddb_supereq_ctx_t,&plugin); + + equ_init (&supereq->state, 10, 2); + supereq->paramsroot = paramlist_alloc (); + supereq->last_srate = 44100; + supereq->last_nch = 2; + supereq->mutex = deadbeef->mutex_create (); + supereq->preamp = 1; + for (int i = 0; i < 18; i++) { + supereq->bands[i] = 1; + } + recalc_table (supereq); + equ_clearbuf (&supereq->state); + + return (ddb_dsp_context_t*)supereq; +} + +void +supereq_close (ddb_dsp_context_t *ctx) { + ddb_supereq_ctx_t *supereq = (ddb_supereq_ctx_t *)ctx; + if (supereq->mutex) { + deadbeef->mutex_free (supereq->mutex); + supereq->mutex = 0; + } + equ_quit (&supereq->state); + paramlist_free (supereq->paramsroot); + free (ctx); +} + +static const char settings_dlg[] = + "property \"\" hbox[19] hmg fill expand border=0 spacing=8 height=200;\n" + "property \"Preamp\" vscale[20,-20,1] vert 0 0;\n" + "property \"55 Hz\" vscale[20,-20,1] vert 1 0;\n" + "property \"77 Hz\" vscale[20,-20,1] vert 2 0;\n" + "property \"110 Hz\" vscale[20,-20,1] vert 3 0;\n" + "property \"156 Hz\" vscale[20,-20,1] vert 4 0;\n" + "property \"220 Hz\" vscale[20,-20,1] vert 5 0;\n" + "property \"311 Hz\" vscale[20,-20,1] vert 6 0;\n" + "property \"440 Hz\" vscale[20,-20,1] vert 7 0;\n" + "property \"622 Hz\" vscale[20,-20,1] vert 8 0;\n" + "property \"880 Hz\" vscale[20,-20,1] vert 9 0;\n" + "property \"1.2 kHz\" vscale[20,-20,1] vert 10 0;\n" + "property \"1.8 kHz\" vscale[20,-20,1] vert 11 0;\n" + "property \"2.5 kHz\" vscale[20,-20,1] vert 12 0;\n" + "property \"3.5 kHz\" vscale[20,-20,1] vert 13 0;\n" + "property \"5 kHz\" vscale[20,-20,1] vert 14 0;\n" + "property \"7 kHz\" vscale[20,-20,1] vert 15 0;\n" + "property \"10 kHz\" vscale[20,-20,1] vert 16 0;\n" + "property \"14 kHz\" vscale[20,-20,1] vert 17 0;\n" + "property \"20 kHz\" vscale[20,-20,1] vert 18 0;\n" +; + +static DB_dsp_t plugin = { + .plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.api_vminor = DB_API_VERSION_MINOR, + .plugin.version_major = 1, + .plugin.version_minor = 0, + .plugin.type = DB_PLUGIN_DSP, + .plugin.id = "supereq", + .plugin.name = "SuperEQ", + .plugin.descr = "equalizer plugin using SuperEQ library", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses supereq library by Naoki Shibata, http://shibatch.sourceforge.net\n" + "Uses FFT library by Takuya Ooura, http://www.kurims.kyoto-u.ac.jp/~ooura/\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = supereq_plugin_start, + .plugin.stop = supereq_plugin_stop, + .open = supereq_open, + .close = supereq_close, + .process = supereq_process, + .reset = supereq_reset, + .num_params = supereq_num_params, + .get_param_name = supereq_get_param_name, + .set_param = supereq_set_param, + .get_param = supereq_get_param, + .configdialog = settings_dlg, }; DB_plugin_t * diff --git a/plugins/tta/filter.h b/plugins/tta/filter.h index 6e8b13da..1f86cbb1 100644 --- a/plugins/tta/filter.h +++ b/plugins/tta/filter.h @@ -42,7 +42,7 @@ ///////// Filter Settings ////////// static int flt_set[3] = {10, 9, 10}; -__inline void +static __inline void memshl (register int *pA, register int *pB) { *pA++ = *pB++; *pA++ = *pB++; @@ -54,7 +54,7 @@ memshl (register int *pA, register int *pB) { *pA = *pB; } -__inline void +static __inline void hybrid_filter (fltst *fs, int *in) { register int *pA = fs->dl; register int *pB = fs->qm; diff --git a/plugins/tta/ttadec.c b/plugins/tta/ttadec.c index d5466359..7a33d072 100644 --- a/plugins/tta/ttadec.c +++ b/plugins/tta/ttadec.c @@ -267,9 +267,7 @@ int open_tta_file (const char *filename, tta_info *info, unsigned int data_offse info->HANDLE = infile; // get file size - deadbeef->fseek (infile, 0, SEEK_END); - info->FILESIZE = deadbeef->ftell (infile); - deadbeef->fseek (infile, 0, SEEK_SET); + info->FILESIZE = deadbeef->fgetlength (infile); // read id3 tags if (!data_offset) { diff --git a/plugins/tta/ttaplug.c b/plugins/tta/ttaplug.c index 196df859..20a0c8a5 100644 --- a/plugins/tta/ttaplug.c +++ b/plugins/tta/ttaplug.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #include <assert.h> #include <limits.h> #include <unistd.h> +#include <math.h> #include "ttadec.h" #ifdef HAVE_CONFIG_H # include <config.h> @@ -49,7 +50,7 @@ typedef struct { } tta_info_t; static DB_fileinfo_t * -tta_open (void) { +tta_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (tta_info_t)); tta_info_t *info = (tta_info_t *)_info; memset (info, 0, sizeof (tta_info_t)); @@ -60,20 +61,23 @@ static int tta_init (DB_fileinfo_t *_info, DB_playItem_t *it) { tta_info_t *info = (tta_info_t *)_info; - trace ("open_tta_file %s\n", it->fname); - if (open_tta_file (it->fname, &info->tta, 0) != 0) { - fprintf (stderr, "tta: failed to open %s\n", it->fname); + trace ("open_tta_file %s\n", deadbeef->pl_find_meta (it, ":URI")); + if (open_tta_file (deadbeef->pl_find_meta (it, ":URI"), &info->tta, 0) != 0) { + fprintf (stderr, "tta: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } if (player_init (&info->tta) != 0) { - fprintf (stderr, "tta: failed to init player for %s\n", it->fname); + fprintf (stderr, "tta: failed to init player for %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } - _info->bps = info->tta.BPS; - _info->channels = info->tta.NCH; - _info->samplerate = info->tta.SAMPLERATE; + _info->fmt.bps = info->tta.BPS; + _info->fmt.channels = info->tta.NCH; + _info->fmt.samplerate = info->tta.SAMPLERATE; + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } _info->readpos = 0; _info->plugin = &plugin; @@ -86,7 +90,7 @@ tta_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->startsample = 0; info->endsample = (info->tta.DATALENGTH)-1; } - trace ("open_tta_file %s success!\n", it->fname); + trace ("open_tta_file %s success!\n", deadbeef->pl_find_meta (it, ":URI")); return 0; } @@ -101,65 +105,40 @@ tta_free (DB_fileinfo_t *_info) { } static int -tta_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +tta_read (DB_fileinfo_t *_info, char *bytes, int size) { tta_info_t *info = (tta_info_t *)_info; - int out_ch = min (_info->channels, 2); - if (info->currentsample + size / (2 * out_ch) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * out_ch; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; if (size <= 0) { return 0; } } int initsize = size; - int sample_size = 2 * out_ch; while (size > 0) { if (info->samples_to_skip > 0 && info->remaining > 0) { int skip = min (info->remaining, info->samples_to_skip); if (skip < info->remaining) { - memmove (info->buffer, info->buffer + skip * info->tta.BSIZE * info->tta.NCH, (info->remaining - skip) * info->tta.BSIZE * info->tta.NCH); + memmove (info->buffer, info->buffer + skip * samplesize, (info->remaining - skip) * samplesize); } info->remaining -= skip; info->samples_to_skip -= skip; } if (info->remaining > 0) { - int n = size / sample_size; + int n = size / samplesize; n = min (n, info->remaining); int nn = n; char *p = info->buffer; - if (_info->bps > 8) { - // hack: shift buffer, so that we always get 2 most significant bytes (24 bit support) - // same hack kinda works for 8bit, but it's not lossless - p += (_info->bps >> 3) - 2; - while (n > 0) { - *((int16_t*)bytes) = (int16_t)(((uint8_t)p[1]<<8) | (uint8_t)p[0]); - bytes += 2; - if (out_ch == 2) { - *((int16_t*)bytes) = (int16_t)(((uint8_t)(p+info->tta.BSIZE)[1]<<8) | (uint8_t)(p+info->tta.BSIZE)[0]); - bytes += 2; - } - n--; - size -= sample_size; - p += info->tta.NCH * info->tta.BSIZE; - } - p -= (_info->bps >> 3) - 2; - } - else { - while (n > 0) { - *((int16_t*)bytes) = ((int16_t)(p[0])) << 8; - bytes += 2; - if (out_ch == 2) { - *((int16_t*)bytes) = ((int16_t)(p[1])) << 8; - bytes += 2; - } - n--; - size -= sample_size; - p += info->tta.NCH * info->tta.BSIZE; - } - } + + memcpy (bytes, p, n * samplesize); + bytes += n * samplesize; + size -= n * samplesize; + p += n * samplesize; + if (info->remaining > nn) { - memmove (info->buffer, p, (info->remaining - nn) * info->tta.BSIZE * info->tta.NCH); + memmove (info->buffer, p, (info->remaining - nn) * samplesize); } info->remaining -= nn; } @@ -171,7 +150,8 @@ tta_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } } } - info->currentsample += (initsize-size) / sample_size; + info->currentsample += (initsize-size) / samplesize; + deadbeef->streamer_set_bitrate (info->tta.BITRATE); return initsize-size; } @@ -187,14 +167,14 @@ tta_seek_sample (DB_fileinfo_t *_info, int sample) { info->currentsample = sample + info->startsample; info->remaining = 0; - _info->readpos = sample / _info->samplerate; + _info->readpos = sample / _info->fmt.samplerate; return 0; } static int tta_seek (DB_fileinfo_t *_info, float time) { tta_info_t *info = (tta_info_t *)_info; - return tta_seek_sample (_info, time * _info->samplerate); + return tta_seek_sample (_info, time * _info->fmt.samplerate); } static DB_playItem_t * @@ -213,15 +193,16 @@ tta_insert (DB_playItem_t *after, const char *fname) { int totalsamples = tta.DATALENGTH; double dur = tta.LENGTH; - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "TTA"; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "TTA"); deadbeef->pl_set_item_duration (it, dur); close_tta_file (&tta); DB_FILE *fp = deadbeef->fopen (fname); + + int64_t fsize = -1; if (fp) { + fsize = deadbeef->fgetlength (fp); /*int v2err = */deadbeef->junk_id3v2_read (it, fp); /*int v1err = */deadbeef->junk_id3v1_read (it, fp); deadbeef->fclose (fp); @@ -242,6 +223,18 @@ tta_insert (DB_playItem_t *after, const char *fname) { } deadbeef->pl_unlock (); + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + snprintf (s, sizeof (s), "%d", tta.BPS); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", tta.NCH); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", tta.SAMPLERATE); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + snprintf (s, sizeof (s), "%d", tta.BITRATE); + deadbeef->pl_add_meta (it, ":BITRATE", s); + cue = deadbeef->pl_insert_cue (after, it, totalsamples, tta.SAMPLERATE); if (cue) { deadbeef->pl_item_unref (it); @@ -257,7 +250,7 @@ tta_insert (DB_playItem_t *after, const char *fname) { } static int tta_read_metadata (DB_playItem_t *it) { - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -292,7 +285,8 @@ static int tta_write_metadata (DB_playItem_t *it) { } int id3v2_version = 4; - const char *id3v1_encoding = deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1"); + char id3v1_encoding[50]; + deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1", id3v1_encoding, sizeof (id3v1_encoding)); return deadbeef->junk_rewrite_tags (it, junk_flags, id3v2_version, id3v1_encoding); } @@ -313,22 +307,39 @@ static const char *filetypes[] = { "TTA", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "tta", .plugin.name = "tta decoder", .plugin.descr = "tta decoder based on TTA Hardware Players Library Version 1.2", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified TTA Hardware Players Library Version 1.2,\n" + "(c) 2004 Alexander Djourik. All rights reserved.\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = tta_start, .plugin.stop = tta_stop, .open = tta_open, .init = tta_init, .free = tta_free, - .read_int16 = tta_read_int16, -// .read_float32 = tta_read_float32, + .read = tta_read, .seek = tta_seek, .seek_sample = tta_seek_sample, .insert = tta_insert, diff --git a/plugins/uade2/plugin.c b/plugins/uade2/plugin.c new file mode 100644 index 00000000..a07f82fe --- /dev/null +++ b/plugins/uade2/plugin.c @@ -0,0 +1,692 @@ +/* + ddb_input_uade2 - UADE input plugin for DeaDBeeF player + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + based on UADE2 plugin for Audacious, Copyright (C) 2005-2006 Heikki Orsila, UADE TEAM + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <signal.h> +#include "../../deadbeef.h" +#include "uadeipc.h" +#include "eagleplayer.h" +#include "uadeconfig.h" +#include "uadecontrol.h" +#include "uadeconstants.h" +#include "ossupport.h" +#include "uadeconf.h" +#include "effects.h" +#include "sysincludes.h" +#include "songdb.h" +#include "songinfo.h" + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static char configname[PATH_MAX]; +static struct uade_config config_backup; +static time_t config_load_time; +static time_t md5_load_time; +static char md5name[PATH_MAX]; +static char songconfname[PATH_MAX]; +static char gui_filename[PATH_MAX]; + +static const char * +get_uade_base_conf_dir (void) { + return UADE_CONFIG_BASE_DIR; +} + +static void load_content_db(void) +{ + struct stat st; + time_t curtime = time(NULL); + char name[PATH_MAX]; + + if (curtime) + md5_load_time = curtime; + + if (md5name[0] == 0) { + char *home = uade_open_create_home(); + if (home) + snprintf(md5name, sizeof md5name, "%s/.uade2/contentdb", home); + } + + /* User database has priority over global database, so we read it first */ + if (md5name[0]) { + if (stat(md5name, &st) == 0) { + if (uade_read_content_db(md5name)) + return; + } else { + FILE *f = fopen(md5name, "w"); + if (f) + fclose(f); + uade_read_content_db(md5name); + } + } + + snprintf(name, sizeof name, "%s/contentdb.conf", get_uade_base_conf_dir ()); + if (stat(name, &st) == 0) + uade_read_content_db(name); +} + +/* xmms initializes uade by calling this function */ +static void uade_init(void) +{ + static int initialized = 0; + if (!initialized) { + char *home; + int config_loaded; + + config_load_time = time(NULL); + + config_loaded = uade_load_initial_config(configname, sizeof configname, + &config_backup, NULL); + + load_content_db(); + + uade_load_initial_song_conf(songconfname, sizeof songconfname, + &config_backup, NULL); + + home = uade_open_create_home(); + + if (home != NULL) { + /* If config exists in home, ignore global uade.conf. */ + snprintf(configname, sizeof configname, "%s/.uade2/uade.conf", home); + } + + if (config_loaded == 0) { + fprintf(stderr, "No config file found for UADE XMMS plugin. Will try to load config from\n"); + fprintf(stderr, "$HOME/.uade2/uade.conf in the future.\n"); + } + initialized = 1; + } +} + +static void uade_get_song_info(const char *filename, char **title, int *length) +{ + char tempname[PATH_MAX]; + const char *t; + + if (strncmp(filename, "uade://", 7) == 0) + filename += 7; + + strlcpy(tempname, filename, sizeof tempname); + t = basename(tempname); + if (t == NULL) + t = filename; + if ((*title = strdup(t)) == NULL) + trace("Not enough memory for song info.\n"); + *length = -1; +} + +int uade_get_max_subsong(struct uade_state *state, int def) +{ + int subsong; + subsong = -1; + if (state->song != NULL) + subsong = state->song->max_subsong; + if (subsong == -1) + subsong = def; + return subsong; +} + + +int uade_get_min_subsong(struct uade_state *state, int def) +{ + int subsong; + subsong = -1; + if (state->song != NULL) + subsong = state->song->min_subsong; + if (subsong == -1) + subsong = def; + return subsong; +} + +DB_functions_t *deadbeef; +static DB_decoder_t plugin; + +static const char *exts[] = { NULL }; +static const char *prefixes[] = { "mod", NULL }; +static const char *filetypes[] = { "UADE", NULL }; + +#define UADE_BUFFER_SIZE 100000 + +typedef struct { + DB_fileinfo_t info; + struct uade_state state; + int controlstate; + int record_playtime; + int remaining; + char buffer[UADE_BUFFER_SIZE]; + int subsong_end; + int song_end_trigger; + int64_t skip_bytes; + int abort_playing; + int uade_seek_forward; +} uade_info_t; + +DB_fileinfo_t * +uadeplug_open (uint32_t hints) { + DB_fileinfo_t *_info = malloc (sizeof (uade_info_t)); + uade_info_t *info = (uade_info_t *)_info; + memset (info, 0, sizeof (uade_info_t)); + return _info; +} + +static int +uadeplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { + uade_info_t *info = (uade_info_t *)_info; + + uade_init (); + char modulename[PATH_MAX]; + char playername[PATH_MAX]; + char scorename[PATH_MAX]; + char gui_module_filename[PATH_MAX]; + char gui_player_filename[PATH_MAX]; + + info->state.config = config_backup; + info->state.validconfig = 1; + + printf ("probing the file\n"); + int ret = uade_is_our_file(it->fname, 0, &info->state); + + if (!ret) { + trace ("not uade file\n"); + return -1; + } + strlcpy(modulename, it->fname, sizeof modulename); + trace ("modulename: %s\n", modulename); + strlcpy(gui_module_filename, it->fname, sizeof gui_module_filename); + trace ("gui_module_fname: %s\n", gui_module_filename); + + snprintf(scorename, sizeof scorename, "%s/score", get_uade_base_conf_dir ()); + trace ("scorename: %s\n", scorename); + + if (strcmp(info->state.ep->playername, "custom") == 0) { + strlcpy(playername, modulename, sizeof playername); + modulename[0] = 0; + gui_module_filename[0] = 0; + } else { + snprintf(playername, sizeof playername, "%s/players/%s", get_uade_base_conf_dir (), info->state.ep->playername); + } + trace ("playername: %s\n", playername); + + if (!uade_alloc_song(&info->state, it->fname)) { + trace ("uade_alloc_song fail\n"); + return -1; + } + + uade_set_ep_attributes(&info->state); + + uade_set_song_attributes(&info->state, playername, sizeof playername); + + uade_set_effects(&info->state); + + strlcpy(gui_player_filename, playername, sizeof gui_player_filename); + + if (!info->state.pid) { + char configname[PATH_MAX]; + snprintf(configname, sizeof configname, "%s/uaerc", UADE_CONFIG_BASE_DIR); + uade_spawn(&info->state, UADE_CONFIG_UADE_CORE, configname); + } + + printf ("uade_song_initialization\n"); + ret = uade_song_initialization(scorename, playername, modulename, &info->state); + if (ret) { + if (ret != UADECORE_CANT_PLAY && ret != UADECORE_INIT_ERROR) { + fprintf(stderr, "Can not initialize song. Unknown error.\n"); + return -1; + } + uade_unalloc_song(&info->state); + return -1; + } + printf ("init done\n"); + int minsong = uade_get_min_subsong (&info->state, 0); + int maxsong = uade_get_max_subsong (&info->state, 0); + info->state.song->cur_subsong = it->tracknum; + uade_change_subsong(&info->state); + + _info->fmt.bps = 16; + _info->fmt.channels = UADE_CHANNELS; + _info->fmt.samplerate = info->state.config.frequency; + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } + _info->readpos = 0; + _info->plugin = &plugin; + info->record_playtime = 1; + info->controlstate = UADE_S_STATE; + + return 0; +} + +// free everything allocated in _init +static void +uadeplug_free (DB_fileinfo_t *_info) { + uade_info_t *info = (uade_info_t *)_info; + if (info) { + uade_unalloc_song(&info->state); + if (info->state.pid) { + kill(info->state.pid, SIGTERM); + } + free (info); + } +} + +int +uadeplug_frame (uade_info_t *info) { + uint8_t space[UADE_MAX_MESSAGE_SIZE]; + struct uade_msg *um = (struct uade_msg *) space; + uint16_t *sm; + int i; + unsigned int play_bytes, tailbytes = 0; + uint64_t subsong_bytes = 0; + int framesize = UADE_CHANNELS * UADE_BYTES_PER_SAMPLE; + int left = 0; + char gui_formatname[256]; + char gui_modulename[256]; + char gui_playername[256]; + char *reason; + uint32_t *u32ptr; + int frame_received = 0; + + while (!frame_received) { + if (info->controlstate == UADE_S_STATE) { + + assert(left == 0); + + if (info->abort_playing) { + info->record_playtime = 0; + break; + } + + if (info->uade_seek_forward) { + info->skip_bytes += info->uade_seek_forward * (UADE_BYTES_PER_FRAME * info->state.config.frequency); + info->uade_seek_forward = 0; + } + + left = uade_read_request(&info->state.ipc); + + if (uade_send_short_message(UADE_COMMAND_TOKEN, &info->state.ipc)) { + fprintf(stderr, "Can not send token.\n"); + return -1; + } + info->controlstate = UADE_R_STATE; + + } else { + + if (uade_receive_message(um, sizeof(space), &info->state.ipc) <= 0) { + fprintf(stderr, "Can not receive events from uade\n"); + exit(-1); + } + + + switch (um->msgtype) { + + case UADE_COMMAND_TOKEN: + info->controlstate = UADE_S_STATE; + break; + + case UADE_REPLY_DATA: + sm = (uint16_t *) um->data; + for (i = 0; i < um->size; i += 2) { + *sm = ntohs(*sm); + sm++; + } + + if (info->subsong_end) { + play_bytes = tailbytes; + tailbytes = 0; + } else { + play_bytes = um->size; + } + + if (info->subsong_end == 0 && info->song_end_trigger == 0 && + uade_test_silence(um->data, play_bytes, &info->state)) { + info->subsong_end = 1; + } + + subsong_bytes += play_bytes; + info->state.song->out_bytes += play_bytes; + + if (info->skip_bytes > 0) { + if (play_bytes <= info->skip_bytes) { + info->skip_bytes -= play_bytes; + play_bytes = 0; + } else { + play_bytes -= info->skip_bytes; + info->skip_bytes = 0; + } + } + + uade_effect_run(&info->state.effects, (int16_t *) um->data, play_bytes / framesize); + + // ... copy data ... + memcpy (info->buffer + info->remaining, um->data, play_bytes); + frame_received = 1; + info->remaining += play_bytes; + + if (info->state.config.timeout != -1 && info->state.config.use_timeouts) { + if (info->song_end_trigger == 0) { + if (info->state.song->out_bytes / (UADE_BYTES_PER_FRAME * info->state.config.frequency) >= info->state.config.timeout) { + info->song_end_trigger = 1; + info->record_playtime = 0; + } + } + } + + if (info->state.config.subsong_timeout != -1 && info->state.config.use_timeouts) { + if (info->subsong_end == 0 && info->song_end_trigger == 0) { + if (subsong_bytes / (UADE_BYTES_PER_FRAME * info->state.config.frequency) >= info->state.config.subsong_timeout) { + info->subsong_end = 1; + info->record_playtime = 0; + } + } + } + + assert (left >= um->size); + left -= um->size; + break; + + case UADE_REPLY_FORMATNAME: + uade_check_fix_string(um, 128); + strlcpy(gui_formatname, (char *) um->data, sizeof gui_formatname); + strlcpy(info->state.song->formatname, (char *) um->data, sizeof info->state.song->formatname); + break; + + case UADE_REPLY_MODULENAME: + uade_check_fix_string(um, 128); + strlcpy(gui_modulename, (char *) um->data, sizeof gui_modulename); + strlcpy(info->state.song->modulename, (char *) um->data, sizeof info->state.song->modulename); + break; + + case UADE_REPLY_MSG: + uade_check_fix_string(um, 128); + trace ("Message: %s\n", (char *) um->data); + break; + + case UADE_REPLY_PLAYERNAME: + uade_check_fix_string(um, 128); + strlcpy(gui_playername, (char *) um->data, sizeof gui_playername); + strlcpy(info->state.song->playername, (char *) um->data, sizeof info->state.song->playername); + break; + + case UADE_REPLY_SONG_END: + if (um->size < 9) { + fprintf(stderr, "Invalid song end reply\n"); + exit(-1); + } + tailbytes = ntohl(((uint32_t *) um->data)[0]); + /* next ntohl() is only there for a principle. it is not useful */ + if (ntohl(((uint32_t *) um->data)[1]) == 0) { + /* normal happy song end. go to next subsong if any */ + info->subsong_end = 1; + } else { + /* unhappy song end (error in the 68k side). skip to next song + ignoring possible subsongs */ + info->song_end_trigger = 1; + } + i = 0; + reason = (char *) &um->data[8]; + while (reason[i] && i < (um->size - 8)) + i++; + if (reason[i] != 0 || (i != (um->size - 9))) { + fprintf(stderr, "Broken reason string with song end notice\n"); + exit(-1); + } + /* fprintf(stderr, "Song end (%s)\n", reason); */ + break; + + case UADE_REPLY_SUBSONG_INFO: + if (um->size != 12) { + fprintf(stderr, "subsong info: too short a message\n"); + exit(-1); + } + u32ptr = (uint32_t *) um->data; + info->state.song->min_subsong = ntohl(u32ptr[0]); + info->state.song->max_subsong = ntohl(u32ptr[1]); + info->state.song->cur_subsong = ntohl(u32ptr[2]); + + if (!(-1 <= info->state.song->min_subsong && info->state.song->min_subsong <= info->state.song->cur_subsong && info->state.song->cur_subsong <= info->state.song->max_subsong)) { + int tempmin = info->state.song->min_subsong, tempmax = info->state.song->max_subsong; + fprintf(stderr, "uade: The player is broken. Subsong info does not match with %s.\n", gui_filename); + info->state.song->min_subsong = tempmin <= tempmax ? tempmin : tempmax; + info->state.song->max_subsong = tempmax >= tempmin ? tempmax : tempmin; + if (info->state.song->cur_subsong > info->state.song->max_subsong) + info->state.song->max_subsong = info->state.song->cur_subsong; + else if (info->state.song->cur_subsong < info->state.song->min_subsong) + info->state.song->min_subsong = info->state.song->cur_subsong; + } + break; + + default: + fprintf(stderr, "Expected sound data. got %d.\n", um->msgtype); + return -1; + } + } + } + return 0; +} + + +// try decode `size' bytes +// return number of decoded bytes +// or 0 on EOF/error +static int +uadeplug_read (DB_fileinfo_t *_info, char *bytes, int size) { + uade_info_t *info = (uade_info_t *)_info; + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + int initsize = size; + + while (size > 0) { + if (info->remaining) { + int n = min (info->remaining, size); + memcpy (bytes, info->buffer, n); + bytes += n; + size -= n; + if (n < info->remaining) { + memmove (info->buffer, info->buffer + n, info->remaining-n); + info->remaining -= n; + break; + } + info->remaining = 0; + } + + if (uadeplug_frame (info) < 0) { + return initsize-size; + } + } + + _info->readpos += ((initsize-size) / samplesize) / (float)(_info->fmt.samplerate); + return initsize-size; +} + +// seek to specified sample (frame) +// return 0 on success +// return -1 on failure +static int +uadeplug_seek_sample (DB_fileinfo_t *_info, int sample) { + uade_info_t *info = (uade_info_t *)_info; + + _info->readpos = (float)sample / _info->fmt.samplerate; + return 0; +} + +// seek to specified time in seconds +// return 0 on success +// return -1 on failure +static int +uadeplug_seek (DB_fileinfo_t *_info, float time) { + return uadeplug_seek_sample (_info, time * _info->fmt.samplerate); +} + +static DB_playItem_t * +uadeplug_insert (DB_playItem_t *after, const char *fname) { + uade_init (); + + char modulename[PATH_MAX]; + char playername[PATH_MAX]; + char scorename[PATH_MAX]; + char gui_module_filename[PATH_MAX]; + char gui_player_filename[PATH_MAX]; + + struct uade_state state; + memset (&state, 0, sizeof (state)); + state.config = config_backup; + state.validconfig = 1; + + printf ("probing the file\n"); + int ret = uade_is_our_file(fname, 0, &state); + + if (!ret) { + trace ("not uade file\n"); + return NULL; + } + + strlcpy(modulename, fname, sizeof modulename); + trace ("modulename: %s\n", modulename); + strlcpy(gui_module_filename, fname, sizeof gui_module_filename); + trace ("gui_module_fname: %s\n", gui_module_filename); + + snprintf(scorename, sizeof scorename, "%s/score", get_uade_base_conf_dir ()); + trace ("scorename: %s\n", scorename); + + if (strcmp(state.ep->playername, "custom") == 0) { + strlcpy(playername, modulename, sizeof playername); + modulename[0] = 0; + gui_module_filename[0] = 0; + } else { + snprintf(playername, sizeof playername, "%s/players/%s", get_uade_base_conf_dir (), state.ep->playername); + } + trace ("playername: %s\n", playername); + + if (!uade_alloc_song(&state, fname)) { + trace ("uade_alloc_song fail\n"); + return NULL; + } + + uade_set_ep_attributes(&state); + + uade_set_song_attributes(&state, playername, sizeof playername); + + uade_set_effects(&state); + + strlcpy(gui_player_filename, playername, sizeof gui_player_filename); + + if (!state.pid) { + char configname[PATH_MAX]; + snprintf(configname, sizeof configname, "%s/uaerc", UADE_CONFIG_BASE_DIR); + uade_spawn(&state, UADE_CONFIG_UADE_CORE, configname); + } + + printf ("uade_song_initialization\n"); + ret = uade_song_initialization(scorename, playername, modulename, &state); + if (ret) { + if (ret != UADECORE_CANT_PLAY && ret != UADECORE_INIT_ERROR) { + fprintf(stderr, "Can not initialize song. Unknown error.\n"); + return NULL; + } + uade_unalloc_song(&state); + return NULL; + } + printf ("init done\n"); + int minsong = uade_get_min_subsong (&state, 0); + int maxsong = uade_get_max_subsong (&state, 0); + + char info[256]; + int playtime = state.song->playtime; + + /* Hack. Set info text and song length late because we didn't know + subsong amounts before this. Pass zero as a length so that the + graphical play time counter will run but seek is still enabled. + Passing -1 as playtime would disable seeking. */ + if (playtime <= 0) + playtime = 0; + + if (uade_generate_song_title(info, sizeof info, &state)) + strlcpy(info, gui_filename, sizeof info); + +// playhandle->set_params(playhandle, info, playtime, +// UADE_BYTES_PER_FRAME * state.config.frequency, +// state.config.frequency, UADE_CHANNELS); + + + // no cuesheet, prepare track for addition + DB_playItem_t *it = deadbeef->pl_item_alloc (); + it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); + it->fname = strdup (fname); + it->filetype = filetypes[0]; + float duration = -1; + if (playtime != -1) { + duration = playtime / 1000.f; + } + deadbeef->pl_set_item_duration (it, duration); + + // title is empty, this call will set track title to filename without extension + const char *fn = strrchr (fname, '/'); + if (fn) { + fn++; + } + else { + fn = fname; + } + deadbeef->pl_add_meta (it, "title", info); + + // now the track is ready, insert into playlist + after = deadbeef->pl_insert_item (after, it); + deadbeef->pl_item_unref (it); + uade_unalloc_song(&state); + if (state.pid) { + kill(state.pid, SIGTERM); + } + return after; +} + +// define plugin interface +static DB_decoder_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 1, + .plugin.version_minor = 0, + .plugin.type = DB_PLUGIN_DECODER, + .plugin.id = "uade", + .plugin.name = "UADE player", + .plugin.descr = "amiga module player based on UADE (http://zakalwe.fi/uade/)", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .open = uadeplug_open, + .init = uadeplug_init, + .free = uadeplug_free, + .read = uadeplug_read, + .seek = uadeplug_seek, + .seek_sample = uadeplug_seek_sample, + .insert = uadeplug_insert, + .exts = exts, + .prefixes = prefixes, + .filetypes = filetypes +}; + +DB_plugin_t * +ddb_uade2_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} diff --git a/plugins/uade2/uade-2.13/AUTHORS b/plugins/uade2/uade-2.13/AUTHORS new file mode 100644 index 00000000..b1d24130 --- /dev/null +++ b/plugins/uade2/uade-2.13/AUTHORS @@ -0,0 +1,59 @@ +Authors of UADE +--------------- + +Main authors +------------ + + - Heikki Orsila <heikki.orsila@iki.fi> + - Michael Doering <mldoering@gmx.net> + +Subsystems +---------- + + - Antti S. Lankila <alankila@bel.fi> for filter emulation and sound effects + - Claudio Matsuoka and Hipolito Carraro Jr for module decruncing code in + uade 1.xy. + jah@fh-zwickau.de for unsqsh + Sipos Attila for unsqsh checksum + Olivier Lapicque for mmcp + Marc Espie (old depack.c) + - Harry "Piru" Sintonen <sintonen@iki.fi> for AmigaOS and MorphOS port + - Martin Blapp for configure fixes and enhancements from FreeBSD project + - Michael S. Baltaks for Mac OS X port + - Nicolas A. Mendoza (part of the AmigaOS port) + - Stuart 'kyzer' Caie for Mac OS X port and powerpacker decruncher + http://www.kyz.uklinux.net + +Everyone from UAE project. See doc/UAE-CREDITS. + +Eagleplayers +------------ + + - Don Adan / Wanted Team + - Eagleeye and Buggs from Defect (Eagleplayer project authors) + - Nicolas Frank <gryzor@club-internet.fr> for PTK-Prowiz + - Andy Silva <silva@psi5.com> for his replayers + - Bartman and Dexter from Abyss for AHX v2 replay routine: + http://www.the-leader-of-the-eighties.de + - Brian Postma <b.postma@hetnet.nl> for Brian's Soundmonitor player + http://www.homepages.hetnet.nl/~brianpostma + - Nicolas Pomared <npomarede@kaptech.com> for MYST/YMST replayer + - Sean Connolly for EMS V6 replay: http://www.cosine-systems.com/ + - Stephen Mifsud (Malta) <teknologik@technologist.com> for Darius Zendeh + replayer. http://www.marz-kreations.com + - Sunbeam/Shelter for his replayers + - Paul v.d. Valk for Medley replay routine + - Tap & Walt for digibooster source + http://www.highantdev2.de/dbpro/index.php + - The Welder / Divine for protracker replay routine + + - Everyone else whose Eagleplayer plugins we use. Respective authors of + eagleplayer plugins can be found from inside the plugin. + +Media +----- + +Manfred Linzner aka Pink/Abyss <linzner@shinen.com> for a great test tune +(AHX.Cruisin). + + diff --git a/plugins/uade2/uade-2.13/COPYING b/plugins/uade2/uade-2.13/COPYING new file mode 100644 index 00000000..d8917caa --- /dev/null +++ b/plugins/uade2/uade-2.13/COPYING @@ -0,0 +1,12 @@ +This source distribution contains works with various licenses. Please read +copyright notices of those works before reuse. + +All the code from the UAE (Unix Amiga Emulator) project is licensed +under the GNU GPL. Read COPYING.GPL for details of the GNU GPL license. +UAE people work is credited in doc/UAE-* files. + +Files under players/ directory are licensed with various different licenses +and quite a many different copyright holders. See inside the player binaries +for more details. + +Sound core in directory amigasrc/score/ is licensed under the GNU LGPL. diff --git a/plugins/uade2/uade-2.13/COPYING.GPL b/plugins/uade2/uade-2.13/COPYING.GPL new file mode 100644 index 00000000..60549be5 --- /dev/null +++ b/plugins/uade2/uade-2.13/COPYING.GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/plugins/uade2/uade-2.13/COPYING.LGPL b/plugins/uade2/uade-2.13/COPYING.LGPL new file mode 100644 index 00000000..8add30ad --- /dev/null +++ b/plugins/uade2/uade-2.13/COPYING.LGPL @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/plugins/uade2/uade-2.13/ChangeLog b/plugins/uade2/uade-2.13/ChangeLog new file mode 100644 index 00000000..54a2d409 --- /dev/null +++ b/plugins/uade2/uade-2.13/ChangeLog @@ -0,0 +1,1907 @@ + +* ALL THE CHANGE LOG ENTRIES SINCE 2007-05-28 ARE IN THE GIT REPOSITORY. USE + "git log" COMMAND + +2007-05-28 Heikki Orsila <heikki.orsila@iki.fi> + - The development has been moved to a Git repository. Please issue + "git-clone git://zakalwe.fi/uade uade.git" to checkout the latest + version from the repository. + +2007-05-27 Heikki Orsila <heikki.orsila@iki.fi> + - Commit messages into version repository should from now on include + a short tag explaining the nature of the commit. + The tags are: + * [ADAPTIVE] - adaptive maintenance: the commit makes software to + work better with the surrounding environment (compilers, libraries, + path names ...) + * [CORRECTVE] - corrective maintenance: fix a defect in the program + * [PERFECTIVE] - perfective maintenance: used when adding a feature + or improving an existing feature + * [PREVENTIVE] - preventive maintenance: used when refactoring, + cleaning or asserting the code, or adding self-tests + + See http://www.site.uottawa.ca:4321/oose/index.html#maintenance + + - Fixed Audacious plugin to work for Audacious 1.4/2.x + Christian Birchinger <joker@netswarm.net> [ADAPTIVE] [CORRECTIVE] + +2007-05-03 Michael Doering <mld@users.sourceforge.net> + - Added a new version of Synthdream replayer from Don Adan + +2007-04-30 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.07 (Walpurgis night) + - Added Special FX ST replayer from Don Adan + - Added a new version of Special FX replayer from Don Adan + - Work-arounded libao/alsa so that it drains the audio device for + the last few milliseconds of sample data for the last played song. + Thanks to Cthulhu for pointing out the problem. + - Many fixes in song length database code + # Fixed a bug that only the last played subsong time was recorded + into the song db + # Fixed a file-locking race condition in song db + # Fixed a song db corruption bug + # Cleaned song db code + - Support for full Drag and Drop/Local URL Support in Audacious 1.3 + plugin + - Fixed misdetection and modlen calculation bug of Soundtracker IV + mods + +2007-04-29 Heikki Orsila <heikki.orsila@iki.fi> + - Added Special FX ST replayer from Don Adan + - Added a new version of Special FX replayer from Don Adan + +2007-04-27 Heikki Orsila <heikki.orsila@iki.fi> + - Work-arounded libao/alsa so that it drains the audio device for + the last few milliseconds of song data for the last played song. + Thanks to Cthulhu for pointing out the problem. + +2007-04-15 Michael Doering <mld@users.sourceforge.net> + - Bug in songlength database handling fixed (shd) + +2007-04-14 Michael Doering <mld@users.sourceforge.net> + - Support for full Drag and Drop/Local URL Support in Audacious 1.3 + plugin. (shd, mld) + - Fixed misdetection and modlen calculation bug of Soundtracker IV + mods. (shd/mld) + +2007-04-09 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.06 (7 YEARS BIRTHDAY PARTY AT PAULAS!) + - UADE project is now exactly 7 years old \o/ PARTY! + - Started working for Audacious 1.3 support, at least songs that + are added directly through playlist should work if they are + regular files. Audacious VFS is not supported in general. Nothing + else is guaranteed. Audacious < 1.3 works as in the past. + - Infogrames replayer improved, gobliins 2 et al. play with correct + tempo + - Added new Quartet ST player from Don Adan / Wanted Team + (qts.* files) + - KRIS aka Chip Tracker replayer (KRIS.* files) from Don Adan / + Wanted team. This replaces PTK-Prowiz for Chip Tracker songs, so + not a new format. + - Quartet PSG replayer (SQT.* files) from Don Adan / Wanted Team + - Many small changes, cleanups etc + + - Fixed user installation of Audacious 1.3 plugin... + +2007-03-19 Michael Doering <mld@users.sourceforge.net> + - Merged Christian Birchingers Audacious 1.3.x API Patches in. + +2007-03-03 Heikki Orsila <heikki.orsila@iki.fi> + - KRIS aka Chip Tracker replayer (KRIS.* files) from Don Adan / + Wanted team. This replaces PTK-Prowiz for Chip Tracker songs, so + not a new format. + - Quartet PSG replayer (SQT.* files) from Don Adan / Wanted Team + +2007-02-13 Heikki Orsila <heikki.orsila@iki.fi> + - Updated Inforgrames player to use tempo 0x24ff for Ween songs. + Thanks to DrMcCoy of SCUMM VM project. + - Reverted Michael's 2007-02-13 patch regarding LC_Numeric and + strtod(). Fixed the problem manually by converting x,y values into + x.y format and vice versa. + +2007-02-13 Michael Doering <mld@users.sourceforge.net> + - Fix for German LC_Numeric="," parameters in uadeconfig.c. + Thanks for Steffen Wulf for reporting. + - Bumped Version to 2.05. :-) + +2007-02-08 Michael Doering <mld@users.sourceforge.net> + - Added new Quartet ST player from Don Adan / Wanted Team + (qts.* files). Great work as ever Don! + - Disabled Hippel COSO check in amifilemagic.c to avoid a conflict + between Amiga and Atari ST Coso Files. + TODO: The file heuristics for Coso in amifilemagic.c has to be fixed, + the checkroutine of the Amiga Hippel-Coso replayer isn't HIP-ST Coso + aware either. + +2007-02-06 Michael Doering <mld@users.sourceforge.net> + - Small correction of shd's patch in PTK-Prowiz commited. + +2007-02-05 Michael Doering <mld@users.sourceforge.net> + - Applied shd's uade_new_get_info patch to PTK-Prowiz. + +2007-02-04 Michael Doering <mld@users.sourceforge.net> + - Added sanity check to query eagleopts in PTK-Prowiz (needed for + score fix) + - Added a new uade.library method: UadeNewGetInfo(). It is now used + with Infogrames replayer (see + amigasrc/players/infogrames/Infogrames.asm). It will be used with + PTK-Prowiz soon. Documentation for UadeNewGetInfo() can be read + from amigasrc/score/score.s, see function "uade_new_get_info". + +2007-02-03 Heikki Orsila <heikki.orsila@iki.fi> + - Disassembled Andy Silvas Infogrames replayer, added a work-around + list for Gobliins 2 songs to fix tempo. Thanks to Sven Hesse of + ScummVM project for letting us know of the tempo problem. + The next thing to do is go through all Infogrames games with UAE + and record the tempo value for each song? Any volunteers?-) + +2007-01-30 Michael Doering <mld@users.sourceforge.net> + - Changed Scrollbar policy for audacious modinfo to avoid + ugly line breaking in hexmode + +2007-01-25 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.05 (It came from the Paula) + - This release workarounds scheduler features of some 2.6.x Linux + kernels. IPC method was changed to use sockets instead of pipes, + which significantly reduces buffer underruns. This is really the + only change affecting scheduling! Weird. + +2007-01-24 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed compilation for platforms that lack memmem(), such as Mac. + The compilation bug was introduced at 2007-01-21 when unixipc + and unixsupport were merged. + +2007-01-22 Michael Doering <mld@users.sourceforge.net> + - Fixed songinfo for mods detected as Protracker compatible + +2007-01-21 Heikki Orsila <heikki.orsila@iki.fi> + - Merge unixipc.c and unixsupport.c. Modularise uadecore spawn into + unixsupport.c. + - Move from pipe based IPC communication to UNIX socket based + communication. This solves a scheduling problem for some 2.6.x Linux + kernels. + +2007-01-21 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.04 (Defective by Design .org) + - Added Jochen Hippel ST player (hst.* files) + - Added Quartet player from Don Adan / Wanted Team (qpa.* files) + - Updated Mark Cookset replayer with a new version from Don Adan of + Wanted Team + - It's now possible to command an eagleplayer listed in + eagleplayer.conf to ignore file type check. This is useful + with the CustomMade replayer as it rejects some valid song files. + See the change log entry from 2007-01-02 and the man page + about song.conf and eagleplayer.conf. + - Cygwin work-around (locking on song contents db is broken). Does + NOT affect unixes. + - Amiga memory size can now be configured properly from ~/.uade2/uaerc. + This is useful with big sound files (rare). + - Fixed the sinc filter, it had a bug that made it less accurate + - Man page updates about filters + - GCC 4.x clean ups + - Small bug fixes. The default uade.conf had a deprecated option + in comments. It was removed. + - Updated installation instructions + +2007-01-18 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a bug in default uade.conf. "headphone" option for the + uade.conf was obsoleted at 2006-05-13, the new option name + became "headphones". The backward compatibility is retained + again and thus "headphone" option will work to some future. + - Fixed several missing cvs sticky bits (-kb) in players/ dir + +2007-01-15 Michael Doering <mld@users.sourceforge.net> + - Made "detect_format_by_content" parameter for modfiles default. + This way only true Amiga 4ch mod files now get played. + - Added Quartet player from Don Adan / Wanted Team. It recognizes + QPA.* files. + +2007-01-12 Heikki Orsila <heikki.orsila@iki.fi> + - Added "ignore_player_check" option for eagleplayer.conf and + song.conf. It is useful with bad eagleplayers and rips. This + feature was requested for use with CustomMade player and therefore + this option will also be made default for that player. See the + new eagleplayer.conf. + +2007-01-11 Antti S. Lankila <alankila@bel.fi> + - Correct some ancient mistakes in uade123.1, regarding filter + operation. For instance the LED filter center frequency was + reported halved due to an earlier mistake in graph drawing + +2006-12-22 Heikki Orsila <heikki.orsila@iki.fi> + - Added Jochen Hippel ST player from Don Adan / Wanted Team. It + recognizes HST.* files + - Replaced old Mark Cooksey replayer with a new version from + Don Adan / Wanted Team + +2006-12-07 Antti S. Lankila <alankila@bel.fi> + - Remove uadecore with make clean + +2006-12-03 Antti S. Laknila <alankila@bel.fi> + - Fixed a strict aliasing warning that occured with GCC 4.1 in + newcpu.h + +2006-12-01 Heikki Orsila <heikki.orsila@iki.fi> + - Memory size of Amiga can now be increased over 2 MiB by editing + the uaerc file (e.g. ~/.uade2/uaerc). The variable named chipmem_size + should be edited. Allocated memory for the amiga is determined by + chipmem_size * 512 KiB + By default, chipmem_size = 4 -> 2 MiB of memory. This variable can + be set up to 16 (8 MiB of memory). + +2006-11-26 Heikki Orsila <heikki.orsila@iki.fi> + - Updated instructions about -x option in uade123 and its man page. + +2006-10-28 Antti S. Lankila <alankila@bel.fi> + - Due to me misunderstanding the Kaiser window beta parameter, + the BLEP tables for sinc synthesis were set to attenuate aliasing + only for 40 dB instead of the target 80 dB. + +2006-10-24 Heikki Orsila <heikki.orsila@iki.fi> + - No locking with Cygwin -> uade is dangerous with songdb. + +2006-10-02 Heikki Orsila <heikki.orsila@iki.fi> + - Updated INSTALL.readme to include more dependencies (pkg-config + and sed) and tips for Mac OS X compiling (workarounds) + +2006-08-27 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.03 (Microsoft punishment) + - Added song.conf support to set song specific attributes for + playback, such as ntsc, blank, filtering etc. See the man + page. An example, one may add following line to ~/.uade2/song.conf + to make fred.hybris_light always timeout on 200 seconds: + md5=60f3bb11cc1bacaf77d7c16d13361844 broken_song_end timeout=200 comment FRED.Hybris_Light + This need not be added manually, one can just issue: + uade123 --set=broken_song_end --set=timeout=200 FRED.Hybris_Light + and afterwards uade will always play the song correctly. + Similarly, one can fix volume gain for some songs: + uade123 --set=gain=2 foo + To reset all options for a given song, do: + uade123 --set= foo + - Fixed PAL audio synthesis frequency (inaudible) + - --interpolator=x is now --resampler=x because the function of + interpolators was actually resampling. + - Only A500 and A1200 filters are now supported and A500 is the + default. + - Added Play time database support for uade123 + - Compatibility fixes + - Lots of bug fixes + - Improvements in uade123 UI (press i or I for info on song) + - Improved file magic support for some formats (P61A, Fuzzac, ...) + - Hacky NTSC mode support available now. Use --ntsc or ntsc + directive in uade123. Also works per-song with song.conf. + One can now make specific songs be played in NTSC mode by + programming song.conf with proper values. One can issue: + uade123 --set=ntsc dw.FooBar + or put a line to ~/.uade2/song.conf: + md5=225bbb405433c7c05fe10b99b8e088ff ntsc comment dw.AlfredChicken + - One can force protracker replayer to use VBlank timing with many + ways, see the man page on section EAGLEPLAYER OPTIONS. For example, + do: + uade123 -x vblank mod.FooBar + - Enhancements in PTK-Prowiz. Protracker version is now selectable + between 3.0b, 2.3a and 1.0c. See the man page section EAGLEPLAYERS + OPTIONS. This option is not yet guaranteed to be 100% but may + fix some immediate problems with some songs.. These settings + may also be recorded to song.conf so that one only has to give + these parameters once. Example: + uade123 -x type:pt10c mod.TheClassicProtrackerVersionedSong + btw. uade is probably the first mod player for non-amigas that + has a direct version support for protrackers. + - Improved some players + - Titles in audacious and xmms plugins on the playlist are now + programmable with uade.conf. The default is + song_title %F %X [%P] + This displays filename, subsong (if more than one) and player. + See SONG TITLE SPECIFICATION section in the man page. There's a + small help in the uade.conf too. + - Significant code refactorizations + - Fixed a memory leak issue (see Change log on 2006-04-15) + - Added Dirk Bialluch format support by Don Adan/Wanted Team + +2006-07-27 Heikki Orsila <heikki.orsila@iki.fi> + - Removed -fno-strength-reduce from src/Makefile.in. Some old + GCC 2.x versions had bugged strength reduce, but it shouldn't + matter anymore. Maybe the compiler will produce better code + without this. AHX.cruisin showed approx 3% speedup :) Thanks + to alankila for pointing out use of this flag. + +2006-07-17 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a segfault bug in songdb caused by recent changes to add + subsong volume normalisation info to contentdb. Segfaults were + catched in several places due to uninitialized vplist in + struct uade_content. + +2006-07-02 Heikki Orsila <heikki.orsila@iki.fi> + - Merged an initial version of volume normalization effect from + alankila. + - Split song.conf and contentdb related functionality away from + eagleplayer.c to songdb.c. + +2006-06-30 Heikki Orsila <heikki.orsila@iki.fi> + - Work-arounded signedness warnings with GCC4 in src/frontends/common/ + +2006-06-23 Heikki Orsila <heikki.orsila@iki.fi> + - Fix sinc resampler to support --filter=none (alankila) + - Optimize sinc_handler() inner-loop by avoiding unnecessary + indexing (alankila) + +2006-06-22 Heikki Orsila <heikki.orsila@iki.fi> + - Separated accumulator and sinc resamplers into separate functions + in src/audio.c. + - Removed "anti" name from resampler list. "anti" has been the + "default" for a long time already. + - Change FIR convolution of sinc into bandlimited step synthesis. + Filters are applied directly by the BLEPs. Warning, sinc and + filtering are now integrated together so they can not be + changed separately (breaking modular development idea). The + default resampler does not suffer from this modularity problem + and it is still the recommended resampler. (alankila) + +2006-06-18 Heikki Orsila <heikki.orsila@iki.fi> + - Added play time database saving support into uade123. + +2006-05-22 Heikki Orsila <heikki.orsila@iki.fi> + - Changed --magic to --detect-format-by-content and changed + corresponding "content_detection" directive in eagleplayer.conf and + uade.conf to "detect_format_by_content". Changed --no-song-end to + "--no-ep-end-detect". + +2006-05-20 Heikki Orsila <heikki.orsila@iki.fi> + - Changed default input file to be /dev/tty instead of stdin for + uade123. Also, failing terminal setup (input file is not a tty) + is not a fatal error. + - Added a warning to eagleplayer.conf parsing for the situation + that user has removed all prefixes from an eagleplayer. It makes + all kind of detection (including content detection) unusable. + If you don't want to detect a particular file type by name + extensions, add "content_detection" option for the line in + eagleplayer.conf. But note that there isn't content detection + for all formats. For example, adding "content_detection" for + Thomas Hermann player will make all Thomas Hermann songs + unplayable because there is no content detection for it. + +2006-05-19 Heikki Orsila <heikki.orsila@iki.fi> + - Added Gryzors (Nicholas Franck <gryzor@club-internet.fr>) + Protracker converter source code into CVS. See + amigasrc/players/tracker/converter/README.txt for copyright and + licensing information. Thanks to Stuart Caie for leading us + to the distribution terms (we did have the source before :-) + +2006-05-18 Heikki Orsila <heikki.orsila@iki.fi> + - Added p5x and p6x file extensions for The Player 5.x/6.x to + eagleplayer.conf (Stuart Caie requested it) + - Added "P50A" four letter magic detection to amifilemagic.c + +2006-05-17 Michael Doering <mld@users.sourceforge.net> + - Removed deprecated <audacious/configfile.h> from audacious + plugin. Thanks to Joker for the report. + +2006-05-16 Michael Doering <mld@users.sourceforge.net> + - Raised length of extension[] in uade_analyze_file from 11 to 16, + which caused the xmms and audacious plugin to segfault on + very long prefixes (such as mod15_st-iv) in eagleplayer.conf. + +2006-06-15 Heikki Orsila <heikki.orsila@iki.fi> + - Added "-x y" to set eagleplayer options more conveniently. It's + now possible to do: uade123 -x type:pt11b mod.foo. -x can be + used multiple times on the same command line. + +2006-05-15 Michael Doering <mld@users.sourceforge.net> + - PTK-Prowiz now uses the epopt config system. You can set options + such as "vblank" and/or "type:" on a song base with the uade123 + "--set=xyz" command line parameter or edit uade.conf or song.conf + manually. For valid protracker types: check the uade123 man page. + Please test. I hope I didn't break anything. + - Changed Protracker and compatible tag in amifilemagic. It seems + it was too long for xmms/audacious and crashed. Odd. + +2006-05-13 Heikki Orsila <heikki.orsila@iki.fi> + - Merged man page update from alankila, explaining filter and + resampling features + - Merged headphones 2 effect patch from alankila, making it sample + rate independent + - Added --version to uade123 + - Cleaned up frontends with respect to configuration management. + Initial configuration loading is made in uade_load_initial_config() + in all frontends. + - Fixed song.conf locking + - Fixed recursive mode song.conf option setting. Try: + uade123 -r --set=gain=2 songdir + +2006-05-12 Michael Doering <mld@users.sourceforge.net> + - Audacious modinfo GTK-2.8's assertion error fixed by "porting" + the legacy gdk_font_load/gtk_text to gtk2's tag, viewport, + buffer system. + - Worked around gtk2's assumption any text has to be clean UTF-8 + which obviously will break when displaying binaryhexdumps or + Amiga locale strings. + +2006-05-11 Heikki Orsila <heikki.orsila@iki.fi> + - Updated documentation to reflect implementation. + - Added support for eagleplayer/song specific eagleplayer options. + Use epopt=x in eagleplayer.conf and song.conf. Look at the + man page section "EAGLEPLAYER OPTIONS" for valid options. + +2006-05-09 Heikki Orsila <heikki.orsila@iki.fi> + - Added information about sample rate into effects API. + - Fixed a race condition for Audacious and XMMS plugins. + A shared static buffer was not locked for file magic detection + in eagleplayer.c:analyze_file_format(). + - Fixed a bug in eagleplayer.c:analyze_file_format() that might + have caused a partial read for file to be detected. + - Fixed a buffer overrun bug in uade.c:uade_get_amiga_message(). + With AMIGAMSG_GET_INFO request, the eagleplayer could + cause a slight overrun of the amiga memory to uade data memory. + The attacker could not however choose the string to be written + over the bounds. + +2006-05-07 Heikki Orsila <heikki.orsila@iki.fi> + - Improved filters to work on arbitrary frequency (alankila) + - Removed A500S and A1200S filters. For compatibility, they're now + aliased to A500 and A1200. + - Moved computation of audio emulation parameters away from + init_sound() to audio_set_rate(). init_sound() calls + audio_set_rate(). + - It's now possible to use frequency directive in uade.conf to + set playback frequency. But watch out, it can cause bugs + and sound quality degradation. + - Now only two models (FILTER_MODEL_(A500|A1200)) exist in + amigafilter.h. + - Code cleanups + - Renamed interpolator to be resampler in command line options + and configuration files. Use --resampler=x instead of + --interpolator=x. + - Fixed IIR filter not to waste CPU time with denormal numbers. + This idea was presented to us by Antti Huovilainen, thanks. + Try playing BD.Mickey_mouse with an older version and see how + much cpu it takes with A500 filter. The new version is one + quarter CPU time on an AMD64 machine (alankila) + - Optimized event handling in include/events.h by removing + redundant vsynctime check that relied upon processor/OS + specific timers (that we've removed long ago). + +2006-05-06 Heikki Orsila <heikki.orsila@iki.fi> + - Added two util functions into uadeipc.c. They are uade_send_u32() + and uade_send_two_u32s(). + - Started changes towards changeable output frequency. You can + experiment with --freq=x but filter emulation only works for + 44,1 kHz at the moment. Note that filter emulation always has an + effect on sound. + +2006-05-05 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed --filter=x and --force-led=y to work again. + +2006-05-02 Michael Doering <mld@users.sourceforge.net> + - Fixed crash due to uninitialized pointer to subsong scale in seek + window for xmms and auda plugin. + Seems the UI got into a race condition when trying to update the + scale belonging to the seek window and the pointer for the seek + window was there, while the scale wasn't ready. This happend for + very short subsongs only... + - Added NTK/STK detection to modinfo + - Check for Dirk Bialluch songs in amifilemagic. + +2006-05-02 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a small bug that allows resetting song.conf options from + the command line. Use 'uade123 --set= foo' to reset options. + +2006-05-01 Heikki Orsila <heikki.orsila@iki.fi> + - Changing to a previous song is now possible in uade123. Try + pressing '<'. -z option now means randomizing the playlist + order before playing, but random play mode (press 's') does + randomizing on the fly. Both alternatives support seeking to + previous song in the same way, but seeking to next song will + always be random in random play mode (not with -z). + - Added --repeat option into uade123 to play playlist over and + over again. + +2006-04-30 Heikki Orsila <heikki.orsila@iki.fi> + - It's now possible to set options into song.conf by using uade123 + directly. This is not the final feature, only a step towards + a more usable feature. Try: + # uade123 --set="gain=2" foo + It should add the associated entry into ~/.uade2/song.conf. + Eventually uade123 should be used like: + # uade123 --set --gain=2 foo + +2006-04-29 Heikki Orsila <heikki.orsila@iki.fi> + - Changed hexdump of module/player to show 2 KiBs of text. + - Fixed some memory leaks in eagleplayer.c and songinfo.c. + - Removed doc/eagleplayer.conf and doc/song.conf. doc/uade123.1 is + the authorative documentation for all configuration files. + - Many uade song attribute and configuration management changes. + - Added broken song end directive for eagleplayers and songs. + To work-around a defective timeout logics in song.conf you could + add a following line to your song.conf: + md5=60f3bb11cc1bacaf77d7c16d13361844 broken_song_end timeout=200 comment FRED.Hybris_Light + - Marked song end detection of FRED format as defective in + eagleplayer.conf: + Fred prefixes=fred broken_song_end + - Unified eagleplayer.conf and song.conf settings. See uade123 + man page for those options. + +2006-04-28 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed another config bug introduced by last config refactorization. + Filter type could not be set from config file in uade123. The bug + did not happen on Audacious/XMMS plugins. + - uade123 -v now prints the uade.conf and song.conf files that were + loaded. + +2006-04-28 Michael Doering <mld@users.sourceforge.net> + - Fixed bug in Soundtracker 31 instruments check introduced + by fixing the misdetection of a digibooster as protracker mod which + was introduced by easing accepting mods with bad length... + See a pattern there, folks?!? + - Along the way, added a distinction between Soundtracker 2.5/ + Noisetracker1.0 and Soundtracker 2.4. ST2.5/NT1.0 obviously shared + the same replay by Kaktus & Mahoney, while ST2.4 was still based on + the old 2.3 replay by Unknown and Mnemotron (using repeat offset in + bytes). + +2006-04-27 Michael Doering <mld@users.sourceforge.net> + - Fixed misdetection of a digibooster mod as protracker in + amifilemagic. + - Fortified modplayer checks against amifilemagic's new policy + to accept modfiles with trailing garbage... + +2006-04-26 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed panning, gain and recursive mode to work again. The problem + was the new configuration management system. Command line options + were not merged into used options. Also, even if they were merged, + they would happen before effects were set ;) Sorry. Fixed now. + - Improved gain effect. It can now clip samples if an overflow + happens. + - Improved song.conf settings. It now supports all but two + options. See doc/song.conf. + +2006-04-21 Michael Doering <mld@users.sourceforge.net> + - Fixed detection bug triptodestroy.mod in PTK-Prowiz. Thanks to + Joker for the report. + The mod uses very large instruments with loops which rolled over + to negative checking instrument sizes in words and when large + enough. Stupid bug. Stupid me. :-) + - Replaced \t with white spaces. This might fix Joker's bug report + about garbled output in modinfo for audacious. + - Fixed audacious crash, when trying to access fileinfo while not + playing a song... Bug was simple we did't have uadesong struct. + get_cur_subsong, get_max_subsong and get_min_ subsong was trying + read from that struct even when idle and uade went up in a blaze... + Now checking towards uadsong struct exists and all is nice + - Fixed the Audacious crash fix. It contained a race condition with + respect to the NULL pointer. Also fixed the XMMS plugin. (shd) + +2006-04-19 Michael Doering <mld@users.sourceforge.net> + - Added new replayer for the Dirk Bialluch format by + Don Adan/Wanted Team. + - Amended Startrekker/Audiosculpture detection in amifilemagic again. + +2006-04-18 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a bug in memmem() replacement. If needle length was zero, + it returned NULL, but glibc would return pointer to haystack. + +2006-04-15 Michael Doering <mld@users.sourceforge.net> + - Fixed bug in sanity check in pt_karplusstrong effect (e8x) in + mod player. (thanks Heikki) + - Disabled Effect E8x for Protracker 2.3a, 1.1b and 1.0c compatibility + mode in PTK-Prowiz. + E.g. Playing mod.cyberlogik as PTK3.0 will result in timing bugs, + playing it as PTK2.3a will play ok. + - Fixed a very serious memory leak issue. Each played song that was + read into memory was leaked. My chip collection is 230 MiB and + after running + valgrind -zr -t1 --enable-timeouts -f /dev/null /chips + I noticed a huge chunk of mem leaked :( Now it's fixed. (shd) + +2006-04-14 Heikki Orsila <heikki.orsila@iki.fi> + - Refactored and changed amifilemagic.c. Changed tracker type magic + values into enums. A warning about differing file size and + calculated file size for protracker modules is given if one of + two conditions happen: + - uade123 is run in verbose mode (-v) + - xmms or audacious plugin starts to play file + - Refactored configuration code + - Fixed a subsong/total timeout bug in xmms and audacious plugins. + Always ends directive was not obeyed. + +2006-04-13 Heikki Orsila <heikki.orsila@iki.fi> + - Committed a programmable option for displaying song titles on + playlist. Try setting + song_title %F %X [%P] + to uade.conf. + - Reverted xmms buffer underrun fix that was ported to audacious + plugin. With produce_audio() one does not need to wait for + buffer_free(). (shd) + [Comment: it still locks up here :-\] (mld) + - Added songtitle feature to audacious plugin and uade.conf (mld) + - Added #include <songinfo.h> to audacious plugin and removed some + unused variables. + - Added a work-around against ALSA output plugin into XMMS plugin + which avoids to old subsong change blocking bug. Now sleeping is + hardlimited to 5 secs. If work-around is activated, you will see: + UADE: blocking work-around activated. + on stderr. It seems like snd_pcm_state(pcm) in ALSA output plugin + never comes out of SND_PCM_STATE_RUNNING. + - Set song_title default to %F %X [%P] + - Fixed all xmms and audacious plugin symbols to be non-exportable + symbols. The get_iplugin_info() remains the only exportable + symbol. 'objcopy -G get_iplugin_info foo.so' should do it, right? + +2006-04-12 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed configure script to handle uade.pc file correctly in the + situation of '--user'. + - Added a user warning to amifilemagic.c about truncated protracker + modules. + - Reverted Michaels change partially. Audacious and XMMS plugins will + not show song name that is obtained from the module because it + is unreliable. This will be made configurable in the future. + - Fixed a potential bug in XMMS plugin. If maximum free audio buffers + was less than 4088 bytes, uade plugin would refuse to write + anything. This was noticed because XMMS wouldn't recover from + a buffer underrun in ALSA mmap mode. The output plugin would + not increase the amount of free buffers even if time passes. + XMMS 1.2.10 ALSA output plugin (or alsa library) is still buggy. + - Ported shd's xmms buffer underrun fix to audacious (mld) + - Changed user warning about truncated protracker modules in + amifilemagic to produce less false positives. (mld) + +2006-04-11 Michael Doering <mld@users.sourceforge.net> + - Fixed my crap indentation in audacious plugin + - Sync'd xmms and audacious playlist display + - Fixed possible changing current subsong > maximal subsong + in audacious plugin. + +2006-04-10 Heikki Orsila <heikki.orsila@iki.fi> + - --no-song-end is now aliased to -n in uade123 + - Fixed bug in ArtOfNoise8 replayer end routine. (mld) + - Added Songtitle to ArtOfNoise 4V/8V players (mld) + - Display "Guru Meditation" error in audacious playlist for a song + that crashed uadecore. :-) (mld). + - Fixed bug updating the playtime in audacious playlist (mld) + +2006-04-09 Heikki Orsila <heikki.orsila@iki.fi> + - Code refactorization to unify configuration, effect and song + attribute handling among all frontends. + - Added memmem() replacement for operating systems not having it. + - Added uade.pc for pkg-config. + - Upgraded MED/OctaMED replay to v7.0, using Fastmem replay if + available and needed by the song (long samples :-) (mld) + - DigiBooster player now reports songname. (mld) + - Fixed ommited pointer in MED/Octamed fastmem replay detection. + (mld) + +2006-04-06 Heikki Orsila <heikki.orsila@iki.fi> + - Added a small README file + - Moved some effect related configuration issues to + src/frontends/common/ so that they're not reimplemented in all + frontends. See src/frontends/common/uadeconf.c function + uade_enable_conf_effects(). + - Use of uade_enable_conf_effects() in audacious plugin (mld) + - Disabled debug messages in Audacious and XMMS plugins + - Removed debug message about song.conf not found. It's perfectly + valid that song.conf does not exist anywhere. + - Cleaned songinfo.c. Added a common implementation of read_be_u16/u32 + to src/include/uadeutils.h, which is now used from songinfo.c and + amifilemagic.c. Changed length types to size_t. Made find_tag() + more generic and made it use memmem() function. + - Added Heikki's dpiutil to songinfo for CUST.* songs. (mld) + +2006-04-05 Michael Doering <mld@users.sourceforge.net> + - PTK-Prowiz: changed opcodes beq, sf to seq on Heikkis advice. + - Tiny Protracker player compatibility change when calling + playvoice. + - Nicer playlist entries for Audcious plugin: + title (cur/max) - [Format] + +2006-04-03 Heikki Orsila <heikki.orsila@iki.fi> + - Implemented NTSC mode support. It can be buggy and it will not + affect some players at all (those which set CIA tempo by + themselves). NTSC mode can detected in eagleplayers by reading + $212(execbase) aka VBlankFrequency(execbase). It's a byte + with value 50 (PAL) or 60 (NTSC): + move.l 4.w,a6 + cmp.b #60,$212(a6) + beq is_an_ntsc_system + * pal system + - Changed Timing configuration in players/env/PTK-Prowiz.cfg + from CIA <-> VBI/NTSC to CIA <-> VBI. (mld) + - Mod player now honours pal/ntsc setting automatically. (mld) + - Fixed stupid *oops* in mod player reading cia timing from config + file. + - Added ntsc option uade.conf. (mld) + +2006-04-02 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed P61A detection (introduced by mlds change sometime ago). + src/frontends/common/amifilemagic.c had "P60A" as the pattern + for Player 6.1, but obviously it should be "P61A". + - Improved the man page. + +2006-03-31 Michael Doering <mld@users.sourceforge.net> + - Changed DMAWait to dtg_DMAWait and add dtg_Timer support + to DIGI-Booster + - DIGI-booster songinfo now matches modfiles songinfo. + +2006-03-30 Michael Doering <mld@users.sourceforge.net> + - Small check for Fuzzac Packer in amifilemagic. + +2006-03-27 Michael Doering <mld@users.sourceforge.net> + - Renamed XANN-Packer to XANN-Player, following Sylvain 'Asle' + Chipeaux' suggestion. + - Merged Funkok saftey check from EP2.04 to protracker player... + +2006-03-25 Heikki Orsila <heikki.orsila@iki.fi> + - Added a new action key into uade123. 'i' will print module info + and 'I' will print a hex dump of a head of the module. + +2006-03-23 Michael Doering <mld@users.sourceforge.net> + - Sync'd songinfo for modfiles with the more verbose appearance of + Asle's ModInfo v2.31 appearance... It displays now sample sizes, + volume, finetune, loop start and loop size, too. + - Added yet some more tiny differences between Protracker 2.3 and 3.0 + compatibility mode for completeness... (set sample_num in pt_plvskip, + and n_period in pt_doretrig). + These changes might have no effect on the replay quality like e.g. + earlier mentioned Updatefunk calling difference or volume setting, + though. + +2006-03-21 Michael Doering <mld@users.sourceforge.net> + - Small protacker replayer config file parsing fix... + +2006-03-20 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed slight bugs in build system. Architecture specific CFLAGS + were missing from xmms and audacious frontends. Also, those flags + will be the last flags always so that they can be used for + overriding other options. + - Made debug flags really optional. They can be turned off with + --no-debug (for configure). + +2006-03-20 Michael Doering <mld@users.sourceforge.net> + - Figured out the period multiplier issue. It was a bug in my + brain. *g* + - Forgot to add notecut volume setting for protracker 2.3 and below. + This is now fixed. + - Set the default playback style to Protracker 3.0b. + - Added an experimental hybrid protracker setup: 9. + Effects are done ptk2.3a style, volume setting like 3.0. + This fixes some pops and clicks which even the original + protacker 2.3a replay and below had. + +2006-03-17 Michael Doering <mld@users.sourceforge.net> + - Enhanced compatibilty concerning protracker 2.3a/1.1b(fixed), + 2.1a/1.1b, 1.0c and Prottracker 3.0b concerning access of + the periodtable while using the effects. + It's astonishing in how many ways the protracker replays + differ... :-) + E.g. Protracker 3.0b (like Noisetracker) uses a multiplication of + 37*2 for all effects to get the right period. Protracker 2.3a and + the socalled bugfixed 1.1b replay use a value of 36*2 (like the + old Soundtracker) + Last but least ptk 1.0c, the original ptk1.1b and ptk2.1a use + one or the other value for some effects... + All in all, it's a mess and we have 4 different setups in our + protracker config file :-) + - Temporarily disabled the period multiplier hack mentioned above + because it borked on some tunes. + - Added "update volume when skip/hold note" for protracker 2.3 and + below. Protracker 3.0 doesn't do it. + +2006-03-16 Heikki Orsila <heikki.orsila@iki.fi> + - Changed PAL audio synthesis frequency to be exactly 3546895 Hz. It + was 3541200 Hz before. The old value was totally synchronous with + video hw. Changing this does not affect anything else than audio. + The new value is from the hardware reference manual. + +2006-03-16 Michael Doering <mld@users.sourceforge.net> + - Compatibility for PTK-Prowiz can now be set to play files like + Protracker 3.0b, 2.3a or 1.0c. + INFO: Files being played as PT1.0c will have a different vibrato + depth, and use funk_repeat effect instead of the invert loop effect + which might break all mods composed with later protrackers. + - Added support for the different ways Protrackers 1.0, 2.3 and 3.0 + updated periods. + +2006-03-14 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.02 (Muhammad pictures) + - Fixed a bug in xmms plugin that cut off sound data from the end of + a song. + - New Sierra AGI player by Don Adan / Wanted Team + - Better support for old sonic arranger files. + - Improved file type detection on many formats. + - Debugger improvements (see 'c' and 'i' commands) + - Added --buffer-time=x option for uade123 to set audio buffer + length in milliseconds. + - A configuration file was added for PTK-Prowiz (that plays protracker + and clones) to set compatibility to either protracker v3.0b or + v2.3a. Please edit file: players/ENV/EaglePlayer/EP-PTK-Prowiz.cfg. + - More KDE integration: support for kfmexec wrapper. + - Fixed many bugs. + - Many other changes :-) + +2006-03-12 Heikki Orsila <heikki.orsila@iki.fi> + - Changed various uade123 parameters. -k and -K have been changed + to one option: + -k x or --keys=x, where x is 0 or 1 (disabling and enabling action + keys). + --no-filter has been removed. Use --filter=none instead. + +2006-03-10 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed some compiler warnings. Using %u to print unsigned ints :) + +2006-03-01 Heikki Orsila <heikki.orsila@iki.fi> + - Reverted mlds work-around to avoid subsong change lockups. + - Did a potential fix for the XMMS plugin lockup bug. However, I + do not have a test case for this. The problem was, I think, that + play_loop() called uade_lock(), then called + uade_gui_subsong_changed(), then gtk called some function, + which would eventually call get_next_subsong() which would + try to re-take the mutex by uade_lock() -> deadlock. + * THIS THEORY WAS WRONG. + - Merged a patch from Martin Jeppensen to rename some eagleplayers + to better names. + +2006-02-28 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed -m option for uade123. "uade123 -m file" would only print help + but not play the file. + +2006-02-17 Michael Doering <mld@users.sourceforge.net> + - Added some missing file extensions for the KDE mimelnk. + - Use of kfmexec wrapper in KDE mimelnk for easy ftp:// http://, + smb://, zip:// etc. support under KDE/Konqueror. + - Added install script for KDE users in /src/frontents/uade123/KDE. + Now playing Amiga music is just one click away from you ;-) + +2006-02-15 Michael Doering <mld@users.sourceforge.net> + - NTSC Flag in config file ENV:eagleplayer/ for PTK-Prowiz affecting + Sound, Noisetracker, Startrekker and Protracker (vblank) added. + Normal Pro- and Fastracker are not affected. + Info: it's not a real ntsc mode in emulation but just calculating + the CIAA timer to use a value ~ 7.15Mhz, 60khz, 125bpm on PAL + machines... + It's a cludge I know :) + +2006-02-14 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed /dev/urandom detection. Using test -c rather than test -e. + Test -c is more compatible and more exact too. + - Worked around random lock ups when switching very short subsongs + with the xmms subsong changer (mld) + - Config file for PTK-Prowiz' Protracker engine added to set the + way _Protracker_ mods are played. There's currently two values: 0 + for Protracker 3.0b like, 1 for Protracker 2.3A like (for anyone + interested: funkrepeat is updated before parsing the extended fx :) + Differences are probl. not audible but Latter is currently the + default. If it breaks a mod file, you can savely turn back to + "0" (mld) + +2006-02-13 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed uade123 to accept -k and -K switches. Their meaning is now + changed. '-k' disables action keys and '-K' enables. + +2006-02-10 Michael Doering <mld@users.sourceforge.net> + - A new email alias. ;) + - Cosmetical change in name from 32 to 31 instr. for Soundtracker II + in PTK-Prowiz. + +2006-02-08 Michael Doering <mldoering@gmx.net> + - Updated mod2ogg.sh to set encoding quality (Giulio Canevari). + +2006-02-07 Michael Doering <mldoering@gmx.net> + - Put new Sierra AGI player by Don Adan/Wanted Team into players dir. + +2006-02-02 Heikki Orsila <heikki.orsila@iki.fi> + - Cleaned up gensound.h. It contained unuseful external variables + such as sample16s_handler and sample_handler. + - Improved mod detection in amifilemagic and Protracker replayer for + Protracker compatible mods using effects 5,6,7 and 9. (mld) + - Quick "work around" of a lock up with the subsong seek popup and very + short subsongs in audacious plugin. Needs further investigation + though. Some kind of race condition, I think. (mld) + - Reintroduced produce_audio(); to audacious plugin for the freshly + released Audacious 0.2 at http://audacious-media-player.org (mld) + +2006-02-01 Michael Doering <mldoering@gmx.net> + - Changed association of jp file extension to Jason Page New. + - Added simple detection for Jason Page old in amifilemagic. + - Added alternative Jason Page Player for one file Jason Page New + files (Thanks dom from the legacy.de for the report). + - Changed HIP, HIPC and HIP7 to SOG, SOC and S7G prefix in + amifilemagic. + +2006-01-31 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed song length database bug in the XMMS plugin. The XMMS plugin + didn't use the correct md5sum in struct uade_song, instead it used + the old and useless array called curmd5[] which was an empty + string. + - Continued to move common variables in frontends to struct uade_song. + - Fixed instrument name len in songinfo for mods. (mld) + +2006-01-25 Michael Doering <mldoering@gmx.net> + - Added Turbo/Infect's SonicArranger Player for "old" sa.* files + - Added experimental filedetection for two different + Sonicarranger_pc types... + +2006-01-24 Michael Doering <mldoering@gmx.net> + - Synced audacious plugin with xmms plugin and changed back + to support the current stable audacious 0.1.2. + The audacious supporting "produce_audio()" will be supported as + soon as there's a stable release... (mld) + - Ported songinfo for Wanted Team song formats [e.g BSS, DL, FP...] + - Fixed mod title display in songinfo + +2006-01-22 Heikki Orsila <heikki.orsila@iki.fi> + - Added a new option to uade.conf: buffer_time x. buffer_time x in + uade.conf sets audio buffer length to x milliseconds. This can + be used to avoid buffer underruns on some systems. Beware that + Alsa support in libao is buggy in versions 0.8.6 and earlier so + that the actual buffer time must be given in microseconds. At + this time it is not fixed in any official libao releases. + - uade123 option --buffer-time=x can be used to set audio buffer + length to x milliseconds. + +2006-01-21 Heikki Orsila <heikki.orsila@iki.fi> + - Imported the LGPL'ed Max Trax source to amigasrc/players/max_trax. + Then applied a patch from alankila. It is not yet an eagleplayer + but a work-in-progress. (alankila) + - Added a command 'i' to the debugger that traces till next hw + interrupt. + - 'c' command in debugger will now also print cycle count. + +2006-01-20 Heikki Orsila <heikki.orsila@iki.fi> + - Continued frontend modularization and code sharing effort. Created + struct uade_song in eagleplayer.h to store relevant properties + of songs that are played. Modified uade123 and XMMS plugin to use + it. The transition is not completed yet. Many fields are still + unused and those fields unnecessarily duplicated in frontends. + Notice that audacious plugin is broken because of this, but it's + also in a transition phase. + - Fixed merging conflicts between latest frontend modularization + effort and mlds mod/fileinfo changes. + - Fixed bugs in songinfo.c. Some file checking could have read over + over memory boundary causing a segfault (but nothing else). + - Backported fileinfo for DIGI-Booster mods. (mld) + - Fixed a bug in XMMS plugin that it stopped a subsong too early + if there was another subsong to play. The sound data that was + buffered in audio output plugin was discarded. Thanks to Cthulhu + (the old one) for the bug report. + + +2006-01-19 Michael Doering <mldoering@gmx.net> + - Improved vblank detection in Protracker replay for mod.slow_motion + by Jogeir Liljedahl. (mld) + - Started work on backporting modinfo from uade 1. Ideally all + frontends should be able to use it. (mld) + - Experimental "update fileinfo window on songchange" feature... + +2006-01-18 Heikki Orsila <heikki.orsila@iki.fi> + - Added a simple and broken XMMS file info window. Module info + displays a hexdump of the first 1024 bytes of the module. + - Fixed #include issues with FreeBSD in unixatomic.c. + - Optimized sinc interpolator (alankila) + - Changed fileinfo.c to allow many different types of infos for + modules. Hexdump is the current default since nothing else has + been implemented. + +2006-01-17 Michael Doering <mldoering@gmx.net> + - (hopefully) Fixed broken subsong detection for Digital Illusion + mods. + - Fixed unallocating timer resources when changing subsong in + MED/Octamed replayer. As a consequence, changing subsongs works + again. + - Removed sinc table from audio.c and put it into a separate file + named sinctable.c. (alankila) + - audacious plugin: changed legacy uade_ip audio output from xmms + to audacious 0.2 produce_audio(); Seems to work here, but I'm not + sure if it's 100% ok... So give it a test and report back. + - Fixed strlrep.h to #include <strings.h> to have size_t type (shd) + +2006-01-15 Heikki Orsila <heikki.orsila@iki.fi> + - Added more debug messages into the interface between emulator and + sound core. Now Amiga file loading events will give informative + message to the frontend. Use uade123 with -v to see all the spam. + +2006-01-14 Heikki Orsila <heikki.orsila@iki.fi> + - Merged sinc interpolator patch (alankila) + - Changed RK to CM prefix in amifilemagic. This affects custom made + format. Old RK and RKB prefixes are still supported but they may + be removed in the future. + - Added contrib/sinc-integral.py which computes the sinc antialiasing + window for audio.c synthesis. (alankila) + - Made huge changes into sound cores subsong restart and interrupt + logic. Some formats were fixed with respect to subsong changing. + Try Monkey Island now. The tempo should be correct after subsong + change. 4ch MED/OctaMED is still broken. Try changing to subsong + 9 in DaveNinja.med (some channels do not get re-initialized and + a disturbing sound plays on the background). The changes are: + 1. Earlier we didn't call StopInt and EndSound in subsong change, + but now we do. The old procedure just called InitSound and + StartInt because our allocators in sound core were robust + against double allocation. + 2. Implemented unallocation for CIA resources. See + rem_cia[ab]_interrupt functions. + 3. StopInt unallocates the CIA resource used for the player + interrupt. Set_player_interrupt allocates the player interrupt + and calls StartInt. + 4. EndSound has a default code now which turns off audio DMA and + sets volumes to zero. + 5. exec.library/SetIntVector() does not enable interrupts anymore. + If interrupts are disabled they stay disabled. + +2006-01-12 Heikki Orsila <heikki.orsila@iki.fi> + - Made anti (accumulator) interpolator to be the default. + +2006-01-08 Michael Doering <mldoering@gmx.net> + - Commited audacious input plugin based on the XMMS plugin + (http://audacious.nenoload.net). + +2006-01-08 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed XMMS plugin installation which didn't obey package prefix + (Michal Januszewski <spock@gentoo.org>). + - Added some more b-flags for fopen(). This time in code in + src/frontends/common. + - Cleaned up sound data buffering code in uadeipc.c, uade.c and + audio.c. Removed uademsg.h as being useless. + - Changed uadecontrol.c to uadeipc.c in src/Makefile.in because + uadecontrol.c is long gone. + - Optimized uade123 to issue next song data request for the uadecore + before passing sample data to libao. This way the uadecore may + do useful work while libao is blocked on sound device. This didn't + solve the underrun problem completely but effect of improving + behavior is clearly observable with 'top'. Before this change, top + showed only 0.4% CPU usage for uadecore, but after the change it + shows 3.3% CPU usage, and the latter reflects reality much better. + +2006-01-07 Heikki Orsila <heikki.orsila@iki.fi> + - Changed fopen() to use "b" flag for porting to Windows environment. + - Thanks to sasq <jonas@nightmode.org> for help with cross-compiling. + +2006-01-06 Heikki Orsila <heikki.orsila@iki.fi> + - Made uadeipc() re-entrant so that uadecore and a frontend could in + theory be run in the same process but different threads. + - Replaced poll() with select() in unixatomic.c to be compatible with + weak systems. + +2006-01-05 Heikki Orsila <heikki.orsila@iki.fi> + - Continued cleaning up run-time configuration issues. uade123 and + XMMS plugin do not duplicate settings anymore. Most configuration + and command line options are stored inside 'struct uade_config' + which is defined in src/frontends/common/uadeconf.h. + +2006-01-04 Heikki Orsila <heikki.orsila@iki.fi> + - Changed -I./include/ to be -I./include in src/Makefile.in to make + it compatible with mingw. + - PTK-Prowiz now accepts mods with max 1KiB trailing garbage... + Nevertheless use 100% good rips, people! (mld) + - Fixed spelling mistake in PTK-Prowiz *g* (mld) + - Updated uade123 man page with apologies about bad file detection + heuristic :( + +2006-01-04 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.01 + - Compatibility fixes for OpenLSD and Mac OS X to make UADE compile + - Added 'cm.' prefix for CustomMade format (Ron Klaren) + - PTK-Prowiz subsong scanning was improved (mld) + +2006-01-03 Heikki Orsila <heikki.orsila@iki.fi> + - Cleaned up post-processing of sound data. Removed postprocessing.[ch] + because it was redundant functionality and added necessary + functionality into effects.[ch]. Also, moved effect state out of + effects.c by creating 'struct uade_effect'. This allows easy + loading and storing of state. + The goal is to have a unified mechanism for handling information + from command line options, uade.conf, eagleplayer.conf and + song.conf that is easy. All the setting must be storable and + restorable through that mechanism. Currently that is broken, + incomplete and messy. The functionality is even duplicated between + frontends :( It will probably take many cleanup steps to achieve + the goal. + - Fixed Mac OS X compatibility issue. poll() was replaced with + select(). Thanks to Juuso Raitala. + - Improved (hopefully) subsong detection for mods and similar. Now + there should be less false positives in PTK-Prowiz than there + used to be. (mld) + - Removed 255 BPM SetTempo fix in PTK-Prowiz for mod.loader from + Coolspot to fix mod.alkupala by jorma... *sigh* (mld) + - Fixed broken Prorunner support introduced by reorganization + PTK-Prowiz (mld) + +2006-01-02 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a configure bug that occured when --with-xmms was specified. + (Michal Januszewski <spock@gentoo.org>) + - Made contrib/uadexmmsadd script to be installed if XMMS plugin is + installed + - Added a work-around for OpenBSD 3.8 which lacks portable types from + stdint.h. inttypes.h is used instead. Possibly this avoids type + problems on some other OSes too. Note that we require some C99 + features from standard C libraries. Thanks to ave@silokki.org. + - Made OpenBSD use 'gmake'. Thanks to ave@silokki.org. + - Fixed execlp() call in src/frontends/common/uadecontrol.c by + casting NULL as (char *). Thanks to ave@silokki.org. + +2006-01-01 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 2.00 (Mental hangover) + - Finally the first stable release of UADE 2 series. The work began + 6 months ago. There are still rough edges and deficiencies but + it is superior to UADE 1 in many respects. + - UADE 2 series has following improvements over UADE 1 series: + * Superior audio quality due to excellent Amiga 500 and 1200 + filter models. The default sound model is now the Amiga 500 + model. This affects all songs whether or not they use filtering. + (Antti S. Lankila) + * A component architecture which makes creating new frontends + easier + * Unified configuration files to set defaults for all frontends + * Improved command line tool, uade123, supports run-time control + of song playback for switching subsong, skipping to next song, + skipping fast forward, pausing and altering post-processing + effects + * Post-processing effect for headphone users (Antti S. Lankila) + * Skip fast forward feature in uade123 and XMMS plugin + * Many core subsystems have been rewritten or heavily altered + * New supported formats + * UADE 1 produces snaps in audio output because of a bug in + audio DMA engine, but is fixed in UADE 2 + +2006-01-01 Heikki Orsila <heikki.orsila@iki.fi> + - Added contrib/uadexmmsadd script to add uade:// prefixed songs into + XMMS playlist. This is useful to avoid conflicts with protracker + songs with modplug and other XMMS plugins. Any other plugin will not + accept uade:// prefixed entries from the playlist. + - Changed forward seeking button in XMMS plugin from ">>" to "10s fwd". + - Allow gain values greater than 1.0. + - Improved uade123 man page. + - Fixed a NULL pointer derefecence in eagleplayer.conf loading. If + eagleplayer.conf couldn't be loaded it would crash. + - Fixed a directory creation problem with 'make feinstall'. + +2005-12-30 Heikki Orsila <heikki.orsila@iki.fi> + - Merged an altered sample accumulation patch from alankila. The + patch has an effect only if anti interpolator is used. + - Removed crux, linear and rh interpolators because they're broken + by design. Only default and anti are now supported. + +2005-12-22 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed shared library compilation for Mac OS X. Thanks to + Michael Baltaks <mbaltaks@mac.com> for information. + +2005-12-21 Michael Doering <mldoering@gmx.net> + - PTK-Prowiz: Commited various changes to cvs. + o Almost finished reorganizing and redoing the mod checks. + o Fixed bug in Soundtracker with repeat in bytes handling. + o Fixed some bugs in handling empty instruments. + o Added hack for vblank mod detection. + o Hopefully working, support for Karplusstrong fx. + - Amifilemagic: backported a small bugfix in mod detection. + +2005-12-20 Heikki Orsila <heikki.orsila@iki.fi> + - Added a short note for Max OS X users to address a compilation + problem. + - Renamed INSTALL to be INSTALL.readme to avoid makefile problems with + Mac OS X. + +2005-12-19 Heikki Orsila <heikki.orsila@iki.fi> + - Merged accurate audio output patch from Antti S. Lankila. The patch + solves the problem that Paula's 3.5 MHz output was sampled at + regular integer valued intervals that caused inaccuracy. The old + interval was round_down(3541200 / 44100) = 80 paula cycles, but + the real interval is ~80.299 paula cycles. This means 0.4% relative + error in outputted sampling rate that is not audible, but it is + harming filter accuracy analysis. + +2005-12-17 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre10 + - Cleaned up src/include/events.h. Removed unnecessary event + scheduler. + - Reworked audio subsystem to be more debuggable and added comments. + - Fixed a bug in audio state engine that caused DMA engine to play + one word too much of sample data. How could this bug have gone + unnoticed for so long? The same bug exists in UAE too. + - Reverted back to not using interpolation with A500E and A1200E + filters. The anti interpolator caused problems with many songs. + It will be fixed at some point and changed back, but at the + moment there's doubt how to fix the problem. + +2005-12-16 Heikki Orsila <heikki.orsila@iki.fi> + - Applied filter improvement and optimization to audio.c. The + filter should be slightly better than the old. Affects only + A500E and A1200E filters. (alankila) + - Cleaned up audio.c + - Reworked configuration loading system to avoid conflicts with + uade123 command line parameters. Command line parameters have + priority over uade.conf. + +2005-12-15 Michael Doering <mldoering@gmx.net> + - Some progress on porting the mod detection to m68k asm. + +2005-12-15 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a bug that forced filter to be A500E type when --force-led + was uade with uade123. (alankila) + +2005-12-14 Heikki Orsila <heikki.orsila@iki.fi> + - Added displaying total playtime based on content database into + uade123 + +2005-12-12 Michael Doering <mldoering@gmx.net> + - replaced Grouleff replayer with Wanted Team's EarAche. + - added new EMS replayer by Wanted Team. + +2005-12-12 Heikki Orsila <heikki.orsila@iki.fi> + - Added INSTALL file to document build process. + +2005-12-11 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre9 + - An XMMS plugin has been added. New features compared to UADE1 XMMS + plugin are seek fast forward and correct subsong seeking bar. The + plugin still lacks GUI for configuration, but uade.conf can be + edited directly. + - Filtering settings are audibly different compared to last release. + A500E filter model is now the default. It was A1200 in the last + release. This affects all songs, even those which do not use the + filter. + - New players for 15 instrument soundtracker variants have been added. + - uade123 man page has been updated. + - Most subsystems have gone through changes. Some important changes + have been left out. See log entries for further details. + +2005-12-11 Heikki Orsila <heikki.orsila@iki.fi> + - Significant changes in filter default values. Current default filter + is A500E, which is audibly different on every song compared to the + old default (A1200). To restore the old behavior, set "filter a1200" + in uade.conf. If you want to advocate another default value, please + post to the forum or send email. The forum is preferred. + +2005-12-10 Heikki Orsila <heikki.orsila@iki.fi> + - Added notices to documentation that setting filter model has an + audible effect even if a song doesn't use filtering at all. + - Cleaned up configure script (that is originated from uade1) + +2005-12-07 Heikki Orsila <heikki.orsila@iki.fi> + - Added unrecognizable type "packed" into amifilemagic. It is + used to inform user about files that are packed with an amiga + packer. + - some work on m68k mod checking and replay routine (mld) + +2005-12-06 Michael Doering <mldoering@gmx.net> + - XMMS plugin now automagically advances the subsong seek slider, if + the replay rolls over to the next subsong. + - Soundtracker15 name check that was added yesterday was removed. (shd) + - Fixed a bug in uade_filemagic() that char *pre was not initialized + to be an empty string by default. This caused + uade_analyze_file_format() to receive garbage data when file format + was not recognized. (shd) + - Valgrinded a memory allocation bug in two places of eagleplayer.c + (the same error actually due to replicating same lines of code), + where space for (n + 1) pointers should have been allocated, but + only ((n * sizeof ptr) + 1) bytes was allocated. (shd) + - Added gain effect into uade.conf. You can use variable named + 'gain' to set scaling value for each outputted sample. For + example, add a following line to uade.conf: + gain 0.25 (shd) + - Made length test of mods with 32 instruments less strict. Due to + popular demand *g* now "oversize mods" get accepted. + - Added check for sane finetune and volume values in modchecks. + +2005-12-05 Heikki Orsila <heikki.orsila@iki.fi> + - Added initial version of song length database into XMMS plugin. It + is compatible with uade1 db-content, but named differently: + ~/.uade2/contentdb. You can just copy the old db: + cp ~/.uade/db-content ~/.uade2/contentdb + - Content db is loaded during play_file if it has changed + on the disk. If it has not changed on the disk, then it is + saved if an hour has passed and the db has been changed. + - Added a requirement for Soundtracker15 song content detection that + the name must have prefix or postix being: "mod", "mod15", or + "mod15_". This change could be reverted at some point but now it's + the safest choice. + +2005-12-04 Heikki Orsila <heikki.orsila@iki.fi> + - Added missing #include for uadecontrol.h (sys/types.h for pid_t). + +2005-12-03 Heikki Orsila <heikki.orsila@iki.fi> + - Made XMMS plugin display play time correctly in the play list + after a song ends. If song ended volutarily, tell XMMS the + play time. If song ended involuntarily by user intervention, + error, or timeout, tell XMMS that the song doesn't have a length + leaving the play list time empty. + +2005-12-02 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a latency and time bug in the XMMS plugin that affected + fast forward seeking. When forward seeking happened the XMMS time + display was not updated and the seeking happened with a delay. + - Made XMMS plugin report the play time for XMMS after a song + ends. + - Added comments about variable locking in XMMS plugin. + +2005-12-01 Michael Doering <mldoering@gmx.net> + - eagleplayer.conf: Amended the different MarkCooksey prefixes. + - amifilemagic: fixed TFMX 1.5 detection bug introduced by the recent + clean-up. + - amifilemagic: fixed another TFMX detection bug. Should be alright + again now. + +2005-11-30 Heikki Orsila <heikki.orsila@iki.fi> + - Make XMMS plugin auto detectable in configure script. The plugin is + still very experimental and incomplete in features, but it can + already play songs, or at least it should. + - GCC4 gave warnings of problems I had not noticed: Fixed a bug in + md5.h. uint32_t was accidently redefined (that is #included + originally from stdint.h). XMMS plugin's uade_ip structure was + declared static but later as external. Fixed signedness warnings + from various modules. + - Added seek-forward button into XMMS plugin. (mld) + - New replayer for Soundtracker v2-v5 mods with 15 instruments and + a lot of different effects added. (mld) + - fixed WaitAudioDMA for all old mod15 replayers. (thanks heikki!) + - changed mod15 (again) for stricter st-iv detection (mld) + - changed to a stricter tracker module length policy... + If uade doesn't play your modfiles anymore, it's a bad rip. + Get a good one! :) + - Since Master-Soundtracker files seem to use a subset of the normal + Soundtracker fx, they now get played with the mod15 replay + This makes Master-ST replayer kind of obsolete atm. (mld) + +2005-11-29 Heikki Orsila <heikki.orsila@iki.fi> + - Partial and buggy implementation of song.conf. It is used for uade + specific work-arounds for broken or bad songs. Look at doc/song.conf + and src/frontends/common/eagleplayer.c. Please do not use this yet. + - Fixed some compiler warnings that surprisingly came with gcc 4.0.2. + - Fixed Makefile.in to not report error on 'make install' when XMMS + plugin is not installed. + +2005-11-28 Heikki Orsila <heikki.orsila@iki.fi> + - Modularized loading and parsing on uade.conf options. See + src/frontends/common/uadeconf.[ch]. + - It is possible that config parsing for uade123 breaks now. + - Added partial config loading support for XMMS plugin. Doesn't + support setting filter type or interpolation mode yet. You must + edit uade.conf by hand if you want configuration changes. No GUI + for setting options yet. + - Added cleaning rule for XMMS plugin + - New make rule: 'make feclean' will clean all frontend objects + - The XMMS plugin determines configuration file path for uade.conf + once during XMMS plugin initialization, and it will not change + afterwards. If no global or user configuration is found, then + the plugin chooses the file under HOME ($HOME/.uade2/uade.conf). + It will try to load the configuration each time a new song is + selected from the XMMS. + - Optimization: XMMS will re-read configuration when a new song is + selected if the file timestamp of uade.conf has changed. + - Fixed synchronization problem in XMMS plugin's play loop. The audio + device is drained of written sound data before actually stopping + the device. The earlier version cut of audible data from the end + of the song :( However, draining can be interrupted if the user + requests something urgent, such as wanting an immediate song change. + Draining takes time as long as audio + device has buffered data. Buffering settings can be found from + output plugin settings, as usual. + +2005-11-28 Heikki Orsila <heikki.orsila@iki.fi> + - Added hardcoded timeouts for the XMMS plugin. They are the same + as uade123 defaults. 20 seconds for silence and 512 seconds for + subsong. Reading uade.conf variables is not supported yet. + +2005-11-27 Heikki Orsila <heikki.orsila@iki.fi> + - Continuing modularization of uade frontends. Various commands, + such as set subsong, change subsong, set filter type, and set + interpolation mode, were moved to src/frontends/common/uadecontrol.c. + The idea is that all the non-trivial commands have a wrapper in + uadecontrol.c, but trivial commands that don't require parameters + can be used through uadeipc.c (uade_send_short_message()). + - Added src/include/uadeconstants.h that should contain constants that + are common with uadecore and frontends. + - Subsong seeking works in XMMS plugin :-) + - Made XMMS plugin globally installable as it should be + +2005-11-26 Heikki Orsila <heikki.orsila@iki.fi> + - Continuing modularization of uade frontends. Renamed + src/uadecontrol.c to be src/uadeipc.c, and added + src/frontends/common/uadecontrol.c which contains a set of + helper functions to control and spawn uadecore. + - Added --with-xmms to configure script for developing the XMMS + plugin. It does not work yet! + +2005-11-25 Heikki Orsila <heikki.orsila@iki.fi> + - Cleaned up and fixed tronictest check in amifilemagic. Added + read_be_u16() and read_be_u32() to help parsing amiga formats. + - Cleaned up tfmxtest in amifilemagic. + - Cleaned up modparsing in amifilemagic. + +2005-11-24 Michael Doering <mldoering@gmx.net> + - amifilemagic: improved mod32 and mod15 checks a bit + - ha! found pitchbend incompatibility bewteen + Master-ST and DOC Soundtracker II in amifilemagic. Now + Mods using the pitchbends bigger than 0xf are played as + DOC Soundtracker II:) + +2005-11-23 Michael Doering <mldoering@gmx.net> + - Master-ST and Ultimate-ST replayers now check for a valid file + length, thus badly ripped mod15 songs won't get played anymore with + UADE. No exceptions. + FYI this will also be future for all other Sound and Protracker + derivates, so for anyone having bad rips - get some valid files! :) + +2005-11-22 Michael Doering <mldoering@gmx.net> + - Improved amifilemagic: mod32 check now tries to distinguish + 10 different mod types. (BTW. 4ch Fastracker mods and similar + now default to mod_pc and get played by the Multichannel PS3M player) + - Some more work on the mod15 check in amifilemagic again. + - Started to update the amiga mod15 replayers (Ultimate-ST and + Master-ST with better checks, resulting in breaking support for other + players like EP or DT atm. + +2005-11-18 Michael Doering <mldoering@gmx.net> + - Lowered the file buffer size to 8192 bytes to reduce overhead with + xmms plugin scans. Unfortunately mods with a header and pattern data + beyond that buffer size can't be detected properly and get played + as plain mod15. + - Improved mod15 check. It should produce now less false positives. + - Added a smarter(?) way of the mod check for larger files that + don't fit into the check buffer completely. + - Renamed filemagic() to uade_filemagic() (shd) + - Filemagic buffer size (8192) is now passed as an argument to + uade_filemagic(). Previously both the caller and callee knew the + size. + - Made amifilemagic tables static (only visible inside the module) + (shd) + - Name prefix conflict between Future Player and PTK-Prowiz was + resolved in favor of Future Player. The name prefix/extension is + 'fp'. (shd) + +2005-11-16 Heikki Orsila <heikki.orsila@iki.fi> + - Made install to a standard path by default. That is /usr/local. + Use ./configure --prefix to override. configure --user will install + to users home directory as in the past. + - Committed initial version of man page for uade123. + - added a first uade-only mod15_Mastertracker player (mld) + - improved (?) mod15 type checks in amifilemagic (mld) + - started to work on more complete mod type check in amifilemagic + (mld) + +2005-11-13 Heikki Orsila <heikki.orsila@iki.fi> + - Cleaned up src/frontends/common/eagleplayer.c. Removed skip_ws(), + skip_nws(), and loops that used them, and replaced those with + shorted loops that use strsep(). Changed index variables to use + size_t instead of int to be more robust against bad input. + - Fixed a bug that if eagleplayers.conf specifies always_ends for + an eagleplayer then forcing timeout from command line with -t + didn't work. + - Fixed a dirty bug in score that made score crash if an eagleplayer + gave a NULL pointer as module name. This happened with Frontier + custom. Closer inspection revealed that Frontier custom is also + buggy because it sets module name in InitSound() but the + specification says module name is evaluated after InitPlayer() + which is before InitSound(). This looks like a design bug in + the interface, or a typo in the documentation, because setting + module separately for each subsong is useful, and that is what + Frontier actually does. + +2005-11-12 Heikki Orsila <heikki.orsila@iki.fi> + - Add new antialiasing interpolation mode, which corrects for noisy + treble especially audible in the A1200 filter model. It does its + work by computing the average value of Paula's output pins between + samples. + +2005-11-09 Michael Doering <mldoering@gmx.net> + - Fixed missing hipc and soc prefixes in eagleplayer.conf. + - Associated #chn, ##ch mods to PS3M again. (note: s3m, xm or mtm + are still omitted by uade) + +2005-11-08 Heikki Orsila <heikki.orsila@iki.fi> + - Added slight noise reduction into CSpline code to reduce + noise due to interpolation errors, and snapping sounds from + sudden volume changes. (alankila) + - Updated headphone filtering parameters to place virtual + sound sources closer to head and reduced the associated treble + filtering to make the sound at the same time brighter and more + forward placed. The sources still appear slightly behind and + perhaps elevated, so the illusion will be further improved. + (alankila) + - fixed AON8 timing now for real *grin* (mld) + - added Musicline Editor to be detected by amifilemagic (mld) + +2005-11-07 Michael Doering <mldoering@gmx.net> + - Removed dupes from new eagleplayer.conf (mld) + - Added new player: Musicline Editor (mld) + - Broke ArtOfNoise8 timing, and added song end detection. (mld) + +2005-11-06 Heikki Orsila <heikki.orsila@iki.fi> + - Added an experimental support for eagleplayer.conf, which is + specified at doc/eagleplayer.conf. The new system allows eagleplayer + specific settings. uadeformats is no longer used, so it has been + removed. Here's a list of currently possible eagleplayer options: + a500 (A500 type filter emulation is used.) + a1200 (A1200 type filter emulation is used.) + always_ends (A song always ends, so timeouts are not used.) + content_detection (File name prefix or postfix heuristics are not + used for determining format.) + speed_hack (Speed hack is enabled.) + Tips: + Speed hack can be turned on for a specific format by editing + eagleplayer.conf. A format can be marked as ending always, which + means that timeout values are not used (except silence timeout). + There are other options too, and all of them are specified in + doc/eagleplayer.conf. + + +2005-11-05 Heikki Orsila <heikki.orsila@iki.fi> + - Player interrupt is now a CIA A interrupt by default. As a + consequence CUST.Loom works (it requires that player interrupt + runs at a lower priority level than audio interrupts). + THM.BlueAngel69 might play better now - a good comparison is needed. + There are still a few things to do in the sound core's interrupt + system, see amigasrc/score/todo.txt. + - Headphones effect clipping fix (alankila) + - Added improved (but currently experimental) LED filtering code. + The old code was based on assumption that A500 and A1200 have same + output filtering circuitry, but this was not correct. After + analysing hi-fi measurements by pyksy, I designed new filters + by tweaking a couple of equalizers on top of a RC filter that + provided basic treble attenuation. Use --filter=a500e or a1200e to + test the new filters. (alankila) + +2005-11-04 Heikki Orsila <heikki.orsila@iki.fi> + - Separated CIA B timer A and timer B interrupts finally. Some + Forgotten World songs started to work, but not all. Thomas + Herman BlueAngel69 swent back to its earlier buggy state. Our + recent interrupt handler change broke Thomas Hermann completely. + This change fixes our original design fault that only one CIA + timer interrupt may be used, but not both. This patch takes us + much closer to full CIA interrupt handler support. + - Heikki's CIAB-A and CIAB-B separation in soundcore gave us full + support of the MusiclineEditor player from Eagleplayer 2.02. + You still have to use --speedhack to give it enough + cpu cycles, though. (mld) + - forgot to add detection bug fix of the two different MCMD formats + in amifilemagic to changelog *grin* (mld) + +2005-11-03 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre8 + - Many bug fixes and cleanups. + - Headphones postprocessing effect. + - Improved A1200 filter emulation. + - Added A500 filter emulation. Use --filter=a500. + - Cleaned up sound cores timer code. + - Fixed CIA timer initialization. + - Improved uade123 user interface. Press 'h' in uade123 or list + action keys with uade123 --help. + - New sample interpolation code (--interpolator=cspline). + - --stderr can be used to pipe sound data to stdout. + +2005-11-02 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a very stupid bug I introduced yesterday to src/uade.c. + if (curs > maxs) + foo(); + bar(); + Duh. Why did I forgot {}?? + - Network byte orderized, or big endianized, subsong info transmission + between uadecore and frontend. This wasn't a bug. Just a change for + consistency. + +2005-11-01 Heikki Orsila <heikki.orsila@iki.fi> + - Removed src/effects.c. It was accidently left there from uade 1 + (alankila) + - Removed unnecessary functionality from src/players.c. Black listing + Protracker modules to be VBI timed should be in frontend logic + rather than emulator logic. + - Cleaned up text messages all over the place. + +2005-10-31 Heikki Orsila <heikki.orsila@iki.fi> + - Did highly experimental changes to sound core's timing interrupt + system. I tested the change with all known players, and only the + Thomas Hermann broke out, but it was broken already, so no big + loss there. Of course I only tested each format with a few songs, + so it's very possible that if the new system doesn't work, + then I couldn't catch the problems. Nevertheless, I spent over an + hour listening to different samples. Testing this change would be + appreciated. One should especially pay attention to tempo, because + that may have gone wrong. The changes I technically made were: + - Made default interrupt timer to be CIA B timer A. Took away all + the hacks to use VBI when ever possible to be the default timer. + - While turning on any CIA B timer (A or B), it does not turn off + the other CIA B timer. + - Fixed CIA B timer initialization. Setting a timer (A or B) gets + the timer value from dtg_Timer(eaglebase). + - Cleaned up audio interrupt server. + - Fixed tiny bug in uade123 file extension detection (mld) + - Moved amifilemagic, uadeformats, and unixwalkdir modules to + frontends/common/ where they belong. + +2005-10-30 Heikki Orsila <heikki.orsila@iki.fi> + - Modified headphones effect. (alankila) + - Added 'H' action key to toggle headphones effect, and 'P' to toggle + panning effect. Default panning value is 0.7, unless specified + otherwise with command line option or in uade.conf. + - Added forgotten -O2 optimization flag to uade123 Makefile + - Cleaned audio.c. Removed some debug #defines. + - Imported amiga player sources from uade1 project to amigasrc/players/ + +2005-10-29 Heikki Orsila <heikki.orsila@iki.fi> + - Antti S. Lankila <alankila@bel.fi> will be referred to as 'alankila' + in ChangeLog from now. + - Beautified and cleaned up audio.c and sd-sound-generic.h. + - Fixed a bug that caused 1 bit precision loss in audio output. + Sample data was multiplied after filtering, but it should have been + multiplied before. (alankila) + - cust.Bubble_Bobble gives wrong subsong information. Added a logic + into uade123 that determines subsong ranges if default subsong + is outside the reported range. + - Changed score's CIA setup so that both CIA-A and CIA-B time of day + counters run continously. Some players, such as Oktalyzer and + sean connolly depend on this. As a consequence it was possible to + remove a work around from score that patched the sean connolly + player not to use CIA TOD. However, any player using CIA TOD is at + risk of not functioning properly, but I have only seen two cases + so far (those which I mentioned). + - Removed score from uade source root, now it's only located at + amigasrc/score/ directory. + - Fixed a bug in uade123/playloop.c. If one subsong ended because of + silence, all the rest subsongs would end because of silence too. + I forgot to reset the silence counter to zero.. + - Added a headphone postprocessing effect, and an effect framework for + different uade frontends. Try --headphone. (alankila) + - Try pressing 'p' on uade123 to toggle postprocessing effects on + and off. + - New filter code (alankila) + +2005-10-28 Heikki Orsila <heikki.orsila@iki.fi> + - Radical cleaned up in audio.c. + - Fixed rh and crux interpolators. Thanks to Antti S. Lankila. + <alankila@bel.fi> + - Removed lots of unnecessary macros, variables and functions. + - rh interpolator is now names as linear. + - Removed src/config.h. It is unnecessary. + - uade123 help will now print usable action keys too. Also, one may + press 'h' at run-time to see usable keys. It will print currently + as follows: + Action keys for interactive mode: + '.' Skip 10 seconds forward. + SPACE, 'b' Go to next subsong. + 'c' Pause. + 'f' Toggle filter (takes filter control away from eagleplayer). + 'h' Print this list. + RETURN, 'n' Next song. + 'q' Quit. + 's' Toggle between shuffle mode and normal play. + 'x' Restart current subsong. + 'z' Previous subsong. + - To verify whether a song uses filter or not, enable verbose mode + for uade123 by -v. You'll see messages like: + Message: Filter ON + - Added two different types of filters: A500 and A1200. Thanks to + Antti S. Lankila, again. Use --filter=a500 or --filter=a1200, + the A1200 case is the default. Both types of filter are + but A1200 filter is better tested. We need feedback on A500 filter. + - --force-filter was changed to --force-led. + - Fixed a bug with getopts. The long options list was not zero + terminated, and it had worked by luck so long.. + - Added good ol' speed hack feature into uade123. Use --speedhack + to increase CPU speed for players that extra virtual power. It + is useful for players that require more cpu than 68k can give, + multichannel oktalyzer for example. + +2005-10-27 Heikki Orsila <heikki.orsila@iki.fi> + - Made interpolation mode selectable from command line. Use + --interpolator=x to choose the interpolation mode, where + x = default (no interpolation), + rh = rh interpolator (broken atm), + crux = crux interpolator (broken atm), + cspline = Antti S. Lankila's spline interpolator + (This is not recommended for chip songs! The spline + spline interpolator is good for natural instruments, + but may produce bad sounds with chips.) + - Interpolator is selectable from uade.conf file by adding line: + interpolator foo + Note that using anything else but default is not recommended + at the moment. + - As per Michael Doering's needs, I added --stderr option to uade123. + It will force all terminal output printed onto the stderr, and thus + allows piping pure sound data. Example: + uade123 --stderr -e raw -f /dev/stdout DW.Platoon |postprocessing + +2005-10-27 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre7 + - Antti S. Lankila <alankila@bel.fi> fixed and improved filter + behavior. The filter state must be updated even when filtering + is not outputted. Output scaling was fixed to the right place + so that it does not distort filter state. Unnecessary range + scaling of input samples was removed. Some documentation was + added about technical properties of the IIR filter. + - Filtering by default was not enabled for those people who had an + earlier uade2 version installed to their home directory, because + make install does not overwrite ~/.uade2/uade.conf. Now the default + is hard coded into the source code. + +2005-10-27 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre6 (tester pre) + - Uade core now supports only 16-bits stereo. If 8-bits or mono is + needed the sample data can be postprocessed. + - Added experimental filtering support. It should be better than the + one found from uade1. Thanks to Antti S. Lankila <alankila@bel.fi> + for IIR filter coefficients and advice. + - New uade.conf options: + filter - enable filter emulation + no_filter - disable filter emulation + filter_off - turn filter always off + - New command line options: + --filter Enable filter emulation + --force-filter=x, where x = 0, or x = 1. Set filter state either + off (0) or on (1). + +2005-10-16 Heikki Orsila <heikki.orsila@iki.fi> + - Added a fuzzy state diagram on client-server interaction from client + perspective. + +2005-10-08 Heikki Orsila <heikki.orsila@iki.fi> + - Unknown keywords in uade.conf are ignored. Old behavior was to + terminate uade123 on an unknown keyword. + - First try to load uade.conf from ~/.uade2/uade.conf, and then + try the global config file ($PREFIX/etc/uade.conf) + - Simplify playloop in uade123. + - Beautify directory hierarchy scanning by avoiding unnecessary + '/' characters. + - Started specifying uade client-server protocol. See + doc/play_loop_state_diagram.dia. + - Removed S3M support. It's not an Amiga format, and thus it doesn't + belong to UADE. It might also interfere with other players when + UADE is used as an XMMS plugin. Use XMP, modplug or something + else for these formats. + - 'make check' is now 'make soundcheck' because it's more a sound + check than a good test set. + +2005-10-03 Heikki Orsila <heikki.orsila@iki.fi> + - Action keys made into default behavior for uade123. + +2005-09-08 Heikki Orsila <heikki.orsila@iki.fi> + - Giulio Canevari pointed out that --panning doesn't work. For + some reason I didn't notice that (perhaps didn't test ;-). The + problem was false parameters for GNU getopt. + - mod2ogg2.sh from Giulio Canevari + +2005-09-04 Heikki Orsila <heikki.orsila@iki.fi> + - Added -g option to uade123 to only print information about songs. + Songs are not played in this mode. All relevant output goes into + stdout. This should be useful for scripting people. An example: + + $ uade123 -g /svu/chip/mod/mod.Unit-a-remix 2>/dev/null + playername: Protracker & relatives + modulename: unit-a-remix + formatname: type: Protracker + subsong_info: 1 1 1 (cur, min, max) + <uade123 quits fast> + +2005-09-01 Heikki Orsila <heikki.orsila@iki.fi> + - Cygwin fixes. Add cygwin detection to configure script. Makefile + should be aware that uade123 is named uade123.exe on cygwin. + +2005-08-27 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a memory copying bug which could cause sound data + corruption when skipping 10 seconds forward with uade123. The bug + was at playloop.c:273. memmove should be used instead of memcpy (shd) + - A patch from Jarno Paananen <jpaana@s2.org>. It fixes use of C99 + anonymous initializers with sigaction (2) on Cygwin. + +2005-07-28 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre5 (developer release) + - Nothing better to do. Let's release the new version for users to + test. + +2005-07-25 Heikki Orsila <heikki.orsila@iki.fi> + - It seems ALSA lib could fork and consequently terminate a process + when used through libao, and we must not consider it an error in + our signal handler that catches all dead children. The signal + handler assumed that the only child that could die is uade. (shd) + +2005-07-24 Heikki Orsila <heikki.orsila@iki.fi> + - New keys for interactive mode: + [0-9] - Select subsong in range [0, 9] + q - Quit player + s - Switch between normal and shuffle mode + x - Restart current subsong + - Added -£ or --no-song-end switches. Song just keeps playing even if + the amiga player says it has ended. Dude! You can get pretty weird + sounds with this, and sometimes the sound core crashes, and should + crash. Fortunately that doesn't kill the simulator :) + - Made help print (-h) prettier by aligning tex columns + - Added -K or --no-keys to disable action keys (this can be used to + override the uade.conf if it enables actions by default). + +2005-07-23 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre4 (developer release) + - Added shell interaction keys into UADE123. The keys can be enabled + with -k switch, or adding line "action_keys" into uade.conf. + The keys are (mimiking XMMS): + z - Previous subsong + c - Pause + b - Next subsong + n - Next song + . - Skip 10 seconds forward + ENTER - Next song + SPACE - Next subsong + Does someone want these configurable into uade.conf? Please email + me. + +2005-07-22 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre3 (developer release) + - Added a -j to skip x seconds of audio from the beginning. Note that + this does not affect timeout parameters in any way. If timeout is + 1 minute and skip is 2 minutes, the song will just end before + anything is played. + +2005-07-21 Heikki Orsila <heikki.orsila@iki.fi> + - Added silence timeout + - Wrote a config file parser for uade123. Look at uade.conf file + for instructions. uade123 tries to load following files in order on + startup: BASEDIR/uade.conf and $(HOME)/.uade2/uade.conf. Command + line options can override config file parameters. Users of uade123 + might want to configure timeout, panning and such values as + personal defaults. This is a very important feature important over + uade 1.0x command line tool. + - Restructured uade123 code into different code modules to make + maintaining and code reuse easier. + +2005-07-18 Heikki Orsila <heikki.orsila@iki.fi> + - Fixed a bug in uade123 that prevented it from playing the last + subsong of a song. + +2005-07-18 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre2 (developer release) + - uade123 now has eagleplayer fileformat check ignoring feature (-i), + panning (-p), song timeout (-t), and subsong timeouts (-w) + +2005-07-17 Heikki Orsila <heikki.orsila@iki.fi> + - uade123 now uses GNU getopt + - uade123 can now output both raw and wav formats by using libao + file output mechanism. Wav format is the default. Example: + uade123 -e wav -f foo.wav songfile + - The sound core now reports to the simulator when audio output should + start. Traditionally the simulator has produced audio output from + the reboot of the amiga even if it is only useful to output audio + after all the lengthy player initializations have been made in + the sound core. For example, AHX.Cruisin now has 0.96 seconds less + zero samples in the beginning. + - Cleaned up sound core a bit. Removed some unused definitions + of messages between sound core and the simulator. Removed unused + code that was designed to be used when running sound core under a + _real_ AmigaOS. + - Made uade123 less verbose. Use -v option to get more details. + - Renamed uade-trivial.c to 'uade123.c' in src/frontends/uade123 + - Renamed directory 'trivial' to 'uade123' in src/frontends/ + +2005-07-15 Heikki Orsila <heikki.orsila@iki.fi> + * UADE 1.50-pre1 (developer release) + - Lots of changes into uade123 + - This is just a preview of the new system. There are no interesting + features over uade 1.0x versions. This release doesn't even have + xmms / beepmp plugins. + - Short instructions for testing: + $ ./configure && make + $ make test + $ make install + Will install everything to $(HOME)/.uade2/. Then + $(HOME)/.uade2/uade123 is the player you can use. This version + uses libao for audio output. + +2005-07-12 Heikki Orsila <heikki.orsila@iki.fi> + - Code in src/amifilemagic.c does amiga fileformats detection. If it's + useful for any other project out there, it is now dual licensed + under the GNU GPL _and_ Public Domain. By public domain we mean + that you can do anything you like with the code, including + relicensing arbitrarily for your projects. + +2005-07-11 Heikki Orsila <heikki.orsila@iki.fi> + - Improved the command line frontend in src/frontends/trivial/, + and now it is called uade123. It can now do fileformat detection by + content, and load proper players from their installation place. + Also, it can play multiple songs in a sequence if one switches to + next song with ctrl-c before the song actually ends. If the song + ends by itself, the system will crash ;) + - Found a bug in amifilemagic by accident. chk_id_offset() function + tested patterns of length sizeof(patterns[i]) which is totally + wrong. It was corrected to strlen(patterns[i]). + +2005-07-09 Heikki Orsila <heikki.orsila@iki.fi> + - Started hacking uade. The goal is to release uade 2.00 someday + http://board.kohina.com/viewtopic.php?p=3499#3499 + - These changes start a series. Version 1.50 will be the first public + release in this series. + - src/frontends/trivial/ can now play single file songs. + - Debugging is broken because libao can't handle signals well. + - Tons of things missing from the system. diff --git a/plugins/uade2/uade-2.13/README b/plugins/uade2/uade-2.13/README new file mode 100644 index 00000000..9ada771a --- /dev/null +++ b/plugins/uade2/uade-2.13/README @@ -0,0 +1,53 @@ +UADE - Unix Amiga Delitracker Emulator +====================================== + +UADE is a music player for UNIX platforms that plays music formats used on +the Amiga computer. + +Very short instructions for installing UADE +=========================================== + +1. Read INSTALL.readme +2. Install the program globally or directly to your home directory. Do either + ./configure + or + ./configure --user (makes uade to be installed under ~/.uade2) +3. make +4. make install (as root if installed globally, but as the user if configure + was given --user) + +The program is ready now. + +Now you can edit uade.conf, if you want. uade.conf is located at +$PREFIX/share/uade2/uade.conf or ~/.uade2/uade.conf. If you installed +globally, you can make a copy of uade.conf to ~/.uade2/. + +Fire up xmms, audacious or use the command line tool. + +$ uade123 -zr /my/chips + +Credits +======= + +See AUTHORS file for credits. + +Information sources +=================== + +Web site: + + http://zakalwe.fi/uade + +Public web forum (most issues should go here): + + http://board.kohina.net/index.php?c=5 + +Public IRC channel: + + #amigaexotic at IRCNet + +Subscribe to new releases at: http://freshmeat.net/projects/uade + +Project maintainer: + Heikki Orsila + heikki.orsila@iki.fi diff --git a/plugins/uade2/uade-2.13/src/frontends/common/amifilemagic.c b/plugins/uade2/uade-2.13/src/frontends/common/amifilemagic.c new file mode 100644 index 00000000..9365436c --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/amifilemagic.c @@ -0,0 +1,1168 @@ +/* + Copyright (C) 2000-2005 Heikki Orsila + Copyright (C) 2000-2005 Michael Doering + + This module is dual licensed under the GNU GPL and the Public Domain. + Hence you may use _this_ module (not another code module) in any way you + want in your projects. + + About security: + + This module tries to avoid any buffer overruns by not copying anything but + hard coded strings (such as "FC13"). This doesn't + copy any data from modules to program memory. Any memory writing with + non-hard-coded data is an error by assumption. This module will only + determine the format of a given module. + + Occasional memory reads over buffer ranges can occur, but they will of course + be fixed when spotted :P The worst that can happen with reading over the + buffer range is a core dump :) +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#include <uadeutils.h> +#include <amifilemagic.h> + +#define FILEMAGIC_DEBUG 0 + +#if FILEMAGIC_DEBUG +#define amifiledebug(fmt, args...) do { fprintf(stderr, "%s:%d: %s: " fmt, __FILE__, __LINE__, __func__, ## args); } while(0) +#else +#define amifiledebug(fmt, args...) +#endif + +#define WAV_HEADER_LEN 44 + +enum { + MOD_UNDEFINED = 0, + MOD_SOUNDTRACKER25_NOISETRACKER10, + MOD_NOISETRACKER12, + MOD_NOISETRACKER20, + MOD_STARTREKKER4, + MOD_STARTREKKER8, + MOD_AUDIOSCULPTURE4, + MOD_AUDIOSCULPTURE8, + MOD_PROTRACKER, + MOD_FASTTRACKER, + MOD_NOISETRACKER, + MOD_PTK_COMPATIBLE, + MOD_SOUNDTRACKER24 +}; + + +#define S15_HEADER_LENGTH 600 +#define S31_HEADER_LENGTH 1084 + + +static int chk_id_offset(unsigned char *buf, int bufsize, + const char *patterns[], int offset, char *pre); + + +/* Do not use '\0'. They won't work in patterns */ +static const char *offset_0000_patterns[] = { + /* ID: Prefix: Desc: */ + "DIGI Booster", "DIGI", /* Digibooster */ + "OKTASONG", "OKT", /* Oktalyzer */ + "SYNTRACKER", "SYNMOD", /* Syntracker */ + "OBISYNTHPACK", "OSP", /* Synthpack */ + "SOARV1.0", "SA", /* Sonic Arranger */ + "AON4", "AON4", /* Art Of Noise (4ch) */ + "AON8", "AON8", /* Art Of Noise (8ch) */ + "ARP.", "MTP2", /* HolyNoise / Major Tom */ + "AmBk", "ABK", /* Amos ABK */ + "FUCO", "BSI", /* FutureComposer BSI */ + "MMU2", "DSS", /* DSS */ + "GLUE", "GLUE", /* GlueMon */ + "ISM!", "IS", /* In Stereo */ + "IS20", "IS20", /* In Stereo 2 */ + "SMOD", "FC13", /* FC 1.3 */ + "FC14", "FC14", /* FC 1.4 */ + "MMDC", "MMDC", /* Med packer */ + "MSOB", "MSO", /* Medley */ + "MODU", "NTP", /* Novotrade */ +/* HIPPEL-ST CONFLICT: "COSO", "SOC",*/ /* Hippel Coso */ + "BeEp", "JAM", /* Jamcracker */ + "ALL ", "DM1", /* Deltamusic 1 */ + "YMST", "YM", /* MYST ST-YM */ + "AMC ", "AMC", /* AM-Composer */ + "P40A", "P40A", /* The Player 4.0a */ + "P40B", "P40B", /* The Player 4.0b */ + "P41A", "P41A", /* The Player 4.1a */ + "P50A", "P50A", /* The Player 5.0a */ + "P60A", "P60A", /* The Player 6.0a */ + "P61A", "P61A", /* The Player 6.1a */ + "SNT!", "PRU2", /* Prorunner 2 */ + "MEXX_TP2", "TP2", /* Tracker Packer 2 */ + "CPLX_TP3", "TP3", /* Tracker Packer 3 */ + "MEXX", "TP1", /* Tracker Packer 2 */ + "PM40", "PM40", /* Promizer 4.0 */ + "FC-M", "FC-M", /* FC-M */ + "E.M.S. V6.", "EMSV6", /* EMS version 6 */ + "MCMD", "MCMD_org", /* 0x00 MCMD format */ + "STP3", "STP3", /* Soundtracker Pro 2 */ + "MTM", "MTM", /* Multitracker */ + "Extended Module:", "XM", /* Fasttracker2 */ + "MLEDMODL", "ML", /* Musicline Editor */ + "FTM", "FTM", /* Face The Music */ + "MXTX", "MXTX", /* Maxtrax*/ + "M1.0", "FUZZ", /* Fuzzac*/ + "MSNG", "TPU", /* Dirk Bialluch*/ + "YM!", "", /* stplay -- intentionally sabotaged */ + "ST1.2 ModuleINFO", "", /* Startrekker AM .NT -- intentionally sabotaged */ + "AudioSculpture10", "", /* Audiosculpture .AS -- intentionally sabotaged */ + NULL, NULL +}; + +static const char *offset_0024_patterns[] = { + /* ID: Prefix: Desc: */ + "UNCLEART", "DL", /* Dave Lowe WT */ + "DAVELOWE", "DL_deli", /* Dave Lowe Deli */ + "J.FLOGEL", "JMF", /* Janko Mrsic-Flogel */ + "BEATHOVEN", "BSS", /* BSS */ + "FREDGRAY", "GRAY", /* Fred Gray */ + "H.DAVIES", "HD", /* Howie Davies */ + "RIFFRAFF", "RIFF", /* Riff Raff */ + "!SOPROL!", "SPL", /* Soprol */ + "F.PLAYER", "FP", /* F.Player */ + "S.PHIPPS", "CORE", /* Core Design */ + "DAGLISH!", "BDS", /* Benn Daglish */ + NULL, NULL +}; + + +/* check for 'pattern' in 'buf'. + the 'pattern' must lie inside range [0, maxlen) in the buffer. + returns true if pattern is at buf[offset], otherwrise false + */ +static int patterntest(const unsigned char *buf, const char *pattern, + int offset, int bytes, int maxlen) +{ + if ((offset + bytes) <= maxlen) + return (memcmp(buf + offset, pattern, bytes) == 0) ? 1 : 0; + return 0; +} + + +static int tronictest(unsigned char *buf, size_t bufsize) +{ + size_t a = read_be_u16(&buf[0x02]) + read_be_u16(&buf[0x06]) + + read_be_u16(&buf[0x0a]) + read_be_u16(&buf[0x0e]) + 0x10; + + if (((a + 2) >= bufsize) || (a & 1)) + return 0; /* size & btst #0, d1; */ + + a = read_be_u16(&buf[a]) + a; + if (((a + 8) >= bufsize) || (a & 1)) + return 0; /*size & btst #0,d1 */ + + if (read_be_u32(&buf[a + 4]) != 0x5800b0) + return 0; + + amifiledebug("tronic recognized\n"); + + return 1; +} + +static int tfmxtest(unsigned char *buf, size_t bufsize, char *pre) +{ + if (bufsize <= 0x208) + return 0; + + if (strncmp((char *) buf, "TFHD", 4) == 0) { + if (buf[0x8] == 0x01) { + strcpy(pre, "TFHD1.5"); /* One File TFMX format by Alexis NASR */ + return 1; + } else if (buf[0x8] == 0x02) { + strcpy(pre, "TFHDPro"); + return 1; + } else if (buf[0x8] == 0x03) { + strcpy(pre, "TFHD7V"); + return 1; + } + } + + if (strncasecmp((char *) buf, "TFMX", 4) == 0) { + if (strncmp((char *) &buf[4], "-SONG", 5) == 0 || + strncmp((char *) &buf[4], "_SONG ", 6) == 0 || + strncasecmp((char *) &buf[4], "SONG", 4) == 0 || + buf[4] == 0x20) { + strcpy(pre, "MDAT"); /*default TFMX: TFMX Pro */ + + if (strncmp((char *) &buf[10], "by", 2) == 0 || + strncmp((char *) &buf[16], " ", 2) == 0 || + strncmp((char *) &buf[16], "(Empty)", 7) == 0 || + /* Lethal Zone */ + (buf[16] == 0x30 && buf[17] == 0x3d) || + (buf[4] == 0x20)){ + + if (read_be_u32(&buf[464]) == 0x00000000) { + uint16_t x = read_be_u16(&buf[14]); + if ((x != 0x0e60) || /* z-out title */ + (x == 0x0860 && bufsize > 4645 && read_be_u16(&buf[4644]) != 0x090c) || /* metal law */ + (x == 0x0b20 && bufsize > 5121 && read_be_u16(&buf[5120]) != 0x8c26) || /* bug bomber */ + (x == 0x0920 && bufsize > 3977 && read_be_u16(&buf[3876]) != 0x9305)) { /* metal preview */ + strcpy(pre, "TFMX1.5"); /*TFMX 1.0 - 1.6 */ + } + } + return 1; + + } else if (((buf[0x0e] == 0x08 && buf[0x0f] == 0xb0) && /* BMWi */ + (buf[0x140] == 0x00 && buf[0x141] == 0x0b) && /*End tackstep 1st subsong */ + (buf[0x1d2] == 0x02 && buf[0x1d3] == 0x00) && /*Trackstep datas */ + (buf[0x200] == 0xff && buf[0x201] == 0x00 && /*First effect */ + buf[0x202] == 0x00 && buf[0x203] == 0x00 && + buf[0x204] == 0x01 && buf[0x205] == 0xf4 && + buf[0x206] == 0xff && buf[0x207] == 0x00)) || + ((buf[0x0e] == 0x0A && buf[0x0f] == 0xb0) && /* B.C Kid */ + (buf[0x140] == 0x00 && buf[0x141] == 0x15) && /*End tackstep 1st subsong */ + (buf[0x1d2] == 0x02 && buf[0x1d3] == 0x00) && /*Trackstep datas */ + (buf[0x200] == 0xef && buf[0x201] == 0xfe && /*First effect */ + buf[0x202] == 0x00 && buf[0x203] == 0x03 && + buf[0x204] == 0x00 && buf[0x205] == 0x0d && + buf[0x206] == 0x00 && buf[0x207] == 0x00))) { + strcpy(pre, "TFMX7V"); /* "special cases TFMX 7V */ + return 1; + + } else { + int e, i, s, t; + + /* Trackstep datas offset */ + s = read_be_u32(&buf[0x1d0]); + if (s == 0x00000000) { + /* unpacked */ + s = 0x00000800; + } + + for (i = 0; i < 0x3d; i += 2) { + if (read_be_u16(&buf[0x140 + i]) != 0x0000) { /* subsong */ + /* Start of subsongs Trackstep data :) */ + t = read_be_u16(&buf[0x100 + i]) * 16 + s; + /* End of subsongs Trackstep data :) */ + e = read_be_u16(&buf[0x140 + i]) * 16 + s; + if (e < bufsize) { + for (; t < e && (t + 6) < bufsize; t += 2) { + if (read_be_u16(&buf[t]) == 0xeffe && + read_be_u32(&buf[t + 2]) == 0x0003ff00 && + buf[t + 6] == 0x00) { + strcpy(pre, "TFMX7V"); /*TFMX 7V */ + return 1; + } + } + } + } + } + } + } + } + return 0; +} + +/* Calculate Module length: Just need at max 1084 */ +/* data in buf for a */ +/* succesful calculation */ +/* returns: */ +/* -1 for no mod */ +/* 1 for a mod with good length */ +static size_t modlentest(unsigned char *buf, size_t bufsize, size_t filesize, + int header) +{ + int i; + int no_of_instr; + int smpl = 0; + int plist; + int maxpattern = 0; + + if (header > bufsize) + return -1; /* no mod */ + + if (header == S15_HEADER_LENGTH) { + no_of_instr = 15; + plist = header - 128; + } else if (header == S31_HEADER_LENGTH) { + no_of_instr = 31; + plist = header - 4 - 128; + } else { + return -1; + } + + for (i = 0; i < 128; i++) { + if (buf[plist + i] > maxpattern) + maxpattern = buf[plist + i]; + } + + if (maxpattern > 100) + return -1; + + for (i = 0; i < no_of_instr; i++) + smpl += 2 * read_be_u16(&buf[42 + i * 30]); /* add sample length in bytes*/ + + return header + (maxpattern + 1) * 1024 + smpl; +} + + +static void modparsing(unsigned char *buf, size_t bufsize, size_t header, int max_pattern, int pfx[], int pfxarg[]) +{ + int offset; + int i, j, fx; + unsigned char fxarg; + + for (i = 0; i < max_pattern; i++) { + for (j = 0; j < 256; j++) { + offset = header + i * 1024 + j * 4; + + if ((offset + 4) > bufsize) + return; + + fx = buf[offset + 2] & 0x0f; + fxarg = buf[offset + 3]; + + if (fx == 0) { + if (fxarg != 0 ) + pfx[fx] += 1; + pfxarg[fx] = (pfxarg[fx] > fxarg) ? pfxarg[fx] : fxarg; + + } else if (1 <= fx && fx <= 13) { + pfx[fx] +=1; + pfxarg[fx] = (pfxarg[fx] > fxarg) ? pfxarg[fx] : fxarg; + + } else if (fx == 14) { + pfx[((fxarg >> 4) & 0x0f) + 16] +=1; + + } else if (fx == 15) { + if (fxarg > 0x1f) + pfx[14] +=1; + else + pfx[15] +=1; + pfxarg[15] = (pfxarg[15] > fxarg) ? pfxarg[15] : fxarg; + } + } + } + +} + + +static int mod32check(unsigned char *buf, size_t bufsize, size_t realfilesize, + const char *path, int verbose) +{ + /* mod patterns at file offset 0x438 */ + char *mod_patterns[] = { "M.K.", ".M.K", NULL}; + /* startrekker patterns at file offset 0x438 */ + char *startrekker_patterns[] = { "FLT4", "FLT8", "EXO4", "EXO8", NULL}; + + int max_pattern = 0; + int i, j, t, ret; + int pfx[32]; + int pfxarg[32]; + + /* instrument var */ + int vol, slen, srep, sreplen; + + int has_slen_sreplen_zero = 0; /* sreplen empty of non looping instrument */ + int no_slen_sreplen_zero = 0; /* sreplen */ + + int has_slen_sreplen_one = 0; + int no_slen_sreplen_one = 0; + + int no_slen_has_volume = 0; + int finetune_used = 0; + + size_t calculated_size; + + /* returns: 0 for undefined */ + /* 1 for a Soundtracker2.5/Noisetracker 1.0 */ + /* 2 for a Noisetracker 1.2 */ + /* 3 for a Noisetracker 2.0 */ + /* 4 for a Startrekker 4ch */ + /* 5 for a Startrekker 8ch */ + /* 6 for Audiosculpture 4 ch/fm */ + /* 7 for Audiosculpture 8 ch/fm */ + /* 8 for a Protracker */ + /* 9 for a Fasttracker */ + /* 10 for a Noisetracker (M&K!) */ + /* 11 for a PTK Compatible */ + /* 12 for a Soundtracker 31instr. with repl in bytes */ + + /* Special cases first */ + if (patterntest(buf, "M&K!", (S31_HEADER_LENGTH - 4), 4, bufsize)) + return MOD_NOISETRACKER; /* Noisetracker (M&K!) */ + + if (patterntest(buf, "M!K!", (S31_HEADER_LENGTH - 4), 4, bufsize)) + return MOD_PROTRACKER; /* Protracker (100 patterns) */ + + if (patterntest(buf, "N.T.", (S31_HEADER_LENGTH - 4), 4, bufsize)) + return MOD_NOISETRACKER20; /* Noisetracker2.x */ + + for (i = 0; startrekker_patterns[i]; i++) { + if (patterntest(buf, startrekker_patterns[i], (S31_HEADER_LENGTH - 4), 4, bufsize)) { + t = 0; + for (j = 0; j < 30 * 0x1e; j = j + 0x1e) { + if (buf[0x2a + j] == 0 && buf[0x2b + j] == 0 && buf[0x2d + j] != 0) { + t = t + 1; /* no of AM instr. */ + } + } + if (t > 0) { + if (buf[0x43b] == '4'){ + ret = MOD_AUDIOSCULPTURE4; /* Startrekker 4 AM / ADSC */ + } else { + ret = MOD_AUDIOSCULPTURE8; /* Startrekker 8 AM / ADSC */ + } + } else { + if (buf[0x43b] == '4'){ + ret = MOD_STARTREKKER4; /* Startrekker 4ch */ + } else { + ret = MOD_STARTREKKER8; /* Startrekker 8ch */ + } + } + return ret; + } + } + + calculated_size = modlentest(buf, bufsize, realfilesize, S31_HEADER_LENGTH); + + if (calculated_size == -1) + return MOD_UNDEFINED; + + + for (i = 0; mod_patterns[i]; i++) { + if (patterntest(buf, mod_patterns[i], S31_HEADER_LENGTH - 4, 4, bufsize)) { + /* seems to be a generic M.K. MOD */ + /* only spam filesize message when it's a tracker module */ + + if (calculated_size != realfilesize) { + fprintf(stderr, "uade: file size is %zd but calculated size for a mod file is %zd (%s).\n", realfilesize, calculated_size, path); + } + + if (calculated_size > realfilesize) { + fprintf(stderr, "uade: file is truncated and won't get played (%s)\n", path); + return MOD_UNDEFINED; + } + + if (calculated_size < realfilesize) { + fprintf(stderr, "uade: file has trailing garbage behind the actual module data. Please fix it. (%s)\n", path); + } + + /* parse instruments */ + for (i = 0; i < 31; i++) { + vol = buf[45 + i * 30]; + slen = ((buf[42 + i * 30] << 8) + buf[43 + i * 30]) * 2; + srep = ((buf[46 + i * 30] << 8) + buf[47 + i * 30]) *2; + sreplen = ((buf[48 + i * 30] << 8) + buf[49 + i * 30]) * 2; + /* fprintf (stderr, "%d, slen: %d, %d (srep %d, sreplen %d), vol: %d\n",i, slen, srep+sreplen,srep, sreplen, vol); */ + + if (vol > 64) + return MOD_UNDEFINED; + + if (buf[44 + i * 30] != 0) { + if (buf[44+i*30] > 15) { + return MOD_UNDEFINED; + } else { + finetune_used++; + } + } + + if (slen > 0 && (srep + sreplen) > slen) { + /* Old Noisetracker /Soundtracker with repeat offset in bytes */ + return MOD_SOUNDTRACKER24; + } + + if (srep == 0) { + if (slen > 0) { + if (sreplen == 2){ + has_slen_sreplen_one++; + } + if (sreplen == 0){ + has_slen_sreplen_zero++; + } + } else { + if (sreplen > 0){ + no_slen_sreplen_one++; + } else { + no_slen_sreplen_zero++; + } + if (vol > 0) + no_slen_has_volume++; + } + } + } + + for (i = 0; i < 128; i++) { + if (buf[1080 - 130 + 2 + i] > max_pattern) + max_pattern = buf[1080 - 130 + 2 + i]; + } + + if (max_pattern > 100) { + /* pattern number can only be 0 <-> 100 for mod*/ + return MOD_UNDEFINED; + } + + memset (pfx, 0, sizeof (pfx)); + memset (pfxarg, 0, sizeof (pfxarg)); + modparsing(buf, bufsize, S31_HEADER_LENGTH-4, max_pattern, pfx, pfxarg); + + /* and now for let's see if we can spot the mod */ + + /* FX used: */ + /* DOC Soundtracker 2.x(2.5): 0,1,2(3,4) a,b,c,d,e,f */ + /* Noisetracker 1.x: 0,1,2,3,4 a,b,c,d,e,f */ + /* Noisetracker 2.x: 0,1,2,3,4 a,b,c,d,e,f */ + /* Protracker: 0,1,2,3,4,5,6,7 9,a,b,c,d,e,f +e## */ + /* PC tracker: 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f +e## */ + + for (j = 17; j <= 31; j++) { + if (pfx[j] != 0 || finetune_used >0) /* Extended fx used */ { + if (buf[0x3b7] != 0x7f && buf[0x3b7] != 0x78) { + return MOD_FASTTRACKER; /* Definetely Fasttracker*/ + } else { + return MOD_PROTRACKER; /* Protracker*/ + } + } + } + + if ((buf[0x3b7] == 0x7f) && + (has_slen_sreplen_zero <= has_slen_sreplen_one) && + (no_slen_sreplen_zero <=no_slen_sreplen_one)) + return MOD_PROTRACKER; /* Protracker */ + + if (buf[0x3b7] >0x7f) + return MOD_PTK_COMPATIBLE; /* Protracker compatible */ + + if ((buf[0x3b7] == 0) && + (has_slen_sreplen_zero > has_slen_sreplen_one) && + (no_slen_sreplen_zero > no_slen_sreplen_one)){ + if (pfx[0x10] == 0) { + /* probl. Fastracker or Protracker compatible */ + return MOD_PTK_COMPATIBLE; + } + /* FIXME: Investigate + else { + return MOD_PROTRACKER; // probl. Protracker + } */ + } + + if (pfx[0x05] != 0 || pfx[0x06] != 0 || pfx[0x07] != 0 || + pfx[0x09] != 0) { + /* Protracker compatible */ + return MOD_PTK_COMPATIBLE; + } + + if ((buf[0x3b7] >0 && buf[0x3b7] <= buf[0x3b6]) && + (has_slen_sreplen_zero <= has_slen_sreplen_one) && + (no_slen_sreplen_zero == 1) && + (no_slen_sreplen_zero <= no_slen_sreplen_one)) + return MOD_NOISETRACKER12; // Noisetracker 1.2 + + if ((buf[0x3b7] <0x80) && + (has_slen_sreplen_zero <= has_slen_sreplen_one) && + (no_slen_sreplen_zero <=no_slen_sreplen_one)) + return MOD_NOISETRACKER20; // Noisetracker 2.x + + if ((buf[0x3b7] <0x80) && + (pfx[0x0e] ==0) && + (has_slen_sreplen_zero <= has_slen_sreplen_one) && + (no_slen_sreplen_zero >=no_slen_sreplen_one)) + return MOD_SOUNDTRACKER25_NOISETRACKER10; // Noisetracker 1.x + + return MOD_PTK_COMPATIBLE; // Protracker compatible + } + } + + return MOD_UNDEFINED; +} + + +static int mod15check(unsigned char *buf, size_t bufsize, size_t realfilesize, + const char *path) +/* pattern parsing based on Sylvain 'Asle' Chipaux' */ +/* Modinfo-V2 */ +/* */ +/* returns: 0 for an undefined mod */ +/* 1 for a DOC Soundtracker mod */ +/* 2 for a Ultimate ST mod */ +/* 3 for a Mastersoundtracker */ +/* 4 for a SoundtrackerV2.0 -V4.0 */ +{ + int i = 0, j = 0; + int slen = 0; + int srep = 0; + int sreplen = 0; + int vol = 0; + + int noof_slen_zero_sreplen_zero = 0; + int noof_slen_zero_vol_zero = 0; + int srep_bigger_slen = 0; + int srep_bigger_ffff = 0; + int st_xy = 0; + + int max_pattern = 1; + int pfx[32]; + int pfxarg[32]; + + size_t calculated_size; + + /* sanity checks */ + if (bufsize < 0x1f3) + return 0; /* file too small */ + + if (bufsize < 2648+4 || realfilesize <2648+4) /* size 1 pattern + 1x 4 bytes Instrument :) */ + return 0; + + calculated_size = modlentest(buf, bufsize, realfilesize, S15_HEADER_LENGTH); + if (calculated_size == -1) + return 0; /* modlentest failed */ + + if (calculated_size != realfilesize) { + return 0 ; + } + + if (calculated_size > realfilesize) { + fprintf(stderr, "uade: file is truncated and won't get played (%s)\n", path); + return 0 ; + } + + + + /* check for 15 instruments */ + if (buf[0x1d6] != 0x00 && buf[0x1d6] < 0x81 && buf[0x1f3] !=1) { + for (i = 0; i < 128; i++) { /* pattern list table: 128 posbl. entries */ + max_pattern=(buf[600 - 130 + 2 + i] > max_pattern) ? buf[600 - 130 + 2 + i] : max_pattern; + } + if (max_pattern > 63) + return 0; /* pattern number can only be 0 <-> 63 for mod15 */ + } else { + return 0; + } + + /* parse instruments */ + for (i = 0; i < 15; i++) { + vol = buf[45 + i * 30]; + slen = ((buf[42 + i * 30] << 8) + buf[43 + i * 30]) * 2; + srep = ((buf[46 + i * 30] << 8) + buf[47 + i * 30]); + sreplen = ((buf[48 + i * 30] << 8) + buf[49 + i * 30]) * 2; + /* fprintf (stderr, "%d, slen: %d, %d (srep %d, sreplen %d), vol: %d\n",i, slen, srep+sreplen,srep, sreplen, vol); */ + + if (vol > 64 && buf[44+i*30] != 0) return 0; /* vol and finetune */ + + if (slen == 0) { + + if (vol == 0) + noof_slen_zero_vol_zero++; + + if (sreplen == 0 ) + noof_slen_zero_sreplen_zero++; + + } else { + if ((srep+sreplen) > slen) + srep_bigger_slen++; + } + + /* slen < 9999 */ + slen = (buf[42 + i * 30] << 8) + buf[43 + i * 30]; + if (slen <= 9999) { + /* repeat offset + repeat size*2 < word size */ + srep = ((buf[48 + i * 30] << 8) + buf[49 + i * 30]) * 2 + + ((buf[46 + i * 30] << 8) + buf[47 + i * 30]); + if (srep > 0xffff) srep_bigger_ffff++; + } + + if (buf[25+i*30] ==':' && buf [22+i*30] == '-' && + ((buf[20+i*30] =='S' && buf [21+i*30] == 'T') || + (buf[20+i*30] =='s' && buf [21+i*30] == 't'))) st_xy++; + } + + /* parse pattern data -> fill pfx[] with number of times fx being used*/ + memset (pfx, 0, sizeof (pfx)); + memset (pfxarg, 0, sizeof (pfxarg)); + + modparsing(buf, bufsize, S15_HEADER_LENGTH, max_pattern, pfx, pfxarg); + + /* and now for let's see if we can spot the mod */ + +/* FX used: */ +/* Ultimate ST: 0,1,2 */ +/* MasterSoundtracker: 0,1,2, c, e,f */ +/* DOC-Soundtracker V2.2: 0,1,2,a,b,c,d,e,f */ +/* Soundtracker I-VI 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f*/ + + + /* Check for fx used between 0x3 <-> 0xb for some weird ST II-IV mods */ + for (j = 0x5; j < 0xa; j++) { + if (pfx[j] != 0) + return 4; /* ST II-IV */ + } + + for (j = 0x0c; j < 0x11; j++) { + if (pfx[j] != 0) { + + if (pfx[0x0d] != 0 && pfxarg[0x0d] != 0) + return 4; /* ST II-IV */ + + if (pfx[0x0b] != 0 || pfx[0x0d] != 0 || pfx[0x0a]!= 0 ) { + return 1; /* DOC ST */ + } else { + if (pfxarg[1] > 0xe || pfxarg[2] > 0xe) + return 1; /* DOC ST */ + + return 3; /* Master ST */ + } + } + } + + /* pitchbend out of range ? */ + if ((pfxarg[1] > 0 && pfxarg[1] <0x1f) || + (pfxarg[2] > 0 && pfxarg [2] <0x1f) || + pfx [0] >2) return 1; // ST style Arpeggio, Pitchbends ??? + + if (pfx[1] > 0 || pfx[2] > 0) + return 2; /* nope UST like fx */ + + /* the rest of the files has no fx. so check instruments */ + if (st_xy!=0 && noof_slen_zero_vol_zero == 0 && + noof_slen_zero_sreplen_zero == 0 && buf[0x1d7] == 120) { + return 3; + } + + /* no fx, no loops... let's simply guess :)*/ + if (srep_bigger_slen == 0 && srep_bigger_ffff == 0 && + ((st_xy != 0 && buf[0x1d7] != 120 ) || st_xy==0)) + return 2; + + return 3; /* anything is played as normal soundtracker */ +} + +/* Reject WAV files so that uadefs doesn't cause bad behaviour */ +static int is_wav_file(unsigned char *buf, size_t size) +{ + if (size < WAV_HEADER_LEN) + return 0; + + if (memcmp(buf, "RIFF", 4)) + return 0; + + if (memcmp(buf + 8, "WAVEfmt ", 8)) + return 0; + + if (memcmp(buf + 36, "data", 4)) + return 0; + + return 1; +} + +void uade_filemagic(unsigned char *buf, size_t bufsize, char *pre, + size_t realfilesize, const char *path, int verbose) +{ + /* char filemagic(): + detects formats like e.g.: tfmx1.5, hip, hipc, fc13, fc1.4 + - tfmx 1.5 checking based on both tfmx DT and tfmxplay by jhp, + and the EP by Don Adan/WT. + - tfmx 7v checking based on info by don adan, the amore file + ripping description and jhp's desc of the tfmx format. + - other checks based on e.g. various player sources from Exotica + or by checking bytes with a hexeditor + by far not complete... + + NOTE: Those Magic ID checks are quite lame compared to the checks the + amiga replayer do... well, after all we are not ripping. so they + have to do at the moment :) + */ + + int i, modtype, t; + + struct modtype { + int e; + char *str; + }; + + struct modtype mod32types[] = { + {.e = MOD_SOUNDTRACKER25_NOISETRACKER10, .str = "MOD_NTK"}, + {.e = MOD_NOISETRACKER12, .str = "MOD_NTK1"}, + {.e = MOD_NOISETRACKER20, .str = "MOD_NTK2"}, + {.e = MOD_STARTREKKER4, .str = "MOD_FLT4"}, + {.e = MOD_STARTREKKER8, .str = "MOD_FLT8"}, + {.e = MOD_AUDIOSCULPTURE4, .str = "MOD_ADSC4"}, + {.e = MOD_AUDIOSCULPTURE8, .str = "MOD_ADSC8"}, + {.e = MOD_PROTRACKER, .str = "MOD"}, + {.e = MOD_FASTTRACKER, .str = "MOD_COMP"}, + {.e = MOD_NOISETRACKER, .str = "MOD_NTKAMP"}, + {.e = MOD_PTK_COMPATIBLE, .str = "MOD_COMP"}, + {.e = MOD_SOUNDTRACKER24, .str = "MOD_DOC"}, + {.str = NULL} + }; + + struct modtype mod15types[] = { + {.e = 1, .str = "MOD15"}, + {.e = 2, .str = "MOD15_UST"}, + {.e = 3, .str = "MOD15_MST"}, + {.e = 4, .str = "MOD15_ST-IV"}, + {.str = NULL} + }; + + /* Mark format unknown by default */ + pre[0] = 0; + + if (is_wav_file(buf, bufsize)) { + strcpy(pre, "reject"); + return; + } + + modtype = mod32check(buf, bufsize, realfilesize, path, verbose); + if (modtype != MOD_UNDEFINED) { + for (t = 0; mod32types[t].str != NULL; t++) { + if (modtype == mod32types[t].e) { + strcpy(pre, mod32types[t].str); + return; + } + } + } + + /* 0x438 == S31_HEADER_LENGTH - 4 */ + if (((buf[0x438] >= '1' && buf[0x438] <= '3') + && (buf[0x439] >= '0' && buf[0x439] <= '9') && buf[0x43a] == 'C' + && buf[0x43b] == 'H') || ((buf[0x438] >= '2' && buf[0x438] <= '8') + && buf[0x439] == 'C' && buf[0x43a] == 'H' + && buf[0x43b] == 'N') + || (buf[0x438] == 'T' && buf[0x439] == 'D' && buf[0x43a] == 'Z') + || (buf[0x438] == 'O' && buf[0x439] == 'C' && buf[0x43a] == 'T' + && buf[0x43b] == 'A') || (buf[0x438] == 'C' && buf[0x439] == 'D' + && buf[0x43a] == '8' + && buf[0x43b] == '1')) { + strcpy(pre, "MOD_PC"); /*Multichannel Tracker */ + + } else if (buf[0x2c] == 'S' && buf[0x2d] == 'C' && buf[0x2e] == 'R' + && buf[0x2f] == 'M') { + strcpy(pre, "S3M"); /*Scream Tracker */ + + } else if ((buf[0] == 0x60 && buf[2] == 0x60 && buf[4] == 0x48 + && buf[5] == 0xe7) || (buf[0] == 0x60 && buf[2] == 0x60 + && buf[4] == 0x41 && buf[5] == 0xfa) + || (buf[0] == 0x60 && buf[1] == 0x00 && buf[4] == 0x60 + && buf[5] == 0x00 && buf[8] == 0x48 && buf[9] == 0xe7) + || (buf[0] == 0x60 && buf[1] == 0x00 && buf[4] == 0x60 + && buf[5] == 0x00 && buf[8] == 0x60 && buf[9] == 0x00 + && buf[12] == 0x60 && buf[13] == 0x00 && buf[16] == 0x48 + && buf[17] == 0xe7)) { + strcpy(pre, "SOG"); /* Hippel */ + + } else if (buf[0x348] == '.' && buf[0x349] == 'Z' && buf[0x34A] == 'A' + && buf[0x34B] == 'D' && buf[0x34c] == 'S' && buf[0x34d] == '8' + && buf[0x34e] == '9' && buf[0x34f] == '.') { + strcpy(pre, "MKII"); /* Mark II */ + + } else if (read_be_u16(&buf[0x00]) == 0x2b7c && + read_be_u16(&buf[0x08]) == 0x2b7c && + read_be_u16(&buf[0x10]) == 0x2b7c && + read_be_u16(&buf[0x18]) == 0x2b7c && + read_be_u32(&buf[0x20]) == 0x303c00ff && + read_be_u32(&buf[0x24]) == 0x32004eb9 && + read_be_u16(&buf[0x2c]) == 0x4e75) { + strcpy(pre, "JPO"); /* Steve Turner*/ + + } else if (((buf[0] == 0x08 && buf[1] == 0xf9 && buf[2] == 0x00 + && buf[3] == 0x01) && (buf[4] == 0x00 && buf[5] == 0xbb + && buf[6] == 0x41 && buf[7] == 0xfa) + && ((buf[0x25c] == 0x4e && buf[0x25d] == 0x75) + || (buf[0x25c] == 0x4e && buf[0x25d] == 0xf9))) + || ((buf[0] == 0x41 && buf[1] == 0xfa) + && (buf[4] == 0xd1 && buf[5] == 0xe8) + && (((buf[0x230] == 0x4e && buf[0x231] == 0x75) + || (buf[0x230] == 0x4e && buf[0x231] == 0xf9)) + || ((buf[0x29c] == 0x4e && buf[0x29d] == 0x75) + || (buf[0x29c] == 0x4e && buf[0x29d] == 0xf9)) + ))) { + strcpy(pre, "SID1"); /* SidMon1 */ + + } else if (buf[0] == 0x4e && buf[1] == 0xfa && + buf[4] == 0x4e && buf[5] == 0xfa && + buf[8] == 0x4e && buf[9] == 0xfa && + buf[2] == 0x00 && buf[6] == 0x06 && buf[10] == 0x07) { + if (buf[3] == 0x2a && buf[7] == 0xfc && buf[11] == 0x7c) { + strcpy(pre, "SA_old"); + } else if (buf[3] == 0x1a && buf[7] == 0xc6 && buf[11] == 0x3a) { + strcpy(pre, "SA"); + } + + } else if (buf[0] == 0x4e && buf[1] == 0xfa && + buf[4] == 0x4e && buf[5] == 0xfa && + buf[8] == 0x4e && buf[9] == 0xfa && + buf[0xc] == 0x4e && buf[0xd] == 0xfa) { + for (i = 0x10; i < 256; i = i + 2) { + if (buf[i + 0] == 0x4e && buf[i + 1] == 0x75 && buf[i + 2] == 0x47 + && buf[i + 3] == 0xfa && buf[i + 12] == 0x4e && buf[i + 13] == 0x75) { + strcpy(pre, "FRED"); /* FRED */ + break; + } + } + + } else if (buf[0] == 0x60 && buf[1] == 0x00 && + buf[4] == 0x60 && buf[5] == 0x00 && + buf[8] == 0x60 && buf[9] == 0x00 && + buf[12] == 0x48 && buf[13] == 0xe7) { + strcpy(pre, "MA"); /*Music Assembler */ + + } else if (buf[0] == 0x00 && buf[1] == 0x00 && + buf[2] == 0x00 && buf[3] == 0x28 && + (buf[7] >= 0x34 && buf[7] <= 0x64) && + buf[0x20] == 0x21 && (buf[0x21] == 0x54 || buf[0x21] == 0x44) + && buf[0x22] == 0xff && buf[0x23] == 0xff) { + strcpy(pre, "SA-P"); /*SonicArranger Packed */ + + + } else if (buf[0] == 0x4e && buf[1] == 0xfa && + buf[4] == 0x4e && buf[5] == 0xfa && + buf[8] == 0x4e && buf[9] == 0xfa) { + t = ((buf[2] * 256) + buf[3]); + if (t < bufsize - 9) { + if (buf[2 + t] == 0x4b && buf[3 + t] == 0xfa && + buf[6 + t] == 0x08 && buf[7 + t] == 0xad && buf[8 + t] == 0x00 + && buf[9 + t] == 0x00) { + strcpy(pre, "MON"); /*M.O.N */ + } + } + + } else if (buf[0] == 0x02 && buf[1] == 0x39 && + buf[2] == 0x00 && buf[3] == 0x01 && + buf[8] == 0x66 && buf[9] == 0x02 && + buf[10] == 0x4e && buf[11] == 0x75 && + buf[12] == 0x78 && buf[13] == 0x00 && + buf[14] == 0x18 && buf[15] == 0x39) { + strcpy(pre, "MON_old"); /*M.O.N_old */ + + } else if (buf[0] == 0x48 && buf[1] == 0xe7 && buf[2] == 0xf1 + && buf[3] == 0xfe && buf[4] == 0x61 && buf[5] == 0x00) { + t = ((buf[6] * 256) + buf[7]); + if (t < (bufsize - 17)) { + for (i = 0; i < 10; i = i + 2) { + if (buf[6 + t + i] == 0x47 && buf[7 + t + i] == 0xfa) { + strcpy(pre, "DW"); /*Whittaker Type1... FIXME: incomplete */ + } + } + } + + } else if (buf[0] == 0x13 && buf[1] == 0xfc && + buf[2] == 0x00 && buf[3] == 0x40 && + buf[8] == 0x4e && buf[9] == 0x71 && + buf[10] == 0x04 && buf[11] == 0x39 && + buf[12] == 0x00 && buf[13] == 0x01 && + buf[18] == 0x66 && buf[19] == 0xf4 && + buf[20] == 0x4e && buf[21] == 0x75 && + buf[22] == 0x48 && buf[23] == 0xe7 && + buf[24] == 0xff && buf[25] == 0xfe) { + strcpy(pre, "EX"); /*Fashion Tracker */ + +/* Magic ID */ + } else if (buf[0x3a] == 'S' && buf[0x3b] == 'I' && buf[0x3c] == 'D' && + buf[0x3d] == 'M' && buf[0x3e] == 'O' && buf[0x3f] == 'N' && + buf[0x40] == ' ' && buf[0x41] == 'I' && buf[0x42] == 'I') { + strcpy(pre, "SID2"); /* SidMon II */ + + } else if (buf[0x28] == 'R' && buf[0x29] == 'O' && buf[0x2a] == 'N' && + buf[0x2b] == '_' && buf[0x2c] == 'K' && buf[0x2d] == 'L' && + buf[0x2e] == 'A' && buf[0x2f] == 'R' && buf[0x30] == 'E' && + buf[0x31] == 'N') { + strcpy(pre, "CM"); /* Ron Klaren (CustomMade) */ + + } else if (buf[0x3e] == 'A' && buf[0x3f] == 'C' && buf[0x40] == 'T' + && buf[0x41] == 'I' && buf[0x42] == 'O' && buf[0x43] == 'N' + && buf[0x44] == 'A' && buf[0x45] == 'M') { + strcpy(pre, "AST"); /*Actionanamics */ + + } else if (buf[26] == 'V' && buf[27] == '.' && buf[28] == '2') { + strcpy(pre, "BP"); /* Soundmon V2 */ + + } else if (buf[26] == 'V' && buf[27] == '.' && buf[28] == '3') { + strcpy(pre, "BP3"); /* Soundmon V2.2 */ + + } else if (buf[60] == 'S' && buf[61] == 'O' && buf[62] == 'N' + && buf[63] == 'G') { + strcpy(pre, "SFX13"); /* Sfx 1.3-1.8 */ + + } else if (buf[124] == 'S' && buf[125] == 'O' && buf[126] == '3' + && buf[127] == '1') { + strcpy(pre, "SFX20"); /* Sfx 2.0 */ + + } else if (buf[0x1a] == 'E' && buf[0x1b] == 'X' && buf[0x1c] == 'I' + && buf[0x1d] == 'T') { + strcpy(pre, "AAM"); /*Audio Arts & Magic */ + } else if (buf[8] == 'E' && buf[9] == 'M' && buf[10] == 'O' + && buf[11] == 'D' && buf[12] == 'E' && buf[13] == 'M' + && buf[14] == 'I' && buf[15] == 'C') { + strcpy(pre, "EMOD"); /* EMOD */ + + /* generic ID Check at offset 0x24 */ + + } else if (chk_id_offset(buf, bufsize, offset_0024_patterns, 0x24, pre)) { + + /* HIP7 ID Check at offset 0x04 */ + } else if (patterntest(buf, " **** Player by Jochen Hippel 1990 **** ", + 0x04, 40, bufsize)) { + strcpy(pre, "S7G"); /* HIP7 */ + + /* Magic ID at Offset 0x00 */ + } else if (buf[0] == 'M' && buf[1] == 'M' && buf[2] == 'D') { + if (buf[0x3] >= '0' && buf[0x3] < '3') { + /*move.l mmd_songinfo(a0),a1 */ + int s = (buf[8] << 24) + (buf[9] << 16) + (buf[0xa] << 8) + buf[0xb]; + if (((int) buf[s + 767]) & (1 << 6)) { /* btst #6, msng_flags(a1); */ + strcpy(pre, "OCTAMED"); + /*OCTAMED*/} else { + strcpy(pre, "MED"); + /*MED*/} + } else if (buf[0x3] != 'C') { + strcpy(pre, "MMD3"); /* mmd3 and above */ + } + + /* all TFMX format tests here */ + } else if (tfmxtest(buf, bufsize, pre)) { + /* is TFMX, nothing to do here ('pre' set in tfmxtest() */ + + } else if (buf[0] == 'T' && buf[1] == 'H' && buf[2] == 'X') { + if ((buf[3] == 0x00) || (buf[3] == 0x01)) { + strcpy(pre, "AHX"); /* AHX */ + } + + } else if (buf[1] == 'M' && buf[2] == 'U' && buf[3] == 'G' + && buf[4] == 'I' && buf[5] == 'C' && buf[6] == 'I' + && buf[7] == 'A' && buf[8] == 'N') { + if (buf[9] == '2') { + strcpy(pre, "MUG2"); /* Digimugi2 */ + } else { + strcpy(pre, "MUG"); /* Digimugi */ + } + + } else if (buf[0] == 'L' && buf[1] == 'M' && buf[2] == 'E' && buf[3] == 0x00) { + strcpy(pre, "LME"); /* LegLess */ + + } else if (buf[0] == 'P' && buf[1] == 'S' && buf[2] == 'A' && buf[3] == 0x00) { + strcpy(pre, "PSA"); /* PSA */ + + } else if ((buf[0] == 'S' && buf[1] == 'y' && buf[2] == 'n' && buf[3] == 't' + && buf[4] == 'h' && buf[6] == '.' && buf[8] == 0x00) + && (buf[5] > '1' && buf[5] < '4')) { + strcpy(pre, "SYN"); /* Synthesis */ + + } else if (buf[0xbc6] == '.' && buf[0xbc7] == 'F' && buf[0xbc8] == 'N' + && buf[0xbc9] == 'L') { + strcpy(pre, "DM2"); /* Delta 2.0 */ + + } else if (buf[0] == 'R' && buf[1] == 'J' && buf[2] == 'P') { + + if (buf[4] == 'S' && buf[5] == 'M' && buf[6] == 'O' && buf[7] == 'D') { + strcpy(pre, "RJP"); /* Vectordean (Richard Joseph Player) */ + } else { + strcpy(pre, ""); /* but don't play .ins files */ + } + } else if (buf[0] == 'F' && buf[1] == 'O' && buf[2] == 'R' && buf[3] == 'M') { + if (buf[8] == 'S' && buf[9] == 'M' && buf[10] == 'U' && buf[11] == 'S') { + strcpy(pre, "SMUS"); /* Sonix */ + } + // } else if (buf[0x00] == 0x00 && buf[0x01] == 0xfe && + // buf[0x30] == 0x00 && buf[0x31] ==0x00 && buf[0x32] ==0x01 && buf[0x33] ==0x40 && + // realfilesize > 332 ){ + // } + // strcpy (pre, "SMUS"); /* Tiny Sonix*/ + + } else if (tronictest(buf, bufsize)) { + strcpy(pre, "TRONIC"); /* Tronic */ + + /* generic ID Check at offset 0x00 */ + } else if (chk_id_offset(buf, bufsize, offset_0000_patterns, 0x00, pre)) { + + /*magic ids of some modpackers */ + } else if (buf[0x438] == 'P' && buf[0x439] == 'W' && buf[0x43a] == 'R' + && buf[0x43b] == 0x2e) { + strcpy(pre, "PPK"); /*Polkapacker */ + + } else if (buf[0x100] == 'S' && buf[0x101] == 'K' && buf[0x102] == 'Y' + && buf[0x103] == 'T') { + strcpy(pre, "SKT"); /*Skytpacker */ + + } else if ((buf[0x5b8] == 'I' && buf[0x5b9] == 'T' && buf[0x5ba] == '1' + && buf[0x5bb] == '0') || (buf[0x5b8] == 'M' && buf[0x5b9] == 'T' + && buf[0x5ba] == 'N' + && buf[0x5bb] == 0x00)) { + strcpy(pre, "ICE"); /*Ice/Soundtracker 2.6 */ + + } else if (buf[0x3b8] == 'K' && buf[0x3b9] == 'R' && buf[0x3ba] == 'I' + && buf[0x3bb] == 'S') { + strcpy(pre, "KRIS"); /*Kristracker */ + + } else if (buf[0] == 'X' && buf[1] == 'P' && buf[2] == 'K' && buf[3] == 'F'&& + read_be_u32(&buf[4]) + 8 == realfilesize && + buf[8] == 'S' && buf[9] == 'Q' && buf[10] == 'S' && buf[11] == 'H') { + fprintf(stderr, "uade: The file is SQSH packed. Please depack first.\n"); + strcpy(pre, "packed"); + + } else if ((modtype = mod15check(buf, bufsize, realfilesize, path)) != 0) { + for (t = 0; mod15types[t].str != NULL; t++) { + if (modtype == mod15types[t].e) { + strcpy(pre, mod15types[t].str); + return; + } + } + + /* Custom file check */ + } else if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x03 + && buf[3] == 0xf3) { + /*CUSTOM*/ i = (buf[0x0b] * 4) + 0x1c; /* beginning of first chunk */ + + if (i < bufsize - 0x42) { + + t = 0; + /* unfort. we can't always assume: moveq #-1,d0 rts before "delirium" */ + /* search 0x40 bytes from here, (enough?) */ + while ((buf[i + t + 0] != 'D' && buf[i + t + 1] != 'E' + && buf[i + t + 2] != 'L' && buf[i + t + 3] != 'I') + && (t < 0x40)) { + t++; + } + + if (t < 0x40) { + /* longword after Delirium is rel. offset from first chunk + where "hopefully" the delitags are */ + int s = (buf[i + t + 10] * 256) + buf[i + t + 11] + i; /* 64K */ + if (s < bufsize - 0x33) { + for (i = 0; i < 0x30; i = i + 4) { + if (buf[i + s + 0] == 0x80 && buf[i + s + 1] == 0x00 && + buf[i + s + 2] == 0x44 && buf[i + s + 3] == 0x55) { + strcpy(pre, "CUST"); /* CUSTOM */ + break; + } + } + } + } + } + + } else if (buf[12] == 0x00) { + int s = (buf[12] * 256 + buf[13] + 1) * 14; + if (s < (bufsize - 91)) { + if (buf[80 + s] == 'p' && buf[81 + s] == 'a' && buf[82 + s] == 't' + && buf[83 + s] == 't' && buf[87 + s] == 32 && buf[88 + s] == 'p' + && buf[89 + s] == 'a' && buf[90 + s] == 't' && buf[91 + s] == 't') { + strcpy(pre, "PUMA"); /* Pumatracker */ + } + } + } +} + + +/* We are currently stupid and check only for a few magic IDs at the offsets + * chk_id_offset returns 1 on success and sets the right prefix/extension + * in pre + * TODO: more and less easy check for the rest of the 52 trackerclones + */ +static int chk_id_offset(unsigned char *buf, int bufsize, + const char *patterns[], int offset, char *pre) +{ + int i; + for (i = 0; patterns[i]; i = i + 2) { + if (patterntest(buf, patterns[i], offset, strlen(patterns[i]), bufsize)) { + /* match found */ + strcpy(pre, patterns[i + 1]); + return 1; + } + } + return 0; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/amifilemagic.h b/plugins/uade2/uade-2.13/src/frontends/common/amifilemagic.h new file mode 100644 index 00000000..45e5cd48 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/amifilemagic.h @@ -0,0 +1,9 @@ +#ifndef _UADE_AMIFILEMAGIC_H_ +#define _UADE_AMIFILEMAGIC_H_ + +#include <stdio.h> + +void uade_filemagic(unsigned char *buf, size_t bufsize, char *pre, + size_t realfilesize, const char *path, int verbose); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/eagleplayer.c b/plugins/uade2/uade-2.13/src/frontends/common/eagleplayer.c new file mode 100644 index 00000000..7c8dfce2 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/eagleplayer.c @@ -0,0 +1,502 @@ +/* + * Loads contents of 'eagleplayer.conf'. The file formats are + * specified in doc/uade123.1. + * + * Copyright 2005-2007 Heikki Orsila <heikki.orsila@iki.fi> + * + * This source code module is dual licensed under GPL and Public Domain. + * Hence you may use _this_ module (not another code module) in any you + * want in your projects. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <stdint.h> + +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "eagleplayer.h" +#include "ossupport.h" +#include "amifilemagic.h" +#include "uadeconf.h" +#include "unixatomic.h" +#include "songdb.h" +#include "support.h" +#include "uadestate.h" + + +#define OPTION_DELIMITER "," + +#define MAX_SUFFIX_LENGTH 16 + +#define eperror(fmt, args...) do { uadeerror("Eagleplayer.conf error on line %zd: " fmt, lineno, ## args); } while (0) + + +/* Table for associating eagleplayer.conf, song.conf and uade.conf options + * together. + */ +const struct epconfattr epconf[] = { + {.s = "a500", .e = ES_A500, .o = UC_FILTER_TYPE, .c = "a500"}, + {.s = "a1200", .e = ES_A1200, .o = UC_FILTER_TYPE, .c = "a1200"}, + {.s = "always_ends", .e = ES_ALWAYS_ENDS, .o = UC_DISABLE_TIMEOUTS}, + {.s = "broken_song_end", .e = ES_BROKEN_SONG_END, .o = UC_NO_EP_END}, + {.s = "detect_format_by_content", .e = ES_CONTENT_DETECTION, .o = UC_CONTENT_DETECTION}, + {.s = "detect_format_by_name", .e = ES_NAME_DETECTION, .o = 0}, + {.s = "ignore_player_check",.e = ES_IGNORE_PLAYER_CHECK, .o = UC_IGNORE_PLAYER_CHECK}, + {.s = "led_off", .e = ES_LED_OFF, .o = UC_FORCE_LED_OFF}, + {.s = "led_on", .e = ES_LED_ON, .o = UC_FORCE_LED_ON}, + {.s = "never_ends", .e = ES_NEVER_ENDS, .o = 0}, + {.s = "no_ep_end_detect", .e = ES_BROKEN_SONG_END, .o = UC_NO_EP_END}, + {.s = "no_filter", .e = ES_NO_FILTER, .o = UC_NO_FILTER}, + {.s = "no_headphones", .e = ES_NO_HEADPHONES, .o = UC_NO_HEADPHONES}, + {.s = "no_panning", .e = ES_NO_PANNING, .o = UC_NO_PANNING}, + {.s = "no_postprocessing", .e = ES_NO_POSTPROCESSING, .o = UC_NO_POSTPROCESSING}, + {.s = "ntsc", .e = ES_NTSC, .o = UC_NTSC}, + {.s = "one_subsong", .e = ES_ONE_SUBSONG, .o = UC_ONE_SUBSONG}, + {.s = "pal", .e = ES_PAL, .o = UC_PAL}, + {.s = "reject", .e = ES_REJECT, .o = 0}, + {.s = "speed_hack", .e = ES_SPEED_HACK, .o = UC_SPEED_HACK}, + {.s = NULL} +}; + + +/* Variables for eagleplayer.conf and song.conf */ +static const struct epconfattr epconf_variables[] = { + {.s = "epopt", .t = UA_STRING, .e = ES_EP_OPTION}, + {.s = "gain", .t = UA_STRING, .e = ES_GAIN}, + {.s = "interpolator", .t = UA_STRING, .e = ES_RESAMPLER}, + {.s = "panning", .t = UA_STRING, .e = ES_PANNING}, + {.s = "player", .t = UA_STRING, .e = ES_PLAYER}, + {.s = "resampler", .t = UA_STRING, .e = ES_RESAMPLER}, + {.s = "silence_timeout", .t = UA_STRING, .e = ES_SILENCE_TIMEOUT}, + {.s = "subsong_timeout", .t = UA_STRING, .e = ES_SUBSONG_TIMEOUT}, + {.s = "subsongs", .t = UA_STRING, .e = ES_SUBSONGS}, + {.s = "timeout", .t = UA_STRING, .e = ES_TIMEOUT}, + {.s = NULL} +}; + + +static int ufcompare(const void *a, const void *b); +static struct eagleplayerstore *read_eagleplayer_conf(const char *filename); + + +static struct eagleplayer *get_eagleplayer(const char *extension, + struct eagleplayerstore *playerstore); + + +static int load_playerstore(struct uade_state *state) +{ + static int warnings = 1; + char formatsfile[PATH_MAX]; + + if (state->playerstore == NULL) { + snprintf(formatsfile, sizeof(formatsfile), + "%s/eagleplayer.conf", state->config.basedir.name); + + state->playerstore = read_eagleplayer_conf(formatsfile); + if (state->playerstore == NULL) { + if (warnings) { + fprintf(stderr, "Tried to load eagleplayer.conf from %s, but failed\n", formatsfile); + } + warnings = 0; + return 0; + } + + if (state->config.verbose) + fprintf(stderr, "Loaded eagleplayer.conf: %s\n", + formatsfile); + } + + return 1; +} + + +static struct eagleplayer *analyze_file_format(int *content, + const char *modulename, + struct uade_state *state) +{ + struct stat st; + char ext[MAX_SUFFIX_LENGTH]; + FILE *f; + struct eagleplayer *contentcandidate = NULL; + struct eagleplayer *namecandidate = NULL; + char *prefix, *postfix, *t; + size_t bufsize, bytesread; + uint8_t buf[8192]; + + *content = 0; + + if ((f = fopen(modulename, "rb")) == NULL) + return NULL; + + if (fstat(fileno(f), &st)) + uadeerror("Very weird stat error: %s (%s)\n", modulename, strerror(errno)); + + bufsize = sizeof buf; + bytesread = atomic_fread(buf, 1, bufsize, f); + fclose(f); + if (bytesread == 0) + return NULL; + memset(&buf[bytesread], 0, bufsize - bytesread); + + uade_filemagic(buf, bytesread, ext, st.st_size, modulename, state->config.verbose); + + if (strcmp(ext, "reject") == 0) + return NULL; + + if (ext[0] != 0 && state->config.verbose) + fprintf(stderr, "Content recognized: %s (%s)\n", ext, modulename); + + if (strcmp(ext, "packed") == 0) + return NULL; + + if (!load_playerstore(state)) + return NULL; + + /* First do filename detection (we'll later do content detection) */ + t = xbasename(modulename); + + if (strlcpy((char *) buf, t, sizeof buf) >= sizeof buf) + return NULL; + + t = strchr((char *) buf, '.'); + if (t == NULL) + return NULL; + + *t = 0; + prefix = (char *) buf; + + if (strlen(prefix) < MAX_SUFFIX_LENGTH) + namecandidate = get_eagleplayer(prefix, state->playerstore); + + if (namecandidate == NULL) { + /* Try postfix */ + t = xbasename(modulename); + strlcpy((char *) buf, t, sizeof buf); + postfix = strrchr((char *) buf, '.') + 1; /* postfix != NULL */ + + if (strlen(postfix) < MAX_SUFFIX_LENGTH) + namecandidate = get_eagleplayer(postfix, state->playerstore); + } + + /* If filemagic found a match, we'll use player plugins associated with + that extension */ + if (ext[0]) { + contentcandidate = get_eagleplayer(ext, state->playerstore); + if (contentcandidate != NULL) { + /* Do not recognize name detectable eagleplayers by + content */ + if (namecandidate == NULL || + (namecandidate->flags & ES_NAME_DETECTION) == 0) { + *content = 1; + return contentcandidate; + } + } else { + if (state->config.verbose) + fprintf(stderr, "%s not in eagleplayer.conf\n", ext); + } + } + + if (state->config.verbose) + fprintf(stderr, "Format detection by filename\n"); + + return namecandidate; +} + + +static void handle_attribute(struct uade_attribute **attributelist, + const struct epconfattr *attr, + char *item, size_t len, size_t lineno) +{ + struct uade_attribute *a; + char *str, *endptr; + int success = 0; + + if (item[len] != '=') { + fprintf(stderr, "Invalid song item: %s\n", item); + return; + } + str = item + len + 1; + + if ((a = calloc(1, sizeof *a)) == NULL) + eperror("No memory for song attribute.\n"); + + switch (attr->t) { + case UA_DOUBLE: + a->d = strtod(str, &endptr); + if (*endptr == 0) + success = 1; + break; + case UA_INT: + a->i = strtol(str, &endptr, 10); + if (*endptr == 0) + success = 1; + break; + case UA_STRING: + a->s = strdup(str); + if (a->s == NULL) + eperror("Out of memory allocating string option for song\n"); + success = 1; + break; + default: + fprintf(stderr, "Unknown song option: %s\n", + item); + break; + } + + if (success) { + a->type = attr->e; + a->next = *attributelist; + *attributelist = a; + } else { + fprintf(stderr, "Invalid song option: %s\n", item); + free(a); + } +} + + +int uade_song_and_player_attribute(struct uade_attribute **attributelist, + int *flags, char *item, size_t lineno) +{ + size_t i, len; + + for (i = 0; epconf[i].s != NULL; i++) { + if (strcasecmp(item, epconf[i].s) == 0) { + *flags |= epconf[i].e; + return 1; + } + } + + for (i = 0; epconf_variables[i].s != NULL; i++) { + len = strlen(epconf_variables[i].s); + if (strncasecmp(item, epconf_variables[i].s, len) != 0) + continue; + + handle_attribute(attributelist, &epconf_variables[i], + item, len, lineno); + return 1; + } + + return 0; +} + +/* Compare function for bsearch() and qsort() to sort eagleplayers with + respect to name extension. */ +static int ufcompare(const void *a, const void *b) +{ + const struct eagleplayermap *ua = a; + const struct eagleplayermap *ub = b; + + return strcasecmp(ua->extension, ub->extension); +} + +int uade_is_our_file(const char *modulename, int scanmode, + struct uade_state *state) +{ + int content; + struct eagleplayer *ep; + + ep = analyze_file_format(&content, modulename, state); + + if (!scanmode) + state->ep = ep; + + if (ep == NULL) + return 0; + + if (content) + return 1; + + if (state->config.content_detection && content == 0) + return 0; + + if ((ep->flags & ES_CONTENT_DETECTION) != 0) + return 0; + + return 1; +} + +static struct eagleplayer *get_eagleplayer(const char *extension, + struct eagleplayerstore *ps) +{ + struct eagleplayermap *uf = ps->map; + struct eagleplayermap *f; + struct eagleplayermap key = {.extension = (char *)extension }; + + f = bsearch(&key, uf, ps->nextensions, sizeof(uf[0]), ufcompare); + if (f == NULL) + return NULL; + + return f->player; +} + +/* Read eagleplayer.conf. */ +static struct eagleplayerstore *read_eagleplayer_conf(const char *filename) +{ + FILE *f; + struct eagleplayer *p; + size_t allocated; + size_t lineno = 0; + struct eagleplayerstore *ps = NULL; + size_t exti; + size_t i, j; + int epwarning; + + f = fopen(filename, "r"); + if (f == NULL) + goto error; + + ps = calloc(1, sizeof ps[0]); + if (ps == NULL) + eperror("No memory for ps."); + + allocated = 16; + if ((ps->players = malloc(allocated * sizeof(ps->players[0]))) == NULL) + eperror("No memory for eagleplayer.conf file.\n"); + + while (1) { + char **items; + size_t nitems; + + items = read_and_split_lines(&nitems, &lineno, f, UADE_WS_DELIMITERS); + if (items == NULL) + break; + + assert(nitems > 0); + + if (ps->nplayers == allocated) { + allocated *= 2; + ps->players = realloc(ps->players, allocated * sizeof(ps->players[0])); + if (ps->players == NULL) + eperror("No memory for players."); + } + + p = &ps->players[ps->nplayers]; + ps->nplayers++; + + memset(p, 0, sizeof p[0]); + + p->playername = strdup(items[0]); + if (p->playername == NULL) + uadeerror("No memory for playername.\n"); + + for (i = 1; i < nitems; i++) { + + if (strncasecmp(items[i], "prefixes=", 9) == 0) { + char prefixes[UADE_LINESIZE]; + char *prefixstart = items[i] + 9; + char *sp, *s; + size_t pos; + + assert(p->nextensions == 0 && p->extensions == NULL); + + p->nextensions = 0; + strlcpy(prefixes, prefixstart, + sizeof(prefixes)); + sp = prefixes; + while ((s = strsep(&sp, OPTION_DELIMITER)) != NULL) { + if (*s == 0) + continue; + p->nextensions++; + } + + p->extensions = + malloc((p->nextensions + + 1) * sizeof(p->extensions[0])); + if (p->extensions == NULL) + eperror("No memory for extensions."); + + pos = 0; + sp = prefixstart; + while ((s = strsep(&sp, OPTION_DELIMITER)) != NULL) { + if (*s == 0) + continue; + + p->extensions[pos] = strdup(s); + if (s == NULL) + eperror("No memory for prefix."); + pos++; + } + p->extensions[pos] = NULL; + assert(pos == p->nextensions); + + continue; + } + + if (strncasecmp(items[i], "comment:", 7) == 0) + break; + + if (uade_song_and_player_attribute(&p->attributelist, &p->flags, items[i], lineno)) + continue; + + fprintf(stderr, "Unrecognized option: %s\n", items[i]); + } + + for (i = 0; items[i] != NULL; i++) + free(items[i]); + + free(items); + } + + fclose(f); + + if (ps->nplayers == 0) { + free(ps->players); + free(ps); + return NULL; + } + + for (i = 0; i < ps->nplayers; i++) + ps->nextensions += ps->players[i].nextensions; + + ps->map = malloc(sizeof(ps->map[0]) * ps->nextensions); + if (ps->map == NULL) + eperror("No memory for extension map."); + + exti = 0; + epwarning = 0; + for (i = 0; i < ps->nplayers; i++) { + p = &ps->players[i]; + if (p->nextensions == 0) { + if (epwarning == 0) { + fprintf(stderr, + "uade warning: %s eagleplayer lacks prefixes in " + "eagleplayer.conf, which makes it unusable for any kind of " + "file type detection. If you don't want name based file type " + "detection for a particular format, use content_detection " + "option for the line in eagleplayer.conf.\n", + ps->players[i].playername); + epwarning = 1; + } + continue; + } + for (j = 0; j < p->nextensions; j++) { + assert(exti < ps->nextensions); + ps->map[exti].player = p; + ps->map[exti].extension = p->extensions[j]; + exti++; + } + } + + assert(exti == ps->nextensions); + + /* Make the extension map bsearch() ready */ + qsort(ps->map, ps->nextensions, sizeof(ps->map[0]), ufcompare); + + return ps; + + error: + if (ps) + free(ps->players); + free(ps); + if (f != NULL) + fclose(f); + return NULL; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/eagleplayer.h b/plugins/uade2/uade-2.13/src/frontends/common/eagleplayer.h new file mode 100644 index 00000000..fc3497b5 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/eagleplayer.h @@ -0,0 +1,127 @@ +#ifndef _UADE_EAGLEPLAYER_H_ +#define _UADE_EAGLEPLAYER_H_ + +#include <stdio.h> +#include <stdint.h> +#include <limits.h> + +#include "uadeconfstructure.h" + +/* We maintain alphabetical order even if that forces us to renumber bits + when a new option is added */ +#define ES_A1200 (1 << 0) +#define ES_A500 (1 << 1) +#define ES_ALWAYS_ENDS (1 << 2) +#define ES_BROKEN_SONG_END (1 << 3) +#define ES_CONTENT_DETECTION (1 << 4) +#define ES_EP_OPTION (1 << 5) +#define ES_GAIN (1 << 6) +#define ES_IGNORE_PLAYER_CHECK (1 << 7) +#define ES_LED_OFF (1 << 8) +#define ES_LED_ON (1 << 9) +#define ES_NAME_DETECTION (1 << 10) +#define ES_NEVER_ENDS (1 << 11) +#define ES_NO_FILTER (1 << 12) +#define ES_NO_HEADPHONES (1 << 13) +#define ES_NO_PANNING (1 << 14) +#define ES_NO_POSTPROCESSING (1 << 15) +#define ES_NTSC (1 << 16) +#define ES_ONE_SUBSONG (1 << 17) +#define ES_PAL (1 << 18) +#define ES_PANNING (1 << 19) +#define ES_PLAYER (1 << 20) +#define ES_REJECT (1 << 21) +#define ES_RESAMPLER (1 << 22) +#define ES_SILENCE_TIMEOUT (1 << 23) +#define ES_SPEED_HACK (1 << 24) +#define ES_SUBSONGS (1 << 25) +#define ES_SUBSONG_TIMEOUT (1 << 26) +#define ES_TIMEOUT (1 << 27) + +#define UADE_WS_DELIMITERS " \t\n" + +struct eagleplayer { + char *playername; + size_t nextensions; + char **extensions; + int flags; + struct uade_attribute *attributelist; +}; + +struct eagleplayermap { + char *extension; + struct eagleplayer *player; +}; + +struct eagleplayerstore { + size_t nplayers; + struct eagleplayer *players; + size_t nextensions; + struct eagleplayermap *map; +}; + +enum uade_attribute_type { + UA_STRING = 1, + UA_INT, + UA_DOUBLE +}; + +struct uade_attribute; + +struct uade_attribute { + struct uade_attribute *next; + enum uade_attribute_type type; + char *s; + int i; + double d; +}; + +struct uade_song { + char md5[33]; + + char module_filename[PATH_MAX]; + + char playername[256]; /* Eagleplayer name in players directory */ + char modulename[256]; /* From score */ + char formatname[256]; + + uint8_t *buf; + size_t bufsize; + + int min_subsong; + int max_subsong; + int cur_subsong; + + int playtime; + int flags; + int nsubsongs; + uint8_t *subsongs; + struct uade_attribute *songattributes; + struct uade_ep_options ep_options; + char *normalisation; + + int64_t out_bytes; + + int64_t silence_count; +}; + +struct epconfattr { + char *s; /* config file directive/variable name */ + int e; /* ES_* flags for eagleplayers and songs */ + int o; /* UC_* flag for uade.conf option */ + char *c; /* constant for an UC_* flag */ + enum uade_attribute_type t; /* if variable, its special type */ +}; + + +extern const struct epconfattr epconf[]; + + +/* FIX: A forward declaration to avoid circular dependency */ +struct uade_state; + +int uade_is_our_file(const char *modulename, int scanmode, struct uade_state *state); +int uade_song_and_player_attribute(struct uade_attribute **attributelist, + int *flags, char *item, size_t lineno); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/effects.c b/plugins/uade2/uade-2.13/src/frontends/common/effects.c new file mode 100644 index 00000000..c75c859d --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/effects.c @@ -0,0 +1,490 @@ +/* Effect module for UADE2 frontends. + + Copyright 2005 (C) Antti S. Lankila <alankila@bel.fi> + + This module is licensed under the GNU LGPL. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <math.h> + +#include <compilersupport.h> + +#include "effects.h" + +/*** old headphone effect ***/ +#define UADE_EFFECT_HEADPHONES_DELAY_LENGTH 22 +#define UADE_EFFECT_HEADPHONES_DELAY_DIRECT 0.3 +#define UADE_EFFECT_HEADPHONES_CROSSMIX_VOL 0.80 + +static float headphones_ap_l[UADE_EFFECT_HEADPHONES_DELAY_LENGTH]; +static float headphones_ap_r[UADE_EFFECT_HEADPHONES_DELAY_LENGTH]; +static float headphones_rc_l[4]; +static float headphones_rc_r[4]; + +/*** new headphone effect ***/ + +/* delay time defines the width of the head. 0.5 ms gives us 15 cm virtual distance + * between sound arriving to either ear. */ +#define HEADPHONE2_DELAY_TIME 0.49e-3 +#define HEADPHONE2_DELAY_K 0.15 +/* head shadow frequency cutoff */ +#define HEADPHONE2_SHADOW_FREQ 8000.0 +/* high shelve keeps frequencies below cutoff intact and attenuates + * the rest in an uniform way. The effect is to make bass more "mono" than "stereo". */ +#define HEADPHONE2_SHELVE_FREQ 100.0 +#define HEADPHONE2_SHELVE_LEVEL -2.0 + +#define MAXIMUM_SAMPLING_RATE 96000 +#define HEADPHONE2_DELAY_MAX_LENGTH ((int)(MAXIMUM_SAMPLING_RATE*HEADPHONE2_DELAY_TIME+1)) +#define DENORMAL_OFFSET 1E-10 + +#define NORMALISE_RESOLUTION 10 /* in bits */ +#define NORMALISE_DEFAULT_GAIN 8.0 +#define NORMALISE_MAXIMUM_GAIN 8.0 + +/* Headphone variables */ +typedef struct { + float b0, b1, b2, a1, a2, x[2], y[2]; +} biquad_t; + +static float headphone2_ap_l[HEADPHONE2_DELAY_MAX_LENGTH]; +static float headphone2_ap_r[HEADPHONE2_DELAY_MAX_LENGTH]; +static int headphone2_delay_length; +static biquad_t headphone2_shelve_l; +static biquad_t headphone2_shelve_r; +static biquad_t headphone2_rc_l; +static biquad_t headphone2_rc_r; + +/* Normalise variables */ +static int normalise_peak_level; +static int normalise_historic_maximum_peak; +static int normalise_oldlevel; + +static void gain(int gain_amount, int16_t * sm, int frames); +static void pan(int pan_amount, int16_t * sm, int frames); +static void headphones(int16_t * sm, int frames); +static void headphones2(int16_t * sm, int frames); +static void normalise(int change_level, int16_t * sm, int frames); +static int normalise_compute_gain(int peak); + +static inline int sampleclip(int x) +{ + if (unlikely(x > 32767 || x < -32768)) { + if (x > 32767) + x = 32767; + else + x = -32768; + } + return x; +} + +/* calculate a high shelve filter */ +static void calculate_shelve(double fs, double fc, double g, biquad_t * bq) +{ + float A, omega, sn, cs, beta, b0, b1, b2, a0, a1, a2; + + A = powf(10, g / 40); + omega = 2 * M_PI * fc / fs; + omega = tan(omega / 2) * 2; + sn = sin(omega); + cs = cos(omega); + beta = sqrt(A + A); + + b0 = A * ((A + 1) + (A - 1) * cs + beta * sn); + b1 = -2 * A * ((A - 1) + (A + 1) * cs); + b2 = A * ((A + 1) + (A - 1) * cs - beta * sn); + a0 = (A + 1) - (A - 1) * cs + beta * sn; + a1 = 2 * ((A - 1) - (A + 1) * cs); + a2 = (A + 1) - (A - 1) * cs - beta * sn; + + bq->b0 = b0 / a0; + bq->b1 = b1 / a0; + bq->b2 = b2 / a0; + bq->a1 = a1 / a0; + bq->a2 = a2 / a0; +} + +/* calculate 1st order lowpass filter */ +static void calculate_rc(double fs, double fc, biquad_t * bq) +{ + float omega; + + if (fc >= fs / 2) { + bq->b0 = 1.0; + bq->b1 = 0.0; + bq->b2 = 0.0; + bq->a1 = 0.0; + bq->a2 = 0.0; + return; + } + omega = 2 * M_PI * fc / fs; + omega = tan(omega / 2) * 2; + + bq->b0 = 1 / (1 + 1 / omega); + bq->b1 = 0; + bq->b2 = 0; + bq->a1 = -1 + bq->b0; + bq->a2 = 0; +} + +static inline float evaluate_biquad(float input, biquad_t * bq) +{ + float output = DENORMAL_OFFSET; + + output += input * bq->b0 + bq->x[0] * bq->b1 + bq->x[1] * bq->b2; + output -= bq->y[0] * bq->a1 + bq->y[1] * bq->a2; + + bq->x[1] = bq->x[0]; + bq->x[0] = input; + + bq->y[1] = bq->y[0]; + bq->y[0] = output; + + return output; +} + +static void reset_biquad(biquad_t * bq) +{ + bq->x[0] = bq->x[1] = bq->y[0] = bq->y[1] = 0; +} + +/* Reset effects' state variables. + * Call this method between before starting playback */ +void uade_effect_reset_internals(void) +{ + /* old headphones */ + memset(headphones_ap_l, 0, sizeof(headphones_ap_l)); + memset(headphones_ap_r, 0, sizeof(headphones_ap_r)); + memset(headphones_rc_l, 0, sizeof(headphones_rc_l)); + memset(headphones_rc_r, 0, sizeof(headphones_rc_r)); + + /* new headphones */ + memset(headphone2_ap_l, 0, sizeof(headphone2_ap_l)); + memset(headphone2_ap_r, 0, sizeof(headphone2_ap_r)); + reset_biquad(&headphone2_shelve_l); + reset_biquad(&headphone2_shelve_r); + reset_biquad(&headphone2_rc_l); + reset_biquad(&headphone2_rc_r); + + normalise_peak_level = 0; + normalise_historic_maximum_peak = 0; + normalise_oldlevel = 1 << NORMALISE_RESOLUTION; +} + +void uade_effect_disable_all(struct uade_effect *ue) +{ + ue->enabled = 0; +} + +void uade_effect_disable(struct uade_effect *ue, uade_effect_t effect) +{ + ue->enabled &= ~(1 << effect); +} + +void uade_effect_enable(struct uade_effect *ue, uade_effect_t effect) +{ + ue->enabled |= 1 << effect; +} + +/* Returns 1 if effect is enabled, and zero otherwise. Ignores + UADE_EFFECT_ALLOW. */ +int uade_effect_is_enabled(struct uade_effect *ue, uade_effect_t effect) +{ + return (ue->enabled & (1 << effect)) != 0; +} + +void uade_effect_run(struct uade_effect *ue, int16_t * samples, int frames) +{ + if (ue->enabled & (1 << UADE_EFFECT_ALLOW)) { + normalise(ue->enabled & (1 << UADE_EFFECT_NORMALISE), samples, + frames); + if (ue->enabled & (1 << UADE_EFFECT_PAN)) + pan(ue->pan, samples, frames); + if (ue->enabled & (1 << UADE_EFFECT_HEADPHONES)) + headphones(samples, frames); + if (ue->enabled & (1 << UADE_EFFECT_HEADPHONES2) && ue->rate) + headphones2(samples, frames); + if (ue->enabled & (1 << UADE_EFFECT_GAIN)) + gain(ue->gain, samples, frames); + } +} + +void uade_effect_toggle(struct uade_effect *ue, uade_effect_t effect) +{ + ue->enabled ^= 1 << effect; +} + +void uade_effect_set_defaults(struct uade_effect *ue) +{ + memset(ue, 0, sizeof(*ue)); + uade_effect_disable_all(ue); + uade_effect_enable(ue, UADE_EFFECT_ALLOW); + uade_effect_gain_set_amount(ue, 1.0); + uade_effect_pan_set_amount(ue, 0.7); +} + +/* Rate of 0 means undefined. Effects that depend on sample rate must + self-check against this because they can not implemented properly */ +void uade_effect_set_sample_rate(struct uade_effect *ue, int rate) +{ + assert(rate >= 0); + ue->rate = rate; + + if (rate == 0) + return; + + calculate_shelve(rate, HEADPHONE2_SHELVE_FREQ, HEADPHONE2_SHELVE_LEVEL, + &headphone2_shelve_l); + calculate_shelve(rate, HEADPHONE2_SHELVE_FREQ, HEADPHONE2_SHELVE_LEVEL, + &headphone2_shelve_r); + calculate_rc(rate, HEADPHONE2_SHADOW_FREQ, &headphone2_rc_l); + calculate_rc(rate, HEADPHONE2_SHADOW_FREQ, &headphone2_rc_r); + headphone2_delay_length = HEADPHONE2_DELAY_TIME * rate + 0.5; + if (headphone2_delay_length > HEADPHONE2_DELAY_MAX_LENGTH) { + fprintf(stderr, "effects.c: truncating headphone delay line due to samplerate exceeding 96 kHz.\n"); + headphone2_delay_length = HEADPHONE2_DELAY_MAX_LENGTH; + } +} + +void uade_effect_gain_set_amount(struct uade_effect *ue, float amount) +{ + assert(amount >= 0.0 && amount <= 128.0); + ue->gain = amount * 256.0; +} + +void uade_effect_pan_set_amount(struct uade_effect *ue, float amount) +{ + assert(amount >= 0.0 && amount <= 2.0); + ue->pan = amount * 256.0 / 2.0; +} + +static int normalise_compute_gain(int peak) +{ + if (normalise_historic_maximum_peak == 0) { + /* if the peak is not known, we cap gain in an attempt to avoid + * boosting silent intros too much. */ + if (peak < 32768 / NORMALISE_DEFAULT_GAIN) + return NORMALISE_DEFAULT_GAIN * + (1 << NORMALISE_RESOLUTION); + else + return (32768 << NORMALISE_RESOLUTION) / peak; + } else { + int largerpeak; + if (peak < normalise_historic_maximum_peak) + largerpeak = normalise_historic_maximum_peak; + else + largerpeak = peak; + /* if the peak is known, we use the recorded value but adapt + if this rendition comes out louder for some reason (for + instance, updated UADE) */ + if (largerpeak < 32768 / NORMALISE_MAXIMUM_GAIN) + return NORMALISE_MAXIMUM_GAIN * + (1 << NORMALISE_RESOLUTION); + else + return (32768 << NORMALISE_RESOLUTION) / largerpeak; + } +} + +/* We save gain from maximum known level. This is an one-way street, + the gain can * only decrease with time. If the historic level is + known and larger, we prefer it. */ +void uade_effect_normalise_serialise(char *buf, size_t len) +{ + int peak = normalise_peak_level; + + assert(len > 0); + + if (normalise_historic_maximum_peak > normalise_peak_level) + peak = normalise_historic_maximum_peak; + + if (snprintf(buf, len, "v=1,p=%d", peak) >= len) { + fprintf(stderr, "normalise effect: buffer too short, gain would be truncated. This is a bug in UADE.\n"); + exit(-1); + } +} + +/* similarly, this should only be called if gain has a positive value, + * but we try to recover from misuse. */ +void uade_effect_normalise_unserialise(const char *buf) +{ + int version, readcount; + float peak; + + normalise_historic_maximum_peak = 0; + + if (buf == NULL) + return; + + readcount = sscanf(buf, "v=%d,p=%f", &version, &peak); + + if (readcount == 0) { + fprintf(stderr, "normalise effect: gain string invalid: '%s'\n", buf); + exit(-1); + } + + if (version != 1) { + fprintf(stderr, "normalise effect: unrecognized gain version: '%s'\n", buf); + exit(-1); + } + + if (readcount != 2) { + fprintf(stderr, "Could not read peak value for version 1: '%s'\n", buf); + exit(-1); + } + + if (peak >= 0.0 && peak <= 1.0) { + normalise_oldlevel = normalise_historic_maximum_peak = + 32768 * peak; + } else { + fprintf(stderr, "normalise effect: invalid peak level: '%s'\n", buf); + } +} + +static void normalise(int change_level, int16_t * sm, int frames) +{ + int i; + + /* Negative side is mirrored. but positive side gains by 1. + * This is to make both semiwaves have same max. */ + for (i = 0; i < 2 * frames; i += 1) { + int tmp = sm[i]; + tmp = (tmp >= 0) ? tmp + 1 : -tmp; + if (tmp > normalise_peak_level) + normalise_peak_level = tmp; + } + + /* Slight clipping may result in first playback while the system + * adjusts. With a bit of "advance warning" of clipping about to + * occur, the level begins to adjust as soon as the buffer + * begins. Typical adjustment times are not large -- a few hundred + * samples are to be expected -- and the clipping should only + * occur on the first rendition of the song, if at all. */ + if (change_level) { + int newlevel = normalise_compute_gain(normalise_peak_level); + + for (i = 0; i < 2 * frames; i += 1) { + /* same gain for the frame */ + if ((i & 1) == 0) { + if (normalise_oldlevel < newlevel) + normalise_oldlevel += 1; + if (normalise_oldlevel > newlevel) + normalise_oldlevel -= 1; + } + sm[i] = + sampleclip((sm[i] * + normalise_oldlevel) >> + NORMALISE_RESOLUTION); + } + } +} + +static void gain(int gain_amount, int16_t * sm, int frames) +{ + int i; + for (i = 0; i < 2 * frames; i += 1) + sm[i] = sampleclip((sm[i] * gain_amount) >> 8); +} + +/* Panning effect. Turns stereo into mono in a specific degree */ +static void pan(int pan_amount, int16_t * sm, int frames) +{ + int i, l, r, m; + for (i = 0; i < frames; i += 1) { + l = sm[0]; + r = sm[1]; + m = (r - l) * pan_amount; + sm[0] = ((l << 8) + m) >> 8; + sm[1] = ((r << 8) - m) >> 8; + sm += 2; + } +} + +/* All-pass delay. Its purpose is to confuse the phase of the sound a bit + * and also provide some delay to locate the source outside the head. This + * seems to work better than a pure delay line. */ +static float headphones_allpass_delay(float in, float *state) +{ + int i; + float tmp, output; + + tmp = in - UADE_EFFECT_HEADPHONES_DELAY_DIRECT * state[0]; + output = state[0] + UADE_EFFECT_HEADPHONES_DELAY_DIRECT * tmp; + + /* FIXME: use modulo and index */ + for (i = 1; i < UADE_EFFECT_HEADPHONES_DELAY_LENGTH; i += 1) + state[i - 1] = state[i]; + state[UADE_EFFECT_HEADPHONES_DELAY_LENGTH - 1] = tmp; + + return output; +} + +static float headphones_lpf(float in, float *state) +{ + float out = in * 0.53; + out += 0.47 * state[0]; + state[0] = out; + + return out; +} + +/* A real implementation would simply perform FIR with recorded HRTF data. */ +static void headphones(int16_t * sm, int frames) +{ + int i; + float ld, rd; + int l_final, r_final; + for (i = 0; i < frames; i += 1) { + ld = headphones_allpass_delay(sm[0], headphones_ap_l); + rd = headphones_allpass_delay(sm[1], headphones_ap_r); + ld = headphones_lpf(ld, headphones_rc_l); + rd = headphones_lpf(rd, headphones_rc_r); + + l_final = + (sm[0] + rd * UADE_EFFECT_HEADPHONES_CROSSMIX_VOL) / 2; + r_final = + (sm[1] + ld * UADE_EFFECT_HEADPHONES_CROSSMIX_VOL) / 2; + sm[0] = sampleclip(l_final); + sm[1] = sampleclip(r_final); + + sm += 2; + } +} + +static float headphone2_allpass_delay(float in, float *state) +{ + int i; + float tmp, output; + + tmp = in - HEADPHONE2_DELAY_K * state[0]; + output = state[0] + HEADPHONE2_DELAY_K * tmp; + + /* FIXME: use modulo and index */ + for (i = 1; i < headphone2_delay_length; i += 1) + state[i - 1] = state[i]; + state[headphone2_delay_length - 1] = tmp; + + return output; +} + +static void headphones2(int16_t * sm, int frames) +{ + int i; + for (i = 0; i < frames; i += 1) { + float ld, rd; + + ld = headphone2_allpass_delay(sm[0], headphone2_ap_l); + rd = headphone2_allpass_delay(sm[1], headphone2_ap_r); + ld = evaluate_biquad(ld, &headphone2_rc_l); + rd = evaluate_biquad(rd, &headphone2_rc_r); + ld = evaluate_biquad(ld, &headphone2_shelve_l); + rd = evaluate_biquad(rd, &headphone2_shelve_r); + + sm[0] = sampleclip((sm[0] + rd) / 2); + sm[1] = sampleclip((sm[1] + ld) / 2); + sm += 2; + } +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/effects.h b/plugins/uade2/uade-2.13/src/frontends/common/effects.h new file mode 100644 index 00000000..57a779df --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/effects.h @@ -0,0 +1,42 @@ +#ifndef _UADE2_EFFECTS_H_ +#define _UADE2_EFFECTS_H_ + +#include <stdint.h> + +typedef enum { + UADE_EFFECT_ALLOW, + UADE_EFFECT_GAIN, + UADE_EFFECT_HEADPHONES, + UADE_EFFECT_HEADPHONES2, + UADE_EFFECT_PAN, + UADE_EFFECT_NORMALISE, +} uade_effect_t; + +struct uade_effect { + uade_effect_t enabled; + int gain; + int pan; + int rate; +}; + +void uade_effect_disable(struct uade_effect *ue, uade_effect_t effect); +void uade_effect_disable_all(struct uade_effect *ue); +void uade_effect_enable(struct uade_effect *ue, uade_effect_t effect); +int uade_effect_is_enabled(struct uade_effect *ue, uade_effect_t effect); +void uade_effect_set_defaults(struct uade_effect *ue); +void uade_effect_set_sample_rate(struct uade_effect *ue, int rate); +void uade_effect_toggle(struct uade_effect *ue, uade_effect_t effect); + +/* effect-specific knobs */ +void uade_effect_gain_set_amount(struct uade_effect *ue, float amount); +void uade_effect_normalise_unserialise(const char *buf); +void uade_effect_normalise_serialise(char *buf, size_t len); +void uade_effect_pan_set_amount(struct uade_effect *ue, float amount); + +/* reset state at start of song */ +void uade_effect_reset_internals(void); + +/* process n frames of sample buffer */ +void uade_effect_run(struct uade_effect *ue, int16_t * sample, int frames); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/md5.c b/plugins/uade2/uade-2.13/src/frontends/common/md5.c new file mode 100644 index 00000000..7614ea48 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/md5.c @@ -0,0 +1,247 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include <string.h> /* for memcpy() */ +#include "md5.h" + +#if __BYTE_ORDER == 1234 +#define byteReverse(buf, len) /* Nothing */ +#else +static void byteReverse(unsigned char *buf, unsigned longs); + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32_t t; + do { + t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32_t *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(MD5_CTX *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], MD5_CTX *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32_t *) ctx->in)[14] = ctx->bits[0]; + ((uint32_t *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32_t *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32_t buf[4], uint32_t const in[16]) +{ + uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/md5.copyright b/plugins/uade2/uade-2.13/src/frontends/common/md5.copyright new file mode 100644 index 00000000..72b040b3 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/md5.copyright @@ -0,0 +1,8 @@ +The algorithm is due to Ron Rivest. This code was written by Colin +Plumb in 1993, no copyright is claimed. This code is in the public +domain; do with it what you wish. +.Pp +Equivalent code is available from RSA Data Security, Inc. +This code has been tested against that, and is equivalent, +except that you don't need to include two pages of legalese +with every copy. diff --git a/plugins/uade2/uade-2.13/src/frontends/common/md5.h b/plugins/uade2/uade-2.13/src/frontends/common/md5.h new file mode 100644 index 00000000..6f471e3b --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/md5.h @@ -0,0 +1,21 @@ +#ifndef _UADE_MD5_H_ +#define _UADE_MD5_H_ + +#include <sys/types.h> +#include <stdint.h> + +#define MD5_HASHBYTES 16 + +typedef struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +} MD5_CTX; + +void MD5Init(MD5_CTX *context); +void MD5Update(MD5_CTX *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[MD5_HASHBYTES], MD5_CTX *context); +void MD5Transform(uint32_t buf[4], uint32_t const in[16]); + +#endif /* !_UADE_MD5_H_ */ diff --git a/plugins/uade2/uade-2.13/src/frontends/common/songdb.c b/plugins/uade2/uade-2.13/src/frontends/common/songdb.c new file mode 100644 index 00000000..b7d79165 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/songdb.c @@ -0,0 +1,798 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include "songdb.h" +#include "uadeconf.h" +#include "md5.h" +#include "unixatomic.h" +#include "ossupport.h" +#include "uadeconfig.h" +#include "support.h" +#include "uadeconstants.h" + +#define NORM_ID "n=" +#define NORM_ID_LENGTH 2 + +#define eserror(fmt, args...) do { fprintf(stderr, "song.conf error on line %zd: " fmt "\n", lineno, ## args); exit(-1); } while (0) + + +struct eaglesong { + int flags; + char md5[33]; + struct uade_attribute *attributes; +}; + +struct persub { + int sub; + char *normalisation; +}; + +static struct uade_content *contentchecksums; +static size_t nccused; /* number of valid entries in content db */ +static size_t nccalloc; /* number of allocated entries for content db */ +static int ccmodified; +static int cccorrupted; + +static int nsongs; +static struct eaglesong *songstore; + +static int escompare(const void *a, const void *b); +static struct uade_content *get_content(const char *md5); + + +static void add_sub_normalisation(struct uade_content *n, char *normalisation) +{ + struct persub *subinfo; + char *endptr; + + subinfo = malloc(sizeof(*subinfo)); + if (subinfo == NULL) + uadeerror("Can't allocate memory for normalisation entry\n"); + + subinfo->sub = strtol(normalisation, &endptr, 10); + if (*endptr != ',' || subinfo->sub < 0) { + fprintf(stderr, "Invalid normalisation entry: %s\n", normalisation); + return; + } + + subinfo->normalisation = strdup(endptr + 1); + if (subinfo->normalisation == NULL) + uadeerror("Can't allocate memory for normalisation string\n"); + + vplist_append(n->subs, subinfo); +} + +/* Compare function for bsearch() and qsort() to sort songs with respect + to their md5sums */ +static int contentcompare(const void *a, const void *b) +{ + return strcasecmp(((struct uade_content *)a)->md5, + ((struct uade_content *)b)->md5); +} + +static int escompare(const void *a, const void *b) +{ + return strcasecmp(((struct eaglesong *)a)->md5, + ((struct eaglesong *)b)->md5); +} + +static struct uade_content *get_content(const char *md5) +{ + struct uade_content key; + + if (contentchecksums == NULL) + return NULL; + + memset(&key, 0, sizeof key); + strlcpy(key.md5, md5, sizeof key.md5); + + return bsearch(&key, contentchecksums, nccused, + sizeof contentchecksums[0], contentcompare); +} + +static struct uade_content *create_content_checksum(const char *md5, + uint32_t playtime) +{ + struct uade_content *n; + + if (nccused == nccalloc) { + nccalloc = MAX(nccalloc * 2, 16); + n = realloc(contentchecksums, + nccalloc * sizeof(struct uade_content)); + if (n == NULL) { + fprintf(stderr, + "uade: No memory for new content checksums.\n"); + return NULL; + } + contentchecksums = n; + } + + n = &contentchecksums[nccused]; + + if (md5 == NULL) + return n; + + nccused++; + + ccmodified = 1; + + memset(n, 0, sizeof(*n)); + strlcpy(n->md5, md5, sizeof(n->md5)); + n->playtime = playtime; + + n->subs = vplist_create(1); + + return n; +} + +static void md5_from_buffer(char *dest, size_t destlen, + uint8_t * buf, size_t bufsize) +{ + uint8_t md5[16]; + int ret; + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, buf, bufsize); + MD5Final(md5, &ctx); + ret = + snprintf(dest, destlen, + "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", + md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], + md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], + md5[14], md5[15]); + if (ret >= destlen || ret != 32) { + fprintf(stderr, "md5 buffer error (%d/%zd)\n", ret, destlen); + exit(1); + } +} + +static void update_playtime(struct uade_content *n, uint32_t playtime) +{ + if (n->playtime != playtime) { + ccmodified = 1; + n->playtime = playtime; + } +} + +static void sort_content_checksums(void) +{ + if (contentchecksums == NULL) + return; + + qsort(contentchecksums, nccused, sizeof contentchecksums[0], + contentcompare); +} + +/* replace must be zero if content db is unsorted */ +struct uade_content *uade_add_playtime(const char *md5, uint32_t playtime) +{ + struct uade_content *n; + + /* If content db hasn't been read into memory already, it is not used */ + if (contentchecksums == NULL) + return NULL; + + /* Do not record song shorter than 3 secs */ + if (playtime < 3000) + return NULL; + + if (strlen(md5) != 32) + return NULL; + + n = get_content(md5); + if (n != NULL) { + update_playtime(n, playtime); + return n; + } + + n = create_content_checksum(md5, playtime); + + sort_content_checksums(); + + return n; +} + +void uade_lookup_volume_normalisation(struct uade_state *state) +{ + size_t i, nsubs; + struct uade_effect *ue = &state->effects; + struct uade_config *uc = &state->config; + struct uade_song *us = state->song; + struct uade_content *content = get_content(us->md5); + + if (content != NULL) { + + nsubs = vplist_len(content->subs); + + for (i = 0; i < nsubs; i++) { + + struct persub *subinfo = vplist_get(content->subs, i); + + if (subinfo->sub == us->cur_subsong) { + uade_set_config_option(uc, UC_NORMALISE, + subinfo->normalisation); + uade_effect_normalise_unserialise(uc-> + normalise_parameter); + uade_effect_enable(ue, UADE_EFFECT_NORMALISE); + break; + } + } + } +} + +static void get_song_flags_and_attributes_from_songstore(struct uade_song *us) +{ + struct eaglesong key; + struct eaglesong *es; + + if (songstore != NULL) { + /* Lookup md5 from the songdb */ + strlcpy(key.md5, us->md5, sizeof key.md5); + es = bsearch(&key, songstore, nsongs, sizeof songstore[0], escompare); + + if (es != NULL) { + /* Found -> copy flags and attributes from database */ + us->flags |= es->flags; + us->songattributes = es->attributes; + } + } +} + +int uade_alloc_song(struct uade_state *state, const char *filename) +{ + struct uade_song *us; + struct uade_content *content; + + state->song = NULL; + + us = calloc(1, sizeof *us); + if (us == NULL) + goto error; + + strlcpy(us->module_filename, filename, sizeof us->module_filename); + + us->buf = atomic_read_file(&us->bufsize, filename); + if (us->buf == NULL) + goto error; + + /* Compute an md5sum of the song */ + md5_from_buffer(us->md5, sizeof us->md5, us->buf, us->bufsize); + + /* Needs us->md5 sum */ + get_song_flags_and_attributes_from_songstore(us); + + /* Lookup playtime from content database */ + us->playtime = -1; + content = get_content(us->md5); + if (content != NULL && content->playtime > 0) + us->playtime = content->playtime; + + /* We can't know subsong numbers yet. The eagleplayer will report them + * in the playback state */ + us->min_subsong = us->max_subsong = us->cur_subsong = -1; + + state->song = us; + return 1; + + error: + if (us != NULL) { + free(us->buf); + free(us); + } + return 0; +} + +static int uade_open_and_lock(const char *filename, int create) +{ + int fd, ret; + fd = open(filename, O_RDWR); + if (fd < 0) { + if (errno == ENOENT && create) { + fd = open(filename, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR); + if (fd < 0) + return -1; + } else { + return -1; + } + } +#ifndef UADE_HAVE_CYGWIN + ret = lockf(fd, F_LOCK, 0); + if (ret) { + fprintf(stderr, "uade: Unable to lock song.conf: %s (%s)\n", + filename, strerror(errno)); + atomic_close(fd); + return -1; + } +#endif + + return fd; +} + + +static struct uade_content *store_playtime(const char *md5, long playtime, + int *newccmodified, + size_t oldnccused) +{ + struct uade_content *n = NULL; + + if (oldnccused > 0) { + struct uade_content key; + memset(&key, 0, sizeof key); + strlcpy(key.md5, md5, sizeof key.md5); + + /* We use "oldnccused" here as the length, while new entries + are added in unsorted manner to the end of the array */ + n = bsearch(&key, contentchecksums, oldnccused, + sizeof contentchecksums[0], contentcompare); + if (n == NULL) + /* new songs on disk db -> merge -> need saving */ + *newccmodified = 1; + } + + /* We value a playtime determined during run-time over + a database value */ + if (n == NULL) { + /* Note, create_content_checksum() makes "ccmodified" + true, which we work-around later with the "newccmodified" */ + n = create_content_checksum(md5, (uint32_t) playtime); + } + + if (n == NULL) { + /* No memory, fuck. We shouldn't save anything to + avoid losing data. */ + fprintf(stderr, + "uade: Warning, no memory for the song database\n"); + cccorrupted = 1; + } + + return n; +} + + + +int uade_read_content_db(const char *filename) +{ + char line[1024]; + FILE *f; + size_t lineno = 0; + long playtime; + int i, j, nexti; + char *id, *eptr; + char numberstr[1024]; + char *md5; + + /* We make backups of some variables because following loop will + make it always true, which is not what we want. The end result should + be that ccmodified is true in following cases only: + 1. the in-memory db is already dirty + 2. the in-memory db gets new data from disk db (merge operation) + Otherwise ccmodified should be false. */ + int newccmodified = ccmodified; + size_t oldnccused = nccused; + int fd; + struct uade_content *n; + + /* Try to create a database if it doesn't exist */ + if (contentchecksums == NULL + && create_content_checksum(NULL, 0) == NULL) + return 0; + + fd = uade_open_and_lock(filename, 0); + if (fd < 0) { + fprintf(stderr, "uade: Can not find %s\n", filename); + return 0; + } + + f = fdopen(fd, "r"); + if (f == NULL) { + fprintf(stderr, "uade: Can not create FILE structure for %s\n", + filename); + close(fd); + return 0; + } + + while (xfgets(line, sizeof line, f) != NULL) { + lineno++; + + if (line[0] == '#') + continue; + + md5 = line; + i = skip_and_terminate_word(line, 0); + if (i < 0) + continue; /* playtime doesn't exist */ + + for (j = 0; isxdigit(line[j]); j++); + + if (j != 32) + continue; /* is not a valid md5sum */ + + /* Grab and validate playtime (in milliseconds) */ + nexti = skip_and_terminate_word(line, i); + + playtime = strtol(&line[i], &eptr, 10); + if (*eptr != 0 || playtime < 0) { + fprintf(stderr, "Invalid playtime for md5 %s on contentdb line %zd: %s\n", md5, lineno, numberstr); + continue; + } + + n = store_playtime(md5, playtime, &newccmodified, oldnccused); + if (n == NULL) + continue; + + i = nexti; /* Note, it could be that i < 0 */ + + /* Get rest of the directives in a loop */ + while (i >= 0) { + id = &line[i]; + i = skip_and_terminate_word(line, i); + + /* Subsong volume normalisation: n=sub1,XXX */ + if (strncmp(id, NORM_ID, NORM_ID_LENGTH) == 0) { + id += NORM_ID_LENGTH; + add_sub_normalisation(n, id); + } else { + fprintf(stderr, "Unknown contentdb directive on line %zd: %s\n", lineno, id); + } + } + } + fclose(f); + + ccmodified = newccmodified; + + sort_content_checksums(); + + return 1; +} + +int uade_read_song_conf(const char *filename) +{ + FILE *f = NULL; + struct eaglesong *s; + size_t allocated; + size_t lineno = 0; + size_t i; + int fd; + + fd = uade_open_and_lock(filename, 1); + /* open_and_lock() may fail without harm (it's actually supposed to + fail if the process does not have lock (write) permissions to + the song.conf file */ + + f = fopen(filename, "r"); + if (f == NULL) + goto error; + + nsongs = 0; + allocated = 16; + songstore = calloc(allocated, sizeof songstore[0]); + if (songstore == NULL) + eserror("No memory for song store."); + + while (1) { + char **items; + size_t nitems; + + items = read_and_split_lines(&nitems, &lineno, f, + UADE_WS_DELIMITERS); + if (items == NULL) + break; + + assert(nitems > 0); + + if (nsongs == allocated) { + allocated *= 2; + songstore = realloc(songstore, allocated * sizeof(songstore[0])); + if (songstore == NULL) + eserror("No memory for players."); + } + + s = &songstore[nsongs]; + nsongs++; + + memset(s, 0, sizeof s[0]); + + if (strncasecmp(items[0], "md5=", 4) != 0) { + fprintf(stderr, "Line %zd must begin with md5= in %s\n", + lineno, filename); + free(items); + continue; + } + if (strlcpy(s->md5, items[0] + 4, sizeof s->md5) != + ((sizeof s->md5) - 1)) { + fprintf(stderr, + "Line %zd in %s has too long an md5sum.\n", + lineno, filename); + free(items); + continue; + } + + for (i = 1; i < nitems; i++) { + if (strncasecmp(items[i], "comment:", 7) == 0) + break; + if (uade_song_and_player_attribute(&s->attributes, &s->flags, items[i], lineno)) + continue; + fprintf(stderr, "song option %s is invalid\n", items[i]); + } + + for (i = 0; items[i] != NULL; i++) + free(items[i]); + + free(items); + } + + fclose(f); + + /* we may not have the file locked */ + if (fd >= 0) + atomic_close(fd); /* lock is closed too */ + + /* Sort MD5 sums for binary searching songs */ + qsort(songstore, nsongs, sizeof songstore[0], escompare); + return 1; + + error: + if (f) + fclose(f); + if (fd >= 0) + atomic_close(fd); + return 0; +} + +void uade_save_content_db(const char *filename) +{ + int fd; + FILE *f; + size_t i; + + if (ccmodified == 0 || cccorrupted) + return; + + fd = uade_open_and_lock(filename, 1); + if (fd < 0) { + fprintf(stderr, "uade: Can not write content db: %s\n", + filename); + return; + } + + f = fdopen(fd, "w"); + if (f == NULL) { + fprintf(stderr, + "uade: Can not create a FILE structure for content db: %s\n", + filename); + close(fd); + return; + } + + for (i = 0; i < nccused; i++) { + char str[1024]; + size_t subi, nsubs; + size_t bindex, bleft; + struct uade_content *n = &contentchecksums[i]; + + str[0] = 0; + + bindex = 0; + bleft = sizeof(str); + + nsubs = vplist_len(n->subs); + + for (subi = 0; subi < nsubs; subi++) { + struct persub *sub = vplist_get(n->subs, subi); + int ret; + ret = + snprintf(&str[bindex], bleft, NORM_ID "%s ", + sub->normalisation); + if (ret >= bleft) { + fprintf(stderr, + "Too much subsong infos for %s\n", + n->md5); + break; + } + bleft -= ret; + bindex += ret; + } + + fprintf(f, "%s %u %s\n", n->md5, (unsigned int)n->playtime, + str); + } + + ccmodified = 0; + + fclose(f); + fprintf(stderr, "uade: Saved %zd entries into content db.\n", nccused); +} + +int uade_test_silence(void *buf, size_t size, struct uade_state *state) +{ + int i, s, exceptioncount; + int16_t *sm; + int nsamples; + int64_t count = state->song->silence_count; + int end = 0; + + if (state->config.silence_timeout < 0) + return 0; + + exceptioncount = 0; + sm = buf; + nsamples = size / 2; + + for (i = 0; i < nsamples; i++) { + s = (sm[i] >= 0) ? sm[i] : -sm[i]; + if (s >= (32767 * 1 / 100)) { + exceptioncount++; + if (exceptioncount >= (size * 2 / 100)) { + count = 0; + break; + } + } + } + + if (i == nsamples) { + count += size; + if (count / (UADE_BYTES_PER_FRAME * state->config.frequency) >= state->config.silence_timeout) { + count = 0; + end = 1; + } + } + + state->song->silence_count = count; + + return end; +} + +void uade_unalloc_song(struct uade_state *state) +{ + free(state->song->buf); + state->song->buf = NULL; + + free(state->song); + state->song = NULL; +} + +int uade_update_song_conf(const char *songconfin, const char *songconfout, + const char *songname, const char *options) +{ + int ret; + int fd; + char md5[33]; + void *mem = NULL; + size_t filesize, newsize; + int found = 0; + size_t inputsize; + char *input, *inputptr, *outputptr; + size_t inputoffs; + char newline[256]; + size_t i; + int need_newline = 0; + + if (strlen(options) > 128) { + fprintf(stderr, "Too long song.conf options.\n"); + return 0; + } + + fd = uade_open_and_lock(songconfout, 1); + + input = atomic_read_file(&inputsize, songconfin); + if (input == NULL) { + fprintf(stderr, "Can not read song.conf: %s\n", songconfin); + atomic_close(fd); /* closes the lock too */ + return 0; + } + + newsize = inputsize + strlen(options) + strlen(songname) + 64; + mem = realloc(input, newsize); + if (mem == NULL) { + fprintf(stderr, + "Can not realloc the input file buffer for song.conf.\n"); + free(input); + atomic_close(fd); /* closes the lock too */ + return 0; + } + input = mem; + + mem = atomic_read_file(&filesize, songname); + if (mem == NULL) + goto error; + + md5_from_buffer(md5, sizeof md5, mem, filesize); + + inputptr = outputptr = input; + inputoffs = 0; + + while (inputoffs < inputsize) { + if (inputptr[0] == '#') + goto copyline; + + if ((inputoffs + 37) >= inputsize) + goto copyline; + + if (strncasecmp(inputptr, "md5=", 4) != 0) + goto copyline; + + if (strncasecmp(inputptr + 4, md5, 32) == 0) { + if (found) { + fprintf(stderr, + "Warning: dupe entry in song.conf: %s (%s)\n" + "Need manual resolving.\n", songname, + md5); + goto copyline; + } + found = 1; + snprintf(newline, sizeof newline, "md5=%s\t%s\n", md5, + options); + + /* Skip this line. It will be appended later to the end of the buffer */ + for (i = inputoffs; i < inputsize; i++) { + if (input[i] == '\n') { + i = i + 1 - inputoffs; + break; + } + } + if (i == inputsize) { + i = inputsize - inputoffs; + found = 0; + need_newline = 1; + } + inputoffs += i; + inputptr += i; + continue; + } + + copyline: + /* Copy the line */ + for (i = inputoffs; i < inputsize; i++) { + if (input[i] == '\n') { + i = i + 1 - inputoffs; + break; + } + } + if (i == inputsize) { + i = inputsize - inputoffs; + need_newline = 1; + } + memmove(outputptr, inputptr, i); + inputoffs += i; + inputptr += i; + outputptr += i; + } + + if (need_newline) { + snprintf(outputptr, 2, "\n"); + outputptr += 1; + } + + /* there is enough space */ + ret = snprintf(outputptr, PATH_MAX + 256, "md5=%s\t%s\tcomment %s\n", + md5, options, songname); + outputptr += ret; + + if (ftruncate(fd, 0)) { + fprintf(stderr, "Can not truncate the file.\n"); + goto error; + } + + /* Final file size */ + i = (size_t) (outputptr - input); + + if (atomic_write(fd, input, i) < i) + fprintf(stderr, + "Unable to write file contents back. Data loss happened. CRAP!\n"); + + error: + atomic_close(fd); /* Closes the lock too */ + free(input); + free(mem); + return 1; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/songdb.h b/plugins/uade2/uade-2.13/src/frontends/common/songdb.h new file mode 100644 index 00000000..8cbba160 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/songdb.h @@ -0,0 +1,24 @@ +#ifndef _UADE_SONGDB_H_ +#define _UADE_SONGDB_H_ + +#include "uadestate.h" +#include "vplist.h" + +struct uade_content { + char md5[33]; + uint32_t playtime; /* in milliseconds */ + struct vplist *subs; +}; + +struct uade_content *uade_add_playtime(const char *md5, uint32_t playtime); +int uade_alloc_song(struct uade_state *state, const char *filename); +void uade_lookup_volume_normalisation(struct uade_state *state); +int uade_read_content_db(const char *filename); +int uade_read_song_conf(const char *filename); +void uade_save_content_db(const char *filename); +int uade_test_silence(void *buf, size_t size, struct uade_state *state); +void uade_unalloc_song(struct uade_state *state); +int uade_update_song_conf(const char *songconfin, const char *songconfout, + const char *songname, const char *options); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/songinfo.c b/plugins/uade2/uade-2.13/src/frontends/common/songinfo.c new file mode 100644 index 00000000..5997b183 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/songinfo.c @@ -0,0 +1,721 @@ +#define _GNU_SOURCE +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <stdint.h> +#include <ctype.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "songinfo.h" +#include "uadeutils.h" +#include "ossupport.h" +#include "amifilemagic.h" +#include "support.h" + + +static void asciiline(char *dst, unsigned char *buf) +{ + int i, c; + for (i = 0; i < 16; i++) { + c = buf[i]; + if (isgraph(c) || c == ' ') { + dst[i] = c; + } else { + dst[i] = '.'; + } + } + dst[i] = 0; +} + +static int hexdump(char *info, size_t maxlen, char *filename, size_t toread) +{ + FILE *f; + size_t rb, ret; + uint8_t *buf; + + assert(maxlen >= 8192); + + f = fopen(filename, "rb"); + if (f == NULL) + return 0; + + buf = malloc(toread); + if (buf == NULL) + return 0; + + rb = 0; + while (rb < toread) { + ret = fread(&buf[rb], 1, toread - rb, f); + if (ret == 0) + break; + rb += ret; + } + + if (rb > 0) { + size_t roff = 0; + size_t woff = 0; + + while (roff < rb) { + int iret; + + if (woff >= maxlen) + break; + + if (woff + 32 >= maxlen) { + strcpy(&info[woff], "\nbuffer overflow...\n"); + woff += strlen(&info[woff]); + break; + } + + iret = snprintf(&info[woff], maxlen - woff, "%.3zx: ", + roff); + assert(iret > 0); + woff += iret; + + if (woff >= maxlen) + break; + + if (roff + 16 > rb) { + iret = snprintf(&info[woff], maxlen - woff, + "Aligned line "); + assert(iret > 0); + woff += iret; + + } else { + char dbuf[17]; + asciiline(dbuf, &buf[roff]); + iret = snprintf(&info[woff], maxlen - woff, + "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x |%s|", + buf[roff + 0], buf[roff + 1], + buf[roff + 2], buf[roff + 3], + buf[roff + 4], buf[roff + 5], + buf[roff + 6], buf[roff + 7], + buf[roff + 8], buf[roff + 9], + buf[roff + 10], buf[roff + 11], + buf[roff + 12], buf[roff + 13], + buf[roff + 14], buf[roff + 15], + dbuf); + assert(iret > 0); + woff += iret; + } + + if (woff >= maxlen) + break; + + iret = snprintf(&info[woff], maxlen - woff, "\n"); + woff += iret; + + roff += 16; + } + + if (woff >= maxlen) + woff = maxlen - 1; + info[woff] = 0; + } + + fclose(f); + free(buf); + return rb == 0; +} + +static size_t find_tag(uint8_t * buf, size_t startoffset, size_t buflen, + uint8_t * tag, size_t taglen) +{ + uint8_t *treasure; + + if (startoffset >= buflen) + return -1; + + treasure = memmem(buf + startoffset, buflen - startoffset, tag, taglen); + if (treasure == NULL) + return -1; + + return (size_t) (treasure - buf); +} + +static int string_checker(unsigned char *str, size_t off, size_t maxoff) +{ + assert(maxoff > 0); + while (off < maxoff) { + if (*str == 0) + return 1; + off++; + str++; + } + return 0; +} + +/* Wanted Team's loadseg modules */ +static void process_WTWT_mod(char *credits, size_t credits_len, + unsigned char *buf, size_t len, char *lo, + char *hi, int rel) +{ + int offset, txt_offset, chunk; + char tmpstr[256]; + + /* check for Magic ID */ + offset = find_tag((uint8_t *) buf, 0, len, (uint8_t *) lo, 4); + if (offset == -1) + return; + + offset = + find_tag((uint8_t *) buf, offset + 4, offset + 8, (uint8_t *) hi, + 4); + if (offset == -1) + return; + + chunk = offset - 8; /* here's where our first chunk should be */ + offset = offset + rel; /* offset to our info pointers */ + + if (chunk < len && offset < len) { + txt_offset = read_be_u32(buf + offset) + chunk; + if (txt_offset < len && txt_offset != chunk) { + if (!string_checker(buf, txt_offset, len)) + return; + snprintf(tmpstr, sizeof tmpstr, "\nMODULENAME:\n %s\n", + buf + txt_offset); + strlcat(credits, tmpstr, credits_len); + + } + txt_offset = read_be_u32(buf + offset + 4) + chunk; + if (txt_offset < len && txt_offset != chunk) { + if (!string_checker(buf, txt_offset, len)) + return; + snprintf(tmpstr, sizeof tmpstr, "\nAUTHORNAME:\n %s\n", + buf + txt_offset); + strlcat(credits, tmpstr, credits_len); + } + + txt_offset = read_be_u32(buf + offset + 8) + chunk; + if (txt_offset < len && txt_offset != chunk) { + if (!string_checker(buf, txt_offset, len)) + return; + snprintf(tmpstr, sizeof tmpstr, "\nSPECIALINFO:\n %s", + buf + txt_offset); + strlcat(credits, tmpstr, credits_len); + } + } +} + +/* Get the info out of the AHX module data*/ +static void process_ahx_mod(char *credits, size_t credits_len, + unsigned char *buf, size_t len) +{ + int i; + size_t offset; + char tmpstr[256]; + + if (len < 13) + return; + + offset = read_be_u16(buf + 4); + + if (offset >= len) + return; + + if (!string_checker(buf, offset, len)) + return; + + snprintf(tmpstr, sizeof tmpstr, "\nSong title: %s\n", buf + offset); + strlcat(credits, tmpstr, credits_len); + + for (i = 0; i < buf[12]; i++) { + if (!string_checker(buf, offset, len)) + break; + offset = offset + 1 + strlen((char *)buf + offset); + if (offset < len) { + snprintf(tmpstr, 256, "\n %s", buf + offset); + strlcat(credits, tmpstr, credits_len); + } + } +} + +/* Get the info out of the protracker module data*/ +static void process_ptk_mod(char *credits, size_t credits_len, int inst, + uint8_t * buf, size_t len) +{ + int i; + char tmpstr[256]; + + if (!string_checker(buf, 0, len)) + return; + + snprintf(tmpstr, 35, "\nSong title: %s", buf); + strlcat(credits, tmpstr, credits_len); + + if (inst == 31) { + if (len >= 0x43c) { + snprintf(tmpstr, sizeof tmpstr, + "\nmax positions: %d\n", buf[0x3b6]); + strlcat(credits, tmpstr, credits_len); + } + } else { + if (len >= 0x1da) { + snprintf(tmpstr, sizeof tmpstr, + "\nmax positions: %d\n", buf[0x1d6]); + strlcat(credits, tmpstr, credits_len); + } + } + + snprintf(tmpstr, sizeof tmpstr, "\nINST - NAME SIZE VOL FINE LSTART LSIZE\n"); + strlcat(credits, tmpstr, credits_len); + if (len >= (0x14 + inst * 0x1e)) { + for (i = 0; i < inst; i++) { + if (!string_checker(buf, 0x14 + i * 0x1e, len)) + break; + snprintf(tmpstr, sizeof tmpstr, "[%2d] - ", i + 1); + strlcat(credits, tmpstr, credits_len); + snprintf(tmpstr, 23, "%-23s", buf + 0x14 + (i * 0x1e)); + strlcat(credits, tmpstr, credits_len); + snprintf(tmpstr, sizeof tmpstr, + " %6d %2d %2d %6d %6d\n", + read_be_u16(buf + 42 + i * 0x1e) * 2, + buf[45 + i * 0x1e], buf[44 + i * 0x1e], + read_be_u16(buf + 46 + i * 0x1e) * 2, + read_be_u16(buf + 48 + i * 0x1e) * 2); + strlcat(credits, tmpstr, credits_len); + } + } +} + +/* Get the info out of the digibooster module data*/ +static void process_digi_mod(char *credits, size_t credits_len, + uint8_t * buf, size_t len) +{ + int i; + char tmpstr[256]; + + if (len < (642 + 0x30 * 0x1e)) + return; + + if (!string_checker(buf, 610, len)) + return; + + snprintf(tmpstr, 0x2f, "\nSong title: %s \n", buf + 610); + strlcat(credits, tmpstr, credits_len); + + snprintf(tmpstr, sizeof tmpstr, "max positions: %d\n", buf[47]); + strlcat(credits, tmpstr, credits_len); + + snprintf(tmpstr, sizeof tmpstr, "\nINST - NAME SIZE VOL FINE LSTART LSIZE\n"); + strlcat(credits, tmpstr, credits_len); + if (len >= (642 + 0x1f * 0x1e)) { + for (i = 0; i < 0x1f; i++) { + if (!string_checker(buf, 642 + i * 0x1e, len)) + break; + snprintf(tmpstr, sizeof tmpstr, "[%2d] - ", i + 1); + strlcat(credits, tmpstr, credits_len); + snprintf(tmpstr, 30, "%-30s", buf + 642 + (i * 0x1e)); + strlcat(credits, tmpstr, credits_len); + snprintf(tmpstr, sizeof tmpstr, + " %11d %2d %3d %11d %11d\n", + read_be_u32(buf + 176 + i * 4), buf[548 + i], + buf[579 + i], read_be_u32(buf + 300 + i * 4), + read_be_u32(buf + 424 + i * 4)); + strlcat(credits, tmpstr, credits_len); + } + } +} + +/* Get the info out of custom song. FIX ME, clean this function. */ +static void process_custom(char *credits, size_t credits_len, + unsigned char *buf, size_t len) +{ + char tmpstr[1024]; + unsigned char *hunk; + unsigned char *tag_table; + int hunk_size; + int table_size; + + int i; + int offset; + unsigned int x, y; + unsigned char startpattern[4] = { 0x70, 0xff, 0x4e, 0x75 }; + + if (len < 4) + return; + + if (read_be_u32(buf) != 0x000003f3) + return; + + i = find_tag(buf, 0, len, startpattern, sizeof startpattern); + if (i == -1 || (i + 12) >= len) + return; + + if (strncmp((char *)buf + i + 4, "DELIRIUM", 8) != 0 && + strncmp((char *)buf + i + 4, "EPPLAYER", 8) != 0) { + return; + } + + /* Hunk found */ + hunk = buf + i; + hunk_size = len - i; + + if (16 + i + 5 >= hunk_size) + return; + + /* Check if $VER is available */ + if (!memcmp(&hunk[16], "$VER:", 5)) { + offset = 16 + 5; + while (offset < hunk_size) { + if (memcmp(&hunk[offset], " ", 1)) + break; + offset++; + } + if (offset >= hunk_size) + return; + + if ((offset + strlen((char *)hunk + offset) + 1) > + ((size_t) hunk_size)) + return; + + snprintf(tmpstr, sizeof tmpstr, "\nVERSION:\n%s\n\n", + hunk + offset); + strlcat(credits, tmpstr, credits_len); + } + + offset = read_be_u32(hunk + 12); + if (offset < 0) { + return; + } + + tag_table = hunk + offset; + + if (tag_table >= &buf[len]) + return; + + table_size = ((int)(&buf[len] - tag_table)) / 8; + + if (table_size <= 0) + return; + + /* check all tags in this loop */ + for (i = 0; i < table_size; i += 2) { + x = read_be_u32(tag_table + 4 * i); + y = read_be_u32(tag_table + 4 * (i + 1)); + + if (!x) + break; + + switch (x) { + case 0x8000445a: + if (y >= ((unsigned int)hunk_size)) + return; + if ((y + strlen((char *)hunk + y) + 1) > + ((size_t) hunk_size)) + return; + snprintf(tmpstr, sizeof tmpstr, "\nCREDITS:\n%s\n\n", + hunk + y); + strlcat(credits, tmpstr, credits_len); + break; + default: + break; + } + } +} + +/* + * Get the info out of the Deltamusic 2 module data + */ +static void process_dm2_mod(char *credits, size_t credits_len, + unsigned char *buf, size_t len) +{ + char tmpstr[256]; + if (!string_checker(buf, 0x148, len)) + return; + snprintf(tmpstr, sizeof tmpstr, "\nRemarks:\n%s", buf + 0x148); + strlcat(credits, tmpstr, credits_len); +} + +static int process_module(char *credits, size_t credits_len, char *filename) +{ + FILE *modfile; + struct stat st; + size_t modfilelen; + unsigned char *buf; + char pre[11]; + char tmpstr[256]; + size_t rb; + + if (!(modfile = fopen(filename, "rb"))) + return 0; + + if (fstat(fileno(modfile), &st)) + return 0; + + modfilelen = st.st_size; + + if ((buf = malloc(modfilelen)) == NULL) { + fprintf(stderr, "uade: can't allocate mem in process_module()"); + fclose(modfile); + return 0; + } + + rb = 0; + while (rb < modfilelen) { + size_t ret = fread(&buf[rb], 1, modfilelen - rb, modfile); + if (ret == 0) + break; + rb += ret; + } + + fclose(modfile); + + if (rb < modfilelen) { + fprintf(stderr, "uade: song info could not read %s fully\n", + filename); + free(buf); + return 0; + } + + snprintf(tmpstr, sizeof tmpstr, "UADE2 MODINFO:\n\nFile name: %s\nFile length: %zd bytes\n", filename, modfilelen); + strlcpy(credits, tmpstr, credits_len); + + /* Get filetype in pre */ + uade_filemagic(buf, modfilelen, pre, modfilelen, filename, 0); + + snprintf(tmpstr, sizeof tmpstr, "File prefix: %s.*\n", pre); + strlcat(credits, tmpstr, credits_len); + + if (strcasecmp(pre, "CUST") == 0) { + /* CUST */ + process_custom(credits, credits_len, buf, modfilelen); + + } else if (strcasecmp(pre, "DM2") == 0) { + /* DM2 */ + process_dm2_mod(credits, credits_len, buf, modfilelen); + + } else if (strcasecmp(pre, "DIGI") == 0) { + /* DIGIBooster */ + process_digi_mod(credits, credits_len, buf, modfilelen); + + } else if ((strcasecmp(pre, "AHX") == 0) || + (strcasecmp(pre, "THX") == 0)) { + /* AHX */ + process_ahx_mod(credits, credits_len, buf, modfilelen); + + } else if ((strcasecmp(pre, "MOD15") == 0) || + (strcasecmp(pre, "MOD15_UST") == 0) || + (strcasecmp(pre, "MOD15_MST") == 0) || + (strcasecmp(pre, "MOD15_ST-IV") == 0)) { + /*MOD15 */ + process_ptk_mod(credits, credits_len, 15, buf, modfilelen); + + } else if ((strcasecmp(pre, "MOD") == 0) || + (strcasecmp(pre, "MOD_DOC") == 0) || + (strcasecmp(pre, "MOD_NTK") == 0) || + (strcasecmp(pre, "MOD_NTK1") == 0) || + (strcasecmp(pre, "MOD_NTK2") == 0) || + (strcasecmp(pre, "MOD_FLT4") == 0) || + (strcasecmp(pre, "MOD_FLT8") == 0) || + (strcasecmp(pre, "MOD_ADSC4") == 0) || + (strcasecmp(pre, "MOD_ADSC8") == 0) || + (strcasecmp(pre, "MOD_COMP") == 0) || + (strcasecmp(pre, "MOD_NTKAMP") == 0) || + (strcasecmp(pre, "PPK") == 0) || + (strcasecmp(pre, "MOD_PC") == 0) || + (strcasecmp(pre, "ICE") == 0) || + (strcasecmp(pre, "ADSC") == 0)) { + /*MOD*/ + process_ptk_mod(credits, credits_len, 31, buf, modfilelen); + } else if (strcasecmp(pre, "DL") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "UNCL", + "EART", 0x28); + } else if (strcasecmp(pre, "BSS") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "BEAT", + "HOVE", 0x1c); + } else if (strcasecmp(pre, "GRAY") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "FRED", + "GRAY", 0x10); + } else if (strcasecmp(pre, "JMF") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "J.FL", + "OGEL", 0x14); + } else if (strcasecmp(pre, "SPL") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "!SOP", + "ROL!", 0x10); + } else if (strcasecmp(pre, "HD") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "H.DA", + "VIES", 24); + } else if (strcasecmp(pre, "RIFF") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "RIFF", + "RAFF", 0x14); + } else if (strcasecmp(pre, "FP") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "F.PL", + "AYER", 0x8); + } else if (strcasecmp(pre, "CORE") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "S.PH", + "IPPS", 0x20); + } else if (strcasecmp(pre, "BDS") == 0) { + process_WTWT_mod(credits, credits_len, buf, modfilelen, "DAGL", + "ISH!", 0x14); + } + + free(buf); + + return 0; +} + +int uade_generate_song_title(char *title, size_t dstlen, + struct uade_state *state) +{ + size_t srcoffs; + size_t dstoffs; + size_t srclen; + char *format; + char *bname; + char p[64]; + char *default_format = "%F %X [%P]"; + struct uade_song *us = state->song; + struct uade_config *uc = &state->config; + + /* %A min subsong + %B cur subsong + %C max subsong + %F file base name (us->module_filename) + %P player name + %T title + %X print subsong info if more than one subsong exist + */ + + format = uc->song_title; + + if (format == NULL) + format = default_format; + + if (strcmp("default", format) == 0) + format = default_format; + + if ((srclen = strlen(format)) == 0) { + fprintf(stderr, "Warning: empty song_title format string.\n"); + return 1; + } + + if (dstlen == 0) + return 1; + + if (strlen(us->module_filename) == 0) + return 1; + + bname = xbasename(us->module_filename); + + p[0] = 0; + if (us->formatname[0] == 0) { + if (us->playername[0] == 0) { + strlcpy(p, "Custom", sizeof p); + } else { + strlcpy(p, us->playername, sizeof p); + } + } else { + if (strncmp(us->formatname, "type: ", 6) == 0) { + strlcpy(p, us->formatname + 6, sizeof p); + } else { + strlcpy(p, us->formatname, sizeof p); + } + } + + srcoffs = dstoffs = 0; + + title[0] = 0; + + while (dstoffs < dstlen) { + char c; + if (srcoffs >= srclen) + break; + + if ((c = format[srcoffs]) == 0) + break; + + if (c != '%') { + title[dstoffs++] = format[srcoffs++]; + } else { + size_t inc; + char *dat = NULL; + char tmp[32]; + + if ((srcoffs + 1) >= srclen) { + fprintf(stderr, "Error: no identifier given in song title format: %s\n", format); + title[dstoffs] = 0; + return 1; + } + + c = format[srcoffs + 1]; + + switch (c) { + case 'A': + snprintf(tmp, sizeof tmp, "%d", + us->min_subsong); + dat = tmp; + break; + case 'B': + snprintf(tmp, sizeof tmp, "%d", + us->cur_subsong); + dat = tmp; + break; + case 'C': + snprintf(tmp, sizeof tmp, "%d", + us->max_subsong); + dat = tmp; + break; + case 'F': + dat = bname; + break; + case 'P': + dat = p; + break; + case 'T': + dat = us->modulename; + if (strcmp("<no songtitle>", dat) == 0) + dat[0] = 0; + if (dat[0] == 0) + dat = bname; + break; + case 'X': + if (us->min_subsong == us->max_subsong) { + tmp[0] = 0; + } else { + snprintf(tmp, sizeof tmp, "(%d/%d)", + us->cur_subsong, + us->max_subsong); + } + dat = tmp; + break; + default: + fprintf(stderr, + "Unknown identifier %%%c in song_title format: %s\n", + c, format); + title[dstoffs] = 0; + return 1; + } + inc = strlcpy(&title[dstoffs], dat, dstlen - dstoffs); + srcoffs += 2; + dstoffs += inc; + } + } + + if (dstoffs < dstlen) + title[dstoffs] = 0; + else + title[dstlen - 1] = 0; + + return 0; +} + +/* Returns zero on success, non-zero otherwise. */ +int uade_song_info(char *info, size_t maxlen, char *filename, + enum song_info_type type) +{ + switch (type) { + case UADE_MODULE_INFO: + return process_module(info, maxlen, filename); + case UADE_HEX_DUMP_INFO: + return hexdump(info, maxlen, filename, 2048); + default: + fprintf(stderr, "Illegal info requested.\n"); + exit(-1); + } + return 0; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/songinfo.h b/plugins/uade2/uade-2.13/src/frontends/common/songinfo.h new file mode 100644 index 00000000..e0abd059 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/songinfo.h @@ -0,0 +1,19 @@ +#ifndef _UADE_SONG_INFO_ +#define _UADE_SONG_INFO_ + +#include <stdio.h> + +#include "uadestate.h" + +enum song_info_type { + UADE_MODULE_INFO = 0, + UADE_HEX_DUMP_INFO, + UADE_NUMBER_OF_INFOS +}; + +int uade_generate_song_title(char *title, size_t dstlen, + struct uade_state *state); +int uade_song_info(char *info, size_t maxlen, char *filename, + enum song_info_type type); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/support.c b/plugins/uade2/uade-2.13/src/frontends/common/support.c new file mode 100644 index 00000000..852d4c0e --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/support.c @@ -0,0 +1,183 @@ +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> + +#include "support.h" +#include "ossupport.h" + +/* Zero terminate the current word. Returns -1 is *s == 0 or the next word + does not exist. Otherwise returns offset to the beginning of next word. */ +int skip_and_terminate_word(char *s, int i) +{ + i = skipnws(s, i); + if (i < 0) + return -1; + + /* Zero terminate word */ + s[i] = 0; + + i = skipws(s, i + 1); + if (i < 0) + return -1; + + return i; +} + +char *xbasename(const char *s) +{ + char *t = strrchr(s, (int) '/'); + if (t == NULL) { + t = (char *) s; + } else { + t++; + } + return t; +} + +/* + * Split a string into 2 whitespace separated fields returned in "key" and + * "value". If more than 2 fields are found, they are cut off by zero + * terminating "key" and "value" inside the string. If "value" is not found, + * *value is set to NULL. If "key" is not found, *key is set to NULL. + * If something is found, both *key and *value become pointers inside the + * string s. + * + * Return values: + * - 0 if neither "key" nor "value" is found + * - 1 if only "key" is found + * - 2 if both "key" and "value" are found + */ +int get_two_ws_separated_fields(char **key, char **value, char *s) +{ + int i; + + *key = NULL; + *value = NULL; + + i = skipws(s, 0); /* Skip initial whitespace */ + + if (i < 0) + return 0; /* We got nothing */ + + *key = s + i; + + i = skip_and_terminate_word(s, i); + + if (i < 0) + return 1; /* We got a "key", but not a "value" */ + + *value = s + i; + + skip_and_terminate_word(s, i); + + return 2; /* We got both a "key" and a "value" */ +} + +/* + * Skip whitespace characters in string starting from offset i. Returns offset + * j >= i as the next non-whitespace character offset, or -1 if non-whitespace + * are not found. + */ +int skipws(const char *s, int i) +{ + while (isspace(s[i])) + i++; + + if (s[i] == 0) + return -1; + + return i; +} + +/* + * Skip non-whitespace characters in string starting from offset i. Returns + * offset j >= i as the next whitespace character offset, or -1 if no + * whitespace if found. + */ +int skipnws(const char *s, int i) +{ + while (!isspace(s[i]) && s[i] != 0) + i++; + + if (s[i] == 0) + return -1; + + return i; +} + + +/* Split line with respect to white space. */ +char **read_and_split_lines(size_t *nitems, size_t *lineno, FILE *f, + const char *delim) +{ + char line[UADE_LINESIZE], templine[UADE_LINESIZE]; + char **items = NULL; + size_t pos; + char *sp, *s; + + *nitems = 0; + + while (xfgets(line, sizeof line, f) != NULL) { + + if (lineno != NULL) + (*lineno)++; + + /* Skip, if a comment line */ + if (line[0] == '#') + continue; + + /* strsep() modifies line that it touches, so we make a copy + of it, and then count the number of items on the line */ + strlcpy(templine, line, sizeof(templine)); + sp = templine; + while ((s = strsep(&sp, delim)) != NULL) { + if (*s == 0) + continue; + (*nitems)++; + } + + if (*nitems > 0) + break; + } + + if (*nitems == 0) + return NULL; + + if ((items = malloc(sizeof(items[0]) * (*nitems + 1))) == NULL) + uadeerror("No memory for nws items.\n"); + + sp = line; + pos = 0; + while ((s = strsep(&sp, delim)) != NULL) { + if (*s == 0) + continue; + + if ((items[pos] = strdup(s)) == NULL) + uadeerror("No memory for an nws item.\n"); + + pos++; + } + items[pos] = NULL; + assert(pos == *nitems); + + return items; +} + + +char *xfgets(char *s, int size, FILE *stream) +{ + char *ret; + + while (1) { + ret = fgets(s, size, stream); + if (ret != NULL) + break; + + if (feof(stream)) + break; + } + + return ret; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/support.h b/plugins/uade2/uade-2.13/src/frontends/common/support.h new file mode 100644 index 00000000..1b32fe92 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/support.h @@ -0,0 +1,31 @@ +#ifndef _UADE_SUPPORT_H_ +#define _UADE_SUPPORT_H_ + +#include <stdio.h> + +#define UADE_LINESIZE 1024 + +#define uadeerror(fmt, args...) do { fprintf(stderr, "uade: " fmt, ## args); exit(1); } while (0) + +#define MAX(x, y) ((x) >= (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + + +char *xbasename(const char *path); + +int get_two_ws_separated_fields(char **key, char **value, char *s); + +int skipnws(const char *s, int i); + +int skip_and_terminate_word(char *s, int i); + +int skipws(const char *s, int i); + +char **read_and_split_lines(size_t *nitems, size_t *lineno, FILE *f, + const char *delim); + +/* Same as fgets(), but guarantees that feof() or ferror() have happened + when xfgets() returns NULL */ +char *xfgets(char *s, int size, FILE *stream); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/uadeconf.c b/plugins/uade2/uade-2.13/src/frontends/common/uadeconf.c new file mode 100644 index 00000000..17300340 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/uadeconf.c @@ -0,0 +1,888 @@ +/* Handle uade.conf file + + Copyright (C) 2005 Heikki Orsila <heikki.orsila@iki.fi> + + This source code module is dual licensed under GPL and Public Domain. + Hence you may use _this_ module (not another code module) in any way you + want in your projects. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "ossupport.h" +#include "uadeconf.h" +#include "uadeconfig.h" +#include "amigafilter.h" +#include "uadeconstants.h" +#include "songdb.h" +#include "uadeutils.h" +#include "support.h" + +static int uade_set_silence_timeout(struct uade_config *uc, const char *value); +static int uade_set_subsong_timeout(struct uade_config *uc, const char *value); +static int uade_set_timeout(struct uade_config *uc, const char *value); + + +struct uade_conf_opts { + char *str; + int l; + enum uade_option e; +}; + +/* List of uade.conf options. The list includes option name, minimum + string match length for the option name and its enum code. */ +static const struct uade_conf_opts uadeconfopts[] = { + {.str = "action_keys", .l = 2, .e = UC_ACTION_KEYS}, + {.str = "ao_option", .l = 2, .e = UC_AO_OPTION}, + {.str = "buffer_time", .l = 1, .e = UC_BUFFER_TIME}, + {.str = "cygwin", .l = 1, .e = UC_CYGWIN_DRIVE_WORKAROUND}, + {.str = "detect_format_by_detection", .l = 18, .e = UC_CONTENT_DETECTION}, + {.str = "disable_timeout", .l = 1, .e = UC_DISABLE_TIMEOUTS}, + {.str = "enable_timeout", .l = 2, .e = UC_ENABLE_TIMEOUTS}, + {.str = "ep_option", .l = 2, .e = UC_EAGLEPLAYER_OPTION}, + {.str = "filter_type", .l = 2, .e = UC_FILTER_TYPE}, + {.str = "force_led_off", .l = 12, .e = UC_FORCE_LED_OFF}, + {.str = "force_led_on", .l = 12, .e = UC_FORCE_LED_ON}, + {.str = "force_led", .l = 9, .e = UC_FORCE_LED}, + {.str = "frequency", .l = 2, .e = UC_FREQUENCY}, + {.str = "gain", .l = 1, .e = UC_GAIN}, + {.str = "headphones", .l = 11, .e = UC_HEADPHONES}, + {.str = "headphones2", .l = 11, .e = UC_HEADPHONES2}, + {.str = "headphone", .l = 11, .e = UC_HEADPHONES}, + {.str = "ignore_player_check", .l = 2, .e = UC_IGNORE_PLAYER_CHECK}, + {.str = "interpolator", .l = 2, .e = UC_RESAMPLER}, + {.str = "magic_detection", .l = 1, .e = UC_CONTENT_DETECTION}, + {.str = "no_ep_end_detect", .l = 4, .e = UC_NO_EP_END}, + {.str = "no_filter", .l = 4, .e = UC_NO_FILTER}, + {.str = "no_song_end", .l = 4, .e = UC_NO_EP_END}, + {.str = "normalise", .l = 1, .e = UC_NORMALISE}, + {.str = "ntsc", .l = 2, .e = UC_NTSC}, + {.str = "one_subsong", .l = 1, .e = UC_ONE_SUBSONG}, + {.str = "pal", .l = 3, .e = UC_PAL}, + {.str = "panning_value", .l = 3, .e = UC_PANNING_VALUE}, + {.str = "random_play", .l = 3, .e = UC_RANDOM_PLAY}, + {.str = "recursive_mode", .l = 3, .e = UC_RECURSIVE_MODE}, + {.str = "resampler", .l = 3, .e = UC_RESAMPLER}, + {.str = "silence_timeout_value", .l = 2, .e = UC_SILENCE_TIMEOUT_VALUE}, + {.str = "song_title", .l = 2, .e = UC_SONG_TITLE}, + {.str = "speed_hack", .l = 2, .e = UC_SPEED_HACK}, + {.str = "subsong_timeout_value", .l = 2, .e = UC_SUBSONG_TIMEOUT_VALUE}, + {.str = "timeout_value", .l = 1, .e = UC_TIMEOUT_VALUE}, + {.str = "verbose", .l = 1, .e = UC_VERBOSE}, + {.str = NULL} /* END OF LIST */ +}; + + +/* Map an uade.conf option to an enum */ +static enum uade_option map_str_to_option(const char *key) +{ + size_t i; + + for (i = 0; uadeconfopts[i].str != NULL; i++) { + if (strncmp(key, uadeconfopts[i].str, uadeconfopts[i].l) == 0) + return uadeconfopts[i].e; + } + + return 0; +} + +/* The function sets the default options. No *_set variables are set because + we don't want any option to become mergeable by default. See + uade_merge_configs(). */ +void uade_config_set_defaults(struct uade_config *uc) +{ + memset(uc, 0, sizeof(*uc)); + uc->action_keys = 1; + strlcpy(uc->basedir.name, UADE_CONFIG_BASE_DIR, sizeof uc->basedir.name); + uade_set_filter_type(uc, NULL); + uc->frequency = UADE_DEFAULT_FREQUENCY; + uc->gain = 1.0; + uc->panning = 0.7; + uc->silence_timeout = 20; + uc->subsong_timeout = 512; + uc->timeout = -1; + uc->use_timeouts = 1; +} + +double uade_convert_to_double(const char *value, double def, double low, + double high, const char *type) +{ + char *endptr, *newvalue; + char newseparator; + double v; + + if (value == NULL) + return def; + + v = strtod(value, &endptr); + + /* Decimal separator conversion, if needed */ + if (*endptr == ',' || *endptr == '.') { + newvalue = strdup(value); + if (newvalue == NULL) + uade_error("Out of memory\n"); + + newseparator = (*endptr == ',') ? '.' : ','; + + newvalue[(intptr_t) endptr - (intptr_t) value] = newseparator; + + v = strtod(newvalue, &endptr); + free(newvalue); + } + + if (*endptr != 0 || v < low || v > high) { + fprintf(stderr, "Invalid %s value: %s\n", type, value); + v = def; + } + + return v; +} + +static void uade_add_ep_option(struct uade_ep_options *opts, const char *s) +{ + size_t freespace = sizeof(opts->o) - opts->s; + + if (strlcpy(&opts->o[opts->s], s, freespace) >= freespace) { + fprintf(stderr, "Warning: uade eagleplayer option overflow: %s\n", s); + return; + } + + opts->s += strlen(s) + 1; +} + +static int handle_attributes(struct uade_config *uc, struct uade_song *us, + char *playername, size_t playernamelen, + int flags, struct uade_attribute *attributelist) +{ + struct uade_attribute *a; + size_t i; + + for (i = 0; epconf[i].s != NULL; i++) { + + if (epconf[i].o == 0) + continue; + + if ((flags & epconf[i].e) == 0) + continue; + + uade_set_config_option(uc, epconf[i].o, epconf[i].c); + } + + if (flags & ES_NEVER_ENDS) + fprintf(stderr, "uade: ES_NEVER_ENDS is not implemented. What should it do?\n"); + + if (flags & ES_REJECT) + return -1; + + a = attributelist; + + while (a != NULL) { + + switch (a->type) { + case ES_EP_OPTION: + if (uc->verbose) + fprintf(stderr, "Using eagleplayer option %s\n", a->s); + uade_add_ep_option(&us->ep_options, a->s); + break; + + case ES_GAIN: + uade_set_config_option(uc, UC_GAIN, a->s); + break; + + case ES_RESAMPLER: + uade_set_config_option(uc, UC_RESAMPLER, a->s); + break; + + case ES_PANNING: + uade_set_config_option(uc, UC_PANNING_VALUE, a->s); + break; + + case ES_PLAYER: + if (playername) { + snprintf(playername, playernamelen, "%s/players/%s", uc->basedir.name, a->s); + } else { + fprintf(stderr, "Error: attribute handling was given playername == NULL.\n"); + } + break; + + case ES_SILENCE_TIMEOUT: + uade_set_config_option(uc, UC_SILENCE_TIMEOUT_VALUE, a->s); + break; + + case ES_SUBSONGS: + fprintf(stderr, "Subsongs not implemented.\n"); + break; + + case ES_SUBSONG_TIMEOUT: + uade_set_config_option(uc, UC_SUBSONG_TIMEOUT_VALUE, a->s); + break; + + case ES_TIMEOUT: + uade_set_config_option(uc, UC_TIMEOUT_VALUE, a->s); + break; + + default: + fprintf(stderr, "Unknown song attribute integer: 0x%x\n", a->type); + break; + } + + a = a->next; + } + + return 0; +} + +int uade_set_song_attributes(struct uade_state *state, + char *playername, size_t playernamelen) +{ + struct uade_song *us = state->song; + struct uade_config *uc = &state->config; + + if (us->normalisation) + uade_set_config_option(uc, UC_NORMALISE, us->normalisation); + + return handle_attributes(uc, us, playername, playernamelen, + us->flags, us->songattributes); +} + +int uade_load_config(struct uade_config *uc, const char *filename) +{ + char line[256]; + FILE *f; + char *key, *value; + int linenumber = 0; + enum uade_option opt; + + if ((f = fopen(filename, "r")) == NULL) + return 0; + + uade_config_set_defaults(uc); + + while (xfgets(line, sizeof(line), f) != NULL) { + linenumber++; + + /* Skip comment lines */ + if (line[0] == '#') + continue; + + if (!get_two_ws_separated_fields(&key, &value, line)) + continue; /* Skip an empty line */ + + opt = map_str_to_option(key); + + if (opt) { + uade_set_config_option(uc, opt, value); + } else { + fprintf(stderr, "Unknown config key in %s on line %d: %s\n", filename, linenumber, key); + } + } + + fclose(f); + return 1; +} + +int uade_load_initial_config(char *uadeconfname, size_t maxlen, + struct uade_config *uc, struct uade_config *ucbase) +{ + int loaded; + char *home; + + assert(maxlen > 0); + uadeconfname[0] = 0; + + uade_config_set_defaults(uc); + + loaded = 0; + + /* First try to load from forced base dir (testing mode) */ + if (ucbase != NULL && ucbase->basedir_set) { + snprintf(uadeconfname, maxlen, "%s/uade.conf", + ucbase->basedir.name); + loaded = uade_load_config(uc, uadeconfname); + } + + home = uade_open_create_home(); + + /* Second, try to load config from ~/.uade2/uade.conf */ + if (loaded == 0 && home != NULL) { + snprintf(uadeconfname, maxlen, "%s/.uade2/uade.conf", home); + loaded = uade_load_config(uc, uadeconfname); + } + + /* Third, try to load from install path */ + if (loaded == 0) { + snprintf(uadeconfname, maxlen, "%s/uade.conf", + uc->basedir.name); + loaded = uade_load_config(uc, uadeconfname); + } + + return loaded; +} + +int uade_load_initial_song_conf(char *songconfname, size_t maxlen, + struct uade_config *uc, + struct uade_config *ucbase) +{ + int loaded = 0; + char *home; + + assert(maxlen > 0); + songconfname[0] = 0; + + /* Used for testing */ + if (ucbase != NULL && ucbase->basedir_set) { + snprintf(songconfname, maxlen, "%s/song.conf", + ucbase->basedir.name); + loaded = uade_read_song_conf(songconfname); + } + + /* Avoid unwanted home directory creation for test mode */ + if (loaded) + return loaded; + + home = uade_open_create_home(); + + /* Try to load from home dir */ + if (loaded == 0 && home != NULL) { + snprintf(songconfname, maxlen, "%s/.uade2/song.conf", home); + loaded = uade_read_song_conf(songconfname); + } + + /* No? Try install path */ + if (loaded == 0) { + snprintf(songconfname, maxlen, "%s/song.conf", + uc->basedir.name); + loaded = uade_read_song_conf(songconfname); + } + + return loaded; +} + +void uade_merge_configs(struct uade_config *ucd, const struct uade_config *ucs) +{ +#define MERGE_OPTION(y) do { if (ucs->y##_set) ucd->y = ucs->y; } while (0) + + MERGE_OPTION(action_keys); + MERGE_OPTION(ao_options); + MERGE_OPTION(basedir); + MERGE_OPTION(buffer_time); + MERGE_OPTION(content_detection); + MERGE_OPTION(cygwin_drive_workaround); + MERGE_OPTION(ep_options); + MERGE_OPTION(filter_type); + MERGE_OPTION(frequency); + MERGE_OPTION(gain); + MERGE_OPTION(gain_enable); + MERGE_OPTION(headphones); + MERGE_OPTION(headphones2); + MERGE_OPTION(ignore_player_check); + MERGE_OPTION(led_forced); + MERGE_OPTION(led_state); + MERGE_OPTION(no_ep_end); + MERGE_OPTION(no_filter); + MERGE_OPTION(no_postprocessing); + + /* Special merge -> don't use MERGE_OPTION macro */ + if (ucs->normalise_set && ucs->normalise) { + ucd->normalise = 1; + if (ucs->normalise_parameter != NULL) + ucd->normalise_parameter = ucs->normalise_parameter; + } + + MERGE_OPTION(one_subsong); + MERGE_OPTION(panning); + MERGE_OPTION(panning_enable); + MERGE_OPTION(random_play); + MERGE_OPTION(recursive_mode); + MERGE_OPTION(resampler); + MERGE_OPTION(silence_timeout); + MERGE_OPTION(song_title); + MERGE_OPTION(speed_hack); + MERGE_OPTION(subsong_timeout); + + MERGE_OPTION(timeout); + MERGE_OPTION(use_timeouts); + if (ucs->timeout_set) { + ucd->use_timeouts = 1; + ucd->use_timeouts_set = 1; + } + + MERGE_OPTION(use_text_scope); + MERGE_OPTION(use_ntsc); + MERGE_OPTION(verbose); +} + +char *uade_open_create_home(void) +{ + /* Create ~/.uade2 directory if it does not exist */ + char *home = getenv("HOME"); + if (home) { + char name[PATH_MAX]; + struct stat st; + snprintf(name, sizeof name, "%s/.uade2", home); + if (stat(name, &st) != 0) + mkdir(name, S_IRUSR | S_IWUSR | S_IXUSR); + } + + return home; +} + +int uade_parse_subsongs(int **subsongs, char *option) +{ + char substr[256]; + char *sp, *str; + size_t pos; + int nsubsongs; + + nsubsongs = 0; + *subsongs = NULL; + + if (strlcpy(substr, option, sizeof subsongs) >= sizeof subsongs) { + fprintf(stderr, "Too long a subsong option: %s\n", option); + return -1; + } + + sp = substr; + while ((str = strsep(&sp, ",")) != NULL) { + if (*str == 0) + continue; + nsubsongs++; + } + + *subsongs = malloc((nsubsongs + 1) * sizeof((*subsongs)[0])); + if (*subsongs == NULL) { + fprintf(stderr, "No memory for subsongs.\n"); + return -1; + } + + strlcpy(substr, option, sizeof subsongs); + + pos = 0; + sp = substr; + while ((str = strsep(&sp, ",")) != NULL) { + if (*str == 0) + continue; + (*subsongs)[pos] = atoi(str); + pos++; + } + + (*subsongs)[pos] = -1; + assert(pos == nsubsongs); + + return nsubsongs; +} + +void uade_set_effects(struct uade_state *state) +{ + struct uade_effect *effects = &state->effects; + struct uade_config *uc = &state->config; + + uade_effect_set_defaults(effects); + + if (uc->no_postprocessing) + uade_effect_disable(effects, UADE_EFFECT_ALLOW); + + if (uc->gain_enable) { + uade_effect_gain_set_amount(effects, uc->gain); + uade_effect_enable(effects, UADE_EFFECT_GAIN); + } + + if (uc->headphones) + uade_effect_enable(effects, UADE_EFFECT_HEADPHONES); + + if (uc->headphones2) + uade_effect_enable(effects, UADE_EFFECT_HEADPHONES2); + + if (uc->normalise) { + uade_effect_normalise_unserialise(uc->normalise_parameter); + uade_effect_enable(effects, UADE_EFFECT_NORMALISE); + } + + if (uc->panning_enable) { + uade_effect_pan_set_amount(effects, uc->panning); + uade_effect_enable(effects, UADE_EFFECT_PAN); + } + + uade_effect_set_sample_rate(effects, uc->frequency); +} + +void uade_set_config_option(struct uade_config *uc, enum uade_option opt, + const char *value) +{ + char *endptr; + long x; + +#define SET_OPTION(opt, value) do { uc->opt = (value); uc->opt##_set = 1; } while (0) + + switch (opt) { + case UC_ACTION_KEYS: + if (value != NULL) { + uc->action_keys_set = 1; + if (!strcasecmp(value, "on") || !strcmp(value, "1")) { + uc->action_keys = 1; + } else if (!strcasecmp(value, "off") || + !strcmp(value, "0")) { + uc->action_keys = 0; + } else { + fprintf(stderr, + "uade.conf: Unknown setting for action keys: %s\n", + value); + } + } + break; + + case UC_AO_OPTION: + strlcat(uc->ao_options.o, value, sizeof uc->ao_options.o); + strlcat(uc->ao_options.o, "\n", sizeof uc->ao_options.o); + uc->ao_options_set = 1; + break; + + case UC_BASE_DIR: + if (value != NULL) { + strlcpy(uc->basedir.name, value, + sizeof uc->basedir.name); + uc->basedir_set = 1; + } else { + fprintf(stderr, "uade: Passed NULL to UC_BASE_DIR.\n"); + } + break; + + case UC_BUFFER_TIME: + if (value != NULL) { + uc->buffer_time_set = 1; + uc->buffer_time = strtol(value, &endptr, 10); + if (uc->buffer_time <= 0 || *endptr != 0) { + fprintf(stderr, "Invalid buffer_time: %s\n", + value); + uc->buffer_time = 0; + } + } else { + fprintf(stderr, + "uade: Passed NULL to UC_BUFFER_TIME.\n"); + } + break; + + case UC_CONTENT_DETECTION: + SET_OPTION(content_detection, 1); + break; + + case UC_CYGWIN_DRIVE_WORKAROUND: + SET_OPTION(cygwin_drive_workaround, 1); + break; + + case UC_DISABLE_TIMEOUTS: + SET_OPTION(use_timeouts, 0); + break; + + case UC_ENABLE_TIMEOUTS: + SET_OPTION(use_timeouts, 1); + break; + + case UC_EAGLEPLAYER_OPTION: + if (value != NULL) { + uade_add_ep_option(&uc->ep_options, value); + uc->ep_options_set = 1; + } else { + fprintf(stderr, + "uade: Passed NULL to UC_EAGLEPLAYER_OPTION.\n"); + } + break; + + case UC_FILTER_TYPE: + SET_OPTION(no_filter, 0); + + if (value != NULL) { + if (strcasecmp(value, "none") != 0) { + /* Filter != NONE */ + uade_set_filter_type(uc, value); + uc->filter_type_set = 1; + } else { + /* Filter == NONE */ + uc->no_filter = 1; + } + } + break; + + case UC_FORCE_LED: + if (value == NULL) { + fprintf(stderr, "uade: UC_FORCE_LED value is NULL\n"); + break; + } + if (strcasecmp(value, "off") == 0 || strcmp(value, "0") == 0) { + uc->led_state = 0; + } else if (strcasecmp(value, "on") == 0 + || strcmp(value, "1") == 0) { + uc->led_state = 1; + } else { + fprintf(stderr, "Unknown force led argument: %s\n", + value); + break; + } + uc->led_state_set = 1; + + SET_OPTION(led_forced, 1); + break; + + case UC_FORCE_LED_OFF: + SET_OPTION(led_forced, 1); + SET_OPTION(led_state, 0); + break; + + case UC_FORCE_LED_ON: + SET_OPTION(led_forced, 1); + SET_OPTION(led_state, 1); + break; + + case UC_FREQUENCY: + if (value == NULL) { + fprintf(stderr, "uade: UC_FREQUENCY value is NULL\n"); + break; + } + x = strtol(value, &endptr, 10); + if (*endptr != 0) { + fprintf(stderr, "Invalid frequency number: %s\n", + value); + break; + } + /* The upper bound is NTSC Amigas bus freq */ + if (x < 1 || x > 3579545) { + fprintf(stderr, "Frequency out of bounds: %ld\n", x); + x = UADE_DEFAULT_FREQUENCY; + } + SET_OPTION(frequency, x); + break; + + case UC_GAIN: + if (value == NULL) { + fprintf(stderr, "uade: UC_GAIN value is NULL\n"); + break; + } + SET_OPTION(gain_enable, 1); + SET_OPTION(gain, uade_convert_to_double(value, 1.0, 0.0, 128.0, "gain")); + break; + + case UC_HEADPHONES: + SET_OPTION(headphones, 1); + break; + + case UC_HEADPHONES2: + SET_OPTION(headphones2, 1); + break; + + case UC_IGNORE_PLAYER_CHECK: + SET_OPTION(ignore_player_check, 1); + break; + + case UC_RESAMPLER: + if (value == NULL) { + fprintf(stderr, "uade.conf: No resampler given.\n"); + break; + } + uc->resampler = strdup(value); + if (uc->resampler != NULL) { + uc->resampler_set = 1; + } else { + fprintf(stderr, "uade.conf: no memory for resampler.\n"); + } + break; + + case UC_NO_EP_END: + SET_OPTION(no_ep_end, 1); + break; + + case UC_NO_FILTER: + SET_OPTION(no_filter, 1); + break; + + case UC_NO_HEADPHONES: + SET_OPTION(headphones, 0); + SET_OPTION(headphones2, 0); + break; + + case UC_NO_PANNING: + SET_OPTION(panning_enable, 0); + break; + + case UC_NO_POSTPROCESSING: + SET_OPTION(no_postprocessing, 1); + break; + + case UC_NORMALISE: + if (value == NULL) { + fprintf(stderr, "uade: UC_NORMALISE is NULL\n"); + break; + } + SET_OPTION(normalise, 1); + uc->normalise_parameter = (char *) value; + break; + + case UC_NTSC: + SET_OPTION(use_ntsc, 1); + break; + + case UC_ONE_SUBSONG: + SET_OPTION(one_subsong, 1); + break; + + case UC_PAL: + SET_OPTION(use_ntsc, 0); + break; + + case UC_PANNING_VALUE: + if (value == NULL) { + fprintf(stderr, "uade: UC_PANNING_VALUE is NULL\n"); + break; + } + SET_OPTION(panning_enable, 1); + SET_OPTION(panning, uade_convert_to_double(value, 0.0, 0.0, 2.0, "panning")); + break; + + case UC_RANDOM_PLAY: + SET_OPTION(random_play, 1); + break; + + case UC_RECURSIVE_MODE: + SET_OPTION(recursive_mode, 1); + break; + + case UC_SILENCE_TIMEOUT_VALUE: + if (value == NULL) { + fprintf(stderr, + "uade: UC_SILENCE_TIMEOUT_VALUE is NULL\n"); + break; + } + uade_set_silence_timeout(uc, value); + break; + + case UC_SONG_TITLE: + if (value == NULL) { + fprintf(stderr, "uade: No song_title format given.\n"); + break; + } + if ((uc->song_title = strdup(value)) == NULL) { + fprintf(stderr, "No memory for song title format\n"); + } else { + uc->song_title_set = 1; + } + break; + + case UC_SPEED_HACK: + SET_OPTION(speed_hack, 1); + break; + + case UC_SUBSONG_TIMEOUT_VALUE: + if (value == NULL) { + fprintf(stderr, + "uade: UC_SUBSONG_TIMEOUT_VALUE is NULL\n"); + break; + } + uade_set_subsong_timeout(uc, value); + break; + + case UC_TIMEOUT_VALUE: + if (value == NULL) { + fprintf(stderr, "uade: UC_TIMEOUT_VALUE is NULL\n"); + break; + } + uade_set_timeout(uc, value); + break; + + case UC_USE_TEXT_SCOPE: + SET_OPTION(use_text_scope, 1); + break; + + case UC_VERBOSE: + SET_OPTION(verbose, 1); + break; + + default: + fprintf(stderr, "uade_set_config_option(): unknown enum: %d\n", + opt); + exit(1); + } +} + +void uade_set_ep_attributes(struct uade_state *state) +{ + handle_attributes(&state->config, state->song, NULL, 0, state->ep->flags, state->ep->attributelist); +} + +void uade_set_filter_type(struct uade_config *uc, const char *model) +{ + uc->filter_type = FILTER_MODEL_A500; + + if (model == NULL) + return; + + /* a500 and a500e are the same */ + if (strncasecmp(model, "a500", 4) == 0) { + uc->filter_type = FILTER_MODEL_A500; + + /* a1200 and a1200e are the same */ + } else if (strncasecmp(model, "a1200", 5) == 0) { + uc->filter_type = FILTER_MODEL_A1200; + + } else { + fprintf(stderr, "Unknown filter model: %s\n", model); + } +} + +static int uade_set_silence_timeout(struct uade_config *uc, const char *value) +{ + char *endptr; + int t; + if (value == NULL) { + return -1; + } + t = strtol(value, &endptr, 10); + if (*endptr != 0 || t < -1) { + fprintf(stderr, "Invalid silence timeout value: %s\n", value); + return -1; + } + uc->silence_timeout = t; + uc->silence_timeout_set = 1; + return 0; +} + +static int uade_set_subsong_timeout(struct uade_config *uc, const char *value) +{ + char *endptr; + int t; + if (value == NULL) { + return -1; + } + t = strtol(value, &endptr, 10); + if (*endptr != 0 || t < -1) { + fprintf(stderr, "Invalid subsong timeout value: %s\n", value); + return -1; + } + uc->subsong_timeout = t; + uc->subsong_timeout_set = 1; + return 0; +} + +static int uade_set_timeout(struct uade_config *uc, const char *value) +{ + char *endptr; + int t; + if (value == NULL) { + return -1; + } + t = strtol(value, &endptr, 10); + if (*endptr != 0 || t < -1) { + fprintf(stderr, "Invalid timeout value: %s\n", value); + return -1; + } + uc->timeout = t; + uc->timeout_set = 1; + return 0; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/uadeconf.h b/plugins/uade2/uade-2.13/src/frontends/common/uadeconf.h new file mode 100644 index 00000000..62b11ef9 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/uadeconf.h @@ -0,0 +1,27 @@ +#ifndef _UADE_FRONTEND_CONFIG_H_ +#define _UADE_FRONTEND_CONFIG_H_ + +#include <uadestate.h> + +void uade_config_set_defaults(struct uade_config *uc); +double uade_convert_to_double(const char *value, double def, + double low, double high, const char *type); +int uade_load_config(struct uade_config *uc, const char *filename); +int uade_load_initial_config(char *uadeconfname, size_t maxlen, + struct uade_config *uc, + struct uade_config *ucbase); +int uade_load_initial_song_conf(char *songconfname, size_t maxlen, + struct uade_config *uc, + struct uade_config *ucbase); +void uade_merge_configs(struct uade_config *ucd, const struct uade_config *ucs); +char *uade_open_create_home(void); +int uade_parse_subsongs(int **subsongs, char *option); +void uade_set_config_option(struct uade_config *uc, enum uade_option opt, + const char *value); +void uade_set_effects(struct uade_state *state); +void uade_set_ep_attributes(struct uade_state *state); +int uade_set_song_attributes(struct uade_state *state, char *playername, + size_t playernamelen); +void uade_set_filter_type(struct uade_config *uc, const char *value); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/uadeconfstructure.h b/plugins/uade2/uade-2.13/src/frontends/common/uadeconfstructure.h new file mode 100644 index 00000000..d6cff1e0 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/uadeconfstructure.h @@ -0,0 +1,139 @@ +#ifndef _UADECONF_STRUCTURE_H_ +#define _UADECONF_STRUCTURE_H_ + +#include <limits.h> + +enum uade_option { + UC_ACTION_KEYS = 0x1000, + UC_AO_OPTION, + UC_BASE_DIR, + UC_BUFFER_TIME, + UC_CONTENT_DETECTION, + UC_CYGWIN_DRIVE_WORKAROUND, + UC_DISABLE_TIMEOUTS, + UC_ENABLE_TIMEOUTS, + UC_EAGLEPLAYER_OPTION, + UC_FILTER_TYPE, + UC_FORCE_LED_OFF, + UC_FORCE_LED_ON, + UC_FORCE_LED, + UC_FREQUENCY, + UC_GAIN, + UC_HEADPHONES, + UC_HEADPHONES2, + UC_IGNORE_PLAYER_CHECK, + UC_NO_FILTER, + UC_NO_HEADPHONES, + UC_NO_PANNING, + UC_NO_POSTPROCESSING, + UC_NO_EP_END, + UC_NORMALISE, + UC_NTSC, + UC_ONE_SUBSONG, + UC_PAL, + UC_PANNING_VALUE, + UC_RANDOM_PLAY, + UC_RECURSIVE_MODE, + UC_RESAMPLER, + UC_SILENCE_TIMEOUT_VALUE, + UC_SONG_TITLE, + UC_SPEED_HACK, + UC_SUBSONG_TIMEOUT_VALUE, + UC_TIMEOUT_VALUE, + UC_USE_TEXT_SCOPE, + UC_VERBOSE +}; + +struct uade_dir { + char name[PATH_MAX]; +}; + +struct uade_ep_options { + char o[256]; + size_t s; +}; + +struct uade_ao_options { + char o[256]; +}; + +#define UADE_CHAR_CONFIG(x) char x; char x##_set; +#define UADE_FLOAT_CONFIG(x) float x; char x##_set; +#define UADE_INT_CONFIG(x) int x; char x##_set; + +/* All the options are put into an instance of this structure. + * There can be many structures, one for uade.conf and the other for + * command line options. Then these structures are then merged together + * to know the complete behavior for each case. Note, these structures + * can be conflicting, so the options are merged in following order + * so that the last merge will determine true behavior: + * + * 1. set uade.conf options + * 2. set eagleplayer attributes + * 3. set song attributes + * 4. set command line options + * + * Merging works by looking at X_set members of this structure. X_set + * member indicates that feature X has explicitly been set, so the + * merge will notice the change in value. + */ +struct uade_config { + UADE_CHAR_CONFIG(action_keys); + + struct uade_ao_options ao_options; + char ao_options_set; + + struct uade_dir basedir; + char basedir_set; + + UADE_INT_CONFIG(buffer_time); + UADE_CHAR_CONFIG(content_detection); + UADE_CHAR_CONFIG(cygwin_drive_workaround); + + struct uade_ep_options ep_options; + char ep_options_set; + + UADE_CHAR_CONFIG(filter_type); + UADE_INT_CONFIG(frequency); + UADE_CHAR_CONFIG(led_forced); + UADE_CHAR_CONFIG(led_state); + + UADE_CHAR_CONFIG(gain_enable); + /* should be removed of uade_effect integrated */ + UADE_FLOAT_CONFIG(gain); + + UADE_CHAR_CONFIG(headphones); + UADE_CHAR_CONFIG(headphones2); + UADE_CHAR_CONFIG(ignore_player_check); + + char *resampler; + char resampler_set; + + UADE_CHAR_CONFIG(no_ep_end); + UADE_CHAR_CONFIG(no_filter); + UADE_CHAR_CONFIG(no_postprocessing); + + UADE_CHAR_CONFIG(normalise); + /* no normalise_parameter_set entry, use manual merging code */ + char *normalise_parameter; + + UADE_CHAR_CONFIG(one_subsong); + UADE_FLOAT_CONFIG(panning); /* should be removed */ + UADE_CHAR_CONFIG(panning_enable); + UADE_CHAR_CONFIG(random_play); + UADE_CHAR_CONFIG(recursive_mode); + UADE_INT_CONFIG(silence_timeout); + + char *song_title; + char song_title_set; + + UADE_CHAR_CONFIG(speed_hack); + UADE_INT_CONFIG(subsong_timeout); + UADE_INT_CONFIG(timeout); + UADE_CHAR_CONFIG(use_text_scope); + UADE_CHAR_CONFIG(use_timeouts); + UADE_CHAR_CONFIG(use_ntsc); + UADE_CHAR_CONFIG(verbose); +}; + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/uadecontrol.c b/plugins/uade2/uade-2.13/src/frontends/common/uadecontrol.c new file mode 100644 index 00000000..66e3eac8 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/uadecontrol.c @@ -0,0 +1,249 @@ +/* uadecontrol.c is a helper module to control uade core through IPC: + + Copyright (C) 2005 Heikki Orsila <heikki.orsila@iki.fi> + + This source code module is dual licensed under GPL and Public Domain. + Hence you may use _this_ module (not another code module) in any way you + want in your projects. +*/ + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/socket.h> + +#include "uadecontrol.h" +#include "ossupport.h" +#include "sysincludes.h" +#include "uadeconstants.h" +#include "songdb.h" + +static void subsong_control(int subsong, int command, struct uade_ipc *ipc); + +void uade_change_subsong(struct uade_state *state) +{ + state->song->silence_count = 0; + + uade_lookup_volume_normalisation(state); + + subsong_control(state->song->cur_subsong, UADE_COMMAND_CHANGE_SUBSONG, &state->ipc); +} + +int uade_read_request(struct uade_ipc *ipc) +{ + uint32_t left = UADE_MAX_MESSAGE_SIZE - sizeof(struct uade_msg); + if (uade_send_u32(UADE_COMMAND_READ, left, ipc)) { + fprintf(stderr, "\ncan not send read command\n"); + return 0; + } + return left; +} + +static int send_ep_options(struct uade_ep_options *eo, struct uade_ipc *ipc) +{ + if (eo->s > 0) { + size_t i = 0; + while (i < eo->s) { + char *s = &eo->o[i]; + size_t l = strlen(s) + 1; + assert((i + l) <= eo->s); + if (uade_send_string + (UADE_COMMAND_SET_PLAYER_OPTION, s, ipc)) { + fprintf(stderr, + "Can not send eagleplayer option.\n"); + return -1; + } + i += l; + } + } + return 0; +} + +void uade_send_filter_command(struct uade_state *state) +{ + struct uade_config *uadeconf = &state->config; + struct uade_ipc *ipc = &state->ipc; + + int filter_type = uadeconf->filter_type; + int filter_state = uadeconf->led_state; + int force_filter = uadeconf->led_forced; + + if (uadeconf->no_filter) + filter_type = 0; + + /* Note that filter state is not normally forced */ + filter_state = force_filter ? (2 + (filter_state & 1)) : 0; + + if (uade_send_two_u32s + (UADE_COMMAND_FILTER, filter_type, filter_state, ipc)) { + fprintf(stderr, "Can not setup filters.\n"); + exit(-1); + } +} + +static void send_resampling_command(struct uade_ipc *ipc, + struct uade_config *uadeconf) +{ + char *mode = uadeconf->resampler; + if (mode != NULL) { + if (strlen(mode) == 0) { + fprintf(stderr, "Resampling mode may not be empty.\n"); + exit(-1); + } + if (uade_send_string + (UADE_COMMAND_SET_RESAMPLING_MODE, mode, ipc)) { + fprintf(stderr, "Can not set resampling mode.\n"); + exit(-1); + } + } +} + +static void subsong_control(int subsong, int command, struct uade_ipc *ipc) +{ + assert(subsong >= 0 && subsong < 256); + if (uade_send_u32(command, (uint32_t) subsong, ipc) < 0) { + fprintf(stderr, "Could not changet subsong\n"); + exit(-1); + } +} + +void uade_set_subsong(int subsong, struct uade_ipc *ipc) +{ + subsong_control(subsong, UADE_COMMAND_SET_SUBSONG, ipc); +} + +int uade_song_initialization(const char *scorename, + const char *playername, + const char *modulename, + struct uade_state *state) +{ + uint8_t space[UADE_MAX_MESSAGE_SIZE]; + struct uade_msg *um = (struct uade_msg *)space; + struct uade_ipc *ipc = &state->ipc; + struct uade_config *uc = &state->config; + struct uade_song *us = state->song; + + if (uade_send_string(UADE_COMMAND_SCORE, scorename, ipc)) { + fprintf(stderr, "Can not send score name.\n"); + goto cleanup; + } + + if (uade_send_string(UADE_COMMAND_PLAYER, playername, ipc)) { + fprintf(stderr, "Can not send player name.\n"); + goto cleanup; + } + + if (uade_send_string(UADE_COMMAND_MODULE, modulename, ipc)) { + fprintf(stderr, "Can not send module name.\n"); + goto cleanup; + } + + if (uade_send_short_message(UADE_COMMAND_TOKEN, ipc)) { + fprintf(stderr, "Can not send token after module.\n"); + goto cleanup; + } + + printf ("uade_song_initialization: receive message\n"); + if (uade_receive_message(um, sizeof(space), ipc) <= 0) { + fprintf(stderr, "Can not receive acknowledgement.\n"); + goto cleanup; + } + + if (um->msgtype == UADE_REPLY_CANT_PLAY) { + if (uade_receive_short_message(UADE_COMMAND_TOKEN, ipc)) { + fprintf(stderr, + "Can not receive token in main loop.\n"); + exit(-1); + } + return UADECORE_CANT_PLAY; + } + + if (um->msgtype != UADE_REPLY_CAN_PLAY) { + fprintf(stderr, "Unexpected reply from uade: %u\n", + (unsigned int)um->msgtype); + goto cleanup; + } + + if (uade_receive_short_message(UADE_COMMAND_TOKEN, ipc) < 0) { + fprintf(stderr, "Can not receive token after play ack.\n"); + goto cleanup; + } + + if (uc->ignore_player_check) { + if (uade_send_short_message(UADE_COMMAND_IGNORE_CHECK, ipc) < 0) { + fprintf(stderr, "Can not send ignore check message.\n"); + goto cleanup; + } + } + + if (uc->no_ep_end) { + if (uade_send_short_message + (UADE_COMMAND_SONG_END_NOT_POSSIBLE, ipc) < 0) { + fprintf(stderr, + "Can not send 'song end not possible'.\n"); + goto cleanup; + } + } + + uade_send_filter_command(state); + + send_resampling_command(ipc, uc); + + if (uc->speed_hack) { + if (uade_send_short_message(UADE_COMMAND_SPEED_HACK, ipc)) { + fprintf(stderr, "Can not send speed hack command.\n"); + goto cleanup; + } + } + + if (uc->use_ntsc) { + if (uade_send_short_message(UADE_COMMAND_SET_NTSC, ipc)) { + fprintf(stderr, "Can not send ntsc command.\n"); + goto cleanup; + } + } + + if (uc->frequency != UADE_DEFAULT_FREQUENCY) { + if (uade_send_u32 + (UADE_COMMAND_SET_FREQUENCY, uc->frequency, ipc)) { + fprintf(stderr, "Can not send frequency.\n"); + goto cleanup; + } + } + + if (uc->use_text_scope) { + if (uade_send_short_message(UADE_COMMAND_USE_TEXT_SCOPE, ipc)) { + fprintf(stderr, "Can not send use text scope command.\n"); + goto cleanup; + } + } + + if (send_ep_options(&us->ep_options, ipc) || + send_ep_options(&uc->ep_options, ipc)) + goto cleanup; + + printf ("uade_song_initialization: success\n"); + + return 0; + + cleanup: + return UADECORE_INIT_ERROR; +} + +void uade_spawn(struct uade_state *state, const char *uadename, + const char *configname) +{ + uade_arch_spawn(&state->ipc, &state->pid, uadename); + + if (uade_send_string(UADE_COMMAND_CONFIG, configname, &state->ipc)) { + fprintf(stderr, "Can not send config name: %s\n", + strerror(errno)); + kill(state->pid, SIGTERM); + state->pid = 0; + abort(); + } +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/uadecontrol.h b/plugins/uade2/uade-2.13/src/frontends/common/uadecontrol.h new file mode 100644 index 00000000..769521a9 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/uadecontrol.h @@ -0,0 +1,24 @@ +#ifndef _UADE_CONTROL_ +#define _UADE_CONTROL_ + +#include <sys/types.h> + +#include <uadestate.h> + +enum { + UADECORE_INIT_OK = 0, + UADECORE_INIT_ERROR, + UADECORE_CANT_PLAY +}; + +void uade_change_subsong(struct uade_state *state); +int uade_read_request(struct uade_ipc *ipc); +void uade_send_filter_command(struct uade_state *state); +void uade_set_subsong(int subsong, struct uade_ipc *ipc); +int uade_song_initialization(const char *scorename, const char *playername, + const char *modulename, + struct uade_state *state); +void uade_spawn(struct uade_state *state, const char *uadename, + const char *configname); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/uadestate.h b/plugins/uade2/uade-2.13/src/frontends/common/uadestate.h new file mode 100644 index 00000000..7014a30a --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/uadestate.h @@ -0,0 +1,25 @@ +#ifndef _UADE_STATE_H_ +#define _UADE_STATE_H_ + +#include <sys/types.h> +#include <unistd.h> + +#include <eagleplayer.h> +#include <effects.h> +#include <uadeipc.h> + +struct uade_state { + /* Per song members */ + struct uade_config config; + struct uade_song *song; + struct uade_effect effects; + struct eagleplayer *ep; + + /* Permanent members */ + int validconfig; + struct eagleplayerstore *playerstore; + struct uade_ipc ipc; + pid_t pid; +}; + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/unixwalkdir.c b/plugins/uade2/uade-2.13/src/frontends/common/unixwalkdir.c new file mode 100644 index 00000000..292976d7 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/unixwalkdir.c @@ -0,0 +1,69 @@ +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> + +#include <unixwalkdir.h> + +void *uade_walk_directories(const char *dirname, + void *(*fn) (const char *file, + enum uade_wtype wtype, void *opaque), + void *opaque) +{ + char *dename; + DIR *dir; + size_t namelen; + struct dirent *de; + void *ret = NULL; + struct stat st; + enum uade_wtype wtype; + + namelen = strlen(dirname) + 256 + 2; + if ((dename = malloc(namelen)) == NULL) + return NULL; + + if ((dir = opendir(dirname)) == NULL) + return NULL; + + while ((de = readdir(dir)) != NULL) { + + if (strcmp(de->d_name, ".") == 0 + || strcmp(de->d_name, "..") == 0) + continue; + + if (snprintf(dename, namelen, "%s/%s", dirname, de->d_name) >= + namelen) { + fprintf(stderr, "interesting: too long a filename\n"); + continue; + } + + if (lstat(dename, &st)) + continue; + + if (S_ISREG(st.st_mode)) + wtype = UADE_WALK_REGULAR_FILE; + else if (S_ISDIR(st.st_mode)) + wtype = UADE_WALK_DIRECTORY; + else if (S_ISLNK(st.st_mode)) + wtype = UADE_WALK_SYMLINK; + else + wtype = UADE_WALK_SPECIAL; + + if ((ret = fn(dename, wtype, opaque)) != NULL) + break; + + if (wtype == UADE_WALK_DIRECTORY) { + if ((ret = + uade_walk_directories(dename, fn, opaque)) != NULL) + break; + } + } + + closedir(dir); + free(dename); + + return ret; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/unixwalkdir.h b/plugins/uade2/uade-2.13/src/frontends/common/unixwalkdir.h new file mode 100644 index 00000000..69844f47 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/unixwalkdir.h @@ -0,0 +1,16 @@ +#ifndef _UADE_UNIXWALKDIR_H_ +#define _UADE_UNIXWALKDIR_H_ + +enum uade_wtype { + UADE_WALK_REGULAR_FILE = 1, + UADE_WALK_DIRECTORY, + UADE_WALK_SYMLINK, + UADE_WALK_SPECIAL +}; + +void *uade_walk_directories(const char *dirname, + void *(*fn) (const char *file, + enum uade_wtype wtype, void *opaque), + void *opaque); + +#endif diff --git a/plugins/uade2/uade-2.13/src/frontends/common/vplist.c b/plugins/uade2/uade-2.13/src/frontends/common/vplist.c new file mode 100644 index 00000000..5546f54d --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/vplist.c @@ -0,0 +1,115 @@ +#include <stdlib.h> +#include <string.h> + +#include "vplist.h" + + +#define VPLIST_BASIC_LENGTH 5 + + +static void shrink_vplist(struct vplist *v, size_t newsize) +{ + size_t ncopied = v->tail - v->head; + void **newl; + if (newsize >= v->allocated) { + fprintf(stderr, "vplist not shrinked.\n"); + return; + } + memmove(v->l, &v->l[v->head], ncopied * sizeof(v->l[0])); + v->head = 0; + v->tail = ncopied; + v->allocated = newsize; + if ((newl = realloc(v->l, v->allocated * sizeof(v->l[0]))) == NULL) { + fprintf(stderr, "Not enough memory for shrinking vplist.\n"); + exit(-1); + } + v->l = newl; +} + + +void vplist_grow(struct vplist *v) +{ + size_t newsize = v->allocated * 2; + void **newl; + if (newsize == 0) + newsize = VPLIST_BASIC_LENGTH; + newl = realloc(v->l, newsize * sizeof(v->l[0])); + if (newl == NULL) { + fprintf(stderr, "Not enough memory for growing vplist.\n"); + exit(-1); + } + v->l = newl; + v->allocated = newsize; +} + + +struct vplist *vplist_create(size_t initial_length) +{ + struct vplist *v; + if ((v = calloc(1, sizeof(*v))) == NULL) { + fprintf(stderr, "No memory for vplist.\n"); + exit(-1); + } + if (initial_length == 0) + initial_length = VPLIST_BASIC_LENGTH; + v->allocated = initial_length; + if ((v->l = malloc(v->allocated * sizeof(v->l[0]))) == NULL) { + fprintf(stderr, "Can not create a vplist.\n"); + exit(-1); + } + return v; +} + + +void vplist_flush(struct vplist *v) +{ + v->head = v->tail = 0; + if (v->allocated >= (2 * VPLIST_BASIC_LENGTH)) + shrink_vplist(v, VPLIST_BASIC_LENGTH); +} + + +void vplist_free(struct vplist *v) +{ + free(v->l); + memset(v, 0, sizeof(*v)); + free(v); +} + + +void *vplist_pop_head(struct vplist *v) +{ + void *item; + + if (v->head == v->tail) { + fprintf(stderr, "Error: can not pop head from an empty vplist.\n"); + exit(-1); + } + + item = v->l[v->head++]; + + /* If 3/4 of a list is unused, free half the list */ + if (v->allocated >= VPLIST_BASIC_LENGTH && v->head >= ((v->allocated / 4) * 3)) + shrink_vplist(v, v->allocated / 2); + + return item; +} + + +void *vplist_pop_tail(struct vplist *v) +{ + void *item; + + if (v->head == v->tail) { + fprintf(stderr, "Error: can not pop tail from an empty vplist.\n"); + exit(-1); + } + + item = v->l[v->tail--]; + + /* If 3/4 of a list is unused, free half the list */ + if (v->allocated >= VPLIST_BASIC_LENGTH && v->tail < (v->allocated / 4)) + shrink_vplist(v, v->allocated / 2); + + return item; +} diff --git a/plugins/uade2/uade-2.13/src/frontends/common/vplist.h b/plugins/uade2/uade-2.13/src/frontends/common/vplist.h new file mode 100644 index 00000000..ef79f5bd --- /dev/null +++ b/plugins/uade2/uade-2.13/src/frontends/common/vplist.h @@ -0,0 +1,44 @@ +#ifndef _SHD_VPLIST_H_ +#define _SHD_VPLIST_H_ + +#include <stdio.h> +#include <assert.h> + + +struct vplist { + size_t head; + size_t tail; + size_t allocated; + void **l; +}; + + +struct vplist *vplist_create(size_t initial_length); +void vplist_flush(struct vplist *v); +void vplist_free(struct vplist *v); +void vplist_grow(struct vplist *v); +void *vplist_pop_head(struct vplist *v); +void *vplist_pop_tail(struct vplist *v); + + +static inline void vplist_append(struct vplist *v, void *item) +{ + if (v->tail == v->allocated) + vplist_grow(v); + v->l[v->tail++] = item; +} + + +static inline void *vplist_get(struct vplist *v, size_t i) +{ + assert(i < (v->tail - v->head)); + return v->l[v->head + i]; +} + + +static inline size_t vplist_len(struct vplist *v) +{ + return v->tail - v->head; +} + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/.gitignore b/plugins/uade2/uade-2.13/src/include/.gitignore new file mode 100644 index 00000000..c74efa80 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/.gitignore @@ -0,0 +1,4 @@ +compilersupport.h +ossupport.h +sysincludes.h +uadeconfig.h diff --git a/plugins/uade2/uade-2.13/src/include/amigafilter.h b/plugins/uade2/uade-2.13/src/include/amigafilter.h new file mode 100644 index 00000000..761fd33f --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/amigafilter.h @@ -0,0 +1,10 @@ +#ifndef _UADE_AMIGA_FILTER_H_ +#define _UADE_AMIGA_FILTER_H_ + +enum { + FILTER_MODEL_A500 = 1, + FILTER_MODEL_A1200, + FILTER_MODEL_UPPER_BOUND +}; + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/amigamsg.h b/plugins/uade2/uade-2.13/src/include/amigamsg.h new file mode 100644 index 00000000..457529c3 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/amigamsg.h @@ -0,0 +1,24 @@ +#ifndef _AMIGAMSG_H_ +#define _AMIGAMSG_H_ + +enum amigamsg { + AMIGAMSG_SETSUBSONG = 1, + AMIGAMSG_SONG_END, + AMIGAMSG_PLAYERNAME, + AMIGAMSG_MODULENAME, + AMIGAMSG_SUBSINFO, + AMIGAMSG_CHECKERROR, + AMIGAMSG_SCORECRASH, + AMIGAMSG_SCOREDEAD, + AMIGAMSG_GENERALMSG, + AMIGAMSG_NTSC, + AMIGAMSG_FORMATNAME, + AMIGAMSG_LOADFILE, + AMIGAMSG_READ, + AMIGAMSG_FILESIZE, + AMIGAMSG_TIME_CRITICAL, + AMIGAMSG_GET_INFO, + AMIGAMSG_START_OUTPUT +}; + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/audio.h b/plugins/uade2/uade-2.13/src/include/audio.h new file mode 100644 index 00000000..5716f5f9 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/audio.h @@ -0,0 +1,57 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Sound emulation stuff + * + * Copyright 1995, 1996, 1997 Bernd Schmidt + */ + +#ifndef _UADE_AUDIO_H_ +#define _UADE_AUDIO_H_ + +#include "sinctable.h" + +#define AUDIO_DEBUG 0 +/* Queue length 256 implies minimum emulated period of 8. This should be + * sufficient for all imaginable purposes. This must be power of two. */ +#define SINC_QUEUE_LENGTH 256 + +typedef struct { + int time, output; +} sinc_queue_t; + +extern struct audio_channel_data { + unsigned long adk_mask; + unsigned long evtime; + unsigned char dmaen, intreq2, data_written; + uaecptr lc, pt; + + int state, wper, wlen; + int current_sample; + int sample_accum, sample_accum_time; + int output_state; + sinc_queue_t sinc_queue[SINC_QUEUE_LENGTH]; + int sinc_queue_time; + int sinc_queue_head; + int vol; + uae_u16 dat, nextdat, per, len; + + /* Debug variables */ + uaecptr ptend, nextdatpt, nextdatptend, datpt, datptend; +} audio_channel[4]; + +extern void AUDxDAT (int nr, uae_u16 value); +extern void AUDxVOL (int nr, uae_u16 value); +extern void AUDxPER (int nr, uae_u16 value); +extern void AUDxLCH (int nr, uae_u16 value); +extern void AUDxLCL (int nr, uae_u16 value); +extern void AUDxLEN (int nr, uae_u16 value); + +void audio_reset (void); +void audio_set_filter(int filter_type, int filter_force); +void audio_set_rate (int rate); +void audio_set_resampler(char *name); +void audio_use_text_scope(void); +void update_audio (void); + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/cia.h b/plugins/uade2/uade-2.13/src/include/cia.h new file mode 100644 index 00000000..558b11c0 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/cia.h @@ -0,0 +1,26 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * CIA chip support + * + * (c) 1995 Bernd Schmidt + */ + +extern void CIA_reset(void); +extern void CIA_vsync_handler(void); +extern void CIA_hsync_handler(void); +extern void CIA_handler(void); + +extern void diskindex_handler(void); + +extern void dumpcia(void); + +extern unsigned int ciaaicr,ciaaimask,ciabicr,ciabimask; +extern unsigned int ciaacra,ciaacrb,ciabcra,ciabcrb; +extern unsigned int ciaapra, ciabpra; +extern unsigned long ciaata,ciaatb,ciabta,ciabtb; +extern unsigned long ciaatod,ciabtod,ciaatol,ciabtol,ciaaalarm,ciabalarm; +extern int ciaatlatch,ciabtlatch; + +extern unsigned int gui_ledstate; +extern int gui_ledstate_forced; diff --git a/plugins/uade2/uade-2.13/src/include/commpipe.h b/plugins/uade2/uade-2.13/src/include/commpipe.h new file mode 100644 index 00000000..c7f4d814 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/commpipe.h @@ -0,0 +1,155 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Communication between threads + * + * Copyright 1997, 2001 Bernd Schmidt + */ + +typedef union { + int i; + uae_u32 u32; + void *pv; +} uae_pt; + +/* These currently require the maximum size to be known at initialization + * time, but it wouldn't be hard to use a "normal" pipe as an extension once the + * user-level one gets full. + * We queue up to chunks pieces of data before signalling the other thread to + * avoid overhead. */ + +typedef struct { + uae_sem_t lock; + uae_sem_t reader_wait; + uae_sem_t writer_wait; + uae_pt *data; + int size, chunks; + volatile int rdp, wrp; + volatile int writer_waiting; + volatile int reader_waiting; +} smp_comm_pipe; + +static inline void init_comm_pipe (smp_comm_pipe *p, int size, int chunks) +{ + p->data = (uae_pt *)malloc (size*sizeof (uae_pt)); + p->size = size; + p->chunks = chunks; + p->rdp = p->wrp = 0; + p->reader_waiting = 0; + p->writer_waiting = 0; + uae_sem_init (&p->lock, 0, 1); + uae_sem_init (&p->reader_wait, 0, 0); + uae_sem_init (&p->writer_wait, 0, 0); +} + +static inline void destroy_comm_pipe (smp_comm_pipe *p) +{ + uae_sem_destroy (&p->lock); + uae_sem_destroy (&p->reader_wait); + uae_sem_destroy (&p->writer_wait); +} + +static inline void maybe_wake_reader (smp_comm_pipe *p, int no_buffer) +{ + if (p->reader_waiting + && (no_buffer || ((p->wrp - p->rdp + p->size) % p->size) >= p->chunks)) + { + p->reader_waiting = 0; + uae_sem_post (&p->reader_wait); + } +} + +static inline void write_comm_pipe_pt (smp_comm_pipe *p, uae_pt data, int no_buffer) +{ + int nxwrp = (p->wrp + 1) % p->size; + + if (p->reader_waiting) { + /* No need to do all the locking */ + p->data[p->wrp] = data; + p->wrp = nxwrp; + maybe_wake_reader (p, no_buffer); + return; + } + + uae_sem_wait (&p->lock); + if (nxwrp == p->rdp) { + /* Pipe full! */ + p->writer_waiting = 1; + uae_sem_post (&p->lock); + /* Note that the reader could get in between here and do a + * sem_post on writer_wait before we wait on it. That's harmless. + * There's a similar case in read_comm_pipe_int_blocking. */ + uae_sem_wait (&p->writer_wait); + uae_sem_wait (&p->lock); + } + p->data[p->wrp] = data; + p->wrp = nxwrp; + maybe_wake_reader (p, no_buffer); + uae_sem_post (&p->lock); +} + +static inline uae_pt read_comm_pipe_pt_blocking (smp_comm_pipe *p) +{ + uae_pt data; + + uae_sem_wait (&p->lock); + if (p->rdp == p->wrp) { + p->reader_waiting = 1; + uae_sem_post (&p->lock); + uae_sem_wait (&p->reader_wait); + uae_sem_wait (&p->lock); + } + data = p->data[p->rdp]; + p->rdp = (p->rdp + 1) % p->size; + + /* We ignore chunks here. If this is a problem, make the size bigger in the init call. */ + if (p->writer_waiting) { + p->writer_waiting = 0; + uae_sem_post (&p->writer_wait); + } + uae_sem_post (&p->lock); + return data; +} + +static inline int comm_pipe_has_data (smp_comm_pipe *p) +{ + return p->rdp != p->wrp; +} + +static inline int read_comm_pipe_int_blocking (smp_comm_pipe *p) +{ + uae_pt foo = read_comm_pipe_pt_blocking (p); + return foo.i; +} +static inline uae_u32 read_comm_pipe_u32_blocking (smp_comm_pipe *p) +{ + uae_pt foo = read_comm_pipe_pt_blocking (p); + return foo.u32; +} + +static inline void *read_comm_pipe_pvoid_blocking (smp_comm_pipe *p) +{ + uae_pt foo = read_comm_pipe_pt_blocking (p); + return foo.pv; +} + +static inline void write_comm_pipe_int (smp_comm_pipe *p, int data, int no_buffer) +{ + uae_pt foo; + foo.i = data; + write_comm_pipe_pt (p, foo, no_buffer); +} + +static inline void write_comm_pipe_u32 (smp_comm_pipe *p, int data, int no_buffer) +{ + uae_pt foo; + foo.u32 = data; + write_comm_pipe_pt (p, foo, no_buffer); +} + +static inline void write_comm_pipe_pvoid (smp_comm_pipe *p, void *data, int no_buffer) +{ + uae_pt foo; + foo.pv = data; + write_comm_pipe_pt (p, foo, no_buffer); +} diff --git a/plugins/uade2/uade-2.13/src/include/compiler.h b/plugins/uade2/uade-2.13/src/include/compiler.h new file mode 100644 index 00000000..545dd6a4 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/compiler.h @@ -0,0 +1,111 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * m68k -> i386 compiler + * + * (c) 1995 Bernd Schmidt + */ + +typedef uaecptr (*code_execfunc)(void); + +struct code_page { + struct code_page *next; + uae_u32 allocmask; +}; + +struct hash_block { + struct hash_block *lru_next, *lru_prev; + struct hash_entry *he_first; + + struct code_page *cpage; + int alloclen; + uae_u32 page_allocmask; + char *compile_start; + + int nrefs; + + int translated:1; + int untranslatable:1; + int allocfailed:1; +}; + +struct hash_entry { + code_execfunc execute; /* For the sake of the stubs in X86.S */ + struct hash_entry *next,*prev; + struct hash_entry *next_same_block, *lru_next, *lru_prev; + struct hash_block *block; + + uaecptr addr; + uae_u32 matchword; + int ncalls:8; + int locked:1; + int cacheflush:1; +}; + +extern int nr_bbs_start; +extern uae_u8 nr_bbs_to_run; +extern code_execfunc exec_me; + +#ifdef USE_COMPILER +static inline void run_compiled_code(void) +{ + + /*if (regs.spcflags == SPCFLAG_EXEC && may_run_compiled) {*/ + while (regs.spcflags == SPCFLAG_EXEC) { + uaecptr newpc; + regs.spcflags = 0; + /* newpc = (*exec_me)();*/ + __asm__ __volatile__ ("pushl %%ebp; call *%1; popl %%ebp" : "=a" (newpc) : "r" (exec_me) : + "%eax", "%edx", "%ecx", "%ebx", + "%edi", "%esi", "memory", "cc"); + if (nr_bbs_to_run == 0) { + struct hash_entry *h = (struct hash_entry *)newpc; + regs.spcflags = SPCFLAG_EXEC; + exec_me = h->execute; + regs.pc = h->addr; + regs.pc_p = regs.pc_oldp = get_real_address(h->addr); + nr_bbs_to_run = nr_bbs_start; + } else + m68k_setpc_fast(newpc); + do_cycles(); + } +/*} else */ + regs.spcflags &= ~SPCFLAG_EXEC; +} + +extern void compiler_init(void); +extern void possible_loadseg(void); + +extern void m68k_do_rts(void); +extern void m68k_do_bsr(uaecptr, uae_s32); +extern void m68k_do_jsr(uaecptr, uaecptr); +extern void compiler_flush_jsr_stack(void); + +#else + +#define run_compiled_code() do { ; } while (0) +#define compiler_init() do { ; } while (0) +#define possible_loadseg() do { ; } while (0) +#define compiler_flush_jsr_stack() do { ; } while (0) + +static inline void m68k_do_rts(void) +{ + m68k_setpc(get_long(m68k_areg(regs, 7))); + m68k_areg(regs, 7) += 4; +} + +static inline void m68k_do_bsr(uaecptr oldpc, uae_s32 offset) +{ + m68k_areg(regs, 7) -= 4; + put_long(m68k_areg(regs, 7), oldpc); + m68k_incpc(offset); +} + +static inline void m68k_do_jsr(uaecptr oldpc, uaecptr dest) +{ + m68k_areg(regs, 7) -= 4; + put_long(m68k_areg(regs, 7), oldpc); + m68k_setpc(dest); +} + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/custom.h b/plugins/uade2/uade-2.13/src/include/custom.h new file mode 100644 index 00000000..15e3d689 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/custom.h @@ -0,0 +1,115 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * custom chip support + * + * (c) 1995 Bernd Schmidt + */ + +/* These are the masks that are ORed together in the chipset_mask option. + * If CSMASK_AGA is set, the ECS bits are guaranteed to be set as well. */ +#define CSMASK_ECS_AGNUS 1 +#define CSMASK_ECS_DENISE 2 +#define CSMASK_AGA 4 + +extern void custom_init (void); +extern void customreset (void); +extern int intlev (void); +extern void dumpcustom (void); + +extern void do_disk (void); + +extern void notice_new_xcolors (void); +extern void notice_screen_contents_lost (void); +extern void init_row_map (void); + +extern int picasso_requested_on; +extern int picasso_on; + +/* Set to 1 to leave out the current frame in average frame time calculation. + * Useful if the debugger was active. */ +extern int bogusframe; + +extern uae_u16 dmacon; +extern uae_u16 intena,intreq; + +extern int current_hpos (void); + +static inline int dmaen (unsigned int dmamask) +{ + return (dmamask & dmacon) && (dmacon & 0x200); +} + +#define SPCFLAG_STOP 2 +#define SPCFLAG_DISK 4 +#define SPCFLAG_INT 8 +#define SPCFLAG_BRK 16 +#define SPCFLAG_EXTRA_CYCLES 32 +#define SPCFLAG_TRACE 64 +#define SPCFLAG_DOTRACE 128 +#define SPCFLAG_DOINT 256 +#define SPCFLAG_BLTNASTY 512 +#define SPCFLAG_EXEC 1024 +#define SPCFLAG_MODE_CHANGE 8192 + +extern int dskdmaen; +extern uae_u16 adkcon; + +extern unsigned int joy0dir, joy1dir; +extern int joy0button, joy1button; + +extern void INTREQ (uae_u16); +extern uae_u16 INTREQR (void); + +/* maximums for statically allocated tables */ + +/* PAL/NTSC values */ + +/* The HRM says: The vertical blanking area (PAL) ranges from line 0 to line 29, + * and no data can be displayed there. Nevertheless, we lose some overscan data + * if minfirstline is set to 29. */ + +#define MAXHPOS_PAL 227 +#define MAXHPOS_NTSC 227 +#define MAXVPOS_PAL 312 +#define MAXVPOS_NTSC 262 +#define MINFIRSTLINE_PAL 21 +#define MINFIRSTLINE_NTSC 18 +#define VBLANK_ENDLINE_PAL 29 +#define VBLANK_ENDLINE_NTSC 24 +#define VBLANK_HZ_PAL 50 +#define VBLANK_HZ_NTSC 60 + +#define SOUNDTICKS_PAL 3546895 +#define SOUNDTICKS_NTSC 3579545 + +#define MAXHPOS (MAXHPOS_PAL) +#define MAXVPOS (MAXVPOS_PAL) +#define SOUNDTICKS (SOUNDTICKS_PAL) + +extern int maxhpos, maxvpos, minfirstline, vblank_endline, numscrlines, vblank_hz; +extern unsigned long syncbase; +#define NUMSCRLINES (maxvpos+1-minfirstline+1) + +#define DMA_AUD0 0x0001 +#define DMA_AUD1 0x0002 +#define DMA_AUD2 0x0004 +#define DMA_AUD3 0x0008 +#define DMA_DISK 0x0010 +#define DMA_SPRITE 0x0020 +#define DMA_BLITTER 0x0040 +#define DMA_COPPER 0x0080 +#define DMA_BITPLANE 0x0100 +#define DMA_BLITPRI 0x0400 + +extern unsigned long frametime, timeframes; + +/* 50 words give you 800 horizontal pixels. An A500 can't do that, so it ought + * to be enough. Don't forget to update the definition in genp2c.c as well. */ +#define MAX_WORDS_PER_LINE 50 + +extern uae_u32 hirestab_h[256][2]; +extern uae_u32 lorestab_h[256][4]; + +extern uae_u32 hirestab_l[256][1]; +extern uae_u32 lorestab_l[256][2]; diff --git a/plugins/uade2/uade-2.13/src/include/debug.h b/plugins/uade2/uade-2.13/src/include/debug.h new file mode 100644 index 00000000..8ca10ab5 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/debug.h @@ -0,0 +1,25 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Debugger + * + * (c) 1995 Bernd Schmidt + * + */ + +#define MAX_HIST 10000 + +extern int firsthist; +extern int lasthist; +extern int debugging; +extern int debug_interrupt_happened; + +#ifdef NEED_TO_DEBUG_BADLY +extern struct regstruct history[MAX_HIST]; +extern union flagu historyf[MAX_HIST]; +#else +extern uaecptr history[MAX_HIST]; +#endif + +extern void debug(void); +extern void activate_debugger(void); diff --git a/plugins/uade2/uade-2.13/src/include/events.h b/plugins/uade2/uade-2.13/src/include/events.h new file mode 100644 index 00000000..4b692650 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/events.h @@ -0,0 +1,83 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Events + * These are best for low-frequency events. Having too many of them, + * or using them for events that occur too frequently, can cause massive + * slowdown. + * + * Copyright 1995-1998 Bernd Schmidt + */ + +extern void reset_frame_rate_hack (void); +extern int rpt_available; + +extern unsigned long int cycles, nextevent, is_lastline; +extern unsigned long int sample_evtime; +typedef void (*evfunc)(void); + +struct ev +{ + int active; + unsigned long int evtime, oldcycles; + evfunc handler; +}; + +enum { + ev_hsync, ev_copper, ev_cia, + ev_blitter, ev_diskblk, ev_diskindex, + ev_max +}; + +extern struct ev eventtab[ev_max]; + +static void events_schedule (void) +{ + unsigned long int mintime = ~0L; + unsigned long int eventtime; + /* HSYNC */ + if(eventtab[ev_hsync].active) { + eventtime = eventtab[ev_hsync].evtime - cycles; + if (eventtime < mintime) mintime = eventtime; + } + /* AUDIO */ +#if 0 + if(eventtab[ev_audio].active) { + eventtime = eventtab[ev_audio].evtime - cycles; + if (eventtime < mintime) mintime = eventtime; + } +#endif + /* CIA */ + if(eventtab[ev_cia].active) { + eventtime = eventtab[ev_cia].evtime - cycles; + if (eventtime < mintime) mintime = eventtime; + } + nextevent = cycles + mintime; +} + +static void do_cycles_slow (unsigned long cycles_to_add) { + if ((nextevent - cycles) <= cycles_to_add) { + for (; cycles_to_add != 0; cycles_to_add--) { + if (++cycles == nextevent) { + /* HSYNC */ + if(eventtab[ev_hsync].active && eventtab[ev_hsync].evtime == cycles) { + (*eventtab[ev_hsync].handler)(); + } + /* AUDIO */ +#if 0 + if(eventtab[ev_audio].active && eventtab[ev_audio].evtime == cycles) { + (*eventtab[ev_audio].handler)(); + } +#endif + /* CIA */ + if(eventtab[ev_cia].active && eventtab[ev_cia].evtime == cycles) { + (*eventtab[ev_cia].handler)(); + } + events_schedule(); + } + } + } + cycles += cycles_to_add; +} + +#define do_cycles do_cycles_slow diff --git a/plugins/uade2/uade-2.13/src/include/execlib.h b/plugins/uade2/uade-2.13/src/include/execlib.h new file mode 100644 index 00000000..c0777221 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/execlib.h @@ -0,0 +1,40 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Miscellaneous bits for exec emulation + * + * Copyright 1996 Bernd Schmidt + */ + +#define CMD_INVALID 0 +#define CMD_RESET 1 +#define CMD_READ 2 +#define CMD_WRITE 3 +#define CMD_UPDATE 4 +#define CMD_CLEAR 5 +#define CMD_STOP 6 +#define CMD_START 7 +#define CMD_FLUSH 8 +#define CMD_NONSTD 9 + +#define NT_TASK 1 +#define NT_DEVICE 3 +#define NT_MSGPORT 4 +#define NT_MESSAGE 5 +#define NT_FREEMSG 6 +#define NT_REPLYMSG 7 +#define NT_RESOURCE 8 +#define NT_LIBRARY 9 +#define NT_SIGNALSEM 15 + +#ifndef MEMF_PUBLIC /* protection for AmigaDOS */ +#define MEMF_PUBLIC 1 +#define MEMF_CHIP 2 +#define MEMF_FAST 4 +#define MEMF_LOCAL 256 +#define MEMF_24BITDMA 512 +#define MEMF_CLEAR (1<<16) +#define MEMF_LARGEST (1<<17) +#define MEMF_REVERSE (1<<18) +#define MEMF_TOTAL (1<<19) +#endif diff --git a/plugins/uade2/uade-2.13/src/include/gensound.h b/plugins/uade2/uade-2.13/src/include/gensound.h new file mode 100644 index 00000000..63daeb04 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/gensound.h @@ -0,0 +1,19 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Prototypes for general sound related functions + * This use to be called sound.h, but that causes confusion + * + * Copyright 1997 Bernd Schmidt + */ + +extern int sound_available; + +/* Determine if we can produce any sound at all. This can be only a guess; + * if unsure, say yes. Any call to init_sound may change the value. */ +extern int setup_sound (void); + +extern void set_sound_freq (int x); +extern void init_sound (void); +extern void flush_sound (void); +extern void close_sound (void); diff --git a/plugins/uade2/uade-2.13/src/include/memory.h b/plugins/uade2/uade-2.13/src/include/memory.h new file mode 100644 index 00000000..96a967b5 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/memory.h @@ -0,0 +1,181 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * memory management + * + * Copyright 1995 Bernd Schmidt + */ + +/* Enabling this adds one additional native memory reference per 68k memory + * access, but saves one shift (on the x86). Enabling this is probably + * better for the cache. My favourite benchmark (PP2) doesn't show a + * difference, so I leave this enabled. */ + +#if 1 || defined SAVE_MEMORY +#define SAVE_MEMORY_BANKS +#endif + +#ifndef REGPARAM +#define REGPARAM +#endif + +typedef uae_u32 (*mem_get_func)(uaecptr) REGPARAM; +typedef void (*mem_put_func)(uaecptr, uae_u32) REGPARAM; +typedef uae_u8 *(*xlate_func)(uaecptr) REGPARAM; +typedef int (*check_func)(uaecptr, uae_u32) REGPARAM; + +extern char *address_space, *good_address_map; +extern uae_u8 *chipmemory; + +extern uae_u32 allocated_chipmem; +extern uae_u32 allocated_fastmem; +extern uae_u32 allocated_bogomem; +extern uae_u32 allocated_gfxmem; +extern uae_u32 allocated_z3fastmem; +extern uae_u32 allocated_a3000mem; + +#undef DIRECT_MEMFUNCS_SUCCESSFUL +#include "machdep/maccess.h" + +#ifndef CAN_MAP_MEMORY +#undef USE_COMPILER +#endif + +#if defined(USE_COMPILER) && !defined(USE_MAPPED_MEMORY) +#define USE_MAPPED_MEMORY +#endif + +#define kickmem_size 0x080000 + +#define chipmem_start 0x00000000 +#define bogomem_start 0x00C00000 +#define a3000mem_start 0x07000000 +#define kickmem_start 0x00F80000 + +extern int ersatzkickfile; + +typedef struct { + /* These ones should be self-explanatory... */ + mem_get_func lget, wget, bget; + mem_put_func lput, wput, bput; + /* Use xlateaddr to translate an Amiga address to a uae_u8 * that can + * be used to address memory without calling the wget/wput functions. + * This doesn't work for all memory banks, so this function may call + * abort(). */ + xlate_func xlateaddr; + /* To prevent calls to abort(), use check before calling xlateaddr. + * It checks not only that the memory bank can do xlateaddr, but also + * that the pointer points to an area of at least the specified size. + * This is used for example to translate bitplane pointers in custom.c */ + check_func check; +} addrbank; + +extern uae_u8 filesysory[65536]; + +extern addrbank chipmem_bank; +extern addrbank kickmem_bank; +extern addrbank custom_bank; +extern addrbank clock_bank; +extern addrbank cia_bank; +extern addrbank rtarea_bank; +extern addrbank expamem_bank; +extern addrbank fastmem_bank; +extern addrbank gfxmem_bank; + +extern void rtarea_init (void); +extern void rtarea_setup (void); +extern void expamem_init (void); +extern void expamem_reset (void); + +extern uae_u32 gfxmem_start; +extern uae_u8 *gfxmemory; +extern uae_u32 gfxmem_mask; +extern int address_space_24; + +/* Default memory access functions */ + +extern int default_check(uaecptr addr, uae_u32 size) REGPARAM; +extern uae_u8 *default_xlate(uaecptr addr) REGPARAM; + +#define bankindex(addr) (((uaecptr)(addr)) >> 16) + +#ifdef SAVE_MEMORY_BANKS +extern addrbank *mem_banks[65536]; +#define get_mem_bank(addr) (*mem_banks[bankindex(addr)]) +#define put_mem_bank(addr, b) (mem_banks[bankindex(addr)] = (b)) +#else +extern addrbank mem_banks[65536]; +#define get_mem_bank(addr) (mem_banks[bankindex(addr)]) +#define put_mem_bank(addr, b) (mem_banks[bankindex(addr)] = *(b)) +#endif + +extern void memory_init(void); +extern void map_banks(addrbank *bank, int first, int count); + +#ifndef NO_INLINE_MEMORY_ACCESS + +#define longget(addr) (call_mem_get_func(get_mem_bank(addr).lget, addr)) +#define wordget(addr) (call_mem_get_func(get_mem_bank(addr).wget, addr)) +#define byteget(addr) (call_mem_get_func(get_mem_bank(addr).bget, addr)) +#define longput(addr,l) (call_mem_put_func(get_mem_bank(addr).lput, addr, l)) +#define wordput(addr,w) (call_mem_put_func(get_mem_bank(addr).wput, addr, w)) +#define byteput(addr,b) (call_mem_put_func(get_mem_bank(addr).bput, addr, b)) + +#else + +extern uae_u32 alongget(uaecptr addr); +extern uae_u32 awordget(uaecptr addr); +extern uae_u32 longget(uaecptr addr); +extern uae_u32 wordget(uaecptr addr); +extern uae_u32 byteget(uaecptr addr); +extern void longput(uaecptr addr, uae_u32 l); +extern void wordput(uaecptr addr, uae_u32 w); +extern void byteput(uaecptr addr, uae_u32 b); + +#endif + +#ifndef MD_HAVE_MEM_1_FUNCS + +#define longget_1 longget +#define wordget_1 wordget +#define byteget_1 byteget +#define longput_1 longput +#define wordput_1 wordput +#define byteput_1 byteput + +#endif + +static inline uae_u32 get_long(uaecptr addr) +{ + return longget_1(addr); +} +static inline uae_u32 get_word(uaecptr addr) +{ + return wordget_1(addr); +} +static inline uae_u32 get_byte(uaecptr addr) +{ + return byteget_1(addr); +} +static inline void put_long(uaecptr addr, uae_u32 l) +{ + longput_1(addr, l); +} +static inline void put_word(uaecptr addr, uae_u32 w) +{ + wordput_1(addr, w); +} +static inline void put_byte(uaecptr addr, uae_u32 b) +{ + byteput_1(addr, b); +} + +static inline uae_u8 *get_real_address(uaecptr addr) +{ + return get_mem_bank(addr).xlateaddr(addr); +} + +static inline int valid_address(uaecptr addr, uae_u32 size) +{ + return get_mem_bank(addr).check(addr, size); +} diff --git a/plugins/uade2/uade-2.13/src/include/newcpu.h b/plugins/uade2/uade-2.13/src/include/newcpu.h new file mode 100644 index 00000000..84732f10 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/newcpu.h @@ -0,0 +1,275 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * MC68000 emulation + * + * Copyright 1995 Bernd Schmidt + */ + +#include <machdep/m68k.h> + +void m68k_run_1 (void); + +#ifndef SET_CFLG + +#define SET_CFLG(x) (CFLG = (x)) +#define SET_NFLG(x) (NFLG = (x)) +#define SET_VFLG(x) (VFLG = (x)) +#define SET_ZFLG(x) (ZFLG = (x)) +#define SET_XFLG(x) (XFLG = (x)) + +#define GET_CFLG CFLG +#define GET_NFLG NFLG +#define GET_VFLG VFLG +#define GET_ZFLG ZFLG +#define GET_XFLG XFLG + +#define CLEAR_CZNV do { \ + SET_CFLG (0); \ + SET_ZFLG (0); \ + SET_NFLG (0); \ + SET_VFLG (0); \ +while (0) + +#define COPY_CARRY (SET_XFLG (GET_CFLG)) +#endif + +extern int areg_byteinc[]; +extern int imm8_table[]; + +extern int movem_index1[256]; +extern int movem_index2[256]; +extern int movem_next[256]; + +extern int fpp_movem_index1[256]; +extern int fpp_movem_index2[256]; +extern int fpp_movem_next[256]; + +extern int broken_in; + +typedef unsigned long cpuop_func (uae_u32) REGPARAM; + +struct cputbl { + cpuop_func *handler; + int specific; + uae_u16 opcode; +}; + +extern unsigned long op_illg (uae_u32) REGPARAM; + +typedef char flagtype; + +extern struct regstruct +{ + uae_u32 regs[16]; + uaecptr usp,isp,msp; + uae_u16 sr; + flagtype t1; + flagtype t0; + flagtype s; + flagtype m; + flagtype x; + flagtype stopped; + int intmask; + + uae_u32 pc; + uae_u8 *pc_p; + uae_u8 *pc_oldp; + + uae_u32 vbr,sfc,dfc; + + double fp[8]; + uae_u32 fpcr,fpsr,fpiar; + + uae_u32 spcflags; + uae_u32 kick_mask; + + /* Fellow sources say this is 4 longwords. That's impossible. It needs + * to be at least a longword. The HRM has some cryptic comment about two + * instructions being on the same longword boundary. + * The way this is implemented now seems like a good compromise. + */ + uae_u32 prefetch; +} regs, lastint_regs; + +#define m68k_dreg(r,num) ((r).regs[(num)]) +#define m68k_areg(r,num) (((r).regs + 8)[(num)]) + +#define get_ibyte(o) do_get_mem_byte((uae_u8 *)(regs.pc_p + (o) + 1)) +#define get_iword(o) do_get_mem_word((uae_u16 *)(regs.pc_p + (o))) +#define get_ilong(o) do_get_mem_long((uae_u32 *)(regs.pc_p + (o))) + +#ifdef HAVE_GET_WORD_UNSWAPPED +#define GET_OPCODE (do_get_mem_word_unswapped (regs.pc_p)) +#else +#define GET_OPCODE (get_iword (0)) +#endif + +static inline uae_u32 get_ibyte_prefetch (uae_s32 o) +{ + if (o > 3 || o < 0) + return do_get_mem_byte((uae_u8 *)(regs.pc_p + o + 1)); + + return do_get_mem_byte((uae_u8 *)(((uae_u8 *)®s.prefetch) + o + 1)); +} +static inline uae_u32 get_iword_prefetch (uae_s32 o) +{ + if (o > 3 || o < 0) + return do_get_mem_word((uae_u16 *)(regs.pc_p + o)); + + return do_get_mem_word((uae_u16 *)(((uae_u8 *)®s.prefetch) + o)); +} +static inline uae_u32 get_ilong_prefetch (uae_s32 o) +{ + union { + uae_u32 *u32; + uae_u16 *u16; + } prefetch_u; + + if (o > 3 || o < 0) + return do_get_mem_long((uae_u32 *)(regs.pc_p + o)); + if (o == 0) + return do_get_mem_long(®s.prefetch); + + prefetch_u.u32 = ®s.prefetch; + + return (do_get_mem_word (prefetch_u.u16 + 1) << 16) | do_get_mem_word ((uae_u16 *)(regs.pc_p + 4)); +} + +#define m68k_incpc(o) (regs.pc_p += (o)) + +static inline void fill_prefetch_0 (void) +{ + uae_u32 r; +#ifdef UNALIGNED_PROFITABLE + r = *(uae_u32 *)regs.pc_p; + regs.prefetch = r; +#else + r = do_get_mem_long ((uae_u32 *)regs.pc_p); + do_put_mem_long (®s.prefetch, r); +#endif +} + +#if 0 +static inline void fill_prefetch_2 (void) +{ + uae_u32 r = do_get_mem_long (®s.prefetch) << 16; + uae_u32 r2 = do_get_mem_word (((uae_u16 *)regs.pc_p) + 1); + r |= r2; + do_put_mem_long (®s.prefetch, r); +} +#else +#define fill_prefetch_2 fill_prefetch_0 +#endif + +/* These are only used by the 68020/68881 code, and therefore don't + * need to handle prefetch. */ +static inline uae_u32 next_ibyte (void) +{ + uae_u32 r = get_ibyte (0); + m68k_incpc (2); + return r; +} + +static inline uae_u32 next_iword (void) +{ + uae_u32 r = get_iword (0); + m68k_incpc (2); + return r; +} + +static inline uae_u32 next_ilong (void) +{ + uae_u32 r = get_ilong (0); + m68k_incpc (4); + return r; +} + +#if !defined USE_COMPILER +static inline void m68k_setpc (uaecptr newpc) +{ + regs.pc_p = regs.pc_oldp = get_real_address(newpc); + regs.pc = newpc; +} +#else +extern void m68k_setpc (uaecptr newpc); +#endif + +static inline uaecptr m68k_getpc (void) +{ + return regs.pc + ((char *)regs.pc_p - (char *)regs.pc_oldp); +} + +static inline uaecptr m68k_getpc_p (uae_u8 *p) +{ + return regs.pc + ((char *)p - (char *)regs.pc_oldp); +} + +#ifdef USE_COMPILER +extern void m68k_setpc_fast (uaecptr newpc); +extern void m68k_setpc_bcc (uaecptr newpc); +extern void m68k_setpc_rte (uaecptr newpc); +#else +#define m68k_setpc_fast m68k_setpc +#define m68k_setpc_bcc m68k_setpc +#define m68k_setpc_rte m68k_setpc +#endif + +static inline void m68k_setstopped (int stop) +{ + regs.stopped = stop; + if (stop) + regs.spcflags |= SPCFLAG_STOP; +} + +extern uae_u32 get_disp_ea_020 (uae_u32 base, uae_u32 dp); +extern uae_u32 get_disp_ea_000 (uae_u32 base, uae_u32 dp); + +extern uae_s32 ShowEA (int reg, amodes mode, wordsizes size, char *buf); + +extern void MakeSR (void); +extern void MakeFromSR (void); +extern void Exception (int, uaecptr); +extern void dump_counts (void); +extern void m68k_move2c (int, uae_u32 *); +extern void m68k_movec2 (int, uae_u32 *); +extern void m68k_divl (uae_u32, uae_u32, uae_u16, uaecptr); +extern void m68k_mull (uae_u32, uae_u32, uae_u16); +extern void init_m68k (void); +extern void m68k_go (void); +extern void m68k_dumpstate (uaecptr *); +extern void m68k_disasm (uaecptr, uaecptr *, int); +extern void m68k_reset (void); + +extern void mmu_op (uae_u32, uae_u16); + +extern void fpp_opp (uae_u32, uae_u16); +extern void fdbcc_opp (uae_u32, uae_u16); +extern void fscc_opp (uae_u32, uae_u16); +extern void ftrapcc_opp (uae_u32,uaecptr); +extern void fbcc_opp (uae_u32, uaecptr, uae_u32); +extern void fsave_opp (uae_u32); +extern void frestore_opp (uae_u32); + +/* Opcode of faulting instruction */ +extern uae_u16 last_op_for_exception_3; +/* PC at fault time */ +extern uaecptr last_addr_for_exception_3; +/* Address that generated the exception */ +extern uaecptr last_fault_for_exception_3; + +#define CPU_OP_NAME(a) op ## a + +/* 68020 + 68881 */ +extern struct cputbl op_smalltbl_0[]; +/* 68020 */ +extern struct cputbl op_smalltbl_1[]; +/* 68010 */ +extern struct cputbl op_smalltbl_2[]; +/* 68000 */ +extern struct cputbl op_smalltbl_3[]; +/* 68000 slow but compatible. */ +extern struct cputbl op_smalltbl_4[]; + +extern cpuop_func *cpufunctbl[65536]; + diff --git a/plugins/uade2/uade-2.13/src/include/options.h b/plugins/uade2/uade-2.13/src/include/options.h new file mode 100644 index 00000000..2ec4befc --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/options.h @@ -0,0 +1,262 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Stuff + * + * Copyright 1995, 1996 Ed Hanway + * Copyright 1995-98 Bernd Schmidt + */ + +#define UAEMAJOR 0 +#define UAEMINOR 8 +#define UAESUBREV 9 + +typedef enum { KBD_LANG_US, KBD_LANG_DE, KBD_LANG_SE, KBD_LANG_FR, KBD_LANG_IT, KBD_LANG_ES } KbdLang; + +extern long int version; + +struct uaedev_mount_info; + +struct strlist { + struct strlist *next; + char *str; +}; + +struct uae_prefs { + struct strlist *unknown_lines; + + char description[256]; + + int illegal_mem; + int no_xhair; + int use_serial; + int serial_demand; + int parallel_demand; + int automount_uaedev; + int use_gfxlib; + int socket_emu; + + int start_debugger; + int start_gui; + + int jport0; + int jport1; + KbdLang keyboard_lang; + int allow_save; + int emul_accuracy; + int test_drawing_speed; + + int produce_sound; + int stereo; + int sound_bits; + int sound_freq; + int sound_minbsiz; + int sound_maxbsiz; + int sound_pri_time; + int sound_pri_cutoff; + int sound_interpol; + + int gfx_framerate; + int gfx_width; + int gfx_height; + int gfx_lores; + int gfx_linedbl; + int gfx_correct_aspect; + int gfx_afullscreen; + int gfx_pfullscreen; + int gfx_xcenter; + int gfx_ycenter; + int color_mode; + + int blits_32bit_enabled; + int immediate_blits; + unsigned int chipset_mask; + int ntscmode; + + char df[4][256]; + char romfile[256]; + char keyfile[256]; + char prtname[256]; + + char path_floppy[256]; + char path_hardfile[256]; + char path_rom[256]; + + int m68k_speed; + int cpu_level; + int cpu_compatible; + int address_space_24; + + uae_u32 z3fastmem_size; + uae_u32 fastmem_size; + uae_u32 chipmem_size; + uae_u32 bogomem_size; + uae_u32 a3000mem_size; + uae_u32 gfxmem_size; + + struct uaedev_mount_info *mountinfo; + + /* Target specific options */ + int x11_use_low_bandwidth; + int x11_use_mitshm; + int x11_use_dgamode; + int x11_hide_cursor; + int svga_no_linear; + int win32_middle_mouse; + int win32_sound_style; + int win32_sound_tweak; + int win32_logfile; + int win32_iconified_nospeed; + int win32_iconified_nosound; +}; + +extern void save_options (FILE *, struct uae_prefs *); + +extern void default_prefs (struct uae_prefs *); +extern void discard_prefs (struct uae_prefs *); + +extern int cfgfile_yesno (char *option, char *value, char *name, int *location); +extern int cfgfile_intval (char *option, char *value, char *name, int *location, int scale); +extern int cfgfile_strval (char *option, char *value, char *name, int *location, const char *table[], int more); +extern int cfgfile_string (char *option, char *value, char *name, char *location, int maxsz); +extern char *cfgfile_subst_path (const char *path, const char *subst, const char *file); + +extern int target_parse_option (struct uae_prefs *, char *option, char *value); +extern void target_save_options (FILE *, struct uae_prefs *); + +extern int cfgfile_load (struct uae_prefs *, const char *filename); +extern int cfgfile_save (struct uae_prefs *, const char *filename); +extern void cfgfile_parse_line (struct uae_prefs *p, char *); +extern int cfgfile_parse_option (struct uae_prefs *p, char *option, char *value); +extern int cfgfile_get_description (const char *filename, char *description); +extern void cfgfile_show_usage (void); + +extern void fixup_prefs_dimensions (struct uae_prefs *prefs); + +extern void check_prefs_changed_custom (void); +extern void check_prefs_changed_cpu (void); +extern int check_prefs_changed_gfx (void); + +#define JSEM_DECODEVAL(n,v) ((n) == 0 ? (v)->jport0 : (v)->jport1) +/* Determine how port n is configured */ +#define JSEM_ISJOY0(n,v) (JSEM_DECODEVAL(n,v) == 0) +#define JSEM_ISJOY1(n,v) (JSEM_DECODEVAL(n,v) == 1) +#define JSEM_ISMOUSE(n,v) (JSEM_DECODEVAL(n,v) == 2) +#define JSEM_ISNUMPAD(n,v) (JSEM_DECODEVAL(n,v) == 3) +#define JSEM_ISCURSOR(n,v) (JSEM_DECODEVAL(n,v) == 4) +#define JSEM_ISSOMEWHEREELSE(n,v) (JSEM_DECODEVAL(n,v) == 5) +extern const char *gameport_state (int n); + +extern struct uae_prefs currprefs, changed_prefs; + +#if __GNUC__ - 1 > 1 || __GNUC_MINOR__ - 1 > 6 +extern void write_log (const char *, ...) __attribute__ ((format (printf, 1, 2))); +#else +extern void write_log (const char *, ...); +#endif + +extern void machdep_init (void); + +/* AIX doesn't think it is Unix. Neither do I. */ +#if defined(_ALL_SOURCE) || defined(_AIX) +#undef __unix +#define __unix +#endif + +extern char romfile[], keyfile[], prtname[], sername[]; + +extern int cloanto_rom; + +#define MAX_COLOR_MODES 5 + +extern int fast_memcmp(const void *foo, const void *bar, int len); +extern int memcmpy(void *foo, const void *bar, int len); + +/* + * You can specify numbers from 0 to 5 here. It is possible that higher + * numbers will make the CPU emulation slightly faster, but if the setting + * is too high, you will run out of memory while compiling. + * Best to leave this as it is. + */ +#define CPU_EMU_SIZE 0 + +/* #define NEED_TO_DEBUG_BADLY */ + +#if !defined(USER_PROGRAMS_BEHAVE) +#define USER_PROGRAMS_BEHAVE 0 +#endif + +/* Some memsets which know that they can safely overwrite some more memory + * at both ends and use that knowledge to align the pointers. */ + +#define QUADRUPLIFY(c) (((c) | ((c) << 8)) | (((c) | ((c) << 8)) << 16)) + +/* When you call this routine, bear in mind that it rounds the bounds and + * may need some padding for the array. */ + +#define fuzzy_memset(p, c, o, l) fuzzy_memset_1 ((p), QUADRUPLIFY (c), (o) & ~3, ((l) + 4) >> 2) +static inline void fuzzy_memset_1 (void *p, uae_u32 c, int offset, int len) +{ + uae_u32 *p2 = (uae_u32 *)((char *)p + offset); + int a = len & 7; + len >>= 3; + switch (a) { + case 7: p2--; goto l1; + case 6: p2-=2; goto l2; + case 5: p2-=3; goto l3; + case 4: p2-=4; goto l4; + case 3: p2-=5; goto l5; + case 2: p2-=6; goto l6; + case 1: p2-=7; goto l7; + case 0: if (!--len) return; break; + } + + for (;;) { + p2[0] = c; + l1: + p2[1] = c; + l2: + p2[2] = c; + l3: + p2[3] = c; + l4: + p2[4] = c; + l5: + p2[5] = c; + l6: + p2[6] = c; + l7: + p2[7] = c; + + if (!len) + break; + len--; + p2 += 8; + } +} + +/* This one knows it will never be asked to clear more than 32 bytes. Make sure you call this with a + constant for the length. */ +#define fuzzy_memset_le32(p, c, o, l) fuzzy_memset_le32_1 ((p), QUADRUPLIFY (c), (o) & ~3, ((l) + 7) >> 2) +static inline void fuzzy_memset_le32_1 (void *p, uae_u32 c, int offset, int len) +{ + uae_u32 *p2 = (uae_u32 *)((char *)p + offset); + + switch (len) { + case 9: p2[0] = c; p2[1] = c; p2[2] = c; p2[3] = c; p2[4] = c; p2[5] = c; p2[6] = c; p2[7] = c; p2[8] = c; break; + case 8: p2[0] = c; p2[1] = c; p2[2] = c; p2[3] = c; p2[4] = c; p2[5] = c; p2[6] = c; p2[7] = c; break; + case 7: p2[0] = c; p2[1] = c; p2[2] = c; p2[3] = c; p2[4] = c; p2[5] = c; p2[6] = c; break; + case 6: p2[0] = c; p2[1] = c; p2[2] = c; p2[3] = c; p2[4] = c; p2[5] = c; break; + case 5: p2[0] = c; p2[1] = c; p2[2] = c; p2[3] = c; p2[4] = c; break; + case 4: p2[0] = c; p2[1] = c; p2[2] = c; p2[3] = c; break; + case 3: p2[0] = c; p2[1] = c; p2[2] = c; break; + case 2: p2[0] = c; p2[1] = c; break; + case 1: p2[0] = c; break; + case 0: break; + default: printf("Hit the programmer.\n"); break; + } +} + +#if defined(AMIGA) && defined(__GNUC__) +/* #include "od-amiga/amiga-kludges.h" */ +#endif diff --git a/plugins/uade2/uade-2.13/src/include/osemu.h b/plugins/uade2/uade-2.13/src/include/osemu.h new file mode 100644 index 00000000..817c5c9b --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/osemu.h @@ -0,0 +1,19 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * OS emulation prototypes + * + * Copyright 1996 Bernd Schmidt + */ + +static inline char *raddr(uaecptr p) +{ + return p == 0 ? NULL : (char *)get_real_address(p); +} + +extern void gfxlib_install(void); + +/* graphics.library */ + +extern int GFX_WritePixel(uaecptr rp, int x, int y); + diff --git a/plugins/uade2/uade-2.13/src/include/readcpu.h b/plugins/uade2/uade-2.13/src/include/readcpu.h new file mode 100644 index 00000000..62a81c6a --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/readcpu.h @@ -0,0 +1,99 @@ +ENUMDECL { + Dreg, Areg, Aind, Aipi, Apdi, Ad16, Ad8r, + absw, absl, PC16, PC8r, imm, imm0, imm1, imm2, immi, am_unknown, am_illg +} ENUMNAME (amodes); + +ENUMDECL { + i_ILLG, + + i_OR, i_AND, i_EOR, i_ORSR, i_ANDSR, i_EORSR, + i_SUB, i_SUBA, i_SUBX, i_SBCD, + i_ADD, i_ADDA, i_ADDX, i_ABCD, + i_NEG, i_NEGX, i_NBCD, i_CLR, i_NOT, i_TST, + i_BTST, i_BCHG, i_BCLR, i_BSET, + i_CMP, i_CMPM, i_CMPA, + i_MVPRM, i_MVPMR, i_MOVE, i_MOVEA, i_MVSR2, i_MV2SR, + i_SWAP, i_EXG, i_EXT, i_MVMEL, i_MVMLE, + i_TRAP, i_MVR2USP, i_MVUSP2R, i_RESET, i_NOP, i_STOP, i_RTE, i_RTD, + i_LINK, i_UNLK, + i_RTS, i_TRAPV, i_RTR, + i_JSR, i_JMP, i_BSR, i_Bcc, + i_LEA, i_PEA, i_DBcc, i_Scc, + i_DIVU, i_DIVS, i_MULU, i_MULS, + i_ASR, i_ASL, i_LSR, i_LSL, i_ROL, i_ROR, i_ROXL, i_ROXR, + i_ASRW, i_ASLW, i_LSRW, i_LSLW, i_ROLW, i_RORW, i_ROXLW, i_ROXRW, + i_CHK,i_CHK2, + i_MOVEC2, i_MOVE2C, i_CAS, i_CAS2, i_DIVL, i_MULL, + i_BFTST,i_BFEXTU,i_BFCHG,i_BFEXTS,i_BFCLR,i_BFFFO,i_BFSET,i_BFINS, + i_PACK, i_UNPK, i_TAS, i_BKPT, i_CALLM, i_RTM, i_TRAPcc, i_MOVES, + i_FPP, i_FDBcc, i_FScc, i_FTRAPcc, i_FBcc, i_FSAVE, i_FRESTORE, + i_MMUOP +} ENUMNAME (instrmnem); + +extern struct mnemolookup { + instrmnem mnemo; + const char *name; +} lookuptab[]; + +ENUMDECL { + sz_byte, sz_word, sz_long +} ENUMNAME (wordsizes); + +ENUMDECL { + fa_set, fa_unset, fa_zero, fa_one, fa_dontcare, fa_unknown, fa_isjmp +} ENUMNAME (flagaffect); + +ENUMDECL { + fu_used, fu_unused, fu_maybecc, fu_unknown, fu_isjmp +} ENUMNAME (flaguse); + +ENUMDECL { + bit0, bit1, bitc, bitC, bitf, biti, bitI, bitj, bitJ, bitk, bitK, + bits, bitS, bitd, bitD, bitr, bitR, bitz, lastbit +} ENUMNAME (bitvals); + +struct instr_def { + unsigned int bits; + int n_variable; + char bitpos[16]; + unsigned int mask; + int cpulevel; + int plevel; + struct { + unsigned int flaguse:3; + unsigned int flagset:3; + } flaginfo[5]; + unsigned char sduse; + const char *opcstr; +}; + +extern struct instr_def defs68k[]; +extern int n_defs68k; + +extern struct instr { + long int handler; + unsigned char dreg; + unsigned char sreg; + signed char dpos; + signed char spos; + unsigned char sduse; + int flagdead:8, flaglive:8; + unsigned int mnemo:8; + unsigned int cc:4; + unsigned int plev:2; + unsigned int size:2; + unsigned int smode:5; + unsigned int stype:3; + unsigned int dmode:5; + unsigned int suse:1; + unsigned int duse:1; + unsigned int unused1:1; + unsigned int clev:3; + unsigned int unused2:5; +} *table68k; + +extern void read_table68k (void); +extern void do_merges (void); +extern int get_no_mismatches (void); +extern int nr_cpuop_funcs; + diff --git a/plugins/uade2/uade-2.13/src/include/sinctable.h b/plugins/uade2/uade-2.13/src/include/sinctable.h new file mode 100644 index 00000000..56fb62d7 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/sinctable.h @@ -0,0 +1,7 @@ +#ifndef _SINCTABLE_H_ +#define _SINCTABLE_H_ + +#define SINC_QUEUE_MAX_AGE 2048 +extern const int winsinc_integral[5][SINC_QUEUE_MAX_AGE]; + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/sysdeps.h b/plugins/uade2/uade-2.13/src/include/sysdeps.h new file mode 100644 index 00000000..5d407b2d --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/sysdeps.h @@ -0,0 +1,347 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Try to include the right system headers and get other system-specific + * stuff right & other collected kludges. + * + * If you think about modifying this, think twice. Some systems rely on + * the exact order of the #include statements. That's also the reason + * why everything gets included unconditionally regardless of whether + * it's actually needed by the .c file. + * + * Copyright 1996, 1997 Bernd Schmidt + */ + +/* MODIF PMO */ +#ifndef _H_SYSDEPS +#define _H_SYSDEPS +/* ENDOF MODIF PMO */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <limits.h> + +#ifndef __STDC__ +#error "Your compiler is not ANSI. Get a real one." +#endif + +#include <stdarg.h> + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_VALUES_H +#include <values.h> +#endif + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if HAVE_DIRENT_H +# include <dirent.h> +#else +# define dirent direct +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#ifdef HAVE_SYS_UTIME_H +# include <sys/utime.h> +#endif + +#include <errno.h> +#include <assert.h> + +#if EEXIST == ENOTEMPTY +#define BROKEN_OS_PROBABLY_AIX +#endif + +#ifdef __NeXT__ +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC +#define S_ISDIR(val) (S_IFDIR & val) +struct utimbuf +{ + time_t actime; + time_t modtime; +}; +#endif + +#define REGPARAM2 + +#ifdef __DOS__ +#include <pc.h> +#include <io.h> +#endif + +/* MODIF PMO */ +/* WIN32/UNIX compatibility */ +#ifdef WINDOWS_VCPP + #define COMP_SEPARATOR '\\' + #define COMP_S_SEPARATOR "\\" +#else + #define COMP_SEPARATOR '/' + #define COMP_S_SEPARATOR "/" +#endif +/* ENDOF MODIF PMO */ + +/* Acorn specific stuff */ +#ifdef ACORN + +#define S_IRUSR S_IREAD +#define S_IWUSR S_IWRITE +#define S_IXUSR S_IEXEC + +#define strcasecmp stricmp + +#endif + +#ifndef L_tmpnam +#define L_tmpnam 128 /* ought to be safe */ +#endif + +/* If char has more then 8 bits, good night. */ +typedef unsigned char uae_u8; +typedef signed char uae_s8; + +typedef struct { uae_u8 RGB[3]; } RGB; + +#if SIZEOF_SHORT == 2 +typedef unsigned short uae_u16; +typedef short uae_s16; +#elif SIZEOF_INT == 2 +typedef unsigned int uae_u16; +typedef int uae_s16; +#else +#error No 2 byte type, you lose. +#endif + +#if SIZEOF_INT == 4 +typedef unsigned int uae_u32; +typedef int uae_s32; +#elif SIZEOF_LONG == 4 +typedef unsigned long uae_u32; +typedef long uae_s32; +#else +#error No 4 byte type, you lose. +#endif + +typedef uae_u32 uaecptr; + +#undef uae_s64 +#undef uae_u64 + +#if SIZEOF_LONG_LONG == 8 +#define uae_s64 long long +#define uae_u64 long long +#define VAL64(a) (a ## LL) +#define UVAL64(a) (a ## uLL) +#elif SIZEOF___INT64 == 8 +#define uae_s64 __int64 +#define uae_u64 unsigned __int64 +#define VAL64(a) (a) +#define UVAL64(a) (a) +#elif SIZEOF_LONG == 8 +#define uae_s64 long; +#define uae_u64 unsigned long; +#define VAL64(a) (a ## l) +#define UVAL64(a) (a ## ul) +#endif + +#ifdef HAVE_STRDUP +#define my_strdup strdup +#else +extern char *my_strdup (const char*s); +#endif +extern void *xmalloc(size_t); + +/* We can only rely on GNU C getting enums right. Mickeysoft VSC++ is known + * to have problems, and it's likely that other compilers choke too. */ +#ifdef __GNUC__ +#define ENUMDECL typedef enum +#define ENUMNAME(name) name +#else +#define ENUMDECL enum +#define ENUMNAME(name) ; typedef int name +#endif + +/* + * Porters to weird systems, look! This is the preferred way to get + * filesys.c (and other stuff) running on your system. Define the + * appropriate macros and implement wrappers in a machine-specific file. + * + * I guess the Mac port could use this (Ernesto?) + */ + +#undef DONT_HAVE_POSIX +#undef DONT_HAVE_REAL_POSIX /* define if open+delete doesn't do what it should */ +#undef DONT_HAVE_STDIO +#undef DONT_HAVE_MALLOC + +#if defined _WIN32 + +#if defined __WATCOMC__ + +#define O_NDELAY 0 +#include <direct.h> +#define dirent direct +#define mkdir(a,b) mkdir(a) +#define strcasecmp stricmp + +#elif defined __MINGW32__ + +#define O_NDELAY 0 +#define mkdir(a,b) mkdir(a) + +#endif + +#endif /* _WIN32 */ + +#ifdef DONT_HAVE_POSIX + +#define access posixemu_access +extern int posixemu_access (const char *, int); +#define open posixemu_open +extern int posixemu_open (const char *, int, int); +#define close posixemu_close +extern void posixemu_close (int); +#define read posixemu_read +extern int posixemu_read (int, char *, int); +#define write posixemu_write +extern int posixemu_write (int, const char *, int); +#undef lseek +#define lseek posixemu_seek +extern int posixemu_seek (int, int, int); +#define stat(a,b) posixemu_stat ((a), (b)) +extern int posixemu_stat (const char *, STAT *); +#define mkdir posixemu_mkdir +extern int mkdir (const char *, int); +#define rmdir posixemu_rmdir +extern int posixemu_rmdir (const char *); +#define unlink posixemu_unlink +extern int posixemu_unlink (const char *); +#define truncate posixemu_truncate +extern int posixemu_truncate (const char *, long int); +#define rename posixemu_rename +extern int posixemu_rename (const char *, const char *); +#define chmod posixemu_chmod +extern int posixemu_chmod (const char *, int); +#define tmpnam posixemu_tmpnam +extern void posixemu_tmpnam (char *); +#define utime posixemu_utime +extern int posixemu_utime (const char *, struct utimbuf *); +#define opendir posixemu_opendir +extern DIR * posixemu_opendir (const char *); +#define readdir posixemu_readdir +extern struct dirent* readdir (DIR *); +#define closedir posixemu_closedir +extern void closedir (DIR *); + +/* This isn't the best place for this, but it fits reasonably well. The logic + * is that you probably don't have POSIX errnos if you don't have the above + * functions. */ +extern long dos_errno (void); + +#endif + +#ifdef DONT_HAVE_STDIO + +extern FILE *stdioemu_fopen (const char *, const char *); +#define fopen(a,b) stdioemu_fopen(a, b) +extern int stdioemu_fseek (FILE *, int, int); +#define fseek(a,b,c) stdioemu_fseek(a, b, c) +extern int stdioemu_fread (char *, int, int, FILE *); +#define fread(a,b,c,d) stdioemu_fread(a, b, c, d) +extern int stdioemu_fwrite (const char *, int, int, FILE *); +#define fwrite(a,b,c,d) stdioemu_fwrite(a, b, c, d) +extern int stdioemu_ftell (FILE *); +#define ftell(a) stdioemu_ftell(a) +extern int stdioemu_fclose (FILE *); +#define fclose(a) stdioemu_fclose(a) + +#endif + +#ifdef DONT_HAVE_MALLOC + +#define malloc(a) mallocemu_malloc(a) +extern void *mallocemu_malloc (int size); +#define free(a) mallocemu_free(a) +extern void mallocemu_free (void *ptr); + +#endif + +#ifdef X86_ASSEMBLY +#define ASM_SYM_FOR_FUNC(a) __asm__(a) +#else +#define ASM_SYM_FOR_FUNC(a) +#endif + +#if defined USE_COMPILER +#undef NO_PREFETCH_BUFFER +#undef NO_EXCEPTION_3 +#define NO_EXCEPTION_3 +#define NO_PREFETCH_BUFFER +#endif + +#include "target.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* Every Amiga hardware clock cycle takes this many "virtual" cycles. This + used to be hardcoded as 1, but using higher values allows us to time some + stuff more precisely. + 512 is the official value from now on - it can't change, unless we want + _another_ config option "finegrain2_m68k_speed". + + We define this value here rather than in events.h so that gencpu.c sees + it. */ +#define CYCLE_UNIT 512 + +/* MODIF PMO */ +#endif + +/* ENDOF MODIF PMO */ + diff --git a/plugins/uade2/uade-2.13/src/include/text_scope.h b/plugins/uade2/uade-2.13/src/include/text_scope.h new file mode 100644 index 00000000..7bc9d206 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/text_scope.h @@ -0,0 +1,21 @@ +#ifndef _TEXT_SCOPE_H_ +#define _TEXT_SCOPE_H_ + +#include "uadeconfig.h" + +#ifdef UADE_CONFIG_TEXT_SCOPE +#define TEXT_SCOPE(cycles, voice, e, value) \ + do { \ + if (use_text_scope) \ + text_scope(cycles, voice, e, value); \ + } while (0) +#else +#define TEXT_SCOPE(cycles, voice, e, value) do {} while (0) +#endif + +enum PaulaEventType {PET_VOL, PET_PER, PET_DAT, PET_LEN, PET_LCH, PET_LCL}; + +void text_scope(unsigned long cycles, int voice, enum PaulaEventType e, + int value); + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/uade.h b/plugins/uade2/uade-2.13/src/include/uade.h new file mode 100644 index 00000000..91f590fe --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/uade.h @@ -0,0 +1,43 @@ +#ifndef _UADE_MAIN_H_ +#define _UADE_MAIN_H_ + +#include <limits.h> +#include <stdlib.h> + +#include "uadeipc.h" + +struct uade_song { + char playername[PATH_MAX]; /* filename of eagleplayer */ + char modulename[PATH_MAX]; /* filename of song */ + char scorename[PATH_MAX]; /* filename of score file */ + + int min_subsong; + int max_subsong; + int cur_subsong; +}; + + +void uade_change_subsong(int subs); +void uade_check_sound_buffers(int bytes); +void uade_send_debug(const char *fmt, ...); +void uade_get_amiga_message(void); +void uade_handle_r_state(void); +void uade_option(int, char**); /* handles command line parameters */ +void uade_reset(void); +void uade_send_amiga_message(int msgtype); +void uade_set_automatic_song_end(int song_end_possible); +void uade_set_ntsc(int usentsc); +void uade_song_end(char *reason, int kill_it); +void uade_swap_buffer_bytes(void *data, int bytes); + +extern int uade_audio_output; +extern int uade_audio_skip; +extern int uade_debug; +extern int uade_local_sound; +extern int uade_read_size; +extern int uade_reboot; +extern int uade_time_critical; + +extern struct uade_ipc uadeipc; + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/uadeconstants.h b/plugins/uade2/uade-2.13/src/include/uadeconstants.h new file mode 100644 index 00000000..5a69953a --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/uadeconstants.h @@ -0,0 +1,10 @@ +#ifndef _UADE_CONSTANTS_H_ +#define _UADE_CONSTANTS_H_ + +/* You must not change anything */ +#define UADE_CHANNELS (2) +#define UADE_DEFAULT_FREQUENCY (44100) +#define UADE_BYTES_PER_SAMPLE (2) +#define UADE_BYTES_PER_FRAME (UADE_CHANNELS * UADE_BYTES_PER_SAMPLE) + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/uadeipc.h b/plugins/uade2/uade-2.13/src/include/uadeipc.h new file mode 100644 index 00000000..3bad980b --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/uadeipc.h @@ -0,0 +1,77 @@ +#ifndef _UADEIPC_H_ +#define _UADEIPC_H_ + +#include <stdlib.h> +#include <stdint.h> + +#include "uadeutils.h" + +#define UADE_MAX_MESSAGE_SIZE (4096) + +enum uade_msgtype { + UADE_MSG_FIRST = 0, + UADE_COMMAND_ACTIVATE_DEBUGGER, + UADE_COMMAND_CHANGE_SUBSONG, + UADE_COMMAND_CONFIG, + UADE_COMMAND_SCORE, + UADE_COMMAND_PLAYER, + UADE_COMMAND_MODULE, + UADE_COMMAND_READ, + UADE_COMMAND_REBOOT, + UADE_COMMAND_SET_SUBSONG, + UADE_COMMAND_IGNORE_CHECK, + UADE_COMMAND_SONG_END_NOT_POSSIBLE, + UADE_COMMAND_SET_NTSC, + UADE_COMMAND_FILTER, + UADE_COMMAND_SET_FREQUENCY, + UADE_COMMAND_SET_PLAYER_OPTION, + UADE_COMMAND_SET_RESAMPLING_MODE, + UADE_COMMAND_SPEED_HACK, + UADE_COMMAND_TOKEN, + UADE_COMMAND_USE_TEXT_SCOPE, + UADE_REPLY_MSG, + UADE_REPLY_CANT_PLAY, + UADE_REPLY_CAN_PLAY, + UADE_REPLY_SONG_END, + UADE_REPLY_SUBSONG_INFO, + UADE_REPLY_PLAYERNAME, + UADE_REPLY_MODULENAME, + UADE_REPLY_FORMATNAME, + UADE_REPLY_DATA, + UADE_MSG_LAST +}; + +struct uade_msg { + uint32_t msgtype; + uint32_t size; + uint8_t data[0]; +} __attribute__((packed)); + +enum uade_control_state { + UADE_INITIAL_STATE = 0, + UADE_R_STATE, + UADE_S_STATE +}; + +struct uade_ipc { + void *input; + void *output; + unsigned int inputbytes; + char inputbuffer[UADE_MAX_MESSAGE_SIZE]; + enum uade_control_state state; +}; + +void uade_check_fix_string(struct uade_msg *um, size_t maxlen); +int uade_parse_u32_message(uint32_t *u1, struct uade_msg *um); +int uade_parse_two_u32s_message(uint32_t *u1, uint32_t *u2, struct uade_msg *um); +int uade_receive_message(struct uade_msg *um, size_t maxbytes, struct uade_ipc *ipc); +int uade_receive_short_message(enum uade_msgtype msgtype, struct uade_ipc *ipc); +int uade_receive_string(char *s, enum uade_msgtype msgtype, size_t maxlen, struct uade_ipc *ipc); +int uade_send_message(struct uade_msg *um, struct uade_ipc *ipc); +int uade_send_short_message(enum uade_msgtype msgtype, struct uade_ipc *ipc); +int uade_send_string(enum uade_msgtype msgtype, const char *str, struct uade_ipc *ipc); +int uade_send_u32(enum uade_msgtype com, uint32_t u, struct uade_ipc *ipc); +int uade_send_two_u32s(enum uade_msgtype com, uint32_t u1, uint32_t u2, struct uade_ipc *ipc); +void uade_set_peer(struct uade_ipc *ipc, int peer_is_client, const char *input, const char *output); + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/uadeutils.h b/plugins/uade2/uade-2.13/src/include/uadeutils.h new file mode 100644 index 00000000..fcd24233 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/uadeutils.h @@ -0,0 +1,27 @@ +#ifndef _UADE_UTILS_H_ +#define _UADE_UTILS_H_ + +#include <stdint.h> + +#define uade_error(fmt, args...) do { \ + fprintf(stderr, "%s:%d: %s: " fmt, __FILE__, __LINE__, __func__, ## args); \ + abort(); \ + } while (0) + +static inline uint16_t read_be_u16(void *s) +{ + uint16_t x; + uint8_t *ptr = (uint8_t *) s; + x = ptr[1] + (ptr[0] << 8); + return x; +} + +static inline uint32_t read_be_u32(void *s) +{ + uint32_t x; + uint8_t *ptr = (uint8_t *) s; + x = (ptr[0] << 24) + (ptr[1] << 16) + (ptr[2] << 8) + ptr[3]; + return x; +} + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/uae.h b/plugins/uade2/uade-2.13/src/include/uae.h new file mode 100644 index 00000000..1ab9d75f --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/uae.h @@ -0,0 +1,30 @@ + /* + * UAE - The Un*x Amiga Emulator + * + * Prototypes for main.c + * + * Copyright 1996 Bernd Schmidt + */ + +extern int uade_main (int argc, char **argv); +extern void uae_quit (void); + +extern int quit_program; + +extern char warning_buffer[256]; + +/* This structure is used to define menus. The val field can hold key + * shortcuts, or one of these special codes: + * -4: deleted entry, not displayed, not selectable, but does count in + * select value + * -3: end of table + * -2: line that is displayed, but not selectable + * -1: line that is selectable, but has no keyboard shortcut + * 0: Menu title + */ +struct bstring { + const char *data; + int val; +}; + +extern char *colormodes[]; diff --git a/plugins/uade2/uade-2.13/src/include/unixatomic.h b/plugins/uade2/uade-2.13/src/include/unixatomic.h new file mode 100644 index 00000000..565cf864 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/unixatomic.h @@ -0,0 +1,15 @@ +#ifndef _UNIXATOMIC_H_ +#define _UNIXATOMIC_H_ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +int atomic_close(int fd); +int atomic_dup2(int oldfd, int newfd); +size_t atomic_fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +ssize_t atomic_read(int fd, const void *buf, size_t count); +void *atomic_read_file(size_t *fs, const char *filename); +ssize_t atomic_write(int fd, const void *buf, size_t count); + +#endif diff --git a/plugins/uade2/uade-2.13/src/include/unixsupport.h b/plugins/uade2/uade-2.13/src/include/unixsupport.h new file mode 100644 index 00000000..dc7d545e --- /dev/null +++ b/plugins/uade2/uade-2.13/src/include/unixsupport.h @@ -0,0 +1,32 @@ +#ifndef _UADE_UNIXSUPPORT_H_ +#define _UADE_UNIXSUPPORT_H_ + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <string.h> +#include <errno.h> + +#include "uadeipc.h" + + +#define die(fmt, args...) do { fprintf(stderr, "uade: " fmt, ## args); exit(1); } while(0) + +#define dieerror(fmt, args...) do { fprintf(stderr, "uade: " fmt ": %s\n", ## args, strerror(errno)); exit(1); } while(0) + + +char *uade_dirname(char *dst, char *src, size_t maxlen); +FILE *uade_open_amiga_file(char *aname, const char *playerdir); +void uade_portable_initializations(void); +void uade_arch_spawn(struct uade_ipc *ipc, pid_t *uadepid, const char *uadename); + +/* These read and write functions MUST read and write the full size_t amount + if they are able to. */ +ssize_t uade_ipc_read(void *f, const void *buf, size_t count); +ssize_t uade_ipc_write(void *f, const void *buf, size_t count); +void *uade_ipc_set_input(const char *input); +void *uade_ipc_set_output(const char *output); + +char *windows_to_cygwin_path(const char *path); + +#endif diff --git a/plugins/uade2/uade-2.13/src/ossupport.c b/plugins/uade2/uade-2.13/src/ossupport.c new file mode 100644 index 00000000..6ecfb53e --- /dev/null +++ b/plugins/uade2/uade-2.13/src/ossupport.c @@ -0,0 +1,48 @@ +#include "ossupport.h" + +#include "unixsupport.c" +/* This module was written by Heikki Orsila <heikki.orsila@iki.fi> 2000-2005. + * No copyrights claimed, so this module is in Public Domain (only this + * code module). See OpenBSD man pages for strlcat and strlcpy + */ + +#include <string.h> + +size_t strlcpy(char *dst, const char *src, size_t size) +{ + size_t slen = strlen(src); + if(slen < size) + strcpy(dst, src); + else if (size > 0) { + strncpy(dst, src, size-1); + dst[size-1] = 0; + } + return slen; +} + + +size_t strlcat(char *dst, const char *src, size_t size) +{ + size_t slen = strlen(src); + size_t dlen = 0; + while(dlen < size) { + if(dst[dlen] == 0) + break; + dlen++; + } + + if(dlen == size) { + return slen + dlen; + } + + if((dlen + slen) < size) + strcat(dst, src); + else { + int left = size - dlen - 1; + if(left > 0) { + strncat(dst, src, left); + } + dst[size-1] = 0; + } + return slen + dlen; +} diff --git a/plugins/uade2/uade-2.13/src/uadeipc.c b/plugins/uade2/uade-2.13/src/uadeipc.c new file mode 100644 index 00000000..cba45ce0 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/uadeipc.c @@ -0,0 +1,291 @@ +/* UADE + * + * Copyright 2005 Heikki Orsila <heikki.orsila@iki.fi> + * + * This source code module is dual licensed under GPL and Public Domain. + * Hence you may use _this_ module (not another code module) in any way you + * want in your projects. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <string.h> + +#include "uadeipc.h" +#include "ossupport.h" +#include "sysincludes.h" + + +static int valid_message(struct uade_msg *uc); + + +void uade_check_fix_string(struct uade_msg *um, size_t maxlen) +{ + uint8_t *s = (uint8_t *) um->data; + size_t safelen; + if (um->size == 0) { + s[0] = 0; + fprintf(stderr, "zero string detected\n"); + } + safelen = 0; + while (s[safelen] != 0 && safelen < maxlen) + safelen++; + if (safelen == maxlen) { + safelen--; + fprintf(stderr, "too long a string\n"); + s[safelen] = 0; + } + if (um->size != (safelen + 1)) { + fprintf(stderr, "string size does not match\n"); + um->size = safelen + 1; + s[safelen] = 0; + } +} + + +static ssize_t get_more(size_t bytes, struct uade_ipc *ipc) +{ + if (ipc->inputbytes < bytes) { + ssize_t s = uade_ipc_read(ipc->input, &ipc->inputbuffer[ipc->inputbytes], bytes - ipc->inputbytes); + if (s <= 0) + return -1; + ipc->inputbytes += s; + } + return 0; +} + + +static void copy_from_inputbuffer(void *dst, int bytes, struct uade_ipc *ipc) +{ + if (ipc->inputbytes < bytes) { + fprintf(stderr, "not enough bytes in input buffer\n"); + exit(-1); + } + memcpy(dst, ipc->inputbuffer, bytes); + memmove(ipc->inputbuffer, &ipc->inputbuffer[bytes], ipc->inputbytes - bytes); + ipc->inputbytes -= bytes; +} + + +int uade_parse_u32_message(uint32_t *u1, struct uade_msg *um) +{ + if (um->size != 4) + return -1; + *u1 = ntohl(* (uint32_t *) um->data); + return 0; +} + + +int uade_parse_two_u32s_message(uint32_t *u1, uint32_t *u2, + struct uade_msg *um) +{ + if (um->size != 8) + return -1; + *u1 = ntohl(((uint32_t *) um->data)[0]); + *u2 = ntohl(((uint32_t *) um->data)[1]); + return 0; +} + + +int uade_receive_message(struct uade_msg *um, size_t maxbytes, + struct uade_ipc *ipc) +{ + size_t fullsize; + + if (ipc->state == UADE_INITIAL_STATE) { + ipc->state = UADE_R_STATE; + } else if (ipc->state == UADE_S_STATE) { + fprintf(stderr, "protocol error: receiving in S state is forbidden\n"); + return -1; + } + + if (ipc->inputbytes < sizeof(*um)) { + if (get_more(sizeof(*um), ipc)) + return 0; + } + + copy_from_inputbuffer(um, sizeof(*um), ipc); + + um->msgtype = ntohl(um->msgtype); + um->size = ntohl(um->size); + + if (!valid_message(um)) + return -1; + + fullsize = um->size + sizeof(*um); + if (fullsize > maxbytes) { + fprintf(stderr, "too big a command: %zu\n", fullsize); + return -1; + } + if (ipc->inputbytes < um->size) { + if (get_more(um->size, ipc)) + return -1; + } + copy_from_inputbuffer(&um->data, um->size, ipc); + + if (um->msgtype == UADE_COMMAND_TOKEN) + ipc->state = UADE_S_STATE; + + return 1; +} + + +int uade_receive_short_message(enum uade_msgtype msgtype, struct uade_ipc *ipc) +{ + struct uade_msg um; + + if (ipc->state == UADE_INITIAL_STATE) { + ipc->state = UADE_R_STATE; + } else if (ipc->state == UADE_S_STATE) { + fprintf(stderr, "protocol error: receiving (%d) in S state is forbidden\n", msgtype); + return -1; + } + + if (uade_receive_message(&um, sizeof(um), ipc) <= 0) { + fprintf(stderr, "can not receive short message: %d\n", msgtype); + return -1; + } + return (um.msgtype == msgtype) ? 0 : -1; +} + + +int uade_receive_string(char *s, enum uade_msgtype com, + size_t maxlen, struct uade_ipc *ipc) +{ + uint8_t commandbuf[UADE_MAX_MESSAGE_SIZE]; + struct uade_msg *um = (struct uade_msg *) commandbuf; + int ret; + + if (ipc->state == UADE_INITIAL_STATE) { + ipc->state = UADE_R_STATE; + } else if (ipc->state == UADE_S_STATE) { + fprintf(stderr, "protocol error: receiving in S state is forbidden\n"); + return -1; + } + + ret = uade_receive_message(um, UADE_MAX_MESSAGE_SIZE, ipc); + if (ret <= 0) + return ret; + if (um->msgtype != com) + return -1; + if (um->size == 0) + return -1; + if (um->size != (strlen((char *) um->data) + 1)) + return -1; + strlcpy(s, (char *) um->data, maxlen); + return 1; +} + + +int uade_send_message(struct uade_msg *um, struct uade_ipc *ipc) +{ + uint32_t size = um->size; + + if (ipc->state == UADE_INITIAL_STATE) { + ipc->state = UADE_S_STATE; + } else if (ipc->state == UADE_R_STATE) { + fprintf(stderr, "protocol error: sending in R state is forbidden\n"); + return -1; + } + + if (!valid_message(um)) + return -1; + if (um->msgtype == UADE_COMMAND_TOKEN) + ipc->state = UADE_R_STATE; + um->msgtype = htonl(um->msgtype); + um->size = htonl(um->size); + if (uade_ipc_write(ipc->output, um, sizeof(*um) + size) < 0) + return -1; + + return 0; +} + + +int uade_send_short_message(enum uade_msgtype msgtype, struct uade_ipc *ipc) +{ + struct uade_msg msg = {.msgtype = msgtype}; + + if (uade_send_message(&msg, ipc)) { + fprintf(stderr, "can not send short message: %d\n", msgtype); + return -1; + } + return 0; +} + + +int uade_send_string(enum uade_msgtype com, const char *str, struct uade_ipc *ipc) +{ + uint32_t size = strlen(str) + 1; + struct uade_msg um = {.msgtype = ntohl(com), .size = ntohl(size)}; + + if (ipc->state == UADE_INITIAL_STATE) { + ipc->state = UADE_S_STATE; + } else if (ipc->state == UADE_R_STATE) { + fprintf(stderr, "protocol error: sending in R state is forbidden\n"); + return -1; + } + + if ((sizeof(um) + size) > UADE_MAX_MESSAGE_SIZE) + return -1; + if (uade_ipc_write(ipc->output, &um, sizeof(um)) < 0) + return -1; + if (uade_ipc_write(ipc->output, str, size) < 0) + return -1; + + return 0; +} + + +int uade_send_u32(enum uade_msgtype com, uint32_t u, struct uade_ipc *ipc) +{ + uint8_t space[UADE_MAX_MESSAGE_SIZE]; + struct uade_msg *um = (struct uade_msg *) space; + um->msgtype = com; + um->size = 4; + * (uint32_t *) um->data = htonl(u); + return uade_send_message(um, ipc); +} + + +int uade_send_two_u32s(enum uade_msgtype com, uint32_t u1, uint32_t u2, + struct uade_ipc *ipc) +{ + uint8_t space[UADE_MAX_MESSAGE_SIZE]; + struct uade_msg *um = (struct uade_msg *) space; + um->msgtype = com; + um->size = 8; + ((uint32_t *) um->data)[0] = htonl(u1); + ((uint32_t *) um->data)[1] = htonl(u2); + return uade_send_message(um, ipc); +} + + +void uade_set_peer(struct uade_ipc *ipc, int peer_is_client, const char *input, const char *output) +{ + assert(peer_is_client == 0 || peer_is_client == 1); + assert(input != NULL); + assert(output != NULL); + + *ipc = (struct uade_ipc) {.state = UADE_INITIAL_STATE, + .input= uade_ipc_set_input(input), + .output = uade_ipc_set_output(output)}; +} + + +static int valid_message(struct uade_msg *um) +{ + size_t len; + if (um->msgtype <= UADE_MSG_FIRST || um->msgtype >= UADE_MSG_LAST) { + fprintf(stderr, "unknown command: %u\n", (unsigned int) um->msgtype); + return 0; + } + len = sizeof(*um) + um->size; + if (len > UADE_MAX_MESSAGE_SIZE) { + fprintf(stderr, "too long a message: %zu\n", len); + return 0; + } + return 1; +} diff --git a/plugins/uade2/uade-2.13/src/unixatomic.c b/plugins/uade2/uade-2.13/src/unixatomic.c new file mode 100644 index 00000000..1adbe27a --- /dev/null +++ b/plugins/uade2/uade-2.13/src/unixatomic.c @@ -0,0 +1,149 @@ +#include <errno.h> +#include <stdint.h> +#include <assert.h> + +#include "unixatomic.h" +#include "sysincludes.h" + +int atomic_close(int fd) +{ + while (1) { + if (close(fd) < 0) { + if (errno == EINTR) + continue; + return -1; + } + break; + } + return 0; +} + + +int atomic_dup2(int oldfd, int newfd) +{ + while (1) { + if (dup2(oldfd, newfd) < 0) { + if (errno == EINTR) + continue; + return -1; + } + break; + } + return newfd; +} + + +size_t atomic_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + uint8_t *dest = ptr; + size_t readmembers = 0; + size_t ret; + + while (readmembers < nmemb) { + ret = fread(dest + size * readmembers, size, nmemb - readmembers, stream); + if (ret == 0) + break; + readmembers += ret; + } + + assert(readmembers <= nmemb); + + return readmembers; +} + + +ssize_t atomic_read(int fd, const void *buf, size_t count) +{ + char *b = (char *) buf; + ssize_t bytes_read = 0; + ssize_t ret; + while (bytes_read < count) { + ret = read(fd, &b[bytes_read], count - bytes_read); + if (ret < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) { + fd_set s; + FD_ZERO(&s); + FD_SET(fd, &s); + if (select(fd + 1, &s, NULL, NULL, NULL) == 0) + fprintf(stderr, "atomic_read: very strange. infinite select() returned 0. report this!\n"); + continue; + } + return -1; + } else if (ret == 0) { + return 0; + } + bytes_read += ret; + } + return bytes_read; +} + + +void *atomic_read_file(size_t *fs, const char *filename) +{ + FILE *f; + size_t off; + void *mem = NULL; + size_t msize; + long pos; + + if ((f = fopen(filename, "rb")) == NULL) + goto error; + + if (fseek(f, 0, SEEK_END)) + goto error; + pos = ftell(f); + if (pos < 0) + goto error; + if (fseek(f, 0, SEEK_SET)) + goto error; + + *fs = pos; + msize = (pos > 0) ? pos : 1; + + if ((mem = malloc(msize)) == NULL) + goto error; + + off = atomic_fread(mem, 1, *fs, f); + if (off < *fs) { + fprintf(stderr, "Not able to read the whole file %s\n", filename); + goto error; + } + + fclose(f); + return mem; + + error: + if (f) + fclose(f); + free(mem); + *fs = 0; + return NULL; +} + + +ssize_t atomic_write(int fd, const void *buf, size_t count) +{ + char *b = (char *) buf; + ssize_t bytes_written = 0; + ssize_t ret; + while (bytes_written < count) { + ret = write(fd, &b[bytes_written], count - bytes_written); + if (ret < 0) { + if (errno == EINTR) + continue; + if (errno == EAGAIN) { + fd_set s; + FD_ZERO(&s); + FD_SET(fd, &s); + if (select(fd + 1, NULL, &s, NULL, NULL) == 0) + fprintf(stderr, "atomic_write: very strange. infinite select() returned 0. report this!\n"); + continue; + } + return -1; + } + bytes_written += ret; + } + return bytes_written; +} diff --git a/plugins/uade2/uade-2.13/src/unixsupport.c b/plugins/uade2/uade-2.13/src/unixsupport.c new file mode 100644 index 00000000..d6131bc2 --- /dev/null +++ b/plugins/uade2/uade-2.13/src/unixsupport.c @@ -0,0 +1,350 @@ +/* UNIX support tools for uadecore. + + Copyright 2000 - 2005 (C) Heikki Orsila <heikki.orsila@iki.fi> + + This module is licensed under the GNU GPL. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <libgen.h> +#include <sys/socket.h> +#include <unistd.h> +#include <limits.h> +#include <ctype.h> + +#include "uade.h" +#include "unixatomic.h" + + +static int url_to_fd(const char *url, int flags, mode_t mode) +{ + int fd; + if (strncmp(url, "fd://", 5) == 0) { + char *endptr; + if (url[5] == 0) + return -1; + fd = strtol(&url[5], &endptr, 10); + if (*endptr != 0) + return -1; + } else { + if (flags & O_WRONLY) { + fd = open(url, flags, mode); + } else { + fd = open(url, flags); + } + } + if (fd < 0) + fd = -1; + return fd; +} + + +/* This must read the full size_t count if it can, and therefore we use + atomic_read() */ +ssize_t uade_ipc_read(void *f, const void *buf, size_t count) +{ + int fd = (intptr_t) f; + return atomic_read(fd, buf, count); +} + + +/* This must write the full size_t count if it can, and therefore we use + atomic_write() */ +ssize_t uade_ipc_write(void *f, const void *buf, size_t count) +{ + int fd = (intptr_t) f; + return atomic_write(fd, buf, count); +} + + +void *uade_ipc_set_input(const char *input) +{ + int fd; + if ((fd = url_to_fd(input, O_RDONLY, 0)) < 0) { + fprintf(stderr, "can not open input file %s: %s\n", input, strerror(errno)); + exit(-1); + } + return (void *) ((intptr_t) fd); +} + + +void *uade_ipc_set_output(const char *output) +{ + int fd; + if ((fd = url_to_fd(output, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + fprintf(stderr, "can not open output file %s: %s\n", output, strerror(errno)); + exit(-1); + } + return (void *) ((intptr_t) fd); +} + + +static int uade_amiga_scandir(char *real, char *dirname, char *fake, int ml) +{ + DIR *dir; + struct dirent *direntry; + if (!(dir = opendir(dirname))) { + fprintf(stderr, "uade: can't open dir (%s) (amiga scandir)\n", dirname); + return 0; + } + while ((direntry = readdir(dir))) { + if (!strcmp(fake, direntry->d_name)) { + if (((int) strlcpy(real, direntry->d_name, ml)) >= ml) { + fprintf(stderr, "uade: %s does not fit real", direntry->d_name); + closedir(dir); + return 0; + } + break; + } + } + if (direntry) { + closedir(dir); + return 1; + } + rewinddir(dir); + while ((direntry = readdir(dir))) { + if (!strcasecmp(fake, direntry->d_name)) { + if (((int) strlcpy(real, direntry->d_name, ml)) >= ml) { + fprintf(stderr, "uade: %s does not fit real", direntry->d_name); + closedir(dir); + return 0; + } + break; + } + } + closedir(dir); + return direntry ? 1 : 0; +} + + +char *uade_dirname(char *dst, char *src, size_t maxlen) +{ + char *srctemp = strdup(src); + if (srctemp == NULL) + return NULL; + strlcpy(dst, dirname(srctemp), maxlen); + free(srctemp); + return dst; +} + + +/* opens file in amiga namespace */ +FILE *uade_open_amiga_file(char *aname, const char *playerdir) +{ + char *separator; + char *ptr; + char copy[PATH_MAX]; + char dirname[PATH_MAX]; + char fake[PATH_MAX]; + char real[PATH_MAX]; + int len; + DIR *dir; + FILE *file; + + if (strlcpy(copy, aname, sizeof(copy)) >= sizeof(copy)) { + fprintf(stderr, "uade: error: amiga tried to open a very long filename\nplease REPORT THIS!\n"); + return NULL; + } + ptr = copy; + /* fprintf(stderr, "uade: opening %s\n", ptr); */ + if ((separator = strchr(ptr, (int) ':'))) { + len = (int) (separator - ptr); + memcpy(dirname, ptr, len); + dirname[len] = 0; + if (!strcasecmp(dirname, "ENV")) { + snprintf(dirname, sizeof(dirname), "%s/ENV/", playerdir); + } else if (!strcasecmp(dirname, "S")) { + snprintf(dirname, sizeof(dirname), "%s/S/", playerdir); + } else { + fprintf(stderr, "uade: open_amiga_file: unknown amiga volume (%s)\n", aname); + return NULL; + } + if (!(dir = opendir(dirname))) { + fprintf(stderr, "uade: can't open dir (%s) (volume parsing)\n", dirname); + return NULL; + } + closedir(dir); + /* fprintf(stderr, "uade: opening from dir %s\n", dirname); */ + ptr = separator + 1; + } else { + if (*ptr == '/') { + /* absolute path */ + strlcpy(dirname, "/", sizeof(dirname)); + ptr++; + } else { + /* relative path */ + strlcpy(dirname, "./", sizeof(dirname)); + } + } + + while ((separator = strchr(ptr, (int) '/'))) { + len = (int) (separator - ptr); + if (!len) { + ptr++; + continue; + } + memcpy(fake, ptr, len); + fake[len] = 0; + if (uade_amiga_scandir(real, dirname, fake, sizeof(real))) { + /* found matching entry */ + if (strlcat(dirname, real, sizeof(dirname)) >= sizeof(dirname)) { + fprintf(stderr, "uade: too long dir path (%s + %s)\n", dirname, real); + return NULL; + } + if (strlcat(dirname, "/", sizeof(dirname)) >= sizeof(dirname)) { + fprintf(stderr, "uade: too long dir path (%s + %s)\n", dirname, "/"); + return NULL; + } + } else { + /* didn't find entry */ + /* fprintf (stderr, "uade: %s not found from (%s) (dir scanning)\n", fake, dirname); */ + return NULL; + } + ptr = separator + 1; + } + /* fprintf(stderr, "uade: pass 3: (%s) (%s)\n", dirname, ptr); */ + + if (!(dir = opendir(dirname))) { + fprintf(stderr, "can't open dir (%s) (after dir scanning)\n", dirname); + return NULL; + } + closedir(dir); + + if (uade_amiga_scandir(real, dirname, ptr, sizeof(real))) { + /* found matching entry */ + if (strlcat(dirname, real, sizeof(dirname)) >= sizeof(dirname)) { + fprintf(stderr, "uade: too long dir path (%s + %s)\n", dirname, real); + return NULL; + } + } else { + /* didn't find entry */ + /* fprintf (stderr, "uade: %s not found from %s\n", ptr, dirname); */ + return NULL; + } + if (!(file = fopen(dirname, "r"))) { + fprintf (stderr, "uade: couldn't open file (%s) induced by (%s)\n", dirname, aname); + } + return file; +} + + +void uade_portable_initializations(void) +{ + int signals[] = {SIGINT, -1}; + int *signum = signals; + struct sigaction act; + memset(&act, 0, sizeof act); + act.sa_handler = SIG_IGN; + + while (*signum != -1) { + while (1) { + if ((sigaction(*signum, &act, NULL)) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "can not ignore signal %d: %s\n", *signum, strerror(errno)); + exit(-1); + } + break; + } + signum++; + } +} + + +void uade_arch_spawn(struct uade_ipc *ipc, pid_t *uadepid, + const char *uadename) +{ + int fds[2]; + char input[32], output[32]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { + fprintf(stderr, "Can not create socketpair: %s\n", strerror(errno)); + abort(); + } + + *uadepid = fork(); + if (*uadepid < 0) { + fprintf(stderr, "Fork failed: %s\n", strerror(errno)); + abort(); + } + + /* The child (*uadepid == 0) will execute uadecore */ + if (*uadepid == 0) { + int fd; + int maxfds; + + if ((maxfds = sysconf(_SC_OPEN_MAX)) < 0) { + maxfds = 1024; + fprintf(stderr, "Getting max fds failed. Using %d.\n", maxfds); + } + + /* close everything else but stdin, stdout, stderr, and in/out fds */ + for (fd = 3; fd < maxfds; fd++) { + if (fd != fds[1]) + atomic_close(fd); + } + + /* give in/out fds as command line parameters to the uade process */ + snprintf(input, sizeof(input), "fd://%d", fds[1]); + snprintf(output, sizeof(output), "fd://%d", fds[1]); + + execlp(uadename, uadename, "-i", input, "-o", output, (char *) NULL); + fprintf(stderr, "uade execlp failed: %s\n", strerror(errno)); + abort(); + } + + /* Close fds that the uadecore uses */ + if (atomic_close(fds[1]) < 0) { + fprintf(stderr, "Could not close uadecore fds: %s\n", strerror(errno)); + kill (*uadepid, SIGTERM); + abort(); + } + + do { + snprintf(output, sizeof output, "fd://%d", fds[0]); + snprintf(input, sizeof input, "fd://%d", fds[0]); + uade_set_peer(ipc, 1, input, output); + } while (0); +} + +/* + * A hack that converts X:\something style windows names into cygwin style name + * /cygdrive/X/something. All '\\' characters are converted into '/' + * characters. + */ +char *windows_to_cygwin_path(const char *path) +{ + size_t i; + char *s; + size_t len = strlen(path); + + if (len == 0) + return calloc(1, 1); + + if (len >= 2 && isalpha(path[0]) && path[1] == ':') { + /* uses windows drive names */ + size_t newlen = len + 32; + s = malloc(newlen); + if (s != NULL) + snprintf(s, newlen, "/cygdrive/%c/%s", path[0], &path[2]); + } else { + s = strdup(path); + } + if (s == NULL) + return NULL; + + for (i = 0; s[i] != 0; i++) { + if (s[i] == '\\') + s[i] = '/'; + } + + return s; +} diff --git a/plugins/vfs_curl/Makefile.am b/plugins/vfs_curl/Makefile.am index 95794831..4c69c51f 100644 --- a/plugins/vfs_curl/Makefile.am +++ b/plugins/vfs_curl/Makefile.am @@ -4,6 +4,6 @@ pkglib_LTLIBRARIES = vfs_curl.la vfs_curl_la_SOURCES = vfs_curl.c vfs_curl_la_LDFLAGS = -module -vfs_curl_la_LIBADD = $(LDADD) $(CURL_LIBS) +vfs_curl_la_LIBADD = $(LDADD) $(VFS_CURL_LIBS) AM_CFLAGS = $(CFLAGS) -std=c99 endif diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c index 048f964e..528b3809 100644 --- a/plugins/vfs_curl/vfs_curl.c +++ b/plugins/vfs_curl/vfs_curl.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -46,6 +46,7 @@ enum { STATUS_FINISHED = 2, STATUS_ABORTED = 3, STATUS_SEEK = 4, + STATUS_DESTROY = 5, }; typedef struct { @@ -72,6 +73,8 @@ typedef struct { int metadata_size; // size of metadata in stream int metadata_have_size; // amount which is already in metadata buffer + char http_err[CURL_ERROR_SIZE]; + // flags (bitfields to save some space) unsigned seektoend : 1; // indicates that next tell must return length unsigned gotheader : 1; // tells that all headers (including ICY) were processed (to start reading body) @@ -81,13 +84,34 @@ typedef struct { static DB_vfs_t plugin; -static char http_err[CURL_ERROR_SIZE]; - static int allow_new_streams; static size_t http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream); +static int64_t biglock; + +#define MAX_ABORT_FILES 100 +static DB_FILE *open_files[MAX_ABORT_FILES]; +static int num_open_files = 0; +static DB_FILE *abort_files[MAX_ABORT_FILES]; +static int num_abort_files = 0; + +static int +http_need_abort (DB_FILE *fp); + +static void +http_cancel_abort (DB_FILE *fp); + +static void +http_abort (DB_FILE *fp); + +static void +http_reg_open_file (DB_FILE *fp); + +static void +http_unreg_open_file (DB_FILE *fp); + static size_t http_curl_write_wrapper (HTTP_FILE *fp, void *ptr, size_t size) { size_t avail = size; @@ -98,7 +122,8 @@ http_curl_write_wrapper (HTTP_FILE *fp, void *ptr, size_t size) { deadbeef->mutex_unlock (fp->mutex); return 0; } - if (fp->status == STATUS_ABORTED) { + if (http_need_abort ((DB_FILE*)fp)) { + fp->status = STATUS_ABORTED; trace ("vfs_curl STATUS_ABORTED in the middle of packet\n"); deadbeef->mutex_unlock (fp->mutex); break; @@ -217,13 +242,14 @@ http_curl_write (void *ptr, size_t size, size_t nmemb, void *stream) { // trace ("http_curl_write %d bytes, wait_meta=%d\n", size * nmemb, fp->wait_meta); gettimeofday (&fp->last_read_time, NULL); - if (fp->status == STATUS_ABORTED) { + if (http_need_abort (stream)) { + fp->status = STATUS_ABORTED; trace ("vfs_curl STATUS_ABORTED at start of packet\n"); return 0; } - if (fp->gotsomeheader) { - fp->gotheader = 1; - } +// if (fp->gotsomeheader) { +// fp->gotheader = 1; +// } if (!fp->gotheader) { // check if that's ICY if (!fp->icyheader && avail >= 10 && !memcmp (ptr, "ICY 200 OK", 10)) { @@ -467,7 +493,8 @@ http_curl_control (void *stream, double dltotal, double dlnow, double ultotal, d deadbeef->mutex_unlock (fp->mutex); return -1; } - if (fp->status == STATUS_ABORTED) { + if (http_need_abort ((DB_FILE *)fp)) { + fp->status = STATUS_ABORTED; trace ("vfs_curl STATUS_ABORTED in progress callback\n"); deadbeef->mutex_unlock (fp->mutex); return -1; @@ -477,6 +504,23 @@ http_curl_control (void *stream, double dltotal, double dlnow, double ultotal, d } static void +http_destroy (HTTP_FILE *fp) { + if (fp->content_type) { + free (fp->content_type); + } + if (fp->track) { + deadbeef->pl_item_unref (fp->track); + } + if (fp->url) { + free (fp->url); + } + if (fp->mutex) { + deadbeef->mutex_free (fp->mutex); + } + free (fp); +} + +static void http_thread_func (void *ctx) { HTTP_FILE *fp = (HTTP_FILE *)ctx; CURL *curl; @@ -492,10 +536,11 @@ http_thread_func (void *ctx) { struct curl_slist *headers = NULL; curl_easy_reset (curl); curl_easy_setopt (curl, CURLOPT_URL, fp->url); + curl_easy_setopt (curl, CURLOPT_USERAGENT, "deadbeef"); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, http_curl_write); curl_easy_setopt (curl, CURLOPT_WRITEDATA, ctx); - curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, http_err); + curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, fp->http_err); curl_easy_setopt (curl, CURLOPT_BUFFERSIZE, BUFFER_SIZE/2); curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, http_content_header_handler); @@ -513,9 +558,10 @@ http_thread_func (void *ctx) { curl_easy_setopt (curl, CURLOPT_RESUME_FROM, fp->pos); } if (deadbeef->conf_get_int ("network.proxy", 0)) { - curl_easy_setopt (curl, CURLOPT_PROXY, deadbeef->conf_get_str ("network.proxy.address", "")); + deadbeef->conf_lock (); + curl_easy_setopt (curl, CURLOPT_PROXY, deadbeef->conf_get_str_fast ("network.proxy.address", "")); curl_easy_setopt (curl, CURLOPT_PROXYPORT, deadbeef->conf_get_int ("network.proxy.port", 8080)); - const char *type = deadbeef->conf_get_str ("network.proxy.type", "HTTP"); + const char *type = deadbeef->conf_get_str_fast ("network.proxy.type", "HTTP"); int curlproxytype = CURLPROXY_HTTP; if (!strcasecmp (type, "HTTP")) { curlproxytype = CURLPROXY_HTTP; @@ -543,8 +589,8 @@ http_thread_func (void *ctx) { #endif curl_easy_setopt (curl, CURLOPT_PROXYTYPE, curlproxytype); - const char *proxyuser = deadbeef->conf_get_str ("network.proxy.username", ""); - const char *proxypass = deadbeef->conf_get_str ("network.proxy.password", ""); + const char *proxyuser = deadbeef->conf_get_str_fast ("network.proxy.username", ""); + const char *proxypass = deadbeef->conf_get_str_fast ("network.proxy.password", ""); if (*proxyuser || *proxypass) { #if LIBCURL_VERSION_MINOR >= 19 && LIBCURL_VERSION_PATCH >= 1 curl_easy_setopt (curl, CURLOPT_PROXYUSERNAME, proxyuser); @@ -555,6 +601,7 @@ http_thread_func (void *ctx) { curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, pwd); #endif } + deadbeef->conf_unlock (); } // fp->status = STATUS_INITIAL; trace ("vfs_curl: calling curl_easy_perform (status=%d)...\n", fp->status); @@ -562,7 +609,7 @@ http_thread_func (void *ctx) { status = curl_easy_perform (curl); trace ("vfs_curl: curl_easy_perform retval=%d\n", status); if (status != 0) { - trace ("curl error:\n%s\n", http_err); + trace ("curl error:\n%s\n", fp->http_err); } deadbeef->mutex_lock (fp->mutex); if (status == 0 && fp->length < 0 && fp->status != STATUS_ABORTED && fp->status != STATUS_SEEK) { @@ -583,10 +630,12 @@ http_thread_func (void *ctx) { continue; } if (fp->status != STATUS_SEEK) { + trace ("vfs_curl: break loop\n"); deadbeef->mutex_unlock (fp->mutex); break; } else { + trace ("vfs_curl: restart loop\n"); fp->skipbytes = 0; fp->status = STATUS_INITIAL; trace ("seeking to %d\n", fp->pos); @@ -612,15 +661,22 @@ http_thread_func (void *ctx) { curl_easy_cleanup (curl); deadbeef->mutex_lock (fp->mutex); + + if (fp->status == STATUS_ABORTED) { + trace ("vfs_curl: thread ended due to abort signal\n"); + } + else { + trace ("vfs_curl: thread ended normally\n"); + } fp->status = STATUS_FINISHED; deadbeef->mutex_unlock (fp->mutex); - fp->tid = 0; } static void http_start_streamer (HTTP_FILE *fp) { fp->mutex = deadbeef->mutex_create (); fp->tid = deadbeef->thread_start (http_thread_func, fp); +// deadbeef->thread_detach (fp->tid); } static DB_FILE * @@ -630,6 +686,7 @@ http_open (const char *fname) { } trace ("http_open\n"); HTTP_FILE *fp = malloc (sizeof (HTTP_FILE)); + http_reg_open_file ((DB_FILE *)fp); memset (fp, 0, sizeof (HTTP_FILE)); fp->vfs = &plugin; fp->url = strdup (fname); @@ -651,24 +708,13 @@ http_close (DB_FILE *stream) { assert (stream); HTTP_FILE *fp = (HTTP_FILE *)stream; - deadbeef->mutex_lock (fp->mutex); + http_abort (stream); if (fp->tid) { - fp->status = STATUS_ABORTED; - trace ("http_close thread_join\n"); - deadbeef->mutex_unlock (fp->mutex); deadbeef->thread_join (fp->tid); } - if (fp->content_type) { - free (fp->content_type); - } - if (fp->track) { - deadbeef->pl_item_unref (fp->track); - } - if (fp->url) { - free (fp->url); - } - deadbeef->mutex_free (fp->mutex); - free (stream); + http_cancel_abort ((DB_FILE *)fp); + http_destroy (fp); + http_unreg_open_file ((DB_FILE *)fp); trace ("http_close done\n"); } @@ -690,7 +736,7 @@ http_read (void *ptr, size_t size, size_t nmemb, DB_FILE *stream) { { // wait until data is available while ((fp->remaining == 0 || fp->skipbytes > 0) && fp->status != STATUS_FINISHED) { -// trace ("vfs_curl: readwait..\n"); + trace ("vfs_curl: readwait, status: %d..\n", fp->status); deadbeef->mutex_lock (fp->mutex); if (fp->status == STATUS_READING) { struct timeval tm; @@ -867,43 +913,163 @@ http_get_content_type (DB_FILE *stream) { return fp->content_type; } -void +static void http_abort (DB_FILE *fp) { - trace ("http_abort %p\n", fp); - HTTP_FILE *f = (HTTP_FILE *)fp; - if (f->tid) { - deadbeef->mutex_lock (f->mutex); - f->status = STATUS_ABORTED; - deadbeef->mutex_unlock (f->mutex); - } - trace ("http_abort done\n"); + trace ("abort file: %p\n", fp); + deadbeef->mutex_lock (biglock); + int i; + for (i = 0; i < num_abort_files; i++) { + if (abort_files[i] == fp) { + break; + } + } + if (i == num_abort_files) { + if (num_abort_files == MAX_ABORT_FILES) { + trace ("vfs_curl: abort_files list overflow\n"); + } + else { + trace ("added file to abort list: %p\n", fp); + abort_files[num_abort_files++] = fp; + } + } + deadbeef->mutex_unlock (biglock); +} + +static int +http_need_abort (DB_FILE *fp) { + deadbeef->mutex_lock (biglock); + for (int i = 0; i < num_abort_files; i++) { + if (abort_files[i] == fp) { + trace ("need to abort: %p\n", fp); + deadbeef->mutex_unlock (biglock); + return 1; + } + } + deadbeef->mutex_unlock (biglock); + return 0; +} + +static void +http_cancel_abort (DB_FILE *fp) { + deadbeef->mutex_lock (biglock); + for (int i = 0; i < num_abort_files; i++) { + if (abort_files[i] == fp) { + if (i != num_abort_files-1) { + abort_files[i] = abort_files[num_abort_files-1]; + } + num_abort_files--; + break; + } + } + deadbeef->mutex_unlock (biglock); +} + +static void +http_reg_open_file (DB_FILE *fp) { + deadbeef->mutex_lock (biglock); + for (int i = 0; i < num_open_files; i++) { + if (open_files[i] == fp) { + deadbeef->mutex_unlock (biglock); + return; + } + } + if (num_open_files == MAX_ABORT_FILES) { + fprintf (stderr, "vfs_curl: open files overflow\n"); + deadbeef->mutex_unlock (biglock); + return; + } + open_files[num_open_files++] = fp; + deadbeef->mutex_unlock (biglock); +} + +static void +http_unreg_open_file (DB_FILE *fp) { + deadbeef->mutex_lock (biglock); + int i; + for (i = 0; i < num_open_files; i++) { + if (open_files[i] == fp) { + if (i != num_open_files-1) { + open_files[i] = open_files[num_open_files-1]; + } + num_open_files--; + trace ("remove from open list: %p\n", fp); + break; + } + } + + // gc abort_files + int j = 0; + while (j < num_abort_files) { + for (i = 0; i < num_open_files; i++) { + if (abort_files[j] == open_files[i]) { + break; + } + } + if (i == num_open_files) { + // remove abort + http_cancel_abort (abort_files[j]); + continue; + } + j++; + } + deadbeef->mutex_unlock (biglock); } static int vfs_curl_start (void) { allow_new_streams = 1; + biglock = deadbeef->mutex_create (); return 0; } static int vfs_curl_stop (void) { allow_new_streams = 0; + if (biglock) { + deadbeef->mutex_free (biglock); + biglock = 0; + } return 0; } static const char *scheme_names[] = { "http://", "ftp://", NULL }; +const char ** +http_get_schemes (void) { + return scheme_names; +} + +int +http_is_streaming (void) { + return 1; +} + // standard stdio vfs static DB_vfs_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_VFS, .plugin.id = "vfs_curl", .plugin.name = "cURL vfs", .plugin.descr = "http and ftp streaming module using libcurl, with ICY protocol support", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = vfs_curl_start, .plugin.stop = vfs_curl_stop, @@ -917,8 +1083,8 @@ static DB_vfs_t plugin = { .rewind = http_rewind, .getlength = http_getlength, .get_content_type = http_get_content_type, - .scheme_names = scheme_names, - .streaming = 1 + .get_schemes = http_get_schemes, + .is_streaming = http_is_streaming, }; DB_plugin_t * diff --git a/plugins/vfs_zip/Makefile.am b/plugins/vfs_zip/Makefile.am new file mode 100644 index 00000000..8a03b68d --- /dev/null +++ b/plugins/vfs_zip/Makefile.am @@ -0,0 +1,9 @@ +if HAVE_VFS_ZIP +pkglib_LTLIBRARIES = vfs_zip.la +vfs_zip_la_SOURCES = vfs_zip.c + +vfs_zip_la_LDFLAGS = -module + +vfs_zip_la_LIBADD = $(LDADD) $(ZLIB_LIBS) $(ZIP_LIBS) +AM_CFLAGS = $(CFLAGS) -std=c99 +endif diff --git a/plugins/vfs_zip/vfs_zip.c b/plugins/vfs_zip/vfs_zip.c new file mode 100644 index 00000000..5e77b740 --- /dev/null +++ b/plugins/vfs_zip/vfs_zip.c @@ -0,0 +1,255 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <string.h> +#include <zip.h> +#include <stdlib.h> +#include <assert.h> +#include "../../deadbeef.h" + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +#define min(x,y) ((x)<(y)?(x):(y)) + +static DB_functions_t *deadbeef; +static DB_vfs_t plugin; + +typedef struct { + DB_FILE file; + struct zip* z; + struct zip_file *zf; + int64_t offset; + int index; + int64_t size; +} zip_file_t; + +static const char *scheme_names[] = { "zip://", NULL }; + +const char ** +vfs_zip_get_schemes (void) { + return scheme_names; +} + +int +vfs_zip_is_streaming (void) { + return 0; +} + +// fname must have form of zip://full_filepath.zip:full_filepath_in_zip +DB_FILE* +vfs_zip_open (const char *fname) { + if (strncasecmp (fname, "zip://", 6)) { + return NULL; + } + + fname += 6; + const char *colon = strchr (fname, ':'); + if (!colon) { + return NULL; + } + + + char zipname[colon-fname+1]; + memcpy (zipname, fname, colon-fname); + zipname[colon-fname] = 0; + + fname = colon+1; + + struct zip *z = zip_open (zipname, 0, NULL); + if (!z) { + return NULL; + } + struct zip_stat st; + memset (&st, 0, sizeof (st)); + + int res = zip_stat(z, fname, 0, &st); + if (res != 0) { + zip_close (z); + return NULL; + } + + struct zip_file *zf = zip_fopen_index (z, st.index, 0); + if (!zf) { + zip_close (z); + return NULL; + } + + zip_file_t *f = malloc (sizeof (zip_file_t)); + memset (f, 0, sizeof (zip_file_t)); + f->file.vfs = &plugin; + f->z = z; + f->zf = zf; + f->index = st.index; + f->size = st.size; + return (DB_FILE*)f; +} + +void +vfs_zip_close (DB_FILE *f) { + zip_file_t *zf = (zip_file_t *)f; + if (zf->zf) { + zip_fclose (zf->zf); + } + if (zf->z) { + zip_close (zf->z); + } + free (zf); +} + +size_t +vfs_zip_read (void *ptr, size_t size, size_t nmemb, DB_FILE *f) { + zip_file_t *zf = (zip_file_t *)f; + ssize_t rb = zip_fread (zf->zf, ptr, size * nmemb); + zf->offset += rb; + return rb / size; +} + +int +vfs_zip_seek (DB_FILE *f, int64_t offset, int whence) { + zip_file_t *zf = (zip_file_t *)f; + + if (whence == SEEK_CUR) { + offset = zf->offset + offset; + } + else if (whence == SEEK_END) { + offset = zf->size + offset; + } + + if (offset < zf->offset) { + // reopen + zip_fclose (zf->zf); + zf->zf = zip_fopen_index (zf->z, zf->index, 0); + if (!zf->zf) { + return -1; + } + zf->offset = 0; + } + char buf[4096]; + int64_t n = offset - zf->offset; + while (n > 0) { + int sz = min (n, sizeof (buf)); + ssize_t rb = zip_fread (zf->zf, buf, sz); + n -= rb; + assert (n >= 0); + zf->offset += rb; + if (rb != sz) { + break; + } + } + if (n > 0) { + return -1; + } + return 0; +} + +int64_t +vfs_zip_tell (DB_FILE *f) { + zip_file_t *zf = (zip_file_t *)f; + return zf->offset; +} + +void +vfs_zip_rewind (DB_FILE *f) { + zip_file_t *zf = (zip_file_t *)f; + zip_fclose (zf->zf); + zf->zf = zip_fopen_index (zf->z, zf->index, 0); + assert (zf->zf); // FIXME: better error handling? + zf->offset = 0; +} + +int64_t +vfs_zip_getlength (DB_FILE *f) { + zip_file_t *zf = (zip_file_t *)f; + return zf->size; +} + +int +vfs_zip_scandir (const char *dir, struct dirent ***namelist, int (*selector) (const struct dirent *), int (*cmp) (const struct dirent **, const struct dirent **)) { + struct zip *z = zip_open (dir, ZIP_CHECKCONS, NULL); + if (!z) { + return -1; + } + + int n = zip_get_num_files (z); + *namelist = malloc (sizeof (void *) * n); + for (int i = 0; i < n; i++) { + (*namelist)[i] = malloc (sizeof (struct dirent)); + memset ((*namelist)[i], 0, sizeof (struct dirent)); + const char *nm = zip_get_name (z, i, 0); + snprintf ((*namelist)[i]->d_name, sizeof ((*namelist)[i]->d_name), "zip://%s:%s", dir, nm); + } + + zip_close (z); + return n; +} + +int +vfs_zip_is_container (const char *fname) { + const char *ext = strrchr (fname, '.'); + if (ext && !strcasecmp (ext, ".zip")) { + return 1; + } + return 0; +} + +static DB_vfs_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 1, + .plugin.version_minor = 0, + .plugin.type = DB_PLUGIN_VFS, + .plugin.id = "vfs_zip", + .plugin.name = "ZIP vfs", + .plugin.descr = "play files directly from zip files", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.website = "http://deadbeef.sf.net", + .open = vfs_zip_open, + .close = vfs_zip_close, + .read = vfs_zip_read, + .seek = vfs_zip_seek, + .tell = vfs_zip_tell, + .rewind = vfs_zip_rewind, + .getlength = vfs_zip_getlength, + .get_schemes = vfs_zip_get_schemes, + .is_streaming = vfs_zip_is_streaming, + .is_container = vfs_zip_is_container, + .scandir = vfs_zip_scandir, +}; + +DB_plugin_t * +vfs_zip_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c index fc0afe9e..691e40f3 100644 --- a/plugins/vorbis/vorbis.c +++ b/plugins/vorbis/vorbis.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include <assert.h> #include <limits.h> #include <unistd.h> +#include <math.h> #ifdef HAVE_CONFIG_H # include <config.h> #endif @@ -88,6 +89,7 @@ static const char *metainfo[] = { "DISCNUMBER", "disc", "COPYRIGHT", "copyright", "TOTALTRACKS", "numtracks", + "TRACKTOTAL", "numtracks", "ALBUM ARTIST", "band", NULL }; @@ -112,7 +114,6 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc, int refresh_playl for (m = 0; metainfo[m]; m += 2) { int l = strlen (metainfo[m]); if (vc->comment_lengths[i] > l && !strncasecmp (metainfo[m], s, l) && s[l] == '=') { - trace ("ogg adding %s\n", s); if (refresh_playlist == 2) { const char *val = deadbeef->pl_find_meta (it, metainfo[m+1]); if (!val || strcmp (val, s+l+1)) { @@ -121,6 +122,7 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc, int refresh_playl } else { deadbeef->pl_append_meta (it, metainfo[m+1], s + l + 1); + break; } } } @@ -129,16 +131,28 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc, int refresh_playl deadbeef->pl_add_meta (it, "cuesheet", s + 9); } else if (!strncasecmp (s, "replaygain_album_gain=", 22)) { - it->replaygain_album_gain = atof (s + 22); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, atof (s+22)); } else if (!strncasecmp (s, "replaygain_album_peak=", 22)) { - it->replaygain_album_peak = atof (s + 22); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, atof (s+22)); } else if (!strncasecmp (s, "replaygain_track_gain=", 22)) { - it->replaygain_track_gain = atof (s + 22); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, atof (s+22)); } else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { - it->replaygain_track_peak = atof (s + 22); + deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (s+22)); + } + else { + const char *p = s; + while (*p && *p != '=') { + p++; + } + if (*p == '=') { + char key[p-s+1]; + memcpy (key, s, p-s); + key[p-s] = 0; + deadbeef->pl_add_meta (it, key, p+1); + } } } } @@ -154,10 +168,11 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc, int refresh_playl if (refresh_playlist) { deadbeef->plug_trigger_event_playlistchanged (); } + return 0; } static DB_fileinfo_t * -cvorbis_open (void) { +cvorbis_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (ogg_info_t)); ogg_info_t *info = (ogg_info_t *)_info; memset (info, 0, sizeof (ogg_info_t)); @@ -173,18 +188,19 @@ cvorbis_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->cur_bit_stream = -1; } else { - info->cur_bit_stream = it->tracknum; + int tracknum = deadbeef->pl_find_meta_int (it, ":TRACKNUM", -1); + info->cur_bit_stream = tracknum; } info->ptrack = it; deadbeef->pl_item_ref (it); - info->info.file = deadbeef->fopen (it->fname); + info->info.file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->info.file) { trace ("ogg: failed to open file %s\n", it->fname); return -1; } int ln = deadbeef->fgetlength (info->info.file); - if (info->info.file->vfs->streaming && ln == -1) { + if (info->info.file->vfs->is_streaming () && ln == -1) { ov_callbacks ovcb = { .read_func = cvorbis_fread, .seek_func = NULL, @@ -227,13 +243,18 @@ cvorbis_init (DB_fileinfo_t *_info, DB_playItem_t *it) { return -1; } _info->plugin = &plugin; - _info->bps = 16; + _info->fmt.bps = 16; //_info->dataSize = ov_pcm_total (&vorbis_file, -1) * vi->channels * 2; - _info->channels = info->vi->channels; - _info->samplerate = info->vi->rate; + _info->fmt.channels = info->vi->channels; + _info->fmt.samplerate = info->vi->rate; + + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } + _info->readpos = 0; info->currentsample = 0; - if (!info->info.file->vfs->streaming) { + if (!info->info.file->vfs->is_streaming ()) { if (it->endsample > 0) { info->startsample = it->startsample; info->endsample = it->endsample; @@ -266,8 +287,12 @@ cvorbis_free (DB_fileinfo_t *_info) { deadbeef->pl_item_unref (info->ptrack); } if (info->info.file) { - ov_clear (&info->vorbis_file); - //fclose (file); //-- ov_clear closes it + if (info->vorbis_file.datasource) { + ov_clear (&info->vorbis_file); + } + else { + deadbeef->fclose (info->info.file); + } } free (info); } @@ -277,10 +302,12 @@ static int cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { ogg_info_t *info = (ogg_info_t *)_info; // trace ("cvorbis_read %d bytes\n", size); - int out_ch = min (_info->channels, 2); - if (!info->info.file->vfs->streaming) { - if (info->currentsample + size / (2 * out_ch) > info->endsample) { - size = (info->endsample - info->currentsample + 1) * 2 * out_ch; + + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + + if (!info->info.file->vfs->is_streaming ()) { + if (info->currentsample + size / samplesize > info->endsample) { + size = (info->endsample - info->currentsample + 1) * samplesize; trace ("size truncated to %d bytes, cursample=%d, info->endsample=%d, totalsamples=%d\n", size, info->currentsample, info->endsample, ov_pcm_total (&info->vorbis_file, -1)); if (size <= 0) { return 0; @@ -288,7 +315,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { } } else { - if (info->ptrack && info->currentsample - info->last_comment_update > 5 * _info->samplerate) { + if (info->ptrack && info->currentsample - info->last_comment_update > 5 * _info->fmt.samplerate) { if (info->ptrack) { info->last_comment_update = info->currentsample; vorbis_comment *vc = ov_comment (&info->vorbis_file, -1); @@ -311,23 +338,33 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { endianess = 1; #endif - if (out_ch != _info->channels) { - int nframes = size / out_ch / 2; - int16_t buf[nframes * _info->channels]; - ret=ov_read (&info->vorbis_file, (char*)buf, sizeof(buf), endianess, 2, 1, &info->cur_bit_stream); + if (_info->fmt.channels <= 2 || _info->fmt.channels == 4) { + ret=ov_read (&info->vorbis_file, bytes, size, endianess, 2, 1, &info->cur_bit_stream); + } + else { + int16_t temp[size/2]; + ret=ov_read (&info->vorbis_file, (char *)temp, size, endianess, 2, 1, &info->cur_bit_stream); if (ret > 0) { - int n = ret / _info->channels / 2; - for (int i = 0; i < n; i++) { - for (int j = 0; j < out_ch; j++) { - ((int16_t *)bytes)[i * out_ch + j] = buf[i * _info->channels + j]; + // remap channels to wav format + int idx = _info->fmt.channels - 3; + static int remap[4][6] = { + {0,2,1}, + {0,1,2,3}, // should not be used + {0,2,1,3,4}, + {0,2,1,4,5,3} + }; + + int i, j; + int16_t *src = temp; + int n = ret / samplesize; + for (i = 0; i < n; i++) { + for (j = 0; j < _info->fmt.channels; j++) { + ((int16_t *)(bytes + samplesize * i))[remap[idx][j]] = src[j]; } + src += _info->fmt.channels; } - ret = n * out_ch * 2; } } - else { - ret=ov_read (&info->vorbis_file, bytes, size, endianess, 2, 1, &info->cur_bit_stream); - } if (ret <= 0) { if (ret < 0) { @@ -352,12 +389,12 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { } else if (ret < size) { - info->currentsample += ret / (out_ch * 2); + info->currentsample += ret / samplesize; size -= ret; bytes += ret; } else { - info->currentsample += ret / (out_ch * 2); + info->currentsample += ret / samplesize; size = 0; break; } @@ -410,10 +447,10 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) { trace ("vorbis: failed to fopen %s\n", fname); return NULL; } - if (fp->vfs->streaming) { - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->fname = strdup (fname); - it->filetype = "OggVorbis"; + int64_t fsize = deadbeef->fgetlength (fp); + if (fp->vfs->is_streaming ()) { + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "OggVorbis"); deadbeef->pl_set_item_duration (it, -1); deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->pl_insert_item (after, it); @@ -448,11 +485,9 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) { float duration = ov_time_total (&vorbis_file, stream); int totalsamples = ov_pcm_total (&vorbis_file, stream); - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "OggVorbis"; - it->tracknum = stream; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "OggVorbis"); + deadbeef->pl_set_meta_int (it, ":TRACKNUM", stream); deadbeef->pl_set_item_duration (it, duration); if (nstreams > 1) { it->startsample = currentsample; @@ -465,6 +500,19 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) { update_vorbis_comments (it, vc, 0); int samplerate = vi->rate; + char s[100]; + snprintf (s, sizeof (s), "%lld", fsize); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + deadbeef->pl_add_meta (it, ":BPS", "16"); + snprintf (s, sizeof (s), "%d", vi->channels); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", samplerate); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + int br = (int)roundf(fsize / duration * 8 / 1000); + snprintf (s, sizeof (s), "%d", br); + deadbeef->pl_add_meta (it, ":BITRATE", s); + + if (nstreams == 1) { DB_playItem_t *cue = deadbeef->pl_insert_cue (after, it, totalsamples, samplerate); if (cue) { @@ -513,12 +561,12 @@ cvorbis_read_metadata (DB_playItem_t *it) { OggVorbis_File vorbis_file; vorbis_info *vi = NULL; - fp = deadbeef->fopen (it->fname); + fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { trace ("cvorbis_read_metadata: failed to fopen %s\n", it->fname); return -1; } - if (fp->vfs->streaming) { + if (fp->vfs->is_streaming ()) { trace ("cvorbis_read_metadata: failed to fopen %s\n", it->fname); goto error; } @@ -533,14 +581,15 @@ cvorbis_read_metadata (DB_playItem_t *it) { trace ("cvorbis_read_metadata: ov_open_callbacks returned %d\n", res); goto error; } - vi = ov_info (&vorbis_file, it->tracknum); + int tracknum = deadbeef->pl_find_meta_int (it, ":TRACKNUM", -1); + vi = ov_info (&vorbis_file, tracknum); if (!vi) { // not a vorbis stream trace ("cvorbis_read_metadata: failed to ov_open %s\n", it->fname); goto error; } // metainfo - vorbis_comment *vc = ov_comment (&vorbis_file, it->tracknum); + vorbis_comment *vc = ov_comment (&vorbis_file, tracknum); if (vc) { update_vorbis_comments (it, vc, 0); } @@ -576,7 +625,7 @@ cvorbis_write_metadata (DB_playItem_t *it) { trace ("cvorbis_write_metadata: vcedit_new_state failed\n"); return -1; } - fp = fopen (it->fname, "rb"); + fp = fopen (deadbeef->pl_find_meta (it, ":URI"), "rb"); if (!fp) { trace ("cvorbis_write_metadata: failed to read metadata from %s\n", it->fname); goto error; @@ -592,6 +641,7 @@ cvorbis_write_metadata (DB_playItem_t *it) { goto error; } +#if 0 // copy all unknown fields to separate buffer for (int i = 0; i < vc->comments; i++) { int m; @@ -612,42 +662,55 @@ cvorbis_write_metadata (DB_playItem_t *it) { preserved_fields = f; } } +#endif vorbis_comment_clear(vc); vorbis_comment_init(vc); - // add known fields - for (int m = 0; metainfo[m]; m += 2) { - const char *val = deadbeef->pl_find_meta (it, metainfo[m+1]); - if (val && *val) { - while (val) { - const char *next = strchr (val, '\n'); - int l; - if (next) { - l = next - val; - next++; - } - else { - l = strlen (val); + // add unknown/custom fields + deadbeef->pl_lock (); + DB_metaInfo_t *m = deadbeef->pl_get_metadata_head (it); + while (m) { + if (m->key[0] != ':') { + int i; + for (i = 0; metainfo[i]; i += 2) { + if (!strcasecmp (metainfo[i+1], m->key)) { + break; } - if (l > 0) { - char s[100+l+1]; - int n = snprintf (s, sizeof (s), "%s=", metainfo[m]); - strncpy (s+n, val, l); - *(s+n+l) = 0; - vorbis_comment_add (vc, s); + } + const char *val = m->value; + if (val && *val) { + while (val) { + const char *next = strchr (val, '\n'); + int l; + if (next) { + l = next - val; + next++; + } + else { + l = strlen (val); + } + if (l > 0) { + char s[100+l+1]; + int n = snprintf (s, sizeof (s), "%s=", metainfo[i] ? metainfo[i] : m->key); + strncpy (s+n, val, l); + *(s+n+l) = 0; + vorbis_comment_add (vc, s); + } + val = next; } - val = next; } } + m = m->next; } + deadbeef->pl_unlock (); // add preserved fields for (struct field *f = preserved_fields; f; f = f->next) { vorbis_comment_add (vc, f->data); } - snprintf (outname, sizeof (outname), "%s.temp.ogg", it->fname); + snprintf (outname, sizeof (outname), "%s.temp.ogg", deadbeef->pl_find_meta (it, ":URI")); out = fopen (outname, "w+b"); if (!out) { @@ -678,7 +741,7 @@ error: } if (!err) { - rename (outname, it->fname); + rename (outname, deadbeef->pl_find_meta (it, ":URI")); } else if (out) { unlink (outname); @@ -694,21 +757,36 @@ static const char *filetypes[] = { "OggVorbis", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "stdogg", .plugin.name = "OggVorbis decoder", .plugin.descr = "OggVorbis decoder using standard xiph.org libraries", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = vorbis_start, .plugin.stop = vorbis_stop, .open = cvorbis_open, .init = cvorbis_init, .free = cvorbis_free, - .read_int16 = cvorbis_read, + .read = cvorbis_read, // vorbisfile can't output float32 // .read_float32 = cvorbis_read_float32, .seek = cvorbis_seek, diff --git a/plugins/vtx/vtx.c b/plugins/vtx/vtx.c index 3e4ed6b9..aa62422e 100644 --- a/plugins/vtx/vtx.c +++ b/plugins/vtx/vtx.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -47,7 +47,7 @@ typedef struct { } vtx_info_t; static DB_fileinfo_t * -vtx_open (void) { +vtx_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (vtx_info_t)); memset (_info, 0, sizeof (vtx_info_t)); return _info; @@ -62,9 +62,9 @@ vtx_init (DB_fileinfo_t *_info, DB_playItem_t *it) { size_t sz = 0; char *buf = NULL; - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { - trace ("vtx: failed to open file %s\n", it->fname); + trace ("vtx: failed to open file %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } @@ -102,16 +102,21 @@ vtx_init (DB_fileinfo_t *_info, DB_playItem_t *it) { int samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); - ayemu_set_sound_format (&info->ay, samplerate, deadbeef->get_output ()->channels (), deadbeef->get_output ()->bitspersample ()); - info->left = 0; - info->rate = deadbeef->get_output ()->channels () * deadbeef->get_output ()->bitspersample () / 8; info->vtx_pos = 0; _info->plugin = &plugin; - _info->bps = deadbeef->get_output ()->bitspersample (); - _info->channels = deadbeef->get_output ()->channels (); - _info->samplerate = samplerate; + _info->fmt.bps = deadbeef->conf_get_int ("vtx.bps", 16);; + if (_info->fmt.bps != 16 && _info->fmt.bps != 8) { + _info->fmt.bps = 16; + } + _info->fmt.channels = 2; + _info->fmt.samplerate = samplerate; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; + + ayemu_set_sound_format (&info->ay, samplerate, _info->fmt.channels, _info->fmt.bps); + + info->rate = _info->fmt.channels * _info->fmt.bps / 8; return 0; } @@ -149,7 +154,7 @@ ayemu_vtx_get_next_frame (vtx_info_t *info) } static int -vtx_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +vtx_read (DB_fileinfo_t *_info, char *bytes, int size) { // try decode `size' bytes // return number of decoded bytes // return 0 on EOF @@ -170,7 +175,7 @@ vtx_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } else { // number of samples it current frame - info->left = _info->samplerate / info->decoder->playerFreq; + info->left = _info->fmt.samplerate / info->decoder->playerFreq; // mul by rate to get number of bytes; info->left *= info->rate; ayemu_set_regs (&info->ay, info->regs); @@ -179,7 +184,7 @@ vtx_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { } } info->currentsample += (initsize - size) / 4; - _info->readpos = (float)info->currentsample / _info->samplerate; + _info->readpos = (float)info->currentsample / _info->fmt.samplerate; return initsize - size; } @@ -192,7 +197,7 @@ vtx_seek_sample (DB_fileinfo_t *_info, int sample) { // get frame int num_frames = info->decoder->regdata_size / AY_FRAME_SIZE; - int samples_per_frame = _info->samplerate / info->decoder->playerFreq; + int samples_per_frame = _info->fmt.samplerate / info->decoder->playerFreq; // start of frame info->vtx_pos = sample / samples_per_frame; @@ -206,11 +211,11 @@ vtx_seek_sample (DB_fileinfo_t *_info, int sample) { info->regs[n] = *p; } // set number of bytes left in frame - info->left = _info->samplerate / info->decoder->playerFreq - (sample % samples_per_frame); + info->left = _info->fmt.samplerate / info->decoder->playerFreq - (sample % samples_per_frame); // mul by rate to get number of bytes info->left *= info->rate; info->currentsample = sample; - _info->readpos = (float)info->currentsample / _info->samplerate; + _info->readpos = (float)info->currentsample / _info->fmt.samplerate; return 0; } @@ -220,7 +225,7 @@ vtx_seek (DB_fileinfo_t *_info, float time) { // seek to specified time in seconds // return 0 on success // return -1 on failure - return vtx_seek_sample (_info, time * _info->samplerate); + return vtx_seek_sample (_info, time * _info->fmt.samplerate); } static DB_playItem_t * @@ -253,11 +258,8 @@ vtx_insert (DB_playItem_t *after, const char *fname) { } trace ("vtx: datasize: %d\n", hdr->regdata_size); - DB_playItem_t *it = deadbeef->pl_item_alloc (); - - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = filetypes[0]; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", filetypes[0]); int numframes = hdr->regdata_size / AY_FRAME_SIZE; // int totalsamples = numframes * hdr->playerFreq; @@ -290,25 +292,48 @@ vtx_stop (void) { // return -1 on failure return 0; } + +static const char settings_dlg[] = + "property \"Bits Per Sample (8 or 16)\" entry vtx.bps 16;\n" +; + // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "vtx", .plugin.name = "VTX decoder", .plugin.descr = "AY8910/12 chip emulator and vtx file player", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses libayemu - AY/YM sound chip emulator and music file loader\n" + "Copyright (C) 2003-2004 Sashnov Alexander\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = vtx_start, .plugin.stop = vtx_stop, + .plugin.configdialog = settings_dlg, .open = vtx_open, .init = vtx_init, .free = vtx_free, - .read_int16 = vtx_read_int16, -// .read_float32 = vtx_read_float32, + .read = vtx_read, .seek = vtx_seek, .seek_sample = vtx_seek_sample, .insert = vtx_insert, diff --git a/plugins/wavpack/COPYING b/plugins/wavpack/COPYING new file mode 100644 index 00000000..f1cf0011 --- /dev/null +++ b/plugins/wavpack/COPYING @@ -0,0 +1,26 @@ +WavPack plugin for DeaDBeeF Player +Copyright (C) 2009-2011, Alexey Yakovenko <waker@users.sourceforge.net> +With contributions from David Bryant <david@wavpack.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/wavpack/wavpack.c b/plugins/wavpack/wavpack.c index e1eb4787..15973421 100644 --- a/plugins/wavpack/wavpack.c +++ b/plugins/wavpack/wavpack.c @@ -1,20 +1,30 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +WavPack plugin for DeaDBeeF Player +Copyright (C) 2009-2011, Alexey Yakovenko <waker@users.sourceforge.net> +Copyright (C) 2010-2011, David Bryant <david@wavpack.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <string.h> @@ -106,7 +116,7 @@ static WavpackStreamReader wsr = { #endif static DB_fileinfo_t * -wv_open (void) { +wv_open (uint32_t hints) { DB_fileinfo_t *_info = malloc (sizeof (wvctx_t)); memset (_info, 0, sizeof (wvctx_t)); return _info; @@ -115,15 +125,15 @@ wv_open (void) { static int wv_init (DB_fileinfo_t *_info, DB_playItem_t *it) { wvctx_t *info = (wvctx_t *)_info; - info->file = deadbeef->fopen (it->fname); + info->file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!info->file) { return -1; } #ifndef TINYWV - char *c_fname = alloca (strlen (it->fname) + 2); + char *c_fname = alloca (strlen (deadbeef->pl_find_meta (it, ":URI")) + 2); if (c_fname) { - strcpy (c_fname, it->fname); + strcpy (c_fname, deadbeef->pl_find_meta (it, ":URI")); strcat (c_fname, "c"); info->c_file = deadbeef->fopen (c_fname); } @@ -136,16 +146,18 @@ wv_init (DB_fileinfo_t *_info, DB_playItem_t *it) { #ifdef TINYWV info->ctx = WavpackOpenFileInput (wv_read_stream, info->file, error); #else - info->ctx = WavpackOpenFileInputEx (&wsr, info->file, info->c_file, error, OPEN_2CH_MAX | OPEN_NORMALIZE, 0); + info->ctx = WavpackOpenFileInputEx (&wsr, info->file, info->c_file, error, OPEN_NORMALIZE, 0); #endif if (!info->ctx) { fprintf (stderr, "wavpack error: %s\n", error); return -1; } _info->plugin = &plugin; - _info->bps = WavpackGetBytesPerSample (info->ctx) * 8; - _info->channels = WavpackGetNumChannels (info->ctx); - _info->samplerate = WavpackGetSampleRate (info->ctx); + _info->fmt.bps = WavpackGetBytesPerSample (info->ctx) * 8; + _info->fmt.channels = WavpackGetNumChannels (info->ctx); + _info->fmt.samplerate = WavpackGetSampleRate (info->ctx); + _info->fmt.is_float = (WavpackGetMode (info->ctx) & MODE_FLOAT) ? 1 : 0; + _info->fmt.channelmask = WavpackGetChannelMask (info->ctx); _info->readpos = 0; if (it->endsample > 0) { info->startsample = it->startsample; @@ -184,49 +196,57 @@ wv_free (DB_fileinfo_t *_info) { } static int -wv_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { +wv_read (DB_fileinfo_t *_info, char *bytes, int size) { wvctx_t *info = (wvctx_t *)_info; int currentsample = WavpackGetSampleIndex (info->ctx); - int nchannels = WavpackGetReducedChannels (info->ctx); - if (size / (2 * nchannels) + currentsample > info->endsample) { - size = (info->endsample - currentsample + 1) * 2 * nchannels; - trace ("wv: size truncated to %d bytes, cursample=%d, endsample=%d\n", size, currentsample, info->endsample); + int samplesize = _info->fmt.channels * _info->fmt.bps / 8; + if (size / samplesize + currentsample > info->endsample) { + size = (info->endsample - currentsample + 1) * samplesize; + trace ("wv: size truncated to %d bytes (%d samples), cursample=%d, endsample=%d\n", size, info->endsample - currentsample + 1, currentsample, info->endsample); if (size <= 0) { return 0; } } - int32_t buffer[size/2]; - int n = WavpackUnpackSamples (info->ctx, buffer, size/(2*nchannels)); - size = n * 2 * nchannels; - // convert to int16 - int32_t *p = buffer; - n *= nchannels; - + int initsize = size; + int n; if (WavpackGetMode (info->ctx) & MODE_FLOAT) { - while (n > 0) { - float val = *(float*)p; - if (val >= 1.0) - *((int16_t *)bytes) = 32767; - else if (val <= -1.0) - *((int16_t *)bytes) = -32768; - else - *((int16_t *)bytes) = floor (val * 32768.f); - bytes += sizeof (int16_t); - p++; - n--; - } + _info->fmt.is_float = 1; + } + if (_info->fmt.is_float || _info->fmt.bps == 32) { + n = WavpackUnpackSamples (info->ctx, (int32_t *)bytes, size / samplesize); + size -= n * samplesize; } else { - while (n > 0) { - if (_info->bps >= 16) { - *((int16_t *)bytes) = (int16_t)((*p) >> (_info->bps-16)); + int32_t buffer[size/(_info->fmt.bps / 8)]; + n = WavpackUnpackSamples (info->ctx, (int32_t *)buffer, size / samplesize); + size -= n * samplesize; + n *= _info->fmt.channels; + + // convert from int32 to input (what???) + int32_t *p = buffer; + if (_info->fmt.bps == 16) { + while (n > 0) { + *((int16_t *)bytes) = (int16_t)(*p); + bytes += sizeof (int16_t); + p++; + n--; } - else { - *((int16_t *)bytes) = (int16_t)((*p) << (16-_info->bps)); + } + else if (_info->fmt.bps == 8) { + while (n > 0) { + *bytes++ = (char)(*p); + p++; + n--; + } + } + else if (_info->fmt.bps == 24) { + while (n > 0) { + *bytes++ = (*p)&0xff; + *bytes++ = ((*p)&0xff00)>>8; + *bytes++ = ((*p)&0xff0000)>>16; + p++; + n--; } - bytes += sizeof (int16_t); - p++; - n--; } } _info->readpos = (float)(WavpackGetSampleIndex (info->ctx)-info->startsample)/WavpackGetSampleRate (info->ctx); @@ -235,45 +255,7 @@ wv_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) { deadbeef->streamer_set_bitrate (WavpackGetInstantBitrate (info->ctx) / 1000); #endif - return size; -} - -static int -wv_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) { - wvctx_t *info = (wvctx_t *)_info; - int nchannels = WavpackGetReducedChannels (info->ctx); - int currentsample = WavpackGetSampleIndex (info->ctx); - if (size / (4 * nchannels) + currentsample > info->endsample) { - size = (info->endsample - currentsample + 1) * 4 * nchannels; - trace ("wv: size truncated to %d bytes, cursample=%d, endsample=%d\n", size, currentsample, info->endsample); - if (size <= 0) { - return 0; - } - } - int32_t buffer[size/4]; - int n = WavpackUnpackSamples (info->ctx, buffer, size/(4*nchannels)); - size = n * 4 * nchannels; - // convert to float32 - n *= nchannels; - - if (WavpackGetMode (info->ctx) & MODE_FLOAT) { - memcpy (bytes, buffer, size); - } - else { - float mul = 1.f/ (1UL << (_info->bps-1)); - int32_t *p = buffer; - while (n > 0) { - *((float *)bytes) = (*p) * mul; - bytes += sizeof (float); - p++; - n--; - } - } - _info->readpos = (float)(WavpackGetSampleIndex (info->ctx)-info->startsample)/WavpackGetSampleRate (info->ctx); -#ifndef TINYWV - deadbeef->streamer_set_bitrate (WavpackGetInstantBitrate (info->ctx) / 1000); -#endif - return size; + return initsize-size; } static int @@ -294,7 +276,6 @@ wv_seek (DB_fileinfo_t *_info, float sec) { static DB_playItem_t * wv_insert (DB_playItem_t *after, const char *fname) { - DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { return NULL; @@ -314,10 +295,8 @@ wv_insert (DB_playItem_t *after, const char *fname) { int samplerate = WavpackGetSampleRate (ctx); float duration = (float)totalsamples / samplerate; - DB_playItem_t *it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); - it->fname = strdup (fname); - it->filetype = "wv"; + DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); + deadbeef->pl_add_meta (it, ":FILETYPE", "wv"); deadbeef->pl_set_item_duration (it, duration); trace ("wv: totalsamples=%d, samplerate=%d, duration=%f\n", totalsamples, samplerate, duration); @@ -340,28 +319,21 @@ wv_insert (DB_playItem_t *after, const char *fname) { if (!v1err) { trace ("wv: id3v1 tag found\n"); } - -#if 0 - // embedded cue - char *emb_cuesheet; - int len = WavpackGetTagItem (ctx, "cuesheet", NULL, 0); - if (len) { - emb_cuesheet = malloc (len); - if (emb_cuesheet) { - WavpackGetTagItem (ctx, "Cuesheet", emb_cuesheet, len); - trace ("got cuesheet\n%s\n", emb_cuesheet); - DB_playItem_t *last = deadbeef->pl_insert_cue_from_buffer (after, it, emb_cuesheet, strlen (emb_cuesheet), totalsamples, samplerate); - free (emb_cuesheet); - if (last) { - deadbeef->pl_item_unref (it); - deadbeef->fclose (fp); - WavpackCloseFile (ctx); - return last; - } - trace ("pl_insert_cue_from_buffer failed!\n"); - } - } -#endif + deadbeef->pl_add_meta (it, "title", NULL); + + char s[100]; + snprintf (s, sizeof (s), "%lld", deadbeef->fgetlength (fp)); + deadbeef->pl_add_meta (it, ":FILE_SIZE", s); + snprintf (s, sizeof (s), "%d", WavpackGetBytesPerSample (ctx) * 8); + deadbeef->pl_add_meta (it, ":BPS", s); + snprintf (s, sizeof (s), "%d", WavpackGetNumChannels (ctx)); + deadbeef->pl_add_meta (it, ":CHANNELS", s); + snprintf (s, sizeof (s), "%d", WavpackGetSampleRate (ctx)); + deadbeef->pl_add_meta (it, ":SAMPLERATE", s); + snprintf (s, sizeof (s), "%d", (int)(WavpackGetAverageBitrate (ctx, 1) / 1000)); + deadbeef->pl_add_meta (it, ":BITRATE", s); + snprintf (s, sizeof (s), "%s", (WavpackGetMode (ctx) & MODE_FLOAT) ? "FLOAT" : "INTEGER"); + deadbeef->pl_add_meta (it, ":WAVPACK_MODE", s); // embedded cue const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); @@ -396,7 +368,7 @@ wv_insert (DB_playItem_t *after, const char *fname) { int wv_read_metadata (DB_playItem_t *it) { - DB_FILE *fp = deadbeef->fopen (it->fname); + DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); if (!fp) { return -1; } @@ -442,20 +414,45 @@ static const char *filetypes[] = { "wv", NULL }; // define plugin interface static DB_decoder_t plugin = { DB_PLUGIN_SET_API_VERSION - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_DECODER, .plugin.id = "wv", .plugin.name = "WavPack decoder", - .plugin.descr = ".wv player using libwavpack", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.descr = "WavPack (.wv, .iso.wv) player", + .plugin.copyright = + "WavPack plugin for DeaDBeeF Player\n" + "Copyright (C) 2009-2011, Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Copyright (C) 2010-2011, David Bryant <david@wavpack.com>\n" + "All rights reserved.\n" + "\n" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted provided that the following conditions are met:\n" + " * Redistributions of source code must retain the above copyright\n" + " notice, this list of conditions and the following disclaimer.\n" + " * Redistributions in binary form must reproduce the above copyright\n" + " notice, this list of conditions and the following disclaimer in the\n" + " documentation and/or other materials provided with the distribution.\n" + " * Neither the name of the <organization> nor the\n" + " names of its contributors may be used to endorse or promote products\n" + " derived from this software without specific prior written permission.\n" + "\n" + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n" + "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n" + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n" + "DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\n" + "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n" + "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" + "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n" + "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n" + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + , .plugin.website = "http://deadbeef.sf.net", .open = wv_open, .init = wv_init, .free = wv_free, - .read_int16 = wv_read_int16, - .read_float32 = wv_read_float32, + .read = wv_read, .seek = wv_seek, .seek_sample = wv_seek_sample, .insert = wv_insert, diff --git a/plugins/wildmidi/wildmidiplug.c b/plugins/wildmidi/wildmidiplug.c index e9c976b3..87aee260 100644 --- a/plugins/wildmidi/wildmidiplug.c +++ b/plugins/wildmidi/wildmidiplug.c @@ -1,6 +1,6 @@ /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -42,7 +42,7 @@ typedef struct { } wmidi_info_t; DB_fileinfo_t * -wmidi_open (void) { +wmidi_open (uint32_t hints) { DB_fileinfo_t *_info = (DB_fileinfo_t *)malloc (sizeof (wmidi_info_t)); memset (_info, 0, sizeof (wmidi_info_t)); return _info; @@ -52,16 +52,17 @@ int wmidi_init (DB_fileinfo_t *_info, DB_playItem_t *it) { wmidi_info_t *info = (wmidi_info_t *)_info; - info->m = WildMidi_Open (it->fname); + info->m = WildMidi_Open (deadbeef->pl_find_meta (it, ":URI")); if (!info->m) { - trace ("wmidi: failed to open %s\n", it->fname); + trace ("wmidi: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI")); return -1; } _info->plugin = &wmidi_plugin; - _info->channels = 2; - _info->bps = 16; - _info->samplerate = 44100; + _info->fmt.channels = 2; + _info->fmt.bps = 16; + _info->fmt.samplerate = 44100; + _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; return 0; @@ -96,13 +97,13 @@ wmidi_seek_sample (DB_fileinfo_t *_info, int sample) { wmidi_info_t *info = (wmidi_info_t *)_info; unsigned long int s = sample; WildMidi_SampledSeek (info->m, &s); - _info->readpos = s/44100.0f; + _info->readpos = s/(float)_info->fmt.samplerate; return 0; } int wmidi_seek (DB_fileinfo_t *_info, float time) { - return wmidi_seek_sample (_info, time * 44100); + return wmidi_seek_sample (_info, time * _info->fmt.samplerate); } DB_playItem_t * @@ -116,12 +117,10 @@ wmidi_insert (DB_playItem_t *after, const char *fname) { } struct _WM_Info *inf = WildMidi_GetInfo (m); - it = deadbeef->pl_item_alloc (); - it->decoder_id = deadbeef->plug_get_decoder_id (wmidi_plugin.plugin.id); - it->fname = strdup (fname); + it = deadbeef->pl_item_alloc_init (fname, wmidi_plugin.plugin.id); deadbeef->pl_add_meta (it, "title", NULL); deadbeef->pl_set_item_duration (it, inf->approx_total_samples / 44100.f); - it->filetype = "MID"; + deadbeef->pl_add_meta (it, ":FILETYPE", "MID"); after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); WildMidi_Close (m); @@ -132,7 +131,8 @@ wmidi_insert (DB_playItem_t *after, const char *fname) { int wmidi_start (void) { - const char *config_files = deadbeef->conf_get_str ("wildmidi.config", DEFAULT_TIMIDITY_CONFIG); + char config_files[1000]; + deadbeef->conf_get_str ("wildmidi.config", DEFAULT_TIMIDITY_CONFIG, config_files, sizeof (config_files)); char config[1024] = ""; const char *p = config_files; while (p) { @@ -186,12 +186,30 @@ static const char settings_dlg[] = DB_decoder_t wmidi_plugin = { DB_PLUGIN_SET_API_VERSION .plugin.type = DB_PLUGIN_DECODER, - .plugin.version_major = 0, - .plugin.version_minor = 1, + .plugin.version_major = 1, + .plugin.version_minor = 0, .plugin.name = "WildMidi player", - .plugin.descr = "MIDI player based on WildMidi library", - .plugin.author = "Alexey Yakovenko", - .plugin.email = "waker@users.sourceforge.net", + .plugin.descr = "MIDI player based on WildMidi library\n\nRequires freepats package to be installed\nSee http://freepats.zenvoid.org/\nMake sure to set correct freepats.cfg path in plugin settings.", + .plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "Uses modified WildMidi v0.2.2\n" + "(C) 2001-2004 Chris Ison\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , .plugin.website = "http://deadbeef.sf.net", .plugin.start = wmidi_start, .plugin.stop = wmidi_stop, @@ -200,7 +218,7 @@ DB_decoder_t wmidi_plugin = { .open = wmidi_open, .init = wmidi_init, .free = wmidi_free, - .read_int16 = wmidi_read, + .read = wmidi_read, .seek = wmidi_seek, .seek_sample = wmidi_seek_sample, .insert = wmidi_insert, |