// Runs on a SECONDARY thread
 @Override
 public void processFrame(android.media.audiofx.Visualizer visualizer, boolean playing) {
   if (!lock.lockLowPriority()) return;
   try {
     if (transmitting) {
       // We use ignoreInput, because sampling 1024, 60 times a seconds,
       // is useless, as there are only 44100 or 48000 samples in one second
       if (ignoreInput == 0) {
         // WE MUST NEVER call any method from visualizer
         // while the player is not actually playing
         if (!playing) Arrays.fill(waveform, 0, 1024, (byte) 0x80);
         else visualizer.getWaveForm(waveform);
       }
       if (framesToSkip <= 0) {
         framesToSkip = framesToSkipOriginal;
         bt.getOutputStream()
             .write(
                 waveform,
                 0,
                 SimpleVisualizerJni.commonProcess(waveform, size | ignoreInput | dataType));
         packetsSent++;
       } else {
         SimpleVisualizerJni.commonProcess(waveform, ignoreInput | dataType);
         framesToSkip--;
       }
       ignoreInput ^= IGNORE_INPUT;
     }
     int stateI = state.getAndSet(0);
     if (stateI != 0) {
       // Build and send a Player state message
       waveform[0] = StartOfHeading;
       waveform[1] = (byte) MessagePlayerState;
       waveform[3] = 0;
       int len = 0;
       len = writeByte(waveform, len, stateI & 3);
       len = writeByte(waveform, len, stateVolume);
       stateI = stateSongPosition;
       len = writeByte(waveform, len, stateI);
       len = writeByte(waveform, len, stateI >> 8);
       len = writeByte(waveform, len, stateI >> 16);
       len = writeByte(waveform, len, stateI >> 24);
       stateI = stateSongLength;
       len = writeByte(waveform, len, stateI);
       len = writeByte(waveform, len, stateI >> 8);
       len = writeByte(waveform, len, stateI >> 16);
       len = writeByte(waveform, len, stateI >> 24);
       waveform[2] = (byte) (len << 1);
       waveform[4 + len] = EndOfTransmission;
       bt.getOutputStream().write(waveform, 0, len + 5);
       packetsSent++;
     }
   } catch (IOException ex) {
     // Bluetooth error
     if (connected) MainHandler.sendMessage(this, MSG_BLUETOOTH_RXTX_ERROR);
   } catch (Throwable ex) {
     ex.printStackTrace();
   } finally {
     lock.releaseLowPriority();
   }
 }
 public BluetoothVisualizerControllerJni(
     ActivityHost activity, boolean startTransmissionOnConnection) {
   waveform = new byte[Visualizer.CAPTURE_SIZE];
   lock = new SlimLock();
   state = new AtomicInteger();
   this.startTransmissionOnConnection = startTransmissionOnConnection;
   lastPlayerCommandTime = (int) SystemClock.uptimeMillis();
   ignoreInput = 0;
   Player.bluetoothVisualizerLastErrorMessage = 0;
   Player.bluetoothVisualizerState = Player.BLUETOOTH_VISUALIZER_STATE_CONNECTING;
   bt = new BluetoothConnectionManager(this);
   final int err = bt.showDialogAndScan(activity);
   if (err != BluetoothConnectionManager.OK) onBluetoothError(bt, err);
   else BackgroundActivityMonitor.start(activity);
 }
 public void destroy() {
   if (waveform != null) {
     version++;
     connected = false;
     transmitting = false;
     Player.bluetoothVisualizerState = Player.BLUETOOTH_VISUALIZER_STATE_INITIAL;
     lock.lockHighPriority();
     try {
       waveform = null;
       if (bt != null) {
         bt.destroy();
         bt = null;
       }
     } finally {
       lock.releaseHighPriority();
     }
     if (fxVisualizer != null) {
       fxVisualizer.destroy();
       fxVisualizer = null;
     }
     Player.stopBluetoothVisualizer();
     BackgroundActivityMonitor.bluetoothEnded();
   }
 }
  @Override
  public void run() {
    final int myVersion = version;
    try {
      final InputStream inputStream = bt.getInputStream();
      int state = 0, payloadLength = 0, payload = 0, currentMessage = 0;
      while (connected && myVersion == version) {
        final int data = inputStream.read();
        if (data == StartOfHeading) {
          // Restart the state machine
          state &= (~(FlagEscape | FlagState));
          continue;
        }
        switch ((state & FlagState)) {
          case 0:
            // This byte should be the message type
            switch (data) {
              case MessageStartBinTransmission:
              case MessageStopBinTransmission:
              case MessagePlayerCommand:
                // Take the state machine to its next state
                currentMessage = data;
                state++;
                continue;
              default:
                // Take the state machine to its error state
                state |= FlagState;
                continue;
            }
          case 1:
            // This should be payload length's first byte
            // (bits 0 - 6 left shifted by 1)
            if ((data & 0x01) != 0) {
              // Take the state machine to its error state
              state |= FlagState;
            } else {
              payloadLength = data >> 1;
              // Take the state machine to its next state
              state++;
            }
            continue;
          case 2:
            // This should be payload length's second byte
            // (bits 7 - 13 left shifted by 1)
            if ((data & 0x01) != 0) {
              // Take the state machine to its error state
              state |= FlagState;
            } else {
              payloadLength |= (data << 6);

              if (currentMessage == MessageStopBinTransmission) {
                if (payloadLength != 0) {
                  // Take the state machine to its error state
                  state |= FlagState;
                  continue;
                }
                // Skip two states as this message has no payload
                state += 2;
              } else {
                if (payloadLength != 1) {
                  if (currentMessage != MessagePlayerCommand || payloadLength != 2) {
                    // Take the state machine to its error state
                    state |= FlagState;
                    continue;
                  }
                }
                // Take the state machine to its next state
                state++;
                payload = 0;
              }
            }
            continue;
          case 3:
            // We are receiving the payload

            if (data == Escape) {
              // Until this date, the only payloads which are
              // valid for reception do not include escapable bytes...

              // Take the state machine to its error state
              state |= FlagState;
              continue;
            }

            if (currentMessage == MessagePlayerCommand) {
              payload = (payload << 8) | data;
              payloadLength--;

              // Keep the machine in state 3
              if (payloadLength > 0) continue;
            } else {
              payload = data;
            }

            // For now, the only payload received is 1 byte long
            state++;
            continue;
          case 4:
            // Take the state machine to its error state
            state |= FlagState;

            // Sanity check: data should be EoT
            if (data == EndOfTransmission)
              // Message correctly received
              MainHandler.sendMessage(this, MSG_PLAYER_COMMAND, currentMessage, payload);
        }
      }
    } catch (IOException ex) {
      // Bluetooth error
      if (connected) MainHandler.sendMessage(this, MSG_BLUETOOTH_RXTX_ERROR);
    } catch (Throwable ex) {
      ex.printStackTrace();
    }
  }