2016-02-16 7 views
5

In meiner Webanwendung muss ich einen Teil der mp3-Datei spielen. Dies ist eine lokale Web-App, also ist mir egal, Downloads usw., alles ist lokal gespeichert.Wie kann man nur einen Teil des MP3 für die Verwendung mit der WebAudio API dekodieren?

Mein Anwendungsfall ist wie folgt:

  • Datei bestimmen
  • bestimmen Start- und Stopp des Schall
  • laden Sie die Datei zu spielen [Ich benutze BufferLoader]
  • Spiel

Ganz einfach.

Im Moment schnappe ich mir einfach die MP3-Datei, entziffere sie für die Verwendung mit der WebAudio-API und spiele sie ab. Leider, weil die mp3-Dateien ziemlich lang werden können [30 Minuten Audio zum Beispiel] kann die dekodierte Datei im Speicher bis zu 900MB dauern. Das ist ein bisschen zu viel zu handhaben.

Gibt es eine Option, wo ich nur einen Teil der Datei entschlüsseln könnte? Wie kann ich erkennen, wo ich anfangen soll und wie weit? Ich kann nicht die Bitrate antizipieren, es kann konstant sein, aber ich würde auch Variable erwarten.

Hier ist ein Beispiel dafür, was ich getan habe: http://tinyurl.com/z9vjy34

Der Code [Ich habe versucht, es so kompakt wie möglich zu machen]:

var MediaPlayerAudioContext = window.AudioContext || window.webkitAudioContext; 

var MediaPlayer = function() { 
    this.mediaPlayerAudioContext = new MediaPlayerAudioContext(); 
    this.currentTextItem = 0; 
    this.playing = false; 
    this.active = false; 
    this.currentPage = null; 
    this.currentAudioTrack = 0; 
}; 

MediaPlayer.prototype.setPageNumber = function (page_number) { 
    this.pageTotalNumber = page_number 
}; 

MediaPlayer.prototype.generateAudioTracks = function() { 
    var audioTracks = []; 
    var currentBegin; 
    var currentEnd; 
    var currentPath; 
    audioTracks[0] = { 
     begin: 4.300, 
     end: 10.000, 
     path: "example.mp3" 
    }; 
    this.currentPageAudioTracks = audioTracks; 
}; 

MediaPlayer.prototype.show = function() { 
    this.mediaPlayerAudioContext = new MediaPlayerAudioContext(); 
}; 

MediaPlayer.prototype.hide = function() { 
    if (this.playing) { 
     this.stop(); 
    } 
    this.mediaPlayerAudioContext = null; 
    this.active = false; 
}; 

MediaPlayer.prototype.play = function() { 
    this.stopped = false; 
    console.trace(); 

    this.playMediaPlayer(); 
}; 

MediaPlayer.prototype.playbackStarted = function() { 
    this.playing = true; 
}; 

MediaPlayer.prototype.playMediaPlayer = function() { 
    var instance = this; 

    var audioTrack = this.currentPageAudioTracks[this.currentAudioTrack]; 
    var newBufferPath = audioTrack.path; 

    if (this.mediaPlayerBufferPath && this.mediaPlayerBufferPath === newBufferPath) { 

     this.currentBufferSource = this.mediaPlayerAudioContext.createBufferSource(); 
     this.currentBufferSource.buffer = this.mediaPlayerBuffer; 
     this.currentBufferSource.connect(this.mediaPlayerAudioContext.destination); 

     this.currentBufferSource.onended = function() { 
      instance.currentBufferSource.disconnect(0); 
      instance.audioTrackFinishedPlaying() 
     }; 

     this.playing = true; 
     this.currentBufferSource.start(0, audioTrack.begin, audioTrack.end - audioTrack.begin); 

     this.currentAudioStartTimeInAudioContext = this.mediaPlayerAudioContext.currentTime; 
     this.currentAudioStartTimeOffset = audioTrack.begin; 
     this.currentTrackStartTime = this.mediaPlayerAudioContext.currentTime - (this.currentTrackResumeOffset || 0); 
     this.currentTrackResumeOffset = null; 

    } 
    else { 
     function finishedLoading(bufferList) { 
      instance.mediaPlayerBuffer = bufferList[0]; 
      instance.playMediaPlayer(); 
     } 

     if (this.currentBufferSource){ 
      this.currentBufferSource.disconnect(0); 
      this.currentBufferSource.stop(0); 
      this.currentBufferSource = null; 
     } 

     this.mediaPlayerBuffer = null; 
     this.mediaPlayerBufferPath = newBufferPath; 
     this.bufferLoader = new BufferLoader(this.mediaPlayerAudioContext, [this.mediaPlayerBufferPath], finishedLoading); 
     this.bufferLoader.load(); 
    } 
}; 

MediaPlayer.prototype.stop = function() { 
    this.stopped = true; 
    if (this.currentBufferSource) { 
     this.currentBufferSource.onended = null; 
     this.currentBufferSource.disconnect(0); 
     this.currentBufferSource.stop(0); 
     this.currentBufferSource = null; 

    } 
    this.bufferLoader = null; 
    this.mediaPlayerBuffer = null; 
    this.mediaPlayerBufferPath = null; 
    this.currentTrackStartTime = null; 
    this.currentTrackResumeOffset = null; 
    this.currentAudioTrack = 0; 

    if (this.currentTextTimeout) { 
     clearTimeout(this.currentTextTimeout); 
     this.textHighlightFinished(); 
     this.currentTextTimeout = null; 
     this.currentTextItem = null; 
    } 

    this.playing = false; 
}; 

MediaPlayer.prototype.getNumberOfPages = function() { 
    return this.pageTotalNumber; 
}; 

MediaPlayer.prototype.playbackFinished = function() { 

    this.currentAudioTrack = 0; 
    this.playing = false; 

}; 

MediaPlayer.prototype.audioTrackFinishedPlaying = function() { 

    this.currentAudioTrack++; 

    if (this.currentAudioTrack >= this.currentPageAudioTracks.length) { 
     this.playbackFinished(); 
    } else { 
     this.playMediaPlayer(); 
    } 
}; 

// 
// 
// Buffered Loader 
// 
// Class used to get the sound files 
// 
function BufferLoader(context, urlList, callback) { 
    this.context = context; 
    this.urlList = urlList; 
    this.onload = callback; 
    this.bufferList = []; 
    this.loadCount = 0; 
} 

// this allows us to handle media files with embedded artwork/id3 tags 
function syncStream(node) { // should be done by api itself. and hopefully will. 
    var buf8 = new Uint8Array(node.buf); 
    buf8.indexOf = Array.prototype.indexOf; 
    var i = node.sync, b = buf8; 
    while (1) { 
     node.retry++; 
     i = b.indexOf(0xFF, i); 
     if (i == -1 || (b[i + 1] & 0xE0 == 0xE0)) break; 
     i++; 
    } 
    if (i != -1) { 
     var tmp = node.buf.slice(i); //carefull there it returns copy 
     delete(node.buf); 
     node.buf = null; 
     node.buf = tmp; 
     node.sync = i; 
     return true; 
    } 
    return false; 
} 

BufferLoader.prototype.loadBuffer = function (url, index) { 
    // Load buffer asynchronously 
    var request = new XMLHttpRequest(); 
    request.open("GET", url, true); 
    request.responseType = "arraybuffer"; 

    var loader = this; 

    function decode(sound) { 
     loader.context.decodeAudioData(
       sound.buf, 
       function (buffer) { 
        if (!buffer) { 
         alert('error decoding file data'); 
         return 
        } 
        loader.bufferList[index] = buffer; 
        if (++loader.loadCount == loader.urlList.length) 
         loader.onload(loader.bufferList); 
       }, 
       function (error) { 
        if (syncStream(sound)) { 
         decode(sound); 
        } else { 
         console.error('decodeAudioData error', error); 
        } 
       } 
     ); 
    } 

    request.onload = function() { 
     // Asynchronously decode the audio file data in request.response 
     var sound = {}; 
     sound.buf = request.response; 
     sound.sync = 0; 
     sound.retry = 0; 
     decode(sound); 
    }; 
    request.onerror = function() { 
     alert('BufferLoader: XHR error'); 
    }; 
    request.send(); 
}; 

BufferLoader.prototype.load = function() { 
    for (var i = 0; i < this.urlList.length; ++i) 
     this.loadBuffer(this.urlList[i], i); 
}; 
+0

Konvertieren Sie eine MP3 in eine WAV-Datei? 900MB ist verrückt. – zer00ne

+0

Ich habe keine Möglichkeit gefunden mp3 direkt zu benutzen, muss es immer entschlüsseln. – Krystian

+0

Wenn Sie nur spielen wollen, warum nicht ein 'Audio'-Tag verwenden? –

Antwort

1

Es gibt keine Möglichkeit, mit decodeAudioData von Streaming (), müssen Sie MediaElement mit createMediaStreamSource verwenden und Ihre Daten dann ausführen. decodeAudioData() kann nicht auf einem Teil streamen. @ zre00ne Und mp3 wird groß dekodiert werden !!! Sehr groß!!!

+0

Richtig, genau das passiert. 20MB MP3 wird 900MB PCM: / – Krystian