static gboolean xmms_faad_gapless_try (xmms_xform_t *xform) { xmms_faad_data_t *data; gint64 start = 0, stop = 0; g_return_val_if_fail (xform, FALSE); data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, FALSE); xmms_xform_auxdata_get_int64 (xform, "startsamples", &start); if (start == 0) { XMMS_DBG ("First frame of AAC should be ignored, but is not. Trying to fix."); start = xmms_faad_get_framesize (xform); if (start == 0) { XMMS_DBG ("No luck. Couldn't get the framesize."); } } if (start != 0) { xmms_xform_auxdata_set_int (xform, "startsamples", start); } xmms_xform_auxdata_get_int64 (xform, "stopsamples", &stop); if (stop != 0) { xmms_xform_auxdata_set_int (xform, "stopsamples", stop); } return (start != 0) || (stop != 0); }
gint xmms_asf_get_track (xmms_xform_t *xform, asf_file_t *file) { xmms_asf_data_t *data; uint8_t stream_count; gint i; g_return_val_if_fail (xform, -1); data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); stream_count = asf_get_stream_count (file); for (i=1; i <= stream_count; i++) { asf_stream_t *stream = asf_get_stream (file, i); if (stream->type == ASF_STREAM_TYPE_AUDIO && !(stream->flags & ASF_STREAM_FLAG_HIDDEN)) { asf_waveformatex_t *wfx = stream->properties; const gchar *mimetype; if (wfx->wFormatTag == 0x160) mimetype = "audio/x-ffmpeg-wmav1"; else if (wfx->wFormatTag == 0x161) mimetype = "audio/x-ffmpeg-wmav2"; else continue; data->samplerate = wfx->nSamplesPerSec; data->channels = wfx->nChannels; data->bitrate = wfx->nAvgBytesPerSec * 8; xmms_xform_auxdata_set_bin (xform, "decoder_config", wfx->data, wfx->cbSize); xmms_xform_auxdata_set_int (xform, "block_align", wfx->nBlockAlign); xmms_xform_auxdata_set_int (xform, "bitrate", data->bitrate); xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, mimetype, XMMS_STREAM_TYPE_FMT_SAMPLERATE, data->samplerate, XMMS_STREAM_TYPE_FMT_CHANNELS, data->channels, XMMS_STREAM_TYPE_END); return i; } } return -1; }
static gboolean xmms_mp4_gapless_set (xmms_xform_t *xform, guint64 startsamples, guint64 stopsamples) { g_return_val_if_fail ((gint64)startsamples >= 0, FALSE); g_return_val_if_fail ((gint64)stopsamples >= 0, FALSE); g_return_val_if_fail (startsamples <= stopsamples, FALSE); xmms_xform_auxdata_set_int (xform, "startsamples", startsamples); xmms_xform_auxdata_set_int (xform, "stopsamples", stopsamples); return TRUE; }
static gint xmms_mp4_read (xmms_xform_t *xform, xmms_sample_t *buf, gint len, xmms_error_t *err) { xmms_mp4_data_t *data; guint size, bytes_read = 0; data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); size = MIN (data->outbuf->len, len); while (size == 0) { guchar *tmpbuf; guint tmpbuflen; gint duration, offset; if (data->sampleid >= data->numsamples) { XMMS_DBG ("MP4 EOF"); return 0; } bytes_read = mp4ff_read_sample (data->mp4ff, data->track, data->sampleid, &tmpbuf, &tmpbuflen); offset = mp4ff_get_sample_offset (data->mp4ff, data->track, data->sampleid); duration = mp4ff_get_sample_duration (data->mp4ff, data->track, data->sampleid); data->sampleid++; xmms_xform_auxdata_set_int (xform, "frame_offset", offset); xmms_xform_auxdata_set_int (xform, "frame_duration", duration); if (bytes_read > 0) { g_string_append_len (data->outbuf, (gchar *) tmpbuf, tmpbuflen); g_free (tmpbuf); } size = MIN (data->outbuf->len, len); } memcpy (buf, data->outbuf->str, size); g_string_erase (data->outbuf, 0, size); return size; }
static gboolean xmms_midsquash_init (xmms_xform_t *xform) { xmms_error_t error; xmms_midsquash_data_t *data; gulong track_len, len; gint32 ticks_per_quarter_note; const gchar *metakey; guchar buf[4096]; gint ret; guchar prev_event = 0; GArray *events = NULL; GArray *track_data = NULL; g_return_val_if_fail (xform, FALSE); data = g_new0 (xmms_midsquash_data_t, 1); xmms_xform_private_data_set (xform, data); data->midi0_data = NULL; if (!xmms_xform_auxdata_get_int (xform, "tempo", &ticks_per_quarter_note)) { XMMS_DBG ("MIDI xform missing 'tempo' auxdata value"); goto error_cleanup; } xmms_xform_auxdata_set_int (xform, "tempo", ticks_per_quarter_note); /* Load all the tracks */ events = g_array_new(FALSE, FALSE, sizeof(xmms_midsquash_event_t)); track_data = g_array_new(FALSE, FALSE, sizeof(GString *)); while (xmms_xform_read (xform, buf, 4, &error) == 4) { track_len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; GString *t = g_string_sized_new(track_len); ret = xmms_xform_read (xform, t->str, track_len, &error); g_array_append_val(track_data, t); /* Run through the MIDI data in this track, and convert it to a list of * events in absolute ticks (instead of delta ticks.) */ gulong i = 0; gulong abs_ticks = 0; while (i < track_len) { abs_ticks += xmms_midisquash_read_midi_num(t->str, &i); xmms_midsquash_event_t event; gboolean ignore_event = FALSE; event.time = abs_ticks; event.offset = &t->str[i]; gulong i0 = i; /* Read MIDI event */ guchar midi_event = t->str[i]; if (!(midi_event & 0x80)) { /* This is a running-status byte */ midi_event = prev_event; event.running_status = prev_event; } else { if ((midi_event & 0xF0) != 0xF0) { /* Meta events (0xF0 through 0xFF) can appear in the middle of * running-status data without affecting the running status, so we * only update the 'last event' if this isn't a meta-event. */ prev_event = midi_event; } event.running_status = 0; i++; } switch (midi_event & 0xF0) { case 0x80: /* Note off (two bytes) */ i += 2; break; case 0x90: /* Note on (two bytes) */ i += 2; break; case 0xA0: /* Key pressure (two bytes) */ i += 2; break; case 0xB0: /* Controller change (two bytes) */ i += 2; break; case 0xC0: /* Instrument change (one byte) */ i += 1; break; case 0xD0: /* Channel pressure (one byte) */ i += 1; break; case 0xE0: /* Pitch bend (two bytes) */ i += 2; break; case 0xF0: { if (midi_event == 0xFF) { /* Meta-event */ if (t->str[i] == 0x2F) { /* This is an end-of-track event, so we need to ignore it * otherwise the song will end as soon as we encounter the end * of the shortest track (which could be quite early on.) */ ignore_event = TRUE; } i++; /* event type */ } /* else sysex */ len = xmms_midisquash_read_midi_num(t->str, &i); i += len; break; } default: XMMS_DBG ("Corrupted MIDI file (invalid event 0x%02X)", midi_event); goto error_cleanup; } event.length = i - i0; if (!ignore_event) g_array_append_val(events, event); } /* end loop: run through all the track's events */ } /* Now that all the events have been read in, in absolute time, sorting them * will put them all in playable order. */ g_array_sort(events, xmms_midsquash_sort_events); /* Now copy all the sorted events into a big array, which will be used as * the output data. */ data->midi0_data = g_string_new(""); gulong last_time = 0; guint64 playtime_us = 0; gulong us_per_quarter_note = 500000; gulong i, j; guchar val; for (i = 0; i < events->len; i++) { xmms_midsquash_event_t *e; e = &g_array_index(events, xmms_midsquash_event_t, i); /* Calculate the delta time and write it out in MIDI style */ gulong delta_ticks = e->time - last_time; if (delta_ticks & (0x7F << 21)) { val = ((delta_ticks >> 21) & 0x7F) | 0x80; g_string_append_len(data->midi0_data, (gchar *)&val, 1); } if (delta_ticks & ((0x7F << 21) | (0x7F << 14))) { val = ((delta_ticks >> 14) & 0x7F) | 0x80; g_string_append_len(data->midi0_data, (gchar *)&val, 1); }
static gboolean xmms_mid1_init (xmms_xform_t *xform) { xmms_error_t error; xmms_mid1_data_t *data; guchar buf[4096]; gulong len; gint ret; g_return_val_if_fail (xform, FALSE); data = g_new0 (xmms_mid1_data_t, 1); g_return_val_if_fail (data, FALSE); xmms_xform_private_data_set (xform, data); ret = xmms_xform_read (xform, buf, 4, &error); if (strncmp ((char *)buf, "RIFF", 4) == 0) { /* This is an .rmi file, find the data chunk */ gboolean is_rmid = FALSE; /* Skip the RIFF length and RMID type (we wouldn't be here if it wasn't * RMID thanks to xmms_magic_add above.) */ xmms_xform_read (xform, buf, 8, &error); /* skip RIFF length */ for (;;) { /* Get chunk type and length */ xmms_xform_read (xform, buf, 8, &error); if (strncmp ((char *)buf, "data", 4) == 0) { /* We found the data chunk, the rest is just a normal MIDI file. We * chew up the "MThd" signature though, as the code below expects it * to be gone (as it would in a normal MIDI file when we eat it to * check the RIFF header above.) */ is_rmid = TRUE; ret = xmms_xform_read (xform, buf, 4, &error); break; } /* If we're here, this isn't the "data" chunk, so skip over it */ len = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); while (len > 0) { ret = xmms_xform_read (xform, buf, MIN (len, sizeof (buf)), &error); if (ret < 1) break; len -= ret; } } if (!is_rmid) { xmms_log_error ("RMID file has no 'data' chunk!"); goto cleanup; } } /* Once we get here we're just after the MThd signature */ ret = xmms_xform_read (xform, buf, 10, &error); gint header_len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; if (header_len != 6) { xmms_log_error ("Unexpected MThd header length"); goto cleanup; } /* Don't need to check it's a type-0 or 1 as the magic matching did it */ guint track_count = (buf[6] << 8) | buf[7]; if (track_count == 0) { xmms_log_error ("MIDI file has no tracks?!"); goto cleanup; } guint ticks_per_quarter_note = (buf[8] << 8) | buf[9]; if (ticks_per_quarter_note & 0x8000) { /* TODO */ xmms_log_error ("SMPTE timing not implemented"); goto cleanup; } xmms_xform_auxdata_set_int (xform, "tempo", ticks_per_quarter_note); data->chunked_data = g_string_sized_new (1024); /* Load all the tracks */ while (xmms_xform_read (xform, buf, 8, &error) == 8) { len = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; XMMS_DBG ("%.4s len is %lu", buf, len); if (strncmp ((char *)buf, "MTrk", 4) != 0) { XMMS_DBG ("Ignoring unknown chunk: %.4s", buf); /* Skip over the chunk - we don't use seek as it's not always implemented * by the parent xform. */ while (len > 0) { ret = xmms_xform_read (xform, buf, MIN (len, sizeof (buf)), &error); if (ret < 1) break; len -= ret; } } else { /* Append the big-endian length */ g_string_append_len (data->chunked_data, (gchar *)&buf[4], 4); for (;;) { gulong amt = MIN (len, sizeof (buf)); if (amt == 0) break; ret = xmms_xform_read (xform, (gchar *)buf, amt, &error); if (ret != amt) { /* Short read */ XMMS_DBG ("Short read"); goto cleanup; } g_string_append_len (data->chunked_data, (gchar *)buf, amt); len -= amt; } } } xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, "audio/miditracks", XMMS_STREAM_TYPE_FMT_CHANNELS, 16, XMMS_STREAM_TYPE_END); return TRUE; cleanup: if (data->chunked_data) g_string_free (data->chunked_data, TRUE); g_free (data); return FALSE; }