static int opusdec_read_metadata (DB_playItem_t *it) { int res = -1; DB_FILE *fp = NULL; OggOpusFile *opusfile = NULL; const OpusHead *head = NULL; deadbeef->pl_lock (); const char *uri = strdupa (deadbeef->pl_find_meta (it, ":URI")); deadbeef->pl_unlock (); fp = deadbeef->fopen (uri); if (!fp) { goto error; } if (fp->vfs->is_streaming ()) { goto error; } opusfile = opus_file_open (fp); if (!opusfile) { goto error; } int tracknum = deadbeef->pl_find_meta_int (it, ":TRACKNUM", -1); head = op_head (opusfile, tracknum); if (!head) { goto error; } if (update_vorbis_comments (it, opusfile, tracknum)) { goto error; } res = 0; error: if (opusfile) { op_free (opusfile); } if (fp) { deadbeef->fclose (fp); } return res; }
static bool new_streaming_link(opusdec_info_t *info, const int new_link) { if (!info->info.file->vfs->is_streaming () || new_link < 0) { return false; } update_vorbis_comments(info->it, info->opusfile, new_link); send_event(info->it, DB_EV_SONGSTARTED); send_event(info->it, DB_EV_TRACKINFOCHANGED); deadbeef->sendmessage(DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0); info->cur_bit_stream = new_link; const OpusHead *head = op_head (info->opusfile, new_link); if (head && info->info.fmt.channels != head->channel_count) { info->info.fmt.channels = head->channel_count; deadbeef->pl_set_meta_int (info->it, ":CHANNELS", head->channel_count); return true; } return false; }
// refresh_playlist == 1 means "send playlistchanged event if metadata had been changed" // refresh_playlist == 2 means "don't change memory, just check for changes" static int update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc, int refresh_playlist) { if (refresh_playlist == 1) { if (!update_vorbis_comments (it, vc, 2)) { return 0; } } if (vc) { if (refresh_playlist != 2) { deadbeef->pl_delete_all_meta (it); } for (int i = 0; i < vc->comments; i++) { char *s = vc->user_comments[i]; int m; 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] == '=') { if (refresh_playlist == 2) { deadbeef->pl_lock (); const char *val = deadbeef->pl_find_meta (it, metainfo[m+1]); if (!val || strcmp (val, s+l+1)) { deadbeef->pl_unlock (); return 1; } deadbeef->pl_unlock (); } else { deadbeef->pl_append_meta (it, metainfo[m+1], s + l + 1); break; } } } if (!metainfo[m] && refresh_playlist != 2) { 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 *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); } } } } } if (refresh_playlist == 2) { return 0; } deadbeef->pl_add_meta (it, "title", NULL); uint32_t f = deadbeef->pl_get_item_flags (it); f &= ~DDB_TAG_MASK; f |= DDB_TAG_VORBISCOMMENTS; deadbeef->pl_set_item_flags (it, f); ddb_playlist_t *plt = deadbeef->plt_get_curr (); if (plt) { deadbeef->plt_modified (plt); deadbeef->plt_unref (plt); } if (refresh_playlist) { deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } return 0; }
static DB_playItem_t * opusdec_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { return NULL; } 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->plt_set_item_duration (plt, it, -1); deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); deadbeef->fclose (fp); return after; } OggOpusFile *opusfile = opus_file_open (fp); if (!opusfile) { deadbeef->fclose (fp); return NULL; } long nstreams = op_link_count (opusfile); int64_t currentsample = 0; for (int stream = 0; stream < nstreams; stream++) { const OpusHead *head = op_head (opusfile, stream); if (!head) { continue; } int64_t totalsamples = op_pcm_total (opusfile, stream); const float duration = totalsamples / 48000.f; DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->pl_set_meta_int (it, ":TRACKNUM", stream); deadbeef->plt_set_item_duration (plt, it, duration); if (nstreams > 1) { deadbeef->pl_item_set_startsample (it, currentsample); deadbeef->pl_item_set_endsample (it, currentsample + totalsamples - 1); deadbeef->pl_set_item_flags (it, DDB_IS_SUBTRACK); } if (update_vorbis_comments (it, opusfile, stream)) continue; int samplerate = 48000; int64_t startsample = deadbeef->pl_item_get_startsample (it); int64_t endsample = deadbeef->pl_item_get_endsample (it); const off_t start_offset = sample_offset(opusfile, startsample-1); const off_t end_offset = sample_offset(opusfile, endsample); char *filetype = NULL; const off_t stream_size = oggedit_opus_stream_info(deadbeef->fopen(fname), start_offset, end_offset, &filetype); if (filetype) { deadbeef->pl_replace_meta(it, ":FILETYPE", filetype); free(filetype); } if (stream_size > 0) { set_meta_ll(it, ":OPUS_STREAM_SIZE", stream_size); deadbeef->pl_set_meta_int(it, ":BITRATE", 8.f * samplerate * stream_size / totalsamples / 1000); } set_meta_ll (it, ":FILE_SIZE", fsize); deadbeef->pl_set_meta_int (it, ":CHANNELS", head->channel_count); deadbeef->pl_set_meta_int (it, ":SAMPLERATE", samplerate); if (nstreams == 1) { DB_playItem_t *cue = deadbeef->plt_process_cue (plt, after, it, totalsamples, samplerate); if (cue) { deadbeef->pl_item_unref (it); op_free(opusfile); deadbeef->fclose (fp); return cue; } } else { currentsample += totalsamples; } after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); } op_free(opusfile); deadbeef->fclose (fp); return after; }
static int opusdec_init (DB_fileinfo_t *_info, DB_playItem_t *it) { opusdec_info_t *info = (opusdec_info_t *)_info; if (!info->info.file) { deadbeef->pl_lock (); const char *uri = strdupa (deadbeef->pl_find_meta (it, ":URI")); deadbeef->pl_unlock (); DB_FILE *fp = deadbeef->fopen (uri); if (!fp) { return -1; } info->info.file = fp; info->it = it; deadbeef->pl_item_ref (it); } info->opusfile = opus_file_open (info->info.file); if (!info->opusfile) { return -1; } const OpusHead *head = op_head (info->opusfile, 0); if (head->channel_count > 8) { trace ("opus: the track has %d channels, but 8 is max supported.\n"); return -1; } // take this parameters from your input file // we set constants for clarity sake _info->fmt.bps = 32; _info->fmt.is_float = 1; _info->fmt.channels = head->channel_count; _info->fmt.samplerate = 48000; if (head->mapping_family == 1) { info->channelmap = oggedit_vorbis_channel_map (head->channel_count); } for (int i = 0; i < (_info->fmt.channels&0x1f); i++) { _info->fmt.channelmask |= 1 << i; } _info->readpos = 0; _info->plugin = &plugin; // set all gain adjustment to 0, because deadbeef is performing that. op_set_gain_offset (info->opusfile, OP_ABSOLUTE_GAIN, 0); if (info->info.file->vfs->is_streaming ()) { deadbeef->pl_item_set_startsample (it, 0); if (deadbeef->pl_get_item_duration (it) < 0) { deadbeef->pl_item_set_endsample (it, -1); } else { deadbeef->pl_item_set_endsample (it, op_pcm_total (info->opusfile, -1) - 1); } if (update_vorbis_comments (it, info->opusfile, -1)) return -1; deadbeef->pl_set_meta_int(it, ":TRACKNUM", 0); } else { opusdec_seek_sample (_info, 0); } deadbeef->pl_replace_meta (it, "!FILETYPE", "Ogg Opus"); deadbeef->pl_set_meta_int (it, ":CHANNELS", head->channel_count); info->cur_bit_stream = -1; return 0; }