5

I'm trying to figure out the best way to play a wav file in the background (HTML5-like) when I use a link_to tag in Rails.

Here's a sample link_to from one of my views:

<%= link_to 'At Station', at_station_mdt_index_path, :class => 'btn btn-success btn-medium', :method => :put, :remote => true %>

I'd like to figure out how to use the audio_tag to trigger a sound when the button is pressed. I've tried combining the audio_tag in the link_to ERB but get all sort of syntax errors.

Any examples would be greatly appreciated.

Updated 01/04/14-10:18am CT: The sounds fire once and properly. However since adding the :id to the link_to the links no longer trigger the rails path to change the object, only plays the sound

View code:

    <%= link_to 'En Route', en_route_mdt_index_path(:call_id => call.id), :class => 'btn btn-warning btn-medium', :method => :put, :remote => true, :id => "er" %>
            <%= link_to 'On Scene', on_scene_mdt_index_path(:call_id => call.id), :id => 'to', :class => 'btn btn-primary btn-medium', :method => :put, :remote => true, :id => "os"  %>
            <%= link_to 'To Hospital', to_hospital_mdt_index_path(:call_id => call.id), :class => 'btn btn-warning btn-medium', :method => :put, :remote => true, :id => "to" %>

 <audio id="en-route" class="audio_player" preload="true">
    <source src="audios/en-route.wav" type="audio/wav">
</audio>

<audio id="on-scene" class="audio_player" preload="true">
    <source src="audios/on-scene.wav" type="audio/wav">
</audio>

<audio id="to-hospital" class="audio_player" preload="true">
    <source src="audios/to-hospital.wav" type="audio/wav">
</audio>

<script>
$('#er').click(function (e) {
    e.preventDefault();
    $('#en-route')[0].currentTime = 0;
    $('#en-route')[0].play();
    return true;

});

$('#os').click(function (e) {
    e.preventDefault();
    $('#on-scene')[0].currentTime = 0;
    $('#on-scene')[0].play();
    return true;

});

$('#to').click(function (e) {
    e.preventDefault();
    $('#to-hospital')[0].currentTime = 0;
    $('#to-hospital')[0].play();
    return true;

});
</script>
nulltek
  • 3,247
  • 9
  • 44
  • 94

3 Answers3

5

Here's a simple example for playing a wav file:

http://jsfiddle.net/84pav/

HTML:

<a href="javascript:void(0)" id="playsound">At Station</a>

<audio id="sound_effect" class="audio_player" preload="auto">
    <source src="http://www.villagegeek.com/downloads/webwavs/alrighty.wav" type="audio/wav">
</audio>

JavaScript:

$('#playsound').click(function (e) {
    $('#sound_effect')[0].currentTime = 0;
    $('#sound_effect')[0].play();
    return false;
});

I set currentTime = 0 before playing to make it always plays from the beginning.

It does not work on IE because IE does not support wav file.

Tyler Nguyen
  • 537
  • 3
  • 12
  • This actually works. However after my partials refresh via Javascript the link no longer works. It will only work once I reload the whole page. My JS function refreshes the partials every 10 seconds to pull for new data, somehow that is causing the audio tag not to load. Only on page refresh. – nulltek Jan 03 '14 at 19:00
  • In your question, you got error from application.js. It means that you put your script in one of the js file in assets. And I guess that you load the application.js in the layout. With partial reload, the DOM elements are replaced. The click event need to be bound again to the new DOM elements. But your script is only loaded one time, so it does not work. There're two ways to fix this: (1) put the script in your partial so that it is re-run every time you reload the page, or (2) after you reload the page, you call the function to bind the events yourself. – Tyler Nguyen Jan 04 '14 at 03:45
  • I tried moving the audio element and the javascript into the partial. Even with proper ID elements differentiating the two links any link on the page triggers both sounds. Also when the index.html.erb javascript reloads every 10 seconds it plays both sounds at the same time. I've pasted my current code for you to look at. – nulltek Jan 04 '14 at 14:48
  • @teknull, if you want to suggest code edits, how about using a comment so that Tyler Nguyen can effect them if they so desire? Please note not to change code in Edits. – Bill Woodger Jan 04 '14 at 15:04
  • @teknull, you should remove the autoplay="false" in your audio tags. It causes the audio files to be auto-played. No matter what you put in the autoplay="anything", it will be auto-played. – Tyler Nguyen Jan 04 '14 at 15:10
  • Sorry about the edits. I was just trying to show my current code. Removing the autoplay works, however the links only trigger the sound now, they do not trigger the actual rails route path to change the status of the call. Adding the :id => "element" seems to have broken something. – nulltek Jan 04 '14 at 15:16
  • After clicking on the link, it plays a sound and calls the url. Is that what you want? – Tyler Nguyen Jan 04 '14 at 15:23
  • @TylerNguyen Yes, I want it to play the sound and call the URL via ajax. The current code (which I updated in my original question) triggers the sounds properly but does not call the URL once I added the :id element to the link_to tag. – nulltek Jan 04 '14 at 15:25
  • You can remove the "return false;" lines of the click handlers to make the links work. – Tyler Nguyen Jan 04 '14 at 15:26
  • @TylerNguyen Removing the "return false;" makes the link works. But once the partials refresh, the sounds no longer trigger and in the console I get the error "Uncaught InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable." – nulltek Jan 04 '14 at 15:31
  • I don't have Rails here right now to test. Can you try the following? Instead of binding on click event, bind on ajax:before as $('#er').on('ajax:before', function () { ... }); – Tyler Nguyen Jan 04 '14 at 15:36
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/44501/discussion-between-teknull-and-tyler-nguyen) – nulltek Jan 04 '14 at 15:36
  • This is the closest answer to my question. I've been able to make the links work with removing return false;. But the sound only triggers once. Possibly has to do with the way I'm refreshing the partial. My only thought is to reload the whole page instead of the partials every 10 seconds. This would be messy, but might work. – nulltek Jan 04 '14 at 16:07
  • Using in the index.html.erb view reloads the DOMS and allows me to fire the sounds again. Although it forces a page reload which is ugly and not what I want. How can I reload the DOMS when refreshing the partials? – nulltek Jan 04 '14 at 17:06
1

I'm making a few assumptions in this answer:

  1. You're using HTML5 markup
  2. You want a styled link "button" to trigger a sound
  3. You don't want to display the default HTML5 audio player
  4. The sound files are stored in public/audios
  5. The sound file is pre-loaded on the page

Unfortunately, the Rails 3.2 implementation of audio_tag is broken (https://github.com/rails/rails/issues/9373). So unless you're using Rails 4, you're better off using the actual HTML markup.

In this example, we're loading an mp3 file in the the default HTML5 audio player. The following snippet should be in your view file.

<audio id="sound_effect" class="audio_player" controls="false" preload="true">
  <source src="/audios/TrainWhistle.mp3" type="audio/mpeg">
</audio>

Since the HTML5 audio player comes with "built-in" display chrome, you may want to position it off the page with css. In this example, you would add the following to your css file:

.audio_player {
  position: absolute;
  top: -999em;
{

Your link_to markup would look something like:

<%= link_to 'At Station', '#', class: 'btn btn-success btn-medium', id: 'playsound' %>

The javascript to actually play the sound will look similar to this jQuery example:

$('#playsound').on('click', function (e) {
  e.preventDefault();
  $('#sound_effect').currentTime = 0;
  $('#sound_effect').play();
  return false;
});

This example can easily be extended to support multiple files by small changes to the link_to tag and javascript.

jrmyward
  • 136
  • 1
  • 4
  • Thanks, this is the right track. I've edited the code to reflect my settings (I'm using wav files in the public/audios directory). The CSS does hide the audio player which is nice. I've unhid it to see what's happening as when I click on the link_to it doesn't play anything. It just skips really fast through the file. Any thoughts ? I've edited your code example above to reflect my config. – nulltek Dec 30 '13 at 22:55
  • When I unhide the audio_player class and try to play nothing happens. It just skips over the file really fast. Plus the volume has icon has a slash through it whether or not I enable controls. I'm using chrome btw. Though this feature will be intended for iOS Safari. – nulltek Dec 30 '13 at 22:59
  • Can you post a link? Might help with debugging. – jrmyward Dec 31 '13 at 17:44
  • I only have this in a development environment. It's not deployed to production. Sorry for the hassle. – nulltek Dec 31 '13 at 18:11
  • Forcing the sound to always play from the beginning is not a bad idea. I'll update my answer with that as well. It may take care of the "hi speed" playback. – jrmyward Jan 03 '14 at 22:39
-1

How about something like this...

function playSound(soundfile) {
  document.getElementById("playsound").innerHTML=
  "<embed src=\""+soundfile+"\" hidden=\"true\" autostart=\"true\" loop=\"false\" />";
}

$('#playsound').observe('click', function (event) {
  playSound('URL to soundfile');
  event.stop(); // Prevent link from following through to its given href
});

Then add the playsound id to your link.

Donovan
  • 15,917
  • 4
  • 22
  • 34
  • Thanks for the JS. I've tried this but I get Uncaught TypeError: Object [object Object] has no method 'observe' in the browser console and it does not trigger the soundbyte. I've updated my code to reflect your suggestions in case my syntax is wrong. – nulltek Dec 28 '13 at 14:40
  • I wasn't able to try it locally, the example was primarily to showcase the concept, you may have to tweak it a bit to get to work for you. – Donovan Dec 30 '13 at 14:43
  • I'm working on it, but am a JS/jQuery novice. I need to find some more examples so that I understand it better and can get something working. Pulling out my hair lol – nulltek Dec 30 '13 at 16:42