diff --git a/3rd-party/hxcmod-player/hxcmod.c b/3rd-party/hxcmod-player/hxcmod.c new file mode 100644 index 000000000..7285b4d47 --- /dev/null +++ b/3rd-party/hxcmod-player/hxcmod.c @@ -0,0 +1,1289 @@ +/////////////////////////////////////////////////////////////////////////////////// +//-------------------------------------------------------------------------------// +//-------------------------------------------------------------------------------// +//-----------H----H--X----X-----CCCCC----22222----0000-----0000------11----------// +//----------H----H----X-X-----C--------------2---0----0---0----0--1--1-----------// +//---------HHHHHH-----X------C----------22222---0----0---0----0-----1------------// +//--------H----H----X--X----C----------2-------0----0---0----0-----1-------------// +//-------H----H---X-----X---CCCCC-----222222----0000-----0000----1111------------// +//-------------------------------------------------------------------------------// +//----------------------------------------------------- http://hxc2001.free.fr --// +/////////////////////////////////////////////////////////////////////////////////// +// File : hxcmod.c +// Contains: a tiny mod player +// +// Written by: Jean François DEL NERO +// +// You are free to do what you want with this code. +// A credit is always appreciated if you include it into your prod :) +// +// This file include some parts of the Noisetracker/Soundtracker/Protracker +// Module Format documentation written by Andrew Scott (Adrenalin Software) +// (modformat.txt) +// +// The core (hxcmod.c/hxcmod.h) is designed to have the least external dependency. +// So it should be usable on almost all OS and systems. +// Please also note that no dynamic allocation is done into the HxCMOD core. +// +// Change History (most recent first): +/////////////////////////////////////////////////////////////////////////////////// +// HxCMOD Core API: +// ------------------------------------------- +// int hxcmod_init(modcontext * modctx) +// +// - Initialize the modcontext buffer. Must be called before doing anything else. +// Return 1 if success. 0 in case of error. +// ------------------------------------------- +// int hxcmod_load( modcontext * modctx, void * mod_data, int mod_data_size ) +// +// - "Load" a MOD from memory (from "mod_data" with size "mod_data_size"). +// Return 1 if success. 0 in case of error. +// ------------------------------------------- +// void hxcmod_fillbuffer( modcontext * modctx, unsigned short * outbuffer, unsigned long nbsample, tracker_buffer_state * trkbuf ) +// +// - Generate and return the next samples chunk to outbuffer. +// nbsample specify the number of stereo 16bits samples you want. +// The output format is signed 44100Hz 16-bit Stereo PCM samples. +// The output buffer size in byte must be equal to ( nbsample * 2 * 2 ). +// The optional trkbuf parameter can be used to get detailed status of the player. Put NULL/0 is unused. +// ------------------------------------------- +// void hxcmod_unload( modcontext * modctx ) +// - "Unload" / clear the player status. +// ------------------------------------------- +/////////////////////////////////////////////////////////////////////////////////// + +#include "hxcmod.h" + +/////////////////////////////////////////////////////////////////////////////////// + +// Effects list +#define EFFECT_ARPEGGIO 0x0 // Supported +#define EFFECT_PORTAMENTO_UP 0x1 // Supported +#define EFFECT_PORTAMENTO_DOWN 0x2 // Supported +#define EFFECT_TONE_PORTAMENTO 0x3 // Supported +#define EFFECT_VIBRATO 0x4 // Supported +#define EFFECT_VOLSLIDE_TONEPORTA 0x5 // Supported +#define EFFECT_VOLSLIDE_VIBRATO 0x6 // Supported +#define EFFECT_VOLSLIDE_TREMOLO 0x7 // - TO BE DONE - +#define EFFECT_SET_PANNING 0x8 // - TO BE DONE - +#define EFFECT_SET_OFFSET 0x9 // Supported +#define EFFECT_VOLUME_SLIDE 0xA // Supported +#define EFFECT_JUMP_POSITION 0xB // Supported +#define EFFECT_SET_VOLUME 0xC // Supported +#define EFFECT_PATTERN_BREAK 0xD // Supported + +#define EFFECT_EXTENDED 0xE +#define EFFECT_E_FINE_PORTA_UP 0x1 // Supported +#define EFFECT_E_FINE_PORTA_DOWN 0x2 // Supported +#define EFFECT_E_GLISSANDO_CTRL 0x3 // - TO BE DONE - +#define EFFECT_E_VIBRATO_WAVEFORM 0x4 // - TO BE DONE - +#define EFFECT_E_SET_FINETUNE 0x5 // - TO BE DONE - +#define EFFECT_E_PATTERN_LOOP 0x6 // Supported +#define EFFECT_E_TREMOLO_WAVEFORM 0x7 // - TO BE DONE - +#define EFFECT_E_SET_PANNING_2 0x8 // - TO BE DONE - +#define EFFECT_E_RETRIGGER_NOTE 0x9 // - TO BE DONE - +#define EFFECT_E_FINE_VOLSLIDE_UP 0xA // Supported +#define EFFECT_E_FINE_VOLSLIDE_DOWN 0xB // Supported +#define EFFECT_E_NOTE_CUT 0xC // Supported +#define EFFECT_E_NOTE_DELAY 0xD // - TO BE DONE - +#define EFFECT_E_PATTERN_DELAY 0xE // Supported +#define EFFECT_E_INVERT_LOOP 0xF // - TO BE DONE - +#define EFFECT_SET_SPEED 0xF0 // Supported +#define EFFECT_SET_TEMPO 0xF2 // Supported + +#define PERIOD_TABLE_LENGTH MAXNOTES +#define FULL_PERIOD_TABLE_LENGTH ( PERIOD_TABLE_LENGTH * 8 ) + +static const short periodtable[]= +{ + 27392, 25856, 24384, 23040, 21696, 20480, 19328, 18240, 17216, 16256, 15360, 14496, + 13696, 12928, 12192, 11520, 10848, 10240, 9664, 9120, 8606, 8128, 7680, 7248, + 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624, + 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812, + 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906, + 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, + 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, + 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, + 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56, + 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, + 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14, + 13, 13, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7 +}; + +static const short sintable[]={ + 0, 24, 49, 74, 97, 120, 141,161, + 180, 197, 212, 224, 235, 244, 250,253, + 255, 253, 250, 244, 235, 224, 212,197, + 180, 161, 141, 120, 97, 74, 49, 24 +}; + +typedef struct modtype_ +{ + unsigned char signature[5]; + int numberofchannels; +}modtype; + +modtype modlist[]= +{ + { "M!K!",4}, + { "M.K.",4}, + { "FLT4",4}, + { "FLT8",8}, + { "4CHN",4}, + { "6CHN",6}, + { "8CHN",8}, + { "10CH",10}, + { "12CH",12}, + { "14CH",14}, + { "16CH",16}, + { "18CH",18}, + { "20CH",20}, + { "22CH",22}, + { "24CH",24}, + { "26CH",26}, + { "28CH",28}, + { "30CH",30}, + { "32CH",32}, + { "",0} +}; + +/////////////////////////////////////////////////////////////////////////////////// + +static void memcopy( void * dest, void *source, unsigned long size ) +{ + unsigned long i; + unsigned char * d,*s; + + d=(unsigned char*)dest; + s=(unsigned char*)source; + for(i=0;i= mod->fullperiod[i]) + { + return i; + } + } + + return MAXNOTES; +} + +static void worknote( note * nptr, channel * cptr,char t,modcontext * mod ) +{ + (void) t; + muint sample, period, effect, operiod; + muint curnote, arpnote; + + sample = (nptr->sampperiod & 0xF0) | (nptr->sampeffect >> 4); + period = ((nptr->sampperiod & 0xF) << 8) | nptr->period; + effect = ((nptr->sampeffect & 0xF) << 8) | nptr->effect; + + operiod = cptr->period; + + if ( period || sample ) + { + if( sample && sample<32 ) + { + cptr->sampnum = sample - 1; + } + + if( period || sample ) + { + cptr->sampdata =(char *) mod->sampledata[cptr->sampnum]; + cptr->length = mod->song.samples[cptr->sampnum].length; + cptr->reppnt = mod->song.samples[cptr->sampnum].reppnt; + cptr->replen = mod->song.samples[cptr->sampnum].replen; + + cptr->finetune = (mod->song.samples[cptr->sampnum].finetune)&0xF; + + if(effect>>8!=4 && effect>>8!=6) + { + cptr->vibraperiod=0; + cptr->vibrapointeur=0; + } + } + + if( (sample != 0) && ( (effect>>8) != EFFECT_VOLSLIDE_TONEPORTA ) ) + { + cptr->volume = mod->song.samples[cptr->sampnum].volume; + cptr->volumeslide = 0; + } + + if( ( (effect>>8) != EFFECT_TONE_PORTAMENTO && (effect>>8)!=EFFECT_VOLSLIDE_TONEPORTA) ) + { + if (period!=0) + cptr->samppos = 0; + } + + cptr->decalperiod=0; + if( period ) + { + if(cptr->finetune) + { + if( cptr->finetune <= 7 ) + { + period = mod->fullperiod[getnote(mod,period,0) + cptr->finetune]; + } + else + { + period = mod->fullperiod[getnote(mod,period,0) - (16 - (cptr->finetune)) ]; + } + } + + cptr->period = period; + } + + } + + cptr->effect = 0; + cptr->parameffect = 0; + cptr->effect_code = effect; + + switch (effect >> 8) + { + case EFFECT_ARPEGGIO: + /* + [0]: Arpeggio + Where [0][x][y] means "play note, note+x semitones, note+y + semitones, then return to original note". The fluctuations are + carried out evenly spaced in one pattern division. They are usually + used to simulate chords, but this doesn't work too well. They are + also used to produce heavy vibrato. A major chord is when x=4, y=7. + A minor chord is when x=3, y=7. + */ + + if(effect&0xff) + { + cptr->effect = EFFECT_ARPEGGIO; + cptr->parameffect = effect&0xff; + + cptr->ArpIndex = 0; + + curnote = getnote(mod,cptr->period,cptr->finetune); + + cptr->Arpperiods[0] = cptr->period; + + arpnote = curnote + (((cptr->parameffect>>4)&0xF)*8); + if( arpnote >= FULL_PERIOD_TABLE_LENGTH ) + arpnote = FULL_PERIOD_TABLE_LENGTH - 1; + + cptr->Arpperiods[1] = mod->fullperiod[arpnote]; + + arpnote = curnote + (((cptr->parameffect)&0xF)*8); + if( arpnote >= FULL_PERIOD_TABLE_LENGTH ) + arpnote = FULL_PERIOD_TABLE_LENGTH - 1; + + cptr->Arpperiods[2] = mod->fullperiod[arpnote]; + } + break; + + case EFFECT_PORTAMENTO_UP: + /* + [1]: Slide up + Where [1][x][y] means "smoothly decrease the period of current + sample by x*16+y after each tick in the division". The + ticks/division are set with the 'set speed' effect (see below). If + the period of the note being played is z, then the final period + will be z - (x*16 + y)*(ticks - 1). As the slide rate depends on + the speed, changing the speed will change the slide. You cannot + slide beyond the note B3 (period 113). + */ + + cptr->effect = EFFECT_PORTAMENTO_UP; + cptr->parameffect = effect&0xff; + break; + + case EFFECT_PORTAMENTO_DOWN: + /* + [2]: Slide down + Where [2][x][y] means "smoothly increase the period of current + sample by x*16+y after each tick in the division". Similar to [1], + but lowers the pitch. You cannot slide beyond the note C1 (period + 856). + */ + + cptr->effect = EFFECT_PORTAMENTO_DOWN; + cptr->parameffect = effect&0xff; + break; + + case EFFECT_TONE_PORTAMENTO: + /* + [3]: Slide to note + Where [3][x][y] means "smoothly change the period of current sample + by x*16+y after each tick in the division, never sliding beyond + current period". The period-length in this channel's division is a + parameter to this effect, and hence is not played. Sliding to a + note is similar to effects [1] and [2], but the slide will not go + beyond the given period, and the direction is implied by that + period. If x and y are both 0, then the old slide will continue. + */ + + cptr->effect = EFFECT_TONE_PORTAMENTO; + if( (effect&0xff) != 0 ) + { + cptr->portaspeed = (short)(effect&0xff); + } + + if(period!=0) + { + cptr->portaperiod = period; + cptr->period = operiod; + } + break; + + case EFFECT_VIBRATO: + /* + [4]: Vibrato + Where [4][x][y] means "oscillate the sample pitch using a + particular waveform with amplitude y/16 semitones, such that (x * + ticks)/64 cycles occur in the division". The waveform is set using + effect [14][4]. By placing vibrato effects on consecutive + divisions, the vibrato effect can be maintained. If either x or y + are 0, then the old vibrato values will be used. + */ + + cptr->effect = EFFECT_VIBRATO; + if( ( effect & 0x0F ) != 0 ) // Depth continue or change ? + cptr->vibraparam = (cptr->vibraparam & 0xF0) | ( effect & 0x0F ); + if( ( effect & 0xF0 ) != 0 ) // Speed continue or change ? + cptr->vibraparam = (cptr->vibraparam & 0x0F) | ( effect & 0xF0 ); + + break; + + case EFFECT_VOLSLIDE_TONEPORTA: + /* + [5]: Continue 'Slide to note', but also do Volume slide + Where [5][x][y] means "either slide the volume up x*(ticks - 1) or + slide the volume down y*(ticks - 1), at the same time as continuing + the last 'Slide to note'". It is illegal for both x and y to be + non-zero. You cannot slide outside the volume range 0..64. The + period-length in this channel's division is a parameter to this + effect, and hence is not played. + */ + + if( period != 0 ) + { + cptr->portaperiod = period; + cptr->period = operiod; + } + + cptr->effect = EFFECT_VOLSLIDE_TONEPORTA; + if( ( effect & 0xFF ) != 0 ) + cptr->volumeslide = ( effect & 0xFF ); + + break; + + case EFFECT_VOLSLIDE_VIBRATO: + /* + [6]: Continue 'Vibrato', but also do Volume slide + Where [6][x][y] means "either slide the volume up x*(ticks - 1) or + slide the volume down y*(ticks - 1), at the same time as continuing + the last 'Vibrato'". It is illegal for both x and y to be non-zero. + You cannot slide outside the volume range 0..64. + */ + + cptr->effect = EFFECT_VOLSLIDE_VIBRATO; + if( (effect & 0xFF) != 0 ) + cptr->volumeslide = (effect & 0xFF); + break; + + case EFFECT_SET_OFFSET: + /* + [9]: Set sample offset + Where [9][x][y] means "play the sample from offset x*4096 + y*256". + The offset is measured in words. If no sample is given, yet one is + still playing on this channel, it should be retriggered to the new + offset using the current volume. + */ + + cptr->samppos = ((effect>>4) * 4096) + ((effect&0xF)*256); + + break; + + case EFFECT_VOLUME_SLIDE: + /* + [10]: Volume slide + Where [10][x][y] means "either slide the volume up x*(ticks - 1) or + slide the volume down y*(ticks - 1)". If both x and y are non-zero, + then the y value is ignored (assumed to be 0). You cannot slide + outside the volume range 0..64. + */ + + cptr->effect = EFFECT_VOLUME_SLIDE; + cptr->volumeslide = (effect & 0xFF); + break; + + case EFFECT_JUMP_POSITION: + /* + [11]: Position Jump + Where [11][x][y] means "stop the pattern after this division, and + continue the song at song-position x*16+y". This shifts the + 'pattern-cursor' in the pattern table (see above). Legal values for + x*16+y are from 0 to 127. + */ + + mod->tablepos = (effect & 0xFF); + if(mod->tablepos >= mod->song.length) + mod->tablepos = 0; + mod->patternpos = 0; + mod->jump_loop_effect = 1; + + break; + + case EFFECT_SET_VOLUME: + /* + [12]: Set volume + Where [12][x][y] means "set current sample's volume to x*16+y". + Legal volumes are 0..64. + */ + + cptr->volume = (effect & 0xFF); + break; + + case EFFECT_PATTERN_BREAK: + /* + [13]: Pattern Break + Where [13][x][y] means "stop the pattern after this division, and + continue the song at the next pattern at division x*10+y" (the 10 + is not a typo). Legal divisions are from 0 to 63 (note Protracker + exception above). + */ + + mod->patternpos = ( ((effect>>4)&0xF)*10 + (effect&0xF) ) * mod->number_of_channels; + mod->jump_loop_effect = 1; + mod->tablepos++; + if(mod->tablepos >= mod->song.length) + mod->tablepos = 0; + + break; + + case EFFECT_EXTENDED: + switch( (effect>>4) & 0xF ) + { + case EFFECT_E_FINE_PORTA_UP: + /* + [14][1]: Fineslide up + Where [14][1][x] means "decrement the period of the current sample + by x". The incrementing takes place at the beginning of the + division, and hence there is no actual sliding. You cannot slide + beyond the note B3 (period 113). + */ + + cptr->period -= (effect & 0xF); + if( cptr->period < 113 ) + cptr->period = 113; + break; + + case EFFECT_E_FINE_PORTA_DOWN: + /* + [14][2]: Fineslide down + Where [14][2][x] means "increment the period of the current sample + by x". Similar to [14][1] but shifts the pitch down. You cannot + slide beyond the note C1 (period 856). + */ + + cptr->period += (effect & 0xF); + if( cptr->period > 856 ) + cptr->period = 856; + break; + + case EFFECT_E_FINE_VOLSLIDE_UP: + /* + [14][10]: Fine volume slide up + Where [14][10][x] means "increment the volume of the current sample + by x". The incrementing takes place at the beginning of the + division, and hence there is no sliding. You cannot slide beyond + volume 64. + */ + + cptr->volume += (effect & 0xF); + if( cptr->volume>64 ) + cptr->volume = 64; + break; + + case EFFECT_E_FINE_VOLSLIDE_DOWN: + /* + [14][11]: Fine volume slide down + Where [14][11][x] means "decrement the volume of the current sample + by x". Similar to [14][10] but lowers volume. You cannot slide + beyond volume 0. + */ + + cptr->volume -= (effect & 0xF); + if( cptr->volume > 200 ) + cptr->volume = 0; + break; + + case EFFECT_E_PATTERN_LOOP: + /* + [14][6]: Loop pattern + Where [14][6][x] means "set the start of a loop to this division if + x is 0, otherwise after this division, jump back to the start of a + loop and play it another x times before continuing". If the start + of the loop was not set, it will default to the start of the + current pattern. Hence 'loop pattern' cannot be performed across + multiple patterns. Note that loops do not support nesting, and you + may generate an infinite loop if you try to nest 'loop pattern's. + */ + + if( effect & 0xF ) + { + if( cptr->patternloopcnt ) + { + cptr->patternloopcnt--; + if( cptr->patternloopcnt ) + { + mod->patternpos = cptr->patternloopstartpoint; + mod->jump_loop_effect = 1; + } + else + { + cptr->patternloopstartpoint = mod->patternpos ; + } + } + else + { + cptr->patternloopcnt = (effect & 0xF); + mod->patternpos = cptr->patternloopstartpoint; + mod->jump_loop_effect = 1; + } + } + else // Start point + { + cptr->patternloopstartpoint = mod->patternpos; + } + + break; + + case EFFECT_E_PATTERN_DELAY: + /* + [14][14]: Delay pattern + Where [14][14][x] means "after this division there will be a delay + equivalent to the time taken to play x divisions after which the + pattern will be resumed". The delay only relates to the + interpreting of new divisions, and all effects and previous notes + continue during delay. + */ + + mod->patterndelay = (effect & 0xF); + break; + + case EFFECT_E_NOTE_CUT: + /* + [14][12]: Cut sample + Where [14][12][x] means "after the current sample has been played + for x ticks in this division, its volume will be set to 0". This + implies that if x is 0, then you will not hear any of the sample. + If you wish to insert "silence" in a pattern, it is better to use a + "silence"-sample (see above) due to the lack of proper support for + this effect. + */ + cptr->effect = EFFECT_E_NOTE_CUT; + cptr->cut_param = (effect & 0xF); + if(!cptr->cut_param) + cptr->volume = 0; + break; + + default: + + break; + } + break; + + case 0xF: + /* + [15]: Set speed + Where [15][x][y] means "set speed to x*16+y". Though it is nowhere + near that simple. Let z = x*16+y. Depending on what values z takes, + different units of speed are set, there being two: ticks/division + and beats/minute (though this one is only a label and not strictly + true). If z=0, then what should technically happen is that the + module stops, but in practice it is treated as if z=1, because + there is already a method for stopping the module (running out of + patterns). If z<=32, then it means "set ticks/division to z" + otherwise it means "set beats/minute to z" (convention says that + this should read "If z<32.." but there are some composers out there + that defy conventions). Default values are 6 ticks/division, and + 125 beats/minute (4 divisions = 1 beat). The beats/minute tag is + only meaningful for 6 ticks/division. To get a more accurate view + of how things work, use the following formula: + 24 * beats/minute + divisions/minute = ----------------- + ticks/division + Hence divisions/minute range from 24.75 to 6120, eg. to get a value + of 2000 divisions/minute use 3 ticks/division and 250 beats/minute. + If multiple "set speed" effects are performed in a single division, + the ones on higher-numbered channels take precedence over the ones + on lower-numbered channels. This effect has a large number of + different implementations, but the one described here has the + widest usage. + */ + + if( (effect&0xFF) < 0x21 ) + { + if( effect&0xFF ) + { + mod->song.speed = effect&0xFF; + mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm))); + } + } + + if( (effect&0xFF) >= 0x21 ) + { + /// HZ = 2 * BPM / 5 + mod->bpm = effect&0xFF; + mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm))); + } + + break; + + default: + // Unsupported effect + break; + + } + +} + +static void workeffect( note * nptr, channel * cptr ) +{ + (void) nptr; + + switch(cptr->effect) + { + case EFFECT_ARPEGGIO: + + if( cptr->parameffect ) + { + cptr->decalperiod = cptr->period - cptr->Arpperiods[cptr->ArpIndex]; + + cptr->ArpIndex++; + if( cptr->ArpIndex>2 ) + cptr->ArpIndex = 0; + } + break; + + case EFFECT_PORTAMENTO_UP: + + if(cptr->period) + { + cptr->period -= cptr->parameffect; + + if( cptr->period < 113 || cptr->period > 20000 ) + cptr->period = 113; + } + + break; + + case EFFECT_PORTAMENTO_DOWN: + + if(cptr->period) + { + cptr->period += cptr->parameffect; + + if( cptr->period > 20000 ) + cptr->period = 20000; + } + + break; + + case EFFECT_VOLSLIDE_TONEPORTA: + case EFFECT_TONE_PORTAMENTO: + + if( cptr->period && ( cptr->period != cptr->portaperiod ) && cptr->portaperiod ) + { + if( cptr->period > cptr->portaperiod ) + { + if( cptr->period - cptr->portaperiod >= cptr->portaspeed ) + { + cptr->period -= cptr->portaspeed; + } + else + { + cptr->period = cptr->portaperiod; + } + } + else + { + if( cptr->portaperiod - cptr->period >= cptr->portaspeed ) + { + cptr->period += cptr->portaspeed; + } + else + { + cptr->period = cptr->portaperiod; + } + } + + if( cptr->period == cptr->portaperiod ) + { + // If the slide is over, don't let it to be retriggered. + cptr->portaperiod = 0; + } + } + + if( cptr->effect == EFFECT_VOLSLIDE_TONEPORTA ) + { + if( cptr->volumeslide > 0x0F ) + { + cptr->volume = cptr->volume + (cptr->volumeslide>>4); + + if(cptr->volume>63) + cptr->volume = 63; + } + else + { + cptr->volume = cptr->volume - (cptr->volumeslide); + + if(cptr->volume>63) + cptr->volume=0; + } + } + break; + + case EFFECT_VOLSLIDE_VIBRATO: + case EFFECT_VIBRATO: + + cptr->vibraperiod = ( (cptr->vibraparam&0xF) * sintable[cptr->vibrapointeur&0x1F] )>>7; + + if( cptr->vibrapointeur > 31 ) + cptr->vibraperiod = -cptr->vibraperiod; + + cptr->vibrapointeur = (cptr->vibrapointeur+(((cptr->vibraparam>>4))&0xf)) & 0x3F; + + if( cptr->effect == EFFECT_VOLSLIDE_VIBRATO ) + { + if( cptr->volumeslide > 0xF ) + { + cptr->volume = cptr->volume+(cptr->volumeslide>>4); + + if( cptr->volume > 64 ) + cptr->volume = 64; + } + else + { + cptr->volume = cptr->volume - cptr->volumeslide; + + if( cptr->volume > 64 ) + cptr->volume = 0; + } + } + + break; + + case EFFECT_VOLUME_SLIDE: + + if( cptr->volumeslide > 0xF ) + { + cptr->volume += (cptr->volumeslide>>4); + + if( cptr->volume > 64 ) + cptr->volume = 64; + } + else + { + cptr->volume -= (cptr->volumeslide&0xf); + + if( cptr->volume > 64 ) + cptr->volume = 0; + } + break; + + case EFFECT_E_NOTE_CUT: + if(cptr->cut_param) + cptr->cut_param--; + + if(!cptr->cut_param) + cptr->volume = 0; + break; + + default: + break; + + } + +} + +/////////////////////////////////////////////////////////////////////////////////// +int hxcmod_init(modcontext * modctx) +{ + muint i,j; + + if( modctx ) + { + memclear(modctx,0,sizeof(modcontext)); + modctx->playrate = 44100; + modctx->stereo = 1; + modctx->stereo_separation = 1; + modctx->bits = 16; + modctx->filter = 1; + + for(i=0;ifullperiod[(i*8) + j] = periodtable[i] - ((( periodtable[i] - periodtable[i+1] ) / 8) * j); + } + } + + return 1; + } + + return 0; +} + +int hxcmod_setcfg(modcontext * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter) +{ + if( modctx ) + { + modctx->playrate = samplerate; + + if( stereo ) + modctx->stereo = 1; + else + modctx->stereo = 0; + + if(stereo_separation < 4) + { + modctx->stereo_separation = stereo_separation; + } + + if( bits == 8 || bits == 16 ) + { + modctx->bits = bits; + } + + if( filter ) + modctx->filter = 1; + else + modctx->filter = 0; + + return 1; + } + + return 0; +} + +int hxcmod_load( modcontext * modctx, void * mod_data, int mod_data_size ) +{ + muint i, max; + unsigned short t; + sample *sptr; + unsigned char * modmemory,* endmodmemory; + + modmemory = (unsigned char *)mod_data; + endmodmemory = modmemory + mod_data_size; + + if(modmemory) + { + if( modctx ) + { + memcopy(&(modctx->song.title),modmemory,1084); + + i = 0; + modctx->number_of_channels = 0; + while(modlist[i].numberofchannels) + { + if(memcompare(modctx->song.signature,modlist[i].signature,4)) + { + modctx->number_of_channels = modlist[i].numberofchannels; + } + + i++; + } + + if( !modctx->number_of_channels ) + { + // 15 Samples modules support + // Shift the whole datas to make it look likes a standard 4 channels mod. + memcopy(&(modctx->song.signature), "M.K.", 4); + memcopy(&(modctx->song.length), &(modctx->song.samples[15]), 130); + memclear(&(modctx->song.samples[15]), 0, 480); + modmemory += 600; + modctx->number_of_channels = 4; + } + else + { + modmemory += 1084; + } + + if( modmemory >= endmodmemory ) + return 0; // End passed ? - Probably a bad file ! + + // Patterns loading + for (i = max = 0; i < 128; i++) + { + while (max <= modctx->song.patterntable[i]) + { + modctx->patterndata[max] = (note*)modmemory; + modmemory += (256*modctx->number_of_channels); + max++; + + if( modmemory >= endmodmemory ) + return 0; // End passed ? - Probably a bad file ! + } + } + + for (i = 0; i < 31; i++) + modctx->sampledata[i]=0; + + // Samples loading + for (i = 0, sptr = modctx->song.samples; i <31; i++, sptr++) + { + t= (sptr->length &0xFF00)>>8 | (sptr->length &0xFF)<<8; + sptr->length = t*2; + + t= (sptr->reppnt &0xFF00)>>8 | (sptr->reppnt &0xFF)<<8; + sptr->reppnt = t*2; + + t= (sptr->replen &0xFF00)>>8 | (sptr->replen &0xFF)<<8; + sptr->replen = t*2; + + + if (sptr->length == 0) continue; + + modctx->sampledata[i] = (char*)modmemory; + modmemory += sptr->length; + + if (sptr->replen + sptr->reppnt > sptr->length) + sptr->replen = sptr->length - sptr->reppnt; + + if( modmemory > endmodmemory ) + return 0; // End passed ? - Probably a bad file ! + } + + // States init + + modctx->tablepos = 0; + modctx->patternpos = 0; + modctx->song.speed = 6; + modctx->bpm = 125; + modctx->samplenb = 0; + + modctx->patternticks = (((long)modctx->song.speed * modctx->playrate * 5)/ (2 * modctx->bpm)) + 1; + modctx->patternticksaim = ((long)modctx->song.speed * modctx->playrate * 5) / (2 * modctx->bpm); + + modctx->sampleticksconst = 3546894UL / modctx->playrate; //8448*428/playrate; + + for(i=0; i < modctx->number_of_channels; i++) + { + modctx->channels[i].volume = 0; + modctx->channels[i].period = 0; + } + + modctx->mod_loaded = 1; + + return 1; + } + } + + return 0; +} + +void hxcmod_fillbuffer( modcontext * modctx, unsigned short * outbuffer, unsigned long nbsample, tracker_buffer_state * trkbuf ) +{ + unsigned long i, j; + unsigned long k; + unsigned char c; + unsigned int state_remaining_steps; + int l,r; + int ll,lr; + int tl,tr; + short finalperiod; + note *nptr; + channel *cptr; + + if( modctx && outbuffer ) + { + if(modctx->mod_loaded) + { + state_remaining_steps = 0; + + if( trkbuf ) + { + trkbuf->cur_rd_index = 0; + + memcopy(trkbuf->name,modctx->song.title,sizeof(modctx->song.title)); + + for(i=0;i<31;i++) + { + memcopy(trkbuf->instruments[i].name,modctx->song.samples[i].name,sizeof(trkbuf->instruments[i].name)); + } + } + + ll = modctx->last_l_sample; + lr = modctx->last_r_sample; + + for (i = 0; i < nbsample; i++) + { + //--------------------------------------- + if( modctx->patternticks++ > modctx->patternticksaim ) + { + if( !modctx->patterndelay ) + { + nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]]; + nptr = nptr + modctx->patternpos; + cptr = modctx->channels; + + modctx->patternticks = 0; + modctx->patterntickse = 0; + + for(c=0;cnumber_of_channels;c++) + { + worknote((note*)(nptr+c), (channel*)(cptr+c),(char)(c+1),modctx); + } + + if( !modctx->jump_loop_effect ) + modctx->patternpos += modctx->number_of_channels; + else + modctx->jump_loop_effect = 0; + + if( modctx->patternpos == 64*modctx->number_of_channels ) + { + modctx->tablepos++; + modctx->patternpos = 0; + if(modctx->tablepos >= modctx->song.length) + modctx->tablepos = 0; + } + } + else + { + modctx->patterndelay--; + modctx->patternticks = 0; + modctx->patterntickse = 0; + } + + } + + if( modctx->patterntickse++ > (modctx->patternticksaim/modctx->song.speed) ) + { + nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]]; + nptr = nptr + modctx->patternpos; + cptr = modctx->channels; + + for(c=0;cnumber_of_channels;c++) + { + workeffect(nptr+c, cptr+c); + } + + modctx->patterntickse = 0; + } + + //--------------------------------------- + + if( trkbuf && !state_remaining_steps ) + { + if( trkbuf->nb_of_state < trkbuf->nb_max_of_state ) + { + memclear(&trkbuf->track_state_buf[trkbuf->nb_of_state],0,sizeof(tracker_state)); + } + } + + l=0; + r=0; + + for(j =0, cptr = modctx->channels; j < modctx->number_of_channels ; j++, cptr++) + { + if( cptr->period != 0 ) + { + finalperiod = cptr->period - cptr->decalperiod - cptr->vibraperiod; + if( finalperiod ) + { + cptr->samppos += ( (modctx->sampleticksconst<<10) / finalperiod ); + } + + cptr->ticks++; + + if( cptr->replen<=2 ) + { + if( (cptr->samppos>>10) >= (cptr->length) ) + { + cptr->length = 0; + cptr->reppnt = 0; + + if( cptr->length ) + cptr->samppos = cptr->samppos % (((unsigned long)cptr->length)<<10); + else + cptr->samppos = 0; + } + } + else + { + if( (cptr->samppos>>10) >= (unsigned long)(cptr->replen+cptr->reppnt) ) + { + cptr->samppos = ((unsigned long)(cptr->reppnt)<<10) + (cptr->samppos % ((unsigned long)(cptr->replen+cptr->reppnt)<<10)); + } + } + + k = cptr->samppos >> 10; + + if( cptr->sampdata!=0 && ( ((j&3)==1) || ((j&3)==2) ) ) + { + r += ( cptr->sampdata[k] * cptr->volume ); + } + + if( cptr->sampdata!=0 && ( ((j&3)==0) || ((j&3)==3) ) ) + { + l += ( cptr->sampdata[k] * cptr->volume ); + } + + if( trkbuf && !state_remaining_steps ) + { + if( trkbuf->nb_of_state < trkbuf->nb_max_of_state ) + { + trkbuf->track_state_buf[trkbuf->nb_of_state].number_of_tracks = modctx->number_of_channels; + trkbuf->track_state_buf[trkbuf->nb_of_state].buf_index = i; + trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern = modctx->song.patterntable[modctx->tablepos]; + trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_pos = modctx->patternpos / modctx->number_of_channels; + trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_table_pos = modctx->tablepos; + trkbuf->track_state_buf[trkbuf->nb_of_state].bpm = modctx->bpm; + trkbuf->track_state_buf[trkbuf->nb_of_state].speed = modctx->song.speed; + trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_effect = cptr->effect_code; + trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_parameffect = cptr->parameffect; + trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_period = finalperiod; + trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_volume = cptr->volume; + trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].instrument_number = (unsigned char)cptr->sampnum; + } + } + } + } + + if( trkbuf && !state_remaining_steps ) + { + state_remaining_steps = trkbuf->sample_step; + + if(trkbuf->nb_of_state < trkbuf->nb_max_of_state) + trkbuf->nb_of_state++; + } + else + { + state_remaining_steps--; + } + + tl = (short)l; + tr = (short)r; + + if ( modctx->filter ) + { + // Filter + l = (l+ll)>>1; + r = (r+lr)>>1; + } + + if ( modctx->stereo_separation == 1 ) + { + // Left & Right Stereo panning + l = (l+(r>>1)); + r = (r+(l>>1)); + } + + // Level limitation + if( l > 32767 ) l = 32767; + if( l < -32768 ) l = -32768; + if( r > 32767 ) r = 32767; + if( r < -32768 ) r = -32768; + + // Store the final sample. + outbuffer[(i*2)] = l; + outbuffer[(i*2)+1] = r; + + ll = tl; + lr = tr; + + } + + modctx->last_l_sample = ll; + modctx->last_r_sample = lr; + + modctx->samplenb = modctx->samplenb+nbsample; + } + else + { + for (i = 0; i < nbsample; i++) + { + // Mod not loaded. Return blank buffer. + outbuffer[(i*2)] = 0; + outbuffer[(i*2)+1] = 0; + } + + if(trkbuf) + { + trkbuf->nb_of_state = 0; + trkbuf->cur_rd_index = 0; + trkbuf->name[0] = 0; + memclear(trkbuf->track_state_buf,0,sizeof(tracker_state) * trkbuf->nb_max_of_state); + memclear(trkbuf->instruments,0,sizeof(trkbuf->instruments)); + } + } + } +} + +void hxcmod_unload( modcontext * modctx ) +{ + if(modctx) + { + memclear(&modctx->song,0,sizeof(modctx->song)); + memclear(&modctx->sampledata,0,sizeof(modctx->sampledata)); + memclear(&modctx->patterndata,0,sizeof(modctx->patterndata)); + modctx->tablepos = 0; + modctx->patternpos = 0; + modctx->patterndelay = 0; + modctx->jump_loop_effect = 0; + modctx->bpm = 0; + modctx->patternticks = 0; + modctx->patterntickse = 0; + modctx->patternticksaim = 0; + modctx->sampleticksconst = 0; + + modctx->samplenb = 0; + + memclear(modctx->channels,0,sizeof(modctx->channels)); + + modctx->number_of_channels = 0; + + modctx->mod_loaded = 0; + + modctx->last_r_sample = 0; + modctx->last_l_sample = 0; + } +} diff --git a/3rd-party/hxcmod-player/hxcmod.h b/3rd-party/hxcmod-player/hxcmod.h new file mode 100644 index 000000000..4240ab7a4 --- /dev/null +++ b/3rd-party/hxcmod-player/hxcmod.h @@ -0,0 +1,215 @@ +/////////////////////////////////////////////////////////////////////////////////// +//-------------------------------------------------------------------------------// +//-------------------------------------------------------------------------------// +//-----------H----H--X----X-----CCCCC----22222----0000-----0000------11----------// +//----------H----H----X-X-----C--------------2---0----0---0----0--1--1-----------// +//---------HHHHHH-----X------C----------22222---0----0---0----0-----1------------// +//--------H----H----X--X----C----------2-------0----0---0----0-----1-------------// +//-------H----H---X-----X---CCCCC-----222222----0000-----0000----1111------------// +//-------------------------------------------------------------------------------// +//----------------------------------------------------- http://hxc2001.free.fr --// +/////////////////////////////////////////////////////////////////////////////////// +// File : hxcmod.h +// Contains: a tiny mod player +// +// Written by: Jean François DEL NERO +// +// Change History (most recent first): +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef MODPLAY_DEF +#define MODPLAY_DEF + +// Basic type +typedef unsigned char muchar; +typedef unsigned short muint; +typedef short mint; +typedef unsigned long mulong; + +#define NUMMAXCHANNELS 32 +#define MAXNOTES 12*12 +#define SAMPLE_RATE 44100 +// +// MOD file structures +// + +#pragma pack(1) + +typedef struct { + muchar name[22]; + muint length; + muchar finetune; + muchar volume; + muint reppnt; + muint replen; +} sample; + +typedef struct { + muchar sampperiod; + muchar period; + muchar sampeffect; + muchar effect; +} note; + +typedef struct { + muchar title[20]; + sample samples[31]; + muchar length; + muchar protracker; + muchar patterntable[128]; + muchar signature[4]; + muchar speed; +} module; + +#pragma pack() + +// +// HxCMod Internal structures +// +typedef struct { + char * sampdata; + muint sampnum; + muint length; + muint reppnt; + muint replen; + mulong samppos; + muint period; + muchar volume; + mulong ticks; + muchar effect; + muchar parameffect; + muint effect_code; + + + mint decalperiod; + mint portaspeed; + mint portaperiod; + mint vibraperiod; + mint Arpperiods[3]; + muchar ArpIndex; + + mint oldk; + muchar volumeslide; + + muchar vibraparam; + muchar vibrapointeur; + + muchar finetune; + + muchar cut_param; + + muint patternloopcnt; + muint patternloopstartpoint; +} channel; + +typedef struct { + module song; + char * sampledata[31]; + note * patterndata[128]; + + mulong playrate; + muint tablepos; + muint patternpos; + muint patterndelay; + muint jump_loop_effect; + muchar bpm; + mulong patternticks; + mulong patterntickse; + mulong patternticksaim; + mulong sampleticksconst; + + mulong samplenb; + + channel channels[NUMMAXCHANNELS]; + + muint number_of_channels; + + muint fullperiod[MAXNOTES * 8]; + + muint mod_loaded; + + mint last_r_sample; + mint last_l_sample; + + mint stereo; + mint stereo_separation; + mint bits; + mint filter; + +} modcontext; + +// +// Player states structures +// +typedef struct track_state_ +{ + unsigned char instrument_number; + unsigned short cur_period; + unsigned char cur_volume; + unsigned short cur_effect; + unsigned short cur_parameffect; +}track_state; + +typedef struct tracker_state_ +{ + int number_of_tracks; + int bpm; + int speed; + int cur_pattern; + int cur_pattern_pos; + int cur_pattern_table_pos; + unsigned int buf_index; + track_state tracks[32]; +}tracker_state; + +typedef struct tracker_state_instrument_ +{ + char name[22]; + int active; +}tracker_state_instrument; + +typedef struct tracker_buffer_state_ +{ + int nb_max_of_state; + int nb_of_state; + int cur_rd_index; + int sample_step; + char name[64]; + tracker_state_instrument instruments[31]; + tracker_state * track_state_buf; +}tracker_buffer_state; + +/////////////////////////////////////////////////////////////////////////////////// +// HxCMOD Core API: +// ------------------------------------------- +// int hxcmod_init(modcontext * modctx) +// +// - Initialize the modcontext buffer. Must be called before doing anything else. +// Return 1 if success. 0 in case of error. +// ------------------------------------------- +// int hxcmod_load( modcontext * modctx, void * mod_data, int mod_data_size ) +// +// - "Load" a MOD from memory (from "mod_data" with size "mod_data_size"). +// Return 1 if success. 0 in case of error. +// ------------------------------------------- +// void hxcmod_fillbuffer( modcontext * modctx, unsigned short * outbuffer, unsigned long nbsample, tracker_buffer_state * trkbuf ) +// +// - Generate and return the next samples chunk to outbuffer. +// nbsample specify the number of stereo 16bits samples you want. +// The output format is signed 44100Hz 16-bit Stereo PCM samples. +// The output buffer size in byte must be equal to ( nbsample * 2 * 2 ). +// The optional trkbuf parameter can be used to get detailed status of the player. Put NULL/0 is unused. +// ------------------------------------------- +// void hxcmod_unload( modcontext * modctx ) +// - "Unload" / clear the player status. +// ------------------------------------------- +/////////////////////////////////////////////////////////////////////////////////// + +int hxcmod_init( modcontext * modctx ); +int hxcmod_setcfg( modcontext * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter); +int hxcmod_load( modcontext * modctx, void * mod_data, int mod_data_size ); +void hxcmod_fillbuffer( modcontext * modctx, unsigned short * outbuffer, unsigned long nbsample, tracker_buffer_state * trkbuf ); +void hxcmod_unload( modcontext * modctx ); + +#endif + diff --git a/3rd-party/hxcmod-player/mods/mod.h b/3rd-party/hxcmod-player/mods/mod.h new file mode 100644 index 000000000..b03fa39b6 --- /dev/null +++ b/3rd-party/hxcmod-player/mods/mod.h @@ -0,0 +1,3 @@ +extern unsigned char mod_data[]; +extern unsigned int mod_len; +extern const char * mod_name; \ No newline at end of file diff --git a/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c b/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c new file mode 100644 index 000000000..d0ae20f7b --- /dev/null +++ b/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c @@ -0,0 +1,641 @@ +#include "mod.h" + +unsigned char mod_data[] = { + 0x64, 0x65, 0x63, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, + 0x64, 0x69, 0x73, 0x65, 0x61, 0x73, 0x65, 0x00, 0x62, 0x79, 0x20, 0x6e, + 0x61, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x40, 0x00, 0x08, + 0x00, 0x40, 0x74, 0x61, 0x6e, 0x78, 0x7a, 0x20, 0x74, 0x6f, 0x20, 0x61, + 0x73, 0x74, 0x72, 0x6f, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x00, + 0x02, 0x22, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x6c, 0x2d, + 0x74, 0x72, 0x69, 0x63, 0x6b, 0x20, 0x26, 0x20, 0x64, 0x72, 0x75, 0x6d, + 0x20, 0x73, 0x6d, 0x70, 0x73, 0x00, 0x00, 0x48, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x69, 0x27, 0x64, 0x20, 0x74, 0x65, 0x6c, 0x6c, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x79, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x61, 0x00, + 0x00, 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x08, 0x63, 0x6f, 0x6f, 0x6c, + 0x20, 0x64, 0x75, 0x64, 0x65, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x20, 0x79, + 0x6f, 0x75, 0x20, 0x61, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x6b, 0x6e, 0x6f, 0x77, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x21, 0x20, 0x68, 0x65, 0x68, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x6f, 0x20, 0x6d, + 0x79, 0x20, 0x70, 0x61, 0x6c, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x66, 0x72, + 0x65, 0x65, 0x73, 0x74, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3a, 0x20, 0x77, 0x68, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x66, 0x23, 0x63, 0x6b, 0x20, 0x77, 0x61, 0x73, 0x6e, 0x27, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x79, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6c, 0x69, 0x73, 0x74, 0x3f, 0x21, 0x3f, 0x20, 0x69, 0x74, + 0x27, 0x73, 0x20, 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x3a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x79, 0x6f, 0x75, 0x20, 0x69, 0x6c, 0x6c, 0x69, 0x74, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x20, 0x73, 0x68, 0x69, 0x74, 0x73, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x6f, 0x20, 0x6d, + 0x79, 0x20, 0x70, 0x61, 0x6c, 0x20, 0x6d, 0x69, 0x63, 0x6b, 0x2d, 0x65, + 0x79, 0x3a, 0x6d, 0x69, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x62, 0x65, 0x74, 0x74, 0x65, 0x72, 0x20, 0x77, 0x61, 0x69, + 0x74, 0x20, 0x75, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64, 0x61, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x38, 0x30, 0x30, + 0x20, 0x64, 0x75, 0x64, 0x6f, 0x21, 0x20, 0x63, 0x79, 0x61, 0x20, 0x6c, + 0x38, 0x65, 0x72, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x67, 0x72, 0x65, 0x65, 0x74, 0x73, 0x20, 0x66, 0x6c, 0x65, + 0x63, 0x6b, 0x65, 0x72, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x79, 0x20, 0x66, + 0x61, 0x74, 0x20, 0x61, 0x73, 0x73, 0x20, 0x28, 0x72, 0x61, 0x63, 0x65, + 0x6b, 0x61, 0x6b, 0x29, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6d, 0x69, 0x71, 0x2c, 0x70, 0x61, 0x74, 0x2c, 0x73, 0x6f, + 0x62, 0x2c, 0x61, 0x73, 0x74, 0x72, 0x6f, 0x2c, 0x6b, 0x68, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x63, 0x6c, 0x61, + 0x78, 0x6f, 0x6e, 0x28, 0x66, 0x75, 0x6e, 0x6e, 0x79, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x29, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x73, 0x79, 0x6e, 0x63, 0x2c, 0x74, 0x63, 0x62, 0x2c, 0x74, + 0x73, 0x6c, 0x2c, 0x72, 0x65, 0x62, 0x65, 0x6c, 0x73, 0x2c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x6a, 0x69, 0x6d, + 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x6e, 0x75, + 0x72, 0x73, 0x65, 0x72, 0x79, 0x20, 0x20, 0x73, 0x79, 0x6e, 0x64, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x03, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x2e, 0x4b, 0x2e, 0x02, 0x3a, 0x3e, 0x94, 0x01, 0xe0, 0x46, 0x05, + 0x02, 0xd0, 0x4c, 0x06, 0x01, 0xac, 0x14, 0x00, 0x00, 0x71, 0x20, 0x00, + 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0x80, 0x46, 0x05, + 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, + 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, + 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x20, 0x02, 0x3a, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x93, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x14, 0x00, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, + 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x46, 0x05, + 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x3f, 0x08, 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x68, 0x14, 0x00, 0x00, 0x71, 0x20, 0x00, 0x02, 0x3a, 0x46, 0x05, + 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, + 0x01, 0x68, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, 0x02, 0x3a, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xb4, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x68, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xb4, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, + 0x00, 0xb4, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x20, 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xb4, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x93, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xb4, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x14, 0x00, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0x80, 0x46, 0x05, + 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, + 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0x3a, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x00, 0x00, 0x2c, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x1e, 0xff, + 0x01, 0xac, 0x2e, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, + 0x00, 0x00, 0x04, 0x81, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x82, + 0x00, 0x00, 0x0f, 0x08, 0x01, 0xe0, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x01, 0xc5, 0x16, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x3a, 0x3e, 0x94, 0x02, 0xd0, 0x46, 0x05, 0x00, 0x00, 0x4c, 0x06, + 0x01, 0xac, 0x14, 0x00, 0x00, 0x71, 0x20, 0x00, 0x02, 0x80, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0xfa, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, 0x03, 0x58, 0x46, 0x05, + 0x02, 0xfa, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x01, 0xac, 0x16, 0x05, 0x03, 0x58, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0xd0, 0x46, 0x05, 0x03, 0x58, 0x0e, 0xd3, + 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x00, 0xd6, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0xfa, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x00, 0xd6, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x03, 0x58, 0x46, 0x05, + 0x02, 0xfa, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x80, 0x46, 0x05, 0x03, 0x58, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x20, 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xd6, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0xac, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x93, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xd6, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x14, 0x00, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, + 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x01, 0xfc, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, + 0x01, 0xe0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x3a, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0x80, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0x80, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0x3a, 0x46, 0x05, + 0x02, 0xd0, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x3f, 0x08, + 0x01, 0x68, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x68, 0x14, 0x00, + 0x00, 0x71, 0x20, 0x00, 0x01, 0x7d, 0x46, 0x05, 0x01, 0x68, 0x0e, 0xd3, + 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x01, 0xac, 0x46, 0x05, + 0x01, 0x7d, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x01, 0x7d, 0x46, 0x05, 0x01, 0xac, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x94, 0x01, 0xac, 0x46, 0x05, 0x01, 0x7d, 0x0e, 0xd3, + 0x00, 0xb4, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, + 0x01, 0xac, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x01, 0xac, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x01, 0xe0, 0x46, 0x05, 0x01, 0xac, 0x0e, 0xd3, + 0x00, 0xb4, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xfc, 0x46, 0x05, + 0x01, 0xe0, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x01, 0xe0, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, 0x00, 0xb4, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, + 0x01, 0x68, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, + 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x20, + 0x02, 0x80, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x00, 0xb4, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x01, 0x68, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x68, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x93, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xb4, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x40, 0x14, 0x00, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, + 0x01, 0xe0, 0x0e, 0xd3, 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x01, 0xfc, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xd0, 0x46, 0x05, 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x80, 0x0e, 0xd3, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x01, 0xe0, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x3a, 0x46, 0x05, 0x01, 0xe0, 0x0e, 0xd3, + 0x00, 0xa0, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x80, 0x46, 0x05, 0x01, 0xfc, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0x3a, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0xd0, 0x46, 0x05, + 0x02, 0x3a, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0x80, 0x46, 0x05, 0x02, 0xd0, 0x0e, 0xd3, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x94, 0x02, 0xd0, 0x46, 0x05, 0x02, 0x80, 0x0e, 0xd3, + 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x3e, 0x94, 0x03, 0x58, 0x41, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x7d, 0x14, 0x00, 0x00, 0x71, 0x20, 0x00, + 0x02, 0xfa, 0x44, 0x82, 0x00, 0x00, 0x4c, 0x10, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, 0x03, 0x58, 0x01, 0x0e, + 0x01, 0x7d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x01, + 0x02, 0xfa, 0x04, 0x82, 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, + 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x06, 0x01, + 0x01, 0x7d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x06, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x06, 0x01, 0x00, 0xbe, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x06, 0x10, + 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x10, 0x01, 0x7d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x10, + 0x01, 0x7d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x20, 0x01, 0xfc, 0x43, 0x22, + 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x04, 0x00, 0x01, 0x7d, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, 0x01, 0xfc, 0x03, 0x22, + 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x93, 0x00, 0x00, 0x06, 0x03, + 0x00, 0x00, 0x03, 0x22, 0x00, 0xbe, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0x3a, 0x43, 0x22, 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x14, 0x00, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x02, 0x3a, 0x03, 0x22, 0x01, 0x1d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x22, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0xa6, 0x43, 0x20, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x03, 0x58, 0x43, 0x20, 0x02, 0x80, 0x03, 0x20, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0xfa, 0x43, 0x20, 0x02, 0xa6, 0x03, 0x20, + 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x03, 0x58, 0x03, 0x20, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x02, 0xfa, 0x03, 0x20, 0x00, 0x8f, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x1d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x02, 0xfa, 0x43, 0x20, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, + 0x02, 0xa6, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, + 0x02, 0x3a, 0x3f, 0x08, 0x02, 0x80, 0x43, 0x20, 0x02, 0xfa, 0x03, 0x20, + 0x01, 0x40, 0x14, 0x00, 0x00, 0x71, 0x20, 0x00, 0x00, 0x00, 0x06, 0x02, + 0x02, 0xa6, 0x03, 0x20, 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x01, 0xfc, 0x43, 0x20, 0x02, 0x80, 0x03, 0x20, 0x01, 0x40, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, 0x02, 0x80, 0x43, 0x20, + 0x01, 0xfc, 0x03, 0x20, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x04, 0x00, 0x01, 0x40, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0xa6, 0x43, 0x20, 0x02, 0x80, 0x03, 0x20, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x06, 0x01, 0x02, 0xa6, 0x03, 0x20, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0xa0, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x02, 0x3a, 0x43, 0x20, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x02, 0x80, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, 0x01, 0x40, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x20, 0x02, 0xfa, 0x43, 0x20, 0x02, 0x3a, 0x03, 0x20, + 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0xa6, 0x43, 0x20, + 0x02, 0x80, 0x03, 0x20, 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0xfa, 0x43, 0x20, 0x02, 0xfa, 0x03, 0x20, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x93, 0x02, 0xa6, 0x43, 0x20, 0x02, 0xa6, 0x03, 0x20, + 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x02, 0x80, 0x43, 0x20, + 0x02, 0xfa, 0x03, 0x20, 0x01, 0x1d, 0x14, 0x00, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x02, 0xa6, 0x03, 0x20, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, 0x02, 0x80, 0x03, 0x20, + 0x01, 0x1d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x1d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x01, 0xfc, 0x43, 0x08, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x03, 0x08, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x00, 0x00, 0x03, 0x10, 0x01, 0xfc, 0x03, 0x08, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x03, 0x08, + 0x01, 0x1d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x03, 0x10, 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x10, 0x01, 0x1d, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x3e, 0x94, + 0x03, 0x58, 0x41, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7d, 0x14, 0x00, + 0x00, 0x71, 0x20, 0x00, 0x02, 0xfa, 0x44, 0x82, 0x00, 0x00, 0x4c, 0x10, + 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x03, 0x58, 0x01, 0x0e, 0x01, 0x7d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x06, 0x01, 0x02, 0xfa, 0x04, 0x82, 0x01, 0x7d, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x94, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x04, 0x00, + 0x00, 0xbe, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x01, + 0x00, 0x00, 0x06, 0x01, 0x01, 0x7d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x04, 0x00, 0x01, 0x7d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x00, 0x06, 0x01, + 0x00, 0xbe, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x10, + 0x00, 0x00, 0x06, 0x10, 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x10, + 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x0c, 0x10, 0x01, 0x7d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x20, + 0x01, 0xfc, 0x43, 0x22, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x03, 0x22, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x7d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x01, 0xfc, 0x03, 0x22, 0x01, 0x7d, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x93, + 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x22, 0x00, 0xbe, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x02, 0x3a, 0x43, 0x22, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x1d, 0x14, 0x00, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x03, 0x22, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x00, 0x00, 0x04, 0x00, 0x02, 0x3a, 0x03, 0x22, 0x01, 0x1d, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x22, + 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x43, 0x20, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xa6, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x03, 0x58, 0x43, 0x20, 0x02, 0x80, 0x03, 0x20, + 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x02, 0xfa, 0x43, 0x20, + 0x02, 0xa6, 0x03, 0x20, 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x01, 0xfc, 0x43, 0x30, 0x03, 0x58, 0x03, 0x20, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x02, 0xfa, 0x03, 0x20, + 0x00, 0x8f, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x01, 0xfc, 0x03, 0x30, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x03, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0xfa, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2e, 0x94, 0x02, 0xa6, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x3f, 0x08, 0x02, 0x80, 0x43, 0x20, + 0x02, 0xfa, 0x03, 0x20, 0x01, 0x40, 0x14, 0x00, 0x00, 0x71, 0x20, 0x00, + 0x00, 0x00, 0x06, 0x02, 0x02, 0xa6, 0x03, 0x20, 0x00, 0x00, 0x06, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x01, 0xac, 0x43, 0x20, 0x02, 0x80, 0x03, 0x20, + 0x01, 0x40, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x02, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x94, + 0x01, 0x7d, 0x43, 0x20, 0x01, 0xac, 0x03, 0x20, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x01, 0xfc, 0x43, 0x20, + 0x01, 0x7d, 0x03, 0x20, 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xa0, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0xfc, 0x03, 0x20, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x06, 0x01, + 0x00, 0x00, 0x04, 0x00, 0x00, 0xa0, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x02, 0x3a, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, 0x01, 0x40, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x02, 0x80, 0x43, 0x20, 0x00, 0x00, 0x04, 0x00, + 0x01, 0x40, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x20, 0x02, 0xfa, 0x43, 0x20, + 0x02, 0x3a, 0x03, 0x20, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0xa6, 0x43, 0x20, 0x02, 0x80, 0x03, 0x20, 0x01, 0x40, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x02, 0xfa, 0x43, 0x20, 0x02, 0xfa, 0x03, 0x20, + 0x01, 0x40, 0x16, 0x0a, 0x00, 0x71, 0x2e, 0x93, 0x02, 0xa6, 0x43, 0x20, + 0x02, 0xa6, 0x03, 0x20, 0x00, 0xa0, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x02, 0x3a, 0x43, 0x20, 0x02, 0xfa, 0x03, 0x20, 0x01, 0x1d, 0x14, 0x00, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x02, 0xa6, 0x03, 0x20, + 0x00, 0x00, 0x06, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x02, 0x3a, 0x03, 0x20, 0x01, 0x1d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, + 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x06, 0x0a, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x8f, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, 0x01, 0xfc, 0x43, 0x08, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, + 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, + 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x03, 0x10, 0x01, 0xfc, 0x03, 0x08, + 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2c, 0x10, 0x00, 0x00, 0x03, 0x10, + 0x00, 0x00, 0x03, 0x08, 0x01, 0x1d, 0x16, 0x0a, 0x02, 0x3a, 0x30, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x8f, 0x16, 0x0a, + 0x02, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x10, + 0x01, 0x1d, 0x16, 0x0a, 0x01, 0xac, 0x2c, 0x40, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x01, 0x1d, 0x16, 0x0a, 0x00, 0x71, 0x2b, 0x01, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8f, 0x16, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0xc4, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0x00, 0x00, 0x66, 0x4e, 0x66, 0x56, 0x48, 0x2a, 0x80, 0x1c, 0xff, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x8d, 0xac, 0xf8, 0x2f, 0x7f, 0x7e, 0x7e, 0x7e, + 0x7e, 0x7e, 0x5d, 0xbe, 0x72, 0xf6, 0x02, 0x80, 0xda, 0xbb, 0x9f, 0x5a, + 0x60, 0x3d, 0x2b, 0x3b, 0x2c, 0x1d, 0x0b, 0xc3, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x88, 0x98, 0xc8, 0xd5, 0xcc, 0x1a, 0xc9, 0x0d, 0xe3, 0x00, + 0x3e, 0x7f, 0x7f, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x67, 0x56, + 0x07, 0x47, 0x06, 0xda, 0xfb, 0xc8, 0xa9, 0x80, 0x99, 0x80, 0x80, 0x83, + 0x82, 0x80, 0x80, 0x82, 0x88, 0xaa, 0xb3, 0xc7, 0xf0, 0x2a, 0x43, 0x4e, + 0x7f, 0x41, 0x7f, 0x22, 0x7f, 0x7e, 0x7e, 0x68, 0x6c, 0x7f, 0x71, 0x11, + 0x1e, 0x2e, 0x05, 0x21, 0x15, 0x07, 0x24, 0x1e, 0x38, 0x18, 0xfc, 0xdd, + 0xc6, 0xd2, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xa1, 0x80, 0x8a, + 0xcb, 0xad, 0xda, 0xde, 0xdf, 0x0a, 0x18, 0x19, 0x7e, 0x5b, 0x7f, 0x71, + 0x7f, 0x7e, 0x7e, 0x7e, 0x7e, 0x64, 0x61, 0x20, 0x60, 0x13, 0x1d, 0x0e, + 0xe4, 0xb8, 0xac, 0xb2, 0x80, 0xab, 0x80, 0x85, 0x80, 0x80, 0xae, 0xcd, + 0xba, 0xfa, 0xc8, 0xce, 0xd3, 0x46, 0xe7, 0x0a, 0xe7, 0xee, 0xfe, 0x10, + 0xf7, 0x4e, 0x49, 0x5d, 0x68, 0x51, 0x7f, 0x6c, 0x7f, 0x7e, 0x7e, 0x62, + 0x33, 0x40, 0x4c, 0x12, 0x2a, 0x05, 0x17, 0xeb, 0xf9, 0xc4, 0x80, 0x9f, + 0xa7, 0x80, 0x80, 0xee, 0x91, 0x9a, 0xa3, 0xd4, 0x83, 0xac, 0xca, 0xdf, + 0xbc, 0xb2, 0xe3, 0xd9, 0xfc, 0x50, 0x09, 0x54, 0x4e, 0x7f, 0x62, 0x7f, + 0x62, 0x7f, 0x7e, 0x7e, 0x4c, 0x65, 0x5d, 0x35, 0x2b, 0xff, 0x00, 0xec, + 0xd1, 0xc3, 0xf1, 0xdf, 0xd1, 0x89, 0xbd, 0xb7, 0x93, 0x8f, 0xe8, 0x80, + 0xd2, 0xca, 0xb0, 0xe0, 0xc6, 0x07, 0xd0, 0x01, 0x36, 0x28, 0x29, 0x62, + 0x47, 0x3e, 0x65, 0x3a, 0x04, 0x52, 0x03, 0x3f, 0x34, 0x1f, 0x32, 0x58, + 0x1b, 0xfd, 0x43, 0x0c, 0x2f, 0x09, 0xfe, 0x1d, 0xea, 0xfd, 0x07, 0xb2, + 0x2d, 0xad, 0xb3, 0xa2, 0xc5, 0xa5, 0xab, 0xa5, 0xb2, 0x8d, 0xbf, 0x91, + 0xc1, 0xcb, 0xc1, 0xeb, 0xe4, 0x0f, 0x21, 0x19, 0x74, 0x17, 0x4f, 0x35, + 0x47, 0x7f, 0x3a, 0x67, 0x75, 0x76, 0x3d, 0x57, 0x4f, 0x36, 0x31, 0xfb, + 0x00, 0xdb, 0xee, 0xd1, 0xf5, 0xda, 0xac, 0xdc, 0xba, 0xb7, 0xe6, 0xba, + 0xc6, 0xa6, 0xcf, 0xd3, 0xe2, 0xd0, 0xce, 0xce, 0x07, 0xbb, 0xeb, 0x00, + 0x07, 0x14, 0x1d, 0x0e, 0x26, 0x17, 0x2a, 0x22, 0x60, 0x36, 0x71, 0x68, + 0x4f, 0x30, 0x55, 0x15, 0x2a, 0x52, 0x25, 0x1a, 0x25, 0x04, 0x09, 0xc2, + 0xee, 0xc2, 0xd5, 0xea, 0xe4, 0xb4, 0xeb, 0xc2, 0xc3, 0xca, 0xa5, 0xd4, + 0x9c, 0xd7, 0xe2, 0xb6, 0x05, 0xc7, 0xda, 0xdb, 0x0d, 0xe5, 0x20, 0x23, + 0x5b, 0x26, 0x58, 0x3f, 0x4e, 0x47, 0x41, 0x59, 0x49, 0x5c, 0x52, 0x1f, + 0x61, 0x28, 0x1c, 0x0e, 0xf7, 0xed, 0xe8, 0xd8, 0xf2, 0xaf, 0xf4, 0xc3, + 0xc5, 0xbe, 0xc9, 0xc2, 0xbe, 0xb5, 0xc7, 0xbe, 0xce, 0xeb, 0xea, 0x09, + 0xdf, 0x02, 0x12, 0x07, 0xf7, 0x24, 0x01, 0x08, 0x28, 0x18, 0x3e, 0x49, + 0x44, 0x63, 0x41, 0x2c, 0x46, 0x1f, 0x56, 0x14, 0x18, 0x0f, 0xf6, 0x06, + 0xdf, 0x00, 0xd0, 0xbd, 0xeb, 0x04, 0xd5, 0xee, 0xe6, 0xdc, 0xd9, 0xbc, + 0xdd, 0xbe, 0xfc, 0xb7, 0xe7, 0xd6, 0xd6, 0xd9, 0xff, 0xfd, 0xce, 0xff, + 0xec, 0x02, 0x1d, 0x22, 0x20, 0x20, 0x45, 0x49, 0x60, 0x3f, 0x41, 0x4b, + 0x34, 0x1a, 0x2f, 0x2a, 0x18, 0xfb, 0x13, 0xf2, 0xdf, 0xd0, 0xe3, 0xdb, + 0xdb, 0x06, 0x17, 0xf1, 0xed, 0xee, 0xdd, 0xde, 0xed, 0xdb, 0xd7, 0xe7, + 0xb9, 0x11, 0xdb, 0xe7, 0x0a, 0xe7, 0xe9, 0xd4, 0x06, 0x0c, 0x29, 0xfb, + 0x1f, 0x0b, 0x33, 0x20, 0xfe, 0x2e, 0x2b, 0x14, 0x16, 0x2a, 0x2c, 0x28, + 0x3a, 0x1d, 0x1b, 0x24, 0x03, 0x31, 0xe1, 0x06, 0xcd, 0xfd, 0xd1, 0xd5, + 0xf5, 0xda, 0xe5, 0xc8, 0xcc, 0xd1, 0xee, 0xc7, 0xe7, 0xda, 0xcf, 0xf6, + 0xcf, 0xfb, 0xe8, 0x26, 0xec, 0x0f, 0xf8, 0x1a, 0x24, 0x37, 0x10, 0x18, + 0x25, 0x19, 0x20, 0x39, 0x0b, 0x19, 0x1b, 0x1c, 0x10, 0x17, 0x07, 0x21, + 0x12, 0x03, 0x21, 0xf3, 0x16, 0xec, 0xf5, 0xe6, 0xe8, 0xd6, 0xca, 0xe4, + 0xf2, 0xd5, 0xe7, 0xef, 0xf9, 0xd3, 0xf9, 0xe3, 0xe8, 0xf0, 0xe5, 0xd7, + 0xfd, 0x08, 0xf3, 0x14, 0x0b, 0x0d, 0x19, 0x22, 0x0e, 0x07, 0x11, 0x1f, + 0x12, 0x34, 0x05, 0x2b, 0x11, 0x2c, 0x26, 0x1f, 0x1a, 0x1a, 0x20, 0xf9, + 0x16, 0xe4, 0xec, 0xe9, 0xdf, 0xe0, 0xed, 0xeb, 0xc5, 0xcc, 0xd3, 0xdd, + 0xdb, 0xf0, 0x03, 0xf8, 0xdd, 0xee, 0x10, 0xfd, 0x02, 0xf6, 0xff, 0xf3, + 0x1a, 0x20, 0x05, 0x20, 0x21, 0x1f, 0x1a, 0x13, 0xfd, 0x15, 0x01, 0x13, + 0x0e, 0xeb, 0x0e, 0x0c, 0x0c, 0x21, 0xed, 0x23, 0xfc, 0x00, 0xe5, 0xe7, + 0xee, 0xdc, 0xe6, 0xe3, 0xe8, 0xfd, 0xf9, 0x0d, 0x0a, 0x07, 0x0f, 0x07, + 0xfe, 0x12, 0xe5, 0xf0, 0xef, 0xed, 0xe2, 0xe5, 0xea, 0xe7, 0xf3, 0xfe, + 0x0c, 0x0b, 0x11, 0x18, 0x21, 0x20, 0x20, 0x0b, 0x1b, 0x19, 0x19, 0x0d, + 0x0d, 0xf8, 0x01, 0x0b, 0x0a, 0x0c, 0x0a, 0xff, 0x04, 0x0c, 0xec, 0xef, + 0xfb, 0xdb, 0xe7, 0xd6, 0xd3, 0xf3, 0xd9, 0xf9, 0xdb, 0xe8, 0xf6, 0xf3, + 0xf3, 0x11, 0xf5, 0x0e, 0xff, 0x10, 0x03, 0x19, 0x0e, 0x21, 0x0f, 0x1e, + 0x12, 0x20, 0x13, 0x1b, 0x15, 0xf5, 0xfd, 0x04, 0xfe, 0xf7, 0xf1, 0xff, + 0xed, 0xff, 0x10, 0xf7, 0x02, 0x16, 0xf1, 0x03, 0x03, 0xf7, 0x05, 0x06, + 0x07, 0x06, 0xed, 0xf7, 0x00, 0x05, 0x13, 0xfb, 0x01, 0x10, 0xea, 0xfd, + 0xf8, 0xe4, 0xef, 0xe6, 0xef, 0xf6, 0x11, 0x00, 0x00, 0x02, 0x00, 0x06, + 0x10, 0xfd, 0x0a, 0x0d, 0x00, 0xeb, 0x09, 0x03, 0x09, 0x14, 0x12, 0x05, + 0xfc, 0x0e, 0x16, 0x1a, 0x18, 0x01, 0x17, 0xfe, 0xf2, 0xf5, 0xea, 0xe7, + 0xdb, 0xea, 0xea, 0xf7, 0xf0, 0xf9, 0xf9, 0x01, 0x01, 0x0b, 0xf9, 0x07, + 0xfd, 0xfd, 0x07, 0xf9, 0xfb, 0x02, 0x12, 0x04, 0x04, 0x19, 0x10, 0x09, + 0x1a, 0x0b, 0xf8, 0x12, 0x03, 0xec, 0x1a, 0xee, 0xfa, 0x13, 0x07, 0x0e, + 0x05, 0x05, 0x14, 0xf6, 0xfd, 0xf8, 0xdf, 0xf8, 0xf3, 0xe4, 0xfe, 0xfa, + 0x02, 0x13, 0xf9, 0x0c, 0xf9, 0x10, 0xf7, 0x0d, 0x03, 0x03, 0xf3, 0x03, + 0x00, 0xeb, 0x03, 0xe3, 0xe5, 0xed, 0xed, 0x0d, 0x07, 0x08, 0x23, 0x21, + 0x0b, 0x0a, 0x1b, 0x0b, 0x10, 0x03, 0x0b, 0x0a, 0xef, 0xf6, 0x06, 0xfc, + 0xec, 0xe2, 0x00, 0xf9, 0x09, 0x00, 0x06, 0xf9, 0xf9, 0xf9, 0x01, 0xfa, + 0x01, 0x05, 0xfa, 0x06, 0x05, 0x10, 0x10, 0x06, 0x06, 0x01, 0x01, 0x06, + 0x00, 0x0d, 0x10, 0x02, 0xfc, 0xff, 0x05, 0xfe, 0x03, 0xf2, 0xf8, 0xee, + 0x00, 0xe7, 0x00, 0xf1, 0xf4, 0xf7, 0xf5, 0xfa, 0x01, 0x0f, 0x0a, 0x11, + 0x0b, 0x0e, 0x0d, 0x13, 0x14, 0x0a, 0x0a, 0x0a, 0x00, 0x07, 0xfc, 0x03, + 0x00, 0xff, 0xf5, 0xf6, 0xff, 0xf8, 0xfa, 0xfd, 0xef, 0xf6, 0xf7, 0xf8, + 0xf5, 0xf6, 0x01, 0xf9, 0xfc, 0xf4, 0x03, 0xfe, 0x01, 0x00, 0x02, 0xfc, + 0xfd, 0x07, 0x0a, 0x02, 0x05, 0x06, 0x09, 0x10, 0x05, 0x0b, 0x14, 0x03, + 0x06, 0x06, 0x06, 0x09, 0x05, 0xfe, 0xfe, 0xfd, 0xf6, 0xf8, 0xf9, 0xf9, + 0xfa, 0xfb, 0xfb, 0xfd, 0x01, 0xfa, 0x00, 0x00, 0xf9, 0xfa, 0xfe, 0xff, + 0xfe, 0xfe, 0x01, 0xff, 0xfe, 0x00, 0x01, 0x00, 0x01, 0x05, 0x00, 0x04, + 0x08, 0x03, 0x09, 0x03, 0x06, 0x01, 0x09, 0x07, 0x07, 0x03, 0x02, 0xfe, + 0xff, 0xfe, 0xfe, 0xf9, 0xfa, 0xfa, 0xfd, 0xfb, 0xfc, 0xfd, 0xff, 0xfd, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x37, 0x2a, 0x15, 0x16, 0xfa, 0xd6, 0xd6, 0xd8, 0xbc, + 0xa3, 0x91, 0x85, 0x82, 0x82, 0x84, 0x88, 0x94, 0xaa, 0xc3, 0xe0, 0xfc, + 0x14, 0x2e, 0x49, 0x5e, 0x6c, 0x74, 0x78, 0x74, 0x6c, 0x62, 0x56, 0x45, + 0x2e, 0x14, 0xfc, 0xe4, 0xc9, 0xae, 0x93, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x8d, 0x9e, 0xb4, 0xc9, 0xdf, 0xf3, + 0x06, 0x19, 0x2e, 0x45, 0x5b, 0x70, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7c, 0x73, 0x69, 0x5a, 0x48, 0x33, 0x1f, 0x0a, 0xf6, 0xe1, 0xcd, + 0xb7, 0xa2, 0x8c, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x85, 0x94, 0xa3, 0xae, 0xba, 0xc6, 0xd0, 0xdb, 0xe8, 0xf7, + 0x02, 0x0c, 0x18, 0x25, 0x32, 0x3e, 0x46, 0x4a, 0x4c, 0x4f, 0x50, 0x4f, + 0x4e, 0x49, 0x43, 0x3c, 0x36, 0x30, 0x28, 0x21, 0x1a, 0x13, 0x0c, 0x04, + 0xff, 0xfa, 0xf6, 0xf2, 0xf1, 0xf1, 0xf0, 0xf1, 0xf3, 0xf6, 0xf8, 0x00, + 0x00, 0xff, 0x99, 0x4e, 0x66, 0x56, 0x48, 0x2a, 0x80, 0x1c, 0xff, 0x80, + 0x80, 0x80, 0x80, 0x00 +}; + +unsigned int mod_len = 7600; +const char * mod_name = "nao-deceased by disease"; diff --git a/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.mod b/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.mod new file mode 100644 index 000000000..8b1a9b2fc Binary files /dev/null and b/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.mod differ diff --git a/3rd-party/hxcmod-player/readme.txt b/3rd-party/hxcmod-player/readme.txt new file mode 100644 index 000000000..3edfe8dc8 --- /dev/null +++ b/3rd-party/hxcmod-player/readme.txt @@ -0,0 +1,119 @@ +Local copy used for A2DP Source demo in BTstack. + +Github repository: https://github.com/jfdelnero/HxCModPlayer +Thanks for providing this nice and compact implementation! + +-------------------------------------------------------------------------------------- +Original readme.txt +-------------------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////////// +//-------------------------------------------------------------------------------// +//-------------------------------------------------------------------------------// +//-----------H----H--X----X-----CCCCC----22222----0000-----0000------11----------// +//----------H----H----X-X-----C--------------2---0----0---0----0---1-1-----------// +//---------HHHHHH-----X------C----------22222---0----0---0----0-----1------------// +//--------H----H----X--X----C----------2-------0----0---0----0-----1-------------// +//-------H----H---X-----X---CCCCC-----222222----0000-----0000----1111------------// +//-------------------------------------------------------------------------------// +//----------------------------------------------------- http://hxc2001.free.fr --// +/////////////////////////////////////////////////////////////////////////////////// + + HxCMOD player + +The HxCMOD player is a tiny music module player. + +It currently supports the Noisetracker/Soundtracker/Protracker Module Format (*.mod) + +The core (hxcmod.c/hxcmod.h) is designed to have the least external dependency. +So it should be usable on almost all OS and systems. +You can use the hxcmod.c / hxcmod.h files to add a mod replay support +to a demo/game/software. + +You are free to do what you want with this code. +(A credit is always appreciated if you include it into your prod ;) ) + +The test program is into the win32 folder. Just drag and drop a mod on the main +window to load it. Linux & Mac OS X version it planned. + +Please note that this core was "Emscriptened" successfully and is now working in +JavaScript with Android, Chrome, Firefox, Edge, Safari browsers and probably +with others browsers supporting the Web Audio API support. + +You can test it at this address : http://hxc2001.free.fr/hxcmod/ + +A video demo of the native Mod player can be seen on youtube : +https://www.youtube.com/watch?v=MEU9FGZzjac + +-------------------------------------------------------------------------------------- + HxCMOD Core API +-------------------------------------------------------------------------------------- + +int hxcmod_init( modcontext * modctx ) + +- Initialize the modcontext buffer. Must be called before doing anything else. + Return 1 if success. 0 in case of error. + +int hxcmod_setcfg( modcontext * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter); + +- Configure the player : + samplerate specify the sample rate. (44100 by default). + bits specify the number of bits (16 bits only for the moment). + stereo - if non null, the stereo mode is selected (default) + stereo_separation - Left/Right channel separation. + filter - if non null, the filter is applied (default) + +int hxcmod_load( modcontext * modctx, void * mod_data, int mod_data_size ) + +- "Load" a MOD from memory (from "mod_data" with size "mod_data_size"). + Return 1 if success. 0 in case of error. + + +void hxcmod_fillbuffer( modcontext * modctx, unsigned short * outbuffer, unsigned long nbsample, tracker_buffer_state * trkbuf ) + +- Generate and return the next samples chunk to outbuffer. + nbsample specify the number of stereo 16bits samples you want. + The output format is signed 44100Hz 16-bit Stereo PCM samples. + The output buffer size in byte must be equal to ( nbsample * 2 * 2 ). + The optional trkbuf parameter can be used to get detailed status of the player. Put NULL/0 if unused. + + +void hxcmod_unload( modcontext * modctx ) +- "Unload" / clear the player status. + + +-------------------------------------------------------------------------------------- + Files on the repository +-------------------------------------------------------------------------------------- + +- hxcmod.c / hxcmod.h +The HxC core mod replay routines. These files don't have any dependency with others files +and can be used into other project. + +- framegenerator.c / framegenerator.h +Generate a 640*480 framebuffer from the player status to be displayed in real-time. +(not needed by the core) + +- win32/ +The Windows test software. +(linux & Mac version planned) + +- js_emscripten/ +The Web browser/JavaScript version. (Build with Emscripten) + +- packer/ +Data compression utility. Used to embed one mod and some graphical stuff into the executable. +Not directly used by the core. + +- data/ +Some packed data files. + +-------------------------------------------------------------------------------------- + +Jean-François DEL NERO (Jeff) / HxC2001 + +Email : jeanfrancoisdelnero <> free.fr + +http://hxc2001.free.fr + +11 July 2015 diff --git a/src/btstack_defines.h b/src/btstack_defines.h index 4464f44e8..9ef9eb6d7 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -962,6 +962,7 @@ typedef uint8_t sm_key_t[16]; #define HCI_EVENT_GOEP_META 0xED #define HCI_EVENT_PBAP_META 0xEE #define HCI_EVENT_HID_META 0xEF +#define HCI_EVENT_A2DP_META 0xF0 // Potential other meta groups // #define HCI_EVENT_BNEP_META 0xxx @@ -1258,26 +1259,29 @@ typedef uint8_t sm_key_t[16]; /** AVDTP Subevent */ /** - * @format 1H11 + * @format 1H111 * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid * @param signal_identifier * @param status 0 == OK */ #define AVDTP_SUBEVENT_SIGNALING_ACCEPT 0x01 /** - * @format 1H1 + * @format 1H11 * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid * @param signal_identifier */ #define AVDTP_SUBEVENT_SIGNALING_REJECT 0x02 /** - * @format 1H1 + * @format 1H11 * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid * @param signal_identifier */ #define AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT 0x03 @@ -1285,7 +1289,7 @@ typedef uint8_t sm_key_t[16]; /** * @format 1HB1 * @param subevent_code - * @param con_handle + * @param avdtp_cid * @param bd_addr * @param status 0 == OK */ @@ -1294,14 +1298,14 @@ typedef uint8_t sm_key_t[16]; /** * @format 1H * @param subevent_code - * @param con_handle + * @param avdtp_cid */ #define AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED 0x05 /** * @format 1H1111 * @param subevent_code - * @param handle + * @param avdtp_cid * @param seid 0x01 – 0x3E * @param in_use 0-not in use, 1-in use * @param media_type 0-audio, 1-video, 2-multimedia @@ -1310,9 +1314,11 @@ typedef uint8_t sm_key_t[16]; #define AVDTP_SUBEVENT_SIGNALING_SEP_FOUND 0x06 /** - * @format 1H11111111 + * @format 1H1111111111 * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid + * @param acp_seid * @param media_type * @param sampling_frequency_bitmap * @param channel_mode_bitmap @@ -1325,9 +1331,11 @@ typedef uint8_t sm_key_t[16]; #define AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY 0x07 /** - * @format 1H12LV + * @format 1H1112LV * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid + * @param acp_seid * @param media_type * @param media_codec_type * @param media_codec_information_len @@ -1336,9 +1344,11 @@ typedef uint8_t sm_key_t[16]; #define AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY 0x08 /** - * @format 1H1121111111 + * @format 1H111121111111 * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid + * @param acp_seid * @param reconfigure * @param media_type * @param sampling_frequency @@ -1353,9 +1363,11 @@ typedef uint8_t sm_key_t[16]; #define AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION 0x09 /** - * @format 1H112LV + * @format 1H11112LV * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid + * @param acp_seid * @param reconfigure * @param media_type * @param media_codec_type @@ -1365,9 +1377,11 @@ typedef uint8_t sm_key_t[16]; #define AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION 0x0A /** - * @format 1H1 + * @format 1H111 * @param subevent_code - * @param con_handle + * @param avdtp_cid + * @param int_seid + * @param acp_seid * @param status 0 == OK */ #define AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED 0x0B @@ -1375,12 +1389,64 @@ typedef uint8_t sm_key_t[16]; /** * @format 1H * @param subevent_code - * @param con_handle + * @param avdtp_cid */ #define AVDTP_SUBEVENT_STREAMING_CONNECTION_RELEASED 0x0C +/** + * @format 1H12 + * @param subevent_code + * @param avdtp_cid + * @param int_seid + * @param sequence_number + */ +#define AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW 0x0D +/** A2DP Subevent */ + +/** + * @format 1H111 + * @param subevent_code + * @param a2dp_cid + * @param local_seid + * @param remote_seid + * @param status + */ +#define A2DP_SUBEVENT_STREAM_ESTABLISHED 0x01 + +/** + * @format 1H1 + * @param subevent_code + * @param a2dp_cid + * @param local_seid + */ +#define A2DP_SUBEVENT_STREAM_START_ACCEPTED 0x02 + +/** + * @format 1H1 + * @param subevent_code + * @param a2dp_cid + * @param local_seid + */ +#define A2DP_SUBEVENT_STREAM_SUSPENDED 0x03 + +/** + * @format 1H1 + * @param subevent_code + * @param avdtp_cid + * @param local_seid + */ +#define A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW 0x04 + +/** + * @format 1H1 + * @param subevent_code + * @param avdtp_cid + * @param local_seid + */ +#define A2DP_SUBEVENT_STREAM_RELEASED 0x05 + /** AVRCP Subevent */ /** diff --git a/src/btstack_event.h b/src/btstack_event.h index 40dfb2019..26c590915 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -85,6 +85,14 @@ static inline uint8_t hci_event_ancs_meta_get_subevent_code(const uint8_t * even static inline uint8_t hci_event_avdtp_meta_get_subevent_code(const uint8_t * event){ return event[2]; } +/*** + * @brief Get subevent code for a2dp event + * @param event packet + * @return subevent_code + */ +static inline uint8_t hci_event_a2dp_meta_get_subevent_code(const uint8_t * event){ + return event[2]; +} /*** * @brief Get subevent code for avrcp event * @param event packet @@ -3972,14 +3980,23 @@ static inline hci_con_handle_t ancs_subevent_client_disconnected_get_handle(cons #endif /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_ACCEPT + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_ACCEPT * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_accept_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_accept_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_ACCEPT + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_accept_get_int_seid(const uint8_t * event){ + return event[5]; +} /** * @brief Get field signal_identifier from event AVDTP_SUBEVENT_SIGNALING_ACCEPT * @param event packet @@ -3987,7 +4004,7 @@ static inline hci_con_handle_t avdtp_subevent_signaling_accept_get_con_handle(co * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_accept_get_signal_identifier(const uint8_t * event){ - return event[5]; + return event[6]; } /** * @brief Get field status from event AVDTP_SUBEVENT_SIGNALING_ACCEPT @@ -3996,18 +4013,27 @@ static inline uint8_t avdtp_subevent_signaling_accept_get_signal_identifier(cons * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_accept_get_status(const uint8_t * event){ - return event[6]; + return event[7]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_REJECT + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_REJECT * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_reject_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_reject_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_REJECT + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_reject_get_int_seid(const uint8_t * event){ + return event[5]; +} /** * @brief Get field signal_identifier from event AVDTP_SUBEVENT_SIGNALING_REJECT * @param event packet @@ -4015,18 +4041,27 @@ static inline hci_con_handle_t avdtp_subevent_signaling_reject_get_con_handle(co * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_reject_get_signal_identifier(const uint8_t * event){ - return event[5]; + return event[6]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_general_reject_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_general_reject_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_general_reject_get_int_seid(const uint8_t * event){ + return event[5]; +} /** * @brief Get field signal_identifier from event AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT * @param event packet @@ -4034,16 +4069,16 @@ static inline hci_con_handle_t avdtp_subevent_signaling_general_reject_get_con_h * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_general_reject_get_signal_identifier(const uint8_t * event){ - return event[5]; + return event[6]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_connection_established_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_connection_established_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } /** @@ -4066,22 +4101,22 @@ static inline uint8_t avdtp_subevent_signaling_connection_established_get_status } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_CONNECTION_RELEASED * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_connection_released_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_connection_released_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } /** - * @brief Get field handle from event AVDTP_SUBEVENT_SIGNALING_SEP_FOUND + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_SEP_FOUND * @param event packet - * @return handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_sep_found_get_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_sep_found_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } /** @@ -4122,14 +4157,32 @@ static inline uint8_t avdtp_subevent_signaling_sep_found_get_sep_type(const uint } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_sbc_capability_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_sbc_capability_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_int_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field acp_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY + * @param event packet + * @return acp_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_acp_seid(const uint8_t * event){ + return event[6]; +} /** * @brief Get field media_type from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY * @param event packet @@ -4137,7 +4190,7 @@ static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_sbc_capabili * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_media_type(const uint8_t * event){ - return event[5]; + return event[7]; } /** * @brief Get field sampling_frequency_bitmap from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4146,7 +4199,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_me * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(const uint8_t * event){ - return event[6]; + return event[8]; } /** * @brief Get field channel_mode_bitmap from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4155,7 +4208,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_sa * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(const uint8_t * event){ - return event[7]; + return event[9]; } /** * @brief Get field block_length_bitmap from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4164,7 +4217,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_ch * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(const uint8_t * event){ - return event[8]; + return event[10]; } /** * @brief Get field subbands_bitmap from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4173,7 +4226,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_bl * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_subbands_bitmap(const uint8_t * event){ - return event[9]; + return event[11]; } /** * @brief Get field allocation_method_bitmap from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4182,7 +4235,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_su * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_allocation_method_bitmap(const uint8_t * event){ - return event[10]; + return event[12]; } /** * @brief Get field min_bitpool_value from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4191,7 +4244,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_al * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_min_bitpool_value(const uint8_t * event){ - return event[11]; + return event[13]; } /** * @brief Get field max_bitpool_value from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY @@ -4200,18 +4253,36 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_mi * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_capability_get_max_bitpool_value(const uint8_t * event){ - return event[12]; + return event[14]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_other_capability_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_other_capability_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_other_capability_get_int_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field acp_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY + * @param event packet + * @return acp_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_other_capability_get_acp_seid(const uint8_t * event){ + return event[6]; +} /** * @brief Get field media_type from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY * @param event packet @@ -4219,7 +4290,7 @@ static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_other_capabi * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_other_capability_get_media_type(const uint8_t * event){ - return event[5]; + return event[7]; } /** * @brief Get field media_codec_type from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY @@ -4228,7 +4299,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_other_capability_get_ * @note: btstack_type 2 */ static inline uint16_t avdtp_subevent_signaling_media_codec_other_capability_get_media_codec_type(const uint8_t * event){ - return little_endian_read_16(event, 6); + return little_endian_read_16(event, 8); } /** * @brief Get field media_codec_information_len from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY @@ -4237,7 +4308,7 @@ static inline uint16_t avdtp_subevent_signaling_media_codec_other_capability_get * @note: btstack_type L */ static inline int avdtp_subevent_signaling_media_codec_other_capability_get_media_codec_information_len(const uint8_t * event){ - return little_endian_read_16(event, 8); + return little_endian_read_16(event, 10); } /** * @brief Get field media_codec_information from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY @@ -4246,18 +4317,36 @@ static inline int avdtp_subevent_signaling_media_codec_other_capability_get_medi * @note: btstack_type V */ static inline const uint8_t * avdtp_subevent_signaling_media_codec_other_capability_get_media_codec_information(const uint8_t * event){ - return &event[10]; + return &event[12]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_int_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field acp_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION + * @param event packet + * @return acp_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_acp_seid(const uint8_t * event){ + return event[6]; +} /** * @brief Get field reconfigure from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION * @param event packet @@ -4265,7 +4354,7 @@ static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_sbc_configur * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(const uint8_t * event){ - return event[5]; + return event[7]; } /** * @brief Get field media_type from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4274,7 +4363,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_media_type(const uint8_t * event){ - return event[6]; + return event[8]; } /** * @brief Get field sampling_frequency from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4283,7 +4372,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 2 */ static inline uint16_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(const uint8_t * event){ - return little_endian_read_16(event, 7); + return little_endian_read_16(event, 9); } /** * @brief Get field channel_mode from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4292,7 +4381,7 @@ static inline uint16_t avdtp_subevent_signaling_media_codec_sbc_configuration_ge * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(const uint8_t * event){ - return event[9]; + return event[11]; } /** * @brief Get field num_channels from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4301,7 +4390,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(const uint8_t * event){ - return event[10]; + return event[12]; } /** * @brief Get field block_length from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4310,7 +4399,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_block_length(const uint8_t * event){ - return event[11]; + return event[13]; } /** * @brief Get field subbands from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4319,7 +4408,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_subbands(const uint8_t * event){ - return event[12]; + return event[14]; } /** * @brief Get field allocation_method from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4328,7 +4417,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(const uint8_t * event){ - return event[13]; + return event[15]; } /** * @brief Get field min_bitpool_value from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4337,7 +4426,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(const uint8_t * event){ - return event[14]; + return event[16]; } /** * @brief Get field max_bitpool_value from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION @@ -4346,18 +4435,36 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(const uint8_t * event){ - return event[15]; + return event[17]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_other_configuration_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_other_configuration_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_other_configuration_get_int_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field acp_seid from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION + * @param event packet + * @return acp_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_signaling_media_codec_other_configuration_get_acp_seid(const uint8_t * event){ + return event[6]; +} /** * @brief Get field reconfigure from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION * @param event packet @@ -4365,7 +4472,7 @@ static inline hci_con_handle_t avdtp_subevent_signaling_media_codec_other_config * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_other_configuration_get_reconfigure(const uint8_t * event){ - return event[5]; + return event[7]; } /** * @brief Get field media_type from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION @@ -4374,7 +4481,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_other_configuration_g * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_signaling_media_codec_other_configuration_get_media_type(const uint8_t * event){ - return event[6]; + return event[8]; } /** * @brief Get field media_codec_type from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION @@ -4383,7 +4490,7 @@ static inline uint8_t avdtp_subevent_signaling_media_codec_other_configuration_g * @note: btstack_type 2 */ static inline uint16_t avdtp_subevent_signaling_media_codec_other_configuration_get_media_codec_type(const uint8_t * event){ - return little_endian_read_16(event, 7); + return little_endian_read_16(event, 9); } /** * @brief Get field media_codec_information_len from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION @@ -4392,7 +4499,7 @@ static inline uint16_t avdtp_subevent_signaling_media_codec_other_configuration_ * @note: btstack_type L */ static inline int avdtp_subevent_signaling_media_codec_other_configuration_get_media_codec_information_len(const uint8_t * event){ - return little_endian_read_16(event, 9); + return little_endian_read_16(event, 11); } /** * @brief Get field media_codec_information from event AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION @@ -4401,18 +4508,36 @@ static inline int avdtp_subevent_signaling_media_codec_other_configuration_get_m * @note: btstack_type V */ static inline const uint8_t * avdtp_subevent_signaling_media_codec_other_configuration_get_media_codec_information(const uint8_t * event){ - return &event[11]; + return &event[13]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED * @param event packet - * @return con_handle + * @return avdtp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_streaming_connection_established_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t avdtp_subevent_streaming_connection_established_get_avdtp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_streaming_connection_established_get_int_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field acp_seid from event AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED + * @param event packet + * @return acp_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_streaming_connection_established_get_acp_seid(const uint8_t * event){ + return event[6]; +} /** * @brief Get field status from event AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED * @param event packet @@ -4420,18 +4545,159 @@ static inline hci_con_handle_t avdtp_subevent_streaming_connection_established_g * @note: btstack_type 1 */ static inline uint8_t avdtp_subevent_streaming_connection_established_get_status(const uint8_t * event){ + return event[7]; +} + +/** + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_STREAMING_CONNECTION_RELEASED + * @param event packet + * @return avdtp_cid + * @note: btstack_type H + */ +static inline hci_con_handle_t avdtp_subevent_streaming_connection_released_get_avdtp_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} + +/** + * @brief Get field avdtp_cid from event AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW + * @param event packet + * @return avdtp_cid + * @note: btstack_type H + */ +static inline hci_con_handle_t avdtp_subevent_streaming_can_send_media_packet_now_get_avdtp_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field int_seid from event AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW + * @param event packet + * @return int_seid + * @note: btstack_type 1 + */ +static inline uint8_t avdtp_subevent_streaming_can_send_media_packet_now_get_int_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field sequence_number from event AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW + * @param event packet + * @return sequence_number + * @note: btstack_type 2 + */ +static inline uint16_t avdtp_subevent_streaming_can_send_media_packet_now_get_sequence_number(const uint8_t * event){ + return little_endian_read_16(event, 6); +} + +/** + * @brief Get field a2dp_cid from event A2DP_SUBEVENT_STREAM_ESTABLISHED + * @param event packet + * @return a2dp_cid + * @note: btstack_type H + */ +static inline hci_con_handle_t a2dp_subevent_stream_established_get_a2dp_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field local_seid from event A2DP_SUBEVENT_STREAM_ESTABLISHED + * @param event packet + * @return local_seid + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_stream_established_get_local_seid(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field remote_seid from event A2DP_SUBEVENT_STREAM_ESTABLISHED + * @param event packet + * @return remote_seid + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_stream_established_get_remote_seid(const uint8_t * event){ + return event[6]; +} +/** + * @brief Get field status from event A2DP_SUBEVENT_STREAM_ESTABLISHED + * @param event packet + * @return status + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_stream_established_get_status(const uint8_t * event){ + return event[7]; +} + +/** + * @brief Get field a2dp_cid from event A2DP_SUBEVENT_STREAM_START_ACCEPTED + * @param event packet + * @return a2dp_cid + * @note: btstack_type H + */ +static inline hci_con_handle_t a2dp_subevent_stream_start_accepted_get_a2dp_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field local_seid from event A2DP_SUBEVENT_STREAM_START_ACCEPTED + * @param event packet + * @return local_seid + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_stream_start_accepted_get_local_seid(const uint8_t * event){ return event[5]; } /** - * @brief Get field con_handle from event AVDTP_SUBEVENT_STREAMING_CONNECTION_RELEASED + * @brief Get field a2dp_cid from event A2DP_SUBEVENT_STREAM_SUSPENDED * @param event packet - * @return con_handle + * @return a2dp_cid * @note: btstack_type H */ -static inline hci_con_handle_t avdtp_subevent_streaming_connection_released_get_con_handle(const uint8_t * event){ +static inline hci_con_handle_t a2dp_subevent_stream_suspended_get_a2dp_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } +/** + * @brief Get field local_seid from event A2DP_SUBEVENT_STREAM_SUSPENDED + * @param event packet + * @return local_seid + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_stream_suspended_get_local_seid(const uint8_t * event){ + return event[5]; +} + +/** + * @brief Get field avdtp_cid from event A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW + * @param event packet + * @return avdtp_cid + * @note: btstack_type H + */ +static inline hci_con_handle_t a2dp_subevent_streaming_can_send_media_packet_now_get_avdtp_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field local_seid from event A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW + * @param event packet + * @return local_seid + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_streaming_can_send_media_packet_now_get_local_seid(const uint8_t * event){ + return event[5]; +} + +/** + * @brief Get field avdtp_cid from event A2DP_SUBEVENT_STREAM_RELEASED + * @param event packet + * @return avdtp_cid + * @note: btstack_type H + */ +static inline hci_con_handle_t a2dp_subevent_stream_released_get_avdtp_cid(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field local_seid from event A2DP_SUBEVENT_STREAM_RELEASED + * @param event packet + * @return local_seid + * @note: btstack_type 1 + */ +static inline uint8_t a2dp_subevent_stream_released_get_local_seid(const uint8_t * event){ + return event[5]; +} /** * @brief Get field con_handle from event AVRCP_SUBEVENT_CONNECTION_ESTABLISHED diff --git a/src/classic/a2dp_sink.c b/src/classic/a2dp_sink.c new file mode 100644 index 000000000..2697e5c3f --- /dev/null +++ b/src/classic/a2dp_sink.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS + * RINGWALD OR CONTRIBUTORS 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. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define __BTSTACK_FILE__ "a2dp_sink.c" + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_util.h" +#include "avdtp_sink.h" +#include "a2dp_sink.h" + +static const char * default_a2dp_sink_service_name = "BTstack A2DP Sink Service"; +static const char * default_a2dp_sink_service_provider_name = "BTstack A2DP Sink Service Provider"; + +// static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); + de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AUDIO_SINK); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); + de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* avProtocol = de_push_sequence(attribute); + { + de_add_number(avProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); // avProtocol_service + de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version + } + de_pop_sequence(attribute, avProtocol); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); + attribute = de_push_sequence(service); + { + uint8_t *a2dProfile = de_push_sequence(attribute); + { + de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION); + de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); + } + de_pop_sequence(attribute, a2dProfile); + } + de_pop_sequence(service, attribute); + + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + if (service_name){ + de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); + } else { + de_add_data(service, DE_STRING, strlen(default_a2dp_sink_service_name), (uint8_t *) default_a2dp_sink_service_name); + } + + // 0x0100 "Provider Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); + if (service_provider_name){ + de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); + } else { + de_add_data(service, DE_STRING, strlen(default_a2dp_sink_service_provider_name), (uint8_t *) default_a2dp_sink_service_provider_name); + } + + // 0x0311 "Supported Features" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); +} diff --git a/src/classic/a2dp_sink.h b/src/classic/a2dp_sink.h new file mode 100644 index 000000000..cf58c946f --- /dev/null +++ b/src/classic/a2dp_sink.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS + * RINGWALD OR CONTRIBUTORS 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. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * a2dp_sink.h + * + * Advanced Audio Distribution Transport Profile (A2DP) Sink + * + * A2DP Sink is a device that accepts streamed media data. + */ + +#ifndef __A2DP_SINK_H +#define __A2DP_SINK_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ + +/** + * @brief A2DP Sink service record. + * @param service + * @param service_record_handle + * @param supported_features 16-bit bitmap, see AVDTP_SINK_SF_* values in avdtp.h + * @param service_name + * @param service_provider_name + */ +void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); + +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif // __A2DP_SINK_H \ No newline at end of file diff --git a/src/classic/a2dp_source.c b/src/classic/a2dp_source.c new file mode 100644 index 000000000..4a683321e --- /dev/null +++ b/src/classic/a2dp_source.c @@ -0,0 +1,570 @@ + +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS + * RINGWALD OR CONTRIBUTORS 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. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +#define __BTSTACK_FILE__ "a2dp_source.c" + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_util.h" +#include "avdtp_source.h" +#include "a2dp_source.h" + +static const char * default_a2dp_source_service_name = "BTstack A2DP Source Service"; +static const char * default_a2dp_source_service_provider_name = "BTstack A2DP Source Service Provider"; +static avdtp_context_t a2dp_source_context; + +#define AVDTP_MEDIA_PAYLOAD_HEADER_SIZE 12 + +typedef struct { +// to app + uint32_t fill_audio_ring_buffer_timeout_ms; + uint32_t time_audio_data_sent; // ms + uint32_t acc_num_missed_samples; + uint32_t samples_ready; + btstack_timer_source_t fill_audio_ring_buffer_timer; + btstack_ring_buffer_t sbc_ring_buffer; + btstack_sbc_encoder_state_t sbc_encoder_state; + + int reconfigure; + int num_channels; + int sampling_frequency; + int channel_mode; + int block_length; + int subbands; + int allocation_method; + int min_bitpool_value; + int max_bitpool_value; + avdtp_stream_endpoint_t * local_stream_endpoint; + avdtp_sep_t * active_remote_sep; +} avdtp_stream_endpoint_context_t; + +static a2dp_state_t app_state = A2DP_IDLE; +static avdtp_stream_endpoint_context_t sc; +static uint16_t avdtp_cid = 0; +static int next_remote_sep_index_to_query = 0; + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); + de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AUDIO_SOURCE); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); + de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* avProtocol = de_push_sequence(attribute); + { + de_add_number(avProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); // avProtocol_service + de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version + } + de_pop_sequence(attribute, avProtocol); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); + attribute = de_push_sequence(service); + { + uint8_t *a2dProfile = de_push_sequence(attribute); + { + de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION); + de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); + } + de_pop_sequence(attribute, a2dProfile); + } + de_pop_sequence(service, attribute); + + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + if (service_name){ + de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); + } else { + de_add_data(service, DE_STRING, strlen(default_a2dp_source_service_name), (uint8_t *) default_a2dp_source_service_name); + } + + // 0x0100 "Provider Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); + if (service_provider_name){ + de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); + } else { + de_add_data(service, DE_STRING, strlen(default_a2dp_source_service_provider_name), (uint8_t *) default_a2dp_source_service_provider_name); + } + + // 0x0311 "Supported Features" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); +} + +static void a2dp_streaming_emit_connection_established(btstack_packet_handler_t callback, uint16_t cid, uint8_t local_seid, uint8_t remote_seid, uint8_t status){ + if (!callback) return; + uint8_t event[8]; + int pos = 0; + event[pos++] = HCI_EVENT_A2DP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = A2DP_SUBEVENT_STREAM_ESTABLISHED; + little_endian_store_16(event, pos, cid); + pos += 2; + event[pos++] = local_seid; + event[pos++] = remote_seid; + event[pos++] = status; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +static void a2dp_streaming_emit_can_send_media_packet_now(btstack_packet_handler_t callback, uint16_t cid, uint8_t seid){ + if (!callback) return; + uint8_t event[8]; + int pos = 0; + event[pos++] = HCI_EVENT_A2DP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW; + little_endian_store_16(event, pos, cid); + pos += 2; + event[pos++] = seid; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + bd_addr_t event_addr; + uint8_t signal_identifier; + uint8_t status; + avdtp_sep_t sep; + uint8_t int_seid; + uint8_t acp_seid; + + switch (packet_type) { + + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_PIN_CODE_REQUEST: + // inform about pin code request + log_info("Pin code request - using '0000'"); + hci_event_pin_code_request_get_bd_addr(packet, event_addr); + hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); + break; + case HCI_EVENT_DISCONNECTION_COMPLETE: + // connection closed -> quit test app + log_info("\n --- a2dp source --- HCI_EVENT_DISCONNECTION_COMPLETE ---"); + + break; + case HCI_EVENT_AVDTP_META: + switch (packet[2]){ + case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED: + avdtp_cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet); + status = avdtp_subevent_signaling_connection_established_get_status(packet); + if (status != 0){ + log_info(" --- a2dp source --- AVDTP_SUBEVENT_SIGNALING_CONNECTION could not be established, status %d ---", status); + break; + } + sc.active_remote_sep = NULL; + next_remote_sep_index_to_query = 0; + app_state = A2DP_W2_DISCOVER_SEPS; + log_info(" --- a2dp source --- AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED, avdtp cid 0x%02x ---", avdtp_cid); + avdtp_source_discover_stream_endpoints(avdtp_cid); + break; + + case AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED: + status = avdtp_subevent_streaming_connection_established_get_status(packet); + avdtp_cid = avdtp_subevent_streaming_connection_established_get_avdtp_cid(packet); + int_seid = avdtp_subevent_streaming_connection_established_get_int_seid(packet); + acp_seid = avdtp_subevent_streaming_connection_established_get_acp_seid(packet); + + if (status != 0){ + log_info(" --- a2dp source --- AVDTP_SUBEVENT_STREAMING_CONNECTION could not be established, status %d ---", status); + break; + } + app_state = A2DP_STREAMING_OPENED; + a2dp_streaming_emit_connection_established(a2dp_source_context.a2dp_callback, avdtp_cid, int_seid, acp_seid, 0); + log_info(" --- a2dp source --- AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED --- avdtp_cid 0x%02x, local seid %d, remote seid %d", avdtp_cid, int_seid, acp_seid); + break; + + case AVDTP_SUBEVENT_SIGNALING_SEP_FOUND: + if (app_state != A2DP_W2_DISCOVER_SEPS) return; + sep.seid = avdtp_subevent_signaling_sep_found_get_seid(packet); + sep.in_use = avdtp_subevent_signaling_sep_found_get_in_use(packet); + sep.media_type = avdtp_subevent_signaling_sep_found_get_media_type(packet); + sep.type = avdtp_subevent_signaling_sep_found_get_sep_type(packet); + log_info(" --- a2dp source --- Found sep: seid %u, in_use %d, media type %d, sep type %d (1-SNK)", sep.seid, sep.in_use, sep.media_type, sep.type); + break; + + case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY:{ + if (!sc.local_stream_endpoint) return; + uint8_t sampling_frequency = avdtp_choose_sbc_sampling_frequency(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(packet)); + uint8_t channel_mode = avdtp_choose_sbc_channel_mode(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(packet)); + uint8_t block_length = avdtp_choose_sbc_block_length(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(packet)); + uint8_t subbands = avdtp_choose_sbc_subbands(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_subbands_bitmap(packet)); + + uint8_t allocation_method = avdtp_choose_sbc_allocation_method(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_allocation_method_bitmap(packet)); + uint8_t max_bitpool_value = avdtp_choose_sbc_max_bitpool_value(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_max_bitpool_value(packet)); + uint8_t min_bitpool_value = avdtp_choose_sbc_min_bitpool_value(sc.local_stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_min_bitpool_value(packet)); + + sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[0] = (sampling_frequency << 4) | channel_mode; + sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[1] = (block_length << 4) | (subbands << 2) | allocation_method; + sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[2] = min_bitpool_value; + sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_information[3] = max_bitpool_value; + + sc.local_stream_endpoint->remote_configuration_bitmap = store_bit16(sc.local_stream_endpoint->remote_configuration_bitmap, AVDTP_MEDIA_CODEC, 1); + sc.local_stream_endpoint->remote_configuration.media_codec.media_type = AVDTP_AUDIO; + sc.local_stream_endpoint->remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC; + + app_state = A2DP_W2_SET_CONFIGURATION; + break; + } + case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY: + log_info(" --- a2dp source --- received non SBC codec. not implemented"); + break; + + case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION:{ + sc.sampling_frequency = avdtp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet); + sc.block_length = avdtp_subevent_signaling_media_codec_sbc_configuration_get_block_length(packet); + sc.subbands = avdtp_subevent_signaling_media_codec_sbc_configuration_get_subbands(packet); + sc.allocation_method = avdtp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(packet) - 1; + sc.max_bitpool_value = avdtp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet); + // TODO: deal with reconfigure: avdtp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet); + break; + } + case AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW: + a2dp_streaming_emit_can_send_media_packet_now(a2dp_source_context.a2dp_callback, avdtp_cid, 0); + break; + + case AVDTP_SUBEVENT_SIGNALING_ACCEPT: + signal_identifier = avdtp_subevent_signaling_accept_get_signal_identifier(packet); + status = avdtp_subevent_signaling_accept_get_status(packet); + log_info(" --- a2dp source --- Accepted %d", signal_identifier); + + switch (app_state){ + case A2DP_W2_DISCOVER_SEPS: + app_state = A2DP_W2_GET_ALL_CAPABILITIES; + sc.active_remote_sep = avdtp_source_remote_sep(avdtp_cid, next_remote_sep_index_to_query++); + log_info(" --- a2dp source --- Query get caps for seid %d", sc.active_remote_sep->seid); + avdtp_source_get_capabilities(avdtp_cid, sc.active_remote_sep->seid); + break; + case A2DP_W2_GET_CAPABILITIES: + case A2DP_W2_GET_ALL_CAPABILITIES: + if (next_remote_sep_index_to_query < avdtp_source_remote_seps_num(avdtp_cid)){ + sc.active_remote_sep = avdtp_source_remote_sep(avdtp_cid, next_remote_sep_index_to_query++); + log_info(" --- a2dp source --- Query get caps for seid %d", sc.active_remote_sep->seid); + avdtp_source_get_capabilities(avdtp_cid, sc.active_remote_sep->seid); + } else { + log_info(" --- a2dp source --- No more remote seps found"); + app_state = A2DP_IDLE; + } + break; + case A2DP_W2_SET_CONFIGURATION:{ + if (!sc.local_stream_endpoint) return; + app_state = A2DP_W2_GET_CONFIGURATION; + avdtp_source_set_configuration(avdtp_cid, avdtp_stream_endpoint_seid(sc.local_stream_endpoint), sc.active_remote_sep->seid, sc.local_stream_endpoint->remote_configuration_bitmap, sc.local_stream_endpoint->remote_configuration); + break; + } + case A2DP_W2_GET_CONFIGURATION: + app_state = A2DP_W2_OPEN_STREAM_WITH_SEID; + avdtp_source_get_configuration(avdtp_cid, sc.active_remote_sep->seid); + break; + case A2DP_W2_OPEN_STREAM_WITH_SEID:{ + app_state = A2DP_W4_OPEN_STREAM_WITH_SEID; + btstack_sbc_encoder_init(&sc.sbc_encoder_state, SBC_MODE_STANDARD, + sc.block_length, sc.subbands, + sc.allocation_method, sc.sampling_frequency, + sc.max_bitpool_value); + avdtp_source_open_stream(avdtp_cid, avdtp_stream_endpoint_seid(sc.local_stream_endpoint), sc.active_remote_sep->seid); + break; + } + case A2DP_STREAMING_OPENED: + if (!a2dp_source_context.a2dp_callback) return; + switch (signal_identifier){ + case AVDTP_SI_START:{ + uint8_t event[6]; + int pos = 0; + event[pos++] = HCI_EVENT_A2DP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = A2DP_SUBEVENT_STREAM_START_ACCEPTED; + little_endian_store_16(event, pos, avdtp_cid); + pos += 2; + event[pos++] = avdtp_stream_endpoint_seid(sc.local_stream_endpoint); + (*a2dp_source_context.a2dp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); + break; + } + case AVDTP_SI_CLOSE:{ + uint8_t event[6]; + int pos = 0; + event[pos++] = HCI_EVENT_A2DP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = A2DP_SUBEVENT_STREAM_RELEASED; + little_endian_store_16(event, pos, avdtp_cid); + pos += 2; + log_info("send A2DP_SUBEVENT_STREAM_RELEASED to app"); + event[pos++] = avdtp_stream_endpoint_seid(sc.local_stream_endpoint); + (*a2dp_source_context.a2dp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); + break; + } + case AVDTP_SI_SUSPEND:{ + uint8_t event[6]; + int pos = 0; + event[pos++] = HCI_EVENT_A2DP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = A2DP_SUBEVENT_STREAM_SUSPENDED; + little_endian_store_16(event, pos, avdtp_cid); + pos += 2; + event[pos++] = avdtp_stream_endpoint_seid(sc.local_stream_endpoint); + (*a2dp_source_context.a2dp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); + break; + } + default: + break; + } + break; + default: + app_state = A2DP_IDLE; + break; + } + + break; + case AVDTP_SUBEVENT_SIGNALING_REJECT: + app_state = A2DP_IDLE; + signal_identifier = avdtp_subevent_signaling_reject_get_signal_identifier(packet); + log_info(" --- a2dp source --- Rejected %d", signal_identifier); + break; + case AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT: + app_state = A2DP_IDLE; + signal_identifier = avdtp_subevent_signaling_general_reject_get_signal_identifier(packet); + log_info(" --- a2dp source --- Rejected %d", signal_identifier); + break; + default: + app_state = A2DP_IDLE; + log_info(" --- a2dp source --- not implemented"); + break; + } + break; + default: + break; + } + break; + default: + // other packet type + break; + } +} +void a2dp_source_register_packet_handler(btstack_packet_handler_t callback){ + if (callback == NULL){ + log_error("a2dp_source_register_packet_handler called with NULL callback"); + return; + } + avdtp_source_register_packet_handler(&packet_handler); + a2dp_source_context.a2dp_callback = callback; +} + +void a2dp_source_init(void){ + avdtp_source_init(&a2dp_source_context); + l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_0); +} + +uint8_t a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, + uint8_t * codec_capabilities, uint16_t codec_capabilities_len, + uint8_t * media_codec_info, uint16_t media_codec_info_len){ + avdtp_stream_endpoint_t * local_stream_endpoint = avdtp_source_create_stream_endpoint(AVDTP_SOURCE, media_type); + avdtp_source_register_media_transport_category(avdtp_stream_endpoint_seid(local_stream_endpoint)); + avdtp_source_register_media_codec_category(avdtp_stream_endpoint_seid(local_stream_endpoint), media_type, media_codec_type, + codec_capabilities, codec_capabilities_len); + local_stream_endpoint->remote_configuration.media_codec.media_codec_information = media_codec_info; + local_stream_endpoint->remote_configuration.media_codec.media_codec_information_len = media_codec_info_len; + + return local_stream_endpoint->sep.seid; +} + +void a2dp_source_establish_stream(bd_addr_t bd_addr, uint8_t local_seid){ + sc.local_stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, &a2dp_source_context); + if (!sc.local_stream_endpoint){ + log_error(" no local_stream_endpoint for seid %d", local_seid); + return; + } + avdtp_source_connect(bd_addr); +} + +void a2dp_source_disconnect(uint16_t con_handle){ + avdtp_disconnect(con_handle, &a2dp_source_context); +} + +void a2dp_source_start_stream(uint8_t int_seid){ + avdtp_start_stream(int_seid, &a2dp_source_context); +} + +void a2dp_source_release_stream(uint8_t int_seid){ + avdtp_stop_stream(int_seid, &a2dp_source_context); +} + +void a2dp_source_pause_stream(uint8_t int_seid){ + avdtp_suspend_stream(int_seid, &a2dp_source_context); +} + +uint8_t a2dp_source_stream_endpoint_ready(uint8_t local_seid){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, &a2dp_source_context); + if (!stream_endpoint) { + log_error("No stream_endpoint with seid %d", local_seid); + return 0; + } + return (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING); +} + + +static void a2dp_source_setup_media_header(uint8_t * media_packet, int size, int *offset, uint8_t marker, uint16_t sequence_number){ + if (size < AVDTP_MEDIA_PAYLOAD_HEADER_SIZE){ + log_error("small outgoing buffer"); + return; + } + + uint8_t rtp_version = 2; + uint8_t padding = 0; + uint8_t extension = 0; + uint8_t csrc_count = 0; + uint8_t payload_type = 0x60; + // uint16_t sequence_number = stream_endpoint->sequence_number; + uint32_t timestamp = btstack_run_loop_get_time_ms(); + uint32_t ssrc = 0x11223344; + + // rtp header (min size 12B) + int pos = 0; + // int mtu = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); + + media_packet[pos++] = (rtp_version << 6) | (padding << 5) | (extension << 4) | csrc_count; + media_packet[pos++] = (marker << 1) | payload_type; + big_endian_store_16(media_packet, pos, sequence_number); + pos += 2; + big_endian_store_32(media_packet, pos, timestamp); + pos += 4; + big_endian_store_32(media_packet, pos, ssrc); // only used for multicast + pos += 4; + *offset = pos; +} + +void a2dp_source_stream_endpoint_request_can_send_now(uint8_t local_seid){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(local_seid, &a2dp_source_context); + if (!stream_endpoint) { + log_error("no stream_endpoint for seid %d", local_seid); + return; + } + stream_endpoint->send_stream = 1; + avdtp_request_can_send_now_initiator(stream_endpoint->connection, stream_endpoint->l2cap_media_cid); +} + +int a2dp_max_media_payload_size(uint8_t int_seid){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(int_seid, &a2dp_source_context); + if (!stream_endpoint) { + log_error("no stream_endpoint found for seid %d", int_seid); + return 0; + } + if (stream_endpoint->l2cap_media_cid == 0){ + log_error("no media cid found for seid %d", int_seid); + return 0; + } + return l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid) - AVDTP_MEDIA_PAYLOAD_HEADER_SIZE; +} + +static void a2dp_source_copy_media_payload(uint8_t * media_packet, int size, int * offset, uint8_t * storage, int num_bytes_to_copy, uint8_t num_frames){ + if (size < num_bytes_to_copy + 1){ + log_error("small outgoing buffer"); + return; + } + + int pos = *offset; + media_packet[pos++] = num_frames; // (fragmentation << 7) | (starting_packet << 6) | (last_packet << 5) | num_frames; + memcpy(media_packet + pos, storage, num_bytes_to_copy); + pos += num_bytes_to_copy; + *offset = pos; +} + +int a2dp_source_stream_send_media_payload(uint8_t int_seid, uint8_t * storage, int num_bytes_to_copy, uint8_t num_frames, uint8_t marker){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(int_seid, &a2dp_source_context); + if (!stream_endpoint) { + log_error("no stream_endpoint found for seid %d", int_seid); + return 0; + } + + if (stream_endpoint->l2cap_media_cid == 0){ + log_error("no media cid found for seid %d", int_seid); + return 0; + } + + int size = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); + int offset = 0; + + l2cap_reserve_packet_buffer(); + uint8_t * media_packet = l2cap_get_outgoing_buffer(); + //int size = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); + a2dp_source_setup_media_header(media_packet, size, &offset, marker, stream_endpoint->sequence_number); + a2dp_source_copy_media_payload(media_packet, size, &offset, storage, num_bytes_to_copy, num_frames); + stream_endpoint->sequence_number++; + l2cap_send_prepared(stream_endpoint->l2cap_media_cid, offset); + return size; +} diff --git a/src/classic/a2dp_source.h b/src/classic/a2dp_source.h new file mode 100644 index 000000000..247f581e7 --- /dev/null +++ b/src/classic/a2dp_source.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS + * RINGWALD OR CONTRIBUTORS 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. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * a2dp_source.h + * + * Advanced Audio Distribution Transport Profile (A2DP) Source + * + * A2DP Source is a device that streames media data. + */ + +#ifndef __A2DP_SOURCE_H +#define __A2DP_SOURCE_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ + +/** + * @brief A2DP Source service record. + * @param service + * @param service_record_handle + * @param supported_features 16-bit bitmap, see AVDTP_SOURCE_SF_* values in avdtp.h + * @param service_name + * @param service_provider_name + */ +void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); + +void a2dp_source_init(void); + +void a2dp_source_register_packet_handler(btstack_packet_handler_t callback); + +uint8_t a2dp_source_create_stream_endpoint(avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, + uint8_t * codec_capabilities, uint16_t codec_capabilities_len, + uint8_t * codec_configuration, uint16_t codec_configuration_len); + +/** + * @brief Open stream + * @param avdtp_cid + * @param seid + */ +void a2dp_source_establish_stream(bd_addr_t bd_addr, uint8_t local_seid); + +/** + * @brief Start stream + * @param avdtp_cid + * @param seid + */ +void a2dp_source_start_stream(uint8_t int_seid); + +/** + * @brief Start stream + * @param avdtp_cid + * @param seid + */ +void a2dp_source_pause_stream(uint8_t int_seid); + +/** + * @brief Close stream + * @param avdtp_cid + * @param seid + */ +void a2dp_source_release_stream(uint8_t int_seid); + +/** + * @brief Disconnect from device with connection handle. + * @param avdtp_cid + */ +void a2dp_source_disconnect(uint16_t avdtp_cid); + + +// size for media (does not include media header) +int a2dp_max_media_payload_size(uint8_t int_seid); + +uint8_t a2dp_source_stream_endpoint_ready(uint8_t local_seid); + +void a2dp_source_stream_endpoint_request_can_send_now(uint8_t local_seid); + +int a2dp_source_stream_send_media_payload(uint8_t int_seid, uint8_t * storage, int num_bytes_to_copy, uint8_t num_frames, uint8_t marker); + +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif // __A2DP_SOURCE_H \ No newline at end of file diff --git a/src/classic/avdtp.c b/src/classic/avdtp.c index ccf689f9b..9b33e5c91 100644 --- a/src/classic/avdtp.c +++ b/src/classic/avdtp.c @@ -50,13 +50,37 @@ #include "avdtp_acceptor.h" #include "avdtp_initiator.h" -static uint8_t audio_samples_storage[44100*4]; // 1s buffer -// static btstack_ring_buffer_t audio_ring_buffer; +static int record_id = -1; +static uint8_t attribute_value[1000]; +static const unsigned int attribute_value_buffer_size = sizeof(attribute_value); -static uint8_t sbc_samples_storage[44100*4]; -// static btstack_ring_buffer_t sbc_ring_buffer; +typedef struct { + avdtp_connection_t * connection; + btstack_packet_handler_t avdtp_callback; + avdtp_sep_type_t query_role; + btstack_packet_handler_t packet_handler; +} avdtp_sdp_query_context_t; + +static avdtp_sdp_query_context_t sdp_query_context; static void (*handle_media_data)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size); +static void avdtp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); + +void avdtp_connect(bd_addr_t remote, avdtp_sep_type_t query_role, avdtp_context_t * avdtp_context){ + sdp_query_context.connection = NULL; + avdtp_connection_t * connection = avdtp_connection_for_bd_addr(remote, avdtp_context); + if (!connection){ + connection = avdtp_create_connection(remote, avdtp_context); + } + if (connection->state != AVDTP_SIGNALING_CONNECTION_IDLE) return; + connection->state = AVDTP_SIGNALING_W4_SDP_QUERY_COMPLETE; + sdp_query_context.connection = connection; + sdp_query_context.query_role = query_role; + sdp_query_context.avdtp_callback = avdtp_context->avdtp_callback; + sdp_query_context.packet_handler = avdtp_context->packet_handler; + + sdp_client_query_uuid16(&avdtp_handle_sdp_client_query_result, remote, BLUETOOTH_PROTOCOL_AVDTP); +} void avdtp_register_media_transport_category(avdtp_stream_endpoint_t * stream_endpoint){ if (!stream_endpoint){ @@ -65,7 +89,6 @@ void avdtp_register_media_transport_category(avdtp_stream_endpoint_t * stream_en } uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_MEDIA_TRANSPORT, 1); stream_endpoint->sep.registered_service_categories = bitmap; - printf("registered services AVDTP_MEDIA_TRANSPORT(%d) %02x\n", AVDTP_MEDIA_TRANSPORT, stream_endpoint->sep.registered_service_categories); } void avdtp_register_reporting_category(avdtp_stream_endpoint_t * stream_endpoint){ @@ -122,14 +145,13 @@ void avdtp_register_header_compression_category(avdtp_stream_endpoint_t * stream stream_endpoint->sep.capabilities.header_compression.recovery = recovery; } -void avdtp_register_media_codec_category(avdtp_stream_endpoint_t * stream_endpoint, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len){ +void avdtp_register_media_codec_category(avdtp_stream_endpoint_t * stream_endpoint, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, uint8_t * media_codec_info, uint16_t media_codec_info_len){ if (!stream_endpoint){ log_error("avdtp_register_media_transport_category: stream endpoint with given seid is not registered"); return; } uint16_t bitmap = store_bit16(stream_endpoint->sep.registered_service_categories, AVDTP_MEDIA_CODEC, 1); stream_endpoint->sep.registered_service_categories = bitmap; - printf("registered services AVDTP_MEDIA_CODEC(%d) %02x\n", AVDTP_MEDIA_CODEC, stream_endpoint->sep.registered_service_categories); stream_endpoint->sep.capabilities.media_codec.media_type = media_type; stream_endpoint->sep.capabilities.media_codec.media_codec_type = media_codec_type; stream_endpoint->sep.capabilities.media_codec.media_codec_information = media_codec_info; @@ -203,14 +225,6 @@ avdtp_stream_endpoint_t * avdtp_create_stream_endpoint(avdtp_sep_type_t sep_type stream_endpoint->sep.seid = context->stream_endpoints_id_counter; stream_endpoint->sep.media_type = media_type; stream_endpoint->sep.type = sep_type; - - memset(audio_samples_storage, 0, sizeof(audio_samples_storage)); - btstack_ring_buffer_init(&stream_endpoint->audio_ring_buffer, audio_samples_storage, sizeof(audio_samples_storage)); - - memset(sbc_samples_storage, 0, sizeof(sbc_samples_storage)); - btstack_ring_buffer_init(&stream_endpoint->sbc_ring_buffer, sbc_samples_storage, sizeof(sbc_samples_storage)); - - btstack_linked_list_add(&context->stream_endpoints, (btstack_linked_item_t *) stream_endpoint); return stream_endpoint; } @@ -248,8 +262,9 @@ static void stream_endpoint_state_machine(avdtp_connection_t * connection, avdtp stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; stream_endpoint->connection = connection; stream_endpoint->l2cap_media_cid = l2cap_event_channel_opened_get_local_cid(packet); - printf(" -> AVDTP_STREAM_ENDPOINT_OPENED, stream endpoint %p, connection %p\n", stream_endpoint, connection); - avdtp_streaming_emit_connection_established(context->avdtp_callback, stream_endpoint->l2cap_media_cid, 0); + stream_endpoint->media_con_handle = l2cap_event_channel_opened_get_handle(packet); + log_info(" -> AVDTP_STREAM_ENDPOINT_OPENED, media con handle 0x%02x, l2cap_media_cid 0x%02x", stream_endpoint->media_con_handle, stream_endpoint->l2cap_media_cid); + avdtp_streaming_emit_connection_established(context->avdtp_callback, connection->l2cap_signaling_cid, stream_endpoint->sep.seid, connection->remote_seps[stream_endpoint->remote_sep_index].seid, 0); break; } break; @@ -257,13 +272,11 @@ static void stream_endpoint_state_machine(avdtp_connection_t * connection, avdtp local_cid = l2cap_event_channel_closed_get_local_cid(packet); if (stream_endpoint->l2cap_media_cid == local_cid){ stream_endpoint->l2cap_media_cid = 0; - printf(" -> L2CAP_EVENT_CHANNEL_CLOSED: AVDTP_STREAM_ENDPOINT_IDLE\n"); + log_info(" -> L2CAP_EVENT_CHANNEL_CLOSED: AVDTP_STREAM_ENDPOINT_IDLE"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_IDLE; stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE; stream_endpoint->initiator_config_state = AVDTP_INITIATOR_STREAM_CONFIG_IDLE; - stream_endpoint->remote_seps_num = 0; - memset(stream_endpoint->remote_seps, 0, sizeof(avdtp_sep_t)*MAX_NUM_SEPS); - stream_endpoint->remote_sep_index = AVDTP_INVALID_SEP_INDEX; + stream_endpoint->remote_sep_index = 0; break; } if (stream_endpoint->l2cap_recovery_cid == local_cid){ @@ -287,6 +300,121 @@ static void stream_endpoint_state_machine(avdtp_connection_t * connection, avdtp } } +static void avdtp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(packet_type); + UNUSED(channel); + UNUSED(size); + + des_iterator_t des_list_it; + des_iterator_t prot_it; + uint32_t avdtp_remote_uuid = 0; + uint16_t avdtp_l2cap_psm = 0; + uint16_t avdtp_version = 0; + + if (!sdp_query_context.connection) return; + + switch (hci_event_packet_get_type(packet)){ + case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: + // Handle new SDP record + if (sdp_event_query_attribute_byte_get_record_id(packet) != record_id) { + record_id = sdp_event_query_attribute_byte_get_record_id(packet); + // log_info("SDP Record: Nr: %d", record_id); + } + + if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { + attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); + + if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { + + switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { + case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST: + if (de_get_element_type(attribute_value) != DE_DES) break; + for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { + uint8_t * element = des_iterator_get_element(&des_list_it); + if (de_get_element_type(element) != DE_UUID) continue; + uint32_t uuid = de_get_uuid32(element); + switch (uuid){ + case BLUETOOTH_SERVICE_CLASS_AUDIO_SOURCE: + if (sdp_query_context.query_role != AVDTP_SOURCE) { + sdp_query_context.connection->state = AVDTP_SIGNALING_CONNECTION_IDLE; + avdtp_signaling_emit_connection_established(sdp_query_context.avdtp_callback, sdp_query_context.connection->l2cap_signaling_cid, sdp_query_context.connection->remote_addr, 0); + break; + } + // log_info("SDP Attribute 0x%04x: AVDTP SOURCE protocol UUID: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet), uuid); + avdtp_remote_uuid = uuid; + break; + case BLUETOOTH_SERVICE_CLASS_AUDIO_SINK: + if (sdp_query_context.query_role != AVDTP_SINK) { + sdp_query_context.connection->state = AVDTP_SIGNALING_CONNECTION_IDLE; + avdtp_signaling_emit_connection_established(sdp_query_context.avdtp_callback, sdp_query_context.connection->l2cap_signaling_cid, sdp_query_context.connection->remote_addr, 0); + break; + } + // log_info("SDP Attribute 0x%04x: AVDTP SINK protocol UUID: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet), uuid); + avdtp_remote_uuid = uuid; + break; + default: + break; + } + } + break; + + case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: { + // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet)); + + for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) { + uint8_t *des_element; + uint8_t *element; + uint32_t uuid; + + if (des_iterator_get_type(&des_list_it) != DE_DES) continue; + + des_element = des_iterator_get_element(&des_list_it); + des_iterator_init(&prot_it, des_element); + element = des_iterator_get_element(&prot_it); + + if (de_get_element_type(element) != DE_UUID) continue; + + uuid = de_get_uuid32(element); + switch (uuid){ + case BLUETOOTH_PROTOCOL_L2CAP: + if (!des_iterator_has_more(&prot_it)) continue; + des_iterator_next(&prot_it); + de_element_get_uint16(des_iterator_get_element(&prot_it), &avdtp_l2cap_psm); + break; + case BLUETOOTH_PROTOCOL_AVDTP: + if (!des_iterator_has_more(&prot_it)) continue; + des_iterator_next(&prot_it); + de_element_get_uint16(des_iterator_get_element(&prot_it), &avdtp_version); + break; + default: + break; + } + } + if (!avdtp_l2cap_psm) { + sdp_query_context.connection->state = AVDTP_SIGNALING_CONNECTION_IDLE; + avdtp_signaling_emit_connection_established(sdp_query_context.avdtp_callback, sdp_query_context.connection->l2cap_signaling_cid, sdp_query_context.connection->remote_addr, L2CAP_SERVICE_DOES_NOT_EXIST); + break; + } + sdp_query_context.connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED; + l2cap_create_channel(sdp_query_context.packet_handler, sdp_query_context.connection->remote_addr, avdtp_l2cap_psm, l2cap_max_mtu(), NULL); + } + break; + default: + break; + } + } + } else { + log_error("SDP attribute value buffer size exceeded: available %d, required %d", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); + } + break; + + case SDP_EVENT_QUERY_COMPLETE: + log_info("General query done with status %d.", sdp_event_query_complete_get_status(packet)); + break; + } +} + + void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avdtp_context_t * context){ bd_addr_t event_addr; hci_con_handle_t con_handle; @@ -297,7 +425,7 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet btstack_linked_list_t * avdtp_connections = &context->connections; btstack_linked_list_t * stream_endpoints = &context->stream_endpoints; handle_media_data = context->handle_media_data; - // printf("avdtp_packet_handler packet type %02x, event %02x \n", packet_type, hci_event_packet_get_type(packet)); + // log_info("avdtp_packet_handler packet type %02x, event %02x ", packet_type, hci_event_packet_get_type(packet)); switch (packet_type) { case L2CAP_DATA_PACKET: connection = avdtp_connection_for_l2cap_signaling_cid(channel, context); @@ -325,10 +453,10 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet if (channel == stream_endpoint->l2cap_reporting_cid){ // TODO - printf("L2CAP_DATA_PACKET for reporting: NOT IMPLEMENTED\n"); + log_info("L2CAP_DATA_PACKET for reporting: NOT IMPLEMENTED"); } else if (channel == stream_endpoint->l2cap_recovery_cid){ // TODO - printf("L2CAP_DATA_PACKET for recovery: NOT IMPLEMENTED\n"); + log_info("L2CAP_DATA_PACKET for recovery: NOT IMPLEMENTED"); } else { log_error("avdtp packet handler L2CAP_DATA_PACKET: local cid 0x%02x not found", channel); } @@ -341,16 +469,18 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet local_cid = l2cap_event_incoming_connection_get_local_cid(packet); connection = avdtp_connection_for_bd_addr(event_addr, context); - if (!connection){ + + if (!connection || connection->state == AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED){ connection = avdtp_create_connection(event_addr, context); connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED; + log_info("L2CAP_EVENT_INCOMING_CONNECTION, connection %p, state connection %d", connection, connection->state); l2cap_accept_connection(local_cid); break; } stream_endpoint = avdtp_stream_endpoint_for_seid(connection->query_seid, context); if (!stream_endpoint) { - printf("L2CAP_EVENT_INCOMING_CONNECTION no streamendpoint found for seid %d\n", connection->query_seid); + log_info("L2CAP_EVENT_INCOMING_CONNECTION no streamendpoint found for seid %d", connection->query_seid); break; } @@ -378,8 +508,8 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet con_handle = l2cap_event_channel_opened_get_handle(packet); local_cid = l2cap_event_channel_opened_get_local_cid(packet); - // printf("L2CAP_EVENT_CHANNEL_OPENED: Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", - // bd_addr_to_str(event_addr), con_handle, psm, local_cid, l2cap_event_channel_opened_get_remote_cid(packet)); + log_info("L2CAP_EVENT_CHANNEL_OPENED: Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x", + bd_addr_to_str(event_addr), con_handle, psm, local_cid, l2cap_event_channel_opened_get_remote_cid(packet)); if (psm != BLUETOOTH_PROTOCOL_AVDTP) break; @@ -392,14 +522,14 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet connection->con_handle = con_handle; connection->query_seid = 0; connection->state = AVDTP_SIGNALING_CONNECTION_OPENED; - printf(" -> AVDTP_SIGNALING_CONNECTION_OPENED, connection %p\n", connection); - avdtp_signaling_emit_connection_established(context->avdtp_callback, con_handle, event_addr, 0); + log_info(" -> AVDTP_SIGNALING_CONNECTION_OPENED, connection %p", connection); + avdtp_signaling_emit_connection_established(context->avdtp_callback, connection->l2cap_signaling_cid, event_addr, 0); break; } stream_endpoint = avdtp_stream_endpoint_for_seid(connection->query_seid, context); if (!stream_endpoint){ - printf("L2CAP_EVENT_CHANNEL_OPENED: stream_endpoint not found"); + log_info("L2CAP_EVENT_CHANNEL_OPENED: stream_endpoint not found"); return; } stream_endpoint_state_machine(connection, stream_endpoint, HCI_EVENT_PACKET, L2CAP_EVENT_CHANNEL_OPENED, packet, size, context); @@ -409,10 +539,10 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet // data: event (8), len(8), channel (16) local_cid = l2cap_event_channel_closed_get_local_cid(packet); connection = avdtp_connection_for_l2cap_signaling_cid(local_cid, context); - printf(" -> L2CAP_EVENT_CHANNEL_CLOSED signaling cid 0x%0x\n", local_cid); + log_info(" -> L2CAP_EVENT_CHANNEL_CLOSED signaling cid 0x%0x", local_cid); if (connection){ - printf(" -> AVDTP_STREAM_ENDPOINT_IDLE, connection closed\n"); + log_info(" -> AVDTP_STREAM_ENDPOINT_IDLE, connection closed"); btstack_linked_list_remove(avdtp_connections, (btstack_linked_item_t*) connection); btstack_linked_list_iterator_t it; btstack_linked_list_iterator_init(&it, stream_endpoints); @@ -437,68 +567,12 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet if (!connection) { stream_endpoint = avdtp_stream_endpoint_for_l2cap_cid(channel, context); if (!stream_endpoint->connection) break; - if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING_W2_SEND){ - stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; - //send sbc - uint8_t rtp_version = 2; - uint8_t padding = 0; - uint8_t extension = 0; - uint8_t csrc_count = 0; - uint8_t marker = 0; - uint8_t payload_type = 0x60; - uint16_t sequence_number = stream_endpoint->sequence_number; - uint32_t timestamp = btstack_run_loop_get_time_ms(); - uint32_t ssrc = 0x11223344; - - // rtp header (min size 12B) - int pos = 0; - int mtu = l2cap_get_remote_mtu_for_local_cid(stream_endpoint->l2cap_media_cid); - - uint8_t media_packet[mtu]; - media_packet[pos++] = (rtp_version << 6) | (padding << 5) | (extension << 4) | csrc_count; - media_packet[pos++] = (marker << 1) | payload_type; - big_endian_store_16(media_packet, pos, sequence_number); - pos += 2; - big_endian_store_32(media_packet, pos, timestamp); - pos += 4; - big_endian_store_32(media_packet, pos, ssrc); // only used for multicast - pos += 4; - - // media payload - // sbc_header (size 1B) - uint8_t sbc_header_index = pos; - pos++; - uint8_t fragmentation = 0; - uint8_t starting_packet = 0; // set to 1 for the first packet of a fragmented SBC frame - uint8_t last_packet = 0; // set to 1 for the last packet of a fragmented SBC frame - uint8_t num_frames = 0; - - uint32_t total_sbc_bytes_read = 0; - uint8_t sbc_frame_size = 0; - // payload - while (mtu - 13 - total_sbc_bytes_read >= 120 && btstack_ring_buffer_bytes_available(&stream_endpoint->sbc_ring_buffer)){ - uint32_t number_of_bytes_read = 0; - btstack_ring_buffer_read(&stream_endpoint->sbc_ring_buffer, &sbc_frame_size, 1, &number_of_bytes_read); - btstack_ring_buffer_read(&stream_endpoint->sbc_ring_buffer, media_packet + pos, sbc_frame_size, &number_of_bytes_read); - pos += sbc_frame_size; - total_sbc_bytes_read += sbc_frame_size; - num_frames++; - // printf("send sbc frame: timestamp %d, seq. nr %d\n", timestamp, stream_endpoint->sequence_number); - } - media_packet[sbc_header_index] = (fragmentation << 7) | (starting_packet << 6) | (last_packet << 5) | num_frames; - stream_endpoint->sequence_number++; - l2cap_send(stream_endpoint->l2cap_media_cid, media_packet, pos); - if (btstack_ring_buffer_bytes_available(&stream_endpoint->sbc_ring_buffer)){ - stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING_W2_SEND; - l2cap_request_can_send_now_event(stream_endpoint->l2cap_media_cid); - } - } connection = stream_endpoint->connection; } avdtp_handle_can_send_now(connection, channel, context); break; default: - printf("unknown HCI event type %02x\n", hci_event_packet_get_type(packet)); + log_info("unknown HCI event type %02x", hci_event_packet_get_type(packet)); break; } break; @@ -509,8 +583,8 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet } } -void avdtp_disconnect(uint16_t con_handle, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_disconnect(uint16_t avdtp_cid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection) return; if (connection->state == AVDTP_SIGNALING_CONNECTION_IDLE) return; if (connection->state == AVDTP_SIGNALING_CONNECTION_W4_L2CAP_DISCONNECTED) return; @@ -519,21 +593,25 @@ void avdtp_disconnect(uint16_t con_handle, avdtp_context_t * context){ avdtp_request_can_send_now_self(connection, connection->l2cap_signaling_cid); } -void avdtp_open_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_open_stream(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - printf("avdtp_media_connect: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_media_connect: no connection for signaling cid 0x%02x found", avdtp_cid); return; } - - if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { - printf("avdtp_media_connect: wrong connection state %d\n", connection->state); + if (avdtp_find_remote_sep(connection, acp_seid) == 0xFF){ + log_error("avdtp_media_connect: no remote sep for seid %d found", acp_seid); return; } - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(acp_seid, context); + if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { + log_error("avdtp_media_connect: wrong connection state %d", connection->state); + return; + } + + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(int_seid, context); if (!stream_endpoint) { - printf("avdtp_media_connect: no stream_endpoint with acp seid %d found\n", acp_seid); + log_error("avdtp_media_connect: no stream_endpoint with seid %d found", int_seid); return; } @@ -548,103 +626,79 @@ void avdtp_open_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_start_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); - if (!connection){ - printf("avdtp_media_connect: no connection for handle 0x%02x found\n", con_handle); +void avdtp_start_stream(uint8_t int_seid, avdtp_context_t * context){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(int_seid, context); + if (!stream_endpoint) { + log_error("avdtp_start_stream: no stream_endpoint with seid %d found", int_seid); return; } - - if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { - printf("avdtp_media_connect: wrong connection state %d\n", connection->state); + avdtp_connection_t * connection = stream_endpoint->connection; + if (!connection){ + log_error("avdtp_start_stream: no connection for seid %d found",stream_endpoint->sep.seid); return; } - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(acp_seid, context); - if (!stream_endpoint) { - printf("avdtp_media_connect: no stream_endpoint with acp_seid %d found\n", acp_seid); - return; - } - if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX) return; - if (stream_endpoint->state < AVDTP_STREAM_ENDPOINT_OPENED) return; - connection->initiator_transaction_label++; - connection->acp_seid = acp_seid; - connection->int_seid = stream_endpoint->sep.seid; - stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_STREAMING_START; + if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX || stream_endpoint->start_stream) return; + stream_endpoint->start_stream = 1; + connection->int_seid = int_seid; avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_stop_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); - if (!connection){ - printf("avdtp_stop_stream: no connection for handle 0x%02x found\n", con_handle); - return; - } - if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { - printf("avdtp_stop_stream: wrong connection state %d\n", connection->state); - return; - } - - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(acp_seid, context); +void avdtp_stop_stream(uint8_t int_seid, avdtp_context_t * context){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(int_seid, context); if (!stream_endpoint) { - printf("avdtp_stop_stream: no stream_endpoint with acp seid %d found\n", acp_seid); + log_error("avdtp_stop_stream: no stream_endpoint with seid %d found", int_seid); return; } - if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX) return; - switch (stream_endpoint->state){ - case AVDTP_STREAM_ENDPOINT_OPENED: - case AVDTP_STREAM_ENDPOINT_STREAMING: - printf(" AVDTP_INITIATOR_W2_STREAMING_STOP \n"); - connection->initiator_transaction_label++; - connection->acp_seid = acp_seid; - connection->int_seid = stream_endpoint->sep.seid; - stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_STREAMING_STOP; - avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); - break; - default: - break; + avdtp_connection_t * connection = stream_endpoint->connection; + if (!connection){ + log_error("avdtp_stop_stream: no connection for seid %d found",stream_endpoint->sep.seid); + return; } + if (stream_endpoint->remote_sep_index == 0xFF || stream_endpoint->stop_stream) return; + stream_endpoint->stop_stream = 1; + connection->int_seid = int_seid; + avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_abort_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); - if (!connection){ - printf("avdtp_abort_stream: no connection for handle 0x%02x found\n", con_handle); - return; - } - - if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) { - printf("avdtp_abort_stream: wrong connection state %d\n", connection->state); - return; - } - - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(acp_seid, context); +void avdtp_abort_stream(uint8_t int_seid, avdtp_context_t * context){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(int_seid, context); if (!stream_endpoint) { - printf("avdtp_abort_stream: no stream_endpoint for seid %d found\n", acp_seid); + log_error("avdtp_abort_stream: no stream_endpoint with seid %d found", int_seid); return; } - if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX) return; - switch (stream_endpoint->state){ - case AVDTP_STREAM_ENDPOINT_CONFIGURED: - case AVDTP_STREAM_ENDPOINT_CLOSING: - case AVDTP_STREAM_ENDPOINT_OPENED: - case AVDTP_STREAM_ENDPOINT_STREAMING: - printf(" AVDTP_INITIATOR_W2_STREAMING_ABORT \n"); - connection->initiator_transaction_label++; - connection->acp_seid = acp_seid; - connection->int_seid = stream_endpoint->sep.seid; - stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_STREAMING_ABORT; - avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); - break; - default: - break; + avdtp_connection_t * connection = stream_endpoint->connection; + + if (!connection){ + log_error("avdtp_abort_stream: no connection for seid %d found",stream_endpoint->sep.seid); + return; } + if (stream_endpoint->remote_sep_index == 0xFF || stream_endpoint->abort_stream) return; + stream_endpoint->abort_stream = 1; + avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_discover_stream_endpoints(uint16_t con_handle, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_suspend_stream(uint8_t int_seid, avdtp_context_t * context){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_with_seid(int_seid, context); + if (!stream_endpoint) { + log_error("avdtp_abort_stream: no stream_endpoint with seid %d found", int_seid); + return; + } + avdtp_connection_t * connection = stream_endpoint->connection; if (!connection){ - printf("avdtp_discover_stream_endpoints: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_abort_stream: no connection for seid %d found",stream_endpoint->sep.seid); + return; + } + if (stream_endpoint->remote_sep_index == 0xFF || stream_endpoint->suspend_stream) return; + stream_endpoint->suspend_stream = 1; + connection->int_seid = int_seid; + avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); +} + +void avdtp_discover_stream_endpoints(uint16_t avdtp_cid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); + if (!connection){ + log_error("avdtp_discover_stream_endpoints: no connection for signaling cid 0x%02x found", avdtp_cid); return; } if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; @@ -656,16 +710,16 @@ void avdtp_discover_stream_endpoints(uint16_t con_handle, avdtp_context_t * cont avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); break; default: - printf("avdtp_discover_stream_endpoints: wrong state\n"); + log_error("avdtp_discover_stream_endpoints: wrong state"); break; } } -void avdtp_get_capabilities(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - printf("avdtp_get_capabilities: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_get_capabilities: no connection for signaling cid 0x%02x found", avdtp_cid); return; } if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; @@ -677,10 +731,10 @@ void avdtp_get_capabilities(uint16_t con_handle, uint8_t acp_seid, avdtp_context } -void avdtp_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - printf("avdtp_get_all_capabilities: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_get_all_capabilities: no connection for signaling cid 0x%02x found", avdtp_cid); return; } if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; @@ -691,10 +745,10 @@ void avdtp_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid, avdtp_con avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_get_configuration(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - printf("avdtp_get_configuration: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_get_configuration: no connection for signaling cid 0x%02x found", avdtp_cid); return; } if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; @@ -705,10 +759,10 @@ void avdtp_get_configuration(uint16_t con_handle, uint8_t acp_seid, avdtp_contex avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - log_error("avdtp_set_configuration: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_set_configuration: no connection for signaling cid 0x%02x found", avdtp_cid); return; } if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; @@ -716,55 +770,181 @@ void avdtp_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_ avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(int_seid, context); if (!stream_endpoint) { - log_error("avdtp_set_configuration: no initiator stream endpoint for seid %d\n", int_seid); + log_error("avdtp_set_configuration: no initiator stream endpoint for seid %d", int_seid); return; } - // printf("avdtp_set_configuration int seid %d, acp seid %d\n", int_seid, acp_seid); connection->initiator_transaction_label++; connection->acp_seid = acp_seid; connection->int_seid = int_seid; - connection->remote_capabilities_bitmap = configured_services_bitmap; - connection->remote_capabilities = configuration; + stream_endpoint->remote_capabilities_bitmap = configured_services_bitmap; + stream_endpoint->remote_capabilities = configuration; stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_SET_CONFIGURATION; avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +void avdtp_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - printf("avdtp_reconfigure: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_reconfigure: no connection for signaling cid 0x%02x found", avdtp_cid); return; } //TODO: if opened only app capabilities, enable reconfigure for not opened if (connection->state < AVDTP_SIGNALING_CONNECTION_OPENED) return; if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(acp_seid, context); - if (!stream_endpoint) return; - if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX) return; + + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(int_seid, context); + if (!stream_endpoint) { + log_error("avdtp_reconfigure: no initiator stream endpoint for seid %d", int_seid); + return; + } + + if (stream_endpoint->remote_sep_index == 0xFF){ + log_error("avdtp_reconfigure: no associated remote sep"); + return; + } + connection->initiator_transaction_label++; connection->acp_seid = acp_seid; connection->int_seid = stream_endpoint->sep.seid; - connection->remote_capabilities_bitmap = configured_services_bitmap; - connection->remote_capabilities = configuration; + stream_endpoint->remote_capabilities_bitmap = configured_services_bitmap; + stream_endpoint->remote_capabilities = configuration; stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID; avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); } -void avdtp_suspend(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context){ - avdtp_connection_t * connection = avdtp_connection_for_con_handle(con_handle, context); +uint8_t avdtp_remote_seps_num(uint16_t avdtp_cid, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); if (!connection){ - printf("avdtp_suspend: no connection for handle 0x%02x found\n", con_handle); + log_error("avdtp_suspend: no connection for signaling cid 0x%02x found", avdtp_cid); + return 0; + } + return connection->remote_seps_num; +} + +avdtp_sep_t * avdtp_remote_sep(uint16_t avdtp_cid, uint8_t index, avdtp_context_t * context){ + avdtp_connection_t * connection = avdtp_connection_for_l2cap_signaling_cid(avdtp_cid, context); + if (!connection){ + log_error("avdtp_suspend: no connection for signaling cid 0x%02x found", avdtp_cid); + return NULL; + } + return &connection->remote_seps[index]; +} + +void avdtp_initialize_sbc_configuration_storage(avdtp_stream_endpoint_t * stream_endpoint, uint8_t * config_storage, uint16_t storage_size, uint8_t * packet, uint16_t packet_size){ + UNUSED(packet_size); + if (storage_size < 4) { + log_error("storage must have 4 bytes"); return; } - if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; - if (connection->initiator_connection_state != AVDTP_SIGNALING_CONNECTION_INITIATOR_IDLE) return; - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(acp_seid, context); - if (!stream_endpoint) return; - if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX) return; - connection->initiator_transaction_label++; - connection->acp_seid = acp_seid; - connection->int_seid = stream_endpoint->sep.seid; - stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W2_SUSPEND_STREAM_WITH_SEID; - avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); + uint8_t sampling_frequency = avdtp_choose_sbc_sampling_frequency(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_sampling_frequency_bitmap(packet)); + uint8_t channel_mode = avdtp_choose_sbc_channel_mode(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_channel_mode_bitmap(packet)); + uint8_t block_length = avdtp_choose_sbc_block_length(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_block_length_bitmap(packet)); + uint8_t subbands = avdtp_choose_sbc_subbands(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_subbands_bitmap(packet)); + + uint8_t allocation_method = avdtp_choose_sbc_allocation_method(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_allocation_method_bitmap(packet)); + uint8_t max_bitpool_value = avdtp_choose_sbc_max_bitpool_value(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_max_bitpool_value(packet)); + uint8_t min_bitpool_value = avdtp_choose_sbc_min_bitpool_value(stream_endpoint, avdtp_subevent_signaling_media_codec_sbc_capability_get_min_bitpool_value(packet)); + + config_storage[0] = (sampling_frequency << 4) | channel_mode; + config_storage[1] = (block_length << 4) | (subbands << 2) | allocation_method; + config_storage[2] = min_bitpool_value; + config_storage[3] = max_bitpool_value; + + stream_endpoint->remote_configuration_bitmap = store_bit16(stream_endpoint->remote_configuration_bitmap, AVDTP_MEDIA_CODEC, 1); + stream_endpoint->remote_configuration.media_codec.media_type = AVDTP_AUDIO; + stream_endpoint->remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC; + stream_endpoint->remote_configuration.media_codec.media_codec_information_len = storage_size; + stream_endpoint->remote_configuration.media_codec.media_codec_information = config_storage; } + +uint8_t avdtp_choose_sbc_channel_mode(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_channel_mode_bitmap){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + uint8_t channel_mode_bitmap = (media_codec[0] & 0x0F) & remote_channel_mode_bitmap; + + uint8_t channel_mode = AVDTP_SBC_STEREO; + if (channel_mode_bitmap & AVDTP_SBC_JOINT_STEREO){ + channel_mode = AVDTP_SBC_JOINT_STEREO; + } else if (channel_mode_bitmap & AVDTP_SBC_STEREO){ + channel_mode = AVDTP_SBC_STEREO; + } else if (channel_mode_bitmap & AVDTP_SBC_DUAL_CHANNEL){ + channel_mode = AVDTP_SBC_DUAL_CHANNEL; + } else if (channel_mode_bitmap & AVDTP_SBC_MONO){ + channel_mode = AVDTP_SBC_MONO; + } + return channel_mode; +} + +uint8_t avdtp_choose_sbc_allocation_method(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_allocation_method_bitmap){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + uint8_t allocation_method_bitmap = (media_codec[1] & 0x03) & remote_allocation_method_bitmap; + + uint8_t allocation_method = AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS; + if (allocation_method_bitmap & AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS){ + allocation_method = AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS; + } else if (allocation_method_bitmap & AVDTP_SBC_ALLOCATION_METHOD_SNR){ + allocation_method = AVDTP_SBC_ALLOCATION_METHOD_SNR; + } + return allocation_method; +} + +uint8_t avdtp_stream_endpoint_seid(avdtp_stream_endpoint_t * stream_endpoint){ + if (!stream_endpoint) return 0; + return stream_endpoint->sep.seid; +} +uint8_t avdtp_choose_sbc_subbands(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_subbands_bitmap){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + uint8_t subbands_bitmap = ((media_codec[1] >> 2) & 0x03) & remote_subbands_bitmap; + + uint8_t subbands = AVDTP_SBC_SUBBANDS_8; + if (subbands_bitmap & AVDTP_SBC_SUBBANDS_8){ + subbands = AVDTP_SBC_SUBBANDS_8; + } else if (subbands_bitmap & AVDTP_SBC_SUBBANDS_4){ + subbands = AVDTP_SBC_SUBBANDS_4; + } + return subbands; +} + +uint8_t avdtp_choose_sbc_block_length(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_block_length_bitmap){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + uint8_t block_length_bitmap = (media_codec[1] >> 4) & remote_block_length_bitmap; + + uint8_t block_length = AVDTP_SBC_BLOCK_LENGTH_16; + if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_16){ + block_length = AVDTP_SBC_BLOCK_LENGTH_16; + } else if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_12){ + block_length = AVDTP_SBC_BLOCK_LENGTH_12; + } else if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_8){ + block_length = AVDTP_SBC_BLOCK_LENGTH_8; + } else if (block_length_bitmap & AVDTP_SBC_BLOCK_LENGTH_4){ + block_length = AVDTP_SBC_BLOCK_LENGTH_4; + } + return block_length; +} + +uint8_t avdtp_choose_sbc_sampling_frequency(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_sampling_frequency_bitmap){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + uint8_t sampling_frequency_bitmap = (media_codec[0] >> 4) & remote_sampling_frequency_bitmap; + + uint8_t sampling_frequency = AVDTP_SBC_44100; + if (sampling_frequency_bitmap & AVDTP_SBC_48000){ + sampling_frequency = AVDTP_SBC_48000; + } else if (sampling_frequency_bitmap & AVDTP_SBC_44100){ + sampling_frequency = AVDTP_SBC_44100; + } else if (sampling_frequency_bitmap & AVDTP_SBC_32000){ + sampling_frequency = AVDTP_SBC_32000; + } else if (sampling_frequency_bitmap & AVDTP_SBC_16000){ + sampling_frequency = AVDTP_SBC_16000; + } + return sampling_frequency; +} + +uint8_t avdtp_choose_sbc_max_bitpool_value(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_max_bitpool_value){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + return btstack_min(media_codec[3], remote_max_bitpool_value); +} + +uint8_t avdtp_choose_sbc_min_bitpool_value(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_min_bitpool_value){ + uint8_t * media_codec = stream_endpoint->sep.capabilities.media_codec.media_codec_information; + return btstack_max(media_codec[2], remote_min_bitpool_value); +} \ No newline at end of file diff --git a/src/classic/avdtp.h b/src/classic/avdtp.h index 45fb42d2c..496bf7015 100644 --- a/src/classic/avdtp.h +++ b/src/classic/avdtp.h @@ -60,16 +60,6 @@ extern "C" { #endif #define MAX_NUM_SEPS 10 - -// protocols -#define PSM_AVCTP BLUETOOTH_PROTOCOL_AVCTP -#define PSM_AVDTP BLUETOOTH_PROTOCOL_AVDTP - -// service classes -#define AUDIO_SOURCE_GROUP 0x110A -#define AUDIO_SINK_GROUP 0x110B -#define ADVANCED_AUDIO_DISTRIBUTION 0X110D - #define MAX_CSRC_NUM 15 // Supported Features @@ -185,9 +175,10 @@ typedef struct { avdtp_media_type_t media_type; avdtp_media_codec_type_t media_codec_type; uint16_t media_codec_information_len; - const uint8_t * media_codec_information; + uint8_t * media_codec_information; } adtvp_media_codec_capabilities_t; + typedef struct { uint16_t cp_type; uint16_t cp_type_value_len; @@ -292,7 +283,6 @@ typedef enum { AVDTP_STREAM_ENDPOINT_OPENED, AVDTP_STREAM_ENDPOINT_STREAMING, - AVDTP_STREAM_ENDPOINT_STREAMING_W2_SEND, AVDTP_STREAM_ENDPOINT_CLOSING, AVDTP_STREAM_ENDPOINT_ABORTING, @@ -307,8 +297,6 @@ typedef enum { AVDTP_INITIATOR_W2_OPEN_STREAM, - AVDTP_INITIATOR_W2_STREAMING_START, - AVDTP_INITIATOR_W2_STREAMING_STOP, AVDTP_INITIATOR_W2_STREAMING_ABORT, AVDTP_INITIATOR_FRAGMENTATED_COMMAND, AVDTP_INITIATOR_W4_ANSWER @@ -349,6 +337,7 @@ typedef struct { typedef enum { AVDTP_SIGNALING_CONNECTION_IDLE, + AVDTP_SIGNALING_W4_SDP_QUERY_COMPLETE, AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED, AVDTP_SIGNALING_CONNECTION_OPENED, AVDTP_SIGNALING_CONNECTION_W4_L2CAP_DISCONNECTED @@ -389,6 +378,7 @@ typedef struct { bd_addr_t remote_addr; hci_con_handle_t con_handle; + uint16_t l2cap_signaling_cid; avdtp_service_mode_t service_mode; @@ -408,9 +398,6 @@ typedef struct { uint8_t int_seid; uint8_t acp_seid; - avdtp_capabilities_t remote_capabilities; - uint16_t remote_capabilities_bitmap; - uint8_t wait_to_send_acceptor; uint8_t wait_to_send_initiator; uint8_t wait_to_send_self; @@ -421,14 +408,38 @@ typedef struct { uint8_t reject_service_category; avdtp_signal_identifier_t reject_signal_identifier; uint8_t error_code; + + // store configurations with remote seps + avdtp_sep_t remote_seps[MAX_NUM_SEPS]; + uint8_t remote_seps_num; } avdtp_connection_t; +typedef enum { + A2DP_IDLE, + A2DP_CONNECTED, + A2DP_W2_DISCOVER_SEPS, + A2DP_W2_GET_CAPABILITIES, + A2DP_W2_GET_ALL_CAPABILITIES, + A2DP_W2_SET_CONFIGURATION, + A2DP_W4_GET_CONFIGURATION, + A2DP_W4_SET_CONFIGURATION, + A2DP_W2_SUSPEND_STREAM_WITH_SEID, + A2DP_W2_RECONFIGURE_WITH_SEID, + A2DP_W2_OPEN_STREAM_WITH_SEID, + A2DP_W4_OPEN_STREAM_WITH_SEID, + A2DP_W2_START_STREAM_WITH_SEID, + A2DP_W2_ABORT_STREAM_WITH_SEID, + A2DP_W2_STOP_STREAM_WITH_SEID, + A2DP_W2_GET_CONFIGURATION, + A2DP_STREAMING_OPENED +} a2dp_state_t; typedef struct avdtp_stream_endpoint { btstack_linked_item_t item; // original capabilities avdtp_sep_t sep; + hci_con_handle_t media_con_handle; uint16_t l2cap_media_cid; uint16_t l2cap_reporting_cid; uint16_t l2cap_recovery_cid; @@ -436,27 +447,26 @@ typedef struct avdtp_stream_endpoint { avdtp_stream_endpoint_state_t state; avdtp_acceptor_stream_endpoint_state_t acceptor_config_state; avdtp_initiator_stream_endpoint_state_t initiator_config_state; - + a2dp_state_t a2dp_state; // active connection avdtp_connection_t * connection; - // store configurations with remote seps - avdtp_sep_t remote_seps[MAX_NUM_SEPS]; - uint8_t remote_seps_num; - // currently active remote seid uint8_t remote_sep_index; + avdtp_capabilities_t remote_capabilities; + uint16_t remote_capabilities_bitmap; + + uint16_t remote_configuration_bitmap; + avdtp_capabilities_t remote_configuration; // register request for media L2cap connection release uint8_t media_disconnect; uint8_t media_connect; - - btstack_timer_source_t fill_audio_ring_buffer_timer; - uint32_t time_audio_data_sent; // ms - uint32_t acc_num_missed_samples; - btstack_sbc_encoder_state_t sbc_encoder_state; - uint16_t sequence_number; + uint8_t start_stream; + uint8_t stop_stream; + uint8_t send_stream; + uint8_t abort_stream; + uint8_t suspend_stream; - btstack_ring_buffer_t audio_ring_buffer; - btstack_ring_buffer_t sbc_ring_buffer; + uint16_t sequence_number; } avdtp_stream_endpoint_t; typedef struct { @@ -464,6 +474,7 @@ typedef struct { btstack_linked_list_t stream_endpoints; uint16_t stream_endpoints_id_counter; btstack_packet_handler_t avdtp_callback; + btstack_packet_handler_t a2dp_callback; void (*handle_media_data)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size); btstack_packet_handler_t packet_handler; } avdtp_context_t; @@ -474,7 +485,7 @@ void avdtp_register_delay_reporting_category(avdtp_stream_endpoint_t * stream_en void avdtp_register_recovery_category(avdtp_stream_endpoint_t * stream_endpoint, uint8_t maximum_recovery_window_size, uint8_t maximum_number_media_packets); void avdtp_register_content_protection_category(avdtp_stream_endpoint_t * stream_endpoint, uint16_t cp_type, const uint8_t * cp_type_value, uint8_t cp_type_value_len); void avdtp_register_header_compression_category(avdtp_stream_endpoint_t * stream_endpoint, uint8_t back_ch, uint8_t media, uint8_t recovery); -void avdtp_register_media_codec_category(avdtp_stream_endpoint_t * stream_endpoint, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len); +void avdtp_register_media_codec_category(avdtp_stream_endpoint_t * stream_endpoint, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, uint8_t * media_codec_info, uint16_t media_codec_info_len); void avdtp_register_multiplexing_category(avdtp_stream_endpoint_t * stream_endpoint, uint8_t fragmentation); void avdtp_handle_can_send_now(avdtp_connection_t * connection, uint16_t l2cap_cid, avdtp_context_t * context); @@ -482,21 +493,34 @@ void avdtp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet avdtp_connection_t * avdtp_create_connection(bd_addr_t remote_addr, avdtp_context_t * context); avdtp_stream_endpoint_t * avdtp_create_stream_endpoint(avdtp_sep_type_t sep_type, avdtp_media_type_t media_type, avdtp_context_t * context); -void avdtp_disconnect(uint16_t con_handle, avdtp_context_t * context); -void avdtp_open_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_start_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_stop_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_abort_stream(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_discover_stream_endpoints(uint16_t con_handle, avdtp_context_t * context); -void avdtp_get_capabilities(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_get_configuration(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); -void avdtp_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context); -void avdtp_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context); -void avdtp_suspend(uint16_t con_handle, uint8_t acp_seid, avdtp_context_t * context); +void avdtp_connect(bd_addr_t remote, avdtp_sep_type_t query_role, avdtp_context_t * context); +void avdtp_disconnect(uint16_t avdtp_cid, avdtp_context_t * context); +void avdtp_open_stream(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, avdtp_context_t * context); +void avdtp_start_stream(uint8_t int_seid, avdtp_context_t * context); +void avdtp_stop_stream (uint8_t int_seid, avdtp_context_t * context); +void avdtp_abort_stream(uint8_t int_seid, avdtp_context_t * context); +void avdtp_suspend_stream(uint8_t int_seid, avdtp_context_t * context); -void avdtp_source_stream_data_start(uint16_t con_handle); -void avdtp_source_stream_data_stop(uint16_t con_handle); +void avdtp_discover_stream_endpoints(uint16_t avdtp_cid, avdtp_context_t * context); +void avdtp_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid, avdtp_context_t * context); +void avdtp_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid, avdtp_context_t * context); +void avdtp_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid, avdtp_context_t * context); +void avdtp_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context); +void avdtp_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration, avdtp_context_t * context); +uint8_t avdtp_remote_seps_num(uint16_t avdtp_cid, avdtp_context_t * context); +avdtp_sep_t * avdtp_remote_sep(uint16_t avdtp_cid, uint8_t index, avdtp_context_t * context); + +void avdtp_initialize_sbc_configuration_storage(avdtp_stream_endpoint_t * stream_endpoint, uint8_t * config_storage, uint16_t storage_size, uint8_t * packet, uint16_t packet_size); +uint8_t avdtp_choose_sbc_channel_mode(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_channel_mode_bitmap); +uint8_t avdtp_choose_sbc_allocation_method(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_allocation_method_bitmap); + +uint8_t avdtp_choose_sbc_subbands(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_subbands_bitmap); +uint8_t avdtp_choose_sbc_block_length(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_block_length_bitmap); +uint8_t avdtp_choose_sbc_sampling_frequency(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_sampling_frequency_bitmap); +uint8_t avdtp_choose_sbc_max_bitpool_value(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_max_bitpool_value); +uint8_t avdtp_choose_sbc_min_bitpool_value(avdtp_stream_endpoint_t * stream_endpoint, uint8_t remote_min_bitpool_value); + +uint8_t avdtp_stream_endpoint_seid(avdtp_stream_endpoint_t * stream_endpoint); #if defined __cplusplus } diff --git a/src/classic/avdtp_acceptor.c b/src/classic/avdtp_acceptor.c index d2c46b8af..799695ce8 100644 --- a/src/classic/avdtp_acceptor.c +++ b/src/classic/avdtp_acceptor.c @@ -86,7 +86,7 @@ static int avdtp_acceptor_validate_msg_length(avdtp_signal_identifier_t signal_i void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t * packet, uint16_t size, int offset, avdtp_context_t * context){ avdtp_stream_endpoint_t * stream_endpoint; connection->acceptor_transaction_label = connection->signaling_packet.transaction_label; - + if (!avdtp_acceptor_validate_msg_length(connection->signaling_packet.signal_identifier, size)) { connection->error_code = BAD_LENGTH; connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; @@ -98,7 +98,7 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t switch (connection->signaling_packet.signal_identifier){ case AVDTP_SI_DISCOVER: if (connection->state != AVDTP_SIGNALING_CONNECTION_OPENED) return; - printf(" ACP: AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS\n"); + log_info(" ACP: AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS"); connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_ANSWER_DISCOVER_SEPS; avdtp_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); return; @@ -114,7 +114,7 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t connection->query_seid = packet[offset++] >> 2; stream_endpoint = avdtp_stream_endpoint_with_seid(connection->query_seid, context); if (!stream_endpoint){ - printf(" ACP: cmd %d - RESPONSE REJECT\n", connection->signaling_packet.signal_identifier); + log_info(" ACP: cmd %d - RESPONSE REJECT", connection->signaling_packet.signal_identifier); connection->error_code = BAD_ACP_SEID; if (connection->signaling_packet.signal_identifier == AVDTP_SI_OPEN){ connection->error_code = BAD_STATE; @@ -133,18 +133,18 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t case AVDTP_SI_SUSPEND:{ int i; - printf(" ACP: AVDTP_SI_SUSPEND seids: "); + log_info(" ACP: AVDTP_SI_SUSPEND seids: "); connection->num_suspended_seids = 0; for (i = offset; i < size; i++){ connection->suspended_seids[connection->num_suspended_seids] = packet[i] >> 2; offset++; - printf("%d, \n", connection->suspended_seids[connection->num_suspended_seids]); + log_info("%d, ", connection->suspended_seids[connection->num_suspended_seids]); connection->num_suspended_seids++; } if (connection->num_suspended_seids == 0) { - printf(" ACP: CATEGORY RESPONSE REJECT BAD_ACP_SEID\n"); + log_info(" ACP: CATEGORY RESPONSE REJECT BAD_ACP_SEID"); connection->error_code = BAD_ACP_SEID; connection->reject_service_category = connection->query_seid; connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; @@ -156,7 +156,7 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t connection->query_seid = connection->suspended_seids[0]; stream_endpoint = avdtp_stream_endpoint_with_seid(connection->query_seid, context); if (!stream_endpoint){ - printf(" ACP: stream_endpoint not found, CATEGORY RESPONSE REJECT BAD_ACP_SEID\n"); + log_info(" ACP: stream_endpoint not found, CATEGORY RESPONSE REJECT BAD_ACP_SEID"); connection->error_code = BAD_ACP_SEID; connection->reject_service_category = connection->query_seid; connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; @@ -170,7 +170,7 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t default: connection->acceptor_connection_state = AVDTP_SIGNALING_CONNECTION_ACCEPTOR_W2_GENERAL_REJECT_WITH_ERROR_CODE; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; - printf("AVDTP_CMD_MSG signal %d not implemented, general reject\n", connection->signaling_packet.signal_identifier); + log_info("AVDTP_CMD_MSG signal %d not implemented, general reject", connection->signaling_packet.signal_identifier); avdtp_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); return; } @@ -189,45 +189,41 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t case AVDTP_ACCEPTOR_STREAM_CONFIG_IDLE: switch (connection->signaling_packet.signal_identifier){ case AVDTP_SI_GET_ALL_CAPABILITIES: - printf(" ACP: AVDTP_SI_GET_ALL_CAPABILITIES\n"); + log_info(" ACP: AVDTP_SI_GET_ALL_CAPABILITIES"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_GET_ALL_CAPABILITIES; break; case AVDTP_SI_GET_CAPABILITIES: - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES\n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_GET_CAPABILITIES; break; case AVDTP_SI_SET_CONFIGURATION:{ - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION \n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION "); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION; connection->reject_service_category = 0; - + stream_endpoint->connection = connection; avdtp_sep_t sep; sep.seid = connection->signaling_packet.command[offset++] >> 2; sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, connection->signaling_packet.command+offset, packet_size-offset); sep.in_use = 1; if (connection->error_code){ - printf("fire configuration parsing errors \n"); + log_info("fire configuration parsing errors "); connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; break; } // find or add sep - int i; - stream_endpoint->remote_sep_index = AVDTP_INVALID_SEP_INDEX; - for (i=0; i < stream_endpoint->remote_seps_num; i++){ - if (stream_endpoint->remote_seps[i].seid == sep.seid){ - stream_endpoint->remote_sep_index = i; - } - } - printf(" ACP .. seid %d, index %d\n", sep.seid, stream_endpoint->remote_sep_index); + + stream_endpoint->remote_sep_index = avdtp_find_remote_sep(stream_endpoint->connection, sep.seid); + log_info(" ACP .. seid %d, index %d", sep.seid, stream_endpoint->remote_sep_index); if (stream_endpoint->remote_sep_index != AVDTP_INVALID_SEP_INDEX){ - if (stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].in_use){ + if (stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index].in_use){ // reject if already configured connection->error_code = SEP_IN_USE; // find first registered category and fire the error connection->reject_service_category = 0; + int i; for (i = 1; i < 9; i++){ if (get_bit16(sep.configured_service_categories, i)){ connection->reject_service_category = i; @@ -237,29 +233,28 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; } else { - stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; - printf(" ACP: update seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index] = sep; + log_info(" ACP: update seid %d, to %p", stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); } } else { // add new - printf(" ACP: seid %d not found in %p\n", sep.seid, stream_endpoint); - stream_endpoint->remote_sep_index = stream_endpoint->remote_seps_num; - stream_endpoint->remote_seps_num++; - stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; - printf(" ACP: add seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + log_info(" ACP: seid %d not found in %p", sep.seid, stream_endpoint); + stream_endpoint->remote_sep_index = connection->remote_seps_num; + connection->remote_seps_num++; + connection->remote_seps[stream_endpoint->remote_sep_index] = sep; + log_info(" ACP: add seid %d, to %p", connection->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); } - if (get_bit16(sep.configured_service_categories, AVDTP_MEDIA_CODEC)){ switch (sep.configuration.media_codec.media_codec_type){ case AVDTP_CODEC_SBC: - avdtp_signaling_emit_media_codec_sbc_configuration(context->avdtp_callback, connection->con_handle, sep.configuration.media_codec); + avdtp_signaling_emit_media_codec_sbc_configuration(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->acp_seid, sep.configuration.media_codec); break; default: - avdtp_signaling_emit_media_codec_other_configuration(context->avdtp_callback, connection->con_handle, sep.configuration.media_codec); + avdtp_signaling_emit_media_codec_other_configuration(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->acp_seid, sep.configuration.media_codec); break; } } - avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->signaling_packet.signal_identifier, 0); + avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->signaling_packet.signal_identifier, 0); break; } case AVDTP_SI_RECONFIGURE:{ @@ -268,8 +263,8 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t avdtp_sep_t sep; sep.seid = connection->query_seid; - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE seid %d\n", sep.seid); - // printf_hexdump(connection->signaling_packet.command, packet_size); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE seid %d", sep.seid); + // log_info_hexdump(connection->signaling_packet.command, packet_size); sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, connection->signaling_packet.command+offset, packet_size-offset); @@ -281,76 +276,70 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t } // find sep or raise error - int i; - stream_endpoint->remote_sep_index = AVDTP_INVALID_SEP_INDEX; - for (i = 0; i < stream_endpoint->remote_seps_num; i++){ - if (stream_endpoint->remote_seps[i].seid == sep.seid){ - stream_endpoint->remote_sep_index = i; - } - } + stream_endpoint->remote_sep_index = avdtp_find_remote_sep(stream_endpoint->connection, sep.seid); if (stream_endpoint->remote_sep_index == AVDTP_INVALID_SEP_INDEX){ - printf(" ACP: REJECT AVDTP_SI_RECONFIGURE, BAD_ACP_SEID\n"); + log_info(" ACP: REJECT AVDTP_SI_RECONFIGURE, BAD_ACP_SEID"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; connection->error_code = BAD_ACP_SEID; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; break; } - stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; - printf(" ACP: update seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index] = sep; + log_info(" ACP: update seid %d, to %p", stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); if (get_bit16(sep.configured_service_categories, AVDTP_MEDIA_CODEC)){ switch (sep.capabilities.media_codec.media_codec_type){ case AVDTP_CODEC_SBC: - avdtp_signaling_emit_media_codec_sbc_reconfiguration(context->avdtp_callback, connection->con_handle, sep.capabilities.media_codec); + avdtp_signaling_emit_media_codec_sbc_reconfiguration(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->acp_seid, sep.capabilities.media_codec); break; default: - avdtp_signaling_emit_media_codec_other_reconfiguration(context->avdtp_callback, connection->con_handle, sep.capabilities.media_codec); + avdtp_signaling_emit_media_codec_other_reconfiguration(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->acp_seid, sep.capabilities.media_codec); break; } } - avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->signaling_packet.signal_identifier, 0); + avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->signaling_packet.signal_identifier, 0); break; } case AVDTP_SI_GET_CONFIGURATION: - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION\n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION; break; case AVDTP_SI_OPEN: if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_CONFIGURED){ - printf(" ACP: REJECT AVDTP_SI_OPEN, BAD_STATE\n"); + log_info(" ACP: REJECT AVDTP_SI_OPEN, BAD_STATE"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; connection->error_code = BAD_STATE; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; break; } - printf(" ACP: AVDTP_STREAM_ENDPOINT_W2_ANSWER_OPEN_STREAM\n"); + log_info(" ACP: AVDTP_STREAM_ENDPOINT_W2_ANSWER_OPEN_STREAM"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_OPEN_STREAM; stream_endpoint->state = AVDTP_STREAM_ENDPOINT_W4_L2CAP_FOR_MEDIA_CONNECTED; connection->query_seid = stream_endpoint->sep.seid; break; case AVDTP_SI_START: if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_OPENED){ - printf(" ACP: REJECT AVDTP_SI_START, BAD_STATE\n"); + log_info(" ACP: REJECT AVDTP_SI_START, BAD_STATE"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; connection->error_code = BAD_STATE; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; break; } - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM\n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM; break; case AVDTP_SI_CLOSE: switch (stream_endpoint->state){ case AVDTP_STREAM_ENDPOINT_OPENED: case AVDTP_STREAM_ENDPOINT_STREAMING: - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM\n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CLOSING; stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM; break; default: - printf(" ACP: AVDTP_SI_CLOSE, bad state %d \n", stream_endpoint->state); + log_info(" ACP: AVDTP_SI_CLOSE, bad state %d ", stream_endpoint->state); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; connection->error_code = BAD_STATE; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; @@ -363,12 +352,12 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t case AVDTP_STREAM_ENDPOINT_CLOSING: case AVDTP_STREAM_ENDPOINT_OPENED: case AVDTP_STREAM_ENDPOINT_STREAMING: - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM\n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM; break; default: - printf(" ACP: AVDTP_SI_ABORT, bad state %d \n", stream_endpoint->state); + log_info(" ACP: AVDTP_SI_ABORT, bad state %d ", stream_endpoint->state); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE; connection->error_code = BAD_STATE; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; @@ -376,19 +365,19 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t } break; case AVDTP_SI_SUSPEND: - printf(" entering AVDTP_SI_SUSPEND\n"); + log_info(" entering AVDTP_SI_SUSPEND"); switch (stream_endpoint->state){ case AVDTP_STREAM_ENDPOINT_OPENED: case AVDTP_STREAM_ENDPOINT_STREAMING: stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; connection->num_suspended_seids--; if (connection->num_suspended_seids <= 0){ - printf(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM\n"); + log_info(" ACP: AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM"); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM; } break; default: - printf(" ACP: AVDTP_SI_SUSPEND, bad state \n"); + log_info(" ACP: AVDTP_SI_SUSPEND, bad state "); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE; connection->error_code = BAD_STATE; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; @@ -399,7 +388,7 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t //stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_SUSPEND_STREAM; break; default: - printf(" ACP: NOT IMPLEMENTED, Reject signal_identifier %02x\n", connection->signaling_packet.signal_identifier); + log_info(" ACP: NOT IMPLEMENTED, Reject signal_identifier %02x", connection->signaling_packet.signal_identifier); stream_endpoint->acceptor_config_state = AVDTP_ACCEPTOR_W2_REJECT_UNKNOWN_CMD; connection->reject_signal_identifier = connection->signaling_packet.signal_identifier; break; @@ -410,7 +399,7 @@ void avdtp_acceptor_stream_config_subsm(avdtp_connection_t * connection, uint8_t } if (!request_to_send){ - printf(" ACP: NOT IMPLEMENTED\n"); + log_info(" ACP: NOT IMPLEMENTED"); } avdtp_request_can_send_now_acceptor(connection, connection->l2cap_signaling_cid); } @@ -488,7 +477,7 @@ void avdtp_acceptor_stream_config_subsm_run(avdtp_connection_t * connection, avd break; } if (sent){ - printf(" ACP: DONE\n"); + log_info(" ACP: DONE"); return; } @@ -517,9 +506,9 @@ void avdtp_acceptor_stream_config_subsm_run(avdtp_connection_t * connection, avd pos = avdtp_signaling_create_fragment(cid, &connection->signaling_packet, out_buffer); if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ stream_endpoint->acceptor_config_state = acceptor_config_state; - printf(" ACP: fragmented\n"); + log_info(" ACP: fragmented"); } else { - printf(" ACP:DONE\n"); + log_info(" ACP:DONE"); } l2cap_send_prepared(cid, pos); break; @@ -530,92 +519,89 @@ void avdtp_acceptor_stream_config_subsm_run(avdtp_connection_t * connection, avd pos = avdtp_signaling_create_fragment(cid, &connection->signaling_packet, out_buffer); if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ stream_endpoint->acceptor_config_state = acceptor_config_state; - printf(" ACP: fragmented\n"); + log_info(" ACP: fragmented"); } else { - printf(" ACP:DONE\n"); + log_info(" ACP:DONE"); } l2cap_send_prepared(cid, pos); break; case AVDTP_ACCEPTOR_W2_ANSWER_SET_CONFIGURATION: - printf(" ACP: DONE\n"); - printf(" -> AVDTP_STREAM_ENDPOINT_CONFIGURED\n"); + log_info(" ACP: DONE"); + log_info(" -> AVDTP_STREAM_ENDPOINT_CONFIGURED"); stream_endpoint->connection = connection; stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CONFIGURED; - // TODO: use actual config // TODO: consider reconfiguration - btstack_sbc_encoder_init(&stream_endpoint->sbc_encoder_state, SBC_MODE_STANDARD, 16, 8, 2, 44100, 53); - avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_SET_CONFIGURATION); break; case AVDTP_ACCEPTOR_W2_ANSWER_RECONFIGURE: - printf(" ACP: DONE \n"); + log_info(" ACP: DONE "); avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_RECONFIGURE); break; case AVDTP_ACCEPTOR_W2_ANSWER_GET_CONFIGURATION:{ - avdtp_sep_t sep = stream_endpoint->remote_seps[stream_endpoint->remote_sep_index]; + avdtp_sep_t sep = stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index]; avdtp_prepare_capabilities(&connection->signaling_packet, trid, sep.configured_service_categories, sep.configuration, AVDTP_SI_GET_CONFIGURATION); l2cap_reserve_packet_buffer(); out_buffer = l2cap_get_outgoing_buffer(); pos = avdtp_signaling_create_fragment(cid, &connection->signaling_packet, out_buffer); if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ stream_endpoint->acceptor_config_state = acceptor_config_state; - printf(" ACP: fragmented\n"); + log_info(" ACP: fragmented"); } else { - printf(" ACP:DONE\n"); + log_info(" ACP:DONE"); } l2cap_send_prepared(cid, pos); break; } case AVDTP_ACCEPTOR_W2_ANSWER_OPEN_STREAM: - printf(" ACP: DONE\n"); + log_info(" ACP: DONE"); avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_OPEN); break; case AVDTP_ACCEPTOR_W2_ANSWER_START_STREAM: - printf(" ACP: DONE \n"); - printf(" -> AVDTP_STREAM_ENDPOINT_STREAMING \n"); + log_info(" ACP: DONE "); + log_info(" -> AVDTP_STREAM_ENDPOINT_STREAMING "); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_START); break; case AVDTP_ACCEPTOR_W2_ANSWER_CLOSE_STREAM: - printf(" ACP: DONE\n"); + log_info(" ACP: DONE"); avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_CLOSE); break; case AVDTP_ACCEPTOR_W2_ANSWER_ABORT_STREAM: - printf(" ACP: DONE\n"); + log_info(" ACP: DONE"); avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_ABORT); break; case AVDTP_ACCEPTOR_W2_ANSWER_SUSPEND_STREAM: - printf(" ACP: DONE\n"); + log_info(" ACP: DONE"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; avdtp_acceptor_send_accept_response(cid, trid, AVDTP_SI_SUSPEND); break; case AVDTP_ACCEPTOR_W2_REJECT_UNKNOWN_CMD: status = 1; - printf(" ACP: DONE REJECT\n"); + log_info(" ACP: DONE REJECT"); connection->reject_signal_identifier = 0; avdtp_acceptor_send_response_reject(cid, reject_signal_identifier, trid); break; case AVDTP_ACCEPTOR_W2_REJECT_CATEGORY_WITH_ERROR_CODE: status = 1; - printf(" ACP: DONE REJECT CATEGORY\n"); + log_info(" ACP: DONE REJECT CATEGORY"); connection->reject_service_category = 0; avdtp_acceptor_send_response_reject_service_category(cid, reject_signal_identifier, reject_service_category, error_code, trid); break; case AVDTP_ACCEPTOR_W2_REJECT_WITH_ERROR_CODE: status = 1; - printf(" ACP: DONE REJECT\n"); + log_info(" ACP: DONE REJECT"); connection->reject_signal_identifier = 0; connection->error_code = 0; avdtp_acceptor_send_response_reject_with_error_code(cid, reject_signal_identifier, error_code, trid); break; default: status = 0; - printf(" ACP: NOT IMPLEMENTED\n"); + log_info(" ACP: NOT IMPLEMENTED"); sent = 0; } - avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->signaling_packet.signal_identifier, status); + avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->int_seid, connection->signaling_packet.signal_identifier, status); // check fragmentation if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ diff --git a/src/classic/avdtp_initiator.c b/src/classic/avdtp_initiator.c index 070bc05d3..51fc7751b 100644 --- a/src/classic/avdtp_initiator.c +++ b/src/classic/avdtp_initiator.c @@ -65,32 +65,6 @@ static int avdtp_initiator_send_signaling_cmd_with_seid(uint16_t cid, avdtp_sign return l2cap_send(cid, command, sizeof(command)); } -static void avdtp_signaling_emit_media_codec_capability(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep){ - if (get_bit16(sep.registered_service_categories, AVDTP_MEDIA_CODEC)){ - switch (sep.capabilities.media_codec.media_codec_type){ - case AVDTP_CODEC_SBC: - avdtp_signaling_emit_media_codec_sbc_capability(callback, con_handle, sep.capabilities.media_codec); - break; - default: - avdtp_signaling_emit_media_codec_other_capability(callback, con_handle, sep.capabilities.media_codec); - break; - } - } -} - -static void avdtp_signaling_emit_media_codec_configuration(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep){ - if (get_bit16(sep.registered_service_categories, AVDTP_MEDIA_CODEC)){ - switch (sep.capabilities.media_codec.media_codec_type){ - case AVDTP_CODEC_SBC: - avdtp_signaling_emit_media_codec_sbc_configuration(callback, con_handle, sep.capabilities.media_codec); - break; - default: - avdtp_signaling_emit_media_codec_other_configuration(callback, con_handle, sep.capabilities.media_codec); - break; - } - } -} - void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_t *packet, uint16_t size, int offset, avdtp_context_t * context){ int status = 0; avdtp_stream_endpoint_t * stream_endpoint = NULL; @@ -107,25 +81,23 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_ if (!stream_endpoint) return; sep.seid = connection->acp_seid; - printf(" INT: local seid %d, remote seid %d, ident %d \n", connection->int_seid, connection->acp_seid, connection->signaling_packet.signal_identifier); if (stream_endpoint->initiator_config_state != AVDTP_INITIATOR_W4_ANSWER) return; stream_endpoint->initiator_config_state = AVDTP_INITIATOR_STREAM_CONFIG_IDLE; } switch (connection->signaling_packet.message_type){ case AVDTP_RESPONSE_ACCEPT_MSG: - printf(" INT: AVDTP_RESPONSE_ACCEPT_MSG: "); switch (connection->signaling_packet.signal_identifier){ case AVDTP_SI_DISCOVER:{ - printf("AVDTP_SI_DISCOVER\n"); + log_info("AVDTP_SI_DISCOVER"); if (connection->signaling_packet.transaction_label != connection->initiator_transaction_label){ - printf(" unexpected transaction label, got %d, expected %d\n", connection->signaling_packet.transaction_label, connection->initiator_transaction_label); + log_info(" unexpected transaction label, got %d, expected %d", connection->signaling_packet.transaction_label, connection->initiator_transaction_label); status = BAD_HEADER_FORMAT; break; } if (size == 3){ - printf(" ERROR code %02x\n", packet[offset]); + log_info(" ERROR code %02x", packet[offset]); status = packet[offset]; break; } @@ -135,73 +107,90 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_ sep.seid = packet[i] >> 2; offset++; if (sep.seid < 0x01 || sep.seid > 0x3E){ - printf(" invalid sep id\n"); + log_info(" invalid sep id"); status = BAD_ACP_SEID; break; } sep.in_use = (packet[i] >> 1) & 0x01; sep.media_type = (avdtp_media_type_t)(packet[i+1] >> 4); sep.type = (avdtp_sep_type_t)((packet[i+1] >> 3) & 0x01); - avdtp_signaling_emit_sep(context->avdtp_callback, connection->con_handle, sep); + + if (avdtp_find_remote_sep(connection, sep.seid) == 0xFF){ + connection->remote_seps[connection->remote_seps_num++] = sep; + } + avdtp_signaling_emit_sep(context->avdtp_callback, connection->l2cap_signaling_cid, sep); } break; } case AVDTP_SI_GET_CAPABILITIES: case AVDTP_SI_GET_ALL_CAPABILITIES: - printf("AVDTP_SI_GET(_ALL)_CAPABILITIES\n"); + log_info("AVDTP_SI_GET(_ALL)_CAPABILITIES int %d, acp %d", connection->int_seid, connection->acp_seid); sep.registered_service_categories = avdtp_unpack_service_capabilities(connection, &sep.capabilities, packet+offset, size-offset); - avdtp_signaling_emit_media_codec_capability(context->avdtp_callback, connection->con_handle, sep); + if (get_bit16(sep.registered_service_categories, AVDTP_MEDIA_CODEC)){ + switch (sep.capabilities.media_codec.media_codec_type){ + case AVDTP_CODEC_SBC: + avdtp_signaling_emit_media_codec_sbc_capability(context->avdtp_callback, connection->l2cap_signaling_cid, connection->int_seid, connection->acp_seid, sep.capabilities.media_codec); + break; + default: + avdtp_signaling_emit_media_codec_other_capability(context->avdtp_callback, connection->l2cap_signaling_cid, connection->int_seid, connection->acp_seid, sep.capabilities.media_codec); + break; + } + } break; case AVDTP_SI_GET_CONFIGURATION: - printf("AVDTP_SI_GET_CONFIGURATION\n"); + log_info("AVDTP_SI_GET_CONFIGURATION"); sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, packet+offset, size-offset); - - avdtp_signaling_emit_media_codec_configuration(context->avdtp_callback, connection->con_handle, sep); + if (get_bit16(sep.configured_service_categories, AVDTP_MEDIA_CODEC)){ + switch (sep.configuration.media_codec.media_codec_type){ + case AVDTP_CODEC_SBC: + avdtp_signaling_emit_media_codec_sbc_configuration(context->avdtp_callback, connection->l2cap_signaling_cid, connection->int_seid, connection->acp_seid, sep.configuration.media_codec); + break; + default: + avdtp_signaling_emit_media_codec_other_configuration(context->avdtp_callback, connection->l2cap_signaling_cid, connection->int_seid, connection->acp_seid, sep.configuration.media_codec); + break; + } + } break; case AVDTP_SI_RECONFIGURE: - printf("AVDTP_SI_RECONFIGURE\n"); + log_info("AVDTP_SI_RECONFIGURE"); sep.configured_service_categories = avdtp_unpack_service_capabilities(connection, &sep.configuration, connection->signaling_packet.command+4, connection->signaling_packet.size-4); // TODO check if configuration is supported - remote_sep_index = avdtp_get_index_of_remote_stream_endpoint_with_seid(stream_endpoint, sep.seid); + remote_sep_index = avdtp_find_remote_sep(connection, sep.seid); if (remote_sep_index != 0xFF){ stream_endpoint->remote_sep_index = remote_sep_index; - stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; + connection->remote_seps[stream_endpoint->remote_sep_index] = sep; stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CONFIGURED; - printf(" INT: update seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + log_info(" INT: update seid %d, to %p", connection->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); } break; case AVDTP_SI_SET_CONFIGURATION:{ - printf("AVDTP_SI_SET_CONFIGURATION\n"); - sep.configured_service_categories = connection->remote_capabilities_bitmap; - sep.configuration = connection->remote_capabilities; + log_info("AVDTP_SI_SET_CONFIGURATION"); + sep.configured_service_categories = stream_endpoint->remote_capabilities_bitmap; + sep.configuration = stream_endpoint->remote_capabilities; sep.in_use = 1; // TODO check if configuration is supported // find or add sep - remote_sep_index = avdtp_get_index_of_remote_stream_endpoint_with_seid(stream_endpoint, sep.seid); + remote_sep_index = avdtp_find_remote_sep(connection, sep.seid); if (remote_sep_index != 0xFF){ stream_endpoint->remote_sep_index = remote_sep_index; } else { - stream_endpoint->remote_sep_index = stream_endpoint->remote_seps_num; - stream_endpoint->remote_seps_num++; + stream_endpoint->remote_sep_index = connection->remote_seps_num; + stream_endpoint->connection->remote_seps_num++; } - stream_endpoint->remote_seps[stream_endpoint->remote_sep_index] = sep; - printf(" INT: configured remote seid %d, to %p\n", stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); + connection->remote_seps[stream_endpoint->remote_sep_index] = sep; + log_info(" INT: configured remote seid %d, to %p", connection->remote_seps[stream_endpoint->remote_sep_index].seid, stream_endpoint); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CONFIGURED; - - // TODO: use actual config - // TODO: consider reconfiguration - btstack_sbc_encoder_init(&stream_endpoint->sbc_encoder_state, SBC_MODE_STANDARD, 16, 8, 2, 44100, 53); break; } case AVDTP_SI_OPEN: - printf("AVDTP_SI_OPEN\n"); + log_info("AVDTP_SI_OPEN"); if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_W2_REQUEST_OPEN_STREAM) { log_error("AVDTP_SI_OPEN in wrong stream endpoint state"); return; @@ -211,15 +200,16 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_ l2cap_create_channel(context->packet_handler, connection->remote_addr, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, NULL); return; case AVDTP_SI_START: - printf("AVDTP_SI_START\n"); + log_info("AVDTP_SI_START"); if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_OPENED) { log_error("AVDTP_SI_START in wrong stream endpoint state"); return; } + log_info("AVDTP_STREAM_ENDPOINT_STREAMING"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; break; case AVDTP_SI_SUSPEND: - printf("AVDTP_SI_SUSPEND\n"); + log_info("AVDTP_SI_SUSPEND"); if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_STREAMING) { log_error("AVDTP_SI_SUSPEND in wrong stream endpoint state"); return; @@ -227,58 +217,54 @@ void avdtp_initiator_stream_config_subsm(avdtp_connection_t * connection, uint8_ stream_endpoint->state = AVDTP_STREAM_ENDPOINT_OPENED; break; case AVDTP_SI_CLOSE: - printf("AVDTP_SI_CLOSE\n"); + log_info("AVDTP_SI_CLOSE"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_CLOSING; break; case AVDTP_SI_ABORT: - printf("AVDTP_SI_ABORT\n"); + log_info("AVDTP_SI_ABORT"); stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; break; default: status = 1; - printf(" AVDTP_RESPONSE_ACCEPT_MSG, signal %d not implemented\n", connection->signaling_packet.signal_identifier); + log_info(" AVDTP_RESPONSE_ACCEPT_MSG, signal %d not implemented", connection->signaling_packet.signal_identifier); break; - } + } + avdtp_signaling_emit_accept(context->avdtp_callback, connection->l2cap_signaling_cid, 0, connection->signaling_packet.signal_identifier, status); + connection->initiator_transaction_label++; break; case AVDTP_RESPONSE_REJECT_MSG: - printf(" AVDTP_RESPONSE_REJECT_MSG signal %d\n", connection->signaling_packet.signal_identifier); - avdtp_signaling_emit_reject(context->avdtp_callback, connection->con_handle, connection->signaling_packet.signal_identifier); + log_info(" AVDTP_RESPONSE_REJECT_MSG signal %d", connection->signaling_packet.signal_identifier); + avdtp_signaling_emit_reject(context->avdtp_callback, connection->l2cap_signaling_cid, connection->int_seid, connection->signaling_packet.signal_identifier); return; case AVDTP_GENERAL_REJECT_MSG: - printf(" AVDTP_GENERAL_REJECT_MSG signal %d\n", connection->signaling_packet.signal_identifier); - avdtp_signaling_emit_general_reject(context->avdtp_callback, connection->con_handle, connection->signaling_packet.signal_identifier); + log_info(" AVDTP_GENERAL_REJECT_MSG signal %d", connection->signaling_packet.signal_identifier); + avdtp_signaling_emit_general_reject(context->avdtp_callback, connection->l2cap_signaling_cid, connection->int_seid, connection->signaling_packet.signal_identifier); return; default: break; } - - connection->initiator_transaction_label++; - connection->int_seid = 0; - connection->acp_seid = 0; - avdtp_signaling_emit_accept(context->avdtp_callback, connection->con_handle, connection->signaling_packet.signal_identifier, status); } void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, avdtp_context_t * context){ int sent = 1; - switch (connection->initiator_connection_state){ case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS: - printf(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS\n"); + log_info(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_DISCOVER_SEPS"); connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; avdtp_initiator_send_signaling_cmd(connection->l2cap_signaling_cid, AVDTP_SI_DISCOVER, connection->initiator_transaction_label); break; case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES: - printf(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES\n"); + log_info(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CAPABILITIES"); connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_GET_CAPABILITIES, connection->initiator_transaction_label, connection->acp_seid); break; case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES: - printf(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES\n"); + log_info(" INT: AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_ALL_CAPABILITIES"); connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_GET_ALL_CAPABILITIES, connection->initiator_transaction_label, connection->acp_seid); break; case AVDTP_SIGNALING_CONNECTION_INITIATOR_W2_GET_CONFIGURATION: - printf(" INT: AVDTP_INITIATOR_W4_GET_CONFIGURATION\n"); + log_info(" INT: AVDTP_INITIATOR_W4_GET_CONFIGURATION"); connection->initiator_connection_state = AVDTP_SIGNALING_CONNECTION_INITIATOR_W4_ANSWER; avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_GET_CONFIGURATION, connection->initiator_transaction_label, connection->acp_seid); break; @@ -289,9 +275,8 @@ void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, av if (sent) return; sent = 1; - avdtp_stream_endpoint_t * stream_endpoint = NULL; - // printf(" run int seid %d, acp seid %d\n", connection->int_seid, connection->acp_seid); + avdtp_stream_endpoint_t * stream_endpoint = NULL; stream_endpoint = avdtp_stream_endpoint_associated_with_acp_seid(connection->acp_seid, context); if (!stream_endpoint){ @@ -301,12 +286,69 @@ void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, av avdtp_initiator_stream_endpoint_state_t stream_endpoint_state = stream_endpoint->initiator_config_state; stream_endpoint->initiator_config_state = AVDTP_INITIATOR_W4_ANSWER; + + if (stream_endpoint->start_stream){ + stream_endpoint->start_stream = 0; + if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_OPENED){ + connection->int_seid = stream_endpoint->sep.seid; + connection->acp_seid = connection->remote_seps[stream_endpoint->remote_sep_index].seid; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_START, connection->initiator_transaction_label++, connection->acp_seid); + return; + } + return; + } + + if (stream_endpoint->stop_stream){ + stream_endpoint->stop_stream = 0; + if (stream_endpoint->state >= AVDTP_STREAM_ENDPOINT_OPENED){ + connection->int_seid = stream_endpoint->sep.seid; + connection->acp_seid = connection->remote_seps[stream_endpoint->remote_sep_index].seid; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_CLOSE, connection->initiator_transaction_label++, connection->acp_seid); + return; + } + } + + if (stream_endpoint->abort_stream){ + stream_endpoint->abort_stream = 0; + switch (stream_endpoint->state){ + case AVDTP_STREAM_ENDPOINT_CONFIGURED: + case AVDTP_STREAM_ENDPOINT_CLOSING: + case AVDTP_STREAM_ENDPOINT_OPENED: + case AVDTP_STREAM_ENDPOINT_STREAMING: + connection->int_seid = stream_endpoint->sep.seid; + connection->acp_seid = connection->remote_seps[stream_endpoint->remote_sep_index].seid; + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_ABORT, connection->initiator_transaction_label++, connection->acp_seid); + return; + default: + break; + } + } + + if (stream_endpoint->suspend_stream){ + stream_endpoint->suspend_stream = 0; + if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING){ + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; + avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_SUSPEND, connection->initiator_transaction_label, connection->acp_seid); + return; + } + } + + if (stream_endpoint->send_stream){ + stream_endpoint->send_stream = 0; + if (stream_endpoint->state == AVDTP_STREAM_ENDPOINT_STREAMING){ + stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING; + avdtp_streaming_emit_can_send_media_packet_now(context->avdtp_callback, stream_endpoint->l2cap_media_cid, stream_endpoint->sep.seid, stream_endpoint->sequence_number); + return; + } + } + switch (stream_endpoint_state){ case AVDTP_INITIATOR_W2_SET_CONFIGURATION: case AVDTP_INITIATOR_W2_RECONFIGURE_STREAM_WITH_SEID:{ - printf(" INT: AVDTP_INITIATOR_W2_(RE)CONFIGURATION bitmap, int seid %d, acp seid %d\n", connection->int_seid, connection->acp_seid); - // printf_hexdump( connection->remote_capabilities.media_codec.media_codec_information, connection->remote_capabilities.media_codec.media_codec_information_len); + log_info(" INT: AVDTP_INITIATOR_W2_(RE)CONFIGURATION bitmap, int seid %d, acp seid %d", connection->int_seid, connection->acp_seid); + // log_info_hexdump( connection->remote_capabilities.media_codec.media_codec_information, connection->remote_capabilities.media_codec.media_codec_information_len); connection->signaling_packet.acp_seid = connection->acp_seid; connection->signaling_packet.int_seid = connection->int_seid; @@ -316,13 +358,13 @@ void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, av connection->signaling_packet.signal_identifier = AVDTP_SI_RECONFIGURE; } - avdtp_prepare_capabilities(&connection->signaling_packet, connection->initiator_transaction_label, connection->remote_capabilities_bitmap, connection->remote_capabilities, connection->signaling_packet.signal_identifier); + avdtp_prepare_capabilities(&connection->signaling_packet, connection->initiator_transaction_label, stream_endpoint->remote_capabilities_bitmap, stream_endpoint->remote_capabilities, connection->signaling_packet.signal_identifier); l2cap_reserve_packet_buffer(); uint8_t * out_buffer = l2cap_get_outgoing_buffer(); uint16_t pos = avdtp_signaling_create_fragment(connection->l2cap_signaling_cid, &connection->signaling_packet, out_buffer); if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ stream_endpoint->initiator_config_state = AVDTP_INITIATOR_FRAGMENTATED_COMMAND; - printf(" INT: fragmented\n"); + log_info(" INT: fragmented"); } l2cap_send_prepared(connection->l2cap_signaling_cid, pos); break; @@ -333,7 +375,7 @@ void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, av uint16_t pos = avdtp_signaling_create_fragment(connection->l2cap_signaling_cid, &connection->signaling_packet, out_buffer); if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ stream_endpoint->initiator_config_state = AVDTP_INITIATOR_FRAGMENTATED_COMMAND; - printf(" INT: fragmented\n"); + log_info(" INT: fragmented"); } l2cap_send_prepared(connection->l2cap_signaling_cid, pos); break; @@ -341,35 +383,19 @@ void avdtp_initiator_stream_config_subsm_run(avdtp_connection_t * connection, av case AVDTP_INITIATOR_W2_OPEN_STREAM: switch (stream_endpoint->state){ case AVDTP_STREAM_ENDPOINT_W2_REQUEST_OPEN_STREAM: - printf(" INT: AVDTP_STREAM_ENDPOINT_W2_REQUEST_OPEN_STREAM\n"); + log_info(" INT: AVDTP_STREAM_ENDPOINT_W2_REQUEST_OPEN_STREAM"); avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_OPEN, connection->initiator_transaction_label, connection->acp_seid); break; default: + sent = 0; break; } break; - case AVDTP_INITIATOR_W2_SUSPEND_STREAM_WITH_SEID: - printf(" INT: AVDTP_INITIATOR_W4_SUSPEND_STREAM_WITH_SEID\n"); - avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_SUSPEND, connection->initiator_transaction_label, connection->acp_seid); - break; - case AVDTP_INITIATOR_W2_STREAMING_START: - printf(" INT: AVDTP_INITIATOR_W4_STREAMING_START\n"); - avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_START, connection->initiator_transaction_label, connection->acp_seid); - break; - case AVDTP_INITIATOR_W2_STREAMING_STOP: - printf(" INT: AVDTP_INITIATOR_W4_STREAMING_STOP\n"); - avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_CLOSE, connection->initiator_transaction_label, connection->acp_seid); - break; - case AVDTP_INITIATOR_W2_STREAMING_ABORT: - printf(" INT: AVDTP_INITIATOR_W4_STREAMING_ABORT\n"); - stream_endpoint->state = AVDTP_STREAM_ENDPOINT_ABORTING; - avdtp_initiator_send_signaling_cmd_with_seid(connection->l2cap_signaling_cid, AVDTP_SI_ABORT, connection->initiator_transaction_label, connection->acp_seid); - break; default: + sent = 0; break; } - // check fragmentation if (connection->signaling_packet.packet_type != AVDTP_SINGLE_PACKET && connection->signaling_packet.packet_type != AVDTP_END_PACKET){ avdtp_request_can_send_now_initiator(connection, connection->l2cap_signaling_cid); diff --git a/src/classic/avdtp_sink.c b/src/classic/avdtp_sink.c index 8d3f83667..451508158 100644 --- a/src/classic/avdtp_sink.c +++ b/src/classic/avdtp_sink.c @@ -37,7 +37,6 @@ #define __BTSTACK_FILE__ "avdtp_sink.c" - #include #include #include @@ -51,95 +50,10 @@ #include "avdtp_initiator.h" #include "avdtp_acceptor.h" -static const char * default_avdtp_sink_service_name = "BTstack AVDTP Sink Service"; -static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Sink Service Provider"; - static avdtp_context_t avdtp_sink_context; static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -//static void (*handle_media_data)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size); - -void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ - uint8_t* attribute; - de_create_sequence(service); - - // 0x0000 "Service Record Handle" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); - de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); - - // 0x0001 "Service Class ID List" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UUID, DE_SIZE_16, AUDIO_SINK_GROUP); - } - de_pop_sequence(service, attribute); - - // 0x0004 "Protocol Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); - attribute = de_push_sequence(service); - { - uint8_t* l2cpProtocol = de_push_sequence(attribute); - { - de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); - de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); - } - de_pop_sequence(attribute, l2cpProtocol); - - uint8_t* avProtocol = de_push_sequence(attribute); - { - de_add_number(avProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); // avProtocol_service - de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version - } - de_pop_sequence(attribute, avProtocol); - } - de_pop_sequence(service, attribute); - - // 0x0005 "Public Browse Group" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); - } - de_pop_sequence(service, attribute); - - // 0x0009 "Bluetooth Profile Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); - attribute = de_push_sequence(service); - { - uint8_t *a2dProfile = de_push_sequence(attribute); - { - de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, ADVANCED_AUDIO_DISTRIBUTION); - de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); - } - de_pop_sequence(attribute, a2dProfile); - } - de_pop_sequence(service, attribute); - - - // 0x0100 "Service Name" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); - if (service_name){ - de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); - } else { - de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_name), (uint8_t *) default_avdtp_sink_service_name); - } - - // 0x0100 "Provider Name" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); - if (service_provider_name){ - de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); - } else { - de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_provider_name), (uint8_t *) default_avdtp_sink_service_provider_name); - } - - // 0x0311 "Supported Features" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); - de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); -} - - void avdtp_sink_register_media_transport_category(uint8_t seid){ avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_sink_context); avdtp_register_media_transport_category(stream_endpoint); @@ -170,7 +84,7 @@ void avdtp_sink_register_header_compression_category(uint8_t seid, uint8_t back_ avdtp_register_header_compression_category(stream_endpoint, back_ch, media, recovery); } -void avdtp_sink_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len){ +void avdtp_sink_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, uint8_t * media_codec_info, uint16_t media_codec_info_len){ avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_sink_context); avdtp_register_media_codec_category(stream_endpoint, media_type, media_codec_type, media_codec_info, media_codec_info_len); } @@ -198,7 +112,7 @@ void avdtp_sink_init(void){ avdtp_sink_context.stream_endpoints = NULL; avdtp_sink_context.connections = NULL; avdtp_sink_context.stream_endpoints_id_counter = 0; - // TODO: assign dummy handlers; + avdtp_sink_context.packet_handler = packet_handler; l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_0); } @@ -223,60 +137,55 @@ void avdtp_sink_register_packet_handler(btstack_packet_handler_t callback){ avdtp_sink_context.avdtp_callback = callback; } -void avdtp_sink_connect(bd_addr_t bd_addr){ - avdtp_connection_t * connection = avdtp_connection_for_bd_addr(bd_addr, &avdtp_sink_context); - if (!connection){ - connection = avdtp_create_connection(bd_addr, &avdtp_sink_context); - } - if (connection->state != AVDTP_SIGNALING_CONNECTION_IDLE) return; - connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED; - l2cap_create_channel(packet_handler, connection->remote_addr, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, NULL); +void avdtp_sink_connect(bd_addr_t remote){ + avdtp_connect(remote, AVDTP_SOURCE, &avdtp_sink_context); } -void avdtp_sink_disconnect(uint16_t con_handle){ - avdtp_disconnect(con_handle, &avdtp_sink_context); +void avdtp_sink_disconnect(uint16_t avdtp_cid){ + avdtp_disconnect(avdtp_cid, &avdtp_sink_context); } -void avdtp_sink_open_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_open_stream(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_open_stream(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid){ + avdtp_open_stream(avdtp_cid, int_seid, acp_seid, &avdtp_sink_context); } -void avdtp_sink_start_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_start_stream(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_start_stream(uint8_t int_seid){ + avdtp_start_stream(int_seid, &avdtp_sink_context); } -void avdtp_sink_stop_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_stop_stream(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_stop_stream(uint8_t int_seid){ + avdtp_stop_stream(int_seid, &avdtp_sink_context); } -void avdtp_sink_abort_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_abort_stream(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_abort_stream(uint8_t int_seid){ + avdtp_abort_stream(int_seid, &avdtp_sink_context); } -void avdtp_sink_discover_stream_endpoints(uint16_t con_handle){ - avdtp_discover_stream_endpoints(con_handle, &avdtp_sink_context); +void avdtp_sink_suspend(uint8_t int_seid){ + avdtp_suspend_stream(int_seid, &avdtp_sink_context); } -void avdtp_sink_get_capabilities(uint16_t con_handle, uint8_t acp_seid){ - avdtp_get_capabilities(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_discover_stream_endpoints(uint16_t avdtp_cid){ + avdtp_discover_stream_endpoints(avdtp_cid, &avdtp_sink_context); } -void avdtp_sink_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid){ - avdtp_get_all_capabilities(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid){ + avdtp_get_capabilities(avdtp_cid, acp_seid, &avdtp_sink_context); } -void avdtp_sink_get_configuration(uint16_t con_handle, uint8_t acp_seid){ - avdtp_get_configuration(con_handle, acp_seid, &avdtp_sink_context); +void avdtp_sink_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid){ + avdtp_get_all_capabilities(avdtp_cid, acp_seid, &avdtp_sink_context); } -void avdtp_sink_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ - avdtp_set_configuration(con_handle, int_seid, acp_seid, configured_services_bitmap, configuration, &avdtp_sink_context); +void avdtp_sink_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid){ + avdtp_get_configuration(avdtp_cid, acp_seid, &avdtp_sink_context); } -void avdtp_sink_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ - avdtp_reconfigure(con_handle, acp_seid, configured_services_bitmap, configuration, &avdtp_sink_context); +void avdtp_sink_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ + avdtp_set_configuration(avdtp_cid, int_seid, acp_seid, configured_services_bitmap, configuration, &avdtp_sink_context); +} + +void avdtp_sink_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ + avdtp_reconfigure(avdtp_cid, int_seid, acp_seid, configured_services_bitmap, configuration, &avdtp_sink_context); } -void avdtp_sink_suspend(uint16_t con_handle, uint8_t acp_seid){ - avdtp_suspend(con_handle, acp_seid, &avdtp_sink_context); -} \ No newline at end of file diff --git a/src/classic/avdtp_sink.h b/src/classic/avdtp_sink.h index c7bd81b26..4a5bc6304 100644 --- a/src/classic/avdtp_sink.h +++ b/src/classic/avdtp_sink.h @@ -55,15 +55,6 @@ extern "C" { #endif /* API_START */ -/** - * @brief AVDTP Sink service record. - * @param service - * @param service_record_handle - * @param supported_features 16-bit bitmap, see AVDTP_SINK_SF_* values in avdtp.h - * @param service_name - * @param service_provider_name - */ -void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); /** * @brief Set up AVDTP Sink device. @@ -80,7 +71,7 @@ void avdtp_sink_register_recovery_category(uint8_t seid, uint8_t maximum_recover void avdtp_sink_register_header_compression_category(uint8_t seid, uint8_t back_ch, uint8_t media, uint8_t recovery); void avdtp_sink_register_multiplexing_category(uint8_t seid, uint8_t fragmentation); -void avdtp_sink_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len); +void avdtp_sink_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, uint8_t * media_codec_info, uint16_t media_codec_info_len); void avdtp_sink_register_content_protection_category(uint8_t seid, uint16_t cp_type, const uint8_t * cp_type_value, uint8_t cp_type_value_len); /** @@ -98,82 +89,79 @@ void avdtp_sink_connect(bd_addr_t bd_addr); void avdtp_sink_register_media_handler(void (*callback)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size)); /** * @brief Disconnect from device with connection handle. - * @param con_handle + * @param avdtp_cid */ -void avdtp_sink_disconnect(uint16_t con_handle); +void avdtp_sink_disconnect(uint16_t avdtp_cid); /** * @brief Discover stream endpoints - * @param con_handle + * @param avdtp_cid */ -void avdtp_sink_discover_stream_endpoints(uint16_t con_handle); +void avdtp_sink_discover_stream_endpoints(uint16_t avdtp_cid); /** * @brief Get capabilities - * @param con_handle + * @param avdtp_cid */ -void avdtp_sink_get_capabilities(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); /** * @brief Get all capabilities - * @param con_handle + * @param avdtp_cid */ -void avdtp_sink_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); /** * @brief Set configuration - * @param con_handle + * @param avdtp_cid */ -void avdtp_sink_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); +void avdtp_sink_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); /** * @brief Reconfigure stream - * @param con_handle + * @param avdtp_cid * @param seid */ -void avdtp_sink_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); +void avdtp_sink_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); /** * @brief Get configuration - * @param con_handle + * @param avdtp_cid */ -void avdtp_sink_get_configuration(uint16_t con_handle, uint8_t acp_seid); - -/** - * @brief Suspend stream - * @param con_handle - * @param seid - */ -void avdtp_sink_suspend(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid); /** * @brief Open stream - * @param con_handle + * @param avdtp_cid * @param seid */ -void avdtp_sink_open_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_open_stream(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid); + /** * @brief Start stream - * @param con_handle - * @param seid + * @param local_seid */ -void avdtp_sink_start_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_start_stream(uint8_t local_seid); + +/** + * @brief Abort stream + * @param local_seid + */ +void avdtp_sink_abort_stream(uint8_t local_seid); /** * @brief Start stream - * @param con_handle - * @param seid + * @param local_seid */ -void avdtp_sink_abort_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_stop_stream(uint8_t local_seid); /** - * @brief Start stream - * @param con_handle - * @param seid + * @brief Suspend stream + * @param local_seid */ -void avdtp_sink_stop_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_sink_suspend(uint8_t local_seid); /* API_END */ diff --git a/src/classic/avdtp_source.c b/src/classic/avdtp_source.c index c16ddd917..62b147fbc 100644 --- a/src/classic/avdtp_source.c +++ b/src/classic/avdtp_source.c @@ -51,137 +51,52 @@ #include "avdtp_util.h" #include "avdtp_source.h" -#include "btstack_ring_buffer.h" - -static const char * default_avdtp_source_service_name = "BTstack AVDTP Source Service"; -static const char * default_avdtp_source_service_provider_name = "BTstack AVDTP Source Service Provider"; - -static avdtp_context_t avdtp_source_context; +static avdtp_context_t * avdtp_source_context; static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ - uint8_t* attribute; - de_create_sequence(service); - - // 0x0000 "Service Record Handle" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); - de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); - - // 0x0001 "Service Class ID List" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST); - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UUID, DE_SIZE_16, AUDIO_SOURCE_GROUP); - } - de_pop_sequence(service, attribute); - - // 0x0004 "Protocol Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST); - attribute = de_push_sequence(service); - { - uint8_t* l2cpProtocol = de_push_sequence(attribute); - { - de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP); - de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); - } - de_pop_sequence(attribute, l2cpProtocol); - - uint8_t* avProtocol = de_push_sequence(attribute); - { - de_add_number(avProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVDTP); // avProtocol_service - de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version - } - de_pop_sequence(attribute, avProtocol); - } - de_pop_sequence(service, attribute); - - // 0x0005 "Public Browse Group" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT); - } - de_pop_sequence(service, attribute); - - // 0x0009 "Bluetooth Profile Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST); - attribute = de_push_sequence(service); - { - uint8_t *a2dProfile = de_push_sequence(attribute); - { - de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, ADVANCED_AUDIO_DISTRIBUTION); - de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); - } - de_pop_sequence(attribute, a2dProfile); - } - de_pop_sequence(service, attribute); - - - // 0x0100 "Service Name" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); - if (service_name){ - de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); - } else { - de_add_data(service, DE_STRING, strlen(default_avdtp_source_service_name), (uint8_t *) default_avdtp_source_service_name); - } - - // 0x0100 "Provider Name" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); - if (service_provider_name){ - de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); - } else { - de_add_data(service, DE_STRING, strlen(default_avdtp_source_service_provider_name), (uint8_t *) default_avdtp_source_service_provider_name); - } - - // 0x0311 "Supported Features" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); - de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); -} - - void avdtp_source_register_media_transport_category(uint8_t seid){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_media_transport_category(stream_endpoint); } void avdtp_source_register_reporting_category(uint8_t seid){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_reporting_category(stream_endpoint); } void avdtp_source_register_delay_reporting_category(uint8_t seid){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_delay_reporting_category(stream_endpoint); } void avdtp_source_register_recovery_category(uint8_t seid, uint8_t maximum_recovery_window_size, uint8_t maximum_number_media_packets){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_recovery_category(stream_endpoint, maximum_recovery_window_size, maximum_number_media_packets); } void avdtp_source_register_content_protection_category(uint8_t seid, uint16_t cp_type, const uint8_t * cp_type_value, uint8_t cp_type_value_len){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_content_protection_category(stream_endpoint, cp_type, cp_type_value, cp_type_value_len); } void avdtp_source_register_header_compression_category(uint8_t seid, uint8_t back_ch, uint8_t media, uint8_t recovery){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_header_compression_category(stream_endpoint, back_ch, media, recovery); } -void avdtp_source_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); +void avdtp_source_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, uint8_t * media_codec_info, uint16_t media_codec_info_len){ + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_media_codec_category(stream_endpoint, media_type, media_codec_type, media_codec_info, media_codec_info_len); } void avdtp_source_register_multiplexing_category(uint8_t seid, uint8_t fragmentation){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, &avdtp_source_context); + avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_seid(seid, avdtp_source_context); avdtp_register_multiplexing_category(stream_endpoint, fragmentation); } avdtp_stream_endpoint_t * avdtp_source_create_stream_endpoint(avdtp_sep_type_t sep_type, avdtp_media_type_t media_type){ - return avdtp_create_stream_endpoint(sep_type, media_type, &avdtp_source_context); + return avdtp_create_stream_endpoint(sep_type, media_type, avdtp_source_context); } void avdtp_source_register_packet_handler(btstack_packet_handler_t callback){ @@ -189,232 +104,83 @@ void avdtp_source_register_packet_handler(btstack_packet_handler_t callback){ log_error("avdtp_source_register_packet_handler called with NULL callback"); return; } - avdtp_source_context.avdtp_callback = callback; + avdtp_source_context->avdtp_callback = callback; } -void avdtp_source_connect(bd_addr_t bd_addr){ - avdtp_connection_t * connection = avdtp_connection_for_bd_addr(bd_addr, &avdtp_source_context); - if (!connection){ - connection = avdtp_create_connection(bd_addr, &avdtp_source_context); - } - if (connection->state != AVDTP_SIGNALING_CONNECTION_IDLE) return; - connection->state = AVDTP_SIGNALING_CONNECTION_W4_L2CAP_CONNECTED; - l2cap_create_channel(packet_handler, connection->remote_addr, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, NULL); +void avdtp_source_connect(bd_addr_t remote){ + avdtp_connect(remote, AVDTP_SINK, avdtp_source_context); } void avdtp_source_disconnect(uint16_t con_handle){ - avdtp_disconnect(con_handle, &avdtp_source_context); + avdtp_disconnect(con_handle, avdtp_source_context); } -void avdtp_source_open_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_open_stream(con_handle, acp_seid, &avdtp_source_context); +void avdtp_source_open_stream(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid){ + avdtp_open_stream(con_handle, int_seid, acp_seid, avdtp_source_context); } -void avdtp_source_start_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_start_stream(con_handle, acp_seid, &avdtp_source_context); +void avdtp_source_start_stream(uint8_t int_seid){ + avdtp_start_stream(int_seid, avdtp_source_context); } -void avdtp_source_stop_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_stop_stream(con_handle, acp_seid, &avdtp_source_context); +void avdtp_source_stop_stream(uint8_t int_seid){ + avdtp_stop_stream(int_seid, avdtp_source_context); } -void avdtp_source_abort_stream(uint16_t con_handle, uint8_t acp_seid){ - avdtp_abort_stream(con_handle, acp_seid, &avdtp_source_context); +void avdtp_source_abort_stream(uint8_t int_seid){ + avdtp_abort_stream(int_seid, avdtp_source_context); +} + +void avdtp_source_suspend(uint8_t int_seid){ + avdtp_suspend_stream(int_seid, avdtp_source_context); } void avdtp_source_discover_stream_endpoints(uint16_t con_handle){ - avdtp_discover_stream_endpoints(con_handle, &avdtp_source_context); + avdtp_discover_stream_endpoints(con_handle, avdtp_source_context); } void avdtp_source_get_capabilities(uint16_t con_handle, uint8_t acp_seid){ - avdtp_get_capabilities(con_handle, acp_seid, &avdtp_source_context); + avdtp_get_capabilities(con_handle, acp_seid, avdtp_source_context); } void avdtp_source_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid){ - avdtp_get_all_capabilities(con_handle, acp_seid, &avdtp_source_context); + avdtp_get_all_capabilities(con_handle, acp_seid, avdtp_source_context); } void avdtp_source_get_configuration(uint16_t con_handle, uint8_t acp_seid){ - avdtp_get_configuration(con_handle, acp_seid, &avdtp_source_context); + avdtp_get_configuration(con_handle, acp_seid, avdtp_source_context); } void avdtp_source_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ - avdtp_set_configuration(con_handle, int_seid, acp_seid, configured_services_bitmap, configuration, &avdtp_source_context); + avdtp_set_configuration(con_handle, int_seid, acp_seid, configured_services_bitmap, configuration, avdtp_source_context); } -void avdtp_source_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ - avdtp_reconfigure(con_handle, acp_seid, configured_services_bitmap, configuration, &avdtp_source_context); -} - -void avdtp_source_suspend(uint16_t con_handle, uint8_t acp_seid){ - avdtp_suspend(con_handle, acp_seid, &avdtp_source_context); +void avdtp_source_reconfigure(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration){ + avdtp_reconfigure(con_handle, int_seid, acp_seid, configured_services_bitmap, configuration, avdtp_source_context); } static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - avdtp_packet_handler(packet_type, channel, packet, size, &avdtp_source_context); + avdtp_packet_handler(packet_type, channel, packet, size, avdtp_source_context); } - -/* streaming part */ -#define NUM_CHANNELS 2 -#define SAMPLE_RATE 44100 -#define BYTES_PER_AUDIO_SAMPLE (2*NUM_CHANNELS) -#define LATENCY 300 // ms - -#ifndef M_PI -#define M_PI 3.14159265 -#endif -#define TABLE_SIZE_441HZ 100 - -typedef struct { - int16_t source[TABLE_SIZE_441HZ]; - int left_phase; - int right_phase; -} paTestData; - -static uint32_t fill_audio_ring_buffer_timeout = 50; //ms -static paTestData sin_data; - -static void fill_audio_ring_buffer(void *userData, int num_samples_to_write, avdtp_stream_endpoint_t * stream_endpoint){ - paTestData *data = (paTestData*)userData; - int count = 0; - while (btstack_ring_buffer_bytes_free(&stream_endpoint->audio_ring_buffer) >= BYTES_PER_AUDIO_SAMPLE && count < num_samples_to_write){ - uint8_t write_data[BYTES_PER_AUDIO_SAMPLE]; - *(int16_t*)&write_data[0] = data->source[data->left_phase]; - *(int16_t*)&write_data[2] = data->source[data->right_phase]; - - btstack_ring_buffer_write(&stream_endpoint->audio_ring_buffer, write_data, BYTES_PER_AUDIO_SAMPLE); - count++; - - data->left_phase += 1; - if (data->left_phase >= TABLE_SIZE_441HZ){ - data->left_phase -= TABLE_SIZE_441HZ; - } - data->right_phase += 1; - if (data->right_phase >= TABLE_SIZE_441HZ){ - data->right_phase -= TABLE_SIZE_441HZ; - } - } -} - -static void fill_sbc_ring_buffer(uint8_t * sbc_frame, int sbc_frame_size, avdtp_stream_endpoint_t * stream_endpoint){ - if (btstack_ring_buffer_bytes_free(&stream_endpoint->sbc_ring_buffer) >= sbc_frame_size ){ - // printf(" fill_sbc_ring_buffer\n"); - uint8_t size_buffer = sbc_frame_size; - btstack_ring_buffer_write(&stream_endpoint->sbc_ring_buffer, &size_buffer, 1); - btstack_ring_buffer_write(&stream_endpoint->sbc_ring_buffer, sbc_frame, sbc_frame_size); - } else { - printf("No space in sbc buffer\n"); - } -} - - -static void avdtp_source_stream_endpoint_run(avdtp_stream_endpoint_t * stream_endpoint){ - // performe sbc encoding - int total_num_bytes_read = 0; - int num_audio_samples_to_read = btstack_sbc_encoder_num_audio_frames(); - int audio_bytes_to_read = num_audio_samples_to_read * BYTES_PER_AUDIO_SAMPLE; - - // printf("run: audio_bytes_to_read: %d\n", audio_bytes_to_read); - // printf(" audio buf, bytes available: %d\n", btstack_ring_buffer_bytes_available(&stream_endpoint->audio_ring_buffer)); - // printf(" sbc buf, bytes free: %d\n", btstack_ring_buffer_bytes_free(&stream_endpoint->sbc_ring_buffer)); - - while (btstack_ring_buffer_bytes_available(&stream_endpoint->audio_ring_buffer) >= audio_bytes_to_read - && btstack_ring_buffer_bytes_free(&stream_endpoint->sbc_ring_buffer) >= 120){ // TODO use real value - - uint32_t number_of_bytes_read = 0; - uint8_t pcm_frame[256*BYTES_PER_AUDIO_SAMPLE]; - btstack_ring_buffer_read(&stream_endpoint->audio_ring_buffer, pcm_frame, audio_bytes_to_read, &number_of_bytes_read); - // printf(" num audio bytes read %d\n", number_of_bytes_read); - btstack_sbc_encoder_process_data((int16_t *) pcm_frame); - - uint16_t sbc_frame_bytes = 119; //btstack_sbc_encoder_sbc_buffer_length(); - - total_num_bytes_read += number_of_bytes_read; - fill_sbc_ring_buffer(btstack_sbc_encoder_sbc_buffer(), sbc_frame_bytes, stream_endpoint); - } - // schedule sending - if (total_num_bytes_read != 0){ - stream_endpoint->state = AVDTP_STREAM_ENDPOINT_STREAMING_W2_SEND; - avdtp_request_can_send_now_self(stream_endpoint->connection, stream_endpoint->l2cap_media_cid); - } -} - -static void test_fill_audio_ring_buffer_timeout_handler(btstack_timer_source_t * timer){ - avdtp_stream_endpoint_t * stream_endpoint = btstack_run_loop_get_timer_context(timer); - btstack_run_loop_set_timer(&stream_endpoint->fill_audio_ring_buffer_timer, fill_audio_ring_buffer_timeout); // 2 seconds timeout - btstack_run_loop_add_timer(&stream_endpoint->fill_audio_ring_buffer_timer); - uint32_t now = btstack_run_loop_get_time_ms(); - - uint32_t update_period_ms = fill_audio_ring_buffer_timeout; - if (stream_endpoint->time_audio_data_sent > 0){ - update_period_ms = now - stream_endpoint->time_audio_data_sent; - } - uint32_t num_samples = (update_period_ms * 44100) / 1000; - stream_endpoint->acc_num_missed_samples += (update_period_ms * 44100) % 1000; - - if (stream_endpoint->acc_num_missed_samples >= 1000){ - num_samples++; - stream_endpoint->acc_num_missed_samples -= 1000; - } - - fill_audio_ring_buffer(&sin_data, num_samples, stream_endpoint); - stream_endpoint->time_audio_data_sent = now; - - avdtp_source_stream_endpoint_run(stream_endpoint); - // -} - -static void test_fill_audio_ring_buffer_timer_start(avdtp_stream_endpoint_t * stream_endpoint){ - btstack_run_loop_remove_timer(&stream_endpoint->fill_audio_ring_buffer_timer); - btstack_run_loop_set_timer_handler(&stream_endpoint->fill_audio_ring_buffer_timer, test_fill_audio_ring_buffer_timeout_handler); - btstack_run_loop_set_timer_context(&stream_endpoint->fill_audio_ring_buffer_timer, stream_endpoint); - btstack_run_loop_set_timer(&stream_endpoint->fill_audio_ring_buffer_timer, fill_audio_ring_buffer_timeout); // 50 ms timeout - btstack_run_loop_add_timer(&stream_endpoint->fill_audio_ring_buffer_timer); -} - -static void test_fill_audio_ring_buffer_timer_stop(avdtp_stream_endpoint_t * stream_endpoint){ - btstack_run_loop_remove_timer(&stream_endpoint->fill_audio_ring_buffer_timer); -} - -void avdtp_source_stream_data_start(uint16_t con_handle){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_l2cap_cid(con_handle, &avdtp_source_context); - if (!stream_endpoint) { - printf("no stream_endpoint found for 0x%02x", con_handle); +void avdtp_source_init(avdtp_context_t * avdtp_context){ + if (!avdtp_context){ + log_error("avdtp_source_context is NULL"); return; } - if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_STREAMING){ - printf("stream_endpoint in wrong state %d\n", stream_endpoint->state); - return; - } - - test_fill_audio_ring_buffer_timer_start(stream_endpoint); -} - -void avdtp_source_stream_data_stop(uint16_t con_handle){ - avdtp_stream_endpoint_t * stream_endpoint = avdtp_stream_endpoint_for_l2cap_cid(con_handle, &avdtp_source_context); - if (!stream_endpoint) { - log_error("no stream_endpoint found"); - return; - } - if (stream_endpoint->state != AVDTP_STREAM_ENDPOINT_STREAMING) return; - // TODO: initialize randomly sequence number - stream_endpoint->sequence_number = 0; - test_fill_audio_ring_buffer_timer_stop(stream_endpoint); -} - -void avdtp_source_init(void){ - avdtp_source_context.stream_endpoints = NULL; - avdtp_source_context.connections = NULL; - avdtp_source_context.stream_endpoints_id_counter = 0; - avdtp_source_context.packet_handler = packet_handler; - - /* initialise sinusoidal wavetable */ - int i; - for (i=0; istream_endpoints = NULL; + avdtp_source_context->connections = NULL; + avdtp_source_context->stream_endpoints_id_counter = 0; + avdtp_source_context->packet_handler = packet_handler; l2cap_register_service(&packet_handler, BLUETOOTH_PROTOCOL_AVDTP, 0xffff, LEVEL_0); } + +uint8_t avdtp_source_remote_seps_num(uint16_t avdtp_cid){ + return avdtp_remote_seps_num(avdtp_cid, avdtp_source_context); +} + +avdtp_sep_t * avdtp_source_remote_sep(uint16_t avdtp_cid, uint8_t index){ + return avdtp_remote_sep(avdtp_cid, index, avdtp_source_context); +} diff --git a/src/classic/avdtp_source.h b/src/classic/avdtp_source.h index 39bae3faf..5dde8d9af 100644 --- a/src/classic/avdtp_source.h +++ b/src/classic/avdtp_source.h @@ -54,27 +54,17 @@ extern "C" { /* API_START */ -/** - * @brief AVDTP Source service record. - * @param service - * @param service_record_handle - * @param supported_features 16-bit bitmap, see AVDTP_SOURCE_SF_* values in avdtp.h - * @param service_name - * @param service_provider_name - */ -void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); - void avdtp_source_register_media_transport_category(uint8_t seid); void avdtp_source_register_reporting_category(uint8_t seid); void avdtp_source_register_delay_reporting_category(uint8_t seid); void avdtp_source_register_recovery_category(uint8_t seid, uint8_t maximum_recovery_window_size, uint8_t maximum_number_media_packets); void avdtp_source_register_content_protection_category(uint8_t seid, uint16_t cp_type, const uint8_t * cp_type_value, uint8_t cp_type_value_len); void avdtp_source_register_header_compression_category(uint8_t seid, uint8_t back_ch, uint8_t media, uint8_t recovery); -void avdtp_source_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, const uint8_t * media_codec_info, uint16_t media_codec_info_len); +void avdtp_source_register_media_codec_category(uint8_t seid, avdtp_media_type_t media_type, avdtp_media_codec_type_t media_codec_type, uint8_t * media_codec_info, uint16_t media_codec_info_len); void avdtp_source_register_multiplexing_category(uint8_t seid, uint8_t fragmentation); -void avdtp_source_init(void); +void avdtp_source_init(avdtp_context_t * avdtp_context); void avdtp_source_register_packet_handler(btstack_packet_handler_t callback); /** @@ -83,90 +73,86 @@ void avdtp_source_register_packet_handler(btstack_packet_handler_t callback); */ void avdtp_source_connect(bd_addr_t bd_addr); -void avdtp_source_register_media_handler(void (*callback)(avdtp_stream_endpoint_t * stream_endpoint, uint8_t *packet, uint16_t size)); /** * @brief Disconnect from device with connection handle. - * @param con_handle + * @param avdtp_cid */ -void avdtp_source_disconnect(uint16_t con_handle); +void avdtp_source_disconnect(uint16_t avdtp_cid); /** * @brief Discover stream endpoints - * @param con_handle + * @param avdtp_cid */ -void avdtp_source_discover_stream_endpoints(uint16_t con_handle); +void avdtp_source_discover_stream_endpoints(uint16_t avdtp_cid); /** * @brief Get capabilities - * @param con_handle + * @param avdtp_cid */ -void avdtp_source_get_capabilities(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_get_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); /** * @brief Get all capabilities - * @param con_handle + * @param avdtp_cid */ -void avdtp_source_get_all_capabilities(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_get_all_capabilities(uint16_t avdtp_cid, uint8_t acp_seid); /** * @brief Set configuration - * @param con_handle + * @param avdtp_cid */ -void avdtp_source_set_configuration(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); +void avdtp_source_set_configuration(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); /** * @brief Reconfigure stream - * @param con_handle + * @param avdtp_cid * @param seid */ -void avdtp_source_reconfigure(uint16_t con_handle, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); +void avdtp_source_reconfigure(uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint16_t configured_services_bitmap, avdtp_capabilities_t configuration); /** * @brief Get configuration - * @param con_handle + * @param avdtp_cid */ -void avdtp_source_get_configuration(uint16_t con_handle, uint8_t acp_seid); - -/** - * @brief Suspend stream - * @param con_handle - * @param seid - */ -void avdtp_source_suspend(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_get_configuration(uint16_t avdtp_cid, uint8_t acp_seid); /** * @brief Open stream - * @param con_handle + * @param avdtp_cid * @param seid */ -void avdtp_source_open_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_open_stream(uint16_t con_handle, uint8_t int_seid, uint8_t acp_seid); /** * @brief Start stream - * @param con_handle - * @param seid + * @param local_seid */ -void avdtp_source_start_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_start_stream(uint8_t local_seid); + +/** + * @brief Abort stream + * @param local_seid + */ +void avdtp_source_abort_stream(uint8_t local_seid); /** * @brief Start stream - * @param con_handle - * @param seid + * @param local_seid */ -void avdtp_source_abort_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_stop_stream(uint8_t local_seid); /** - * @brief Start stream - * @param con_handle - * @param seid + * @brief Suspend stream + * @param local_seid */ -void avdtp_source_stop_stream(uint16_t con_handle, uint8_t acp_seid); +void avdtp_source_suspend(uint8_t local_seid); + avdtp_stream_endpoint_t * avdtp_source_create_stream_endpoint(avdtp_sep_type_t sep_type, avdtp_media_type_t media_type); -void avdtp_source_stream_data_start(uint16_t con_handle); -void avdtp_source_stream_data_stop(uint16_t con_handle); +uint8_t avdtp_source_remote_seps_num(uint16_t avdtp_cid); +avdtp_sep_t * avdtp_source_remote_sep(uint16_t avdtp_cid, uint8_t index); /* API_END */ #if defined __cplusplus diff --git a/src/classic/avdtp_util.c b/src/classic/avdtp_util.c index 45809368e..e30eba5f8 100644 --- a/src/classic/avdtp_util.c +++ b/src/classic/avdtp_util.c @@ -59,9 +59,8 @@ void avdtp_initialize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint) stream_endpoint->initiator_config_state = AVDTP_INITIATOR_STREAM_CONFIG_IDLE; stream_endpoint->remote_sep_index = AVDTP_INVALID_SEP_INDEX; stream_endpoint->media_disconnect = 0; - stream_endpoint->remote_seps_num = 0; stream_endpoint->sep.in_use = 0; - memset(stream_endpoint->remote_seps, 0, sizeof(stream_endpoint->remote_seps)); + stream_endpoint->remote_sep_index = 0; } avdtp_stream_endpoint_t * avdtp_stream_endpoint_for_seid(uint16_t seid, avdtp_context_t * context){ @@ -87,17 +86,6 @@ avdtp_connection_t * avdtp_connection_for_bd_addr(bd_addr_t addr, avdtp_context_ return NULL; } -avdtp_connection_t * avdtp_connection_for_con_handle(hci_con_handle_t con_handle, avdtp_context_t * context){ - btstack_linked_list_iterator_t it; - btstack_linked_list_iterator_init(&it, &context->connections); - while (btstack_linked_list_iterator_has_next(&it)){ - avdtp_connection_t * connection = (avdtp_connection_t *)btstack_linked_list_iterator_next(&it); - if (connection->con_handle != con_handle) continue; - return connection; - } - return NULL; -} - avdtp_connection_t * avdtp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid, avdtp_context_t * context){ btstack_linked_list_iterator_t it; btstack_linked_list_iterator_init(&it, &context->connections); @@ -144,8 +132,10 @@ avdtp_stream_endpoint_t * avdtp_stream_endpoint_associated_with_acp_seid(uint16_ btstack_linked_list_iterator_init(&it, &context->stream_endpoints); while (btstack_linked_list_iterator_has_next(&it)){ avdtp_stream_endpoint_t * stream_endpoint = (avdtp_stream_endpoint_t *)btstack_linked_list_iterator_next(&it); + if (stream_endpoint->remote_sep_index != AVDTP_INVALID_SEP_INDEX){ - if (stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid == acp_seid){ + if (!stream_endpoint->connection) continue; + if (stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index].seid == acp_seid){ return stream_endpoint; } } @@ -190,7 +180,7 @@ int avdtp_read_signaling_header(avdtp_signaling_packet_t * signaling_header, uin break; case AVDTP_CONTINUE_PACKET: if (signaling_header->num_packets <= 0) { - printf(" ERROR: wrong num fragmented packets\n"); + log_info(" ERROR: wrong num fragmented packets\n"); break; } signaling_header->num_packets--; @@ -254,7 +244,7 @@ static int avdtp_unpack_service_capabilities_has_errors(avdtp_connection_t * con if (category == AVDTP_SERVICE_CATEGORY_INVALID_0 || (category == AVDTP_SERVICE_CATEGORY_INVALID_FF && connection->signaling_packet.signal_identifier == AVDTP_SI_RECONFIGURE)){ - printf(" ERROR: BAD SERVICE CATEGORY %d\n", category); + log_info(" ERROR: BAD SERVICE CATEGORY %d\n", category); connection->reject_service_category = category; connection->error_code = BAD_SERV_CATEGORY; return 1; @@ -262,7 +252,7 @@ static int avdtp_unpack_service_capabilities_has_errors(avdtp_connection_t * con if (connection->signaling_packet.signal_identifier == AVDTP_SI_RECONFIGURE){ if (category != AVDTP_CONTENT_PROTECTION && category != AVDTP_MEDIA_CODEC){ - printf(" ERROR: REJECT CATEGORY, INVALID_CAPABILITIES\n"); + log_info(" ERROR: REJECT CATEGORY, INVALID_CAPABILITIES\n"); connection->reject_service_category = category; connection->error_code = INVALID_CAPABILITIES; return 1; @@ -272,7 +262,7 @@ static int avdtp_unpack_service_capabilities_has_errors(avdtp_connection_t * con switch(category){ case AVDTP_MEDIA_TRANSPORT: if (cap_len != 0){ - printf(" ERROR: REJECT CATEGORY, BAD_MEDIA_TRANSPORT\n"); + log_info(" ERROR: REJECT CATEGORY, BAD_MEDIA_TRANSPORT\n"); connection->reject_service_category = category; connection->error_code = BAD_MEDIA_TRANSPORT_FORMAT; return 1; @@ -281,7 +271,7 @@ static int avdtp_unpack_service_capabilities_has_errors(avdtp_connection_t * con case AVDTP_REPORTING: case AVDTP_DELAY_REPORTING: if (cap_len != 0){ - printf(" ERROR: REJECT CATEGORY, BAD_LENGTH\n"); + log_info(" ERROR: REJECT CATEGORY, BAD_LENGTH\n"); connection->reject_service_category = category; connection->error_code = BAD_LENGTH; return 1; @@ -289,7 +279,7 @@ static int avdtp_unpack_service_capabilities_has_errors(avdtp_connection_t * con break; case AVDTP_RECOVERY: if (cap_len < 3){ - printf(" ERROR: REJECT CATEGORY, BAD_MEDIA_TRANSPORT\n"); + log_info(" ERROR: REJECT CATEGORY, BAD_MEDIA_TRANSPORT\n"); connection->reject_service_category = category; connection->error_code = BAD_RECOVERY_FORMAT; return 1; @@ -297,7 +287,7 @@ static int avdtp_unpack_service_capabilities_has_errors(avdtp_connection_t * con break; case AVDTP_CONTENT_PROTECTION: if (cap_len < 2){ - printf(" ERROR: REJECT CATEGORY, BAD_CP_FORMAT\n"); + log_info(" ERROR: REJECT CATEGORY, BAD_CP_FORMAT\n"); connection->reject_service_category = category; connection->error_code = BAD_CP_FORMAT; return 1; @@ -327,8 +317,7 @@ uint16_t avdtp_unpack_service_capabilities(avdtp_connection_t * connection, avdt if (avdtp_unpack_service_capabilities_has_errors(connection, category, cap_len)) return 0; int processed_cap_len = 0; int rfa = 0; - //printf(" size %d, cat size %d\n", size, cap_len); - + while (pos < size){ if (cap_len > size - pos){ connection->reject_service_category = category; @@ -398,8 +387,6 @@ uint16_t avdtp_unpack_service_capabilities(avdtp_connection_t * connection, avdt category = (avdtp_service_category_t)packet[pos++]; cap_len = packet[pos++]; if (avdtp_unpack_service_capabilities_has_errors(connection, category, cap_len)) return 0; - //printf("category %d, pos %d + 2 + %d -> %d\n", category, old_pos, cap_len, pos + cap_len); - //printf_hexdump(packet+old_pos, size-old_pos); } } } @@ -437,10 +424,6 @@ void avdtp_prepare_capabilities(avdtp_signaling_packet_t * signaling_packet, uin break; } - // printf("registered_service_categories: 0x%02x\n", registered_service_categories); - - // printf("command before packing:\n"); - // printf_hexdump(signaling_packet->command, signaling_packet->size); for (i = 1; i < 9; i++){ int registered_category = get_bit16(registered_service_categories, i); if (!registered_category && (identifier == AVDTP_SI_SET_CONFIGURATION || identifier == AVDTP_SI_RECONFIGURE)){ @@ -451,13 +434,10 @@ void avdtp_prepare_capabilities(avdtp_signaling_packet_t * signaling_packet, uin } if (registered_category){ // service category - // printf("pack service category: %d\n", i); signaling_packet->command[signaling_packet->size++] = i; signaling_packet->size += avdtp_pack_service_capabilities(signaling_packet->command+signaling_packet->size, sizeof(signaling_packet->command)-signaling_packet->size, capabilities, (avdtp_service_category_t)i, pack_all_capabilities); } } - // printf("command after packing:\n"); - // printf_hexdump(signaling_packet->command, signaling_packet->size); signaling_packet->signal_identifier = identifier; signaling_packet->transaction_label = transaction_label; @@ -465,17 +445,13 @@ void avdtp_prepare_capabilities(avdtp_signaling_packet_t * signaling_packet, uin int avdtp_signaling_create_fragment(uint16_t cid, avdtp_signaling_packet_t * signaling_packet, uint8_t * out_buffer) { int mtu = l2cap_get_remote_mtu_for_local_cid(cid); - // hack for test - // int mtu = 6; int data_len = 0; uint16_t offset = signaling_packet->offset; uint16_t pos = 1; - // printf(" avdtp_signaling_create_fragment offset %d, packet type %d\n", signaling_packet->offset, signaling_packet->packet_type); if (offset == 0){ if (signaling_packet->size <= mtu - 2){ - // printf(" AVDTP_SINGLE_PACKET\n"); signaling_packet->packet_type = AVDTP_SINGLE_PACKET; out_buffer[pos++] = signaling_packet->signal_identifier; data_len = signaling_packet->size; @@ -485,21 +461,17 @@ int avdtp_signaling_create_fragment(uint16_t cid, avdtp_signaling_packet_t * sig out_buffer[pos++] = signaling_packet->signal_identifier; data_len = mtu - 3; signaling_packet->offset = data_len; - // printf(" AVDTP_START_PACKET len %d, offset %d\n", signaling_packet->size, signaling_packet->offset); } } else { int remaining_bytes = signaling_packet->size - offset; if (remaining_bytes <= mtu - 1){ - //signaling_packet->fragmentation = 1; signaling_packet->packet_type = AVDTP_END_PACKET; data_len = remaining_bytes; signaling_packet->offset = 0; - // printf(" AVDTP_END_PACKET len %d, offset %d\n", signaling_packet->size, signaling_packet->offset); } else{ signaling_packet->packet_type = AVDTP_CONTINUE_PACKET; data_len = mtu - 1; signaling_packet->offset += data_len; - // printf(" AVDTP_CONTINUE_PACKET len %d, offset %d\n", signaling_packet->size, signaling_packet->offset); } } out_buffer[0] = avdtp_header(signaling_packet->transaction_label, signaling_packet->packet_type, signaling_packet->message_type); @@ -509,14 +481,14 @@ int avdtp_signaling_create_fragment(uint16_t cid, avdtp_signaling_packet_t * sig } -void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, bd_addr_t addr, uint8_t status){ +void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callback, uint16_t avdtp_cid, bd_addr_t addr, uint8_t status){ if (!callback) return; uint8_t event[12]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; reverse_bd_addr(addr,&event[pos]); pos += 6; @@ -524,27 +496,44 @@ void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callba (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_streaming_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, uint8_t status){ +void avdtp_streaming_emit_can_send_media_packet_now(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t seid, uint16_t sequence_number){ if (!callback) return; - uint8_t event[6]; + uint8_t event[8]; + int pos = 0; + event[pos++] = HCI_EVENT_AVDTP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVDTP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW; + little_endian_store_16(event, pos, avdtp_cid); + pos += 2; + event[pos++] = seid; + little_endian_store_16(event, pos, sequence_number); + pos += 2; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +void avdtp_streaming_emit_connection_established(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint8_t status){ + if (!callback) return; + uint8_t event[8]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; + event[pos++] = acp_seid; event[pos++] = status; (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep){ +void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t avdtp_cid, avdtp_sep_t sep){ if (!callback) return; uint8_t event[9]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_SEP_FOUND; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; event[pos++] = sep.seid; event[pos++] = sep.in_use; @@ -553,55 +542,60 @@ void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t con_ha (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_accept(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier, uint8_t status){ +void avdtp_signaling_emit_accept(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, avdtp_signal_identifier_t identifier, uint8_t status){ if (!callback) return; - uint8_t event[7]; + uint8_t event[8]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_ACCEPT; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; event[pos++] = identifier; event[pos++] = status; (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier){ +void avdtp_signaling_emit_reject(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, avdtp_signal_identifier_t identifier){ if (!callback) return; - uint8_t event[6]; + uint8_t event[7]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_REJECT; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; event[pos++] = identifier; (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_general_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier){ +void avdtp_signaling_emit_general_reject(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, avdtp_signal_identifier_t identifier){ if (!callback) return; - uint8_t event[6]; + uint8_t event[7]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_GENERAL_REJECT; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; event[pos++] = identifier; (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ +void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec){ if (!callback) return; - uint8_t event[13]; + uint8_t event[15]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CAPABILITY; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; + event[pos++] = acp_seid; event[pos++] = media_codec.media_type; event[pos++] = media_codec.media_codec_information[0] >> 4; event[pos++] = media_codec.media_codec_information[0] & 0x0F; @@ -613,15 +607,17 @@ void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t ca (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ +void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec){ if (!callback) return; - uint8_t event[109]; + uint8_t event[111]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; + event[pos++] = acp_seid; event[pos++] = media_codec.media_type; little_endian_store_16(event, pos, media_codec.media_codec_type); pos += 2; @@ -635,16 +631,18 @@ void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -static inline void avdtp_signaling_emit_media_codec_sbc(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec, uint8_t reconfigure){ +static inline void avdtp_signaling_emit_media_codec_sbc(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec, uint8_t reconfigure){ if (!callback) return; - uint8_t event[14+2]; + uint8_t event[16+2]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; + event[pos++] = int_seid; + event[pos++] = acp_seid; event[pos++] = reconfigure; uint8_t num_channels = 0; @@ -713,25 +711,26 @@ static inline void avdtp_signaling_emit_media_codec_sbc(btstack_packet_handler_t (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_media_codec_sbc_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ +void avdtp_signaling_emit_media_codec_sbc_configuration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec){ if (!callback) return; - avdtp_signaling_emit_media_codec_sbc(callback, con_handle, media_codec, 0); + avdtp_signaling_emit_media_codec_sbc(callback, avdtp_cid, int_seid, acp_seid, media_codec, 0); } -void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ +void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec){ if (!callback) return; - avdtp_signaling_emit_media_codec_sbc(callback, con_handle, media_codec, 1); + avdtp_signaling_emit_media_codec_sbc(callback, avdtp_cid, int_seid, acp_seid, media_codec, 1); } -static inline void avdtp_signaling_emit_media_codec_other(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec, uint8_t reconfigure){ - uint8_t event[110]; +static inline void avdtp_signaling_emit_media_codec_other(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec, uint8_t reconfigure){ + uint8_t event[112]; int pos = 0; event[pos++] = HCI_EVENT_AVDTP_META; event[pos++] = sizeof(event) - 2; event[pos++] = AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CONFIGURATION; - little_endian_store_16(event, pos, con_handle); + little_endian_store_16(event, pos, avdtp_cid); pos += 2; - + event[pos++] = int_seid; + event[pos++] = acp_seid; event[pos++] = reconfigure; event[pos++] = media_codec.media_type; @@ -748,14 +747,14 @@ static inline void avdtp_signaling_emit_media_codec_other(btstack_packet_handler (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } -void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ +void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec){ if (!callback) return; - avdtp_signaling_emit_media_codec_other(callback, con_handle, media_codec, 0); + avdtp_signaling_emit_media_codec_other(callback, avdtp_cid, int_seid, acp_seid, media_codec, 0); } -void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec){ +void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec){ if (!callback) return; - avdtp_signaling_emit_media_codec_other(callback, con_handle, media_codec, 1); + avdtp_signaling_emit_media_codec_other(callback, avdtp_cid, int_seid, acp_seid, media_codec, 1); } @@ -773,14 +772,27 @@ void avdtp_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l } uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t seid){ - if (stream_endpoint->remote_seps[stream_endpoint->remote_sep_index].seid == seid){ + if (!stream_endpoint->connection) return 0xFF; + if (stream_endpoint->connection->remote_seps[stream_endpoint->remote_sep_index].seid == seid){ return stream_endpoint->remote_sep_index; } int i; - for (i=0; i < stream_endpoint->remote_seps_num; i++){ - if (stream_endpoint->remote_seps[i].seid == seid){ + for (i=0; i < stream_endpoint->connection->remote_seps_num; i++){ + if (stream_endpoint->connection->remote_seps[i].seid == seid){ return i; } } return 0xFF; } + +uint8_t avdtp_find_remote_sep(avdtp_connection_t * connection, uint8_t remote_seid){ + if (!connection) return 0xFF; + int i; + for (i = 0; i < connection->remote_seps_num; i++){ + if (connection->remote_seps[i].seid == remote_seid){ + return i; + } + } + return 0xFF; +} + \ No newline at end of file diff --git a/src/classic/avdtp_util.h b/src/classic/avdtp_util.h index 4650e6626..58d22ba40 100644 --- a/src/classic/avdtp_util.h +++ b/src/classic/avdtp_util.h @@ -55,7 +55,6 @@ extern "C" { #define AVDTP_INVALID_SEP_INDEX 0xff avdtp_connection_t * avdtp_connection_for_bd_addr(bd_addr_t addr, avdtp_context_t * context); -avdtp_connection_t * avdtp_connection_for_con_handle(hci_con_handle_t con_handle, avdtp_context_t * context); avdtp_connection_t * avdtp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid, avdtp_context_t * context); avdtp_stream_endpoint_t * avdtp_stream_endpoint_for_l2cap_cid(uint16_t l2cap_cid, avdtp_context_t * context); avdtp_stream_endpoint_t * avdtp_stream_endpoint_with_seid(uint8_t seid, avdtp_context_t * context); @@ -74,20 +73,20 @@ uint16_t avdtp_unpack_service_capabilities(avdtp_connection_t * connection, avdt void avdtp_prepare_capabilities(avdtp_signaling_packet_t * signaling_packet, uint8_t transaction_label, uint16_t registered_service_categories, avdtp_capabilities_t configuration, uint8_t identifier); int avdtp_signaling_create_fragment(uint16_t cid, avdtp_signaling_packet_t * signaling_packet, uint8_t * out_buffer); -void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, bd_addr_t addr, uint8_t status); -void avdtp_streaming_emit_connection_established(btstack_packet_handler_t callback, uint16_t con_handle, uint8_t status); -void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_sep_t sep); -void avdtp_signaling_emit_accept(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier, uint8_t status); -void avdtp_signaling_emit_general_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier); -void avdtp_signaling_emit_reject(btstack_packet_handler_t callback, uint16_t con_handle, avdtp_signal_identifier_t identifier); +void avdtp_signaling_emit_connection_established(btstack_packet_handler_t callback, uint16_t avdtp_cid, bd_addr_t addr, uint8_t status); +void avdtp_streaming_emit_connection_established(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, uint8_t status); +void avdtp_signaling_emit_sep(btstack_packet_handler_t callback, uint16_t avdtp_cid, avdtp_sep_t sep); +void avdtp_signaling_emit_accept(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t seid, avdtp_signal_identifier_t identifier, uint8_t status); +void avdtp_signaling_emit_general_reject(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, avdtp_signal_identifier_t identifier); +void avdtp_signaling_emit_reject(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, avdtp_signal_identifier_t identifier); +void avdtp_streaming_emit_can_send_media_packet_now(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint16_t sequence_number); - -void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); -void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); -void avdtp_signaling_emit_media_codec_sbc_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); -void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); -void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); -void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t con_handle, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_sbc_capability(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_other_capability(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_sbc_configuration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_other_configuration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_sbc_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); +void avdtp_signaling_emit_media_codec_other_reconfiguration(btstack_packet_handler_t callback, uint16_t avdtp_cid, uint8_t int_seid, uint8_t acp_seid, adtvp_media_codec_capabilities_t media_codec); void avdtp_request_can_send_now_acceptor(avdtp_connection_t * connection, uint16_t l2cap_cid); void avdtp_request_can_send_now_initiator(avdtp_connection_t * connection, uint16_t l2cap_cid); @@ -96,6 +95,7 @@ void avdtp_request_can_send_now_self(avdtp_connection_t * connection, uint16_t l uint8_t avdtp_get_index_of_remote_stream_endpoint_with_seid(avdtp_stream_endpoint_t * stream_endpoint, uint16_t acp_seid); void avdtp_initialize_stream_endpoint(avdtp_stream_endpoint_t * stream_endpoint); +uint8_t avdtp_find_remote_sep(avdtp_connection_t * connection, uint8_t remote_seid); #if defined __cplusplus } diff --git a/test/avdtp/.gitignore b/test/avdtp/.gitignore index cf01bc6f6..00ca44b15 100644 --- a/test/avdtp/.gitignore +++ b/test/avdtp/.gitignore @@ -1,4 +1,4 @@ -avdtp_source_test +avdtp_source_demo avdtp_sink_test portaudio_test sine_encode_decode_test diff --git a/test/avdtp/Makefile b/test/avdtp/Makefile index 4fd85e884..c9a5e26e5 100644 --- a/test/avdtp/Makefile +++ b/test/avdtp/Makefile @@ -28,6 +28,8 @@ COMMON += \ sdp_server.c \ sdp_util.c \ wav_util.c \ + sdp_server.c \ + sdp_client.c \ CFLAGS += -g -Wall -Wmissing-prototypes -Wstrict-prototypes -Wshadow -Wunused-variable -Wunused-parameter -Werror CFLAGS += -I. @@ -35,6 +37,8 @@ CFLAGS += -I${BTSTACK_ROOT}/src CFLAGS += -I${BTSTACK_ROOT}/src/classic CFLAGS += -I${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include -D OI_DEBUG CFLAGS += -I${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include +CFLAGS += -I${BTSTACK_ROOT}/3rd-party/hxcmod-player +CFLAGS += -I${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods CFLAGS += -I${BTSTACK_ROOT}/platform/posix CFLAGS += -I${BTSTACK_ROOT}/platform/embedded CFLAGS += -I${BTSTACK_ROOT}/port/libusb @@ -46,6 +50,8 @@ VPATH += ${BTSTACK_ROOT}/platform/libusb VPATH += ${BTSTACK_ROOT}/port/libusb VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/srce +VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player +VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods # use pkg-config for libusb CFLAGS += $(shell pkg-config libusb-1.0 --cflags) @@ -63,42 +69,50 @@ SBC_ENCODER += \ ${BTSTACK_ROOT}/src/classic/btstack_sbc_bludroid.c \ ${BTSTACK_ROOT}/src/classic/hfp_msbc.c \ -AVDTP_SINK += \ +AVDTP += \ avdtp_util.c \ avdtp.c \ avdtp_initiator.c \ avdtp_acceptor.c \ avdtp_source.c \ avdtp_sink.c \ + a2dp_source.c \ + a2dp_sink.c \ btstack_ring_buffer.c \ -AVDTP_TESTS = avdtp_source_test avdtp_sink_test portaudio_test sine_encode_decode_ring_buffer_test sine_encode_decode_test sine_encode_decode_performance_test +HXCMOD_PLAYER = \ + ${BTSTACK_ROOT}/3rd-party/hxcmod-player/hxcmod.c \ + ${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c \ + +AVDTP_TESTS = avdtp_source_demo avdtp_sink_test portaudio_test +#sine_encode_decode_ring_buffer_test sine_encode_decode_test sine_encode_decode_performance_test CORE_OBJ = $(CORE:.c=.o) COMMON_OBJ = $(COMMON:.c=.o) SBC_DECODER_OBJ = $(SBC_DECODER:.c=.o) SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o) -AVDTP_SINK_OBJ = $(AVDTP_SINK:.c=.o) +AVDTP_OBJ = $(AVDTP:.c=.o) +HXCMOD_PLAYER_OBJ = $(HXCMOD_PLAYER:.c=.o) all: ${AVDTP_TESTS} -avdtp_sink_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_SINK_OBJ} avdtp_sink_test.o +avdtp_sink_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} avdtp_sink_test.o ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -avdtp_source_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_SINK_OBJ} avdtp_source_test.o +avdtp_source_demo: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} ${HXCMOD_PLAYER_OBJ} avdtp_source_demo.o ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ portaudio_test: btstack_util.o hci_dump.o wav_util.o btstack_ring_buffer.o portaudio_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -sine_encode_decode_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_SINK_OBJ} sine_encode_decode_test.c +sine_encode_decode_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} sine_encode_decode_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -sine_encode_decode_ring_buffer_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_SINK_OBJ} sine_encode_decode_ring_buffer_test.c +sine_encode_decode_ring_buffer_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} sine_encode_decode_ring_buffer_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -sine_encode_decode_performance_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_SINK_OBJ} sine_encode_decode_performance_test.c +sine_encode_decode_performance_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} sine_encode_decode_performance_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ diff --git a/test/avdtp/avdtp_sink_test.c b/test/avdtp/avdtp_sink_test.c index 9f05090ed..2d465c76d 100644 --- a/test/avdtp/avdtp_sink_test.c +++ b/test/avdtp/avdtp_sink_test.c @@ -54,6 +54,7 @@ #include "l2cap.h" #include "stdin_support.h" #include "avdtp_sink.h" +#include "a2dp_sink.h" #include "btstack_sbc.h" #include "wav_util.h" @@ -133,13 +134,13 @@ typedef struct { } avdtp_media_codec_configuration_sbc_t; // mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; -// pts: -static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5}; -// mac 2013: static bd_addr_t remote = {0x84, 0x38, 0x35, 0x65, 0xd1, 0x15}; +// pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5}; +// mac 2013: +static bd_addr_t remote = {0x84, 0x38, 0x35, 0x65, 0xd1, 0x15}; // bt dongle: -u 02-02 static bd_addr_t remote = {0x00, 0x02, 0x72, 0xDC, 0x31, 0xC1}; -static uint16_t con_handle = 0; +static uint16_t avdtp_cid = 0; static uint8_t sdp_avdtp_sink_service_buffer[150]; static avdtp_sep_t sep; @@ -442,8 +443,8 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe case HCI_EVENT_AVDTP_META: switch (packet[2]){ case AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED: - con_handle = avdtp_subevent_signaling_connection_established_get_con_handle(packet); - printf("\n --- avdtp_test: AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED, con handle 0x%02x ---\n", con_handle); + avdtp_cid = avdtp_subevent_signaling_connection_established_get_avdtp_cid(packet); + printf("\n --- avdtp_test: AVDTP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED, cid 0x%02x ---\n", avdtp_cid); break; case AVDTP_SUBEVENT_SIGNALING_SEP_FOUND: if (app_state != AVDTP_APPLICATION_W2_DISCOVER_SEPS) return; @@ -477,7 +478,9 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe sbc_configuration.max_bitpool_value = avdtp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet); sbc_configuration.frames_per_buffer = sbc_configuration.subbands * sbc_configuration.block_length; dump_sbc_configuration(sbc_configuration); - + // // TODO: use actual config + // btstack_sbc_encoder_init(&local_stream_endpoint->sbc_encoder_state, SBC_MODE_STANDARD, 16, 8, 2, 44100, 53); + if (sbc_configuration.reconfigure){ close_media_processing(); init_media_processing(sbc_configuration); @@ -486,6 +489,8 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe } break; } + case AVDTP_SUBEVENT_STREAMING_CONNECTION_ESTABLISHED: + break; case AVDTP_SUBEVENT_SIGNALING_MEDIA_CODEC_OTHER_CAPABILITY: printf(" received non SBC codec. not implemented\n"); break; @@ -528,19 +533,19 @@ static void show_usage(void){ printf("---\n"); } -static const uint8_t media_sbc_codec_capabilities[] = { +static uint8_t media_sbc_codec_capabilities[] = { 0xFF,//(AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO, 0xFF,//(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS, 2, 53 }; -static const uint8_t media_sbc_codec_configuration[] = { +static uint8_t media_sbc_codec_configuration[] = { (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO, (AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS, 2, 53 }; -static const uint8_t media_sbc_codec_reconfiguration[] = { +static uint8_t media_sbc_codec_reconfiguration[] = { (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO, (AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_SNR, 2, 53 @@ -560,23 +565,23 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac break; case 'C': printf("Disconnect not implemented\n"); - avdtp_sink_disconnect(con_handle); + avdtp_sink_disconnect(avdtp_cid); break; case 'd': app_state = AVDTP_APPLICATION_W2_DISCOVER_SEPS; - avdtp_sink_discover_stream_endpoints(con_handle); + avdtp_sink_discover_stream_endpoints(avdtp_cid); break; case 'g': app_state = AVDTP_APPLICATION_W2_GET_CAPABILITIES; - avdtp_sink_get_capabilities(con_handle, sep.seid); + avdtp_sink_get_capabilities(avdtp_cid, sep.seid); break; case 'a': app_state = AVDTP_APPLICATION_W2_GET_ALL_CAPABILITIES; - avdtp_sink_get_all_capabilities(con_handle, sep.seid); + avdtp_sink_get_all_capabilities(avdtp_cid, sep.seid); break; case 'f': app_state = AVDTP_APPLICATION_W2_GET_CONFIGURATION; - avdtp_sink_get_configuration(con_handle, sep.seid); + avdtp_sink_get_configuration(avdtp_cid, sep.seid); break; case 's': app_state = AVDTP_APPLICATION_W2_SET_CONFIGURATION; @@ -585,7 +590,7 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC; remote_configuration.media_codec.media_codec_information_len = sizeof(media_sbc_codec_configuration); remote_configuration.media_codec.media_codec_information = media_sbc_codec_configuration; - avdtp_sink_set_configuration(con_handle, local_stream_endpoint->sep.seid, sep.seid, remote_configuration_bitmap, remote_configuration); + avdtp_sink_set_configuration(avdtp_cid, local_stream_endpoint->sep.seid, sep.seid, remote_configuration_bitmap, remote_configuration); break; case 'R': app_state = AVDTP_APPLICATION_W2_RECONFIGURE_WITH_SEID; @@ -594,27 +599,28 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac remote_configuration.media_codec.media_codec_type = AVDTP_CODEC_SBC; remote_configuration.media_codec.media_codec_information_len = sizeof(media_sbc_codec_reconfiguration); remote_configuration.media_codec.media_codec_information = media_sbc_codec_reconfiguration; - avdtp_sink_reconfigure(con_handle, sep.seid, remote_configuration_bitmap, remote_configuration); + avdtp_sink_reconfigure(avdtp_cid, local_stream_endpoint->sep.seid, sep.seid, remote_configuration_bitmap, remote_configuration); break; case 'o': app_state = AVDTP_APPLICATION_W2_OPEN_STREAM_WITH_SEID; - avdtp_sink_open_stream(con_handle, sep.seid); + avdtp_sink_open_stream(avdtp_cid, local_stream_endpoint->sep.seid, sep.seid); break; case 'm': + printf("AVDTP_APPLICATION_W2_START_STREAM_WITH_SEID \n"); app_state = AVDTP_APPLICATION_W2_START_STREAM_WITH_SEID; - avdtp_sink_start_stream(con_handle, sep.seid); + avdtp_sink_start_stream(local_stream_endpoint->sep.seid); break; case 'A': app_state = AVDTP_APPLICATION_W2_ABORT_STREAM_WITH_SEID; - avdtp_sink_abort_stream(con_handle, sep.seid); + avdtp_sink_abort_stream(local_stream_endpoint->sep.seid); break; case 'S': app_state = AVDTP_APPLICATION_W2_STOP_STREAM_WITH_SEID; - avdtp_sink_stop_stream(con_handle, sep.seid); + avdtp_sink_stop_stream(local_stream_endpoint->sep.seid); break; case 'P': app_state = AVDTP_APPLICATION_W2_SUSPEND_STREAM_WITH_SEID; - avdtp_sink_suspend(con_handle, sep.seid); + avdtp_sink_suspend(local_stream_endpoint->sep.seid); break; case '\n': diff --git a/test/avdtp/avdtp_source_demo.c b/test/avdtp/avdtp_source_demo.c new file mode 100644 index 000000000..c6108946f --- /dev/null +++ b/test/avdtp/avdtp_source_demo.c @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH 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 MATTHIAS + * RINGWALD OR CONTRIBUTORS 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. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack_config.h" +#include "btstack_debug.h" +#include "btstack_event.h" +#include "btstack_memory.h" +#include "btstack_run_loop.h" +#include "gap.h" +#include "hci.h" +#include "hci_cmd.h" +#include "hci_dump.h" +#include "l2cap.h" +#include "stdin_support.h" +#include "avdtp_source.h" +#include "a2dp_source.h" + +#include "btstack_sbc.h" +#include "sbc_encoder.h" +#include "avdtp_util.h" +#include "hxcmod.h" +#include "mod.h" + +#define NUM_CHANNELS 2 +#define A2DP_SAMPLE_RATE 44100 +#define BYTES_PER_AUDIO_SAMPLE (2*NUM_CHANNELS) +#define FILL_AUDIO_BUFFER_TIMEOUT_MS 10 + +#ifndef M_PI +#define M_PI 3.14159265 +#endif +#define TABLE_SIZE_441HZ 100 + +typedef struct { + int left_phase; + int right_phase; +} paTestData; + +typedef enum { + STREAM_SINE, + STREAM_MOD +} stream_data_source_t; + +static uint8_t media_sbc_codec_capabilities[] = { + (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO, + 0xFF,//(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS, + 2, 53 +}; + +static const int16_t sine_int16[] = { + 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, + 19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466, + 31163, 31738, 32187, 32509, 32702, 32767, 32702, 32509, 32187, 31738, + 31163, 30466, 29648, 28714, 27666, 26509, 25247, 23886, 22431, 20886, + 19260, 17557, 15786, 13952, 12062, 10126, 8149, 6140, 4107, 2057, + 0, -2057, -4107, -6140, -8149, -10126, -12062, -13952, -15786, -17557, +-19260, -20886, -22431, -23886, -25247, -26509, -27666, -28714, -29648, -30466, +-31163, -31738, -32187, -32509, -32702, -32767, -32702, -32509, -32187, -31738, +-31163, -30466, -29648, -28714, -27666, -26509, -25247, -23886, -22431, -20886, +-19260, -17557, -15786, -13952, -12062, -10126, -8149, -6140, -4107, -2057, +}; + +static char * device_name = "A2DP Source BTstack"; + +// mac 2011: static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; +// pts: static bd_addr_t remote = {0x00, 0x1B, 0xDC, 0x08, 0x0A, 0xA5}; +// mac 2013: static bd_addr_t remote = {0x84, 0x38, 0x35, 0x65, 0xd1, 0x15}; +// phone 2013: static bd_addr_t remote = {0xD8, 0xBB, 0x2C, 0xDF, 0xF0, 0xF2}; +// minijambox: +static bd_addr_t remote = {0x00, 0x21, 0x3c, 0xac, 0xf7, 0x38}; +// head phones: static bd_addr_t remote = {0x00, 0x18, 0x09, 0x28, 0x50, 0x18}; +// bt dongle: -u 02-04-01 +// static bd_addr_t remote = {0x00, 0x15, 0x83, 0x5F, 0x9D, 0x46}; + + +static uint8_t sdp_avdtp_source_service_buffer[150]; +static uint8_t media_sbc_codec_configuration[4]; + +typedef struct { + uint16_t a2dp_cid; + uint8_t local_seid; + + uint32_t time_audio_data_sent; // ms + uint32_t acc_num_missed_samples; + uint32_t samples_ready; + btstack_timer_source_t fill_audio_buffer_timer; + uint8_t streaming; + + int max_media_payload_size; + + uint8_t sbc_storage[1030]; + uint16_t sbc_storage_count; + uint8_t sbc_ready_to_send; + +} a2dp_media_sending_context_t; + +static a2dp_media_sending_context_t media_tracker; + +static paTestData sin_data; + +static int hxcmod_initialized = 0; +static modcontext mod_context; +static tracker_buffer_state trkbuf; + +static uint8_t local_seid = 0; +stream_data_source_t data_source = STREAM_SINE; + +static btstack_packet_callback_registration_t hci_event_callback_registration; + +static void a2dp_fill_audio_buffer_timer_start(a2dp_media_sending_context_t * context); +static void a2dp_fill_audio_buffer_timer_stop(a2dp_media_sending_context_t * context); +static void a2dp_fill_audio_buffer_timer_pause(a2dp_media_sending_context_t * context); + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + switch (packet_type) { + + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_A2DP_META: + switch (packet[2]){ + case A2DP_SUBEVENT_STREAM_ESTABLISHED: + media_tracker.local_seid = a2dp_subevent_stream_established_get_local_seid(packet); + media_tracker.a2dp_cid = a2dp_subevent_stream_established_get_a2dp_cid(packet); + printf(" --- application --- A2DP_SUBEVENT_STREAM_ESTABLISHED, a2dp_cid 0x%02x, local seid %d, remote seid %d\n", + media_tracker.a2dp_cid, media_tracker.local_seid, a2dp_subevent_stream_established_get_remote_seid(packet)); + break; + + case A2DP_SUBEVENT_STREAM_START_ACCEPTED: + if (local_seid != media_tracker.local_seid) break; + if (!a2dp_source_stream_endpoint_ready(media_tracker.local_seid)) break; + a2dp_fill_audio_buffer_timer_start(&media_tracker); + printf(" --- application --- A2DP_SUBEVENT_STREAM_START_ACCEPTED, local seid %d\n", media_tracker.local_seid); + break; + + case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW:{ + if (local_seid != media_tracker.local_seid) break; + + int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length(); + int bytes_in_storage = media_tracker.sbc_storage_count; + uint8_t num_frames = bytes_in_storage / num_bytes_in_frame; + + a2dp_source_stream_send_media_payload(media_tracker.local_seid, media_tracker.sbc_storage, bytes_in_storage, num_frames, 0); + media_tracker.sbc_storage_count = 0; + media_tracker.sbc_ready_to_send = 0; + break; + } + case A2DP_SUBEVENT_STREAM_SUSPENDED: + printf(" --- application --- A2DP_SUBEVENT_STREAM_SUSPENDED, local seid %d\n", media_tracker.local_seid); + a2dp_fill_audio_buffer_timer_pause(&media_tracker); + break; + + case A2DP_SUBEVENT_STREAM_RELEASED: + printf(" --- application --- A2DP_SUBEVENT_STREAM_RELEASED, local seid %d\n", media_tracker.local_seid); + a2dp_fill_audio_buffer_timer_stop(&media_tracker); + break; + default: + printf(" --- application --- not implemented\n"); + break; + } + break; + default: + break; + } + break; + default: + // other packet type + break; + } +} + +static void show_usage(void){ + bd_addr_t iut_address; + gap_local_bd_addr(iut_address); + printf("\n--- Bluetooth AVDTP SOURCE Test Console %s ---\n", bd_addr_to_str(iut_address)); + printf("c - create connection to addr %s\n", bd_addr_to_str(remote)); + printf("C - disconnect\n"); + printf("x - start streaming sine\n"); + if (hxcmod_initialized){ + printf("z - start streaming '%s'\n", mod_name); + } + printf("p - pause streaming\n"); + printf("X - stop streaming\n"); + printf("Ctrl-c - exit\n"); + printf("---\n"); +} + +static void produce_sine_audio(int16_t * pcm_buffer, void *user_data, int num_samples_to_write){ + paTestData *data = (paTestData*)user_data; + int count; + for (count = 0; count < num_samples_to_write ; count++){ + pcm_buffer[count * 2] = sine_int16[data->left_phase]; + pcm_buffer[count * 2 + 1] = sine_int16[data->right_phase]; + + data->left_phase += 1; + if (data->left_phase >= TABLE_SIZE_441HZ){ + data->left_phase -= TABLE_SIZE_441HZ; + } + data->right_phase += 1; + if (data->right_phase >= TABLE_SIZE_441HZ){ + data->right_phase -= TABLE_SIZE_441HZ; + } + } +} + +static void produce_mod_audio(int16_t * pcm_buffer, int num_samples_to_write){ + hxcmod_fillbuffer(&mod_context, (unsigned short *) &pcm_buffer[0], num_samples_to_write, &trkbuf); +} + +static void produce_audio(int16_t * pcm_buffer, int num_samples){ + switch (data_source){ + case STREAM_SINE: + produce_sine_audio(pcm_buffer, &sin_data, num_samples); + break; + case STREAM_MOD: + produce_mod_audio(pcm_buffer, num_samples); + break; + } +} + +static int fill_sbc_audio_buffer(a2dp_media_sending_context_t * context){ + // perform sbc encodin + int total_num_bytes_read = 0; + int num_audio_samples_per_sbc_buffer = btstack_sbc_encoder_num_audio_frames(); + while (context->samples_ready >= num_audio_samples_per_sbc_buffer + && (context->max_media_payload_size - context->sbc_storage_count) >= btstack_sbc_encoder_sbc_buffer_length()){ + + uint8_t pcm_frame[256*BYTES_PER_AUDIO_SAMPLE]; + + produce_audio((int16_t *) pcm_frame, num_audio_samples_per_sbc_buffer); + btstack_sbc_encoder_process_data((int16_t *) pcm_frame); + + uint16_t sbc_frame_size = btstack_sbc_encoder_sbc_buffer_length(); + uint8_t * sbc_frame = btstack_sbc_encoder_sbc_buffer(); + + total_num_bytes_read += num_audio_samples_per_sbc_buffer; + memcpy(&context->sbc_storage[context->sbc_storage_count], sbc_frame, sbc_frame_size); + context->sbc_storage_count += sbc_frame_size; + context->samples_ready -= num_audio_samples_per_sbc_buffer; + } + return total_num_bytes_read; +} + +static void avdtp_fill_audio_buffer_timeout_handler(btstack_timer_source_t * timer){ + log_info("timer"); + a2dp_media_sending_context_t * context = (a2dp_media_sending_context_t *) btstack_run_loop_get_timer_context(timer); + btstack_run_loop_set_timer(&context->fill_audio_buffer_timer, FILL_AUDIO_BUFFER_TIMEOUT_MS); + btstack_run_loop_add_timer(&context->fill_audio_buffer_timer); + uint32_t now = btstack_run_loop_get_time_ms(); + + uint32_t update_period_ms = FILL_AUDIO_BUFFER_TIMEOUT_MS; + if (context->time_audio_data_sent > 0){ + update_period_ms = now - context->time_audio_data_sent; + } + + uint32_t num_samples = (update_period_ms * A2DP_SAMPLE_RATE) / 1000; + context->acc_num_missed_samples += (update_period_ms * A2DP_SAMPLE_RATE) % 1000; + + while (context->acc_num_missed_samples >= 1000){ + num_samples++; + context->acc_num_missed_samples -= 1000; + } + context->time_audio_data_sent = now; + context->samples_ready += num_samples; + + if (!context->sbc_ready_to_send){ + fill_sbc_audio_buffer(context); + } + + if ((context->sbc_storage_count + btstack_sbc_encoder_sbc_buffer_length()) > context->max_media_payload_size){ + // schedule sending + context->sbc_ready_to_send = 1; + a2dp_source_stream_endpoint_request_can_send_now(context->local_seid); + } +} + +static void a2dp_fill_audio_buffer_timer_start(a2dp_media_sending_context_t * context){ + context->max_media_payload_size = a2dp_max_media_payload_size(context->local_seid); + context->sbc_storage_count = 0; + context->sbc_ready_to_send = 0; + context->streaming = 1; + btstack_run_loop_remove_timer(&context->fill_audio_buffer_timer); + btstack_run_loop_set_timer_handler(&context->fill_audio_buffer_timer, avdtp_fill_audio_buffer_timeout_handler); + btstack_run_loop_set_timer_context(&context->fill_audio_buffer_timer, context); + btstack_run_loop_set_timer(&context->fill_audio_buffer_timer, FILL_AUDIO_BUFFER_TIMEOUT_MS); + btstack_run_loop_add_timer(&context->fill_audio_buffer_timer); +} + +static void a2dp_fill_audio_buffer_timer_stop(a2dp_media_sending_context_t * context){ + context->time_audio_data_sent = 0; + context->acc_num_missed_samples = 0; + context->samples_ready = 0; + context->streaming = 1; + context->sbc_storage_count = 0; + context->sbc_ready_to_send = 0; + btstack_run_loop_remove_timer(&context->fill_audio_buffer_timer); +} + +static void a2dp_fill_audio_buffer_timer_pause(a2dp_media_sending_context_t * context){ + // context->time_audio_data_sent = 0; + btstack_run_loop_remove_timer(&context->fill_audio_buffer_timer); +} + +static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ + UNUSED(ds); + UNUSED(callback_type); + + int cmd = btstack_stdin_read(); + + switch (cmd){ + case 'c': + printf("Creating L2CAP Connection to %s, PSM_AVDTP\n", bd_addr_to_str(remote)); + a2dp_source_establish_stream(remote, local_seid); + break; + case 'C': + printf("Disconnect not implemented\n"); + a2dp_source_disconnect(media_tracker.a2dp_cid); + break; + case 'x': + printf("Stream sine, local seid %d\n", media_tracker.local_seid); + data_source = STREAM_SINE; + a2dp_source_start_stream(media_tracker.local_seid); + break; + case 'z': + printf("Stream mode, local seid %d\n", media_tracker.local_seid); + data_source = STREAM_MOD; + a2dp_source_start_stream(media_tracker.local_seid); + break; + case 'p': + printf("Pause stream, local seid %d\n", media_tracker.local_seid); + a2dp_source_pause_stream(media_tracker.local_seid); + break; + case 'X': + printf("Close stream, local seid %d\n", media_tracker.local_seid); + a2dp_source_release_stream(media_tracker.local_seid); + break; + default: + show_usage(); + break; + } +} + +int btstack_main(int argc, const char * argv[]); +int btstack_main(int argc, const char * argv[]){ + UNUSED(argc); + (void)argv; + + /* Register for HCI events */ + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + l2cap_init(); + // Initialize AVDTP Sink + a2dp_source_init(); + a2dp_source_register_packet_handler(&packet_handler); + + //#ifndef SMG_BI + local_seid = a2dp_source_create_stream_endpoint(AVDTP_AUDIO, AVDTP_CODEC_SBC, media_sbc_codec_capabilities, sizeof(media_sbc_codec_capabilities), media_sbc_codec_configuration, sizeof(media_sbc_codec_configuration)); + + // Initialize SDP + sdp_init(); + memset(sdp_avdtp_source_service_buffer, 0, sizeof(sdp_avdtp_source_service_buffer)); + a2dp_source_create_sdp_record(sdp_avdtp_source_service_buffer, 0x10002, 1, NULL, NULL); + sdp_register_service(sdp_avdtp_source_service_buffer); + + gap_set_local_name(device_name); + gap_discoverable_control(1); + gap_set_class_of_device(0x200408); + + hxcmod_initialized = hxcmod_init(&mod_context); + if (hxcmod_initialized){ + hxcmod_setcfg(&mod_context, A2DP_SAMPLE_RATE, 16, 1, 1, 1); + hxcmod_load(&mod_context, (void *) &mod_data, mod_len); + printf("loaded mod '%s', size %u\n", mod_name, mod_len); + } + + // turn on! + hci_power_control(HCI_POWER_ON); + + btstack_stdin_setup(stdin_process); + return 0; +} diff --git a/test/pts/.gitignore b/test/pts/.gitignore index da7bc00d7..0a084242c 100644 --- a/test/pts/.gitignore +++ b/test/pts/.gitignore @@ -1,3 +1,4 @@ +avdtp_source_test ancs_client ble_central_test ble_central_test.h diff --git a/test/pts/Makefile b/test/pts/Makefile index 63b5cd016..66b8cf892 100644 --- a/test/pts/Makefile +++ b/test/pts/Makefile @@ -1,32 +1,49 @@ # Makefile for libusb based PTS tests BTSTACK_ROOT = ../.. + CORE += \ btstack_memory.c \ btstack_linked_list.c \ btstack_memory_pool.c \ btstack_run_loop.c \ btstack_util.c \ - main.c \ - stdin_support.c \ + main.c \ + stdin_support.c \ - COMMON += \ hci.c \ hci_cmd.c \ hci_dump.c \ l2cap.c \ l2cap_signaling.c \ - hci_transport_h2_libusb.c \ - btstack_run_loop_posix.c \ - btstack_link_key_db_fs.c \ - le_device_db_fs.c \ - wav_util.c \ + hci_transport_h2_libusb.c \ + btstack_run_loop_posix.c \ + btstack_link_key_db_fs.c \ + le_device_db_fs.c \ + wav_util.c \ sdp_util.c \ - sdp_server.c \ + sdp_server.c \ +SBC_DECODER += \ + ${BTSTACK_ROOT}/src/classic/btstack_sbc_plc.c \ + ${BTSTACK_ROOT}/src/classic/btstack_sbc_bludroid.c \ + +SBC_ENCODER += \ + ${BTSTACK_ROOT}/src/classic/btstack_sbc_bludroid.c \ + ${BTSTACK_ROOT}/src/classic/hfp_msbc.c \ + +AVDTP += \ + avdtp_util.c \ + avdtp.c \ + avdtp_initiator.c \ + avdtp_acceptor.c \ + avdtp_source.c \ + btstack_ring_buffer.c \ include ${BTSTACK_ROOT}/example/Makefile.inc +include ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/Makefile.inc +include ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/Makefile.inc CFLAGS += -g -Wall -Wmissing-prototypes -Wstrict-prototypes -Wshadow -Wunused-variable -Wunused-parameter -Werror CFLAGS += -I. @@ -44,31 +61,34 @@ VPATH += ${BTSTACK_ROOT}/src/classic VPATH += ${BTSTACK_ROOT}/platform/posix VPATH += ${BTSTACK_ROOT}/platform/libusb VPATH += ${BTSTACK_ROOT}/port/libusb -# VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce -# VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/srce +VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce +VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/srce # use pkg-config for libusb CFLAGS += $(shell pkg-config libusb-1.0 --cflags) LDFLAGS += $(shell pkg-config libusb-1.0 --libs) -# hard coded flags for libusb in /usr/local/lib -# CFLAGS += -I/usr/local/include -# LDFLAGS += -L/usr/local/lib -lusb-1.0 - # use pkg-config for portaudio -# CFLAGS += $(shell pkg-config portaudio-2.0 --cflags) -# LDFLAGS += $(shell pkg-config portaudio-2.0 --libs) -# hard coded flags for portaudio in /usr/local/lib -# CFLAGS += -I/usr/local/include -# LDFLAGS += -L/sw/lib -lportaudio -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,Carbon +CFLAGS += $(shell pkg-config portaudio-2.0 --cflags) -DHAVE_PORTAUDIO +LDFLAGS += $(shell pkg-config portaudio-2.0 --libs) + +CORE_OBJ = $(CORE:.c=.o) +COMMON_OBJ = $(COMMON:.c=.o) +SBC_DECODER_OBJ = $(SBC_DECODER:.c=.o) +SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o) +AVDTP_OBJ = $(AVDTP:.c=.o) EXAMPLES = iopt ble_peripheral_test ble_central_test l2cap_test classic_test bnep_test hsp_ag_test hsp_hs_test sco_loopback le_data_channel -EXAMPLES = le_data_channel +EXAMPLES = avdtp_source_test le_data_channel all: ${EXAMPLES} # compile .gatt descriptions +avdtp_source_test: ${CORE_OBJ} ${COMMON_OBJ} ${SBC_DECODER_OBJ} ${SM_OBJ} ${SBC_ENCODER_OBJ} ${AVDTP_OBJ} avdtp_source_test.o + ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ + ble_peripheral_test.h: ble_peripheral_test.gatt python ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@ + ble_central_test.h: ble_central_test.gatt python ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@ @@ -101,4 +121,6 @@ sco_loopback: ${CORE_OBJ} ${COMMON_OBJ} sco_loopback.c iopt: ${CORE_OBJ} ${COMMON_OBJ} pan.o hsp_ag.o hsp_hs.o hfp_ag.o hfp_hf.o hfp_gsm_model.o iopt.c hfp.o ${SDP_CLIENT} ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ - \ No newline at end of file + +clean: + rm -rf *.pyc *.o $(AVDTP_TESTS) *.dSYM *_test *.wav *.sbc ${BTSTACK_ROOT}/port/libusb/*.o \ No newline at end of file diff --git a/test/avdtp/avdtp_source_test.c b/test/pts/avdtp_source_test.c similarity index 99% rename from test/avdtp/avdtp_source_test.c rename to test/pts/avdtp_source_test.c index 2c154d3f7..312914b46 100644 --- a/test/avdtp/avdtp_source_test.c +++ b/test/pts/avdtp_source_test.c @@ -54,9 +54,10 @@ #include "hci_dump.h" #include "l2cap.h" #include "stdin_support.h" -#include "avdtp_source.h" #include "btstack_sbc.h" +#include "avdtp.h" +#include "avdtp_source.h" #include "avdtp_util.h" typedef struct { @@ -112,7 +113,7 @@ static const char * avdtp_si_name[] = { "AVDTP_SI_GET_CAPABILITIES", "AVDTP_SI_SET_CONFIGURATION", "AVDTP_SI_GET_CONFIGURATION", - "AVDTP_SI_RECONFIGURE," + "AVDTP_SI_RECONFIGURE", "AVDTP_SI_OPEN", "AVDTP_SI_START", "AVDTP_SI_CLOSE", @@ -455,7 +456,7 @@ int btstack_main(int argc, const char * argv[]){ // Initialize SDP sdp_init(); memset(sdp_avdtp_source_service_buffer, 0, sizeof(sdp_avdtp_source_service_buffer)); - a2dp_sink_create_sdp_record(sdp_avdtp_source_service_buffer, 0x10002, 1, NULL, NULL); + a2dp_source_create_sdp_record(sdp_avdtp_source_service_buffer, 0x10002, 1, NULL, NULL); sdp_register_service(sdp_avdtp_source_service_buffer); gap_set_local_name("BTstack A2DP Source Test"); diff --git a/test/sbc/sine_table.py b/test/sbc/sine_table.py deleted file mode 100755 index f9475062f..000000000 --- a/test/sbc/sine_table.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -import math -import sys - -SAMPLES = 100 -VALUES_PER_LINE = 10 - -print (''' -// input signal: pre-computed sine wave, 160 Hz at 16000 kHz -static const int16_t sine_int16[] = {''') -items = 0 -for sample in range(SAMPLES): - angle = (sample * 360.0) / SAMPLES - sine = math.sin(math.radians(angle)) - rescaled = int(round(sine * 32767)) - print ("%6d, " % rescaled), - items += 1 - if items == VALUES_PER_LINE: - items = 0 - print -print( "};") diff --git a/tool/btstack_event_generator.py b/tool/btstack_event_generator.py index a563ada52..7d9cbf32c 100755 --- a/tool/btstack_event_generator.py +++ b/tool/btstack_event_generator.py @@ -11,6 +11,7 @@ import btstack_parser as parser meta_events = [ 'ANCS', 'AVDTP', + 'A2DP', 'AVRCP', 'GOEP', 'HFP', diff --git a/tool/sine_table_generator.py b/tool/sine_table_generator.py new file mode 100755 index 000000000..c0c67ffd3 --- /dev/null +++ b/tool/sine_table_generator.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +import math +import sys + +# 1HZ == 1 sine +# sample rate at 44100Hz, sine at 441Hz -> 441 sines in 44100 samples -> 44100/441 samples/sine + +sine_array = ''' +// input signal: pre-computed int16 sine wave, {sine_sample_rate} Hz at {sine_frequency} Hz +static const int16_t sine_int16[] = {{''' + +VALUES_PER_LINE = 10 +sine_sample_rate = 44100 +sine_frequency = 441 + +if __name__ == "__main__": + usage = ''' + Usage: ./sine_table.py sine_frequency sine_sample_rate + ''' + + if (len(sys.argv) < 3): + print(usage) + sys.exit(1) + + sine_frequency = int(sys.argv[1]) + sine_sample_rate = int(sys.argv[2]) + + if sine_frequency <= 0: + print(usage) + sys.exit(1) + + if sine_sample_rate <= 0: + print(usage) + sys.exit(1) + + sine_num_samples = sine_sample_rate/sine_frequency + + print(sine_array.format(sine_sample_rate=sine_sample_rate, sine_frequency=sine_frequency)) + items = 0 + for sample in range(0,sine_num_samples): + items = items + 1 + angle = (sample * 360.0) / sine_num_samples + sine = math.sin(math.radians(angle)) + rescaled = int(round(sine * 32767)) + print ("%6d, " % rescaled), + if items == VALUES_PER_LINE: + items = 0 + print + + print( "};") \ No newline at end of file