angular - Använda typescript/web audio api på iOS


Jag arbetar på en webbplats som kommer att spela upp ljudfiler med hjälp av Web Audio API. Jag använder TypeScript och Angular 2 i Visual Studio Code. Jag kan få allt att fungera bra på andra plattformar än iOS; men på t.ex. en iPad stöter jag på några förvirrande fel.

Det första problemet är hur man får ett AudioContext som fungerar både på iOS och andra plattformar. Jag vet att det kan göras i vanlig JavaScript, t.ex. http://gopherwoodstudios.com/wa/mp3/test.htm. Behovet av ett knapptryck är irriterande, men jag kan leva med det. Så jag tror att problemet jag har är relaterat till TypeScript. (Jag är nybörjare på TypeScript, så jag kan missförstå några grundläggande saker här.)

På iOS är det du får vid körning fortfarande en webkitAudioContext, inte en AudioContext. Det verkar som att de TypeScript-definitioner jag använder inte förstår hur man hanterar det. Antingen det, eller så vet jag inte hur man berättar för det vad det behöver veta. Ursprungligen hade jag en medlemsvariabel av typen AudioContext, och på icke-iOS-plattformar fungerade allt bra. För att anpassa till iOS försökte jag ändra variabelns typ till any, så att jag kunde sätta in en webkitAudioContext om den existerar och AudioContext inte gör det; den koden ser ut så här (detta är all prototypkod, så felhanteringen är mycket preliminär):

export class PlayerService {
private static _audioContext: any = null;
//......
public static getAudioContext(): any {
  if (!PlayerService._audioContext) {
    try {
      if (typeof AudioContext !== "undefined") {
        // We have an AudioContext type, so use it.
        PlayerService._audioContext = new AudioContext();
      } else if (window['webkitAudioContext'] !== "undefined") {
        // We don't have AudioContext, but we do have webkitAudioContext,
        // so attempt to use that.
        PlayerService._audioContext = Object.create(window['webkitAudioContext'].prototype);
      } else {
        throw new Error('AudioContext not supported. :(');
      }
    } catch (err) {
      console.log(err.message);
      alert('Cannot create audio context.');
      throw err;
    }
  }
  return PlayerService._audioContext;
} 

Detta fungerar fortfarande på icke-iOS-plattformar; och på iOS lyckas det returnera ett webkitAudioContext, komplett med de förväntade metoderna och egenskaperna. (Object.create(...) var det enda sätt jag kunde hitta för att få detta att fungera så här långt.) Senare, dock, när jag anropar decodeAudioData på detta objekt, får jag ett fel som säger

TypeError: Can only call AudioContext.decodeAudioData on instance of AudioContext.

Eftersom variabeln som den försöker anropa metoden decodeAudioData på är typad som 'any' snarare än 'AudioContext', vet jag inte varifrån idén kommer att den måste vara en AudioContext. Men hur som helst kan detta inte vara rätt sätt att närma sig problemet. Jag har ingen typinformation någonstans om webkitAudioContext, bara om AudioContext. Finns den nödvändiga typinformationen någonstans och har jag bara inte hittat den? Om den inte finns, skulle det vara svårt att skapa den? Alternativt, finns det ett sätt att helt enkelt kringgå typkontrollen för denna uppsättning klasser (inte mitt föredragna alternativ, men om det är det bästa vi kan göra just nu, så tar jag det)? Eller finns det något helt annat sätt att närma sig detta som jag inte känner till?

Svar #1

Löst, åtminstone i en första approximation. Den här raden:

    PlayerService._audioContext = new ((<any>window).AudioContext || (<any>window).webkitAudioContext)();

skapar ett ljudsammanhang som fungerar på iOS och icke-iOS-plattformar, och 'any's hindrar TypeScript från att klaga. Detta räcker för mina ändamål, tills alla webbläsare antingen slutar använda webkit-prefixet, eller det stöds av en lämplig typdefinition.

Problemet jag hade med decodeAudioData() orsakades av att Chrome på iOS inte kunde förstå .ogg-filen jag försökte spela. När jag bytte till mp3 fungerade det.