// ============================================================================= // Projet : Plugin XMMS pour le format bonk // Programme : bonk-xmms.cc // Création : 23.03.2002 // Révision : 15.06.2002 // Objet : fichier principal // ============================================================================= // (c) 2002 - Fabrice Haberer-Proust // ============================================================================= // 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., // 675 Mass Ave, Cambridge, MA 02139, USA. // ============================================================================= // TODO: neutraliser les noms de fichier commencant par http:// (cf vorbis.c) // == En-têtes XMMS et plugin ================================================== #include #include #include #include #include #include #include #include #include //#include "xmms/i18n.h" // == En-têtes bonk ============================================================ #include #include #include #include #include // == Déclarations plugin ====================================================== #define VERSION "0.12" #define MAX_BONK_OFFSET 5000 // 1000000 dans le programme bonk, mais la // recherche de la balise étant très lente, on se // limite à 5000 caractères, ce qui dépasse de loin // la taille raisonnable du champ d'informations char tag_artist[] = "Artist: "; char tag_title [] = "\nTitle: "; // -- En-tête bonk -- typedef struct { char BONK[5]; /**< Balise, doit toujours être '\0BONK' */ guint8 version; /**< Version, doit être zéro */ guint32 length; /**< Nombre d'échantillons */ guint32 rate; /**< Fréquence d'échantillonnage ? */ guint8 channels; /**< Nombre de canaux */ guint8 lossless; /**< 1 si compression sans perte, 0 sinon */ guint8 mid_side; /**< ??? 0 ou 1 */ guint16 n_taps; /**< ??? */ guint8 down_sampling; /**< ??? */ guint16 samples_per_packet; /**< Nombre d'échantillons par paquet */ } BONKHEADER; // -- Entrée dans le sommaire -- struct toc_entry { long file_position; int byte; int bit_no; int length_remaining; // TODO: déduire length_remaining de file_position, byte et bit_no ? // -- Constructeur -- toc_entry(long _file_position, int _byte, int _bit_no, int _length_remaining) { file_position = _file_position ; byte = _byte ; bit_no = _bit_no ; length_remaining = _length_remaining; } }; // == Variables globales ======================================================= std::vector toc; // Table of content unsigned int toc_entry_current; int packet_index = 0; static pthread_t decode_thread; static gboolean audio_error = FALSE; static short bonk_file_playing = 0; static FILE *bonk_file; static BONKHEADER bonk_header; static int bonk_file_seek_to = -1; // == Structure InputPlugin ==================================================== static void bonk_xmms__init (void); static void bonk_xmms__about (void); static int bonk_xmms__is_our_file (char *); static void bonk_xmms__play_file (char *); static void bonk_xmms__stop (void); static void bonk_xmms__pause (short); static void bonk_xmms__seek (int); static int bonk_xmms__get_time (void); static void bonk_xmms__get_song_info(char *, char **, int *); InputPlugin bonk_ip = { NULL, NULL, NULL, bonk_xmms__init, bonk_xmms__about, NULL, bonk_xmms__is_our_file, NULL, bonk_xmms__play_file, bonk_xmms__stop, bonk_xmms__pause, bonk_xmms__seek, NULL, bonk_xmms__get_time, NULL, NULL, NULL, NULL, NULL, NULL, NULL, bonk_xmms__get_song_info, NULL, NULL }; // == Journal ================================================================= /** * Écriture d'un message dans le journal * La longueur du message ne doit pas dépasser 500 caractères. Aucune vérification de longueur n'est effectuée. * @param line int Numéro de la ligne appellant cette fonction * @param func char* Nom de la fonction appellante * @param format char* Chaîne de format * @param ... Liste d'arguments variables */ void bonk_xmms__log(int line, const char *func, char *format, ...) { #ifdef DEBUG FILE *logfile = fopen("/tmp/plugin-bonk.log", "a"); va_list ap; char msg[500]; if (logfile == (FILE *) NULL) return; va_start(ap, format); (void) vsnprintf(msg, 500, format, ap); va_end(ap); (void) fprintf(logfile, "%ld:%4d:%s:%s\n", time(0), line, func, msg); fclose(logfile); #endif } // == Lecture d'entier dans le fichier (endian safe) =========================== /** * Lecture d'un entier non signé de 16 binons * @param file FILE* Fichier à lire, avec le pointeur de lecture correctement positionné * @return guint16 L'entier lu */ guint16 read_guint16(FILE *file) { guint16 u16; guint8 u8; size_t count; count = fread(&u8, sizeof(guint8), 1, file); if (count != 1) bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); u16 = guint16(u8); count = fread(&u8, sizeof(guint8), 1, file); if (count != 1) bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); u16 += guint16(u8 << 8); return u16; } /** * Lecture d'un entier non signé de 32 binons * @param file FILE* Fichier à lire, préalablement ouvert, avec le pointeur de lecture correctement positionné * @return guint32 L'entier lu */ guint32 read_guint32(FILE *file) { guint32 u32; guint8 u8; size_t count; count = fread(&u8, sizeof(guint8), 1, file); if (count != 1) bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); u32 = guint32(u8); count = fread(&u8, sizeof(guint8), 1, file); if (count != 1) bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); u32 += guint32(u8 << 8); count = fread(&u8, sizeof(guint8), 1, file); if (count != 1) bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); u32 += guint32(u8 << 16); count = fread(&u8, sizeof(guint8), 1, file); if (count != 1) bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); u32 += guint32(u8 << 24); return u32; } // == Lecture d'un en-tête bonk ================================================ /** * Lecture et vérification de l'en-tête d'un fichier bonk * @param bonkheader BONKHEADER * Description de l'en-tête bonk * @param bonk FILE * Descripteur du fichier bonk ouvert préalablement en lecture, avec le pointeur en début de fichier * @return long la position de la balise bonk si le format est correct, -1 sinon. Si le format est correcte, le pointeur est positionné sur le début des données */ static long bonkheader_read(BONKHEADER *bonkheader, FILE *bonk) { char bonk_tag[5]; long bonk_tag_offset; size_t count; int result; // -- Recherche de la balise BONK -- count = fread(bonk_tag, sizeof(char), 5, bonk); if (count != 5) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } bonk_tag_offset = -1; while ((feof(bonk) == 0) && (ftell(bonk) < MAX_BONK_OFFSET) && (bonk_tag_offset < 0)) { if ((bonk_tag[0] == '\0') && (bonk_tag[1] == 'B') && (bonk_tag[2] == 'O') && (bonk_tag[3] == 'N') && (bonk_tag[4] == 'K')) { bonk_tag_offset = ftell(bonk) - 5; } else { bonk_tag[0] = bonk_tag[1]; bonk_tag[1] = bonk_tag[2]; bonk_tag[2] = bonk_tag[3]; bonk_tag[3] = bonk_tag[4]; count = fread(bonk_tag + 4, sizeof(char), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } } } if (bonk_tag_offset < 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "pas trouvé la balise bonk"); return -1; } // -- Lecture de l'en-tête bonk -- result = fseek(bonk, -5L, SEEK_CUR); if (result != 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fseek"); return -1; } count = fread(&bonkheader->BONK , sizeof(char ), 5, bonk); if (count != 5) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } count = fread(&bonkheader->version , sizeof(guint8 ), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } // count = fread(&bonkheader->length , sizeof(guint32), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } bonkheader->length = read_guint32(bonk); // count = fread(&bonkheader->rate , sizeof(guint32), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } bonkheader->rate = read_guint32(bonk); count = fread(&bonkheader->channels , sizeof(guint8 ), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } count = fread(&bonkheader->lossless , sizeof(guint8 ), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } count = fread(&bonkheader->mid_side , sizeof(guint8 ), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } bonkheader->n_taps = read_guint16(bonk); count = fread(&bonkheader->down_sampling , sizeof(guint8 ), 1, bonk); if (count != 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return -1; } bonkheader->samples_per_packet = read_guint16(bonk); // -- Vérifications -- if (bonkheader->version != 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "le numéro de version doit être zéro"); return -1; } if ((bonkheader->channels != 1) && (bonkheader->channels != 2)) { bonk_xmms__log(__LINE__, __FUNCTION__, "le nombre de canaux doit être un ou deux"); return -1; } if ((bonkheader->lossless != 0) && (bonkheader->lossless != 1)) { bonk_xmms__log(__LINE__, __FUNCTION__, "lossless doit être zéro ou un"); return -1; } if ((bonkheader->mid_side != 0) && (bonkheader->mid_side != 1)) { bonk_xmms__log(__LINE__, __FUNCTION__, "mid_side doit être zéro ou un"); return -1; } // TODO: reprendre les vérifications du programme original -- // -- Fin -- return bonk_tag_offset; } // ============================================================================= // Original bonk source code by Paul Harrison // ============================================================================= // Number of bits required to store a value: int bits_to_store(guint32 i) { int result = 0; while (i) { result++; i >>= 1; } return result; } // Bitstream reader: /** * Classe bitstream_in : fichier accessible en lecture binon par binon */ struct bitstream_in { FILE *f_in; int byte; int bit_no; /** * Initialisation * @param _f_in FILE * Le fichier à parcourir */ void setup(FILE *_f_in) { f_in = _f_in; bit_no = 8; } /** * Lecture d'un seul binons * Cette méthode modifie les propriétés byte et bit_no, ainsi que la position du fichier f_in * @param * @return */ int read() { if (bit_no == 8) { byte = fgetc(f_in); if (byte == EOF) { bonk_xmms__log(__LINE__, __FUNCTION__, "bitstream_in::read : unexpected end of file"); } bit_no = 0; } return (byte & (1 << bit_no++) ? 1 : 0); } /** * Lecture d'un groupe de binons * @param bits int nombre de binons à lire * @return guint32 La valeur lue */ guint32 read_uint(int bits) { guint32 value = 0; for (int i = 0 ; i < bits ; i++) value += read() << i; return value; } guint32 read_uint_max(int max) { if (!max) return 0; int bits = bits_to_store(max); guint32 value = 0; for (int i = 0 ; i < bits - 1 ; i++) if (read()) value += 1< &list, bool base_2_part, bitstream_in &in) { // Read base 2 part: int low_bits = (base_2_part ? in.read_uint(4) : 0); for (unsigned int i = 0 ; i < list.size() ; i++) { list[i] = in.read_uint(low_bits); } // Decode bitstream: int n_zeros = 0; int step = 256; bool dominant = false; std::vector bits; while ((unsigned int) n_zeros < list.size()) { int steplet = step >> 8; if (!in.read()) { for (int i = 0 ; i < steplet ; i++) { bits.push_back(dominant); } if (!dominant) { n_zeros += steplet; } step += step / adapt_level; } else { int actual_run = in.read_uint_max(steplet - 1); for (int i = 0 ; i < actual_run ; i++) { bits.push_back(dominant); } bits.push_back(!dominant); if (!dominant) { n_zeros += actual_run; } if (dominant) { n_zeros++; } step -= step / adapt_level; } if (step < 256) { step = 65536 / step; dominant = !dominant; } } // Reconstruct (unsigned) values: n_zeros = 0; int pos = 0; int level = 0; for(int i = 0 ; (unsigned int) n_zeros < list.size() ; i++) { for (;;) { if ((unsigned int) pos >= list.size()) { pos = 0; level += 1 << low_bits; } if (list[pos] >= level) break; pos++; } if (bits[i]) list[pos] += 1 << low_bits; else n_zeros++; pos++; } // Read signs: for (unsigned int i = 0 ; i < list.size() ; i++) { if (list[i] && in.read()) { list[i] = -list[i]; } } } // Accuracy of fixed point calculations const int lattice_shift = 10, sample_shift = 4, lattice_factor = 1 << lattice_shift, sample_factor = 1 << sample_shift, // Maximum allowable taps max_tap = 2048; // Default quantization level const double base_quant = 0.6, // Amount of bit rate variation rate_variation = 3.0; const int tap_quant[max_tap] = { // int(sqrt(i+1)) 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, 11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, 21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, 22,22,22,22,22,22,22,22,22,22,22,22 }; inline int shift(int a, int b) { return a + (1 << b - 1) >> b; } inline int shift_down(int a, int b) { return (a >> b) + (a < 0 ? 1 : 0); } struct lattice { int order; std::vector k, state; void init(int _order) { order = _order; k.resize(order); state.resize(order); for (int i = 0 ; i < order ; i++) { state[i] = 0; k[i] = 0; } } void dequantize() { for (int i = 0 ; i < order ; i++) { k[i] *= tap_quant[i]; } } void init_state() { for (int i = order - 2 ; i >= 0 ; i--) { int x = state[i], new_x; for(int j = 0 , p = i + 1 ; p < order ; j++, p++) { new_x = x + shift_down(k[j] * state[p], lattice_shift); state[p] += shift_down(k[j] * x, lattice_shift); x = new_x; } } } int advance_by_error(int error) { // returns value int x = error; x -= shift_down(k[order-1] * state[order-1], lattice_shift); int *k_ptr = &(k[order-2]), *state_ptr = &(state[order-2]); for (int i = order - 2 ; i >= 0 ; i--, k_ptr--, state_ptr--) { int k_value = *k_ptr, state_value = *state_ptr; x -= shift_down(k_value * state_value, lattice_shift); state_ptr[1] = state_value + shift_down(k_value * x, lattice_shift); } // Don't drift too far, to avoid overflows if (x > (sample_factor << 16)) x = (sample_factor << 16); if (x < -(sample_factor << 16)) x = -(sample_factor << 16); state[0] = x; return x; } }; struct decoder { FILE *f_in; bitstream_in bit_in; int length, length_remaining, rate; int channels; bool lossless; bool mid_side; int n_taps; int down_sampling, samples_per_packet; lattice predictor; std::vector< std::vector > predictor_initer; void begin(FILE *_f_in) { f_in = bonk_file; length = bonk_header.length ; rate = bonk_header.rate ; channels = bonk_header.channels ; lossless = bonk_header.lossless ; mid_side = bonk_header.mid_side ; n_taps = bonk_header.n_taps ; down_sampling = bonk_header.down_sampling ; samples_per_packet = bonk_header.samples_per_packet; if (channels == 0 || (channels < 2 && mid_side) || n_taps > max_tap || n_taps == 0 || down_sampling == 0 || samples_per_packet == 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "Bonk file has strange settings"); return; } predictor.init(n_taps); predictor_initer.resize(channels); for (int i = 0 ; i < channels ; i++) { predictor_initer[i].resize(n_taps); for (int j = 0 ; j < n_taps ; j++) { predictor_initer[i][j] = 0; } } length_remaining = length; bit_in.setup(f_in); } void read_packet(std::vector &samples) { // bonk_xmms__log(__LINE__, __FUNCTION__, "decoder::read_packet : ftell(bit_in.f_in) == %10ld : length_remaining == %10d", ftell(bit_in.f_in), length_remaining); // DEVEL // bonk_xmms__log(__LINE__, __FUNCTION__, "decoder::read_packet : bit_in.byte == %d : bit_in.bit_no == %d", bit_in.byte, bit_in.bit_no); // DEVEL samples.resize(samples_per_packet * down_sampling * channels); std::vector input_samples(samples_per_packet); read_list(predictor.k, false, bit_in); predictor.dequantize(); int quant = (lossless ? 1 : bit_in.read_uint(16) * sample_factor); for (int channel = 0 ; channel < channels ; channel++) { int *sample = &(samples[channel]); predictor.state = predictor_initer[channel]; predictor.init_state(); read_list(input_samples, true, bit_in); for (int i = 0 ; i < samples_per_packet ; i++) { for (int j = 0 ; j < down_sampling - 1 ; j++) { *sample = predictor.advance_by_error(0); sample += channels; } *sample = predictor.advance_by_error(input_samples[i] * quant); sample += channels; } for (int i = 0 ; i < n_taps ; i++) { predictor_initer[channel][i] = samples[samples.size() - channels + channel - i * channels]; } } if (mid_side) { for(unsigned int i = 0 ; i < samples.size() ; i += channels) { samples[i + 1] += shift(samples[i], 1); samples[i] -= samples[i + 1]; } } if (!lossless) { for(unsigned int i = 0 ; i < samples.size() ; i++) { samples[i] = shift(samples[i], sample_shift); } } if ((unsigned int) length_remaining < samples.size()) { samples.resize(length_remaining); length_remaining = 0; } else { length_remaining -= samples.size(); } } }; // ============================================================================= // End of original bonk source code // ============================================================================= extern "C" { InputPlugin *get_iplugin_info(void) { bonk_ip.description = g_strdup_printf("Bonk player " #ifdef DEBUG "(%s %s)", __DATE__, __TIME__ #else VERSION #endif ); return &bonk_ip; } } /** * Indique si un fichier est au format bonk * @param char* Nom du fichier * @return int 1 si le fichier est au format bonk, 0 sinon */ int bonk_xmms__is_our_file(char *filename) { BONKHEADER bonkheader; FILE *bonk = fopen(filename, "r"); char *extension = strrchr(filename, '.'); int is_our_file; if ((extension == NULL) || (strcasecmp(extension, ".bonk") != 0)) { return 0; } if (bonk == (FILE *) NULL) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fopen"); return 0; } if (bonkheader_read(&bonkheader, bonk) > -1) { is_our_file = 1; } else { bonk_xmms__log(__LINE__, __FUNCTION__, "bonkheader_read a renvoyé -1"); is_our_file = 0; } fclose(bonk); return is_our_file; } /** * Lecture des informations du fichier * @param in filename char * Nom du fichier * @param out title char ** Informations sur la plage * @param out length int * Longueur en millisecondes de la plage */ void bonk_xmms__get_song_info(char *filename, char **title, int *length) { BONKHEADER bonkheader; long bonk_tag_offset; char *temp; FILE *bonk; int result; size_t count; bonk = fopen(filename, "r"); if (bonk == (FILE *) NULL) return; bonk_tag_offset = bonkheader_read(&bonkheader, bonk); if (bonk_tag_offset < 0) return; if (title != NULL) { if (bonk_tag_offset > 0) { // décodage des informations contenues dans le fichier *title = (char *) g_malloc(bonk_tag_offset + 1); result = fseek(bonk, 0L, SEEK_SET); if (result != 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fseek"); return; } count = fread(*title, sizeof(char), bonk_tag_offset, bonk); if ((long) count != bonk_tag_offset) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); return; } if (strncmp(*title, tag_artist, strlen(tag_artist)) == 0) { (void) memmove(*title, *title + strlen(tag_artist), bonk_tag_offset - strlen(tag_artist)); } temp = strstr(*title, tag_title); if (temp != (char *) NULL) { temp[0] = ' '; temp[1] = '-'; temp[2] = ' '; (void) memmove(temp+ 3, temp + strlen(tag_artist), strlen(temp) - strlen(tag_artist)); } temp = strchr(*title, '\n'); if (temp != (char *) NULL) *temp = '\0'; } else { // pas d'informations, nous utilisons donc le nom du fichier temp = strrchr(filename, '/'); if (temp == (char *) NULL) { temp = filename; } else { temp++; } *title = (char *) g_malloc(strlen(temp) + 1); (void) strcpy(*title, temp); temp = strrchr(*title, '.'); if (temp != (char *) NULL) *temp = '\0'; } } // -- Longueur en millisecondes -- if (length != NULL) { *length = (int) (1000.0 * (float) bonkheader.length / (float) bonkheader.rate / (float) bonkheader.channels); } // -- Fermeture du fichier fclose(bonk); } /** * Initialisation */ static void bonk_xmms__init(void) { } /** * Informations sur ce plugin */ static void bonk_xmms__about(void) { static GtkWidget *box; box = xmms_show_message("xmms-bonk " VERSION, "bonk format by\nPaul Harrison \n\nxmms interface by\nFabrice Haberer-Proust ", "OK", FALSE, NULL, NULL); // gtk_signal_connect(GTK_OBJECT(box), "destroy", gtk_widget_destroyed, &box); } /** * Lecture du fichier bonk * @param *arg void Paramètre inutilisé, mais nécessaire pour respecter le prototype de la fonction pthread_create */ static void *play_loop(void *arg) { AFormat fmt = (G_BYTE_ORDER == G_BIG_ENDIAN) ? FMT_S16_BE : FMT_S16_LE; decoder deco; if (bonk_file == (FILE *) NULL) { bonk_xmms__log(__LINE__, __FUNCTION__, "bonk_file == NULL"); return NULL; } srand(time(0)); deco.begin(bonk_file); // -- Découpage des paquets -- int slice_index = 0; int slice_count = 8; packet_index = 0; // -- Initialisation du sommaire -- toc_entry_current = 0; toc.clear(); toc.push_back(toc_entry(ftell(deco.bit_in.f_in), deco.bit_in.byte, deco.bit_in.bit_no, deco.length_remaining)); // -- Code bonk -- std::vector samples; std::vector little_samples; while (bonk_file_playing == 1) { if (deco.length_remaining) { // -- Lecture du paquet -- if (slice_index == 0) { deco.read_packet(samples); unsigned int sec = (int) (((float)packet_index * (float)bonk_header.samples_per_packet) / (float)bonk_header.rate); // conversion n° de paquet -> seconde toc_entry_current = sec; if (sec > toc.size() - 1) { bonk_xmms__log(__LINE__, __FUNCTION__, "toc_entry_current == %3d - packet_index == %5d", toc_entry_current, packet_index); toc.push_back(toc_entry(ftell(deco.bit_in.f_in), deco.bit_in.byte, deco.bit_in.bit_no, deco.length_remaining)); } packet_index++; little_samples.resize(samples.size()); for (unsigned int i = 0 ; i < samples.size() ; i++) { if (samples[i] > 32767) { little_samples[i] = 32767; } else if (samples[i] < -32768) { little_samples[i] = -32768; } else { little_samples[i] = samples[i]; } } } // -- Temporisation -- while (((unsigned int) bonk_ip.output->buffer_free() < 2 * little_samples.size() * slice_count) && (bonk_file_playing == 1) && (bonk_file_seek_to == -1)) { xmms_usleep(10000); } // -- Données de visualisation -- bonk_ip.add_vis_pcm(bonk_ip.output->written_time(), fmt, samples.size() * sizeof(gint16) / slice_count, samples.size() * sizeof(gint16) * 2048 / slice_count, &little_samples[samples.size() * slice_index / slice_count]); // -- Écriture sur le phériphérique audio -- for (unsigned int i = samples.size() * slice_index / slice_count ; i < samples.size() * (slice_index + 1) / slice_count ; i++) { if ((bonk_file_playing == 1) && (bonk_file_seek_to == -1)) { bonk_ip.output->write_audio(&(little_samples[i]), sizeof(little_samples[i])); } else { bonk_xmms__log(__LINE__, __FUNCTION__, "break"); break; } } // -- Tranche suivante du paquet -- slice_index = (slice_index + 1) % slice_count; // -- Changement de position -- if (bonk_file_seek_to != -1) { unsigned int pos = bonk_file_seek_to; pos = (pos < toc.size()) ? pos : toc.size() - 1; toc_entry_current = pos; packet_index = pos * bonk_header.rate / bonk_header.samples_per_packet; // TODO: attention au débordement d'entiers bonk_xmms__log(__LINE__, __FUNCTION__, "CHANGEMENT DE bonk_file_seek_to"); bonk_xmms__log(__LINE__, __FUNCTION__, "toc_entry_current == %3d - packet_index == %5d", toc_entry_current, packet_index); fseek(bonk_file, toc[pos].file_position, SEEK_SET); deco.length_remaining = toc[pos].length_remaining; deco.bit_in.byte = toc[pos].byte ; deco.bit_in.bit_no = toc[pos].bit_no; slice_index = 0; bonk_file_seek_to = -1; } } else { bonk_xmms__log(__LINE__, __FUNCTION__, "fin de la lecture des paquets"); while (bonk_ip.output->buffer_playing()) xmms_usleep(20000); break; } } bonk_xmms__log(__LINE__, __FUNCTION__, "toc.size() : %d", toc.size()); bonk_ip.output->close_audio(); fclose(bonk_file); bonk_file = (FILE *) NULL; bonk_file_playing = 0; pthread_exit(NULL); } /** * Lecture d'un fichier * @param filename char * Nom du fichier à jouer */ static void bonk_xmms__play_file(char *filename) { // -- Déclarations -- long bonk_tag_offset; long bonk_data_offset; char *bonk_artist_title; char *temp; int result; size_t count; AFormat fmt = (G_BYTE_ORDER == G_BIG_ENDIAN) ? FMT_S16_BE : FMT_S16_LE; // -- Ouverture du fichier et lecture de son en-tête -- bonk_file = fopen(filename, "r"); bonk_tag_offset = bonkheader_read(&bonk_header, bonk_file); if (bonk_tag_offset < 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "le fichier n'est pas au format bonk"); return; } bonk_data_offset = ftell(bonk_file); if (bonk_data_offset < 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de ftell"); fclose(bonk_file); bonk_file = (FILE *) NULL; return; } // -- Informations sur le fichier -- // TODO: regrouper ce code avec celui de bonk_xmms__get_song_info if (bonk_tag_offset > 0) { // décodage des informations contenues dans le fichier bonk_artist_title = new char[bonk_tag_offset + 1]; result = fseek(bonk_file, 0L, SEEK_SET); if (result != 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fseek"); fclose(bonk_file); bonk_file = (FILE *) NULL; return; } count = fread(bonk_artist_title, sizeof(char), bonk_tag_offset, bonk_file); if ((long) count != bonk_tag_offset) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fread"); fclose(bonk_file); bonk_file = (FILE *) NULL; return; } if (strncmp(bonk_artist_title, tag_artist, strlen(tag_artist)) == 0) { (void) memmove(bonk_artist_title, bonk_artist_title + strlen(tag_artist), bonk_tag_offset - strlen(tag_artist)); } temp = strstr(bonk_artist_title, tag_title); if (temp != (char *) NULL) { temp[0] = ' '; temp[1] = '-'; temp[2] = ' '; (void) memmove(temp+ 3, temp + strlen(tag_artist), strlen(temp) - strlen(tag_artist)); } temp = strchr(bonk_artist_title, '\n'); if (temp != (char *) NULL) *temp = '\0'; bonk_xmms__log(__LINE__, __FUNCTION__, bonk_artist_title); // DEVEL } else { // pas d'informations, nous utilisons donc le nom du fichier temp = strrchr(filename, '/'); if (temp == (char *) NULL) { temp = filename; } else { temp++; } bonk_artist_title = new char[strlen(temp) + 1]; (void) strcpy(bonk_artist_title, temp); temp = strrchr(bonk_artist_title, '.'); if (temp != (char *) NULL) *temp = '\0'; } // -- Positionnement au début des données -- result = fseek(bonk_file, bonk_data_offset, SEEK_SET); // TODO: vérifier le résultat if (result != 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "échec de fseek"); fclose(bonk_file); bonk_file = (FILE *) NULL; return; } // -- Ouverture du périphérique audio -- if (bonk_ip.output->open_audio(fmt, bonk_header.rate, bonk_header.channels) == 0) { bonk_xmms__log(__LINE__, __FUNCTION__, "impossible d'ouvrir le périphérique audio"); audio_error = TRUE; fclose(bonk_file); bonk_file = (FILE *) NULL; return; } // -- Affichage des informations du fichier -- bonk_ip.set_info(bonk_artist_title, (int) (1000.0 * (float) bonk_header.length / (float) bonk_header.rate / (float) bonk_header.channels), 8, bonk_header.rate, bonk_header.channels); delete[] bonk_artist_title; // -- -- bonk_file_playing = 1; bonk_file_seek_to = -1; // DEVEL // -- Lecture dans un nouveau thread -- bonk_xmms__log(__LINE__, __FUNCTION__, "appel de play_loop dans un nouveau thread"); pthread_create(&decode_thread, NULL, play_loop, NULL); } /** * Arrêt de la lecture */ static void bonk_xmms__stop(void) { if (bonk_file_playing == 1) { bonk_file_playing = 0; bonk_xmms__log(__LINE__, __FUNCTION__, "Arrêt par l'utilisateur"); pthread_join(decode_thread, NULL); bonk_ip.output->close_audio(); } } /** * Pause * @param paused short État de pause */ static void bonk_xmms__pause(short paused) { if (bonk_file_playing) { bonk_ip.output->pause(paused); } } /** * Déplacement dans la plage * @param time int Position en secondes à atteindre */ static void bonk_xmms__seek(int time) { bonk_xmms__log(__LINE__, __FUNCTION__, "time == %d - toc.size() = %d", time, toc.size()); bonk_xmms__log(__LINE__, __FUNCTION__, "((time <= toc.size()) ? time : toc.size()) == %d", (((unsigned int) time <= toc.size()) ? time : toc.size())); bonk_file_seek_to = time; bonk_ip.output->flush(1000 * (((unsigned int) time <= toc.size()) ? time : toc.size())); // while (bonk_file_seek_to != -1) { // bonk_xmms__log(__LINE__, __FUNCTION__, "bonk_file_seek_to : %d", bonk_file_seek_to); // DEVEL // xmms_usleep(10000); // } bonk_xmms__log(__LINE__, __FUNCTION__, "après while..."); // DEVEL bonk_xmms__log(__LINE__, __FUNCTION__, "toc_entry_current == %d", toc_entry_current); } /** * Renvoi de la position de lecture en cours * @return int La position en secondes, -1 ou -2 en cas d'erreur */ static int bonk_xmms__get_time(void) { if (audio_error) { return -2; } if ((bonk_file_playing == 1) || (bonk_ip.output->buffer_playing())) { return bonk_ip.output->output_time(); } return -1; }